# BEGIN COPYRIGHT BLOCK
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details. 
# END COPYRIGHT BLOCK
#

###########################
#
# This perl module provides a way to set up a new installation after
# the binaries have already been extracted.  This is typically after
# using native packaging support to install the package e.g. RPM,
# pkgadd, depot, etc.  This script will show the license, readme,
# dsktune, then run the usual setup pre and post installers.
#
##########################

package Migration;
use Setup;

use Exporter ();
@ISA       = qw(Exporter Setup);
@EXPORT    = qw();
@EXPORT_OK = qw();

# hostname
use Sys::Hostname;

# load perldap
use Mozilla::LDAP::Conn;
use Mozilla::LDAP::Utils qw(normalizeDN);
use Mozilla::LDAP::API qw(ldap_explode_dn);
use Mozilla::LDAP::LDIF;

use Getopt::Long;

use SetupLog;
use DSUtil;

# process command line options
Getopt::Long::Configure(qw(bundling)); # bundling allows -ddddd

sub VersionMessage {
    print "389 Directory Server Migration Program Version 1.4.3.4\n";
}

sub HelpMessage {
    print <<EOF;

INTRODUCTION

This script will copy instances (data and configuration) from the old
server root directory to their new FHS locations.  This script does a
copy only - the data in the old instances will be left untouched.  The
old instances must be shutdown first to ensure that the databases are
copied safely.  During migration your migrated instances will be started.

USAGE

 $0 [--options] -- [args]

options:
    --help        This message
    --version     Print the version and exit
    --debug       Turn on debugging
    --oldsroot    The old server root directory to migrate from
    --actualsroot This is the old location of the old server root.
                  See below.
    --silent      Use silent setup - no user input
    --file=name   Use the file 'name' in .inf format to supply the
                  default answers
    --keepcache   Do not delete the temporary .inf file generated by
                  this program
    --logfile     Log migration messages to this file - otherwise, a temp
                  file will be used
    --instance    By default, all directory server instances will be
                  migrated.  You can use this argument to specify one
                  or more (e.g. -i slapd-foo -i slapd-bar) if you do
                  not want to migrate all of them.
    --cross       See below.

For all options, you can also use the short name e.g. -h, -d, etc.
For the -d argument, specifying it more than once will increase the
debug level e.g. -ddddd

args:
You can supply default .inf data in this format:
    section.param=value
e.g.
    General.FullMachineName=foo.example.com
or
    "slapd.Suffix=dc=example,dc=com"
Values passed in this manner will override values in an .inf file
given with the -f argument.

actualsroot:
This is used when you must migrate from one machine to another.  The
usual case is that you have mounted the old server root on a different
root directory, either via a network mount, or by copying a tarball
made using a relative directory on the source machine to the
destination machine and untarring it.

For example: machineA is a 32bit machine, and you want to migrate your
servers to a new 64bit machine.  Lets assume your old server root on
machineA was /opt/myds, and your new machine also wants to use a
server root of /opt/myds.  There are a couple of different ways to
proceed.  Either make a tarball of opt/myds from machineA using a
relative path (i.e. NOT /opt/myds) or use NFS to mount
machineA:/opt/myds on a different mount point
(e.g. machineB:/migration/opt/myds).

If you do this, you should give the old "real" server root (/opt/myds)
as the --actualsroot argument, and use /migration/opt/myds for the
--oldsroot argument.  That is, the oldsroot is the physical location of
the files on disk.  The actualsroot is the old value of the server root
on the source machine.

cross:
Also known as crossplatform, or 'c', or 'x'.
This is when the source machine is a different architecture than the
destination machine.  In this case, only certain data will be available
for migration.  Changelog information will not be migrated, and replicas
will need to be reinitialized (if migrating masters or hubs).  This type
of migration requires that all of your old databases have been dumped
to LDIF format, and the LDIF file must be in the default database directory
(usually /opt/389-ds/slapd-instance/db), and the LDIF file must have
the same name as the database instance directory, with a ".ldif".  For
example, if you have
 /opt/389-ds/slapd-instance/db/userRoot/ and
 /opt/389-ds/slapd-instance/db/NetscapeRoot/
you must first use db2ldif to export these databases to LDIF e.g.
 cd /opt/389-ds/slapd-instance
 ./db2ldif -n userRoot -a /opt/389-ds/slapd-instance/db/userRoot.ldif and
 ./db2ldif -n NetscapeRoot -a /opt/389-ds/slapd-instance/db/NetscapeRoot.ldif

Then you must somehow make your old server root directory available on
the destination machine, either by creating a tar archive on the source
and copying it to the destination, or by network mounting the source
directory on the destination machine.
EOF
}

sub init {
    my $self = shift;
    $self->{res} = shift;
    my ($silent, $inffile, $keep, $preonly, $logfile, $oldsroot, $actualsroot, $crossplatform);
    my @instances;

    GetOptions('help|h|?' => sub { VersionMessage(); HelpMessage(); exit 0 },
               'version|v' => sub { VersionMessage(); exit 0 },
               'debug|d+' => \$DSUtil::debuglevel,
               'silent|s' => \$silent,
               'file|f=s' => \$inffile,
               'keepcache|k' => \$keep,
               'preonly|p' => \$preonly,
               'logfile|l=s' => \$logfile,
               'oldsroot|o=s' => \$oldsroot,
               'actualsroot|a=s' => \$actualsroot,
               'crossplatform|cross|c|x' => \$crossplatform,
               'instance|i=s' => \@instances
               );

    my $pkgname = "dirsrv";
	# this is the new pkgname which may be something like
	# 389-ds-base - we have to strip off the -suffix
    if ($pkgname =~ /-(core|base)$/) {
        $pkgname =~ s/-(core|base)$//;
    }
    my $oldpkgname = "389-ds";

    $self->{pkgname} = $pkgname;
    $oldsroot =~ s/\/+$//; # trim trailing '/'s, if any
    $self->{oldsroot} = $oldsroot || "/opt/$oldpkgname";
    $actualsroot =~ s/\/+$//; # trim trailing '/'s, if any
    $self->{actualsroot} = $actualsroot || $self->{oldsroot};
    $self->{silent} = $silent;
    $self->{keep} = $keep;
    $self->{preonly} = $preonly;
    $self->{logfile} = $logfile;
    $self->{crossplatform} = $crossplatform;
    $self->{log} = new SetupLog($self->{logfile}, "migrate");
    DSUtil::setDebugLog($self->{log});
    $self->{start_servers} = 1; # start servers as soon as they are migrated
    # if user supplied inf file, use that to initialize
    if (defined($inffile)) {
        $self->{inf} = new Inf($inffile);
    } else {
        $self->{inf} = new Inf;
    }

    # see if user passed in default inf values - also, command line
    # arguments override those passed in via an inf file - this
    # allows the reuse of .inf files with some parameters overridden
    if (!$self->{inf}->updateFromArgs(@ARGV)) {
        HelpMessage();
        exit 1;
    }

    # this is the base config directory - the directory containing
    # the slapd-instance instance specific config directories
    $self->{configdir} = $ENV{DS_CONFIG_DIR} || "/etc/dirsrv";

	# get list of instances to migrate
    if (! @instances) {
        # an instance must be a directory called $oldsroot/slapd-something and the file
        # $oldsroot/slapd-something/config/dse.ldif must exist
        @instances = grep { -d && -f "$_/config/dse.ldif" && ($_ =~ s,$self->{oldsroot}/,,) }
        	glob("$self->{oldsroot}/slapd-*");
    }

    if (!@instances) {
        $self->msg($FATAL, "error_no_instances", $self->{oldsroot});
        VersionMessage();
        HelpMessage();
        exit 1;
    }

    $self->{instances} = \@instances;
}

# log only goes the the logfile
sub log {
    my $self = shift;
    my $level = shift;
    $self->{log}->logMessage($level, "Migration", @_);
}

sub doExit {
    my $self = shift;
    my $code = shift;
    if (!defined($code)) {
        $code = 1;
    }

    if ($code) {
        $self->msg($FATAL, 'migration_exiting', $self->{log}->{filename});
    } else {
        $self->msg($SUCCESS, 'migration_exiting', $self->{log}->{filename});
    }
	exit $code;
}

sub migrateSecurityFiles {
    my $self = shift;
    my $inst = shift;
    my $destdir = shift;
    my $oldroot = $self->{oldsroot};
    
    if (! -d "$oldroot/alias") {
        $self->msg('old_secdir_error', "$oldroot/alias", $!);
        return 0;
    } elsif (! -d $destdir) {
        $self->msg('new_secdir_error', $destdir, $!);
        return 0;
    } else {
        if (-f "$oldroot/alias/$inst-cert8.db") {
            $self->log($INFO, "Copying $oldroot/alias/$inst-cert8.db to $destdir/cert8.db\n");
            if (system ("cp -p $oldroot/alias/$inst-cert8.db $destdir/cert8.db")) {
                $self->msg($FATAL, 'error_copying_certdb', "$oldroot/alias/$inst-cert8.db",
                           "$destdir/cert8.db", $!);
                return 0;
            }
        } else {
            $self->log($DEBUG, "No file to migrate: $oldroot/alias/$inst-cert8.db\n");
        }

        if (-f "$oldroot/alias/$inst-key3.db") {
            $self->log($INFO, "Copying $oldroot/alias/$inst-key3.db to $destdir/key3.db\n");
            if (system ("cp -p $oldroot/alias/$inst-key3.db $destdir/key3.db")) {
                $self->msg($FATAL, 'error_copying_keydb', "$oldroot/alias/$inst-key3.db",
                           "$destdir/key3.db", $!);
                return 0;
            }
        } else {
            $self->log($DEBUG, "No file to migrate: $oldroot/alias/$inst-key3.db\n");
        }

        if (-f "$oldroot/alias/secmod.db") {
            $self->log($INFO, "Copying $oldroot/alias/secmod.db to $destdir/secmod.db\n");
            if (system ("cp -p $oldroot/alias/secmod.db $destdir/secmod.db")) {
                $self->msg($FATAL, 'error_copying_secmoddb', "$oldroot/alias/secmod.db",
                           "$destdir/secmod.db", $!);
                return 0;
            }
        } else {
            $self->log($DEBUG, "No file to migrate: $oldroot/alias/secmod.db\n");
        }

        if (-f "$oldroot/alias/$inst-pin.txt") {
            $self->log($INFO, "Copying $oldroot/alias/$inst-pin.txt to $destdir/pin.txt\n");
            if (system ("cp -p $oldroot/alias/$inst-pin.txt $destdir/pin.txt")) {
                $self->msg($FATAL, 'error_copying_pinfile', "$oldroot/alias/$inst-pin.txt",
                           "$destdir/pin.txt", $!);
                return 0;
            }
        } else {
            $self->log($INFO, "No $oldroot/alias/$inst-pin.txt to migrate\n");
        }
            
        if (-f "$oldroot/shared/config/certmap.conf") {
            $self->log($INFO, "Copying $oldroot/shared/config/certmap.conf to $destdir/certmap.conf\n");
            if (system ("cp -p $oldroot/shared/config/certmap.conf $destdir/certmap.conf")) {
                $self->msg($FATAL, 'error_copying_certmap', "$oldroot/shared/config/certmap.conf",
                           "$destdir/certmap.conf", $!);
                return 0;
            }
        } else {
            $self->log($INFO, "No $oldroot/shared/config/certmap.conf to migrate\n");
        }
    }

    return 1;
}

#############################################################################
# Mandatory TRUE return value.
#
1;

# emacs settings
# Local Variables:
# mode:perl
# indent-tabs-mode: nil
# tab-width: 4
# End:
