2013-06-30 04:25:58 +00:00
#!/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 Xeon Phi (mic) support
#
package xCAT_plugin::mic ;
BEGIN
{
$ ::XCATROOT = $ ENV { 'XCATROOT' } ? $ ENV { 'XCATROOT' } : '/opt/xcat' ;
}
use strict ;
use Getopt::Long ;
use File::Path ;
use File::Basename ;
use xCAT::Utils ;
use xCAT::MsgUtils ;
use xCAT::TableUtils ;
use xCAT::Table ;
sub handled_commands {
return {
rpower = > 'nodehm:mgt' ,
nodeset = > "nodehm:mgt" , # generate the osimage for mic on the host
rflash = > 'nodehm:mgt' , # update the firmware of mics
rinv = > 'nodehm:mgt' ,
rvitals = > 'nodehm:mgt' ,
copytar = > 'mic' ,
getcons = > 'nodehm:mgt' ,
}
}
my $ CALLBACK ; # used to hanel the output from xdsh
# since the mic is attached to the host node and management of mic needs to be
# done via host node, the host will be used as the target to get the service node
sub preprocess_request {
my $ request = shift ;
my $ callback = shift ;
if ( $ request - > { command } - > [ 0 ] eq 'copytar' )
{
# don't handle copytar (copycds)
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 ( ) ;
}
}
# Get the host for the mic nodes
my % hosts ;
my $ mictab = xCAT::Table - > new ( "mic" ) ;
unless ( $ mictab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the mic table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my $ mictabhash = $ mictab - > getNodesAttribs ( $ nodes , [ 'host' ] ) ;
foreach my $ node ( @$ nodes ) {
if ( ! defined ( $ mictabhash - > { $ node } - > [ 0 ] - > { 'host' } ) ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "The michost attribute was not set for $node" ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
push @ { $ hosts { $ mictabhash - > { $ node } - > [ 0 ] - > { 'host' } } } , $ node ;
}
my @ requests ;
# get the service nodes of hosts instread of mic nodes
my @ hosts = keys ( % hosts ) ;
my $ sn = xCAT::ServiceNodeUtils - > get_ServiceNode ( \ @ hosts , 'xcat' , "MN" ) ;
foreach my $ snkey ( keys %$ sn ) {
my $ reqcopy = { %$ request } ;
my @ nodes4sn ;
foreach ( @ { $ sn - > { $ snkey } } ) {
push @ nodes4sn , @ { $ hosts { $ _ } } ;
}
$ reqcopy - > { node } = \ @ nodes4sn ; # the node attribute will have the real mic nodes
unless ( $ snkey eq '!xcatlocal!' ) {
$ reqcopy - > { '_xcatdest' } = $ snkey ;
}
$ reqcopy - > { _xcatpreprocessed } - > [ 0 ] = 1 ;
push @ requests , $ reqcopy ;
}
return \ @ requests ;
}
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 } ;
# get the mapping between host and mic
my % hosts ;
my $ mictab = xCAT::Table - > new ( "mic" ) ;
unless ( $ mictab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the mic table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my $ mictabhash = $ mictab - > getNodesAttribs ( $ nodes , [ 'host' , 'id' ] ) ;
foreach my $ node ( @$ nodes ) {
$ hosts { $ mictabhash - > { $ node } - > [ 0 ] - > { 'host' } } { $ mictabhash - > { $ node } - > [ 0 ] - > { 'id' } } = $ node ; # $hosts{host}{micid} = micname
$ hosts { $ mictabhash - > { $ node } - > [ 0 ] - > { 'host' } } { 'ids' } . = " $mictabhash->{$node}->[0]->{'id'}" ; # $hosts{host}{ids} = " id0 id1 id2 ..."
}
if ( $ command eq "rvitals" ) {
rinv ( $ request , $ callback , $ subreq , \ % hosts ) ;
} elsif ( $ command eq "rmicctrl" ) {
rmicctrl ( $ callback , $ args ) ;
} elsif ( $ command eq "rinv" ) {
rinv ( $ request , $ callback , $ subreq , \ % hosts ) ;
} elsif ( $ command eq "rpower" ) {
rpower ( $ request , $ callback , $ subreq , \ % hosts ) ;
} elsif ( $ command eq "copytar" ) {
copytar ( $ request , $ callback ) ;
} elsif ( $ command eq "getcons" ) {
getcons ( $ request , $ callback ) ;
} elsif ( $ command eq "rflash" ) {
rflash ( $ request , $ callback , $ subreq , \ % hosts ) ;
} elsif ( $ command eq "nodeset" ) {
nodeset ( $ request , $ callback , $ subreq , \ % hosts ) ;
}
}
# handle the rpower command for the mic node
sub rpower {
my $ request = shift ;
my $ callback = shift ;
my $ subreq = shift ;
my $ host2mic = shift ;
my $ usage_string = "rpower noderange [stat|state|on|off|reset|boot]" ;
my $ args = $ request - > { arg } ;
my ( $ wait , $ timeout , $ action ) ;
if ( $ args ) {
@ ARGV = @ { $ args } ;
GetOptions ( 'w' = > \ $ wait ,
't=s' = > \ $ timeout ) ;
foreach ( @ ARGV ) {
if ( /^stat/ ) {
$ action = "--status" ;
} elsif ( /^on$/ ) {
$ action = "--boot" ;
} elsif ( /^off$/ ) {
$ action = "--shutdown" ;
} elsif ( /^reset$/ ) {
$ action = "--reset" ;
} elsif ( /^boot$/ ) {
$ action = "--reboot" ;
} else {
my $ rsp ;
push @ { $ rsp - > { data } } , $ usage_string ;
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback ) ;
return ;
}
}
} else {
my $ rsp ;
push @ { $ rsp - > { data } } , $ usage_string ;
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback ) ;
return ;
}
# get all the hosts
# use the id list as the key to classify the host, since the handle the host against
# same mic list will be run in a batch
my % hostclass ;
foreach ( keys %$ host2mic ) {
push @ { $ hostclass { $ host2mic - > { $ _ } { 'ids' } } } , $ _ ;
}
foreach ( keys % hostclass ) {
my $ idlist = $ _ ;
my @ hosts = @ { $ hostclass { $ idlist } } ;
my $ miclist = $ idlist ; # currently ,it's " 0 1 2 .."
$ miclist =~ s/(\d+)/mic$1/g ; # then, it should be "mic0 mic1 mic2 ..."
my @ cmd = ( "/usr/sbin/micctrl" , $ action , $ miclist ) ;
if ( $ wait ) {
if ( $ timeout ) {
push @ cmd , ( " --wait" , "--timeout=$timeout" ) ;
} else {
push @ cmd , " --wait" ;
}
}
my $ output = xCAT::Utils - > runxcmd ( { command = > [ 'xdsh' ] ,
node = > \ @ hosts ,
arg = > \ @ cmd } , $ subreq , 0 , 1 ) ;
# parse the output
# replace the mic name from the host like mic0:xxx, mic1:xxx to the real mic name
# which defined in xCAT
foreach ( @$ output ) {
foreach my $ line ( split /\n/ , $ _ ) {
if ( $ line =~ /([^:]*):\s*([^:]*):(.*)/ ) {
my $ host = $ 1 ;
my $ mic = $ 2 ;
my $ msg = $ 3 ;
chomp ( $ host ) ;
chomp ( $ mic ) ;
chomp ( $ msg ) ;
if ( $ mic =~ /^mic\d+/ ) {
my $ micid = $ mic ;
$ micid =~ s/[^\d]*//g ;
my $ micname = $ host2mic - > { $ host } { $ micid } ;
xCAT::MsgUtils - > message ( "I" , { data = > [ "$micname: $msg" ] } , $ callback ) ;
} else {
xCAT::MsgUtils - > message ( "E" , { data = > [ $ line ] } , $ callback ) ;
}
}
}
}
} # end of foreach host class with same mic list
}
# display the inventory information for mic
# this subroutine will handle the both rinv and rvitals commands
sub rinv {
my $ request = shift ;
my $ callback = shift ;
my $ subreq = shift ;
my $ host2mic = shift ;
my $ command = $ request - > { command } - > [ 0 ] ;
my $ usage_string ;
my % validargs ;
# set the valid argurments and correspoding groups which can be recognized by
# micinfo command
if ( $ command eq "rinv" ) {
$ usage_string = "rinv noderange {system|ver|board|core|gddr|all}" ;
% validargs = ( "system" = > "System" , "ver" = > "Version" , "board" = > "Board" ,
"core" = > "Core" , "gddr" = > "GDDR" , "all" = > "all" ) ;
} elsif ( $ command eq "rvitals" ) {
$ usage_string = "rvitals noderange {thermal|all}" ;
% validargs = ( "thermal" = > "Thermal" , "all" = > "all" ) ;
}
my @ args ;
if ( defined ( $ request - > { arg } ) ) {
@ args = @ { $ request - > { arg } } ;
unless ( @ args ) {
push @ args , "all" ;
}
} else {
push @ args , "all" ;
}
my @ groups ; # the groups name which could be displayed by micinfo
foreach my $ arg ( @ args ) {
if ( $ validargs { $ arg } ) {
if ( $ arg eq "all" ) {
if ( $ command eq "rinv" ) {
push @ groups , ( "System" , "Version" , "Board" , "Core" , "GDDR" ) ;
} elsif ( $ command eq "rvitals" ) {
push @ groups , ( "Thermal" ) ;
}
} else {
push @ groups , $ validargs { $ arg } ;
}
} else {
my $ rsp ;
push @ { $ rsp - > { data } } , $ usage_string ;
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback ) ;
return ;
}
}
my % groupflag ;
foreach ( @ groups ) {
$ groupflag { $ _ } = 1 ;
}
# run micinfo on the host to get all the mic information first
my @ hosts = ( keys %$ host2mic ) ;
my $ output = xCAT::Utils - > runxcmd ( { command = > [ 'xdsh' ] ,
node = > \ @ hosts ,
arg = > [ "/opt/intel/mic/bin/micinfo" ] } , $ subreq , 0 , 1 ) ;
# classify all the output with the host name
my % outofhost ;
foreach ( @$ output ) {
if ( /\@\@\@/ ) { next ; } # remove the part of ssh connection warning
foreach my $ line ( split /\n/ , $ _ ) {
$ line =~ s/(\s)+/ /g ;
if ( $ line =~ /([^:]*):(.*)/ ) {
push @ { $ outofhost { $ 1 } } , $ 2 ;
}
}
}
foreach my $ host ( keys % outofhost ) {
my $ micid ;
my $ micname ; # which is the node name of the mic
my $ curgroup ;
my @ sysinfo ;
my $ rsp ;
foreach ( @ { $ outofhost { $ host } } ) {
if ( /^\s*Device No:\s*(\d+)/ ) {
# get the mic name
$ micid = $ 1 ;
$ micname = $ host2mic - > { $ host } - > { $ micid } ;
# display the System infor first
foreach ( @ sysinfo ) {
if ( $ groupflag { 'System' } == 1 && $ micname ) {
push @ { $ rsp - > { data } } , "$micname: $_" ;
}
}
} elsif ( /^\s*System Info$/ ) {
$ curgroup = "System" ;
} elsif ( /^\s*Version$/ ) {
$ curgroup = "Version" ;
} elsif ( /^\s*Board$/ ) {
$ curgroup = "Board" ;
} elsif ( /^\s*Cores$/ ) {
$ curgroup = "Core" ;
} elsif ( /^\s*Thermal$/ ) {
$ curgroup = "Thermal" ;
} elsif ( /^\s*GDDR$/ ) {
$ curgroup = "GDDR" ;
} else {
my $ msg = $ _ ;
if ( $ msg =~ /^\s*$/ ) { next ; }
$ msg =~ s/^\s*// ;
if ( $ curgroup eq "System" ) {
push @ sysinfo , $ msg ;
} elsif ( $ groupflag { $ curgroup } == 1 && $ micname ) {
push @ { $ rsp - > { data } } , "$micname: $msg" ;
}
}
}
xCAT::MsgUtils - > message ( "I" , $ rsp , $ callback ) ;
}
}
# do the copy of metarials (root file system, flash image, kernel) for mic support from mpss tar
sub copytar {
my $ request = shift ;
my $ callback = shift ;
my $ args = $ request - > { arg } ;
my ( $ osname , $ file ) ;
if ( $ args ) {
@ ARGV = @ { $ args } ;
GetOptions ( 'n=s' = > \ $ osname ,
'f=s' = > \ $ file ) ;
}
my $ installroot = "/install" ;
my @ entries = xCAT::TableUtils - > get_site_attribute ( "installdir" ) ;
my $ t_entry = $ entries [ 0 ] ;
if ( defined ( $ t_entry ) ) {
$ installroot = $ t_entry ;
}
my $ tmpdir = "/tmp/mictmp" ;
my $ destdir = "$installroot/$osname" ;
rmtree ( $ tmpdir ) ;
mkpath ( $ tmpdir ) ;
mkpath ( $ destdir ) ;
# extract the files from the mpss tar file
my $ cmd = "tar xvf $file -C $tmpdir" ;
my @ output = xCAT::Utils - > runcmd ( $ cmd , - 1 ) ;
if ( $ ::RUNCMD_RC != 0 ) {
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "E" , { error = > [ "Error when run [$cmd], @output" ] , errorcode = > [ "1" ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
return 1 ;
}
# get the rpm packages intel-mic-gpl and intel-mic-flash which include the files for root file system, flash ...
my @ micgpl = <$tmpdir/*/intel-mic-gpl*> ;
my @ micflash = <$tmpdir/*/intel-mic-flash*> ;
unless ( - r $ micgpl [ 0 ] && - r $ micflash [ 0 ] ) {
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "E" , { error = > [ "Error: Cannot get the rpm files intel-mic-gpl or intel-mic-flash from the tar file." ] , errorcode = > [ "1" ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
return 1 ;
}
# extract the files from rpm packages
$ cmd = "cd $destdir; rpm2cpio $micgpl[0] | cpio -idum; rpm2cpio $micflash[0] | cpio -idum" ;
@ output = xCAT::Utils - > runcmd ( $ cmd , - 1 ) ;
if ( $ ::RUNCMD_RC != 0 ) {
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "E" , { error = > [ "Error when run [$cmd], @output" ] , errorcode = > [ "1" ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
return 1 ;
}
# generate the image objects
my $ oitab = xCAT::Table - > new ( 'osimage' ) ;
unless ( $ oitab ) {
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "E" , { error = > [ "Error: Cannot open table osimage." ] , errorcode = > [ "1" ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
return 1 ;
}
my % values ;
$ values { 'imagetype' } = "linux" ;
$ values { 'provmethod' } = "netboot" ;
$ values { 'rootfstype' } = "ramdisk" ;
$ values { 'description' } = "Linux for Intel mic" ;
$ values { 'osname' } = "Linux" ;
$ values { 'osvers' } = "mic" ;
$ values { 'osarch' } = "x86_64" ;
$ values { 'profile' } = "compute" ;
my $ imagename = "$osname-netboot-compute" ;
$ oitab - > setAttribs ( { 'imagename' = > $ imagename } , \ % values ) ;
my $ litab = xCAT::Table - > new ( 'linuximage' ) ;
unless ( $ litab ) {
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "E" , { error = > [ "Error: Cannot open table linuximage." ] , errorcode = > [ "1" ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
return 1 ;
}
# set a default package list
my $ pkglist = "$::XCATROOT/share/xcat/netboot/mic/compute.pkglist" ;
$ litab - > setAttribs ( { 'imagename' = > $ imagename } , { 'pkgdir' = > $ destdir , 'pkglist' = > $ pkglist } ) ;
2013-07-11 07:01:51 +00:00
xCAT::MsgUtils - > message ( "I" , { data = > [ "The image $imagename has been created." ] } , $ callback ) ;
2013-06-30 04:25:58 +00:00
rmtree ( $ tmpdir ) ;
}
# get the console configuration for rcons:
# see /opt/xcat/share/xcat/cons/mic
sub getcons {
my $ request = shift ;
my $ callback = shift ;
my $ node = $ request - > { node } - > [ 0 ] ;
my $ mictab = xCAT::Table - > new ( "mic" ) ;
unless ( $ mictab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the mic table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
# get the console parameters
my $ sconsparms = { node = > [ { name = > [ $ node ] } ] } ;
my $ mictabhash = $ mictab - > getNodeAttribs ( $ node , [ 'host' , 'id' ] ) ;
if ( defined ( $ mictabhash - > { 'host' } ) ) {
$ sconsparms - > { node } - > [ 0 ] - > { sshhost } = [ $ mictabhash - > { 'host' } ] ;
}
if ( defined ( $ mictabhash - > { 'id' } ) ) {
$ sconsparms - > { node } - > [ 0 ] - > { psuedotty } = [ "/dev/ttyMIC" . $ mictabhash - > { 'id' } ] ;
}
$ sconsparms - > { node } - > [ 0 ] - > { baudrate } = [ "115200" ] ;
$ callback - > ( $ sconsparms ) ;
}
# do the flash of firmware for mic
sub rflash {
my $ request = shift ;
my $ callback = shift ;
my $ subreq = shift ;
my $ host2mic = shift ;
my $ usage_string = "rflash noderange" ;
my $ nodes = $ request - > { 'node' } ;
# get the provision method for all the nodes
my $ nttab = xCAT::Table - > new ( "nodetype" ) ;
unless ( $ nttab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the nodetype table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my $ nthash = $ nttab - > getNodesAttribs ( $ nodes , [ 'provmethod' ] ) ;
foreach my $ node ( @$ nodes ) {
unless ( defined ( $ nthash - > { $ node } - > [ 0 ] - > { 'provmethod' } ) ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "The provmethod for the node $node should be set before the rflash." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# get pkgdir for the osimage
my $ litab = xCAT::Table - > new ( "linuximage" ) ;
unless ( $ litab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the linuximage table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my @ osimages = $ litab - > getAllAttribs ( "imagename" , "pkgdir" ) ;
my % osimage ;
foreach ( @ osimages ) {
$ osimage { $ _ - > { 'imagename' } } = $ _ - > { 'pkgdir' } ;
}
# get the tftp dir and create the path for the mic configuration files
my $ tftpdir = "/tftpboot" ;
my @ entries = xCAT::TableUtils - > get_site_attribute ( "$tftpdir" ) ;
my $ t_entry = $ entries [ 0 ] ;
if ( defined ( $ t_entry ) ) {
$ tftpdir = $ t_entry ;
}
mkpath ( "$tftpdir/xcat/miccfg/" ) ;
# generate the rflash configuration files for each host
# the configureation file should have the following format
#miclist=mic0
#0:name=host1-mic0
#imgpath=/install/mpss3.new
my @ hosts = ( keys %$ host2mic ) ;
foreach my $ host ( @ hosts ) {
my @ cfgfile ;
push @ cfgfile , "#XCAT MIC FLASH CONFIGURATION FILE#" ;
my $ miclist = $ host2mic - > { $ host } { 'ids' } ;
my @ micids = split ( / / , $ miclist ) ;
$ miclist =~ s/(\d+)/mic$1/g ;
$ miclist =~ s/ /,/g ;
$ miclist =~ s/^,// ;
push @ cfgfile , "miclist=$miclist" ;
my $ osimg ;
foreach my $ micid ( @ micids ) {
if ( $ micid eq '' ) { next ; }
my $ micname = $ host2mic - > { $ host } { $ micid } ;
# get the pkgdir of the osimage which set to the mic node.
# and make sure the osimage which set to the mic shold be same for all the mics on one host
if ( $ osimg ) {
if ( $ osimg ne $ nthash - > { $ micname } - > [ 0 ] - > { 'provmethod' } ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "The provmethod for the nodes in the same host should be same." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
} else {
$ osimg = $ nthash - > { $ micname } - > [ 0 ] - > { 'provmethod' } ;
}
push @ cfgfile , "$micid:name=$micname" ;
}
push @ cfgfile , "imgpath=$osimage{$osimg}" ;
if ( open ( CFG , ">$tftpdir/xcat/miccfg/micflash.$host" ) ) {
foreach ( @ cfgfile ) {
print CFG $ _ . "\n" ;
}
close ( CFG ) ;
} else {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the file $tftpdir/xcat/miccfg/micflash.$host to write." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# run the cmd on the host to flash the mic
my @ args = ( "-s" , "-v" , "-e" ) ;
push @ args , "$::XCATROOT/sbin/flashmic" ;
my $ master = $ request - > { '_xcatdest' } ;
push @ args , ( "-m" , "$master" ) ;
push @ args , ( "-p" , "$tftpdir/xcat/miccfg" ) ;
$ CALLBACK = $ callback ;
$ subreq - > ( { command = > [ 'xdsh' ] ,
node = > \ @ hosts ,
arg = > \ @ args } , \ & michost_cb ) ;
}
# run the nodeset to genimage the osimage for each mic
# it gets on host first and mount the root file system from MN:/install/mpssxxx
# to host, and then configure and generate the mic specific osimage for each mic.
#
# If running 'odeset noderange osimage=imagename', the imagename will be used to generate
# image for mic and imagename will be set to the provmethod attr for the mic. If no 'osimage=xx'
# is specified, the osimage will be get from provmethod attr.
sub nodeset {
my $ request = shift ;
my $ callback = shift ;
my $ subreq = shift ;
my $ host2mic = shift ;
my $ usage_string = "nodeset noderange [osimage=imagename]" ;
my $ nodes = $ request - > { 'node' } ;
my $ args = $ request - > { arg } ;
my $ setosimg ;
foreach ( @$ args ) {
if ( /osimage=(.*)/ ) {
$ setosimg = $ 1 ;
} else {
xCAT::MsgUtils - > message ( "E" , { error = > [ $ usage_string ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# get the provision method for all the nodes
my $ nttab = xCAT::Table - > new ( "nodetype" ) ;
unless ( $ nttab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the nodetype table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
# if the osimage=xxx has been specified, then set it to the provmethod attr for the mic.
if ( $ setosimg ) {
my % setpmethod ;
foreach ( @$ nodes ) {
$ setpmethod { $ _ } { 'provmethod' } = $ setosimg ;
}
$ nttab - > setNodesAttribs ( \ % setpmethod ) ;
}
my $ nthash = $ nttab - > getNodesAttribs ( $ nodes , [ 'provmethod' ] ) ;
foreach my $ node ( @$ nodes ) {
unless ( defined ( $ nthash - > { $ node } - > [ 0 ] - > { 'provmethod' } ) ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "The provmethod for the node $node must be set before the nodeset." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# get the virtual bridge, onboot, vlog information for the mic nodes
my $ mictab = xCAT::Table - > new ( "mic" ) ;
unless ( $ mictab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the mic table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my $ michash = $ mictab - > getNodesAttribs ( $ nodes , [ 'bridge' , 'onboot' , 'vlog' ] ) ;
# get ip for the mic nodes from hosts table
my $ hosttab = xCAT::Table - > new ( "hosts" ) ;
unless ( $ hosttab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the host table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my $ hosthash = $ hosttab - > getNodesAttribs ( $ nodes , [ 'ip' ] ) ;
# get pkgdir from the osimage
my $ litab = xCAT::Table - > new ( "linuximage" ) ;
unless ( $ litab ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the linuximage table." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
my @ osimages = $ litab - > getAllAttribs ( "imagename" , "pkgdir" ) ;
my % osimage ;
foreach ( @ osimages ) {
$ osimage { $ _ - > { 'imagename' } } = $ _ - > { 'pkgdir' } ;
}
# get the tftp dir and create the path for mic configuration
my $ tftpdir = "/tftpboot" ;
my @ entries = xCAT::TableUtils - > get_site_attribute ( "$tftpdir" ) ;
my $ t_entry = $ entries [ 0 ] ;
if ( defined ( $ t_entry ) ) {
$ tftpdir = $ t_entry ;
}
mkpath ( "$tftpdir/xcat/miccfg/" ) ;
# generate the configuration file for each host
# the configureation file should have the following format
#miclist=mic0
#0:ip=10.10.10.1|br=mybr0|name=host1-mic0|onboot=yes|vlog=no
#imgpath=/install/mpss3.1
#overlay=ol1
my % imghash ; # cache of osimage information
my @ hosts = ( keys %$ host2mic ) ;
foreach my $ host ( @ hosts ) {
my @ cfgfile ;
push @ cfgfile , "#XCAT MIC CONFIGURATION FILE#" ;
my $ miclist = $ host2mic - > { $ host } { 'ids' } ;
my @ micids = split ( / / , $ miclist ) ;
$ miclist =~ s/(\d+)/mic$1/g ;
$ miclist =~ s/ /,/g ;
$ miclist =~ s/^,// ;
push @ cfgfile , "miclist=$miclist" ;
my $ osimg ;
foreach my $ micid ( @ micids ) {
if ( $ micid eq '' ) { next ; }
my $ micname = $ host2mic - > { $ host } { $ micid } ;
# get the pkgdir of the osimage which set to the mic node,
# and make sure the osimage which set to the mic shold be same for all the mics on one host
if ( $ osimg ) {
if ( $ osimg ne $ nthash - > { $ micname } - > [ 0 ] - > { 'provmethod' } ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "The provmethod for the nodes in the same host should be same." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
} else {
$ osimg = $ nthash - > { $ micname } - > [ 0 ] - > { 'provmethod' } ;
}
# get the ip of the mic node
# get the ip from system resolution first, if failed, get from host table
my $ micip = xCAT::NetworkUtils - > getipaddr ( $ micname ) ;
unless ( $ micip ) {
$ micip = $ hosthash - > { $ micname } - > [ 0 ] - > { 'ip' } ;
unless ( $ micip ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot get the IP from hosts table or system resolution for the $micname." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# get the virtual bridge for the mic node
my $ micbrg = $ michash - > { $ micname } - > [ 0 ] - > { 'bridge' } ;
unless ( $ micbrg ) {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot get the micbridge for the $micname." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
# generate the mic specific entry in the configuration file
my $ micattrs = "$micid:ip=$micip|br=$micbrg|name=$micname" ;
if ( defined ( $ michash - > { $ micname } - > [ 0 ] - > { 'onboot' } ) ) {
$ micattrs . = "|onboot=$michash->{$micname}->[0]->{'onboot'}" ;
}
if ( defined ( $ michash - > { $ micname } - > [ 0 ] - > { 'vlog' } ) ) {
$ micattrs . = "|vlog=$michash->{$micname}->[0]->{'vlog'}" ;
}
push @ cfgfile , $ micattrs ;
}
push @ cfgfile , "imgpath=$osimage{$osimg}" ;
# get all the overlay entries for the osimage and do the cache for image
# search all the dir in the overlay dir execpt the system dir (system dir includes the files
# which generated by genimage command, and will be copied to mic osimage separated)
if ( defined ( $ imghash { $ osimg } { 'ollist' } ) ) {
push @ cfgfile , "overlay=$imghash{$osimg}{'ollist'}" ;
} else {
my @ overlays = <$osimage{$osimg}/opt/intel/mic/filesystem/overlay/*> ;
my $ ollist ; # overlay list
foreach my $ obj ( @ overlays ) {
my $ objname = basename ( $ obj ) ;
if ( - d $ obj && $ objname ne "system" ) {
$ ollist . = ",$objname" ;
}
}
$ ollist =~ s/^,// ;
$ imghash { $ osimg } { 'ollist' } = $ ollist ;
push @ cfgfile , "overlay=$ollist" ;
}
if ( open ( CFG , ">$tftpdir/xcat/miccfg/miccfg.$host" ) ) {
foreach ( @ cfgfile ) {
print CFG $ _ . "\n" ;
}
close ( CFG ) ;
} else {
xCAT::MsgUtils - > message ( "E" , { error = > [ "Cannot open the file $tftpdir/xcat/miccfg/miccfg.$host to write." ] , errorcode = > [ "1" ] } , $ callback ) ;
return ;
}
}
# run the cmd on the host to configure the mic
my @ args = ( "-s" , "-v" , "-e" ) ;
push @ args , "$::XCATROOT/sbin/configmic" ;
my $ master = $ request - > { '_xcatdest' } ;
push @ args , ( "-m" , "$master" ) ;
push @ args , ( "-p" , "$tftpdir/xcat/miccfg" ) ;
$ CALLBACK = $ callback ;
$ subreq - > ( { command = > [ 'xdsh' ] ,
node = > \ @ hosts ,
#arg => \@args }, $callback);
arg = > \ @ args } , \ & michost_cb ) ;
}
# Handle the return message from xdsh command for 'configmic' and 'flashmic' scripts to
# replace the message with correct mic name at head. And remove the unnecessary messages
sub michost_cb {
no strict ;
my $ response = shift ;
my $ rsp ;
foreach my $ type ( keys %$ response )
{
my @ newop ;
foreach my $ output ( @ { $ response - > { $ type } } )
{
# since the remote run of mic configuration will be closed by force in the configmic
# script, remove the error message from xdsh
if ( $ type eq "error" && $ output =~ /(remote shell had error code|remote Command had return code)/ ) {
delete $ response - > { error } ;
delete $ rsp - > { error } ;
} elsif ( $ type eq "data" && $ output =~ /Connection to(.*)closed by remote host/ ) {
delete $ response - > { error } ;
delete $ response - > { errorcode } ;
delete $ rsp - > { error } ;
delete $ rsp - > { errorcode } ;
} else {
$ output =~ s/^[^:]+:\s*MICMSG://g ;
$ output =~ s/\n[^:]+:\s*MICMSG:/\n/g ;
push @ newop , $ output ;
}
}
$ rsp - > { $ type } = \ @ newop ;
}
$ CALLBACK - > ( $ rsp ) ;
}
1 ;