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;
}


