Replikace dat mezi 389 Directory Server a SUN Directory Server
Pro jednoho zákazníka řešíme přechod z adresářového serveru SUN Directory Server 6.3 (Solaris) na 389 Directory Server na linuxu (RHEL). V průběhu přechodu je nutné zajistit replikaci dat z nových serverů 389 DS na staré servery SUN DS co jsou rozmístěné na pobočkách různě po republice.
I když vychází adresářové servery 389 DS a SUN DS ze stejného základu, z iPlanetího kódu, je mezi SUN DS a 389 DS nemožné replikovat data pomocí standardní replikace. Replikační protokol se liší.
Režim replikace, který potřebujeme, je pouze jednosměrný Master->Slave z 389 DS na SUN DS. Počet a četnost replikovaných změn není nějak zvlášť velká.
Požadavky na replikaci:
- Přenesení změn v rozumném čase – v řádu sekund
- Automatické navázání replikace po přerušení v místě kde replikace skončila
Replikaci realizujeme skriptem, který čte data z auditního logu 389 DS a zapisuje je přes LDAP spojení do SUN DS. Server SUN DS do kterého se zapisuje skriptem je vyhrazený jen pro tento účel a z něj se poté standardní cestou replikují data na zbývající pobočkové servery.
Uvedený skript možná není dokonalý, ale na dočasnou replikaci změn z 389DS do SUNDS po dobu než budou nahrazeny všechny servery je plně dostatečný. Případné použití je pouze na vaše vlastní nebezpečí :-)
#!/usr/bin/perl -Tw BEGIN { our $pidname = "repl_pl.pid"; our $piddir = "/var/tmp"; our $pidsilent = 0; } package main; use strict; use warnings; my $audit_file = "/var/log/dirsrv/slapd-ldap1/audit"; my $state_file = "/var/lib/dirsrv_repl/.repl_pl"; $ENV{PATH}='/sbin:/usr/sbin:/bin:/usr/bin'; $ENV{IFS}=''; use Fcntl qw/ SEEK_SET O_RDWR O_CREAT /; use NDBM_File; use Net::LDAP; use Net::LDAP::LDIF; my $sunds; LDAPCONN: { while (1) { $sunds = Net::LDAP->new('127.0.0.1:1389', onerror => 'warn') and last LDAPCONN; print STDERR "Cekam na SUNDS ldap\n"; sleep 10; } } my $result = $sunds->bind("cn=Directory Manager", password=>"PASSWORD"); if ( $result->code ) { LDAPerror ( "Binding", $result ); } my %states; tie(%states, 'NDBM_File', $state_file, O_CREAT | O_RDWR, 0660) or die("cannot tie state to $state_file : $!"); #print STDERR "Obsah stavoveho souboru:\n"; #while ((my $key, my $val) = each %states) { # print STDERR $key, ' = ', $val, "\n"; #} while (1) { if (! -r $audit_file) { next; } my @file_stats = stat($audit_file); my $device = $file_stats[0]; my $inode = $file_stats[1]; my $size = $file_stats[7]; my $state_key = $device . "/" .$inode; if (defined $states{"lastinode"} && $states{"lastinode"} != $inode) { # inode se lisi => nove vytvoreny soubor print STDERR "$audit_file je novy soubor.\n"; } if (! open(FILE, $audit_file) ) { print STDERR "Nemohu otevrit soubor $audit_file : $!"; next; } # posledni zpracovavany soubor $states{"lastinode"} = $inode; my $offset = $states{$state_key} || 0; if ($offset <= $size) { sysseek(FILE, $offset, SEEK_SET); } else { $offset = 0; } my $buffer; my $RAW_LDIF = undef; while ((my $read_count = sysread(FILE, $buffer, 4096)) > 0) { $offset += $read_count; $RAW_LDIF=$RAW_LDIF.$buffer; } close(FILE); if ($RAW_LDIF) { # tady je možné měnit načítaný LDIF z auditu $RAW_LDIF =~ s/^\t389-Directory.+\n\tldap.+\n\n//gm; $RAW_LDIF =~ s/^time: \d{14}\ndn:/dn:/gm; $RAW_LDIF =~ s/^dn: .+\nchangetype: modify\nreplace: passwordgraceusertime\npasswordGraceUserTime: \d+\n-\n\n//gim; } if ($RAW_LDIF && ($RAW_LDIF ne "")) { open(FH, '<', \$RAW_LDIF); my $ldif = Net::LDAP::LDIF->new( *FH{IO}, "r", onerror => 'die'); my $ldifcnt = 1; while( not $ldif->eof() ) { my $entry = $ldif->read_entry(); if ( $ldif->error() ) { print STDERR "Error msg: ", $ldif->error ( ), "\n"; print STDERR "Error lines:\n", $ldif->error_lines ( ), "\n"; die; } else { if (defined $states{"ldifcnt"} && $ldifcnt < $states{"ldifcnt"}) { # zaznam jsme uz zpracovali v drivejsim prubehu, takze ho muzeme preskocit next; } my $entrydn = $entry->dn(); if ($entrydn =~ m/o=ORGANIZACE,c=cz$/) { $result = $entry->update($sunds); if ( $result->code ) { LDAPerror ( "Binding", $result ); die; } } $states{"ldifcnt"} = $ldifcnt; # pocitadlo zpracovanych LDIF zaznamu $ldifcnt++; } } $states{"ldifcnt"} = 1; # reset pocitadla zaznamu $states{$state_key} = $offset; # uspesne nactene zaznamy z dane pozice } sleep 1; } sub LDAPerror { my ($from, $mesg) = @_; print STDERR "Return code: ", $mesg->code; print STDERR "\tMessage: ", $mesg->error_name; print STDERR " :", $mesg->error_text; print STDERR "MessageID: ", $mesg->mesg_id; print STDERR "\tDN: ", $mesg->dn; } # Osetreni zamku procesu aby se nespoustel vicekrat BEGIN { use Proc::Pidfile; our $pid = $$; our ($pidname, $piddir, $pidsilent); our $pidfile = Proc::Pidfile->new( pidfile => "$piddir/$pidname", silent=>$pidsilent ); } END { our ($pidfile, %states); untie(%states); undef $pidfile; }