# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::partimageng;
use Storable qw(dclone);
use Sys::Syslog;
use Thread qw(yield);
use POSIX qw(WNOHANG nice);
use xCAT::Table;
use xCAT::Utils;
use xCAT::MsgUtils;
use xCAT::Yum;
use xCAT::Template;
use xCAT::Postage;
use Data::Dumper;
use Getopt::Long;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
use File::Path;
use File::Copy;
my $cpiopid;

sub handled_commands
{
    return {
            mkinstall => "nodetype:os=image",
            mkimage => "nodetype:os=image",
            };
}

sub preprocess_request
{
    my $req      = shift;
    my $callback = shift;
    if ($req->{command}->[0] eq 'copycd')
    {    #don't farm out copycd
        return [$req];
    }

    my $stab = xCAT::Table->new('site');
    my $sent;
    ($sent) = $stab->getAttribs({key => 'sharedtftp'}, 'value');
    unless (    $sent
            and defined($sent->{value})
            and ($sent->{value} =~ /no/i or $sent->{value} =~ /0/))
    {

        #unless requesting no sharedtftp, don't make hierarchical call
        return [$req];
    }

    my %localnodehash;
    my %dispatchhash;
    my $nrtab = xCAT::Table->new('noderes');
    foreach my $node (@{$req->{node}})
    {
        my $nodeserver;
        my $tent = $nrtab->getNodeAttribs($node, ['tftpserver']);
        if ($tent) { $nodeserver = $tent->{tftpserver} }
        unless ($tent and $tent->{tftpserver})
        {
            $tent = $nrtab->getNodeAttribs($node, ['servicenode']);
            if ($tent) { $nodeserver = $tent->{servicenode} }
        }
        if ($nodeserver)
        {
            $dispatchhash{$nodeserver}->{$node} = 1;
        }
        else
        {
            $localnodehash{$node} = 1;
        }
    }
    my @requests;
    my $reqc = {%$req};
    $reqc->{node} = [keys %localnodehash];
    if (scalar(@{$reqc->{node}})) { push @requests, $reqc }

    foreach my $dtarg (keys %dispatchhash)
    {    #iterate dispatch targets
        my $reqcopy = {%$req};    #deep copy
        $reqcopy->{'_xcatdest'} = $dtarg;
        $reqcopy->{node} = [keys %{$dispatchhash{$dtarg}}];
        push @requests, $reqcopy;
    }
    return \@requests;
}

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

    if ($request->{command}->[0] eq 'mkinstall')
    {
		 return mkinstall($request, $callback, $doreq);
	 }
    elsif ($request->{command}->[0] eq 'mkimage')
    {
		 return mkimage($request, $callback, $doreq);
	 }
}

sub mkinstall
{
    my $request  = shift;
    my $callback = shift;
    my $doreq    = shift;
    my @nodes    = @{$request->{node}};
    my $installroot;
    $installroot = "/install";

    my $node;
    my $ostab = xCAT::Table->new('nodetype');
    foreach $node (@nodes)
    {
        my $osinst;
        my $ent = $ostab->getNodeAttribs($node, ['profile', 'os', 'arch']);
        my @missingparms;
        unless ($ent->{os}) {
            push @missingparms,"nodetype.os";
        }
        unless ($ent->{arch}) {
            push @missingparms,"nodetype.arch";
        }
        unless ($ent->{profile}) {
            push @missingparms,"nodetype.profile";
        }
        unless ($ent->{os} and $ent->{arch} and $ent->{profile})
        {
            $callback->(
                        {
                         error => ["Missing ".join(',',@missingparms)." for $node"],
                         errorcode => [1]
                        }
                        );
            next;    #No profile
        }
        my $os      = $ent->{os};
        my $arch    = $ent->{arch};
        my $profile = $ent->{profile};
	# check for /install/image/$arch/$profile
        unless (-r "$installroot/image/$arch/$profile.img" or
                -r "$installroot/image/$arch/$profile.gz" or
                -r "$installroot/image/$arch/$profile-parta1.gz")
        {
            $callback->(
                        {
                         error => [
                                   "No image file found for $profile in $installroot/image/$arch"
                         ],
                         errorcode => [1]
                        }
                        );
            next;
        }

        #Call the Template class to do substitution to produce a kickstart file in the autoinst dir
        my $tmperr =
          "Unable to find template in $::XCATROOT/share/xcat/install/image (for $profile/$os/$arch combination)";
        if (-r $::XCATROOT
            . "/share/xcat/install/image/$profile.$arch.tmpl")
        {
            $tmperr =
              xCAT::Template->subvars(
                    $::XCATROOT
                      . "/share/xcat/install/image/$profile.$arch.tmpl",
                    "$installroot/autoinst/" . $node,
                    $node
                    );
        }
        elsif (-r $::XCATROOT
               . "/share/xcat/install/image/$profile.tmpl")
        {
            $tmperr =
              xCAT::Template->subvars(
                 $::XCATROOT
                   . "/share/xcat/install/image/$profile.tmpl",
                 "$installroot/autoinst/" . $node,
                 $node
                 );
        }
        elsif (-r $::XCATROOT
               . "/share/xcat/install/image/default.tmpl")
        {
            $tmperr =
              xCAT::Template->subvars(
                 $::XCATROOT
                   . "/share/xcat/install/image/default.tmpl",
                 "$installroot/autoinst/" . $node,
                 $node
                 );
        }
        if ($tmperr)
        {
            $callback->(
                    {
                     node =>
                       [{name => [$node], error => [$tmperr], errorcode => [1]}]
                    }
                    );
            next;
        }

        # copy partimageng script to the node-specific post scripts area
        #mkpath "$installroot/postscripts/$node";
        #xCAT::Postage->writescript($node,"/install/postscripts/".$node, "install", $callback);
        #copy("$installroot/postscripts/partimageng",
        #     "/$installroot/postscripts/$node/");

        if (
                 -r "/tftpboot/xcat/image/$arch/vmlinuz"
             and -r "/tftpboot/xcat/image/$arch/initrd.img"
             and -r "$installroot/image/$arch/installer_files/rootimg.gz"
          )
        {

            #We have a shot...
            my $restab = xCAT::Table->new('noderes');
            my $bptab  = xCAT::Table->new('bootparams',-create=>1);
            my $hmtab  = xCAT::Table->new('nodehm');
            my $ent    =
              $restab->getNodeAttribs($node,
                                     ['nfsserver', 'primarynic', 'installnic', 'kcmdline']);
            my $sent =
              $hmtab->getNodeAttribs(
                                     $node,
                                     [
                                      'serialport', 'serialspeed', 'serialflow'
                                     ]
                                     );
            unless ($ent and $ent->{nfsserver})
            {
                $callback->(
                        {
                         error => ["No noderes.nfsserver defined for " . $node],
                         errorcode => [1]
                        }
                        );
                next;
            }


            # determine image server, if tftpserver use it, else use xcatmaster
            # else use site.Master, last resort use self
            my $imgsrv;
            my $ient;
            $ient = $restab->getNodeAttribs($node, ['tftpserver']);
            if ($ient and $ient->{tftpserver})
            {
                $imgsrv = $ient->{tftpserver};
            }
            else
            {
                $ient = $restab->getNodeAttribs($node, ['xcatmaster']);
                if ($ient and $ient->{xcatmaster})
                {
                    $imgsrv = $ient->{xcatmaster};
                }
                else
                {
                    $ient = $sitetab->getAttribs({key => master}, value);
                    if ($ient and $ient->{value})
                    {
                        $imgsrv = $ient->{value};
                    }
                    else
                    {
                        my $ipfn = xCAT::Utils->my_ip_facing($node);
                        if ($ipfn)
                        {
                            $imgsrv = $ipfn;    #guessing self is second best

                        }
                    }
                }
            }
            unless ($imgsrv)
            {
                $callback->(
                    {
                     error => [
                         "Unable to determine or reasonably guess the image server for $node"
                     ],
                     errorcode => [1]
                    }
                    );
                next;
            }

            my $kcmdline = "imgurl=http://$imgsrv/$installroot/image/$arch/installer_files/rootimg.gz image=$installroot/image/$arch/$profile.img blocks=512 action=restore reboot " . $ent->{kcmdline};

            if (defined($sent->{serialport}))
            {
                unless ($sent->{serialspeed})
                {
                    $callback->(
                        {
                         error => [
                             "serialport defined, but no serialspeed for $node in nodehm table"
                         ],
                         errorcode => [1]
                        }
                        );
                    next;
                }
                $kcmdline .=
                    " console=ttyS"
                  . $sent->{serialport} . ","
                  . $sent->{serialspeed};
                if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/)
                {
                    $kcmdline .= "n8r";
                }
            }
            $kcmdline .= " noipv6";

            $bptab->setNodeAttribs(
                                   $node,
                                   {
                                    kernel   => "xcat/image/$arch/vmlinuz",
                                    initrd   => "xcat/image/$arch/initrd.img",
                                    kcmdline => $kcmdline
                                   }
                                   );
        }
        else
        {
            $callback->(
                    {
                     error => ["vmlinuz and initrd.img not found at /tftpboot/xcat/image/$arch"],
                     errorcode => [1]
                    }
                    );
        }
    }
    #my $rc = xCAT::Utils->create_postscripts_tar();
    #if ($rc != 0)
    #{
    #    xCAT::MsgUtils->message("S", "Error creating postscripts tar file.");
    #}
}

sub mkimage
{
    my $request  = shift;
    my $callback = shift;
    my $doreq    = shift;
    my @nodes    = @{$request->{node}};
    my $installroot;
    $installroot = "/install";

    my $node;
    my $ostab = xCAT::Table->new('nodetype');
    foreach $node (@nodes)
    {
        my $osinst;
        my $ent = $ostab->getNodeAttribs($node, ['profile', 'os', 'arch']);
        my @missingparms;
        unless ($ent->{os}) {
            push @missingparms,"nodetype.os";
        }
        unless ($ent->{arch}) {
            push @missingparms,"nodetype.arch";
        }
        unless ($ent->{profile}) {
            push @missingparms,"nodetype.profile";
        }
        unless ($ent->{os} and $ent->{arch} and $ent->{profile})
        {
            $callback->(
                        {
                         error => ["Missing ".join(',',@missingparms)." for $node"],
                         errorcode => [1]
                        }
                        );
            next;    #No profile
        }
        my $os      = $ent->{os};
        my $arch    = $ent->{arch};
        my $profile = $ent->{profile};
		  # check for /install/image/$arch/$profile
        if (-r "$installroot/image/$arch/$profile.img")
        {
            $callback->(
                        {
                         error => [
                                   "Image file already exists for $profile in $installroot/image/$arch"
                         ],
                         errorcode => [1]
                        }
                        );
            next;
        }

        #Call the Template class to do substitution to produce a kickstart file in the autoinst dir
        my $tmperr =
          "Unable to find template $::XCATROOT/share/xcat/install/image/createimage.$arch.tmpl";
        if (-r $::XCATROOT
            . "/share/xcat/install/image/createimage.$arch.tmpl")
        {
            $tmperr =
              xCAT::Template->subvars(
                    $::XCATROOT
                      . "/share/xcat/install/image/createimage.$arch.tmpl",
                    "$installroot/autoinst/" . $node,
                    $node
                    );
        }
        if ($tmperr)
        {
            $callback->(
                    {
                     node =>
                       [{name => [$node], error => [$tmperr], errorcode => [1]}]
                    }
                    );
            next;
        }

        # copy partimageng script to the node-specific post scripts area
        #mkpath "$installroot/postscripts/$node";
        #xCAT::Postage->writescript($node,"/install/postscripts/".$node, "install", $callback);
        #copy("$installroot/postscripts/partimageng",
        #     "/$installroot/postscripts/$node/");

        if (
                 -r "/tftpboot/xcat/image/$arch/vmlinuz"
             and -r "/tftpboot/xcat/image/$arch/initrd.img"
             and -r "$installroot/image/$arch/installer_files/rootimg.gz"
          )
        {

            #We have a shot...
            my $restab = xCAT::Table->new('noderes');
            my $bptab  = xCAT::Table->new('bootparams',-create=>1);
            my $hmtab  = xCAT::Table->new('nodehm');
            my $ent    =
              $restab->getNodeAttribs($node,
                                     ['nfsserver', 'primarynic', 'installnic', 'kcmdline']);
            my $sent =
              $hmtab->getNodeAttribs(
                                     $node,
                                     [
                                      'serialport', 'serialspeed', 'serialflow'
                                     ]
                                     );
            unless ($ent and $ent->{nfsserver})
            {
                $callback->(
                        {
                         error => ["No noderes.nfsserver defined for " . $node],
                         errorcode => [1]
                        }
                        );
                next;
            }


            # determine image server, if tftpserver use it, else use xcatmaster
            # else use site.Master, last resort use self
            my $imgsrv;
            my $ient;
            $ient = $restab->getNodeAttribs($node, ['tftpserver']);
            if ($ient and $ient->{tftpserver})
            {
                $imgsrv = $ient->{tftpserver};
            }
            else
            {
                $ient = $restab->getNodeAttribs($node, ['xcatmaster']);
                if ($ient and $ient->{xcatmaster})
                {
                    $imgsrv = $ient->{xcatmaster};
                }
                else
                {
                    $ient = $sitetab->getAttribs({key => master}, value);
                    if ($ient and $ient->{value})
                    {
                        $imgsrv = $ient->{value};
                    }
                    else
                    {
                        my $ipfn = xCAT::Utils->my_ip_facing($node);
                        if ($ipfn)
                        {
                            $imgsrv = $ipfn;    #guessing self is second best

                        }
                    }
                }
            }
            unless ($imgsrv)
            {
                $callback->(
                    {
                     error => [
                         "Unable to determine or reasonably guess the image server for $node"
                     ],
                     errorcode => [1]
                    }
                    );
                next;
            }

            my $kcmdline = "imgurl=http://$imgsrv/$installroot/image/$arch/installer_files/rootimg.gz image=$installroot/image/$arch/$profile.img blocks=512 action=save reboot " . $ent->{kcmdline};

            if (defined($sent->{serialport}))
            {
                unless ($sent->{serialspeed})
                {
                    $callback->(
                        {
                         error => [
                             "serialport defined, but no serialspeed for $node in nodehm table"
                         ],
                         errorcode => [1]
                        }
                        );
                    next;
                }
                $kcmdline .=
                    " console=ttyS"
                  . $sent->{serialport} . ","
                  . $sent->{serialspeed};
                if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/)
                {
                    $kcmdline .= "n8r";
                }
            }
            $kcmdline .= " noipv6";

            $bptab->setNodeAttribs(
                                   $node,
                                   {
                                    kernel   => "xcat/image/$arch/vmlinuz",
                                    initrd   => "xcat/image/$arch/initrd.img",
                                    kcmdline => $kcmdline
                                   }
                                   );
        }
        else
        {
            $callback->(
                    {
                     error => ["vmlinuz and initrd.img not found at /tftpboot/xcat/image/$arch"],
                     errorcode => [1]
                    }
                    );
        }
    }
    #my $rc = xCAT::Utils->create_postscripts_tar();
    #if ($rc != 0)
    #{
    #    xCAT::MsgUtils->message("S", "Error creating postscripts tar file.");
    #}
}

1;
