#!/usr/bin/perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT::Template; use strict; use xCAT::Table; use File::Basename; use File::Path; #use Data::Dumper; use Sys::Syslog; use xCAT::ADUtils; #to allow setting of one-time machine passwords use xCAT::Utils; use xCAT::TableUtils; use xCAT::NetworkUtils; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } my $netdnssupport = eval { require Net::DNS; 1; }; my $tmplerr; my $table; my $key; my $field; my $idir; my $node; my %loggedrealms; my $lastmachinepass; my %tab_replacement=( "noderes:nfsserver"=>"noderes:xcatmaster", "noderes:tftpserver"=>"noderes:xcatmaster", ); sub subvars { my $self = shift; my $inf = shift; my $outf = shift; $tmplerr=undef; #clear tmplerr since we are starting fresh $node = shift; my $pkglistfile=shift; my $media_dir = shift; my $platform=shift; my $partitionfile=shift; my %namedargs = @_; #further expansion of this function will be named arguments, should have happened sooner. unless ($namedargs{reusemachinepass}) { $lastmachinepass=""; } my $outh; my $inh; $idir = dirname($inf); open($inh,"<",$inf); unless ($inh) { return "Unable to open $inf, aborting"; } mkpath(dirname($outf)); open($outh,">",$outf); unless($outh) { return "Unable to open $outf for writing/creation, aborting"; } my $inc; #First load input into memory.. while (<$inh>) { $inc.=$_; } close($inh); my $master; #my $sitetab = xCAT::Table->new('site'); my $noderestab = xCAT::Table->new('noderes'); #(my $et) = $sitetab->getAttribs({key=>"master"},'value'); my @masters = xCAT::TableUtils->get_site_attribute("master"); my $tmp = $masters[0]; if ( defined($tmp) ) { $master = $tmp; } my $ipfn = xCAT::NetworkUtils->my_ip_facing($node); if ($ipfn) { $master = $ipfn; } my $et = $noderestab->getNodeAttribs($node,['xcatmaster']); if ($et and $et->{'xcatmaster'}) { $master = $et->{'xcatmaster'}; } unless ($master) { die "Unable to identify master for $node"; } $ENV{XCATMASTER}=$master; my @nodestatus = xCAT::TableUtils->get_site_attribute("nodestatus"); my $tmp=$nodestatus[0]; if( defined($tmp) ){ $ENV{NODESTATUS}=$tmp; } #replace the env with the right value so that correct include files can be found $inc =~ s/#ENV:([^#]+)#/envvar($1)/eg; if ($pkglistfile) { #substitute the tag #INCLUDE_DEFAULT_PKGLIST# with package file name (for full install of rh, centos,SL, esx fedora) $inc =~ s/#INCLUDE_DEFAULT_PKGLIST#/#INCLUDE:$pkglistfile#/g; #substitute the tag #INCLUDE_DEFAULT_PKGLIST_S# with package file name (for full install of sles) #substitute the tag #INCLUDE_DEFAULT_PERNLIST_S# with package file name (for full install sles #substitute the tag #INCLUDE_DEFAULT_RMPKGLIST_S# with package file name (for full install sles) $inc =~ s/#INCLUDE_DEFAULT_PKGLIST_S#/#INCLUDE_PKGLIST:$pkglistfile#/g; $inc =~ s/#INCLUDE_DEFAULT_PTRNLIST_S#/#INCLUDE_PTRNLIST:$pkglistfile#/g; $inc =~ s/#INCLUDE_DEFAULT_RMPKGLIST_S#/#INCLUDE_RMPKGLIST:$pkglistfile#/g; } if (("ubuntu" eq $platform) || ("debian" eq $platform)) { # since debian/ubuntu uses a preseed file instead of a kickstart file, pkglist # must be included via simple string replacement instead of using includefile() # the first line of $pkglistfile is the space-delimited package list # the additional lines are considered preseed directives and included as is if ($pkglistfile) { # handle empty and non-empty $pkglistfile's if (open PKGLISTFILE, "<$pkglistfile") { my $pkglist = ''; # append preseed directive lines while () { chomp $_; $pkglist .= " " . $_; } $inc =~ s/#INCLUDE_DEFAULT_PKGLIST_PRESEED#/$pkglist/g; close PKGLISTFILE; } } else { # handle no $pkglistfile $inc =~ s/#INCLUDE_DEFAULT_PKGLIST_PRESEED#//g; } } #do *all* includes, recursive and all my $doneincludes=0; while (not $doneincludes) { $doneincludes=1; if ($inc =~ /#INCLUDE_PKGLIST:[^#^\n]+#/) { $doneincludes=0; $inc =~ s/#INCLUDE_PKGLIST:([^#^\n]+)#/includefile($1, 0, 1)/eg; } if ($inc =~ /#INCLUDE_PTRNLIST:[^#^\n]+#/) { $doneincludes=0; $inc =~ s/#INCLUDE_PTRNLIST:([^#^\n]+)#/includefile($1, 0, 2)/eg; } if ($inc =~ /#INCLUDE_RMPKGLIST:[^#^\n]+#/) { $doneincludes=0; $inc =~ s/#INCLUDE_RMPKGLIST:([^#^\n]+)#/includefile($1, 0, 3)/eg; } if ($inc =~ /#INCLUDE:[^#^\n]+#/) { $doneincludes=0; $inc =~ s/#INCLUDE:([^#^\n]+)#/includefile($1, 0, 0)/eg; } } #Support hierarchical include $inc =~ s/#ENV:([^#]+)#/envvar($1)/eg; if ($inc =~ /#INCLUDE:[^#^\n]+#/) { $inc =~ s/#INCLUDE:([^#^\n]+)#/includefile($1, 0, 0)/eg; } #ok, now do everything else.. $inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg; $inc =~ s/#ENV:([^#]+)#/envvar($1)/eg; $inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg; $inc =~ s/#TABLE:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3)/eg; $inc =~ s/#TABLEBLANKOKAY:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3,'1')/eg; $inc =~ s/#CRYPT:([^:]+):([^:]+):([^#]+)#/crydb($1,$2,$3)/eg; $inc =~ s/#COMMAND:([^#]+)#/command($1)/eg; $inc =~ s/#KICKSTARTNET#/kickstartnetwork()/eg; $inc =~ s/#ESXIPV6SETUP#/esxipv6setup()/eg; $inc =~ s/#INCLUDE_NOP:([^#^\n]+)#/includefile($1,1,0)/eg; $inc =~ s/#INCLUDE_PKGLIST:([^#^\n]+)#/includefile($1,0,1)/eg; $inc =~ s/#INCLUDE_PTRNLIST:([^#^\n]+)#/includefile($1,0,2)/eg; $inc =~ s/#INCLUDE_RMPKGLIST:([^#^\n]+)#/includefile($1,0,3)/eg; $inc =~ s/#INCLUDE:([^#^\n]+)#/includefile($1, 0, 0)/eg; $inc =~ s/#HOSTNAME#/$node/eg; my $nrtab = xCAT::Table->new("noderes"); my $tftpserver = $nrtab->getNodeAttribs($node, ['tftpserver']); my $sles_sdk_media = "http://" . $tftpserver->{tftpserver} . $media_dir . "/sdk1"; $inc =~ s/#SLES_SDK_MEDIA#/$sles_sdk_media/eg; #if user specify the partion file, replace the default partition strategy if ($partitionfile){ #if the content of the partition file is definition replace the default is ok my $partcontent = ''; my $scriptflag = 0; if ($partitionfile =~ /^s:(.*)/){ $scriptflag = 1; $partitionfile = $1; } if (-r $partitionfile){ open ($inh, "<", $partitionfile); while (<$inh>){ $partcontent .= $_; } close ($inh); #the content of the specified file is a script which can write partition definition into /tmp/partitionfile if ($scriptflag){ #for redhat/sl/centos/kvm/fedora if ($inc =~ /#XCAT_PARTITION_START#/) { my $tempstr = "%inlcude /tmp/partitionfile\n"; $inc =~ s/#XCAT_PARTITION_START#[\s\S]*#XCAT_PARTITION_END#/$tempstr/; #modify the content in the file, and write into %pre part $partcontent = "cat > /tmp/partscript << EOFEOF\n" . $partcontent . "\nEOFEOF\n"; $partcontent .= "chmod 755 /tmp/partscript\n"; $partcontent .= "/tmp/partscript\n"; #replace the #XCA_PARTITION_SCRIPT# $inc =~ s/#XCA_PARTITION_SCRIPT#/$partcontent/; } #for sles/suse elsif ($inc =~ //){ my $tempstr = "XCATPARTITIONTEMP"; $inc =~ s/[\s\S]*/$tempstr/; $partcontent = "cat > /tmp/partscript << EOFEOF\n" . $partcontent . "\nEOFEOF\n"; $partcontent .= "chmod 755 /tmp/partscript\n"; $partcontent .= "/tmp/partscript\n"; $inc =~ s/#XCA_PARTITION_SCRIPT#/$partcontent/; } } else{ $partcontent =~ s/\s$//; if ($inc =~ /#XCAT_PARTITION_START#/){ $inc =~ s/#XCAT_PARTITION_START#[\s\S]*#XCAT_PARTITION_END#/$partcontent/; } elsif ($inc =~ //){ $inc =~ s/[\s\S]*/$partcontent/; } } } } if ($tmplerr) { close ($outh); return $tmplerr; } print $outh $inc; close($outh); return 0; } sub esxipv6setup { if ($::XCATSITEVALS{managedaddressmode} ne "autoula") { return ""; } # blank unless autoula my $hoststab; my $mactab = xCAT::Table->new('mac',-create=>0); my $ent = $mactab->getNodeAttribs($node,['mac']); my $suffix = $ent->{mac}; $suffix = lc($suffix); unless ($mactab) { die "mac table should always exist prior to template processing when doing autoula"; } #in autoula, because ESXi weasel doesn't seemingly grok IPv6 at all, we'll have to do it in %pre unless ($hoststab) { $hoststab = xCAT::Table->new('hosts',-create=>1); } my $ulaaddr = autoulaaddress($suffix); $hoststab->setNodeAttribs($node,{ip=>$ulaaddr}); return 'esxcfg-vmknic -i '.$ulaaddr.'/64 "Management Network"'."\n"; } sub kickstartnetwork { my $line = "network --onboot=yes --bootproto="; my $hoststab; my $mactab = xCAT::Table->new('mac',-create=>0); unless ($mactab) { die "mac table should always exist prior to template processing when doing autoula"; } my $ent = $mactab->getNodeAttribs($node,['mac']); unless ($ent and $ent->{mac}) { die "missing mac data for $node"; } my $suffix = $ent->{mac}; $suffix = lc($suffix); if ($::XCATSITEVALS{managedaddressmode} eq "autoula") { unless ($hoststab) { $hoststab = xCAT::Table->new('hosts',-create=>1); } $line .= "static --device=$suffix --noipv4 --ipv6="; my $ulaaddr = autoulaaddress($suffix); $hoststab->setNodeAttribs($node,{ip=>$ulaaddr}); $line .= $ulaaddr; } else { $line .= "dhcp --device=$suffix"; } return $line; } sub autoulaaddress { my $suffix = shift; my $prefix = $::XCATSITEVALS{autoulaprefix}; $suffix =~ /(..):(..:..):(..:..):(..)/; my $leadbyte = $1; my $mask = ((hex($leadbyte) & 2) ^ 2); if ($mask) { $leadbyte = hex($leadbyte) | $mask; } else { $leadbyte = hex($leadbyte) & 0xfd; #mask out the one bit } $suffix = sprintf("%02x$2ff:fe$3$4",$leadbyte); return $prefix.$suffix; } sub machinepassword { if ($lastmachinepass) { #note, this should only happen after another call #to subvars that does *not* request reuse #the issue being avoiding reuse in the installmonitor case #subvars function clears this if appropriate return $lastmachinepass; } my $domaintab = xCAT::Table->new('domain'); $ENV{HOME}='/etc/xcat'; $ENV{LDAPRC}='ad.ldaprc'; my $ou; if ($domaintab) { my $ouent = $domaintab->getNodeAttribs('node','ou'); if ($ouent and $ouent->{ou}) { $ou = $ouent->{ou}; } } #my $sitetab = xCAT::Table->new('site'); #unless ($sitetab) { # return "ERROR: unable to open site table"; #} my $domain; #(my $et) = $sitetab->getAttribs({key=>"domain"},'value'); my @domains = xCAT::TableUtils->get_site_attribute("domain"); my $tmp = $domains[0]; if (defined($tmp)) { $domain = $tmp; } else { return "ERROR: no domain set in site table"; } my $realm = uc($domain); $realm =~ s/\.$//; $realm =~ s/^\.//; $ENV{KRB5CCNAME}="/tmp/xcat/krbcache.$realm.$$"; unless ($loggedrealms{$realm}) { my $passtab = xCAT::Table->new('passwd',-create=>0); unless ($passtab) { sendmsg([1,"Error authenticating to Active Directory"],$node); return; } (my $adpent) = $passtab->getAttribs({key=>'activedirectory'},['username','password']); unless ($adpent and $adpent->{username} and $adpent->{password}) { return "ERROR: activedirectory entry missing from passwd table"; } my $err = xCAT::ADUtils::krb_login(username=>$adpent->{username},password=>$adpent->{password},realm=>$realm); if ($err) { return "ERROR: authenticating to Active Directory"; } $loggedrealms{$realm}=1; } #my $server = $sitetab->getAttribs({key=>'directoryserver'},['value']); my $server; my @servers = xCAT::TableUtils->get_site_attribute("directoryserver"); $tmp = $servers[0]; if (defined($tmp)) { $server = $tmp; } else { $server = ''; if ($netdnssupport) { my $res = Net::DNS::Resolver->new; my $query = $res->query("_ldap._tcp.$domain","SRV"); if ($query) { foreach my $srec ($query->answer) { $server = $srec->{target}; } } } unless ($server) { sendmsg([1,"Unable to determine a directory server to communicate with, try site.directoryserver"]); return; } } my %args = ( node => $node, dnsdomain => $domain, directoryserver => $server, changepassondupe => 1, ); if ($ou) { $args{ou} = $ou }; my $data = xCAT::ADUtils::add_host_account(%args); if ($data->{error}) { return "ERROR: ".$data->{error}; } else { $lastmachinepass=$data->{password}; return $data->{password}; } } sub includefile { my $file = shift; my $special=shift; my $pkglist=shift; #1 means package list, #2 means pattern list, pattern list starts with @, #3 means remove package list, packages to be removed start with -. my $text = ""; unless ($file =~ /^\//) { $file = $idir."/".$file; } open(INCLUDE,$file) || return "#INCLUDEBAD:cannot open $file#"; my $pkgb = ""; my $pkge = ""; if ($pkglist) { if ($pkglist == 2) { $pkgb = ""; $pkge = ""; } else { $pkgb = ""; $pkge = ""; } } while() { if ($pkglist == 1) { s/#INCLUDE:/#INCLUDE_PKGLIST:/; } elsif ($pkglist == 2) { s/#INCLUDE:/#INCLUDE_PTRNLIST:/; } elsif ($pkglist == 3) { s/#INCLUDE:/#INCLUDE_RMPKGLIST:/; } if (( $_ =~ /^\s*#/ ) || ( $_ =~ /^\s*$/ )) { $text .= "$_"; } else { my $tmp=$_; chomp($tmp); #remove return char $tmp =~ s/\s*$//; #removes trailing spaces next if (($pkglist == 1) && (($tmp=~/^\s*@/) || ($tmp=~/^\s*-/))); #for packge list, do not include the lines start with @ if ($pkglist == 2) { #for pattern list, only include the lines start with @ if ($tmp =~/^\s*@(.*)/) { $tmp=$1; $tmp =~s/^\s*//; #removes leading spaces } else { next; } } elsif ($pkglist == 3) { #for rmpkg list, only include the lines start with - if ($tmp =~/^\s*-(.*)/) { $tmp=$1; $tmp =~s/^\s*//; #removes leading spaces } else { next; } } $text .= "$pkgb$tmp$pkge\n"; } } close(INCLUDE); if ($special) { $text =~ s/\$/\\\$/g; $text =~ s/`/\\`/g; } chomp($text); return($text); } sub command { my $command = shift; my $r; # if(($r = `$command`) == 0) { # chomp($r); # return($r); # } # else { # return("#$command: failed $r#"); # } $r = `$command`; chomp($r); return($r); } sub envvar { my $envvar = shift; if($envvar =~ /^\$/) { $envvar =~ s/^\$//; } return($ENV{$envvar}); } sub genpassword { #Generate a pseudo-random password of specified length my $length = shift; my $password=''; my $characters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; srand; #have to reseed, rand is not rand otherwise while (length($password) < $length) { $password .= substr($characters,int(rand 63),1); } return $password; } sub crydb { my $result = tabdb(@_); unless ($result =~ /^\$1\$/) { $result = crypt($result,'$1$'.genpassword(8)); } return $result; } sub tabdb { my $table = shift; my $key = shift; my $field = shift; my $blankok = shift; if( defined(%::GLOBAL_TAB_HASH) && defined( $::GLOBAL_TAB_HASH{$table}) && defined( $::GLOBAL_TAB_HASH{$table}{$key}) ) { return $::GLOBAL_TAB_HASH{$table}{$key}{$field}; } my $tabh = xCAT::Table->new($table); unless ($tabh) { $tmplerr="Unable to open table named $table"; if ($table =~ /\.tab/) { $tmplerr .= " (.tab should not be specified as part of the table name in xCAT 2, as seems to be the case here)"; } return ""; } my $ent; my $bynode=0; if ($key eq "THISNODE" or $key eq '$NODE') { $ent = $tabh->getNodeAttribs($node,[$field]); $key="node=$node"; } else { my %kp; foreach (split /,/,$key) { my $key; my $val; if ($_ eq 'THISNODE' or $_ eq '$NODE') { $bynode=1; } else { ($key,$val) = split /=/,$_; $kp{$key}=$val; } } if ($bynode) { my @ents = $tabh->getNodeAttribs($node,[keys %kp,$field]); my $tent; #Temporary ent TENT: foreach $tent (@ents) { foreach (keys %kp) { unless ($kp{$_} eq $tent->{$_}) { next TENT; } } #If still here, we found it $ent = $tent; } } else { ($ent) = $tabh->getAttribs(\%kp,$field); } } $tabh->close; unless($ent and defined($ent->{$field})) { unless ($blankok) { if ($field eq "xcatmaster") { my $ipfn = xCAT::NetworkUtils->my_ip_facing($node); if ($ipfn) { return $ipfn; } } #$tmplerr="Unable to find requested $field from $table, with $key"; my $rep=get_replacement($table,$key,$field); if ($rep) { return tabdb($rep->[0], $rep->[1], $rep->[2]); } else { $tmplerr="Unable to find requested $field from $table, with $key" } } return ""; #return "#TABLEBAD:$table:field $field not found#"; } return $ent->{$field}; #if($key =~ /^\$/) { # $key =~ s/^\$//; # $key = $ENV{$key}; #} #if($field =~ /^\$/) { # $field =~ s/^\$//; # $field = $ENV{$field}; #} #if($field == '*') { # $field = 1; # $all = 1; #} #--$field; #if($field < 0) { # return "#TABLE:field not found#" #} #open(TAB,$table) || \ # return "#TABLE:cannot open $table#"; #while() { # if(/^$key(\t|,| )/) { # m/^$key(\t|,| )+(.*)/; # if($all == 1) { # return "$2"; # } # @fields = split(',',$2); # if(defined $fields[$field]) { # return "$fields[$field]"; # } # else { # return "#TABLE:field not found#" # } # } #} #close(TAB); #return "#TABLE:key not found#" } sub get_replacement { my $table=shift; my $key=shift; my $field=shift; my $rep; if (exists($tab_replacement{"$table:$field"})) { my $repstr=$tab_replacement{"$table:$field"}; if ($repstr) { my @a=split(':', $repstr); if (@a > 2) { $rep=\@a; } else { $rep->[0]=$a[0]; $rep->[1]=$key; $rep->[2]=$a[1]; } } } return $rep; } #----------------------------------------------------------------------------- =head3 subvars_for_mypostscript create the mypostscript file for each node in the noderange, according to the template file mypostscript.tmpl. The template file is /opt/xcat/share/xcat/templates/mypostscript/mypostscript.tmpl by default. and uses also can copy it to /install/postscripts/, and customize it there. The mypostscript.tmpl is for all the images. If success, there is a mypostscript.$nodename for each node in the $tftpdir/mypostscripts/ Arguments: hostname Returns: Globals: %::GLOBAL_TAB_HASH: in subvars_for_mypostscript(), it will read mypostscript.tmpl and see what db attrs will be needed. The %::GLOBAL_TAB_HASH will store all the db attrs needed. And the format of value setting looks like: $::GLOBAL_TAB_HASH{$tabname}{$key}{$attrib} = $value; Error: none Example: Comments: none =cut #----------------------------------------------------------------------------- my $os; my $profile; my $arch; my $provmethod; %::GLOBAL_TAB_HASH = (); sub subvars_for_mypostscript { my $self = shift; my $nodes = shift; my $nodesetstate = shift; my $callback = shift; #my $tmpl = shift; #tmplfile default: "/opt/xcat/share/xcat/templates/mypostscript/mypostscript.tmpl" customized: /install/postscripts/mypostscript.tmpl $tmplerr=undef; #clear tmplerr since we are starting fresh my %namedargs = @_; #further expansion of this function will be named arguments, should have happened sooner. my $installroot = my @entries = xCAT::TableUtils->get_site_attribute("installdir"); if($entries[0]) { $installroot = $entries[0]; } my $tmpl="$installroot/postscripts/mypostscript.tmpl"; unless ( -r $tmpl) { $tmpl="$::XCATROOT/share/xcat/templates/mypostscript/mypostscript.tmpl"; } unless ( -r "$tmpl") { $callback->( { error => [ "site.precreatemypostscripts is set to 1 or yes. But No mypostscript template exists" . " in directory $installroot/install/postscripts or $::XCATROOT/share/xcat/templates/mypostscript/mypostscript.tmpl" ], errorcode => [1] } ); return; } my $outh; my $inh; $idir = dirname($tmpl); open($inh,"<",$tmpl); unless ($inh) { my $rsp; $rsp->{errorcode}->[0]=1; $rsp->{error}->[0]="Unable to open $tmpl, aborting\n"; $callback->($rsp); return; } my $inc; my $t_inc; my %table; #First load input into memory.. while (<$inh>) { $t_inc.=$_; if( $_ =~ /#TABLE:([^:]+):([^:]+):([^#]+)#/ ) { my $tabname=$1; my $key=$2; my $attrib = $3; $table{$tabname}{$key}{$attrib} = 1; } } close($inh); ## # $Tabname_hash{$key}{$attrib}=value # for example: $MAC_hash{cn001}{mac}=9a:ca:be:a9:ad:02 # # #%::GLOBAL_TAB_HASH = (); my $rc = collect_all_attribs_for_tables_in_template(\%table, $nodes, $callback); if($rc == -1) { return; } #print Dumper(\%::GLOBAL_TAB_HASH); my %script_fp; my $allattribsfromsitetable; # read all attributes for the site table and write an export # only run this function once for one command with noderange $allattribsfromsitetable = getAllAttribsFromSiteTab(); # get the net', 'mask', 'gateway' from networks table my $nets = getNetworks(); #%image_hash is used to store the attributes in linuximage and osimage tabs my %image_hash; getLinuximage(\%image_hash); # get postscript and postscript my $script_hash = xCAT::Postage::getScripts($nodes, \%image_hash); my $tftpdir = xCAT::TableUtils::getTftpDir(); my $snhash = getservicenode(); foreach my $n (@$nodes ) { $node = $n; $inc = $t_inc; my $script; my $scriptfile; $scriptfile = "$tftpdir/mypostscripts/mypostscript.$node"; mkpath(dirname($scriptfile)); open($script, ">$scriptfile"); unless ($script) { my $rsp; push @{$rsp->{data}}, "Could not open $scriptfile for writing.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } $script_fp{$node}=$script; `/bin/chmod ugo+x $scriptfile`; ##attributes from site tab # #my $master = $attribsfromnoderes->{$node}->{xcatmaster}; my $master = $::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster}; if( !defined($master) ) { $::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster} = $::XCATSITEVALS{MASTER}; } #get the node type, service node or compute node my $nodetype = getNodeType($node, $snhash); my $noderesent; if(exists($::GLOBAL_TAB_HASH{noderes}{$node})) { $noderesent = $::GLOBAL_TAB_HASH{noderes}{$node}; } #print Dumper($noderesent); #routes my $route_vars; if ($noderesent and defined($noderesent->{'routenames'})) { my $rn=$noderesent->{'routenames'}; my @rn_a=split(',', $rn); my $routestab = xCAT::Table->new('routes'); if ((@rn_a > 0) && ($routestab)) { $route_vars .= "NODEROUTENAMES=$rn\n"; $route_vars .= "export NODEROUTENAMES\n"; foreach my $route_name (@rn_a) { my $routesent = $routestab->getAttribs({routename => $route_name}, 'net', 'mask', 'gateway', 'ifname'); if ($routesent and defined($routesent->{net}) and defined($routesent->{mask})) { my $val="ROUTE_$route_name=" . $routesent->{net} . "," . $routesent->{mask}; $val .= ","; if (defined($routesent->{gateway})) { $val .= $routesent->{gateway}; } $val .= ","; if (defined($routesent->{ifname})) { $val .= $routesent->{ifname}; } $route_vars .= "$val\n"; $route_vars .= "export ROUTE_$route_name\n"; } } } } #NODESETSTATE ### vlan related item # for #VLAN_VARS_EXPORT# my $vlan_vars; $vlan_vars = getVlanItems($node); ## get monitoring server and other configuration data for monitoring setup on nodes # for #MONITORING_VARS_EXPORT# my $mon_vars; $mon_vars = getMonItems($node); #print "nodesetstate:$nodesetstate\n"; ## OSPKGDIR export # for #OSIMAGE_VARS_EXPORT# if (!$nodesetstate) { $nodesetstate = xCAT::Postage::getnodesetstate($node); } #print "nodesetstate:$nodesetstate\n"; #my $et = $typehash->{$node}; my $et = $::GLOBAL_TAB_HASH{nodetype}{$node}; $provmethod = $et->{'provmethod'}; $os = $et->{'os'}; $arch = $et->{'arch'}; $profile = $et->{'profile'}; my $osimgname = $provmethod; if($osimgname =~ /install|netboot|statelite/){ $osimgname = "$os-$arch-$provmethod-$profile"; } my $osimage_vars; $osimage_vars = getImageitems_for_node($node, \%image_hash, $nodesetstate); ## network # for #NETWORK_FOR_DISKLESS_EXPORT# # my $diskless_net_vars; my $setbootfromnet = 0; $diskless_net_vars = getDisklessNet($nets, \$setbootfromnet, $image_hash{$osimgname}{provmethod}); ## postscripts # for #INCLUDE_POSTSCRIPTS_LIST# # # my $postscripts; $postscripts = xCAT::Postage::getPostScripts($node, $osimgname, $script_hash, $setbootfromnet, $nodesetstate, $arch); ## postbootscripts # for #INCLUDE_POSTBOOTSCRIPTS_LIST# my $postbootscripts; $postbootscripts = xCAT::Postage::getPostbootScripts($node, $osimgname, $script_hash); #ok, now do everything else.. #$inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg; #$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg; $inc =~ s/#NODE#/$node/eg; $inc =~ s/\$NODE/$node/eg; $inc =~ s/#SITE_TABLE_ALL_ATTRIBS_EXPORT#/$allattribsfromsitetable/eg; $inc =~ s/#TABLE:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3)/eg; $inc =~ s/#ROUTES_VARS_EXPORT#/$route_vars/eg; $inc =~ s/#VLAN_VARS_EXPORT#/$vlan_vars/eg; $inc =~ s/#MONITORING_VARS_EXPORT#/$mon_vars/eg; $inc =~ s/#OSIMAGE_VARS_EXPORT#/$osimage_vars/eg; $inc =~ s/#NETWORK_FOR_DISKLESS_EXPORT#/$diskless_net_vars/eg; $inc =~ s/#INCLUDE_POSTSCRIPTS_LIST#/$postscripts/eg; $inc =~ s/#INCLUDE_POSTBOOTSCRIPTS_LIST#/$postbootscripts/eg; #$inc =~ s/#COMMAND:([^#]+)#/command($1)/eg; $inc =~ s/#NTYPE#/$nodetype/eg; $inc =~ s/#Subroutine:([^:]+)::([^:]+)::([^:]+):([^#]+)#/subroutine($1,$2,$3,$4)/eg; print $script $inc; close($script_fp{$node}); } return 0; } sub getMasterFromNoderes { my $node = shift; my $value; my $noderestab = xCAT::Table->new('noderes'); # if node has service node as master then override site master my $et = $noderestab->getNodeAttribs($node, ['xcatmaster'],prefetchcache=>1); if ($et and defined($et->{'xcatmaster'})) { $value = $et->{'xcatmaster'}; } else { my $sitemaster_value = $value; $value = xCAT::NetworkUtils->my_ip_facing($node); if ($value eq "0") { $value = $sitemaster_value; } } return $value; } sub getMasters { my $nodes = shift; my %masterhash; my $noderestab = xCAT::Table->new('noderes'); # if node has service node as master then override site master my $ethash = $noderestab->getNodesAttribs($nodes, ['xcatmaster'],prefetchcache=>1); if ($ethash) { foreach my $node (@$nodes) { if( $ethash->{$node}->[0] ) { $masterhash{$node} = $ethash->{$node}->[0]->{xcatmaster}; } if ( ! exists($masterhash{$node})) { my $value; $value = xCAT::NetworkUtils->my_ip_facing($node); if ($value eq "0") { undef($value); } $masterhash{$node} = $value; } } } #print Dumper(\%masterhash); return \%masterhash; } sub getservicenode { my %snhash; # reads all nodes from the service node table my $servicenodetab = xCAT::Table->new('servicenode'); unless ($servicenodetab) # no servicenode table { xCAT::MsgUtils->message('I', "Unable to open servicenode table.\n"); return undef; } my @nodes = $servicenodetab->getAllNodeAttribs(['tftpserver'],undef,prefetchcache=>1); $servicenodetab->close; foreach my $n (@nodes) { my $node = $n->{node}; $snhash{$node}=1 } return \%snhash; } sub getNoderes { my $nodes = shift; my %nodereshash; my $noderestab = xCAT::Table->new('noderes'); my $ethash = $noderestab->getNodesAttribs($nodes, ['nfsserver', 'installnic', 'primarynic','routenames', 'xcatmaster'],prefetchcache=>1); if ($ethash ){ foreach my $node (@$nodes) { if( defined( $ethash->{$node}->[0]) ) { $nodereshash{$node}{nfsserver} = $ethash->{$node}->[0]->{nfsserver}; $nodereshash{$node}{installnic} = $ethash->{$node}->[0]->{installnic}; $nodereshash{$node}{primarynic} = $ethash->{$node}->[0]->{primarynic}; $nodereshash{$node}{routenames} = $ethash->{$node}->[0]->{routenames}; $nodereshash{$node}{xcatmaster} = $ethash->{$node}->[0]->{xcatmaster}; if ( ! exists($nodereshash{$node}{xcatmaster})) { my $value; $value = xCAT::NetworkUtils->my_ip_facing($node); if ($value eq "0") { undef($value); } $nodereshash{$node}{xcatmaster} = $value; } } } } return \%nodereshash; } sub getTypeVars { my $nodes = shift; my $callback = shift; my %typehash; my $typetab = xCAT::Table->new('nodetype'); my $ethash = $typetab->getNodesAttribs($nodes, ['os', 'arch', 'profile', 'provmethod'],prefetchcache=>1); if ($ethash ){ foreach my $node (@$nodes) { if( defined( $ethash->{$node}->[0]) ) { $typehash{$node}{os} = $ethash->{$node}->[0]->{os}; $typehash{$node}{arch} = $ethash->{$node}->[0]->{arch}; if ($^O =~ /^linux/i) { unless ($typehash{$node}{'os'} and $typehash{$node}{'arch'}) { my $rsp; push @{$rsp->{data}}, "No os or arch setting in nodetype table for $node.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return undef; } } $typehash{$node}{profile} = $ethash->{$node}->[0]->{profile}; $typehash{$node}{provmethod} = $ethash->{$node}->[0]->{provmethod}; } } } return \%typehash; } sub getAllAttribsFromSiteTab { my $result; # all attributes for the site table are in %::XCATSITEVALS, so write an export # for them in the mypostscript file my $attribute; my $value; my $masterset = 0; foreach (keys(%::XCATSITEVALS)) # export the attribute { $attribute = $_; $attribute =~ tr/a-z/A-Z/; $value = $::XCATSITEVALS{$_}; if ($attribute eq "MASTER") { $masterset = 1; $result .= "SITEMASTER=" . $value . "\n"; $result .= "export SITEMASTER\n"; #if noderes.master for each node exists, the following value will be replaced. #$result .= "$attribute=" . $value . "\n"; #$result .= "export $attribute\n"; } else { # not Master attribute $result .= "$attribute='" . $value . "'\n"; $result .= "export $attribute\n"; } } # end site table attributes return $result; } sub enablesshbetweennodes { my $node = shift; my $result; my $enablessh=xCAT::TableUtils->enablessh($node); if ($enablessh == 1) { $result = "YES"; } else { $result = "NO"; } return $result; } sub subroutine { my $prefix = shift; my $module = shift; my $subroutine_name = shift; my $key = shift; my $result; if ($key eq "THISNODE" or $key eq '$NODE') { $key=$node; } my $function = join("::",$prefix,$module,$subroutine_name); { no strict 'refs'; $result=$function->($key); use strict; } return $result; } sub getNodeType { my $node = shift; my $snhash = shift; my $result; # see if this is a service or compute node? if ($snhash->{$node} == 1) { $result="service"; } else { $result="compute"; } return $result; } sub getVlanItems_t { my $node = shift; my $result; #get vlan related items my $vlan; my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my $tmp = $swtab->getNodeAttribs($node, ['vlan'],prefetchcache=>1); if (defined($tmp) && ($tmp) && $tmp->{vlan}) { $vlan = $tmp->{vlan}; $result .= "VLANID='" . $vlan . "'\n"; $result .= "export VLANID\n"; } else { my $vmtab = xCAT::Table->new("vm", -create => 0); if ($vmtab) { my $tmp1 = $vmtab->getNodeAttribs($node, ['nics'],prefetchcache=>1); if (defined($tmp1) && ($tmp1) && $tmp1->{nics}) { $result .= "VMNODE='YES'\n"; $result .= "export VMNODE\n"; my @nics=split(',', $tmp1->{nics}); foreach my $nic (@nics) { if ($nic =~ /^vl([\d]+)$/) { $vlan = $1; $result .= "VLANID='" . $vlan . "'\n"; $result .= "export VLANID\n"; last; } } } } } if ($vlan) { my $nwtab=xCAT::Table->new("networks", -create =>0); if ($nwtab) { my $sent = $nwtab->getAttribs({vlanid=>"$vlan"},'net','mask'); my $subnet; my $netmask; if ($sent and ($sent->{net})) { $subnet=$sent->{net}; $netmask=$sent->{mask}; } if (($subnet) && ($netmask)) { my $hoststab = xCAT::Table->new("hosts", -create => 0); if ($hoststab) { my $tmp = $hoststab->getNodeAttribs($node, ['otherinterfaces'],prefetchcache=>1); if (defined($tmp) && ($tmp) && $tmp->{otherinterfaces}) { my $otherinterfaces = $tmp->{otherinterfaces}; my @itf_pairs=split(/,/, $otherinterfaces); foreach (@itf_pairs) { my ($name,$ip)=split(/:/, $_); if(xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) { if ($name =~ /^-/ ) { $name = $node.$name; } $result .= "VLANHOSTNAME='" . $name . "'\n"; $result .= "export VLANHOSTNAME\n"; $result .= "VLANIP='" . $ip . "'\n"; $result .= "export VLANIP\n"; $result .= "VLANSUBNET='" . $subnet . "'\n"; $result .= "export VLANSUBNET\n"; $result .= "VLANNETMASK='" . $netmask . "'\n"; $result .= "export VLANNETMASK\n"; last; } } } } } } } } return $result; } sub getVlanItems { my $node = shift; my $result; #get vlan related items my $module_name="xCAT_plugin::vlan"; eval("use $module_name;"); if (!$@) { no strict "refs"; if (defined(${$module_name."::"}{getNodeVlanConfData})) { my @tmp_scriptd=${$module_name."::"}{getNodeVlanConfData}->($node); #print Dumper(@tmp_scriptd); if (@tmp_scriptd > 0) { $result = join(" ", @tmp_scriptd); } } } return $result; } sub getMonItems { my $node = shift; my $result; #get monitoring server and other configuration data for monitoring setup on nodes my %mon_conf = xCAT_monitoring::monitorctrl->getNodeConfData($node); foreach (keys(%mon_conf)) { $result .= "$_=" . $mon_conf{$_} . "\n"; $result .= "export $_\n"; } return $result; } sub getLinuximage { my $image_hash = shift; my $linuximagetab = xCAT::Table->new('linuximage', -create => 1); my @et2 = $linuximagetab->getAllAttribs('imagename', 'pkglist', 'pkgdir', 'otherpkglist', 'otherpkgdir' ); if( @et2 ) { foreach my $tmp_et2 (@et2) { my $imagename= $tmp_et2->{imagename}; $image_hash->{$imagename}->{pkglist}= $tmp_et2->{pkglist}; $image_hash->{$imagename}->{pkgdir} = $tmp_et2->{pkgdir}; $image_hash->{$imagename}->{otherpkglist} = $tmp_et2->{otherpkglist}; $image_hash->{$imagename}->{otherpkgdir} = $tmp_et2->{otherpkgdir}; } } } sub getImageitems_for_node { my $node = shift; my $image_hash = shift; my $nodesetstate = shift; my $result; #get packge names for extra rpms my $pkglist; my $ospkglist; if ( ($^O =~ /^linux/i) && ($provmethod) && ($provmethod ne "install") && ($provmethod ne "netboot") && ($provmethod ne "statelite")) { #this is the case where image from the osimage table is used #my $linuximagetab = xCAT::Table->new('linuximage', -create => 1); #(my $ref1) = # $linuximagetab->getAttribs({imagename => $provmethod}, # 'pkglist', 'pkgdir', 'otherpkglist', # 'otherpkgdir'); my $ref1 = $image_hash->{$provmethod}; if ($ref1) { if ($ref1->{'pkglist'}) { $ospkglist = $ref1->{'pkglist'}; if ($ref1->{'pkgdir'}) { $result .= "OSPKGDIR=" . $ref1->{'pkgdir'} . "\n"; $result .= "export OSPKGDIR\n"; } } if ($ref1->{'otherpkglist'}) { $pkglist = $ref1->{'otherpkglist'}; if ($ref1->{'otherpkgdir'}) { $result .= "OTHERPKGDIR=" . $ref1->{'otherpkgdir'} . "\n"; $result .= "export OTHERPKGDIR\n"; } } } } else { my $stat = "install"; my $installroot = xCAT::TableUtils->getInstallDir(); if ($profile) { my $platform = "rh"; if ($os) { if ($os =~ /rh.*/) { $platform = "rh"; } elsif ($os =~ /centos.*/) { $platform = "centos"; } elsif ($os =~ /fedora.*/) { $platform = "fedora"; } elsif ($os =~ /SL.*/) { $platform = "SL"; } elsif ($os =~ /sles.*/) { $platform = "sles"; } elsif ($os =~ /ubuntu.*/) { $platform = "ubuntu"; } elsif ($os =~ /debian.*/) { $platform = "debian"; } elsif ($os =~ /aix.*/) { $platform = "aix"; } elsif ($os =~ /AIX.*/) { $platform = "AIX"; } } if (($nodesetstate) && ($nodesetstate eq "netboot" || $nodesetstate eq "statelite")) { $stat = "netboot"; } $ospkglist = xCAT::SvrUtils->get_pkglist_file_name( "$installroot/custom/$stat/$platform", $profile, $os, $arch); if (!$ospkglist) { $ospkglist = xCAT::SvrUtils->get_pkglist_file_name( "$::XCATROOT/share/xcat/$stat/$platform", $profile, $os, $arch); } $pkglist = xCAT::SvrUtils->get_otherpkgs_pkglist_file_name( "$installroot/custom/$stat/$platform", $profile, $os, $arch); if (!$pkglist) { $pkglist = xCAT::SvrUtils->get_otherpkgs_pkglist_file_name( "$::XCATROOT/share/xcat/$stat/$platform", $profile, $os, $arch); } } } #print "pkglist=$pkglist\n"; #print "ospkglist=$ospkglist\n"; require xCAT::Postage; if ($ospkglist) { my $pkgtext = xCAT::Postage::get_pkglist_tex($ospkglist); my ($envlist,$pkgtext) = xCAT::Postage::get_envlist($pkgtext); if ($envlist) { $result .= "ENVLIST='".$envlist."'\n"; $result .= "export ENVLIST\n"; } if ($pkgtext) { $result .= "OSPKGS='".$pkgtext."'\n"; $result .= "export OSPKGS\n"; } } if ($pkglist) { my $pkgtext = xCAT::Postage::get_pkglist_tex($pkglist); if ($pkgtext) { my @sublists = split('#NEW_INSTALL_LIST#', $pkgtext); my $sl_index = 0; foreach (@sublists) { $sl_index++; my $tmp = $_; my ($envlist, $tmp) = xCAT::Postage::get_envlist($tmp); if ($envlist) { $result .= "ENVLIST$sl_index='".$envlist."'\n"; $result .= "export ENVLIST$sl_index\n"; } $result .= "OTHERPKGS$sl_index='".$tmp."'\n"; $result .= "export OTHERPKGS$sl_index\n"; } if ($sl_index > 0) { $result .= "OTHERPKGS_INDEX=$sl_index\n"; $result .= "export OTHERPKGS_INDEX\n"; } } } # SLES sdk if ($os =~ /sles.*/) { my $installdir = $::XCATSITEVALS{'installdir'} ? $::XCATSITEVALS{'installdir'} : "/install"; my $sdkdir = "$installdir/$os/$arch/sdk1"; if (-e "$sdkdir") { $result .= "SDKDIR='" . $sdkdir . "'\n"; $result .= "export SDKDIR\n"; } } # check if there are sync files to be handled my $syncfile; if ( ($provmethod) && ($provmethod ne "install") && ($provmethod ne "netboot") && ($provmethod ne "statelite")) { #my $osimagetab = xCAT::Table->new('osimage', -create => 1); #if ($osimagetab) #{ # (my $ref) = # $osimagetab->getAttribs( # {imagename => $provmethod}, 'osvers', # 'osarch', 'profile', # 'provmethod', 'synclists' # ); my $ref = $image_hash->{$provmethod}; if ($ref) { $syncfile = $ref->{'synclists'}; # if($ref->{'provmethod'}) { # $provmethod = $ref->{'provmethod'}; # } } #} } if (!$syncfile) { my $stat = "install"; if (($nodesetstate) && ($nodesetstate eq "netboot" || $nodesetstate eq "statelite")) { $stat = "netboot"; } $syncfile = xCAT::SvrUtils->getsynclistfile(undef, $os, $arch, $profile, $stat); } if (!$syncfile) { $result .= "NOSYNCFILES=1\n"; $result .= "export NOSYNCFILES\n"; } return $result; } sub getNetworks { my $nettab = xCAT::Table->new('networks'); unless ($nettab) { xCAT::MsgUtils->message("E", "Unable to open networks table"); return undef } my @nets = $nettab->getAllAttribs('net', 'mask', 'gateway'); return \@nets; } sub getDisklessNet() { my $nets = shift; my $setbootfromnet = shift; my $provmethod = shift; my $result; my $isdiskless = 0; my $bootfromnet = 0; if (($arch eq "ppc64") || ($os =~ /aix.*/i)) { # on Linux, the provmethod can be install,netboot or statelite, # on AIX, the provmethod can be null or image name #this is for Linux if ( ($provmethod) && (($provmethod eq "netboot") || ($provmethod eq "statelite"))) { $isdiskless = 1; } if ( ($os =~ /aix.*/i) && ($provmethod) && ($provmethod ne "install") && ($provmethod ne "netboot") && ($provmethod ne "statelite")) { my $nimtype; my $nimimagetab = xCAT::Table->new('nimimage', -create => 1); if ($nimimagetab) { (my $ref) = $nimimagetab->getAttribs({imagename => $provmethod}, 'nimtype'); if ($ref) { $nimtype = $ref->{'nimtype'}; } } if ($nimtype eq 'diskless') { $isdiskless = 1; } } if ($isdiskless) { (my $ip, my $mask, my $gw) = xCAT::Postage::net_parms($node, $nets); if (!$ip || !$mask || !$gw) { xCAT::MsgUtils->message( 'S', "Unable to determine IP, netmask or gateway for $node, can not set the node to boot from network" ); } else { $bootfromnet = 1; $result .= "NETMASK=$mask\n"; $result .= "export NETMASK\n"; $result .= "GATEWAY=$gw\n"; $result .= "export GATEWAY\n"; } } } $$setbootfromnet = $bootfromnet; return $result; } sub collect_all_attribs_for_tables_in_template { my $table = shift; my $nodes = shift; my $callback = shift; my $blankok; if(defined($table) ) { foreach my $tabname (keys %$table) { my $key_hash = $table->{$tabname}; my @keys = keys %$key_hash; my $key = $keys[0]; my $attrib_hash = $table->{$tabname}->{$key}; my @attribs = keys %$attrib_hash; my $tabh = xCAT::Table->new($tabname); unless ($tabh) { xCAT::MsgUtils->message( 'E', "Unable to open the table: $table." ); return; } my $ent; my $bynode=0; if ($key eq "THISNODE" or $key eq '$NODE') { $ent = $tabh->getNodesAttribs($nodes,@attribs); if ($ent) { foreach my $node (@$nodes) { if( $ent->{$node}->[0] ) { foreach my $attrib (@attribs) { $::GLOBAL_TAB_HASH{$tabname}{$node}{$attrib} = $ent->{$node}->[0]->{$attrib}; #for noderes.xcatmaster if ($tabname =~ /^noderes$/ && $attrib =~ /^xcatmaster$/ && ! exists($::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster})) { my $value; $value = xCAT::NetworkUtils->my_ip_facing($node); if ($value eq "0") { undef($value); } $::GLOBAL_TAB_HASH{$tabname}{$node}{$attrib} = $value; } # for nodetype.os and nodetype.arch if ($^O =~ /^linux/i && $tabname =~ /^nodetype$/ && ($attrib =~ /^(os|arch)$/)) { unless ( $::GLOBAL_TAB_HASH{nodetype}{$node}{'os'} or $::GLOBAL_TAB_HASH{nodetype}{$node}{'arch'}) { my $rsp; push @{$rsp->{data}}, "No os or arch setting in nodetype table for $node.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return -1; } } } } # for noderes.nfsserver and noderes.tftpserver if( defined($::GLOBAL_TAB_HASH{noderes}) && defined ($::GLOBAL_TAB_HASH{noderes}{$node} ) && defined ($::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster} ) ) { if(!defined ($::GLOBAL_TAB_HASH{noderes}{$node}{nfsserver}) ) { $::GLOBAL_TAB_HASH{noderes}{$node}{nfsserver} = $::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster}; } if(!defined ($::GLOBAL_TAB_HASH{noderes}{$node}{tftpserver}) ) { $::GLOBAL_TAB_HASH{noderes}{$node}{tftpserver} = $::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster}; } } } } $tabh->close; } } } } 1;