#!/usr/bin/env perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT::InstUtils; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } # if AIX - make sure we include perl 5.8.2 in INC path. # Needed to find perl dependencies shipped in deps tarball. if ($^O =~ /^aix/i) { use lib "/usr/opt/perl5/lib/5.8.2/aix-thread-multi"; use lib "/usr/opt/perl5/lib/5.8.2"; use lib "/usr/opt/perl5/lib/site_perl/5.8.2/aix-thread-multi"; use lib "/usr/opt/perl5/lib/site_perl/5.8.2"; } use lib "$::XCATROOT/lib/perl"; require xCAT::Table; use POSIX qw(ceil); use Socket; use Sys::Hostname; use strict; require xCAT::Schema; #require Data::Dumper; use Data::Dumper; require xCAT::NodeRange; require DBI; our @ISA = qw(Exporter); our @EXPORT_OK = qw(genpassword); #------------------------------------------------------------------------------- =head1 xCAT::InstUtils =head2 Package Description This program module file, is a set of utilities used by xCAT install related commands. =cut #------------------------------------------------------------- #---------------------------------------------------------------------------- =head3 getnimprime Get the name of the primary AIX NIM master Returns: hostname - short hostname of primary NIM master undef - could not find primary NIM master Example: my $nimprime = xCAT::InstUtils->getnimprime(); Comments: =cut #----------------------------------------------------------------------------- sub getnimprime { # the primary NIM master is either specified in the site table # or it is the xCAT management node. my $nimprime = xCAT::Utils->get_site_Master(); my $sitetab = xCAT::Table->new('site'); (my $et) = $sitetab->getAttribs({key => "NIMprime"}, 'value'); if ($et and $et->{value}) { $nimprime = $et->{value}; } my $hostname; if ($nimprime) { if ($nimprime =~ /\d+\.\d+\.\d+\.\d+/) { my $packedaddr = inet_aton($nimprime); $hostname = gethostbyaddr($packedaddr, AF_INET); } else { $hostname = $nimprime; } my $shorthost; ($shorthost = $hostname) =~ s/\..*$//; chomp $shorthost; return $shorthost; } return undef; } #---------------------------------------------------------------------------- =head3 myxCATname Gets the name of the node I'm running on - as known by xCAT (Either the management node or a service node) =cut #----------------------------------------------------------------------------- sub myxCATname { my ($junk, $name); $name = hostname(); if (xCAT::Utils->isMN()) { # read the site table, master attrib my $hostname = xCAT::Utils->get_site_Master(); if ($hostname =~ /\d+\.\d+\.\d+\.\d+/) { my $packedaddr = inet_aton($hostname); $name = gethostbyaddr($packedaddr, AF_INET); } else { $name = $hostname; } } elsif (xCAT::Utils->isServiceNode()) { # the myxcatpost_ file should exist on all nodes! my $catcmd = "cat /xcatpost/myxcatpost_* | grep '^NODE='"; my $output = xCAT::Utils->runcmd("$catcmd", -1); if ($::RUNCMD_RC == 0) { ($junk, $name) = split('=', $output); } } my $shorthost; ($shorthost = $name) =~ s/\..*$//; chomp $shorthost; return $shorthost; } #---------------------------------------------------------------------------- =head3 is_me returns 1 if the hostname is the node I am running on Gets all the interfcaes defined on this node and sees if any of them match the IP of the hostname passed in Arguments: none Returns: 1 - this is the node I am running on 0 - this is not the node I am running on Globals: none Error: none Example: if (xCAT::InstUtils->is_me(&somehostname)) { blah; } Comments: none =cut #----------------------------------------------------------------------------- sub is_me { my ($class, $name) = @_; # convert to IP my $nameIP = inet_ntoa(inet_aton($name)); chomp $nameIP; # split into octets my ($b1, $b2, $b3, $b4) = split /\./, $nameIP; # get all the possible IPs for the node I'm running on my $ifcmd = "ifconfig -a | grep 'inet '"; my $result = xCAT::Utils->runcmd($ifcmd, 0, 1); if ($::RUNCMD_RC != 0) { my $rsp; # push @{$rsp->{data}}, "Could not run $ifcmd.\n"; # xCAT::MsgUtils->message("E", $rsp, $callback); return 0; } foreach my $int (@$result) { my ($inet, $myIP, $str) = split(" ", $int); chomp $myIP; # Split the two ip addresses up into octets my ($a1, $a2, $a3, $a4) = split /\./, $myIP; if (($a1 == $b1) && ($a2 == $b2) && ($a3 == $b3) && ($a4 == $b4)) { return 1; } } return 0; } #---------------------------------------------------------------------------- =head3 get_nim_attr_val Use the lsnim command to find the value of a resource attribute. Arguments: Returns: 0 - OK 1 - error Globals: Error: Example: xCAT::InstUtils->get_nim_attr_val Comments: =cut #----------------------------------------------------------------------------- sub get_nim_attr_val { my $class = shift; my $resname = shift; my $attrname = shift; my $callback = shift; my $target = shift; my $sub_req = shift; if (!$target) { $target = xCAT::InstUtils->getnimprime(); } chomp $target; my $cmd = "/usr/sbin/lsnim -a $attrname -Z $resname 2>/dev/null"; my $nout = xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $target, $cmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Could not run lsnim command: \'$cmd\'.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return undef; } # The command output may have the xdsh prefix "target:" #my ($junk, $junk, $junk, $loc) = split(/:/, $nout); #chomp $loc; my $loc; if ($nout =~ /.*$resname:(.*):$/) { $loc = $1; } return $loc; } #------------------------------------------------------------------------------- =head3 xcmd Run command either locally or on a remote system. Calls either runcmd or runxcmd and does either xdcp or xdsh. Arguments: Returns: Output of runcmd or runxcmd or undef. Comments: ex. xCAT::InstUtils->xcmd($callback, $sub_req, "xdcp", $nimprime, $doarray, $cmd); =cut #------------------------------------------------------------------------------- sub xcmd { my $class = shift; my $callback = shift; my $sub_req = shift; my $xdcmd = shift; # xdcp or xdsh my $target = shift; # the node to run it on my $cmd = shift; # the actual cmd to run my $doarray = shift; # should the return be a string or array ptr? my $returnformat = 0; # default is to return string my $exitcode = -1; # don't display error if ($doarray) { $returnformat = $doarray; } # runxcmd uses global $::CALLBACK = $callback; my $output; if (!ref($target)) { # must be node name if (xCAT::InstUtils->is_me($target)) { $output = xCAT::Utils->runcmd($cmd, $exitcode, $returnformat); } else { my @snodes; push(@snodes, $target); $output = xCAT::Utils->runxcmd( { command => [$xdcmd], node => \@snodes, arg => ["-s", $cmd] }, $sub_req, $exitcode, $returnformat ); } } else { # it is an array ref my @snodes; @snodes = @{$target}; $output = xCAT::Utils->runxcmd( { command => [$xdcmd], node => \@snodes, arg => ["-s", $cmd] }, $sub_req, $exitcode, $returnformat ); } if ($returnformat == 1) { return @$output; } else { return $output; } return undef; } #---------------------------------------------------------------------------- =head3 readBNDfile Get the contents of a NIM installp_bundle file based on the name of the NIM resource. =cut #----------------------------------------------------------------------------- sub readBNDfile { my ($class, $callback, $BNDname, $nimprime, $sub_req) = @_; my $junk; my @pkglist, my $pkgname; # get the location of the file from the NIM resource definition my $bnd_file_name = xCAT::InstUtils->get_nim_attr_val($BNDname, 'location', $callback, $nimprime, $sub_req); # The boundle file may be on nimprime my $ccmd = qq~cat $bnd_file_name~; my $output=xCAT::InstUtils->xcmd($callback, $sub_req, "xdsh", $nimprime, $ccmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{$rsp->{data}}, "Command: $ccmd failed."; xCAT::MsgUtils->message("E", $rsp, $callback); } # get the names of the packages #$output =~ s/$nimprime:\s+//g; foreach my $line (split(/\n/, $output)) { #May include xdsh prefix $nimprime: $line =~ s/$nimprime:\s+//; # skip blank and comment lines next if ($line =~ /^\s*$/ || $line =~ /^\s*#/); push(@pkglist, $line); } close(BNDFILE); return (0, \@pkglist, $bnd_file_name); } #---------------------------------------------------------------------------- =head3 restore_request Restores an xcatd request from a remote management server into the proper format by removing arrays that were added by XML and removing tags that were added to numeric hash keys. Arguments: Returns: ptr to hash undef Globals: Example: Comments: =cut #----------------------------------------------------------------------------- sub restore_request { my $class = shift; my $in_struct = shift; my $callback = shift; my $out_struct; if (ref($in_struct) eq "ARRAY") { # flatten the array it it has only one element # otherwise leave it alone if (scalar(@$in_struct) == 1) { return (xCAT::InstUtils->restore_request($in_struct->[0])); } else { return ($in_struct); } } if (ref($in_struct) eq "HASH") { foreach my $struct_key (keys %{$in_struct}) { my $stripped_key = $struct_key; $stripped_key =~ s/^xxXCATxx(\d)/$1/; # do not flatten the arg or node arrays if (($stripped_key =~ /^arg$/) || ($stripped_key =~ /^node$/)) { $out_struct->{$stripped_key} = $in_struct->{$struct_key}; } else { $out_struct->{$stripped_key} = xCAT::InstUtils->restore_request($in_struct->{$struct_key}); } } return $out_struct; } if ((ref($in_struct) eq "SCALAR") || (ref(\$in_struct) eq "SCALAR")) { return ($in_struct); } print "Unsupported data reference in restore_request().\n"; return undef; } #---------------------------------------------------------------------------- =head3 taghash Add a non-numeric tag to any hash keys that are numeric. Arguments: Returns: 0 - OK 1 - error Globals: Example: Comments: XML will choke on numeric values. This happens when including a hash in a request to a remote service node. =cut #----------------------------------------------------------------------- sub taghash { my ($class, $hash) = @_; if (ref($hash) eq "HASH") { foreach my $k (keys %{$hash}) { if ($k =~ /^(\d)./) { my $tagged_key = "xxXCATxx" . $k; $hash->{$tagged_key} = $hash->{$k}; delete($hash->{$k}); } } return 0; } else { return 1; } } #------------------------------------------------------------------------------- =head3 getOSnodes Split a noderange into arrays of AIX and Linux nodes. Arguments: \@noderange - reference to onde list array Returns: $rc - 1 - yes, all the nodes are AIX 0 - no, at least one node is not AIX \@aixnodes - ref to array of AIX nodes \@linuxnodes - ref to array of Linux nodes Comments: Based on "os" attr of node definition. If attr is not set, defaults to OS of current system. Example: my ($rc, $AIXnodes, $Linuxnodes) = xCAT::InstUtils->getOSnodes(\@noderange) =cut #------------------------------------------------------------------------------- sub getOSnodes { my ($class, $nodes) = @_; my @nodelist = @$nodes; my $rc = 1; # all AIX nodes my @aixnodes; my @linuxnodes; my $nodetab = xCAT::Table->new('nodetype'); my $os = $nodetab->getNodesAttribs(\@nodelist, ['node', 'os']); foreach my $n (@nodelist) { my $osname; if (defined($os->{$n}->[0]->{os})) { $osname = $os->{$n}->[0]->{os}; } else { $osname = $^O; } if (($osname ne "AIX") && ($osname ne "aix")) { push(@linuxnodes, $n); $rc = 0; } else { push(@aixnodes, $n); } } $nodetab->close; return ($rc, \@aixnodes, \@linuxnodes); } #------------------------------------------------------------------------------- =head3 get_server_nodes Determines the server node names as known by a lists of nodes. Arguments: A list of node names. Returns: A hash ref of arrays, the key is the service node pointing to an array of nodes that are serviced by that service node Example my %servernodes = &get_server_nodes($callback, \@$AIXnodes); Comments: - Code runs on MN or SNs =cut #------------------------------------------------------------------------------- sub get_server_nodes { my $class = shift; my $callback = shift; my $nodes = shift; my @nodelist; if ($nodes) { @nodelist = @$nodes; } # # get the server name for each node - as known by node # my $noderestab = xCAT::Table->new('noderes'); my $xcatmasters = $noderestab->getNodesAttribs(\@nodelist, ['node', 'xcatmaster']); $noderestab->close; my %servernodes; foreach my $node (@nodelist) { my $serv; if ($xcatmasters->{$node}->[0]->{xcatmaster}) { # get ip of node xcatmaster attribute my $xcatmaster = $xcatmasters->{$node}->[0]->{xcatmaster}; $serv = inet_ntoa(inet_aton($xcatmaster)); } else { # get ip facing node $serv = xCAT::Utils->getFacingIP($node); } chomp $serv; if (xCAT::Utils->validate_ip($serv)) { push (@{$servernodes{$serv}}, $node); } } return \%servernodes; } 1;