# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 xCAT plugin package to handle the snmove command =cut #------------------------------------------------------- package xCAT_plugin::snmove; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use Sys::Hostname; use File::Basename; use File::Path; use xCAT::Table; use xCAT::Utils; use xCAT::NetworkUtils; use xCAT::MsgUtils; use xCAT::SvrUtils; use Getopt::Long; use xCAT::NodeRange; #use Data::Dumper; 1; #------------------------------------------------------- =head3 handled_commands Return list of commands handled by this plugin =cut #------------------------------------------------------- sub handled_commands { return {snmove => "snmove",}; } #------------------------------------------------------- =head3 preprocess_request Preprocess the command =cut #------------------------------------------------------- sub preprocess_request { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; #if already preprocessed, go straight to process_request if ( (defined($request->{_xcatpreprocessed})) && ($request->{_xcatpreprocessed}->[0] == 1)) { return [$request]; } # let process_request handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; return [$reqcopy]; } #------------------------------------------------------- =head3 process_request Process the command =cut #------------------------------------------------------- sub process_request { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; my $error = 0; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); if ( !GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 's|source=s' => \$::SN1, # source SN akb MN 'S|sourcen=s' => \$::SN1N, # source SN akb node 'd|dest=s' => \$::SN2, # dest SN akb MN 'D|destn=s' => \$::SN2N, # dest SN akb node 'l|liteonly' => \$::SLonly, # update statelite only! 'P|postscripts=s' => \$::POST, # postscripts to be run 'i|ignorenodes' => \$::IGNORE, 'V|verbose' => \$::VERBOSE, ) ) { &usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } if (($::IGNORE) && ($::POST)) { my $rsp = {}; $rsp->{data}->[0] = "-P and -i flags cannot be specified at the same time.\n"; $callback->($rsp); return 1; } if (@ARGV > 1) { my $rsp = {}; $rsp->{data}->[0] = "Too many paramters.\n"; $callback->($rsp); &usage($callback); return 1; } if ((@ARGV == 0) && (!$::SN1)) { my $rsp = {}; $rsp->{data}->[0] = "A node range or the source service node must be specified.\n"; $callback->($rsp); &usage($callback); return 1; } my $rsp; push @{$rsp->{data}}, "Moving nodes to their backup service nodes.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); my $nimprime = xCAT::InstUtils->getnimprime(); chomp $nimprime; # # get the list of nodes # - either from the command line or by checking which nodes are # managed by the servicenode (SN1) # my @nodes = (); if (@ARGV == 1) { my $nr = $ARGV[0]; @nodes = noderange($nr); if (nodesmissed) { my $rsp = {}; $rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed); $callback->($rsp); return 1; } } else { # get all the nodes that use SN1 as the primary service nodes my $pn_hash = xCAT::Utils->getSNandNodes(); foreach my $snlist (keys %$pn_hash) { if (($snlist =~ /^$::SN1$/) || ($snlist =~ /^$::SN1\,/)) { push(@nodes, @{$pn_hash->{$snlist}}); } } } # # make sure all the nodes are resolvable # foreach my $n (@nodes) { my $packed_ip = xCAT::NetworkUtils->getipaddr($n); if (!$packed_ip) { my $rsp; $rsp->{data}->[0] = "Could not resolve node \'$n\'.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } } # # get the node object definitions # my %objtype; my %nodehash; foreach my $o (@nodes) { $objtype{$o} = 'node'; } my %nhash = xCAT::DBobjUtils->getobjdefs(\%objtype, $callback); if (!(%nhash)) { my $rsp; push @{$rsp->{data}}, "Could not get xCAT object definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } # are we dealing with AIX or Linux ? # can't use isAIX since the MN could be Linux in mixed cluster $::islinux = 0; $::isaix = 0; foreach my $node (@nodes) { if ($nhash{$node}{os} eq "AIX") { $::isaix++; } else { $::islinux++; } } if ($::islinux && $::isaix) { my $rsp; push @{$rsp->{data}}, "This command does not support a mix of AIX and Linux nodes.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } if ($::SLonly && $::islinux) { my $rsp; push @{$rsp->{data}}, "The '-l' option is not supported for Linux nodes.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } # # get the nimtype for AIX nodes (diskless or standalone) # my %nimtype; if ($::isaix) { # need to check the nimimage table to find the nimtype my $nimtab = xCAT::Table->new('nimimage', -create => 1); if ($nimtab) { foreach my $node (@nodes) { my $provmethod = $nhash{$node}{'provmethod'}; # get the nimtype my $ref = $nimtab->getAttribs({imagename => $provmethod}, 'nimtype'); if ($ref) { $nimtype{$node} = $ref->{'nimtype'}; } } } } # # get the backup sn for each node # my @servlist; # list of new service nodes my %newsn; my $nodehash; if ($::SN2) { # we have the backup for each node from cmd line foreach my $n (@nodes) { $newsn{$n} = $::SN2; } push(@servlist, $::SN2); } else { # check the 2nd value of the servicenode attr foreach my $node (@nodes) { if ($nhash{$node}{'servicenode'}) { my @sn = split(',', $nhash{$node}{'servicenode'}); # if ((scalar(@sn) > 2) && (xCAT::Utils->isAIX())) if ((scalar(@sn) > 2) && ($::isaix)) { my $rsp = {}; $rsp->{error}->[0] = "The service node attribute cannot have more than two values."; $callback->($rsp); } if ($sn[1]) { $newsn{$node} = $sn[1]; if (!grep(/^$sn[1]$/, @servlist)) { push(@servlist, $sn[1]); } } } if (!$newsn{$node}) { my $rsp = {}; $rsp->{error}->[0] = "Could not determine a backup service node for node $node."; $callback->($rsp); $error++; } } } if ($error) { return 1; } # # get the new xcatmaster for each node # my %newxcatmaster; if ($::SN2N) { # we have the xcatmaster for each node from cmd line foreach my $n (@nodes) { $newxcatmaster{$n} = $::SN2N; } } else { # try to calculate the xcatmaster value for each node # get all the interfaces from each SN # $sni{$SN}= list of ip my $s = &getSNinterfaces(\@servlist, $callback, $sub_req); my %sni = %$s; # get the network info for each node # $nethash{nodename}{networks attr name} = value my %nethash = xCAT::DBobjUtils->getNetwkInfo(\@nodes); # determine the xcatmaster value for the new SN foreach my $node (@nodes) { # get the node ip # or use getNodeIPaddress my $nodeIP = xCAT::NetworkUtils->getipaddr($node); chomp $nodeIP; # get the new SN for the node my $mySN = $newsn{$node}; # check each interface on the service node foreach my $IP (@{$sni{$mySN}}) { # if IP is in nodes subnet then thats the xcatmaster if ( xCAT::NetworkUtils->ishostinsubnet( $IP, $nethash{$node}{mask}, $nethash{$node}{net} ) ) { # get the short hostname my $xcatmaster = xCAT::NetworkUtils->gethostname($IP); $xcatmaster =~ s/\..*//; # add the value to the hash $newxcatmaster{$node} = $xcatmaster; last; } } if (!$newxcatmaster{$node}) { my $rsp = {}; $rsp->{error}->[0] = "Could not determine an xcatmaster value for node $node."; $callback->($rsp); $error++; } } } if ($error) { return 1; } # # determine the new node attribute values # my %sn_hash; my $old_node_hash = {}; my $index = 0; foreach my $node (@nodes) { my $sn1; my $sn1n; my $sn1n_ip; # get current xcatmaster if ($::SN1N) { # use command line value $sn1n = $::SN1N; } elsif ($nhash{$node}{'xcatmaster'}) { # use xcatmaster attr $sn1n = $nhash{$node}{'xcatmaster'}; } if ($sn1n) { my @ret = xCAT::Utils::toIP($sn1n); if ($ret[0]->[0] == 0) { $sn1n_ip = $ret[0]->[1]; } } # get the servicenode values my @sn_a; my $snlist = $nhash{$node}{'servicenode'}; @sn_a = split(',', $snlist); # get current servicenode if ($::SN1) { # current SN from the command line $sn1 = $::SN1; } else { # current SN from node attribute $sn1 = $sn_a[0]; } # switch the servicenode attr list my @sn_temp = grep(!/^$newsn{$node}$/, @sn_a); unshift(@sn_temp, $newsn{$node}); my $t = join(',', @sn_temp); $sn_hash{$node}{objtype} = 'node'; # set servicenode and xcatmaster attr $sn_hash{$node}{'servicenode'} = $t; $sn_hash{$node}{'xcatmaster'} = $newxcatmaster{$node}; $old_node_hash->{$node}->{'oldsn'} = $sn1; $old_node_hash->{$node}->{'oldmaster'} = $sn1n; # set tftpserver my $tftp = $nhash{$node}{'tftpserver'}; if ($tftp) { if ($sn1n && ($tftp eq $sn1n)) { $sn_hash{$node}{'tftpserver'} = $newxcatmaster{$node}; } elsif ($sn1n_ip && ($tftp eq $sn1n_ip)) { $sn_hash{$node}{'tftpserver'} = $newxcatmaster{$node}; } } # set nfsserver my $nfs = $nhash{$node}{'nfsserver'}; if ($nfs) { if ($sn1n && ($nfs eq $sn1n)) { $sn_hash{$node}{'nfsserver'} = $newxcatmaster{$node}; } elsif ($sn1n_ip && ($nfs eq $sn1n_ip)) { $sn_hash{$node}{'nfsserver'} = $newxcatmaster{$node}; } } #set monserver ( = "servicenode,xcatmaster" ) my $mon = $nhash{$node}{'monserver'}; if ($mon) # if it is currently set { my @tmp_a = split(',', $mon); if (scalar(@tmp_a) < 2) # it must have two values { my $rsp; push @{$rsp->{data}}, "The current value of the monserver attribute is not valid. It will not be reset.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } else { # if the first value is the current service node then change it if ($tmp_a[0] eq $sn1) { $sn_hash{$node}{'monserver'} = "$newsn{$node},$newxcatmaster{$node}"; } } } } # end - foreach node # check the sharedinstall attr my $sharedinstall=xCAT::Utils->get_site_attribute('sharedinstall'); chomp $sharedinstall; if (!$sharedinstall) { $sharedinstall="no"; } # # handle the statelite update for the sharedinstall=sns case # - using a shared file system across all service nodes # if ( ($::isaix) && ($sharedinstall eq "sns") ){ my $s = &sfsSLconfig(\@nodes, \%nhash, \%sn_hash, $nimprime, $callback, $sub_req); } # TBD - handle sharedinstall =all case ???? # handle the statelite update for sharedinstall=no # - not using a shared files system my %SLmodhash; my %LTmodhash; if ( ($::isaix) && ($sharedinstall eq "no") ) { # # try to rsync statelite dirs from old SN to new SN # - only if old SN is listed in the tables! # if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "Attempting the synchronization of statelite files.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } # # handle statelite table # my $statetab = xCAT::Table->new('statelite', -create => 1); # get hash of entries??? my $recs = $statetab->getAllEntries; my $statemnt; my $server; my $dir; my $item = 0; my $id = 0; my %donehash; # for each entry foreach my $line (@$recs) { $statemnt = $line->{statemnt}; ($server, $dir) = split(/:/, $statemnt); # see what nodes this entry applies to my @nodeattr = &noderange($line->{node}, 0); chomp $server; my @donelist; # list of indices of old/new SN pairs # the server and dir could potentially be different for each node foreach my $n (@nodes) { # if the node is not in the noderange for this # entry then skip it if (!grep(/$n/, @nodeattr)) { next; } # check for the server if (grep /\$/, $server) { my $serv = xCAT::SvrUtils->subVars($server, $n, 'server', $callback); $server = $serv; # note: if a variable is used in the entry then it # does not have to be updated. } else { # if the $server value was the old SN hostname # then we need to # update the statelite table with the new SN name $item++; my $stmnt = "$sn_hash{$n}{'xcatmaster'}:$dir"; $SLmodhash{$item}{'statemnt'} = $stmnt; $SLmodhash{$item}{'node'} = $n; } # check for the directory if (grep /\$|#CMD/, $dir) { $dir = xCAT::SvrUtils->subVars($dir, $n, 'dir', $callback); $dir =~ s/\/\//\//g; } # we only want to sync the subdir for this node # ex. if dir = /nodedata then we sync /nodedata/compute03 my $dodir; my $shorthost; # just to be sure we have the short name ($shorthost = $n) =~ s/\..*$//; if ($dir =~ /\/$/) { $dodir = "$dir$shorthost"; } else { $dodir = "$dir/$shorthost"; } # see if the server in the table matches the nodes SN if ($server eq $old_node_hash->{$n}->{'oldmaster'}) { # see if we did this sync already my $foundit = 0; foreach my $i (keys %donehash) { # if the server and dir are the same then # we already did it if ( ($dodir eq $donehash{$i}{dir}) && ($server eq $donehash{$i}{oldXM}) && ($donehash{$i}{newXM} eq $sn_hash{$n}{'xcatmaster'}) ) { $foundit++; } } # ok - just skip to the next node if ($foundit) { next; } if ( -d $dodir ) { if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "Synchronizing $old_node_hash->{$n}->{'oldmaster'}:$dir to $sn_hash{$n}{'xcatmaster'}\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } my $todir = dirname($dodir); # do rsync of file/dir my $synccmd = qq~/usr/bin/rsync -arlHpEAogDz $dodir $newsn{$n}:$todir 2>&1~; if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "On $old_node_hash->{$n}->{'oldsn'}: Running: \'$synccmd\'\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } my $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $old_node_hash->{$n}->{'oldsn'}, $synccmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not sync statelite \'$dodir\'."; push @{$rsp->{data}}, "$output\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; } else { $id++; $donehash{$id}{oldXM} = $old_node_hash->{$n}->{'oldmaster'}; $donehash{$id}{dir} = $dodir; $donehash{$id}{newXM} = $sn_hash{$n}{'xcatmaster'}; } } # end if dodir exists } # end if servers match } # end - foreach node } # end for each line in statelite table # done with statelite table $statetab->close(); if ($error) { return 1; } # if only statelite sync is required then return now if ($::SLonly) { return 0; } } # end sync statelite and litetree entries my $rsp; push @{$rsp->{data}}, "Setting new values in the xCAT database.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); # # make updates to statelite table # if ( ($::isaix) && ($sharedinstall eq "no") ) { my $statetab = xCAT::Table->new('statelite', -create => 1); # for each key in SLmodhash - update the statelite table foreach my $item (keys %SLmodhash) { my $node = $SLmodhash{$item}{'node'}; my $statemnt = $SLmodhash{$item}{'statemnt'}; $statetab->setAttribs({'node' => $node}, {'statemnt' => $statemnt}); } # done with statelite table $statetab->close(); } # update the node definitions #1 if (keys(%sn_hash) > 0) { # update the node definition if (xCAT::DBobjUtils->setobjdefs(\%sn_hash) != 0) { my $rsp; push @{$rsp->{data}}, "Could not update xCAT node definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $::callback); $error++; } } # # handle conserver # my %sn_hash1; foreach my $node (@nodes) { if ( ($nhash{$node}{'conserver'}) and ($nhash{$node}{'conserver'} eq $old_node_hash->{$node}->{'oldsn'})) { $sn_hash1{$node}{'conserver'} = $newsn{$node}; $sn_hash1{$node}{objtype} = 'node'; } } # update the node definition #2 if (keys(%sn_hash1) > 0) { if (xCAT::DBobjUtils->setobjdefs(\%sn_hash1) != 0) { my $rsp; push @{$rsp->{data}}, "Could not update xCAT node definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $::callback); $error++; } } # run makeconservercf my @nodes_con = keys(%sn_hash1); if (@nodes_con > 0) { my $rsp = {}; $rsp->{data}->[0] = "Running makeconservercf " . join(',', @nodes_con); $callback->($rsp); my $ret = xCAT::Utils->runxcmd( { command => ['makeconservercf'], node => \@nodes_con, }, $sub_req, 0, 1 ); $callback->({data => $ret}); } # # restore .client_data files on the new SN # if ( ($::isaix) && ($sharedinstall eq "sns") ){ # first get the shared_root locations for each SN and osimage my $nimtab = xCAT::Table->new('nimimage'); my %SRloc; foreach my $n (@nodes) { my $osimage = $nhash{$n}{'provmethod'}; my ($sn, $junk) = split(/,/, $nhash{$n}{'servicenode'}); # $sn is name of SN as known by management node if (!$SRloc{$sn}{$osimage}) { my $SRn = $nimtab->getAttribs({'imagename' => $osimage}, 'shared_root'); my $SRname=$SRn->{shared_root}; if ($SRname) { my $srloc = xCAT::InstUtils->get_nim_attr_val($SRname, 'location', $callback, $nimprime, $sub_req); $SRloc{$sn}{$osimage}=$srloc; } } } $nimtab->close(); # now try to restore any backup client data # for each service node foreach my $s (keys %SRloc) { # for each osimage on that SN foreach my $osi (keys %{$SRloc{$s}}) { # set the names of the .client_data and backup directories my $sloc = $SRloc{$s}{$osi}; # ex. /install/nim/shared_root/71Bdskls_shared_root my $cdloc = "$sloc/etc/.client_data"; my $snbk = "$s" . "_" . "$osi"; my $bkloc = "$sloc/$snbk/.client_data"; my $ccmd=qq~/usr/bin/cp -r -p $bkloc/* $cdloc 2>/dev/null~; my $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $s, $ccmd, 0); if ($::RUNCMD_RC != 0) { if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "Could not copy $bkloc on $s.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } } } } } # # - retarget the iscsi dump device to the new server for the nodes # if ((!$::IGNORE) && ($::isaix) && ($sharedinstall eq "sns")) { if (&dump_retarget($callback, \@nodes, $sub_req) != 0) { my $rsp; push @{$rsp->{data}}, "One or more errors occured while attemping to re-target the dump device on cluster nodes.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; } } # # Run niminit on AIX diskful nodes # if (!$::IGNORE) # unless the user does not want us to touch the node { if ($::isaix) { #if the node is aix and the type is standalone foreach my $node (@nodes) { # if this is a standalone node then run niminit if (($nimtype{$node}) && ($nimtype{$node} eq 'standalone')) { if ($::VERBOSE) { my $rsp; push @{$rsp->{data}},"Running niminit on $node.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } my $nimcmd = qq~/usr/sbin/niminit -a name=$node -a master=$newsn{$node} >/dev/null 2>&1~; my $out = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $node, $nimcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run niminit on node $node.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; } } } } } # for Linux system only if ($::islinux) { #tftp, dhcp and nfs (site.disjointdhcps should be set to 1) # get a list of nodes for each provmethod my %nodeset_hash; foreach my $node (@nodes) { my $provmethod = $nhash{$node}{'provmethod'}; if ($provmethod) { if (!grep(/^$node$/, @{$nodeset_hash{$provmethod}})) { push(@{$nodeset_hash{$provmethod}}, $node); } } } # run the nodeset command foreach my $provmethod (keys(%nodeset_hash)) { # need a node list to send to nodeset my @nodeset_nodes = @{$nodeset_hash{$provmethod}}; if ( ($provmethod eq 'netboot') || ($provmethod eq 'install') || ($provmethod eq 'statelite')) { my $ret = xCAT::Utils->runxcmd( { command => ['nodeset'], node => \@nodeset_nodes, arg => [$provmethod], }, $sub_req, 0, 1 ); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run the nodeset command.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; } } else { my $ret = xCAT::Utils->runxcmd( { command => ['nodeset'], node => \@nodeset_nodes, arg => ["osimage=$provmethod"], }, $sub_req, 0, 1 ); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run the nodeset command.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; } } } } # end - for Linux system only # # update the /etc/xcatinfo files on the nodes # switch to the new server name # if (!$::IGNORE) { if ($::isaix) { if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "Updating the /etc/xcatinfo files.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } foreach my $node (@nodes) { # may need to reorg this # could organized into set of nodes for each new xcatmaster # then run runxcmd for that set of nodes my $IP = xCAT::NetworkUtils->getipaddr($newxcatmaster{$node}); chomp $IP; my $cmd = qq~echo "XCATSERVER=$IP" > /etc/xcatinfo~; my @nlist; push(@nlist, $node); my $ret = xCAT::Utils->runxcmd( { command => ['xdsh'], node => \@nlist, arg => ["$cmd"], }, $sub_req, 0, 1 ); if ($::RUNCMD_RC != 0) { $error++; } } } # end if isaix } # end of not ignore if (!$::IGNORE) { # # for both AIX and Linux systems # setup the default gateway if the network.gateway=xcatmaster # for the node # my %nethash; my %ipmap = (); my %gwhash = (); my $nwtab = xCAT::Table->new("networks"); if ($nwtab) { my @tmp1 = $nwtab->getAllAttribs(('net', 'mask', 'gateway', 'mgtifname')); if (@tmp1 && (@tmp1 > 0)) { foreach my $nwitem (@tmp1) { my $gw = $nwitem->{'gateway'}; if (!$gw) { next; } chomp $gw; if ($gw ne '') { next; } #now only handle the networks that has # as the gateway my $NM = $nwitem->{'mask'}; my $net = $nwitem->{'net'}; my $ifname = $nwitem->{'mgtifname'}; chomp $NM; chomp $net; chomp $ifname; # for each node - get the network info foreach my $node (@nodes) { # get, check, split the node IP my $IP = xCAT::NetworkUtils->getipaddr($node); chomp $IP; # check the entries of the networks table # - if the bitwise AND of the IP and the netmask # gives you # the "net" name then that is the entry you want. if (xCAT::NetworkUtils->ishostinsubnet($IP, $NM, $net)) { my $newmaster = $newxcatmaster{$node}; my $newmasterIP; if (exists($ipmap{$newmaster})) { $newmasterIP = $ipmap{$newmaster}; } else { $newmasterIP = xCAT::NetworkUtils->getipaddr($newmaster); chomp($newmasterIP); $ipmap{$newmaster} = $newmasterIP; } $nethash{$node}{'gateway'} = $newmasterIP; $nethash{$node}{'net'} = $net; $nethash{$node}{'mask'} = $NM; $nethash{$node}{'mgtifname'} = $ifname; if ($newmasterIP) { if (exists($gwhash{$newmasterIP})) { my $pa = $gwhash{$newmasterIP}; push(@$pa, $node); } else { $gwhash{$newmasterIP} = [$node]; } } } } } } } if (keys(%gwhash) > 0) { my $rsp; $rsp->{data}->[0] = "Setting up the default routes on the nodes."; xCAT::MsgUtils->message("I", $rsp, $callback); } foreach my $gw (keys %gwhash) { my $cmd = "route add default gw" ; #this is temporary,TODO, set perminant route on the nodes. # if (xCAT::Utils->isAIX()) if ($::isaix) { $cmd = "route add default"; } my $ret = xCAT::Utils->runxcmd( { command => ['xdsh'], node => $gwhash{$gw}, arg => ["-v", "$cmd $gw"], }, $sub_req, -1, 1 ); if ($::RUNCMD_RC != 0) { $error++; } my $rsp; $rsp->{data} = $ret; xCAT::MsgUtils->message("I", $rsp, $callback); } } # run postscripts to take care of syslog, ntp, and mkresolvconf # - if they are included in the postscripts table if (!$::IGNORE) # unless the user does not want us to touch the node { # get all the postscripts that should be run for the nodes my $pstab = xCAT::Table->new('postscripts', -create => 1); my $nodeposhash = {}; if ($pstab) { $nodeposhash = $pstab->getNodesAttribs(\@nodes, ['postscripts', 'postbootscripts']); } else { my $rsp = {}; $rsp->{error}->[0] = "Cannot open postscripts table.\n"; $callback->($rsp); return 1; } my $et = $pstab->getAttribs({node => "xcatdefaults"}, 'postscripts', 'postbootscripts'); my $defscripts = ""; my $defbootscripts = ""; if ($et) { $defscripts = $et->{'postscripts'}; $defbootscripts = $et->{'postbootscripts'}; } my $user_posts; if ($::POST) { $user_posts = $::POST; } my $pos_hash = {}; foreach my $node (@nodes) { foreach my $rec (@{$nodeposhash->{$node}}) { my $scripts; if ($rec) { $scripts = join(',', $defscripts, $rec->{'postscripts'}, $defbootscripts, $rec->{'postbootscripts'}); } else { $scripts = join(',', $defscripts, $defbootscripts); } my @tmp_a = split(',', $scripts); # xCAT's default scripts to be run: syslog, # setupntp, and mkresolvconf my @valid_scripts = ("syslog", "setupntp", "mkresolvconf"); my $scripts1 = ""; if (($user_posts) && ($user_posts eq "all")) { $scripts1 = $scripts; #run all the postscripts defined in the postscripts table } else { foreach my $s (@valid_scripts) { # if it was included in the original list then run it if (grep(/^$s$/, @tmp_a)) { if ($scripts1) { $scripts1 = "$scripts1,$s"; } else { $scripts1 = $s; } } } #append the user given scripts if ($user_posts) { if ($scripts1) { $scripts1 = "$scripts1,$user_posts"; } else { $scripts1 = $user_posts; } } } if ($scripts1) { if (exists($pos_hash->{$scripts1})) { my $pa = $pos_hash->{$scripts1}; push(@$pa, $node); } else { $pos_hash->{$scripts1} = [$node]; } } } } my $rsp; $rsp->{data}->[0] = "Running postscripts on the nodes."; xCAT::MsgUtils->message("I", $rsp, $callback); foreach my $scripts (keys(%$pos_hash)) { my $pos_nodes = $pos_hash->{$scripts}; my $ret = xCAT::Utils->runxcmd( { command => ['updatenode'], node => $pos_nodes, arg => ["-P", "$scripts", "-s"], }, $sub_req, -1, 1 ); if ($::RUNCMD_RC != 0) { $error++; } my $rsp; $rsp->{data} = $ret; xCAT::MsgUtils->message("I", $rsp, $callback); } } # end -for both AIX and Linux systems my $retcode = 0; if ($error) { #my $rsp; #push @{$rsp->{data}}, "One or more errors occurred while attempting to switch nodes to a new service node.\n"; #xCAT::MsgUtils->message("E", $rsp, $callback); $retcode = 1; } #else #{ # my $rsp; # push @{$rsp->{data}}, "The nodes were successfully moved to the new service node.\n"; # xCAT::MsgUtils->message("I", $rsp, $callback); #} return $retcode; } #---------------------------------------------------------------------------- =head3 getSNinterfaces Get a list of ip addresses for each service node in a list Arguments: list of servcie nodes Returns: 1 - could not get list of ips 0 - ok Globals: none Error: none Example: my $sni = xCAT::InstUtils->getSNinterfaces(\@servlist); Comments: none =cut #----------------------------------------------------------------------------- sub getSNinterfaces { my ($list, $callback, $sub_req) = @_; my @snlist = @$list; my %SNinterfaces; # get all the possible IPs for the node I'm running on my $ifcmd; # if (xCAT::Utils->isAIX()) if ($::isaix) { $ifcmd = "/usr/sbin/ifconfig -a "; } else { $ifcmd = "/sbin/ip addr "; } foreach my $sn (@snlist) { my $SNIP; my $result = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $sn, $ifcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp = {}; $rsp->{data}->[0] = "Could not get IP addresses from service node $sn.\n"; $callback->($rsp); next; } foreach my $int (split(/\n/, $result)) { if (!grep(/inet/, $int)) { # only want line with "inet" next; } $int =~ s/$sn:\s+//; # skip hostname from xdsh output my @elems = split(/\s+/, $int); if (xCAT::Utils->isLinux()) { if ($elems[0] eq 'inet6') { #Linux IPv6 TODO, do not return IPv6 networks on # Linux for now next; } ($SNIP, my $mask) = split /\//, $elems[1]; } else { # for AIX if ($elems[0] eq 'inet6') { $SNIP = $elems[1]; $SNIP =~ s/\/.*//; # ipv6 address 4000::99/64 $SNIP =~ s/\%.*//; # ipv6 address ::1%1/128 } else { $SNIP = $elems[1]; } } chomp $SNIP; push(@{$SNinterfaces{$sn}}, $SNIP); } } return \%SNinterfaces; } #----------------------------------------------------------------------------- sub usage { my $cb = shift; my $rsp = {}; push @{$rsp->{data}}, "\nsnmove - Move xCAT compute nodes from one xCAT service node to a \nbackup service node."; push @{$rsp->{data}}, "\nUsage: "; push @{$rsp->{data}}, "\tsnmove [-h | --help ]\n"; push @{$rsp->{data}}, "\tsnmove noderange [-V] [-l|--liteonly] [[-d|--dest] sn2]\n\t\t[[-D|--destn] sn2n] [-i|--ignorenodes] \n\t\t[[-P|--postscripts] script1,script2...|all]\n"; push @{$rsp->{data}}, "\tsnmove [-V] [-l|--liteonly] -s|--source sn1 [[-S|--sourcen] sn1n]\n\t\t[[-d|--dest] sn2] [[-D|--dest ] sn2n]\n\t\t[-i|--ignorenodes][[-P|--postscripts] script1,script2...|all]"; push @{$rsp->{data}}, "\n"; push @{$rsp->{data}}, "\nWhere:"; push @{$rsp->{data}}, "\tsn1 is the hostname of the source service node as known by (facing) the management node."; push @{$rsp->{data}}, "\tsn1n is the hostname of the source service node as known by (facing) the nodes."; push @{$rsp->{data}}, "\tsn2 is the hostname of the destination service node as known by (facing) the management node."; push @{$rsp->{data}}, "\tsn2n is the hostname of the destination service node as known by (facing) the nodes."; push @{$rsp->{data}}, "\tscripts is a comma separated list of postscripts to be run on the nodes. 'all' means all the scripts defined in the postscripts table for each node are to be run."; $cb->($rsp); return 0; } #---------------------------------------------------------------------------- =head3 dump_retarget Switches the iscsi dump target of nodes to a backup service node. Arguments: Returns: 0 - OK 1 - error Usage: $ret = &dump_retarget($callback, \@nodelist, $sub_req); =cut #----------------------------------------------------------------------------- sub dump_retarget { my $callback = shift; my $nodelist = shift; my $sub_req = shift; my @nodes = @$nodelist; my $error; #my $rsp; #push @{$rsp->{data}}, "Re-targetting dump devices for:\n\'@nodes\'\n"; #xCAT::MsgUtils->message("I", $rsp, $callback); # get provmethod and xcatmaster for each node my $nrtab = xCAT::Table->new('noderes'); my $nttab = xCAT::Table->new('nodetype'); my $nrhash; my $nthash; if ($nrtab) { $nrhash = $nrtab->getNodesAttribs(\@nodes, ['xcatmaster', 'servicenode']); } else { my $rsp = {}; $rsp->{data}->[0] = "Can not open noderes table.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } if ($nttab) { $nthash = $nttab->getNodesAttribs(\@nodes, ['provmethod']); } else { my $rsp = {}; $rsp->{data}->[0] = "Can not open nodetype table.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } # get the network info for each node # $nethash{nodename}{networks attr name} = value my %nethash = xCAT::DBobjUtils->getNetwkInfo(\@nodes); # get a list of nodes for each SNs and osimage combo # - also a list of osimages. my %SNosinodes; my @image_names; my %SNname; foreach my $node (@nodes) { my $xmast = $nrhash->{$node}->[0]->{'xcatmaster'}; my ($snode, $junk) = (split /,/, $nrhash->{$node}->[0]->{'servicenode'}); my $osimage = $nthash->{$node}->[0]->{'provmethod'}; push(@{$SNosinodes{$xmast}{$osimage}}, $node); if (!grep(/^$osimage$/, @image_names) ) { push(@image_names, $osimage); } $SNname{$xmast}=$snode; } # # get the image defs from the DB # my %imghash; my %objtype; # for each image foreach my $m (@image_names) { $objtype{$m} = 'osimage'; } my %imghash = xCAT::DBobjUtils->getobjdefs(\%objtype, $callback); if (!(%imghash)) { my $rsp; push @{$rsp->{data}}, "Could not get xCAT osimage definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } # set the default port - todo - user could have set differently??? my $dump_port=32600; # for each SN foreach my $sn (keys %SNosinodes) { # get ip addr of SN as known by the node # - sn is "xcatmaster" chomp $sn; my $SNip = xCAT::NetworkUtils->getipaddr($sn); # this is "servicenode" value - get first in list my ($xcatSNname, $junk) = (split /,/, $SNname{$sn}); # for each osimage needed for this SN foreach my $osi (keys %{$SNosinodes{$sn}}) { if (!$imghash{$osi}{'dump'}) { next; } # get dump target and lun from nim dump res def my %nimattrs; my $dump_target; my $dump_lunid; my @attrs = ("dump_target", "dump_lunid"); my $na = &getnimattr($imghash{$osi}{'dump'}, \@attrs, $callback, $xcatSNname, $sub_req); if ($na) { %nimattrs = %{$na}; $dump_target = $nimattrs{dump_target}; $dump_lunid = $nimattrs{dump_lunid}; } my $configdump; if ($imghash{$osi}{'configdump'}) { $configdump = $imghash{$osi}{'configdump'}; } else { $configdump = "selective"; } if ($::VERBOSE) { # print values ?? # or cmd?? } if (!$dump_target || !$dump_port || !$SNip || !$dump_lunid) { my $rsp; push @{$rsp->{data}}, "Could not re-target the dump device for the following nodes. \n@{$SNosinodes{$sn}{$osi}}\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } my @nodelist = @{$SNosinodes{$sn}{$osi}}; foreach my $nd (@nodelist) { chomp $nd; my $Nodeip = xCAT::NetworkUtils->getipaddr($nd); # need node gateway my $gateway = $nethash{$nd}{'gateway'}; # This should configure the iscsi disc on the client my $tcmd = qq~/usr/lpp/bos.sysmgt/nim/methods/c_disc_target -a operation=discover -a target="$dump_target" -a dump_port="$dump_port" -a ipaddr="$SNip" -a lun_id="$dump_lunid"~; my $hd = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $tcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run \'$tcmd\' on node $nd.\ n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } chomp $hd; my $hdisk = $hd; if ($hd =~ /:/) { my $node; ($node, $hdisk) = split(': ', $hd); } chomp $hdisk; $hdisk =~ s/\s*//g; # define the disk on the client my $mkcmd = qq~/usr/sbin/mkdev -l $hdisk~; my $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $mkcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run \'$mkcmd\' on node $nd.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } # configure the dump device, select either selective or full # for the configdump attribute. my $ccmd = qq~/usr/lpp/bos.sysmgt/nim/methods/c_config_dump -a configdump=$configdump -a target=$dump_target -a dump_port=$dump_port -a ipaddr=$SNip -a lun_id=$dump_lunid~; $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $ccmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run \'$ccmd\' on node $nd.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } # set the dump disk: my $syscmd = qq~/usr/bin/sysdumpdev -p /dev/$hdisk~; $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $syscmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run \'$syscmd\' on node $nd.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } # get the device name to use with the bootlist cmd # my $nimcmd = qq~netstat -in~; my $nimout = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $nimcmd,0); my $myip = xCAT::NetworkUtils->getipaddr($nd); chomp $myip; my $intname; foreach my $line (split(/\n/,$nimout)) { my ($name, $junk1, $junk, $IP, $junk3) = split(" ", $line); chomp $IP; if ($IP eq $myip) { $intname =$name; last; } } my $devicename; if ($intname =~ /hf/) { my $index =~ s/hf//g; $devicename = "hfi" . $index; } if ($intname =~ /en/) { my $index =~ s/en//g; $devicename = "ent" . $index; } if ($intname =~ /et/) { my $index =~ s/et//g; $devicename = "ent" . $index; } # point to the new server my $blcmd = qq~/usr/bin/bootlist -m normal $devicename gateway=$gateway bserver=$SNip client=$Nodeip ~; $output = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nd, $blcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run \'$blcmd\' on node $nd.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); $error++; next; } my $rsp; push @{$rsp->{data}}, "Set the primary dump device for node \'$nd\' to \'/dev/$hdisk\' and changed the dump target to \'$sn\'.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); } } } if ($error) { return 1; } return 0; } #---------------------------------------------------------------------------- =head3 getnimattr Get the specified nim attrs form the named server Returns: undef - error hash ref - =cut #----------------------------------------------------------------------------- sub getnimattr { my $resname = shift; my $attr = shift; my $callback = shift; my $target = shift; my $sub_req = shift; my @attrs = @$attr; my %attrval; if (!$resname) { return undef; } if (!$target) { $target = xCAT::InstUtils->getnimprime(); } chomp $target; my $ncmd = "/usr/sbin/lsnim "; foreach my $a (@attrs) { $ncmd .= "-a $a "; } $ncmd .= "$resname 2>/dev/null"; my $attrlist = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $target, $ncmd, 0); if ($::RUNCMD_RC != 0) { if ($::VERBOSE) { my $rsp; push @{$rsp->{data}}, "Could not run lsnim command: \'$ncmd\'.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } return undef; } foreach my $line (split(/\n/, $attrlist) ){ # look for attr name foreach my $a (@attrs) { chomp $a; if ($line =~ /$a/) { my ($stuff, $value) = split('=', $line); chomp $value; my ($val, $rest) = split(' ', $value); # add to hash $attrval{$a} = $val; } } } return \%attrval; } #---------------------------------------------------------------------------- =head3 sfsSLconfig Does statelite setup when using a shared file system The snmove cmd changes the xcatmaster value for the nodes This means, that since we won't be running mkdsklsnode again, we need to take care of the ststelite file changes here - update statelite tables in DB - run dolitesetup to re-create the statelite files stored in the shared_root directories - copy the new statelite files to the shared_root directory on the target service node (only one since this is a shared filesystem - note: not copying the persistent directory on the MN Arguments: Returns: 0 - OK 1 - error Usage: $ret = &sfsSLconfig(\@nodelist, \%nhash, \%sn_hash, $nimprime, $callback, $sub_req); =cut #----------------------------------------------------------------------------- sub sfsSLconfig { my $nodelist = shift; my $nh = shift; my $n_h = shift; my $nimprime = shift; my $callback = shift; my $sub_req = shift; my @nodes = @$nodelist; my %nhash = %{$nh}; my %sn_hash = %{$n_h}; my %imghash; # osimage def my $statemnt; my $server; my $dir; my $item = 0; my %SLmodhash; # changes for the statelite DB table # gather some basic info my $targetsn; # name of SN to copy files to my %objtype; # need to pass to getobjdefs my %osinodes; # list of nodes for each osimage my @osimage; # list of osimages foreach my $n (@nodes) { if (!grep(/$nhash{$n}{'provmethod'}/, @osimage) ){ push (@osimage, $nhash{$n}{'provmethod'}); $objtype{$nhash{$n}{'provmethod'}} = 'osimage'; push (@{$osinodes{$nhash{$n}{'provmethod'}}}, $n); my ($sn, $snbak) = split(/,/, $nhash{$n}{servicenode}); if (!$targetsn) { if (!xCAT::InstUtils->is_me($sn) ) { $targetsn=$sn; } } } } # get hash of statelite table entries my $statetab = xCAT::Table->new('statelite', -create => 1); my $recs = $statetab->getAllEntries; # # update the statelite DB tables # foreach my $line (@$recs) { # see what nodes this entry applies to my @nodeattr = &noderange($line->{node}, 0); $statemnt = $line->{statemnt}; ($server, $dir) = split(/:/, $statemnt); chomp $server; foreach my $n (@nodes) { # if the node is not in the noderange for this # entry then skip it if (!grep(/$n/, @nodeattr)) { next; } # check for the server if (grep /\$/, $server) { my $serv = xCAT::SvrUtils->subVars($server, $n, 'server', $callback); $server = $serv; # note: if a variable IS used in the entry then it # does not have to be updated. } else { # if the $server value was the old SN hostname # then we need to # update the statelite table with the new SN name $item++; my $stmnt = "$sn_hash{$n}{'xcatmaster'}:$dir"; $SLmodhash{$item}{'statemnt'} = $stmnt; $SLmodhash{$item}{'node'} = $n; } my $rsp; push @{$rsp->{data}}, "Setting new values in the xCAT database.\n"; xCAT::MsgUtils->message("I", $rsp, $callback); # for each key in SLmodhash - update the statelite table foreach my $item (keys %SLmodhash) { my $node = $SLmodhash{$item}{'node'}; my $statemnt = $SLmodhash{$item}{'statemnt'}; $statetab->setAttribs({'node' => $node}, {'statemnt' => $statemnt}); } } } # end statelite DB update # done with statelite table $statetab->close(); # get the osimage defs my %imghash = xCAT::DBobjUtils->getobjdefs(\%objtype, $callback); if (!(%imghash)) { my $rsp; push @{$rsp->{data}}, "Could not get xCAT osimage definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } # # call dolitesetup() for each osimage needed for the nodes # to re-do the statelite tables etc. in the shared_root dir # foreach my $i (@osimage) { # dolitesetup to update the shared_root table files # - updates files in the sopot and shared_root resour my $rc=xCAT::InstUtils->dolitesetup($i, \%imghash, \@{$osinodes{$i}}, $callback, $sub_req); if ($rc eq 1) { # error my $rsp; push @{$rsp->{data}}, "Could not complete the statelite setup.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } } # end statelite setup # # copy files to target SN # foreach my $i (@osimage) { my $SRname = $imghash{$i}{shared_root}; if ($SRname) { my $srloc = xCAT::InstUtils->get_nim_attr_val( $imghash{$i}{shared_root}, "location", $callback, $nimprime, $sub_req); if ($srloc) { my $cpcmd = qq~$::XCATROOT/bin/xdcp $targetsn ~; my $output; if (-f "$srloc/statelite.table") { $cpcmd .= qq~$srloc/statelite.table ~; } if (-f "$srloc/litefile.table") { $cpcmd .= qq~$srloc/litefile.table ~; } if (-f "$srloc/litetree.table") { $cpcmd .= qq~$srloc/litetree.table ~; } if (-f "$srloc/aixlitesetup") { $cpcmd .= qq~$srloc/aixlitesetup ~; } $cpcmd .= qq~$srloc/ ~; $output=xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nimprime, $cpcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not copy new statelite file to $targetsn\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } my $ddir = "$srloc/.default"; if (-d $ddir ) { $cpcmd = qq~$::XCATROOT/bin/xdcp $targetsn -R $srloc/.default $srloc/~; } $output=xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nimprime, $cpcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not copy new statelite information to $targetsn\n"; xCAT::MsgUtils->message("E", $rsp, $callback); } } } } # end copy files return 0; }