#!/usr/bin/env perl -w # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html ##################################################### # # xCAT plugin package to handle commands that manage the xCAT object # definitions # ##################################################### package xCAT_plugin::FIP; use lib ("/opt/xcat/lib/perl"); use Data::Dumper; use Getopt::Long; use xCAT::MsgUtils; use strict; use xCAT::Utils; use xCAT::TableUtils; use xCAT::ServiceNodeUtils; # options can be bundled up like -vV Getopt::Long::Configure("bundling"); $Getopt::Long::ignorecase = 0; #---------------------------------------------------------------------------- =head3 handled_commands Return a list of commands handled by this plugin =cut #----------------------------------------------------------------------------- sub handled_commands { return { swapnodes => "FIP" }; } ########################################################################## # Pre-process request from xCat daemon. Send the request to the the service # nodes of the HCPs. ########################################################################## sub preprocess_request { my $req = shift; #if ($req->{_xcatdest}) { return [$req]; } #exit if preprocessed if ($req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; } my $callback = shift; my $subreq = shift; my @requests; # process the command line my $rc = &parse_args($req); if ($rc != 0) { &swapnodes_usage($callback); return -1; } ##################################### # Parse arguments ##################################### #my $opt = parse_args( $req, $callback); #if ( ref($opt) eq 'ARRAY' ) #{ # send_msg( $req, 1, @$opt ); # delete($req->{callback}); # if not, it will cause an error -- "Can't encode a value of type: CODE" in hierairchy. # return(1); #} #delete($req->{callback}); # remove 'callback' => sub { "DUMMY" } in hierairchy. #$req->{opt} = $opt; #if ( exists( $req->{opt}->{V} )) { # $req->{verbose} = 1; #} my $opt = $req->{opt}; my $current_node = $opt->{c}; my $fip_node = $opt->{f}; my @ppcattribs = ('hcp', 'id', 'pprofile', 'parent', 'supernode', 'comments', 'disable'); my $ppctab = xCAT::Table->new( 'ppc', -create=>1, -autocommit=>0); unless ($ppctab ) { my $rsp->{data}->[0] = "Cannot open ppc table"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } my ($current_ppc_ent) = $ppctab->getNodeAttribs($current_node,@ppcattribs); my ($fip_ppc_ent) = $ppctab->getNodeAttribs($fip_node,@ppcattribs); my @current_attrs; my @fip_attrs; if( $current_ppc_ent->{'parent'} ne $fip_ppc_ent->{'parent'} ) { my $reqcopy = {%$req}; push @requests, $reqcopy; return \@requests; } else { if($current_ppc_ent->{'hcp'} ne $fip_ppc_ent->{'hcp'} ) { $callback->({data=>["The two nodes are on the same CEC, but don't have the same hcp"]}); $req = {}; return; } # find service nodes for the HCPs # build an individual request for each service node my $service = "xcat"; my @hcps=[$current_ppc_ent->{'hcp'}]; my $sn = xCAT::ServiceNodeUtils->get_ServiceNode(\@hcps, $service, "MN"); #print Dumper($sn); if( keys(%$sn) == 0 ) { my $reqcopy = {%$req}; push @requests, $reqcopy; } # build each request for each service node foreach my $snkey (keys %$sn) { #$callback->({data=>["The service node $snkey "]}); my $reqcopy = {%$req}; $reqcopy->{'_xcatdest'} = $snkey; $reqcopy->{_xcatpreprocessed}->[0] = 1; #my $hcps1=$sn->{$snkey}; #my @nodes=(); #foreach (@$hcps1) { # push @nodes, @{$hcp_hash{$_}{nodes}}; #} #@nodes = sort @nodes; #my %hash = map{$_=>1} @nodes; #remove the repeated node for multiple hardware control points #@nodes =keys %hash; #$reqcopy->{node} = \@nodes; #print "nodes=@nodes\n"; push @requests, $reqcopy; } } #print Dumper(\@requests); return \@requests; } #---------------------------------------------------------------------------- =head3 process_request Check for xCAT command and call the appropriate subroutine. Arguments: Returns: 0 - OK 1 - error Globals: Error: Example: Comments: =cut #----------------------------------------------------------------------------- sub process_request { my $request = shift; my $callback = shift; my $ret; my $msg; my $command = $request->{command}->[0]; my $args = $request->{arg}; my $filedata = $request->{stdin}->[0]; # figure out which cmd and call the subroutine to process if ($command eq "swapnodes") { ($ret, $msg) = &swapnodes($request, $callback, $args); } my $rsp; if ($msg) { $rsp->{data}->[0] = $msg; $callback->($rsp); } if ($ret > 0) { $rsp->{errorcode}->[0] = $ret; } } #---------------------------------------------------------------------------- =head3 processArgs Process the command line. Covers all four commands. Also - Process any input files provided on cmd line. Arguments: Returns: 0 - OK 1 - just print usage 2 - error Globals: Error: Example: Comments: =cut #----------------------------------------------------------------------------- sub parse_args { my $request = shift; my $args = $request->{arg}; my $gotattrs = 0; my %opt =(); if (defined(@{$args})) { @ARGV = @{$args}; } else { return 2; } if (scalar(@ARGV) <= 0) { return 2; } # parse the options - include any option from all 4 cmds Getopt::Long::Configure("no_pass_through"); if ( !GetOptions( \%opt, qw(h|help v|version V|verbose c=s f=s o ) )) { return 2; } if ( exists( $opt{v} )) { return( \$::VERSION ); } if ( exists( $opt{h}) || $opt{help}) { return 2; } if ( (!exists( $opt{c})) ||(!exists( $opt{f})) ) { return 2; } $request->{opt} = \%opt; return 0; } #---------------------------------------------------------------------------- =head3 swapnodes Support for the xCAT chdef command. Arguments: Returns: 0 - OK 1 - error Globals: Error: Example: Comments: =cut #----------------------------------------------------------------------------- sub swapnodes { my $request = shift; my $callback = shift; my $args = shift; my $Rc = 0; my $error = 0; my $rsp; my $action; my $values; my $vals; my $opt = $request->{opt}; #print Dumper($opt); my $current_node = $opt->{c}; my $fip_node = $opt->{f}; #check the current_node and fip_node state, they should be in Not Activated state # get the ppc attributes for the two nodes #swap #update it my @ppcattribs = ('hcp', 'id', 'pprofile', 'parent', 'supernode', 'comments', 'disable'); my $ppctab = xCAT::Table->new( 'ppc', -create=>1, -autocommit=>0); unless ($ppctab ) { $rsp->{data}->[0] = "Cannot open ppc table"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } my ($current_ppc_ent) = $ppctab->getNodeAttribs( $current_node,@ppcattribs); my ($fip_ppc_ent) = $ppctab->getNodeAttribs( $fip_node,@ppcattribs); my @current_attrs; my @fip_attrs; my $cec; if( $current_ppc_ent->{'parent'} eq $fip_ppc_ent->{'parent'} ) { my %tabs; $tabs{ppc}=$ppctab; $tabs{vpd}= xCAT::Table->new( 'vpd', -create=>1, -autocommit=>0); unless ( !exists( $tabs{vpd} ) ) { $rsp->{data}->[0] = "Cannot open vpd table"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } xCAT::FSPUtils::getHcpAttribs($request, \%tabs); #the attributes of the current LPAR will be used for fsp_api_action push @current_attrs, $current_ppc_ent->{'id'}; push @current_attrs, "0"; push @current_attrs, $current_ppc_ent->{'parent'}; push @current_attrs, $current_ppc_ent->{'hcp'}; push @current_attrs, "lpar"; push @current_attrs, "0"; #the attributes of the current LPAR will be used for fsp_api_action push @fip_attrs, $fip_ppc_ent->{'id'}; push @fip_attrs, "0"; push @fip_attrs, $fip_ppc_ent->{'parent'}; push @fip_attrs, $fip_ppc_ent->{'hcp'}; push @fip_attrs, "lpar"; push @fip_attrs, "0"; #For the LPARs on the same CEC, we should swap the nodes' attributes and then assign the IO which are assigned to the current LPAR to the FIP LPAR. $cec = $current_ppc_ent->{'parent'}; my $type = "lpar"; $action = "all_lpars_state"; my $values = xCAT::FSPUtils::fsp_state_action ($request, $cec, \@current_attrs, $action); my $Rc = shift(@$values); if ( $Rc != 0 ) { $rsp->{data}->[0] = $$values[0]; xCAT::MsgUtils->message("E", $rsp, $callback); return -1; } foreach ( @$values ) { my ($state,$lparid) = split /,/; if( $lparid eq $current_ppc_ent->{'id'}) { if($state ne "Not Activated") { $rsp->{data}->[0] = "The two LPARs in one same CEC. Please make sure the two LPARs are in Not Activated state before swapping their location information"; xCAT::MsgUtils->message("E", $rsp, $callback); return -1; } } if( $lparid eq $fip_ppc_ent->{'id'}) { if($state ne "Not Activated") { $rsp->{data}->[0] = "The two LPARs in one same CEC. Please make sure the two LPARs are in Not Activated state before swapping their location information"; xCAT::MsgUtils->message("E", $rsp, $callback); return -1; } } } } my %keyhash = (); # swap the current ent and the fip ent $keyhash{'node'} = $current_node; $ppctab->setAttribs( \%keyhash,$fip_ppc_ent ); if( ! (exists($opt->{o}))) { $keyhash{'node'} = $fip_node; $ppctab->setAttribs( \%keyhash,$current_ppc_ent ); } $ppctab->commit; $ppctab->close(); #set the variables to be empty #$current_ent = (); #$fip_ent = (); %keyhash =(); # get the nodepos attributes for the two nodes #swap #update it my @nodeposattribs =('rack','u','chassis','slot','room','comments','disable'); my $nodepostab = xCAT::Table->new( 'nodepos', -create=>1, -autocommit=>0); unless ($nodepostab ) { $rsp->{data}->[0] = "Cannot open nodepos table"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } my $current_ent = $nodepostab->getNodeAttribs( $current_node,@nodeposattribs); my $fip_ent = $nodepostab->getNodeAttribs( $fip_node,@nodeposattribs); # swap the current ent and the fip ent $keyhash{'node'} = $current_node; $nodepostab->setAttribs( \%keyhash,$fip_ent ); if( ! (exists($opt->{o}))) { $keyhash{'node'} = $fip_node; $nodepostab->setAttribs( \%keyhash,$current_ent ); } $nodepostab->commit; $nodepostab->close(); # get the slots information from fsp, swap them, and then assign them to the others. #.... if( $current_ppc_ent->{'parent'} eq $fip_ppc_ent->{'parent'} ) { $action = "get_io_slot_info"; $values = xCAT::FSPUtils::fsp_api_action ($request,$cec, \@current_attrs, $action); #$Rc = shift(@$values); $Rc = pop(@$values); if ( $Rc != 0 ) { $rsp->{data}->[0] = $$values[1]; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } $action = "set_io_slot_owner"; my $tooltype = 0; my @data = split(/\n/, $$values[1]); foreach my $v (@data) { my ($lpar_id, $busid, $location, $drc_index, $owner_type, $owner, $descr) = split(/,/, $v); if( $lpar_id eq $current_ppc_ent->{'id'} ) { $vals = xCAT::FSPUtils::fsp_api_action ($request, $fip_node, \@fip_attrs, $action, $tooltype, $drc_index); $Rc = pop(@$vals); if ( $Rc != 0 ) { $rsp->{data}->[0] = $$vals[1]; xCAT::MsgUtils->message("E", $rsp, $callback); return -1; } } } } return ; } #---------------------------------------------------------------------------- =head3 swapnodes_usage Arguments: Returns: Globals: Error: Example: Comments: =cut #----------------------------------------------------------------------------- sub swapnodes_usage { my $callback = shift; my $rsp; $rsp->{data}->[0] = "\nUsage: swapnodes - swap the location info in the db between 2 nodes. If swapping within a cec, it will assign the IO adapters that were assigned to the defective node to the available node\n"; $rsp->{data}->[1] = " swapnodes [-h | --help ] \n"; $rsp->{data}->[2] = " swapnodes -c current_node -f fip_node [-o]"; xCAT::MsgUtils->message("I", $rsp, $callback); return 0; } 1;