Hi All,
First of all I would like to mention that this is not a question, I would just like to share some of the fun I've had with ClearOS last couple of days.
I have just installed a new ClearOS and want to migrate existing infrastructure (LDAP database+Mail) towards ClearOS.
As I would want to have the same users, passwords and groups I've had in our legacy infrastructure migrated towards the LDAP of the newly installed ClearOS, I looked around and found no tools for migration of the LDAP database so I figured I should write a short script to do that.
I would like to share the script as I hope it can be useful to people who would have the same needs.
First a couple of words on the task (I have hard-coded a few values so understanding my task might help to rewrite the script according to your needs).
I want to migrate just the users and the groups from the old LDAP. All of them are posix users and groups.
So the legacy user entries would look like:
While the ClearOS entries should look like:
The situation with the entries for the groups was almost the same.
My Legacy group entries look like this:
While ClearOS groups by default come like this:
Basically I had to get rid of some keys and change other keys.
Below you can find a perl script that did the magic for me.
It prints the changed ldif entries to the stdio.
I hope it would help other people like me who are migrating existing LDAP users to newly installed ClearOS.
First of all I would like to mention that this is not a question, I would just like to share some of the fun I've had with ClearOS last couple of days.
I have just installed a new ClearOS and want to migrate existing infrastructure (LDAP database+Mail) towards ClearOS.
As I would want to have the same users, passwords and groups I've had in our legacy infrastructure migrated towards the LDAP of the newly installed ClearOS, I looked around and found no tools for migration of the LDAP database so I figured I should write a short script to do that.
I would like to share the script as I hope it can be useful to people who would have the same needs.
First a couple of words on the task (I have hard-coded a few values so understanding my task might help to rewrite the script according to your needs).
I want to migrate just the users and the groups from the old LDAP. All of them are posix users and groups.
So the legacy user entries would look like:
dn: cn=FirstName Lastname,ou=Users,dc=example,dc=com
cn: FirstName Lastname
uid: username
homeDirectory: /home/username
mail: username@example.com
objectClass: CourierMailAccount
objectClass: account
objectClass: posixAccount
uidNumber: 2000
gidNumber: 63000
structuralObjectClass: account
entryUUID: XXXXXXX-XXXX-XXXX-XXXX-XXXXXXX
creatorsName: cn=Manager,dc=example,dc=com
createTimestamp: YYYYYYYYYYYYYYYYYYYY
userPassword:: ZZZzzZZZZzzZZZZzzzZZZZZzzZZZZzzzZZZZ=
entryCSN: YYYYYYYYYYYYYYYYYYYYY#000000#00#000000
modifiersName: cn=Manager,dc=example,dc=com
modifyTimestamp: YYYYYYYYYYYYYYYYYYYY
While the ClearOS entries should look like:
dn: cn=Firstname Lastname,ou=Users,ou=Accounts,dc=example,dc=com
uid: username
givenName: Firstname
sn: Lastname
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: clearAccount
objectClass: clearMailAccount
cn: Firstname Lastname
uidNumber: 2000
loginShell: /sbin/nologin
gidNumber: 63000
homeDirectory: /home/username
clearAccountStatus: enabled
mail: username@example.com
The situation with the entries for the groups was almost the same.
My Legacy group entries look like this:
dn: cn=group1,ou=Groups,dc=example,dc=com
objectClass: posixGroup
gidNumber: 212
cn: group1
memberUid: user1
memberUid: user2
description: Test Group
structuralObjectClass: posixGroup
entryUUID: XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX
creatorsName: cn=Manager,dc=example,dc=com
createTimestamp: YYYYYYYYYYYYYYYYYY
entryCSN: YYYYYYYYYYYY#000000#00#000000
modifiersName: cn=Manager,dc=example,dc=com
modifyTimestamp: YYYYYYYYYYYYYYYY
While ClearOS groups by default come like this:
dn: cn=group1,ou=Groups,ou=Accounts,dc=example,dc=com
description: Test Group
gidNumber: 60002
cn: group1
objectClass: top
objectClass: posixGroup
objectClass: groupOfNames
objectClass: sambaGroupMapping
sambaSID: -60002
sambaGroupType: 2
displayName: group1
clearMailDistributionList: 0
member: cn=No Members,ou=Users,ou=Accounts,dc=example,dc=com
Basically I had to get rid of some keys and change other keys.
Below you can find a perl script that did the magic for me.
It prints the changed ldif entries to the stdio.
I hope it would help other people like me who are migrating existing LDAP users to newly installed ClearOS.
#!/usr/bin/perl
use strict;
use warnings;
# TODO: Set Filename of exported ldif here.
my $in='ldapusers.ldif';
my @users;
my $entry={};
my $i=0;
## Read the old ldif
open IN , "<$in" or die "No such file: $in\n";
while (<IN> {
if ($_ =~ /^\n/) {
push @users, $entry;
$entry={};
} else {
my ($key,$value,$password) = split(/:/,$_);
# Trim the value
if ($key eq "userPassword") {
$value = ":$password";
$entry->{$key}=$value;
# TODO: keys with multiple lines per entry.
} elsif ($key eq "objectClass" or $key eq "memberUid" or $key eq 'member') {
$value =~ s/^\s+|\s+$//g;
if ( exists $entry->{$key} ) {
if ( is_array($entry->{$key}) ) {
push ( $entry->{$key}, $value );
} else {
$entry->{$key} = [$entry->{$key},$value];
}
} else {
$entry->{$key} = $value;
}
} else {
$value =~ s/^\s+|\s+$//g;
$entry->{$key}=$value;
}
}
}
close IN;
## TODO: This needs to be changed according to what the old ldif looks like and according to any specific needs
foreach (@users) {
## Debugging:
#print ">>> Processing: $_->{'dn'}\n";
addToDN($_,"ou=Accounts");
removeKey($_,"structuralObjectClass");
if (ifUser($_)){
my ($firstName, $lastName) = split (/ /,$_->{'cn'});
$lastName=$firstName unless defined $lastName;
removeAttribute($_,"objectClass","CourierMailAccount");
removeAttribute($_,"objectClass","account");
addAttribute ($_,'objectClass','top');
addAttribute ($_,'objectClass','shadowAccount');
addAttribute ($_,'objectClass','inetOrgPerson');
addAttribute ($_,'objectClass','clearAccount');
addAttribute ($_,'objectClass','clearMailAccount');
addAttribute ($_,'loginShell','/sbin/nologin') unless exists $_->{'loginShell'};
addAttribute ($_,'clearAccountStatus','enabled');
addAttribute ($_,'givenName',$firstName);
addAttribute ($_,'sn',$lastName);
} elsif (ifGroup($_)){
addAttribute ($_,'objectClass','top');
addAttribute ($_,'objectClass','groupOfNames');
addAttribute ($_,'objectClass','sambaGroupMapping');
addAttribute ($_,'sambaSID','-'.$_->{'gidNumber'});
addAttribute ($_,'sambaGroupType','2');
# Replace all memberUid:<UID> with member: <dn>
if ( exists $_->{'memberUid'} ) {
if (is_array($_->{'memberUid'})) {
my @memberUids=@{$_->{'memberUid'}};
removeKey($_,'memberUid');
foreach my $m (@memberUids) {
if ( my $e=searchUid($m)) {
addAttribute($_,'member',${$e}->{'dn'});
}
}
} else {
if ( my $n = searchUid($_->{'memberUid'}) ) {
addAttribute($_,'member',${$n}->{'dn'});
removeKey($_,'memberUid');
} else {
addAttribute($_,'member','cn=no members,ou=users,ou=accounts,dc=iis,dc=bg');
}
}
} else {
## TODO : Hardcoded value, To be changed in case of need
addAttribute($_,'member','cn=no members,ou=users,ou=accounts,dc=iis,dc=bg');
}
}
printEntry($_);
}
sub searchUid {
my $uid = shift;
foreach (@users) {
next unless defined $_->{'uid'};
return \$_ if ( ${$_}{'uid'} eq $uid);
}
return 0;
}
sub ifUser {
my $e = shift;
foreach (split(/,/,$e->{"dn"})) {
my ($k,$v) = split(/=/,$_);
return 1 if ($k eq "ou" && $v eq "Users");
}
return 0;
}
sub ifGroup {
my $e = shift;
foreach (split(/,/,$e->{"dn"})) {
my ($k,$v) = split(/=/,$_);
return 1 if ($k eq "ou" && $v eq "Groups");
}
return 0;
}
#TODO: The position of what is added to the dn is hardcoded. Must be changed in case of a need.
sub addToDN {
my $e = shift; # expecte reference to entry;
my $x = shift; # expected : "ou=Accounts"
my @dn =split(/,/,$e->{'dn'});
my $index = $#dn-1;
splice @dn, $index, 0, $x;
$e->{'dn'}=join(',',@dn);
#Debug
#print ">>> DN Changed to : ".$e->{'dn'}."\n";
}
sub removeKey {
my $e = shift;
my $key = shift;
delete $e->{$key};
}
sub addAttribute {
my $e = shift;
my $key = shift;
my $value = shift;
return -1 unless (defined $value and defined $key and defined $e);
unless ( defined $e->{$key} ) {
$e->{$key} = $value ;
} else {
if (is_array($e->{$key})) {
push @{$e->{$key}},$value;
} else {
$e->{$key}=[$e->{$key},$value];
}
}
}
sub removeAttribute {
my $e = shift;
my $key = shift;
my $value = shift;
return -1 unless defined $value and defined $key;
if (is_array($e->{$key})) {
for (my $n=0;$n<=$#{$e->{$key}};$n++){
## DEBUG:
#print '>>> $e->{$key}[$n] eq $value is ';
#print "true\n" if (${$e->{$key}}[$n] eq $value);
#print "false\n" unless (${$e->{$key}}[$n] eq $value);
splice (@{$e->{$key}},$n,1) if (${$e->{$key}}[$n] eq $value);
}
} else {
delete $e->{$key} if (defined $e->{$key} and $e->{$key} eq $value);
}
}
sub printEntry {
my $e = shift;
#dn first
print "dn: $e->{dn}\n";
#cn follows
print "cn: $e->{cn}\n";
#print object Classes
if (is_array($e->{objectClass})) {
foreach (@{$e->{objectClass}}) {
print "objectClass: $_\n";
}
} else {
print "objectClass: $e->{objectClass}\n";
}
#print all the rest
foreach ( sort keys %{$e}){
unless ($_ eq "dn" or $_ eq "cn" or $_ eq "objectClass" or $_ eq "userPassword") {
if (is_array($e->{$_})) {
foreach my $v ( @{$e->{$_}}) {
print "$_: $v\n";
}
} else {
print "$_: $e->{$_}\n"
}
}
print "$_:$e->{$_}\n" if ($_ eq "userPassword");
}
print "\n";
}
sub is_array {
my ($ref) = @_;
# Firstly arrays need to be references, throw
# out non-references early.
return 0 unless ref $ref;
# Now try and eval a bit of code to treat the
# reference as an array. If it complains
# in the 'Not an ARRAY reference' then we're
# sure it's not an array, otherwise it was.
eval {
my $a = @$ref;
};
if ($@=~/^Not an ARRAY reference/) {
return 0;
} elsif ($@) {
die "Unexpected error in eval: $@\n";
} else {
return 1;
}
}
Share this post:
Responses (3)
-
Accepted Answer
-
Accepted Answer
Hi, Thank you the reply.
It is great to know that there is an easy way for migration between ClearOS6 and ClearOS7. One more benefit from migrating towards it.
The point here is that I am migrating from an outdated gentoo based system. Basically rebuilding all the services I've had there on the newly install ClearOS7. Also I decided not to use the same configurations but to configure everything from scratch and migrate just the data so that I can maintain the same user experience.
In the above example the I used the output from "slapcat -n2", removed the "default" entries that have replacements and the new installation and the result I supplied as an input for the script.
The output from the script I used as an input towards "slapadd -n3". I must highlight that a backup of the DB of the new installation is required as the slapadd is very likely to fail a couple of times until managing all the correct schema entries for the new configuration. -
Accepted Answer
Please login to post a reply
You will need to be logged in to be able to post a reply. Login using the form on the right or register an account if you are new here.
Register Here »