2009-03-30 08:36:36 +00:00
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT::PPCboot ;
use strict ;
use Getopt::Long ;
use xCAT::PPCcli qw( SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR ) ;
use xCAT::Usage ;
2009-07-03 08:56:14 +00:00
use xCAT::Utils ;
use xCAT::MsgUtils ;
2009-03-30 08:36:36 +00:00
##########################################################################
# Parse the command line for options and operands
##########################################################################
sub parse_args {
my $ request = shift ;
my % opt = ( ) ;
my $ cmd = $ request - > { command } ;
my $ args = $ request - > { arg } ;
2009-06-02 03:05:05 +00:00
my @ VERSION = qw( 2.1 ) ;
2009-03-30 08:36:36 +00:00
#############################################
# Responds with usage statement
#############################################
local * usage = sub {
my $ usage_string = xCAT::Usage - > getUsage ( $ cmd ) ;
return ( [ $ _ [ 0 ] , $ usage_string ] ) ;
} ;
#############################################
# Process command-line arguments
#############################################
if ( ! defined ( $ args ) ) {
$ request - > { method } = $ cmd ;
return ( \ % opt ) ;
}
#############################################
# Checks case in GetOptions, allows opts
# to be grouped (e.g. -vx), and terminates
# at the first unrecognized option.
#############################################
@ ARGV = @$ args ;
$ Getopt:: Long:: ignorecase = 0 ;
Getopt::Long:: Configure ( "bundling" ) ;
2010-06-01 08:46:21 +00:00
if ( ! GetOptions ( \ % opt , qw( h|help V|Verbose v|version I|iscsiboot F f hfi o s=s m:s@ r=s t=s ) ) ) {
2009-03-30 08:36:36 +00:00
return ( usage ( ) ) ;
}
2009-05-11 04:13:35 +00:00
2009-06-02 03:05:05 +00:00
####################################
# Option -h for Help
####################################
if ( exists ( $ opt { h } ) ) {
return ( usage ( ) ) ;
}
####################################
# Option -v for version
####################################
if ( exists ( $ opt { v } ) ) {
return ( \ @ VERSION ) ;
}
2009-05-11 04:13:35 +00:00
if ( exists ( $ opt { s } ) ) {
my @ boot_devices = split ( /,/ , $ opt { s } ) ;
foreach ( @ boot_devices ) {
if ( ( ! /^net$/ ) && ( ! /^hd$/ ) ) {
return ( usage ( "boot device $_ is not supported" ) ) ;
}
}
}
2009-07-03 08:56:14 +00:00
if ( exists ( $ opt { m } ) ) {
my $ res = xCAT::Utils - > check_deployment_monitoring_settings ( $ request , \ % opt ) ;
if ( $ res != SUCCESS ) {
return ( usage ( ) ) ;
}
}
2009-03-30 08:36:36 +00:00
####################################
# Check for "-" with no option
####################################
if ( grep ( /^-$/ , @ ARGV ) ) {
return ( usage ( "Missing option: -" ) ) ;
}
2009-05-11 04:13:35 +00:00
2009-03-30 08:36:36 +00:00
####################################
# Check for an extra argument
####################################
if ( defined ( $ ARGV [ 0 ] ) ) {
return ( usage ( "Invalid Argument: $ARGV[0]" ) ) ;
}
$ request - > { method } = $ cmd ;
return ( \ % opt ) ;
}
##########################################################################
2009-04-20 05:03:07 +00:00
# Netboot the lpar
2009-03-30 08:36:36 +00:00
##########################################################################
2009-04-20 05:03:07 +00:00
sub do_rnetboot {
2009-03-30 08:36:36 +00:00
my $ request = shift ;
my $ d = shift ;
my $ exp = shift ;
my $ name = shift ;
my $ node = shift ;
my $ opt = shift ;
my $ ssh = @$ exp [ 0 ] ;
my $ userid = @$ exp [ 4 ] ;
my $ pw = @$ exp [ 5 ] ;
2010-05-11 06:22:28 +00:00
my $ subreq = $ request - > { subreq } ;
2009-03-30 08:36:36 +00:00
my $ cmd ;
my $ result ;
#######################################
# Disconnect Expect session
#######################################
xCAT::PPCcli:: disconnect ( $ exp ) ;
#######################################
# Get node data
#######################################
my $ id = @$ d [ 0 ] ;
my $ pprofile = @$ d [ 1 ] ;
my $ fsp = @$ d [ 2 ] ;
my $ hcp = @$ d [ 3 ] ;
#######################################
# Find Expect script
#######################################
$ cmd = ( $ ::XCATROOT ) ? "$::XCATROOT/sbin/" : "/opt/xcat/sbin/" ;
$ cmd . = "lpar_netboot.expect" ;
#######################################
# Check command installed
#######################################
if ( ! - x $ cmd ) {
return ( [ RC_ERROR , "Command not installed: $cmd" ] ) ;
}
2010-03-23 05:32:49 +00:00
if ( ! - x "/usr/bin/expect" ) {
return ( [ RC_ERROR , "Command not installed: /usr/bin/expect" ] ) ;
}
2009-03-30 08:36:36 +00:00
#######################################
2009-04-09 09:32:55 +00:00
# Save user name and passwd of hcp to
# environment variables.
# lpar_netboot.expect depends on it
2009-03-30 08:36:36 +00:00
#######################################
2009-04-09 09:32:55 +00:00
$ ENV { HCP_USERID } = $ userid ;
$ ENV { HCP_PASSWD } = $ pw ;
2009-03-30 08:36:36 +00:00
#######################################
# Turn on verbose and debugging
#######################################
if ( exists ( $ request - > { verbose } ) ) {
$ cmd . = " -v -x" ;
}
#######################################
# Force LPAR shutdown
#######################################
2009-08-19 07:05:13 +00:00
if ( exists ( $ opt - > { f } ) || ! xCAT::Utils - > isAIX ( ) ) {
2009-03-30 08:36:36 +00:00
$ cmd . = " -i" ;
2009-07-30 03:37:04 +00:00
}
2009-05-11 04:13:35 +00:00
#######################################
# Write boot order
#######################################
if ( exists ( $ opt - > { s } ) ) {
foreach ( $ opt - > { s } ) {
if ( /^net$/ ) {
$ cmd . = " -w 1" ;
} elsif ( /^net,hd$/ ) {
$ cmd . = " -w 2" ;
} elsif ( /^hd,net$/ ) {
$ cmd . = " -w 3" ;
} elsif ( /^hd$/ ) {
$ cmd . = " -w 4" ;
}
}
}
2010-05-27 01:43:39 +00:00
if ( exists ( $ opt - > { o } ) ) {
$ cmd . = " -o" ;
}
2010-06-17 11:51:25 +00:00
my $ Rc = SUCCESS ;
my @ macs = split /,/ , $ opt - > { m } ;
foreach my $ mac ( @ macs ) {
#######################################
# Network specified
#######################################
$ cmd . = " -s auto -d auto -m $mac -S $opt->{S} -G $opt->{G} -C $opt->{C} -N $opt->{N}" ;
2010-05-27 01:43:39 +00:00
2010-06-17 11:51:25 +00:00
#######################################
# Get required attributes from master
# of the node if -I|--iscsiboot is
# specified
#######################################
if ( exists ( $ opt - > { I } ) ) {
my $ ret ;
my $ dump_target ;
my $ dump_lun ;
my $ dump_port ;
my $ noderestab = xCAT::Table - > new ( 'noderes' ) ;
unless ( $ noderestab )
{
xCAT::MsgUtils - > message ( 'S' ,
2010-05-11 06:22:28 +00:00
"Unable to open noderes table.\n" ) ;
2010-06-17 11:51:25 +00:00
return 1 ;
}
my $ et = $ noderestab - > getNodeAttribs ( $ node , [ 'xcatmaster' ] ) ;
if ( $ et and $ et - > { 'xcatmaster' } )
2010-05-11 06:22:28 +00:00
{
2010-06-17 11:51:25 +00:00
$ ret = xCAT::Utils - > runxcmd (
{
command = > [ 'xdsh' ] ,
node = > [ $ et - > { 'xcatmaster' } ] ,
arg = > [ 'cat /tftpboot/$node.info' ]
} ,
$ subreq , 0 , 0 ) ;
} else {
$ ret = `cat /tftpboot/$node.info` ;
}
chomp ( $ ret ) ;
my @ attrs = split /\n/ , $ ret ;
foreach ( @ attrs )
{
if ( /DUMP_TARGET=(.*)$/ ) {
$ dump_target = $ 1 ;
} elsif ( /DUMP_LUN=(.*)$/ ) {
$ dump_lun = $ 1 ;
$ dump_lun =~ s/^0x(.*)$/$1/g ;
} elsif ( /DUMP_PORT=(.*)$/ ) {
$ dump_port = $ 1 ;
}
}
if ( defined ( $ dump_target ) and defined ( $ dump_lun ) and defined ( $ dump_port ) ) {
$ cmd . = " -T \"$dump_target\" -L \"$dump_lun\" -p \"$dump_port\"" ;
} else {
return ( [ RC_ERROR , "Unable to find DUMP_TARGET, DUMP_LUN, DUMP_PORT for iscsi dump" ] ) ;
2010-05-11 06:22:28 +00:00
}
}
2010-06-17 11:51:25 +00:00
if ( exists ( $ opt - > { hfi } ) ) {
$ cmd . = " -t hfi-ent" ;
2010-05-11 06:22:28 +00:00
} else {
2010-06-17 11:51:25 +00:00
$ cmd . = " -t ent" ;
2010-05-11 06:22:28 +00:00
}
2009-06-01 16:45:16 +00:00
#######################################
2010-06-17 11:51:25 +00:00
# Add command options
2009-06-01 16:45:16 +00:00
#######################################
2010-06-17 11:51:25 +00:00
$ cmd . = " -f \"$name\" \"$pprofile\" \"$fsp\" $id $hcp \"$node\"" ;
2009-06-01 16:45:16 +00:00
2010-06-17 11:51:25 +00:00
my $ done = 0 ;
while ( $ done < 2 ) {
#######################################
# Execute command
#######################################
my $ pid = open ( OUTPUT , "$cmd 2>&1 |" ) ;
$ SIG { INT } = $ SIG { TERM } = sub { #prepare to process job termination and propogate it down
kill 9 , $ pid ;
return ( [ RC_ERROR , "Received INT or TERM signal" ] ) ;
} ;
if ( ! $ pid ) {
return ( [ RC_ERROR , "$cmd fork error: $!" ] ) ;
}
#######################################
# Get command output
#######################################
while ( <OUTPUT> ) {
$ result . = $ _ ;
}
close OUTPUT ;
#######################################
# Get command exit code
#######################################
2009-03-30 08:36:36 +00:00
2010-06-17 11:51:25 +00:00
foreach ( split /\n/ , $ result ) {
if ( /^lpar_netboot: / ) {
$ Rc = RC_ERROR ;
last ;
}
2009-06-01 16:45:16 +00:00
}
2010-06-17 11:51:25 +00:00
if ( $ Rc == SUCCESS ) {
$ done = 2 ;
} else {
$ done = $ done + 1 ;
sleep 1 ;
}
}
2009-06-01 16:45:16 +00:00
if ( $ Rc == SUCCESS ) {
2010-06-17 11:51:25 +00:00
last ;
2009-03-30 08:36:36 +00:00
}
}
return ( [ $ Rc , $ result ] ) ;
}
##########################################################################
# Get LPAR MAC addresses
##########################################################################
sub rnetboot {
my $ request = shift ;
my $ d = shift ;
my $ exp = shift ;
my $ options = $ request - > { opt } ;
my $ hwtype = @$ exp [ 2 ] ;
my $ result ;
my $ name ;
2009-07-03 08:56:14 +00:00
my $ callback = $ request - > { callback } ;
2009-03-30 08:36:36 +00:00
#####################################
# Get node data
#####################################
my $ lparid = @$ d [ 0 ] ;
my $ mtms = @$ d [ 2 ] ;
my $ type = @$ d [ 4 ] ;
my $ node = @$ d [ 6 ] ;
my $ o = @$ d [ 7 ] ;
#####################################
# Gateway (-G)
# Server (-S)
# Client (-C)
# mac (-m)
#####################################
my % opt = (
G = > $ o - > { gateway } ,
S = > $ o - > { server } ,
C = > $ o - > { client } ,
2010-05-11 06:22:28 +00:00
N = > $ o - > { netmask } ,
2009-03-30 08:36:36 +00:00
m = > $ o - > { mac }
) ;
2010-05-11 06:22:28 +00:00
2009-03-30 08:36:36 +00:00
#####################################
# Strip colons from mac address
#####################################
$ opt { m } =~ s/://g ;
#####################################
# Force LPAR shutdown
#####################################
if ( exists ( $ options - > { f } ) ) {
$ opt { f } = 1 ;
}
2009-05-11 04:13:35 +00:00
#####################################
# Write boot device order
#####################################
if ( exists ( $ options - > { s } ) ) {
$ opt { s } = $ options - > { s } ;
}
2010-05-27 01:43:39 +00:00
if ( exists ( $ options - > { o } ) ) {
$ opt { o } = $ options - > { o } ;
}
2010-06-01 08:46:21 +00:00
if ( exists ( $ options - > { hfi } ) ) {
$ opt { hfi } = 1 ;
}
2010-05-11 06:22:28 +00:00
#####################################
# Do iscsi boot
#####################################
if ( exists ( $ options - > { I } ) ) {
$ opt { I } = 1 ;
}
2009-03-30 08:36:36 +00:00
#####################################
# Invalid target hardware
#####################################
if ( $ type !~ /^lpar$/ ) {
return ( [ [ $ name , "Not supported" , RC_ERROR ] ] ) ;
}
#########################################
# Get name known by HCP
#########################################
my $ filter = "name,lpar_id" ;
my $ values = xCAT::PPCcli:: lssyscfg ( $ exp , $ type , $ mtms , $ filter ) ;
my $ Rc = shift ( @$ values ) ;
#########################################
# Return error
#########################################
if ( $ Rc != SUCCESS ) {
return ( [ [ $ node , @$ values [ 0 ] , $ Rc ] ] ) ;
}
#########################################
# Find LPARs by lpar_id
#########################################
foreach ( @$ values ) {
if ( /^(.*),$lparid$/ ) {
$ name = $ 1 ;
last ;
}
}
#########################################
# Node not found by lpar_id
#########################################
if ( ! defined ( $ name ) ) {
return ( [ [ $ node , "Node not found, lparid=$lparid" , RC_ERROR ] ] ) ;
}
2009-05-27 07:52:18 +00:00
2009-07-30 03:37:04 +00:00
#########################################
# Check current node state.
# It is not allowed to rinitialize node
# if it is in boot state
#########################################
if ( ! exists ( $ options - > { F } ) && ! xCAT::Utils - > isAIX ( ) ) {
my $ chaintab = xCAT::Table - > new ( 'chain' ) ;
my $ vcon = $ chaintab - > getAttribs ( { node = > "$node" } , 'currstate' ) ;
if ( $ vcon and $ vcon - > { "currstate" } and $ vcon - > { "currstate" } eq "boot" ) {
2010-03-09 11:36:23 +00:00
return ( [ [ $ node , "Node is in boot state. Use nodeset command before rnetboot or use -F option with rnetboot" , RC_ERROR ] ] ) ;
2009-07-30 03:37:04 +00:00
}
}
2009-05-27 07:52:18 +00:00
my $ sitetab = xCAT::Table - > new ( 'site' ) ;
2009-07-30 03:37:04 +00:00
my $ vcon = $ sitetab - > getAttribs ( { key = > "conserverondemand" } , 'value' ) ;
2009-05-27 07:52:18 +00:00
if ( $ vcon and $ vcon - > { "value" } and $ vcon - > { "value" } eq "yes" ) {
$ result = xCAT::PPCcli:: lpar_netboot (
$ exp ,
$ request - > { verbose } ,
$ name ,
$ d ,
\ % opt ) ;
} else {
#########################################
# Manually perform boot.
#########################################
$ result = do_rnetboot ( $ request , $ d , $ exp , $ name , $ node , \ % opt ) ;
}
$ sitetab - > close ;
2009-07-03 08:56:14 +00:00
if ( defined ( $ request - > { opt } - > { m } ) ) {
my $ retries = 0 ;
my @ monnodes = ( $ name ) ;
my $ monsettings = xCAT::Utils - > generate_monsettings ( $ request , \ @ monnodes ) ;
xCAT::Utils - > monitor_installation ( $ request , $ monsettings ) ; ;
while ( $ retries + + < $ monsettings - > { 'retrycount' } && scalar ( keys % { $ monsettings - > { 'nodes' } } ) > 0 ) {
####lparnetboot can not support multiple nodes in one invocation
####for now, does not know how the $d and \%opt will be changed if
####support mulitiple nodes in one invocation,
####so just use the original node name and node attribute array and hash
my $ rsp = { } ;
$ rsp - > { data } - > [ 0 ] = "$node: Reinitializing the installation: $retries retry" ;
xCAT::MsgUtils - > message ( "I" , $ rsp , $ callback ) ;
if ( $ vcon and $ vcon - > { "value" } and $ vcon - > { "value" } eq "yes" ) {
$ result = xCAT::PPCcli:: lpar_netboot (
$ exp ,
$ request - > { verbose } ,
$ name ,
$ d ,
\ % opt ) ;
} else {
$ result = do_rnetboot ( $ request , $ d , $ exp , $ name , $ node , \ % opt ) ;
}
xCAT::Utils - > monitor_installation ( $ request , $ monsettings ) ;
}
#failed after retries
if ( scalar ( keys % { $ monsettings - > { 'nodes' } } ) > 0 ) {
foreach my $ node ( keys % { $ monsettings - > { 'nodes' } } ) {
my $ rsp = { } ;
$ rsp - > { data } - > [ 0 ] = "The node \"$node\" can not reach the expected status after $monsettings->{'retrycount'} retries, the installation for this done failed" ;
xCAT::MsgUtils - > message ( "E" , $ rsp , $ callback ) ;
}
}
}
2009-03-30 08:36:36 +00:00
$ Rc = shift ( @$ result ) ;
2009-05-27 07:52:18 +00:00
2009-03-30 08:36:36 +00:00
##################################
# Form string from array results
##################################
if ( exists ( $ request - > { verbose } ) ) {
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , join ( '' , @$ result ) , $ Rc ] ] ) ;
2009-03-30 08:36:36 +00:00
}
##################################
# Return error
# lpar_netboot returns (for example):
# # Connecting to lpar1
# # Connected
# # Checking for power off.
# # Power off the node
# # Wait for power off.
# # Power off complete.
# # Power on lpar1 to Open Firmware.
# # Power on complete.
# lpar_netboot: can not find mac address 42DAB.
#
##################################
if ( $ Rc != SUCCESS ) {
if ( @$ result [ 0 ] =~ /lpar_netboot: (.*)/ ) {
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , $ 1 , $ Rc ] ] ) ;
2009-03-30 08:36:36 +00:00
}
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , join ( '' , @$ result ) , $ Rc ] ] ) ;
2009-03-30 08:36:36 +00:00
}
##################################
# Split array into string
##################################
my $ data = @$ result [ 0 ] ;
if ( $ hwtype eq "hmc" ) {
$ data = join ( '' , @$ result ) ;
}
##################################
# lpar_netboot returns:
#
# # Connecting to lpar1
# # Connected
# ...
# lpar_netboot Status: network boot initiated
# # bootp sent over network.
# lpar_netboot Status: waiting for the boot image to boot up.
# # Network boot proceeding, lpar_netboot is exiting.
# # Finished.
#
#####################################
if ( $ data =~ /Finished/ ) {
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , "Success" , $ Rc ] ] ) ;
2009-03-30 08:36:36 +00:00
}
#####################################
# Can still be error w/ Rc=0:
#
# # Connecting to lpar1
# # Connected
# ...
# lpar_netboot Status: network boot initiated
# # bootp sent over network.
# lpar_netboot Status: waiting for the boot image to boot up.
# lpar_netboot: bootp operation failed.
#
#####################################
if ( $ data =~ /lpar_netboot: (.*)/ ) {
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , $ 1 , RC_ERROR ] ] ) ;
2009-03-30 08:36:36 +00:00
}
2009-04-20 05:03:07 +00:00
return ( [ [ $ node , $ data , RC_ERROR ] ] ) ;
2009-03-30 08:36:36 +00:00
}
1 ;