2007-10-26 22:44:33 +00:00
#-------------------------------------------------------
2008-02-18 15:57:25 +00:00
= head1
2007-10-26 22:44:33 +00:00
xCAT plugin package to handle xdsh
Supported command:
xdsh - > dsh
xdcp - > dcp
= cut
#-------------------------------------------------------
package xCAT_plugin::xdsh ;
2008-07-18 12:04:28 +00:00
use strict ;
2009-07-07 11:38:53 +00:00
use Storable qw( dclone ) ;
2009-09-21 13:09:26 +00:00
use File::Basename ;
use File::Path ;
2010-05-14 16:23:19 +00:00
use POSIX ;
2008-04-07 19:25:44 +00:00
require xCAT::Table ;
2007-10-26 22:44:33 +00:00
2008-04-07 19:25:44 +00:00
require xCAT::Utils ;
2012-08-09 04:07:40 +00:00
require xCAT::TableUtils ;
require xCAT::ServiceNodeUtils ;
2008-04-07 19:25:44 +00:00
require xCAT::MsgUtils ;
2007-12-12 13:24:36 +00:00
use Getopt::Long ;
require xCAT::DSHCLI ;
2007-10-26 22:44:33 +00:00
1 ;
#-------------------------------------------------------
2008-02-18 15:57:25 +00:00
= head3 handled_commands
2007-10-26 22:44:33 +00:00
Return list of commands handled by this plugin
= cut
#-------------------------------------------------------
sub handled_commands
{
return {
xdsh = > "xdsh" ,
xdcp = > "xdsh"
} ;
}
2008-03-10 18:25:22 +00:00
#-------------------------------------------------------
= head3 preprocess_request
Check and setup for hierarchy
= cut
#-------------------------------------------------------
2008-03-31 18:17:00 +00:00
sub preprocess_request
{
2009-12-04 14:48:50 +00:00
my $ req = shift ;
my $ cb = shift ;
my $ sub_req = shift ;
2008-03-31 18:17:00 +00:00
my % sn ;
2008-07-18 12:04:28 +00:00
my $ sn ;
2009-12-04 14:48:50 +00:00
my $ rc = 0 ;
2009-07-22 13:36:13 +00:00
2009-03-16 13:24:16 +00:00
#if already preprocessed, go straight to request
2009-12-04 14:48:50 +00:00
if ( ( defined ( $ req - > { _xcatpreprocessed } ) )
&& ( $ req - > { _xcatpreprocessed } - > [ 0 ] == 1 ) )
{
return [ $ req ] ;
}
my $ command = $ req - > { command } - > [ 0 ] ; # xdsh vs xdcp
2008-10-17 16:04:13 +00:00
my $ nodes = $ req - > { node } ;
my $ service = "xcat" ;
2008-07-18 12:04:28 +00:00
my @ requests ;
2009-12-04 14:48:50 +00:00
$ ::RUNCMD_RC = 0 ;
2012-12-24 14:23:27 +00:00
@ ::good_SN = ( ) ;
@ ::bad_SN = ( ) ;
2009-12-04 14:48:50 +00:00
my $ syncsn = 0 ; # sync service node only if 1
2009-06-25 18:17:53 +00:00
# read the environment variables for rsync setup
2010-05-14 16:23:19 +00:00
# and xdsh -e command
2009-05-19 14:59:17 +00:00
foreach my $ envar ( @ { $ req - > { env } } )
{
my ( $ var , $ value ) = split ( /=/ , $ envar , 2 ) ;
2009-07-22 13:36:13 +00:00
if ( $ var eq "RSYNCSNONLY" )
2009-06-04 12:21:10 +00:00
{ # syncing SN, will change noderange to list of SN
2009-07-22 13:36:13 +00:00
# we are only syncing the service node ( -s flag)
2009-06-04 12:21:10 +00:00
$ syncsn = 1 ;
2009-05-25 12:27:48 +00:00
}
2009-06-04 12:21:10 +00:00
if ( $ var eq "DSH_RSYNC_FILE" ) # from -F flag
{ # if hierarchy,need to copy file to the SN
2012-08-01 13:28:25 +00:00
$ ::syncsnfile = $ value ; # name of syncfile
2009-05-19 14:59:17 +00:00
}
2009-12-04 14:48:50 +00:00
if ( $ var eq "DCP_PULL" ) # from -P flag
{
$ ::dcppull = 1 ; # TBD handle pull hierarchy
2009-09-23 11:44:08 +00:00
}
2010-05-14 16:23:19 +00:00
if ( $ var eq "DSHEXECUTE" ) # from xdsh -e flag
{
2010-05-14 17:18:42 +00:00
$ ::dshexecutecmd = $ value ; # Handle hierarchy
my @ cmd = split ( / / , $ value ) ; # split off args, if any
$ ::dshexecute = $ cmd [ 0 ] ; # This is the executable file
2010-05-14 16:23:19 +00:00
}
2009-05-19 14:59:17 +00:00
}
2012-12-16 14:24:20 +00:00
# if xdcp need to make sure request has full path to input files
if ( $ command eq "xdcp" ) {
$ req = & parse_xdcp_cmd ( $ req ) ;
}
# if xdsh need to make sure request has full path to input files
2012-12-18 13:46:05 +00:00
if ( $ command eq "xdsh" ) {
$ req = & parse_xdsh_cmd ( $ req ) ;
}
2008-03-31 18:17:00 +00:00
2009-12-04 14:48:50 +00:00
# there are nodes in the xdsh command, not xdsh to an image
2008-10-17 16:04:13 +00:00
if ( $ nodes )
{
2009-12-04 14:48:50 +00:00
# find service nodes for requested nodes
# build an individual request for each service node
# find out the names for the Management Node
2012-08-09 04:07:40 +00:00
my @ MNnodeinfo = xCAT::NetworkUtils - > determinehostname ;
2009-12-04 14:48:50 +00:00
my $ MNnodename = pop @ MNnodeinfo ; # hostname
my @ MNnodeipaddr = @ MNnodeinfo ; # ipaddresses
$ ::mnname = $ MNnodeipaddr [ 0 ] ;
$ ::SNpath ; # syncfile path on the service node
2012-08-09 04:07:40 +00:00
$ sn = xCAT::ServiceNodeUtils - > get_ServiceNode ( $ nodes , $ service , "MN" ) ;
2009-09-21 13:09:26 +00:00
my @ snodes ;
my @ snoderange ;
2009-06-25 18:17:53 +00:00
2009-09-21 13:09:26 +00:00
# check to see if service nodes and not just the MN
2011-08-05 11:34:53 +00:00
# if just MN or I am on a Service Node, then no hierarchy to deal with
if ( ! ( xCAT::Utils - > isServiceNode ( ) ) ) { # not on a servicenode
if ( $ sn )
{
2009-09-21 13:09:26 +00:00
foreach my $ snkey ( keys %$ sn )
{
if ( ! grep ( /$snkey/ , @ MNnodeipaddr ) )
2009-12-04 14:48:50 +00:00
{ # if not the MN
2009-09-21 13:09:26 +00:00
push @ snodes , $ snkey ;
$ snoderange [ 0 ] . = "$snkey," ;
2009-12-04 14:48:50 +00:00
chop $ snoderange [ 0 ] ;
2009-09-21 13:09:26 +00:00
}
}
2011-08-05 11:34:53 +00:00
}
2009-12-04 14:48:50 +00:00
}
2009-06-25 18:17:53 +00:00
2010-05-14 16:23:19 +00:00
# if servicenodes and (if xdcp and not pull function or xdsh -e)
2009-12-04 14:48:50 +00:00
# send command to service nodes first and process errors
# return an array of good service nodes
#
2010-05-14 16:23:19 +00:00
my $ synfiledir ;
2009-12-04 14:48:50 +00:00
if ( @ snodes ) # service nodes
{
2010-05-14 16:23:19 +00:00
# if xdcp and not pull function or xdsh -e
if ( ( ( $ command eq "xdcp" ) && ( $ ::dcppull == 0 ) ) or ( $ ::dshexecute ) )
2009-05-25 12:27:48 +00:00
{
2009-09-21 13:09:26 +00:00
# get the directory on the servicenode to put the files in
2012-08-09 04:07:40 +00:00
my @ syndir = xCAT::TableUtils - > get_site_attribute ( "SNsyncfiledir" ) ;
2009-09-21 13:09:26 +00:00
if ( $ syndir [ 0 ] )
{
$ synfiledir = $ syndir [ 0 ] ;
2009-06-25 18:17:53 +00:00
}
2009-09-21 13:09:26 +00:00
else
{
2009-12-04 14:48:50 +00:00
$ synfiledir = "/var/xcat/syncfiles" ; # default
}
2009-09-21 13:09:26 +00:00
2009-12-04 14:48:50 +00:00
# setup the service node with the files to xdcp to the
# compute nodes
2010-05-14 16:23:19 +00:00
if ( $ command eq "xdcp" ) {
$ rc =
& process_servicenodes_xdcp ( $ req , $ cb , $ sub_req , \ @ snodes ,
2009-12-04 14:48:50 +00:00
\ @ snoderange , $ synfiledir ) ;
2009-09-21 13:09:26 +00:00
2010-05-14 16:23:19 +00:00
# fatal error need to stop
if ( $ rc != 0 )
{
return ;
}
} else { # xdsh -e
$ rc =
& process_servicenodes_xdsh ( $ req , $ cb , $ sub_req , \ @ snodes ,
\ @ snoderange , $ synfiledir ) ;
# fatal error need to stop
if ( $ rc != 0 )
{
return ;
}
2009-09-21 13:09:26 +00:00
}
2009-05-25 12:27:48 +00:00
}
2009-12-04 14:48:50 +00:00
else
2010-05-14 16:23:19 +00:00
{ # command is xdsh ( not -e) or xdcp pull
2009-12-04 14:48:50 +00:00
@ ::good_SN = @ snodes ; # all good service nodes for now
}
2009-06-04 12:21:10 +00:00
2009-12-04 14:48:50 +00:00
}
else
{ # no servicenodes, no hierarchy
# process here on the MN
& process_request ( $ req , $ cb , $ sub_req ) ;
return ;
}
# if hierarchical work still to do
# Note there may still be a mix of nodes that are service from
# the MN and nodes that are serviced from the SN, for example
# a dsh to a list of servicenodes and nodes in the noderange.
if ( $ syncsn == 0 ) # not just syncing (-s) the service nodes
# taken care of in process_servicenodes
{
2009-07-22 13:36:13 +00:00
foreach my $ snkey ( keys %$ sn )
{
2009-06-04 12:21:10 +00:00
2009-12-04 14:48:50 +00:00
# if it is not being service by the MN
2009-07-22 13:36:13 +00:00
if ( ! grep ( /$snkey/ , @ MNnodeipaddr ) )
2009-12-04 14:48:50 +00:00
{
# if it is a good SN, one ready to service the nodes
2013-01-03 14:42:56 +00:00
# split if a pool
# if one in the pool is good, send the command to the
# daemon
my @ sn_list = split ',' , $ snkey ;
my $ goodsn = 0 ;
foreach my $ sn ( @ sn_list ) {
if ( grep ( /$sn/ , @ ::good_SN ) ) {
$ goodsn = 1 ;
last ;
}
}
# found a good service node
if ( $ goodsn == 1 )
2009-06-04 12:21:10 +00:00
{
2010-05-14 16:23:19 +00:00
my $ noderequests =
& process_nodes ( $ req , $ sn , $ snkey , $ synfiledir ) ;
2009-12-04 14:48:50 +00:00
push @ requests , $ noderequests ; # build request queue
2009-09-21 13:09:26 +00:00
}
2009-06-04 12:21:10 +00:00
}
2009-12-04 14:48:50 +00:00
else # serviced by the MN, then
{ # just run normal dsh dcp
2009-07-22 13:36:13 +00:00
my $ reqcopy = { %$ req } ;
$ reqcopy - > { node } = $ sn - > { $ snkey } ;
$ reqcopy - > { '_xcatdest' } = $ snkey ;
$ reqcopy - > { _xcatpreprocessed } - > [ 0 ] = 1 ;
push @ requests , $ reqcopy ;
}
} # end foreach
2009-12-04 14:48:50 +00:00
} # end syncing nodes
2008-10-17 16:04:13 +00:00
}
2009-12-04 14:48:50 +00:00
else # no nodes on the command
{ # running on local image
2008-10-17 16:04:13 +00:00
return [ $ req ] ;
2008-03-31 18:17:00 +00:00
}
return \ @ requests ;
}
2012-12-16 14:24:20 +00:00
#-------------------------------------------------------
= head3 parse_xdcp_cmd
Check to see if full path on file ( s ) input to the command
If not add currentpath to the file in the argument
= cut
#-------------------------------------------------------
sub parse_xdcp_cmd
{
my $ req = shift ;
my $ args = $ req - > { arg } ; # argument
my $ orgargarraySize = @ { $ args } ; # get the size of the arg array
my $ currpath = $ req - > { cwd } - > [ 0 ] ; # current path when command was executed
@ ARGV = @ { $ args } ; # get arguments
my @ SaveARGV = @ ARGV ; # save the original argument list
my % options = ( ) ;
Getopt::Long:: Configure ( "posix_default" ) ;
Getopt::Long:: Configure ( "no_gnu_compat" ) ;
Getopt::Long:: Configure ( "bundling" ) ;
if (
! GetOptions (
'f|fanout=i' = > \ $ options { 'fanout' } ,
'F|File=s' = > \ $ options { 'File' } ,
'h|help' = > \ $ options { 'help' } ,
'i|rootimg=s' = > \ $ options { 'rootimg' } ,
'l|user=s' = > \ $ options { 'user' } ,
'n|nodes=s' = > \ $ options { 'nodes' } ,
'o|node-options=s' = > \ $ options { 'node-options' } ,
'q|show-config' = > \ $ options { 'show-config' } ,
'p|preserve' = > \ $ options { 'preserve' } ,
'r|c|node-rcp=s' = > \ $ options { 'node-rcp' } ,
's' = > \ $ options { 'rsyncSN' } ,
't|timeout=i' = > \ $ options { 'timeout' } ,
'v|verify' = > \ $ options { 'verify' } ,
'B|bypass' = > \ $ options { 'bypass' } ,
'Q|silent' = > \ $ options { 'silent' } ,
'P|pull' = > \ $ options { 'pull' } ,
'R|recursive' = > \ $ options { 'recursive' } ,
'T|trace' = > \ $ options { 'trace' } ,
'V|version' = > \ $ options { 'version' } ,
2012-12-17 09:20:57 +00:00
'nodestatus|nodestatus' = > \ $ options { 'nodestatus' } ,
2012-12-16 14:24:20 +00:00
'X:s' = > \ $ options { 'ignore_env' }
)
)
{
xCAT::DSHCLI - > usage_dcp ;
exit 1 ;
}
my $ changedfile = 0 ;
# check to see if -F option and if there is, is the
# input file fully defined path
my $ newfile ;
if ( defined ( $ options { 'File' } ) ) {
if ( $ options { 'File' } !~ /^\// ) { # not a full path
$ newfile = xCAT::Utils - > full_path ( $ options { 'File' } , $ currpath ) ;
$ changedfile = 1 ;
} else { # it is a full path
$ newfile = $ options { 'File' } ;
}
# now need to go through the original argument list and replace the file
# after the -F flag, if a file was changed
my @ newarg ;
my $ updatefile = 0 ;
my $ arglength = 0 ;
if ( $ changedfile == 1 ) {
foreach my $ arg ( @ SaveARGV ) {
if ( $ updatefile == 1 ) { # found the file to change
push @ newarg , $ newfile ;
$ updatefile = 0 ;
next ; # skip the old entry
}
if ( $ arg !~ /^-F/ ) {
push @ newarg , $ arg ;
} else {
# if -F there are two format. -Ffile in one element or -F file
# in two elements of the array
$ arglength = length ( $ arg ) ;
if ( $ arglength <= 2 ) { # this is the -F file format
push @ newarg , $ arg ;
$ updatefile = 1 ;
} else { # this is the -Ffile format
my $ n = "-F" ;
$ n . = $ newfile ;
push @ newarg , $ n ;
$ updatefile = 0 ;
}
}
}
#put the new argument list on the request
@ { $ req - > { arg } } = @ newarg ;
}
} # end -F option
# For xdcp ...... file1 file2 command
# what is left in the argument are the files to copy
# each from and to file needs to be checked if relative or expanded path
# If not expanded, it needs to have current path added
$ changedfile = 0 ; # resetting this but there should be only -F or a list
# or files for xdcp, not both
my @ newfiles ;
my $ leftoverargsize = @ ARGV ;
if ( @ ARGV > 0 ) {
foreach my $ file ( @ ARGV ) {
if ( $ file !~ /^\// ) { # not full path
$ file = xCAT::Utils - > full_path ( $ file , $ currpath ) ;
$ changedfile = 1 ;
}
push @ newfiles , $ file ;
}
}
# if had to add the path to a file, then need to rebuild the
# request->{args} array
if ( $ changedfile == 1 ) {
my $ offset = $ orgargarraySize - $ leftoverargsize ;
# offset is where we start updating
foreach my $ file ( @ newfiles ) {
$ req - > { arg } - > [ $ offset ] = $ file ;
$ offset + +
}
}
return $ req ;
}
2008-03-10 18:25:22 +00:00
2007-10-26 22:44:33 +00:00
#-------------------------------------------------------
2012-12-18 13:46:05 +00:00
= head3 parse_xdsh_cmd
Check to see if full path on file ( s ) input to the command
If not add currentpath to the file in the argument
= cut
#-------------------------------------------------------
sub parse_xdsh_cmd
{
my $ req = shift ;
my $ args = $ req - > { arg } ; # argument
my $ currpath = $ req - > { cwd } - > [ 0 ] ; # current path when command was executed
my $ orgargarraySize = @ { $ args } ; # get the size of the arg array
@ ARGV = @ { $ args } ; # get arguments
my @ SaveARGV = @ ARGV ; # save the original argument list
my % options = ( ) ;
Getopt::Long:: Configure ( "posix_default" ) ;
Getopt::Long:: Configure ( "no_gnu_compat" ) ;
Getopt::Long:: Configure ( "bundling" ) ;
if (
! GetOptions (
'e|execute' = > \ $ options { 'execute' } ,
'f|fanout=i' = > \ $ options { 'fanout' } ,
'h|help' = > \ $ options { 'help' } ,
'l|user=s' = > \ $ options { 'user' } ,
'm|monitor' = > \ $ options { 'monitor' } ,
'o|node-options=s' = > \ $ options { 'node-options' } ,
'q|show-config' = > \ $ options { 'show-config' } ,
'r|node-rsh=s' = > \ $ options { 'node-rsh' } ,
'i|rootimg=s' = > \ $ options { 'rootimg' } ,
's|stream' = > \ $ options { 'streaming' } ,
't|timeout=i' = > \ $ options { 'timeout' } ,
'v|verify' = > \ $ options { 'verify' } ,
'z|exit-status' = > \ $ options { 'exit-status' } ,
'B|bypass' = > \ $ options { 'bypass' } ,
'c|cleanup' = > \ $ options { 'cleanup' } ,
'E|environment=s' = > \ $ options { 'environment' } ,
'I|ignore-sig|ignoresig=s' = > \ $ options { 'ignore-signal' } ,
'K|keysetup' = > \ $ options { 'ssh-setup' } ,
'L|no-locale' = > \ $ options { 'no-locale' } ,
'Q|silent' = > \ $ options { 'silent' } ,
'S|syntax=s' = > \ $ options { 'syntax' } ,
'T|trace' = > \ $ options { 'trace' } ,
'V|version' = > \ $ options { 'version' } ,
'devicetype=s' = > \ $ options { 'devicetype' } ,
'nodestatus|nodestatus' = > \ $ options { 'nodestatus' } ,
2013-01-09 11:56:18 +00:00
'sudo|sudo' = > \ $ options { 'sudo' } ,
2012-12-18 13:46:05 +00:00
'command-name|commandName=s' = > \ $ options { 'command-name' } ,
'command-description|commandDescription=s' = >
\ $ options { 'command-description' } ,
'X:s' = > \ $ options { 'ignore_env' }
)
)
{
xCAT::DSHCLI - > usage_dsh ;
exit 1 ;
}
# elements left in the array after the parse
# these are the script and it's arguments
my $ leftoverargsize = @ ARGV ;
my $ changedfile = 0 ;
# check to see if -e option
# change file to fully defined path
my @ executecmd = @ ARGV ;
if ( defined ( $ options { 'execute' } ) ) {
# this can be the script name + parms
if ( $ executecmd [ 0 ] !~ /^\// ) { # not a full path in the script name
$ executecmd [ 0 ] = xCAT::Utils - > full_path ( $ executecmd [ 0 ] , $ currpath ) ;
$ changedfile = 1 ;
}
# if had to add the path to the script, then need to rebuild the
# request->{args} array
if ( $ changedfile == 1 ) {
my $ offset = $ orgargarraySize - $ leftoverargsize ;
# offset is where we start updating
foreach my $ file ( @ executecmd ) {
$ req - > { arg } - > [ $ offset ] = $ file ;
$ offset + +
}
}
} # end -e option
return $ req ;
}
#-------------------------------------------------------
2010-05-14 16:23:19 +00:00
= head3 process_servicenodes_xdcp
2009-12-04 14:48:50 +00:00
Build the xdcp command to send to the service nodes first
Return an array of servicenodes that do not have errors
Returns error code:
if = 0 , good return continue to process the
nodes .
if = 1 , global error need to quit
= cut
#-------------------------------------------------------
2010-05-14 16:23:19 +00:00
sub process_servicenodes_xdcp
2009-12-04 14:48:50 +00:00
{
my $ req = shift ;
my $ callback = shift ;
my $ sub_req = shift ;
my $ sn = shift ;
my $ snrange = shift ;
my $ synfiledir = shift ;
my @ snodes = @$ sn ;
my @ snoderange = @$ snrange ;
my $ args ;
$ ::RUNCMD_RC = 0 ;
my $ cmd = $ req - > { command } - > [ 0 ] ;
2012-08-01 13:28:25 +00:00
# if xdcp -F command (input $syncsnfile) and the original synclist need
# to be rsync to the $synfiledir directory on the service nodes first
2009-12-04 14:48:50 +00:00
if ( $ ::syncsnfile )
{
if ( ! - f $ ::syncsnfile )
{ # syncfile does not exist, quit
my $ rsp = { } ;
2012-11-28 11:38:15 +00:00
$ rsp - > { error } - > [ 0 ] = "File:$::syncsnfile does not exist." ;
2009-12-04 14:48:50 +00:00
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback , 1 ) ;
return ( 1 ) ; # process no service nodes
}
2012-08-01 13:28:25 +00:00
# xdcp rsync each of the files contained in the -F syncfile and the
# original synclist input on the -F flag to
2010-05-14 16:23:19 +00:00
# the service node first to the site.SNsyncfiledir directory
2009-12-04 14:48:50 +00:00
#change noderange to the service nodes
2012-12-24 14:23:27 +00:00
# sync and check for error
my @ sn = ( ) ;
#build the array of all service nodes
2009-12-04 14:48:50 +00:00
foreach my $ node ( @ snodes )
{
2011-01-05 15:29:59 +00:00
# handle multiple servicenodes for one node
my @ sn_list = split ',' , $ node ;
foreach my $ snode ( @ sn_list ) {
push @ sn , $ snode ;
}
2012-12-24 14:23:27 +00:00
}
2009-12-04 14:48:50 +00:00
2012-12-24 14:23:27 +00:00
@ ::good_SN = @ sn ; # initialize all good
# run the command to the servicenodes
# xdcp <sn> -s -F <syncfile>
# don't use runxcmd, because can go straight to process_request,
# these are all service nodes. Also servicenode is taken from
# the noderes table and may not be the same name as in the nodelist
# table, for example may be an ip address.
# here on the MN
my $ addreq ;
$ addreq - > { '_xcatdest' } = $ ::mnname ;
$ addreq - > { node } = \ @ sn ;
$ addreq - > { noderange } = \ @ sn ;
$ addreq - > { arg } - > [ 0 ] = "-v" ;
$ addreq - > { arg } - > [ 1 ] = "-s" ;
$ addreq - > { arg } - > [ 2 ] = "-F" ;
$ addreq - > { arg } - > [ 3 ] = $ ::syncsnfile ;
$ addreq - > { command } - > [ 0 ] = $ cmd ;
$ addreq - > { cwd } - > [ 0 ] = $ req - > { cwd } - > [ 0 ] ;
$ addreq - > { env } = $ req - > { env } ;
& process_request ( $ addreq , $ callback , $ sub_req ) ;
if ( $ ::FAILED_NODES == 0 )
{
@ ::good_SN = @ sn ; # all servicenodes were sucessful
}
else
{
@ ::bad_SN = @ ::DCP_NODES_FAILED ;
# remove all failing nodes from the good list
my @ tmpgoodnodes ;
foreach my $ gnode ( @ ::good_SN ) {
if ( ! grep ( /$gnode/ , @ ::bad_SN ) ) # if not a bad node
{
push @ tmpgoodnodes , $ gnode ;
2009-12-04 14:48:50 +00:00
}
2012-12-24 14:23:27 +00:00
}
@ ::good_SN = @ tmpgoodnodes ;
}
2009-12-04 14:48:50 +00:00
} # end xdcp -F
else
{
# if other xdcp commands, and not pull function
# mk the directory on the SN to hold the files
# to be sent to the SN.
# build a command to update the service nodes
# change the destination to the tmp location on
# the service node
# hierarchical support for pull (TBD)
#make the needed directory on the service node
# create new directory for path on Service Node
# xdsh <sn> mkdir -p $SNdir
my $ frompath = $ req - > { arg } - > [ - 2 ] ;
$ ::SNpath = $ synfiledir ;
$ ::SNpath . = $ frompath ;
my $ SNdir ;
$ SNdir = dirname ( $ ::SNpath ) ; # get directory
2012-12-24 14:23:27 +00:00
my @ sn = ( ) ;
# build list of servicenodes
2009-12-04 14:48:50 +00:00
foreach my $ node ( @ snodes )
{
2011-01-05 15:29:59 +00:00
# handle multiple servicenodes for one node
my @ sn_list = split ',' , $ node ;
foreach my $ snode ( @ sn_list ) {
push @ sn , $ snode ;
}
2012-12-24 14:23:27 +00:00
}
@ ::good_SN = @ sn ; # initialize all good
# run the command to all servicenodes
# to make the directory under the temporary
# SNsyncfiledir to hold the files that will be
# sent to the service nodes
# xdsh <sn> mkdir -p <SNsyncfiledir>/$::SNpath
my $ addreq ;
$ addreq - > { '_xcatdest' } = $ ::mnname ;
$ addreq - > { node } = \ @ sn ;
$ addreq - > { noderange } = \ @ sn ;
$ addreq - > { arg } - > [ 0 ] = "-v" ;
$ addreq - > { arg } - > [ 1 ] = "mkdir " ;
$ addreq - > { arg } - > [ 2 ] = "-p " ;
$ addreq - > { arg } - > [ 3 ] = $ SNdir ;
$ addreq - > { command } - > [ 0 ] = 'xdsh' ;
$ addreq - > { cwd } - > [ 0 ] = $ req - > { cwd } - > [ 0 ] ;
$ addreq - > { env } = $ req - > { env } ;
& process_request ( $ addreq , $ callback , $ sub_req ) ;
if ( $ ::FAILED_NODES == 0 )
{
@ ::good_SN = @ sn ;
}
else
{
@ ::bad_SN = @ ::DCP_NODES_FAILED ;
# remove all failing nodes from the good list
my @ tmpgoodnodes ;
foreach my $ gnode ( @ ::good_SN ) {
if ( ! grep ( /$gnode/ , @ ::bad_SN ) ) # if not a bad node
{
push @ tmpgoodnodes , $ gnode ;
2009-12-04 14:48:50 +00:00
}
2012-12-24 14:23:27 +00:00
}
@ ::good_SN = @ tmpgoodnodes ;
}
2009-12-04 14:48:50 +00:00
# now xdcp file to the service node to the new
# tmp path
# for all the service nodes that are still good
2012-12-24 14:23:27 +00:00
my @ sn = @ ::good_SN ;
# copy the file to each good servicenode
# xdcp <sn> <file> <SNsyncfiledir/../file>
my $ addreq = dclone ( $ req ) ; # get original request
$ addreq - > { arg } - > [ - 1 ] = $ SNdir ; # change to tmppath on servicenode
$ addreq - > { '_xcatdest' } = $ ::mnname ;
$ addreq - > { node } = \ @ sn ;
$ addreq - > { noderange } = \ @ sn ;
& process_request ( $ addreq , $ callback , $ sub_req ) ;
if ( $ ::FAILED_NODES == 0 )
2009-12-04 14:48:50 +00:00
{
2012-12-24 14:23:27 +00:00
@ ::good_SN = @ sn ;
}
else
{
@ ::bad_SN = @ ::DCP_NODES_FAILED ;
# remove all failing nodes from the good list
my @ tmpgoodnodes ;
foreach my $ gnode ( @ ::good_SN ) {
if ( ! grep ( /$gnode/ , @ ::bad_SN ) ) # if not a bad node
{
push @ tmpgoodnodes , $ gnode ;
2009-12-04 14:48:50 +00:00
}
2012-12-24 14:23:27 +00:00
}
@ ::good_SN = @ tmpgoodnodes ;
}
2009-12-04 14:48:50 +00:00
}
2012-12-24 14:23:27 +00:00
# report bad service nodes
2009-12-04 14:48:50 +00:00
if ( @ ::bad_SN )
{
my $ rsp = { } ;
my $ badnodes ;
foreach my $ badnode ( @ ::bad_SN )
{
$ badnodes . = $ badnode ;
$ badnodes . = ", " ;
}
chop $ badnodes ;
my $ msg =
2010-06-09 18:15:16 +00:00
"\nThe following servicenodes: $badnodes have errors and cannot be updated\n Until the error is fixed, xdcp will not work to nodes serviced by these service nodes." ;
2009-12-04 14:48:50 +00:00
$ rsp - > { data } - > [ 0 ] = $ msg ;
xCAT::MsgUtils - > message ( "D" , $ rsp , $ callback ) ;
}
return ( 0 ) ;
}
2010-05-14 16:23:19 +00:00
#-------------------------------------------------------
= head3 process_servicenodes_xdsh
Build the xdsh command to send the - e file
The executable must be copied into /var/xc at / syncfiles , and then
the command modified so that the xdsh running on the SN will cp the file
from /var/xc at /syncfiles to the compute node / tmp directory and run it .
Return an array of servicenodes that do not have errors
Returns error code:
if = 0 , good return continue to process the
nodes .
if = 1 , global error need to quit
= cut
#-------------------------------------------------------
sub process_servicenodes_xdsh
{
my $ req = shift ;
my $ callback = shift ;
my $ sub_req = shift ;
my $ sn = shift ;
my $ snrange = shift ;
my $ synfiledir = shift ;
my @ snodes = @$ sn ;
my @ snoderange = @$ snrange ;
my $ args ;
$ ::RUNCMD_RC = 0 ;
my $ cmd = $ req - > { command } - > [ 0 ] ;
# if xdsh -e <executable> command, service nodes first need
# to be rsync with the executable file to the $synfiledir
if ( $ ::dshexecute )
{
if ( ! - f $ ::dshexecute )
{ # -e file does not exist, quit
my $ rsp = { } ;
2012-11-28 11:38:15 +00:00
$ rsp - > { error } - > [ 0 ] = "File:$::dshexecute does not exist." ;
2010-05-14 16:23:19 +00:00
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback , 1 ) ;
return ( 1 ) ; # process no service nodes
}
# xdcp the executable from the xdsh -e to the service node first
# change noderange to the service nodes
# sync to each SN and check for error
# if error do not add to good_SN array, add to bad_SN
# build a tmp syncfile with
# $::dshexecute -> $synfiledir . $::dshexecute
my $ tmpsyncfile = POSIX:: tmpnam . ".dsh" ;
my $ destination = $ synfiledir . $ ::dshexecute ;
open ( TMPFILE , "> $tmpsyncfile" )
or die "can not open file $tmpsyncfile" ;
print TMPFILE "$::dshexecute -> $destination\n" ;
close TMPFILE ;
chmod 0755 , $ tmpsyncfile ;
2013-01-04 17:25:19 +00:00
# build array of all service nodes , this is to cover pools where
# an entry might actually be a comma separated list
my @ sn = ( ) ;
2010-05-14 16:23:19 +00:00
foreach my $ node ( @ snodes )
{
2011-01-05 15:29:59 +00:00
# handle multiple servicenodes for one node
my @ sn_list = split ',' , $ node ;
foreach my $ snode ( @ sn_list ) {
push @ sn , $ snode ;
}
2010-05-14 16:23:19 +00:00
2013-01-04 17:25:19 +00:00
}
@ ::good_SN = @ sn ; # initialize all good
# sync the file to the SN /var/xcat/syncfiles directory
# (site.SNsyncfiledir)
# xdcp <sn> -s -F <tmpsyncfile>
# don't use runxcmd, because can go straight to process_request,
# these are all service nodes. Also servicenode is taken from
# the noderes table and may not be the same name as in the nodelist
# table, for example may be an ip address.
# here on the MN
my $ addreq ;
$ addreq - > { '_xcatdest' } = $ ::mnname ;
$ addreq - > { node } = \ @ sn ;
$ addreq - > { noderange } = \ @ sn ;
$ addreq - > { arg } - > [ 0 ] = "-v" ;
$ addreq - > { arg } - > [ 1 ] = "-s" ;
$ addreq - > { arg } - > [ 2 ] = "-F" ;
$ addreq - > { arg } - > [ 3 ] = $ tmpsyncfile ;
$ addreq - > { command } - > [ 0 ] = "xdcp" ;
$ addreq - > { cwd } - > [ 0 ] = $ req - > { cwd } - > [ 0 ] ;
$ addreq - > { env } = $ req - > { env } ;
& process_request ( $ addreq , $ callback , $ sub_req ) ;
if ( $ ::FAILED_NODES == 0 )
{
@ ::good_SN = @ sn ; # all servicenodes were sucessful
}
else
{
@ ::bad_SN = @ ::DCP_NODES_FAILED ;
# remove all failing nodes from the good list
my @ tmpgoodnodes ;
foreach my $ gnode ( @ ::good_SN ) {
if ( ! grep ( /$gnode/ , @ ::bad_SN ) ) # if not a bad node
2010-05-14 16:23:19 +00:00
{
2013-01-04 17:25:19 +00:00
push @ tmpgoodnodes , $ gnode ;
2010-05-14 16:23:19 +00:00
}
2013-01-04 17:25:19 +00:00
}
@ ::good_SN = @ tmpgoodnodes ;
}
2010-05-14 16:23:19 +00:00
# remove the tmp syncfile
`/bin/rm $tmpsyncfile` ;
} # end xdsh -E
# report bad service nodes]
if ( @ ::bad_SN )
{
my $ rsp = { } ;
my $ badnodes ;
foreach my $ badnode ( @ ::bad_SN )
{
$ badnodes . = $ badnode ;
$ badnodes . = ", " ;
}
chop $ badnodes ;
my $ msg =
"\nThe following servicenodes: $badnodes have errors and cannot be updated\n Until the error is fixed, xdsh -e will not work to nodes serviced by these service nodes. Run xdsh <servicenode,...> -c , to clean up the xdcp servicenode directory, and run the command again." ;
$ rsp - > { data } - > [ 0 ] = $ msg ;
xCAT::MsgUtils - > message ( "D" , $ rsp , $ callback ) ;
}
return ( 0 ) ;
}
2009-12-04 14:48:50 +00:00
#-------------------------------------------------------
= head3 process_nodes
Build the request to send to the nodes , serviced by SN
Return the request
= cut
#-------------------------------------------------------
sub process_nodes
{
my $ req = shift ;
my $ sn = shift ;
my $ snkey = shift ;
2010-05-14 16:23:19 +00:00
my $ synfiledir = shift ;
2009-12-04 14:48:50 +00:00
my $ command = $ req - > { command } - > [ 0 ] ;
my @ requests ;
2010-05-14 16:23:19 +00:00
# if the xdcp -F option to sync the nodes
2009-12-04 14:48:50 +00:00
# then for a Node
2012-08-01 13:28:25 +00:00
# change the command to use the -F syncfiledir path to the synclist
2009-12-04 14:48:50 +00:00
# because that is where the file was put on the SN
#
my $ newSNreq = dclone ( $ req ) ;
2012-08-01 13:28:25 +00:00
my $ newsyncfile = $ synfiledir ;
$ newsyncfile . = $ ::syncsnfile ;
2009-12-04 14:48:50 +00:00
if ( $ ::syncsnfile ) # -F option
{
my $ args = $ newSNreq - > { arg } ;
my $ i = 0 ;
foreach my $ argument ( @$ args )
{
# find the -F and change the name of the
2012-08-01 13:28:25 +00:00
# file in the next array entry to the file that
# is in the site.SNsyncfiledir
# directory on the service node
2009-12-04 14:48:50 +00:00
if ( $ argument eq "-F" )
{
$ i + + ;
2012-08-01 13:28:25 +00:00
$ newSNreq - > { arg } - > [ $ i ] = $ newsyncfile ;
2009-12-04 14:48:50 +00:00
last ;
}
$ i + + ;
}
}
2010-05-14 16:23:19 +00:00
2009-12-04 14:48:50 +00:00
else
{ # if other dcp command, change from directory
# to be the site.SNsyncfiledir
# directory on the service node
# if not pull (-P) pullfunction
# xdsh and xdcp pull just use the input request
if ( ( $ command eq "xdcp" ) && ( $ ::dcppull == 0 ) )
{
2012-06-28 15:51:50 +00:00
# have to change each file path and add the SNsynfiledir
# except the last entry which is the destination on the computenode
2012-08-07 13:25:52 +00:00
# skip flags
2012-06-28 15:51:50 +00:00
my $ args = $ newSNreq - > { arg } ;
my $ arraysize = @$ args ;
my $ i = 0 ;
2012-07-02 12:57:24 +00:00
foreach my $ sarg ( @$ args ) {
2012-06-28 15:51:50 +00:00
if ( $ arraysize > 1 ) {
2012-08-07 13:25:52 +00:00
if ( $ sarg =~ /^-/ ) { # just a flag, skip
$ arraysize - - ;
$ i + + ;
} else {
2012-07-02 12:57:24 +00:00
my $ tmpfile = $ synfiledir ;
$ tmpfile . = $ newSNreq - > { arg } - > [ $ i ] ;
$ newSNreq - > { arg } - > [ $ i ] = $ tmpfile ;
$ arraysize - - ;
$ i + + ;
}
2012-06-28 15:51:50 +00:00
} else {
last ;
}
}
2010-05-14 16:23:19 +00:00
} else { # if xdsh -e
if ( $ ::dshexecute ) { # put in new path from SN directory
my $ destination = $ synfiledir . $ ::dshexecute ;
my $ args = $ newSNreq - > { arg } ;
my $ i = 0 ;
foreach my $ argument ( @$ args )
{
# find the -e and change the name of the
# file in the next array entry to SN offset
if ( $ argument eq "-e" )
{
$ i + + ;
$ newSNreq - > { arg } - > [ $ i ] = $ destination ;
last ;
}
$ i + + ;
}
} # end if dshexecute
}
2009-12-04 14:48:50 +00:00
}
$ newSNreq - > { node } = $ sn - > { $ snkey } ;
$ newSNreq - > { '_xcatdest' } = $ snkey ;
$ newSNreq - > { _xcatpreprocessed } - > [ 0 ] = 1 ;
#push @requests, $newSNreq;
return $ newSNreq ;
}
#-------------------------------------------------------
2008-02-18 15:57:25 +00:00
= head3 process_request
2007-10-26 22:44:33 +00:00
Process the command
= cut
#-------------------------------------------------------
sub process_request
{
my $ request = shift ;
my $ callback = shift ;
2009-12-04 14:48:50 +00:00
my $ sub_req = shift ;
2011-01-18 15:39:55 +00:00
$ ::SUBREQ = $ sub_req ;
2009-12-04 14:48:50 +00:00
my $ nodes = $ request - > { node } ;
my $ command = $ request - > { command } - > [ 0 ] ;
my $ args = $ request - > { arg } ;
my $ envs = $ request - > { env } ;
my $ rsp = { } ;
2008-02-18 15:57:25 +00:00
# get the Environment Variables and set them in the current environment
2008-03-31 18:17:00 +00:00
foreach my $ envar ( @ { $ request - > { env } } )
{
my ( $ var , $ value ) = split ( /=/ , $ envar , 2 ) ;
$ ENV { $ var } = $ value ;
2007-12-12 13:24:36 +00:00
}
2012-08-23 18:20:17 +00:00
# if DSH_FROM_USERID does not exist, set for internal calls
2010-03-15 15:50:16 +00:00
# if request->{username} exists, set DSH_FROM_USERID to it
# override input, this is what was authenticated
2012-08-23 18:20:17 +00:00
if ( ! ( $ ENV { 'DSH_FROM_USERID' } ) ) {
if ( ( $ request - > { username } ) && defined ( $ request - > { username } - > [ 0 ] ) ) {
$ ENV { DSH_FROM_USERID } = $ request - > { username } - > [ 0 ] ;
}
2010-03-15 15:50:16 +00:00
}
2007-12-12 13:24:36 +00:00
if ( $ command eq "xdsh" )
{
xdsh ( $ nodes , $ args , $ callback , $ command , $ request - > { noderange } - > [ 0 ] ) ;
2007-10-26 22:44:33 +00:00
}
else
{
2007-12-12 13:24:36 +00:00
if ( $ command eq "xdcp" )
2007-10-26 22:44:33 +00:00
{
2007-12-12 13:24:36 +00:00
xdcp ( $ nodes , $ args , $ callback , $ command ,
$ request - > { noderange } - > [ 0 ] ) ;
2007-10-26 22:44:33 +00:00
}
else
2007-12-12 13:24:36 +00:00
{
2008-10-17 16:04:13 +00:00
my $ rsp = { } ;
2012-11-28 11:38:15 +00:00
$ rsp - > { error } - > [ 0 ] =
2008-02-18 15:57:25 +00:00
"Unknown command $command. Cannot process the command." ;
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback , 1 ) ;
return ;
2007-10-26 22:44:33 +00:00
}
}
}
#-------------------------------------------------------
2008-02-18 15:57:25 +00:00
= head3 xdsh
2007-10-26 22:44:33 +00:00
2008-02-18 15:57:25 +00:00
Parses Builds and runs the dsh
2007-10-26 22:44:33 +00:00
= cut
#-------------------------------------------------------
sub xdsh
{
2007-12-12 13:24:36 +00:00
my ( $ nodes , $ args , $ callback , $ command , $ noderange ) = @ _ ;
2008-03-31 18:17:00 +00:00
2012-12-24 14:23:27 +00:00
# parse dsh input, will return
$ ::FAILED_NODES = 0 ; # this is the count
# @::DSH_NODES_FAILED array of failing nodes.
2008-07-18 12:04:28 +00:00
my @ local_results =
2007-12-12 13:24:36 +00:00
xCAT::DSHCLI - > parse_and_run_dsh ( $ nodes , $ args , $ callback ,
$ command , $ noderange ) ;
2009-12-04 14:48:50 +00:00
my $ maxlines = 10000 ;
my $ arraylen = @ local_results ;
my $ rsp = { } ;
my $ i = 0 ;
2009-10-22 13:53:49 +00:00
my $ j ;
2009-12-04 14:48:50 +00:00
while ( $ i < $ arraylen )
{
for ( $ j = 0 ; $ j < $ maxlines ; $ j + + )
{
2012-09-05 17:22:11 +00:00
if ( $ i >= $ arraylen )
2009-12-04 14:48:50 +00:00
{
last ;
}
else
{
$ rsp - > { data } - > [ $ j ] = $ local_results [ $ i ] ; # send max lines
}
$ i + + ;
}
xCAT::MsgUtils - > message ( "D" , $ rsp , $ callback ) ;
2009-10-22 13:53:49 +00:00
}
2009-12-04 14:48:50 +00:00
2009-11-10 15:57:05 +00:00
# set return code
$ rsp = { } ;
2009-12-04 14:48:50 +00:00
$ rsp - > { errorcode } = $ ::FAILED_NODES ;
2009-11-10 15:57:05 +00:00
$ callback - > ( $ rsp ) ;
2009-12-04 14:48:50 +00:00
return ;
2007-12-12 13:24:36 +00:00
}
#-------------------------------------------------------
2008-02-18 15:57:25 +00:00
= head3 xdcp
2007-12-12 13:24:36 +00:00
2008-02-18 15:57:25 +00:00
Parses , Builds and runs the dcp command
2007-12-12 13:24:36 +00:00
= cut
#-------------------------------------------------------
sub xdcp
{
my ( $ nodes , $ args , $ callback , $ command , $ noderange ) = @ _ ;
2008-03-31 18:17:00 +00:00
2009-12-04 14:48:50 +00:00
2012-12-24 14:23:27 +00:00
# parse dcp input , run the command and return
$ ::FAILED_NODES = 0 ; # number of failing nodes
# @::DCP_NODES_FAILED array of failing nodes
2008-07-18 12:04:28 +00:00
my @ local_results =
2007-12-12 13:24:36 +00:00
xCAT::DSHCLI - > parse_and_run_dcp ( $ nodes , $ args , $ callback ,
$ command , $ noderange ) ;
2008-10-17 16:04:13 +00:00
my $ rsp = { } ;
my $ i = 0 ;
2007-12-12 13:24:36 +00:00
## process return data
2009-12-04 14:48:50 +00:00
if ( @ local_results )
2007-10-26 22:44:33 +00:00
{
2009-12-04 14:48:50 +00:00
foreach my $ line ( @ local_results )
{
$ rsp - > { data } - > [ $ i ] = $ line ;
$ i + + ;
}
2007-10-26 22:44:33 +00:00
2009-12-04 14:48:50 +00:00
xCAT::MsgUtils - > message ( "D" , $ rsp , $ callback ) ;
}
# set return code
$ rsp = { } ;
$ rsp - > { errorcode } = $ ::FAILED_NODES ;
$ callback - > ( $ rsp ) ;
2008-02-18 15:57:25 +00:00
return ;
2007-10-26 22:44:33 +00:00
}