2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00

430 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) || (grep /$image_pkgdir: .* \(binary 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..
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;