# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::boottarget;

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use Storable qw(dclone);
use Sys::Syslog;
use Thread qw(yield);
use POSIX qw(WNOHANG nice);
use xCAT::Table;
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::MsgUtils;
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;



sub handled_commands
{
    return {
        mknetboot => "nodetype:os=(boottarget)|(target)|(bt)",
        mkinstall => "nodetype:os=(boottarget)|(target)|(bt)"
    };
}

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

    #if already preprocessed, go straight to request
    if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }

    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');
    my @entries    = xCAT::TableUtils->get_site_attribute("sharedtftp");
    my $site_entry = $entries[0];
    unless (defined($site_entry)
        and ($site_entry =~ /no/i or $site_entry =~ /0/))
    {

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

    my %localnodehash;
    my %dispatchhash;
    my $nrtab = xCAT::Table->new('noderes');
    my $nrents = $nrtab->getNodesAttribs($req->{node}, [qw(tftpserver servicenode)]);
    foreach my $node (@{ $req->{node} })
    {
        my $nodeserver;
        my $tent = $nrents->{$node}->[0]; #$nrtab->getNodeAttribs($node, ['tftpserver']);
        if ($tent) { $nodeserver = $tent->{tftpserver} }
        unless ($tent and $tent->{tftpserver})
        {
            $tent = $nrents->{$node}->[0]; #$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->{_xcatpreprocessed}->[0] = 1;
        $reqcopy->{node} = [ keys %{ $dispatchhash{$dtarg} } ];
        push @requests, $reqcopy;
    }
    return \@requests;
}

sub process_request
{
    my $request  = shift;
    my $callback = shift;
    my $doreq    = shift;
    my $distname = undef;
    my $arch     = undef;
    my $path     = undef;
    return mknetboot($request, $callback, $doreq);
}

sub mknetboot
{
    my $req      = shift;
    my $callback = shift;
    my $doreq    = shift;
    my $nodes    = @{ $request->{node} };
    my @args     = @{ $req->{arg} };
    my @nodes    = @{ $req->{node} };
    my $ostab    = xCAT::Table->new('nodetype');

    my $installroot = xCAT::TableUtils->getInstallDir();
    my $tftpdir     = xCAT::TableUtils->getTftpDir();

    my %donetftp = ();
    my %oents    = %{ $ostab->getNodesAttribs(\@nodes, [qw(os arch profile)]) };
    my $restab   = xCAT::Table->new('noderes');
    my $bptab    = xCAT::Table->new('bootparams', -create => 1);
    my $hmtab    = xCAT::Table->new('nodehm');
    my $ttab     = xCAT::Table->new('boottarget');

    foreach $node (@nodes)
    {
        my $ent = $oents{$node}->[0]; #ostab->getNodeAttribs($node, ['os', 'arch', 'profile']);
        unless ($ent->{os} and $ent->{profile})
        {
            $callback->(
                {
                    error     => ["Insufficient nodetype entry for $node"],
                    errorcode => [1]
                }
            );
            next;
        }

        my $profile = $ent->{profile};
        ($tent) = $ttab->getAttribs({ 'bprofile' => $profile }, 'kernel', 'initrd', 'kcmdline'); #TODO: coalesce these queries into one Table query, speed it up
        if (!defined($tent)) {
            my $msg = "$profile in nodetype table was not defined in boottarget table";
            $callback->({
                    error     => ["$msg"],
                    errorcode => [1]
            });
        }
        $kernel   = $tent->{kernel};
        $initrd   = $tent->{initrd};
        $kcmdline = $tent->{kcmdline};

        #TODO: big todo, cheap and rapid, but should be more like esx.pm implementation, i.e. more scalable
        while ($kcmdline =~ /#NODEATTRIB:([^:#]+):([^:#]+)#/) {
            my $natab = xCAT::Table->new($1);
            my $naent = $natab->getNodeAttribs($node, [$2]);
            my $naval = $naent->{$2};
            $kcmdline =~ s/#NODEATTRIB:([^:#]+):([^:#]+)#/$naval/;
        }
        while ($kcmdline =~ /#TABLE:([^:#]+):([^:#]+):([^:#]+)#/) {
            my $tabname = $1;
            my $keyname = $2;
            my $colname = $3;
            if ($2 =~ /THISNODE/ or $2 =~ /\$NODE/) {
                my $natab = xCAT::Table->new($tabname);
                my $naent = $natab->getNodeAttribs($node, [$colname]);
                my $naval = $naent->{$colname};
                $kcmdline =~ s/#TABLE:([^:#]+):([^:#]+):([^:#]+)#/$naval/;
            } else {
                my $msg = "Table key of $2 not yet supported by boottarget mini-template";
                $callback->({
                        error     => ["$msg"],
                        errorcode => [1]
                });

            }
        }

        if ($initrd eq '') {
            $bptab->setNodeAttribs(
                $node,
                {
                    kernel   => $kernel,
                    initrd   => '',
                    kcmdline => $kcmdline
                }
            );
        } else {
            $bptab->setNodeAttribs(
                $node,
                {
                    kernel   => $kernel,
                    initrd   => $initrd,
                    kcmdline => $kcmdline
                }
            );
        }
    }

}

1;
