mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-04 05:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			431 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			431 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/env perl
 | 
						|
## IBM(c) 20013 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
#
 | 
						|
# This plugin is used to handle the command requests for cumulus OS support
 | 
						|
#
 | 
						|
 | 
						|
 | 
						|
package xCAT_plugin::onie;
 | 
						|
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
 | 
						|
use strict;
 | 
						|
use Getopt::Long;
 | 
						|
use Expect;
 | 
						|
use File::Path;
 | 
						|
use File::Basename;
 | 
						|
use File::Copy "cp";
 | 
						|
 | 
						|
use xCAT::Utils;
 | 
						|
use xCAT::MsgUtils;
 | 
						|
use xCAT::TableUtils;
 | 
						|
use xCAT::SvrUtils;
 | 
						|
use xCAT::Table;
 | 
						|
my $xcatdebugmode = 0;
 | 
						|
$::VERBOSE        = 0;
 | 
						|
 | 
						|
sub handled_commands {
 | 
						|
    return {
 | 
						|
        nodeset => 'nodehm:mgt=switch',
 | 
						|
        copydata => 'onie',
 | 
						|
        rspconfig => 'switches:switchtype',
 | 
						|
      }
 | 
						|
}
 | 
						|
 | 
						|
my $CALLBACK;                       # used to hanel the output from xdsh
 | 
						|
 | 
						|
sub preprocess_request {
 | 
						|
    my $request  = shift;
 | 
						|
    my $callback = shift;
 | 
						|
 | 
						|
    if ($request->{command}->[0] eq 'copydata')
 | 
						|
    {
 | 
						|
        return [$request];
 | 
						|
    }
 | 
						|
 | 
						|
    # if already preprocessed, go straight to request
 | 
						|
    if ((defined($request->{_xcatpreprocessed}->[0]))
 | 
						|
        && ($request->{_xcatpreprocessed}->[0] == 1)) {
 | 
						|
        return [$request];
 | 
						|
    }
 | 
						|
 | 
						|
    my $nodes     = $request->{node};
 | 
						|
    my $command   = $request->{command}->[0];
 | 
						|
    my $extraargs = $request->{arg};
 | 
						|
 | 
						|
    if ($extraargs) {
 | 
						|
        @ARGV = @{$extraargs};
 | 
						|
        my ($verbose, $help, $ver);
 | 
						|
        GetOptions("V" => \$verbose, 'h|help' => \$help, 'v|version' => \$ver);
 | 
						|
        if ($help) {
 | 
						|
            my $usage_string = xCAT::Usage->getUsage($command);
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, $usage_string;
 | 
						|
            xCAT::MsgUtils->message("I", $rsp, $callback);
 | 
						|
            return ();
 | 
						|
        }
 | 
						|
        if ($ver) {
 | 
						|
            my $ver_string = xCAT::Usage->getVersion($command);
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, $ver_string;
 | 
						|
            xCAT::MsgUtils->message("I", $rsp, $callback);
 | 
						|
            return ();
 | 
						|
        }
 | 
						|
 | 
						|
        if ($verbose) {
 | 
						|
            $::VERBOSE = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return [$request];
 | 
						|
}
 | 
						|
 | 
						|
sub process_request {
 | 
						|
    my $request  = shift;
 | 
						|
    my $callback = shift;
 | 
						|
    my $subreq   = shift;
 | 
						|
 | 
						|
    my $nodes   = $request->{node};
 | 
						|
    my $command = $request->{command}->[0];
 | 
						|
    my $args    = $request->{arg};
 | 
						|
    my @exargs    = ($request->{arg});
 | 
						|
    if (ref($args)) {
 | 
						|
        @exargs = @$args;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    if ($::XCATSITEVALS{xcatdebugmode} != 0) { $::VERBOSE = 1}
 | 
						|
 | 
						|
    if ($command eq "copydata") {
 | 
						|
        copydata($request, $callback);
 | 
						|
    } elsif ($command eq "nodeset") {
 | 
						|
        nodeset($request, $callback, $subreq);
 | 
						|
    } elsif ($command eq "rspconfig") {
 | 
						|
        my $subcmd = $exargs[0];
 | 
						|
        if ($subcmd eq 'sshcfg') {
 | 
						|
            process_sshcfg($nodes, $callback);
 | 
						|
        } else {
 | 
						|
            xCAT::MsgUtils->message("I", { data => ["The rspconfig command $subcmd is not supported"] }, $callback);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
# build cumulus OS image
 | 
						|
sub copydata {
 | 
						|
    my $request  = shift;
 | 
						|
    my $callback = shift;
 | 
						|
    my $file;
 | 
						|
    my $inspection   = undef;
 | 
						|
    my $noosimage    = undef;
 | 
						|
    my $nooverwrite = undef;
 | 
						|
 | 
						|
    # get arguments
 | 
						|
    my $args = $request->{arg};
 | 
						|
    if ($args) {
 | 
						|
        @ARGV = @{$args};
 | 
						|
        GetOptions(
 | 
						|
            'w'   => \$nooverwrite,
 | 
						|
            'o'   => \$noosimage,
 | 
						|
            'i'   => \$inspection,
 | 
						|
            'f=s' => \$file
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    if (!(-x $file)) {
 | 
						|
        xCAT::MsgUtils->message("E", { error => ["$file is not executable, will not process"], errorcode => ["1"] }, $callback);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    #get install dir
 | 
						|
    my $installroot = "/install";
 | 
						|
    my $sitetab     = xCAT::Table->new('site');
 | 
						|
    my @ents     = xCAT::TableUtils->get_site_attribute("installdir");
 | 
						|
    my $site_ent = $ents[0];
 | 
						|
    if (defined($site_ent))
 | 
						|
    {
 | 
						|
        $installroot = $site_ent;
 | 
						|
    }
 | 
						|
 | 
						|
    my $arch;
 | 
						|
    my $desc;
 | 
						|
    my $release;
 | 
						|
    my $osname;
 | 
						|
    my $filename = basename($file);
 | 
						|
    my $output = `$file`;
 | 
						|
    if ($inspection) {
 | 
						|
        $callback->({ data => "file output: $output" });
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    foreach my $line (split /[\r\n]+/, $output) {
 | 
						|
        if ($line =~ /^Architecture/) {
 | 
						|
            ($desc, $arch) = split /: /, $line ;
 | 
						|
            chomp $arch;
 | 
						|
        }
 | 
						|
        if ($line =~ /^Release/) {
 | 
						|
            ($desc, $release) = split /: /, $line ;
 | 
						|
            chomp $release;
 | 
						|
        }
 | 
						|
        if ($line =~ /cumulus/) {
 | 
						|
            $osname = "cumulus" ;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    unless ($osname) {
 | 
						|
        $osname="image";
 | 
						|
    }
 | 
						|
 | 
						|
    my $distname = $osname . $release;
 | 
						|
    my $imagename = $distname . "-" . $arch;
 | 
						|
    my $defaultpath = "$installroot/$distname/$arch";
 | 
						|
 | 
						|
    #check if file exists
 | 
						|
    if ( (-e "$defaultpath/$filename") && ($nooverwrite)){
 | 
						|
        chmod 0755, "$defaultpath/$filename";
 | 
						|
        $callback->({ data => "$defaultpath/$filename is already exists, will not overwrite" });
 | 
						|
    } else {
 | 
						|
        $callback->({ data => "Copying media to $defaultpath" });
 | 
						|
        mkpath ("$defaultpath");
 | 
						|
        cp "$file", "$defaultpath";
 | 
						|
        chmod 0755, "$defaultpath/$filename"; 	
 | 
						|
        $callback->({ data => "Media copy operation successful" });
 | 
						|
    }
 | 
						|
 | 
						|
    if ($noosimage) {
 | 
						|
        $callback->({ data => "Option noosimage is specified, will not create osimage definition" });
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    # generate the image objects
 | 
						|
    my $oitab = xCAT::Table->new('osimage');
 | 
						|
    unless ($oitab) {
 | 
						|
        xCAT::MsgUtils->message("E", { error => ["Error: Cannot open table osimage."], errorcode => ["1"] }, $callback);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    my $litab = xCAT::Table->new('linuximage');
 | 
						|
    unless ($litab) {
 | 
						|
        xCAT::MsgUtils->message("E", { error => ["Error: Cannot open table linuximage."], errorcode => ["1"] }, $callback);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    my $pkgdir = "$defaultpath/$filename";
 | 
						|
    my $imgdir = $litab->getAttribs({ 'imagename' => $imagename }, 'pkgdir');
 | 
						|
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        $callback->({ data => "creating image $imagename with osarch=$arch, osvers=$distname" });
 | 
						|
    }
 | 
						|
 | 
						|
    my %values;
 | 
						|
    $values{'imagetype'}   = "linux";
 | 
						|
    $values{'provmethod'}  = "install";
 | 
						|
    $values{'description'} = "Cumulus Linux";
 | 
						|
    $values{'osname'}      = "$osname";
 | 
						|
    $values{'osvers'}      = "$distname";
 | 
						|
    $values{'osarch'}      = "$arch";
 | 
						|
 | 
						|
    $oitab->setAttribs({ 'imagename' => $imagename }, \%values);
 | 
						|
 | 
						|
    # set a default package list
 | 
						|
    $litab->setAttribs({ 'imagename' => $imagename }, { 'pkgdir' => $pkgdir });
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        $callback->({ data => "setting pkgdir=$pkgdir for image $imagename" });
 | 
						|
    }
 | 
						|
 | 
						|
    #Need to update osdistro table?
 | 
						|
    my @ret = xCAT::SvrUtils->update_osdistro_table($distname, $arch, $defaultpath, $imagename);
 | 
						|
    if ($ret[0] != 0) {
 | 
						|
        xCAT::MsgUtils->message("E", { error => ["Error when updating the osdistro tables."], errorcode => ["1"] }, $callback);
 | 
						|
    }
 | 
						|
 | 
						|
    xCAT::MsgUtils->message("I", { data => ["The image $imagename is created."] }, $callback);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
# run the nodeset to updatenode provmethod
 | 
						|
sub nodeset {
 | 
						|
    my $request  = shift;
 | 
						|
    my $callback = shift;
 | 
						|
    my $subreq   = shift;
 | 
						|
 | 
						|
    my $switches = $request->{'node'};
 | 
						|
    my $args  = $request->{arg};
 | 
						|
    my $provmethod;
 | 
						|
    my $image_pkgdir;
 | 
						|
 | 
						|
    my $setosimg;
 | 
						|
    foreach (@$args) {
 | 
						|
        if (/osimage=(.*)/) {
 | 
						|
            $setosimg = $1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    my $switchestab = xCAT::Table->new('switches');
 | 
						|
    my $switcheshash = $switchestab->getNodesAttribs($switches, ['switchtype']);
 | 
						|
 | 
						|
    my $nodetab  = xCAT::Table->new('nodetype');
 | 
						|
    my $nodehash = $nodetab->getNodesAttribs($switches, [ 'provmethod' ]);
 | 
						|
 | 
						|
    foreach my $switch (@$switches) {
 | 
						|
        if ($switcheshash->{$switch}->[0]->{switchtype} ne "onie") {
 | 
						|
            xCAT::MsgUtils->message("E", { error => ["nodeset command is not processed for $switch, only supports switchtype=onie"], errorcode => ["1"] }, $callback);
 | 
						|
            next;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        if ($setosimg) {
 | 
						|
            $provmethod = $setosimg;
 | 
						|
        } else {
 | 
						|
            $provmethod = $nodehash->{$switch}->[0]->{provmethod};
 | 
						|
        }
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            if (!defined($::DISABLENODESETWARNING)) { # set by AAsn.pm
 | 
						|
                xCAT::MsgUtils->message("I", { data => [ "$switch has provmethod=$provmethod" ] }, $callback);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        #get pkgdir from osimage
 | 
						|
        my $linuximagetab = xCAT::Table->new('linuximage');
 | 
						|
        my $osimagetab = xCAT::Table->new('osimage');
 | 
						|
        my $imagetab = $linuximagetab->getAttribs({ imagename => $provmethod },'pkgdir');
 | 
						|
        my $osimghash = $osimagetab->getAttribs({ imagename => $provmethod },'osvers','osarch');
 | 
						|
        unless($imagetab and $osimghash){
 | 
						|
            if (!defined($::DISABLENODESETWARNING)) {    # set by AAsn.pm
 | 
						|
                xCAT::MsgUtils->message("E", { error => ["cannot find osimage \"$provmethod\" for $switch, please make sure the osimage specified in command line or node.provmethod exists!"], errorcode => ["1"] }, $callback);
 | 
						|
            }
 | 
						|
            next;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        my %attribs=('provmethod' => $provmethod,'os'=>$osimghash->{'osvers'},'arch'=>$osimghash->{'osarch'} );
 | 
						|
        $nodetab->setAttribs({ 'node' => $switch }, \%attribs);
 | 
						|
        $image_pkgdir = $imagetab->{'pkgdir'};
 | 
						|
 | 
						|
        #validate the image pkgdir
 | 
						|
        my $flag=0;
 | 
						|
        if (-r $image_pkgdir) {
 | 
						|
            my @filestat = `file $image_pkgdir`;
 | 
						|
            if (grep /$image_pkgdir: data/, @filestat) {
 | 
						|
                $flag=1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        unless ($flag) {
 | 
						|
            xCAT::MsgUtils->message("E", { error => ["The image '$image_pkgdir' is invalid"], errorcode => ["1"] }, $callback);
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            xCAT::MsgUtils->message("I", { data => ["osimage=$provmethod, pkgdir=$image_pkgdir"] }, $callback);
 | 
						|
        }
 | 
						|
 | 
						|
        #updateing DHCP entries
 | 
						|
        my $ret = xCAT::Utils->runxcmd({ command => ["makedhcp"], node => [$switch] }, $subreq, 0, 1);
 | 
						|
        if ($::RUNCMD_RC) {
 | 
						|
            xCAT::MsgUtils->message("E", { error => ["Failed to run 'makedhcp' command"], errorcode => ["$::RUNCMD_RC"] }, $callback);
 | 
						|
        }
 | 
						|
 | 
						|
        xCAT::MsgUtils->message("I", { data => ["$switch: install $provmethod"] }, $callback);
 | 
						|
    }
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub process_sshcfg {
 | 
						|
    my $noderange = shift;
 | 
						|
    my $callback = shift;
 | 
						|
 | 
						|
    my $password = "CumulusLinux!";
 | 
						|
    my $userid = "cumulus";
 | 
						|
    my $timeout = 30;
 | 
						|
    my $keyfile = "/root/.ssh/id_rsa.pub";
 | 
						|
    my $rootkey = `cat /root/.ssh/id_rsa.pub`;
 | 
						|
 | 
						|
    foreach my $switch (@$noderange) {
 | 
						|
        my $ip = xCAT::NetworkUtils->getipaddr($switch);
 | 
						|
 | 
						|
        #remove old host key from /root/.ssh/known_hosts
 | 
						|
        my $cmd = "ssh-keygen -R $switch";
 | 
						|
        xCAT::Utils->runcmd($cmd, 0);
 | 
						|
        $cmd = "ssh-keygen -R $ip";
 | 
						|
        xCAT::Utils->runcmd($cmd, 0);
 | 
						|
 | 
						|
        my ($exp, $errstr) = cumulus_connect($ip, $userid, $password, $timeout);
 | 
						|
        if (!defined $exp) {
 | 
						|
            xCAT::MsgUtils->message("E", { data => ["Failed to connect to $switch"] }, $callback);
 | 
						|
            next;
 | 
						|
        }
 | 
						|
 | 
						|
        my $ret;
 | 
						|
        my $err;
 | 
						|
 | 
						|
        ($ret, $err) = cumulus_exec($exp, "mkdir -p /root/.ssh");
 | 
						|
        ($ret, $err) = cumulus_exec($exp, "chmod 700 /root/.ssh");
 | 
						|
        ($ret, $err) = cumulus_exec($exp, "echo \"$rootkey\" >/root/.ssh/authorized_keys");
 | 
						|
        ($ret, $err) = cumulus_exec($exp, "chmod 644 /root/.ssh/authorized_keys");
 | 
						|
        if (!defined $ret) {
 | 
						|
            xCAT::MsgUtils->message("E", { data => ["Failed to run command on $switch"] }, $callback);
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        xCAT::MsgUtils->message("I", { data => ["$switch: SSH enabled"] }, $callback);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub cumulus_connect {
 | 
						|
     my $server   = shift;
 | 
						|
     my $userid   = shift;
 | 
						|
     my $password = shift;
 | 
						|
     my $timeout  = shift;
 | 
						|
 | 
						|
     my $ssh      = Expect->new;
 | 
						|
     my $command     = 'ssh';
 | 
						|
     my @parameters  = ($userid . "@" . $server);
 | 
						|
 | 
						|
     $ssh->debug(0);
 | 
						|
     $ssh->log_stdout(0);    # suppress stdout output..
 | 
						|
     $ssh->slave->stty(qw(sane -echo));
 | 
						|
 | 
						|
     unless ($ssh->spawn($command, @parameters))
 | 
						|
     {
 | 
						|
         my $err = $!;
 | 
						|
         $ssh->soft_close();
 | 
						|
         my $rsp;
 | 
						|
         return(undef, "unable to run command $command $err\n");
 | 
						|
     }
 | 
						|
 | 
						|
     $ssh->expect($timeout,
 | 
						|
                   [ "-re", qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED/, sub {die "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"; } ],
 | 
						|
                   [ "-re", qr/\(yes\/no\)\?\s*$/, sub { $ssh->send("yes\n");  exp_continue; } ],
 | 
						|
                   [ "-re", qr/ password:/,        sub {$ssh->send("$password\n"); exp_continue; } ],
 | 
						|
                   [ "-re", qr/:~\$/,              sub { $ssh->send("sudo su\n"); exp_continue; } ],
 | 
						|
                   [ "-re", qr/ password for cumulus:/, sub { $ssh->send("$password\n"); exp_continue; } ],
 | 
						|
                   [ "-re", qr/.*\/home\/cumulus#/, sub { $ssh->clear_accum(); } ],
 | 
						|
                   [ timeout => sub { die "No login.\n"; } ]
 | 
						|
                  );
 | 
						|
     $ssh->clear_accum();
 | 
						|
     return ($ssh);
 | 
						|
}
 | 
						|
 | 
						|
sub cumulus_exec {
 | 
						|
     my $exp = shift;
 | 
						|
     my $cmd = shift;
 | 
						|
     my $timeout    = shift;
 | 
						|
     my $prompt =  shift;
 | 
						|
 | 
						|
     $timeout = 10 unless defined $timeout;
 | 
						|
     $prompt = qr/.*\/home\/cumulus#/ unless defined $prompt;
 | 
						|
 | 
						|
 | 
						|
     $exp->clear_accum();
 | 
						|
     $exp->send("$cmd\n");
 | 
						|
     my ($mpos, $merr, $mstr, $mbmatch, $mamatch) = $exp->expect(6,  "-re", $prompt);
 | 
						|
 | 
						|
     if (defined $merr) {
 | 
						|
         return(undef,$merr);
 | 
						|
     }
 | 
						|
     return($mbmatch);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
1;
 |