# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::geninitrd;
BEGIN
{
  $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}

use strict;
use lib "$::XCATROOT/lib/perl";
use File::Path;
use File::Copy;
use xCAT::MsgUtils;
use xCAT::TableUtils;
use xCAT::Table;
use xCAT::Scope;

sub handled_commands
{
    return {
        geninitrd => "geninitrd",
    }
}

sub preprocess_request
{
    my $req = shift;
    my $callback = shift;

    my $usage = sub {
        my $callback = shift;
        xCAT::MsgUtils->message("I", {data=>["Usage: geninitrd <imagename> [--ignorekernelchk] [-h | --help]"]}, $callback);
    };

    my $osimage;
    my $ignorekernelchk;
    if (defined ($req->{arg})) {
        foreach (@{$req->{arg}}) {
            if (/^-/) {
                if (/--ignorekernelchk/) {
                    $ignorekernelchk = 1;
                } else {
                    $usage->($callback);
                    return;
                }
            }else {
                $osimage = $_;
            }
        }
    } else {
        $usage->($callback);
        return;
    }

    unless ($osimage) {
        $usage->($callback);
        return;
    }
 
    $req->{'ignorekernelchk'} = [$ignorekernelchk];

    #if tftpshared is not set, dispatch this command to all the service nodes
    my @entries =  xCAT::TableUtils->get_site_attribute("sharedtftp");
    my $t_entry = $entries[0];
    if ( defined($t_entry) and ($t_entry == 0 or $t_entry =~ /no/i)) {
        $req->{'_disparatetftp'} = [1];
        return xCAT::Scope->get_broadcast_scope($req,@_);
    }
   return [$req];
}


sub process_request
{
    my $req  = shift;
    my $callback = shift;
    my $doreq = shift;

    if ($req->{command}->[0] eq 'geninitrd')
    {
        return geninitrd($req, $callback, $doreq);
    }

}

sub geninitrd {
    my $req  = shift;
    my $callback = shift;
    my $doreq = shift;

    my $osimage = $req->{arg}->[0];
    my $ignorekernelchk;
    if (defined ($req->{'ignorekernelchk'}) && $req->{'ignorekernelchk'}->[0]) {
        $ignorekernelchk = $req->{'ignorekernelchk'}->[0];
    }

    my ($osvers, $arch, $pkgdir, $driverupdatesrc, $netdrivers, $osdisupdir);

    # get attributes from osimage table
    my $osimagetab=xCAT::Table->new('osimage');
    unless ($osimagetab) {
        xCAT::MsgUtils->message("E", {error=>["Cannot open the table osimage."], errorcode=>["1"]}, $callback);
        return;
    }

    my $oient = $osimagetab->getAttribs({imagename => $osimage}, 'provmethod', 'osvers', 'osarch', 'osupdatename');
    unless ($oient && $oient->{'osvers'} && $oient->{'osarch'} ) {
        xCAT::MsgUtils->message("E", {error=>["The osimage [$osimage] was not defined or [osvers, osarch] attributes were not set."], errorcode=>["1"]}, $callback);
        return;
    }
    $osvers = $oient->{'osvers'};
    $arch = $oient->{'osarch'};

    # get attributes from linuximage table
    my $linuximagetab=xCAT::Table->new('linuximage');
    unless ($linuximagetab) {
        xCAT::MsgUtils->message("E", {error=>["Cannot open the table linuximage."], errorcode=>["1"]}, $callback);
        return;
    }

    my $lient = $linuximagetab->getAttribs({imagename => $osimage}, 'rootimgdir', 'pkgdir',  'driverupdatesrc',  'netdrivers');
    unless ($lient && $lient->{'pkgdir'}) {
        xCAT::MsgUtils->message("E", {error=>["The osimage [$osimage] was not defined or [pkgdir] attribute was not set."], errorcode=>["1"]}, $callback);
        return;
    }
    $pkgdir = $lient->{'pkgdir'};
    $driverupdatesrc = $lient->{'driverupdatesrc'};
    $netdrivers = $lient->{'netdrivers'};

    # if the provmethod equals 'netboot', call the genimage --onlyinitrd directly
    if ($oient->{'provmethod'} && ($oient->{'provmethod'} eq "netboot" || $oient->{'provmethod'} eq "statelite")) {
        if ($lient->{'rootimgdir'}) {
            unless (-d $lient->{'rootimgdir'}."/rootimg/lib/modules") {
                xCAT::MsgUtils->message("E", {error=>["The genimage should be run before running geninitrd."], errorcode=>["1"]}, $callback);
                return;
            }
        } else {
            xCAT::MsgUtils->message("E", {error=>["The rootimgdir attribute for the osimage should be set."], errorcode=>["1"]}, $callback);
            return;
        }
        my @output = `genimage $osimage --onlyinitrd`;
        xCAT::MsgUtils->message("I", {data=>\@output}, $callback);
        #$doreq->({ command => ['genimage'],
        #              arg => [$osimage, '--onlyinitrd'] }, $callback);
        return;
    } elsif (!$oient->{'provmethod'} || $oient->{'provmethod'} ne "install") {
        xCAT::MsgUtils->message("E", {error=>["The attribute [provmethod] for osimage [$osimage] must be set to install, netboot or statelite."], errorcode=>["1"]}, $callback);
        return;
    }

    # get the path list of the osdistroupdate
    if ($oient->{'osupdatename'}) {
        my @osupdatenames = split (/,/, $oient->{'osupdatename'});
        
        my $osdistrouptab=xCAT::Table->new('osdistroupdate', -create=>1);
        unless ($osdistrouptab) {
            xCAT::MsgUtils->message("E", { error => ["Cannot open the table osdistroupdate."], errorcode => [1] }, $callback);
            return;
        }

        my @osdup = $osdistrouptab->getAllAttribs("osupdatename", "dirpath");
        foreach my $upname (@osupdatenames) {
            foreach my $upref (@osdup) {
                if ($upref->{'osupdatename'} eq $upname) {
                    $osdisupdir .= ",$upref->{'dirpath'}";
                    last;
                }
            }
        }

        $osdisupdir =~ s/^,//;
    }

    # get the source path of initrd and kernel from the pkgdir of osimage
    # copy the initrd and kernel to /tftpboot
    # pass the path of initrd and kernel in /tftpboot to insertdd 
    my $initrdpath;
    my $kernelpath;
    my $tftpdir = "/tftpboot";
    my @entries =  xCAT::TableUtils->get_site_attribute("$tftpdir");
    my $t_entry = $entries[0];
    if ( defined($t_entry) ) {
        $tftpdir = $t_entry;
    }
    my $tftppath = "$tftpdir/xcat/osimage/$osimage";
    unless (-d $tftppath) {
        mkpath $tftppath;
    }
    if ($arch =~ /x86/) {
        if ($osvers =~ /(^ol[0-9].*)|(centos.*)|(rh.*)|(fedora.*)|(SL.*)/) {
            $kernelpath = "$tftppath/vmlinuz";
            copy("$pkgdir/images/pxeboot/vmlinuz", $kernelpath);
            $initrdpath = "$tftppath/initrd.img";
            copy("$pkgdir/images/pxeboot/initrd.img", $initrdpath);
        } elsif ($osvers =~ /(sles.*)|(suse.*)/) {
            $kernelpath = "$tftppath/linux";
            copy("$pkgdir/1/boot/$arch/loader/linux", $kernelpath);
            $initrdpath = "$tftppath/initrd";
            copy("$pkgdir/1/boot/$arch/loader/initrd", $initrdpath);
        } else {
            xCAT::MsgUtils->message("E", { error => ["unknow osvers [$osvers]."], errorcode => [1] }, $callback);
            return;
        } 
    } elsif ($arch =~ /ppc/) {
        if ($osvers =~ /(^ol[0-9].*)|(centos.*)|(rh.*)|(fedora.*)|(SL.*)/) {
            $kernelpath = "$tftppath/vmlinuz";
            copy("$pkgdir/ppc/ppc64/vmlinuz", $kernelpath);
            if (-r "$pkgdir/ppc/ppc64/ramdisk.image.gz") {
                $initrdpath = "$tftppath/initrd.img";
                copy ("$pkgdir/ppc/ppc64/ramdisk.image.gz", $initrdpath);
            } elsif (-r "$pkgdir/ppc/ppc64/initrd.img") {
                $initrdpath = "$tftppath/initrd.img";
                copy("$pkgdir/ppc/ppc64/initrd.img", $initrdpath);
            }
        } elsif ($osvers =~ /(sles.*)|(suse.*)/) {
            $kernelpath = undef;
            $initrdpath = "$tftppath/inst64";
            copy ("$pkgdir/1/suseboot/inst64", $initrdpath);
        } else {
            xCAT::MsgUtils->message("E", { error => ["unknow osvers [$osvers]."], errorcode => [1] }, $callback);
            return;
        }
    } else {
        xCAT::MsgUtils->message("E", { error => ["unknow arch [$arch]."], errorcode => [1] }, $callback);
        return;
    }

    # call the insert_dd function in the anaconda or sles to hack the initrd that:
    # 1. Get the new kernel from update distro and copy it to /tftpboot
    # 2. Inject the drivers to initrd in /tftpboot base on the new kernel ver
    if ($osvers =~ /(^ol[0-9].*)|(centos.*)|(rh.*)|(fedora.*)|(SL.*)/) {
        require  xCAT_plugin::anaconda;
        xCAT_plugin::anaconda->insert_dd($callback, $osvers, $arch, $initrdpath, $kernelpath, $driverupdatesrc, $netdrivers, $osdisupdir, $ignorekernelchk);
    } elsif ($osvers =~ /(sles.*)|(suse.*)/) {
        require  xCAT_plugin::sles;
        xCAT_plugin::sles->insert_dd($callback, $osvers, $arch, $initrdpath, $kernelpath, $driverupdatesrc, $netdrivers, $osdisupdir, $ignorekernelchk);
    } 

}

1;
