xcat-core/xCAT-server/lib/perl/xCAT/Template.pm

2024 lines
65 KiB
Perl

#!/usr/bin/perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
use xCAT::TZUtils;
use xCAT::WinUtils;
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 $lastmachinepassdata;
my $localadminenabled; #indicate whether Windows template has local logins enabled or not
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}) {
$lastmachinepassdata->{password}="";
}
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;
#support multiple paths of osimage in rh/sles diskfull installation
my @pkgdirs;
if ( defined($media_dir) ) {
@pkgdirs = split(",", $media_dir);
my $source;
my $c = 0;
foreach my $pkgdir(@pkgdirs) {
if( $platform =~ /^(rh|SL)$/ ) {
$source .= "repo --name=pkg$c --baseurl=http://#TABLE:noderes:\$NODE:nfsserver#/$pkgdir\n";
} elsif ($platform =~ /^(sles|suse)/) {
my $http = "http://#TABLE:noderes:\$NODE:nfsserver#$pkgdir";
$source .= " <listentry>
<media_url>$http</media_url>
<product>SuSE-Linux-pkg$c</product>
<product_dir>/</product_dir>
<ask_on_error config:type=\"boolean\">false</ask_on_error> <!-- available since openSUSE 11.0 -->
<name>SuSE-Linux-pkg$c</name> <!-- available since openSUSE 11.1/SLES11 (bnc#433981) -->
</listentry>";
}
$c++;
}
$inc =~ s/#INSTALL_SOURCES#/$source/g;
}
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 (<PKGLISTFILE>) {
chomp $_;
if (/^\s*#.*/ ){
next;
}
$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/#WINTIMEZONE#/xCAT::TZUtils::get_wintimezone()/eg;
$inc =~ s/#WINPRODKEY:([^#]+)#/get_win_prodkey($1)/eg;
$inc =~ s/#WINNETCFG#/windows_net_cfg()/eg;
$inc =~ s/#WINADJOIN#/windows_join_data()/eg;
$inc =~ s/#WINDNSCFG#/windows_dns_cfg()/eg;
$inc =~ s/#WINACCOUNTDATA#/windows_account_data()/eg;
$inc =~ s/#WINDISABLENULLADMIN#/windows_disable_null_admin()/eg;
$inc =~ s/#MANAGEDADDRESSMODE#/managed_address_mode()/eg;
$inc =~ s/#HOSTNAME#/$node/g;
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 = "%include /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 =~ /<!-- XCAT-PARTITION-START -->/){
my $tempstr = "<drive><device>XCATPARTITIONTEMP</device></drive>";
$inc =~ s/<!-- XCAT-PARTITION-START -->[\s\S]*<!-- XCAT-PARTITION-END -->/$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 =~ /<!-- XCAT-PARTITION-START -->/){
$inc =~ s/<!-- XCAT-PARTITION-START -->[\s\S]*<!-- XCAT-PARTITION-END -->/$partcontent/;
}
}
}
}
if ($tmplerr) {
close ($outh);
return $tmplerr;
}
print $outh $inc;
close($outh);
return 0;
}
sub windows_disable_null_admin {
#in the event where windows_account_data has not set an administrator user, we explicitly disable the administrator user
unless ($localadminenabled) {
return "<RunSynchronousCommand wcm:action=\"add\">\r
<Order>100</Order>\r
<Path>cmd /c %systemroot%\\system32\\net.exe user Administrator /active:no</Path>\r
</RunSynchronousCommand>";
}
return "";
}
sub windows_account_data {
#this will add domain accounts if configured to be in active directory
#it will also put in an administrator password for local account, *if* specified
my $passtab = xCAT::Table->new('passwd',-create=>0);
my $useraccountxml="";
$localadminenabled=0;
if ($passtab) {
my $passent = $passtab->getAttribs({key=>"system",username=>"Administrator"},['password']);
if ($passent and $passent->{password}) {
$useraccountxml="<AdministratorPassword>\r\n<Value>".$passent->{password}."</Value>\r\n<PlainText>true</PlainText>\r\n</AdministratorPassword>\r\n";
$useraccountxml.="<!-- Plaintext=false would only protect against the most cursory over the shoulder glance, this implementation opts not to even give the illusion of privacy by only doing plaintext. -->\r\n";
$localadminenabled=1;
}
}
my $domain;
my $doment;
my $domaintab = xCAT::Table->new('domain',-create=>0);
if ($domaintab) {
$doment = $domaintab->getNodeAttribs($node,['authdomain','type'],prefetchcache=>1);
}
unless ($::XCATSITEVALS{directoryprovider} eq "activedirectory" or ($doment and $doment->{type} eq "activedirectory")) {
return $useraccountxml;
}
if ($doment and $doment->{authdomain}) {
$domain = $doment->{authdomain};
} else {
$domain = $::XCATSITEVALS{domain};
}
$useraccountxml.="<DomainAccounts><DomainAccountList>\r\n<DomainAccount wcm:action=\"add\">\r\n<Group>Administrators</Group>\r\n<Name>Domain Admins</Name>\r\n</DomainAccount>\r\n<Domain>".$domain."</Domain>\r\n</DomainAccountList>\r\n</DomainAccounts>\r\n";
return $useraccountxml;
}
sub windows_net_cfg {
if ($::XCATSITEVALS{managedaddressmode} =~ /static/) { return "<!-- WINCFG Static not supported -->"; }
unless ($::XCATSITEVALS{managedaddressmode} =~ /autoula/) {
return ""; #windows default behavior
}
#autoula,
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'],prefetchcache=>1);
unless ($ent and $ent->{mac}) { die "missing mac data for $node"; }
my $suffix = $ent->{mac};
my $mac = $suffix;
$suffix = lc($suffix);
unless ($hoststab) { $hoststab = xCAT::Table->new('hosts',-create=>1); }
my $ulaaddr = autoulaaddress($suffix);
$hoststab->setNodeAttribs($node,{ip=>$ulaaddr});
return '<component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'."\r\n<Interfaces><Interface wcm:action=\"add\">\r\n<Identifier>$mac</Identifier>\r\n<UnicastIpAddresses>\r\n<IpAddress wcm:action=\"add\" wcm:keyValue=\"1\">$ulaaddr/64</IpAddress>\r\n</UnicastIpAddresses>\r\n</Interface>\r\n</Interfaces>\r\n</component>\r\n";
}
sub windows_dns_cfg {
my $domain;
my $doment;
my $noderesent;
my $noderestab = xCAT::Table->new("noderes",-create=>0);
unless ($noderestab) { return ""; }
$noderesent = $noderestab->getNodeAttribs($node,['nameservers'],prefetchcache=>1);
unless ($noderesent and $noderesent->{nameservers}) { return ""; }
my $mac="==PRINIC==";
my $mactab = xCAT::Table->new('mac',-create=>0);
if ($mactab) {
my $macent = $mactab->getNodeAttribs($node,['mac'],prefetchcache=>1);
if ($macent and $macent->{mac}) {
$mac=$macent->{mac};
$mac=~ s/!.*//;
$mac=~ s/\|.*//;
$mac =~ s/:/-/g;
}
}
my $nameservers = $noderesent->{nameservers};
my $domaintab = xCAT::Table->new('domain',-create=>0);
if ($domaintab) {
$doment = $domaintab->getNodeAttribs($node,['authdomain'],prefetchcache=>1);
}
if ($doment and $doment->{authdomain}) {
$domain = $doment->{authdomain};
} else {
$domain = $::XCATSITEVALS{domain};
}
my $componentxml = '<component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'."\r\n<DNSDomain>$domain</DNSDomain>\r\n".
"<Interfaces><Interface wcm:action=\"add\">\r\n<Identifier>$mac</Identifier>\r\n<DNSServerSearchOrder>\r\n";
my $idx=1;
foreach (split /,/,$nameservers) {
$componentxml.="<IpAddress wcm:action=\"add\" wcm:keyValue=\"$idx\">$_</IpAddress>\r\n";
$idx+=1;
}
$componentxml .= "</DNSServerSearchOrder>\r\n</Interface>\r\n</Interfaces>\r\n</component>\r\n";
return $componentxml;
}
#this will examine table data, decide *if* a Microsoft-Windows-UnattendedJoin is warranted
#there are two variants in how to proceed:
#-Hide domain administrator from node: xCAT will use MACHINEPASSWORD to do joining to AD. Currently requires SSL be enabled on DC. Samba 4 TODO
#-Provide domain administrator credentials, avoiding the SSL scenario. This is by default forbidden as it is high risk for exposing sensitive credentials.
# Also populate MachineObjectOU
sub windows_join_data {
my $doment;
my $domaintab = xCAT::Table->new('domain',-create=>0);
if ($domaintab) {
$doment = $domaintab->getNodeAttribs($node,['ou','type','authdomain','adminuser','adminpassword'],prefetchcache=>1);
}
unless ($::XCATSITEVALS{directoryprovider} eq "activedirectory" or ($doment and $doment->{type} eq "activedirectory")) {
return "";
}
#we are still here, meaning configuration has a domain and activedirectory set, probably want to join..
#TODO: provide a per-node 'disable' so that non-AD could be mixed into a nominally AD environment
my $prejoin =1;
if (defined $::XCATSITEVALS{prejoinactivedirectory} and not $::XCATSITEVALS{prejoinactivedirectory} ) {
$prejoin = 0;
}
my $domain;
my $ou;
if ($doment and $doment->{ou}) {
$ou = $doment->{ou};
}
if ($doment and $doment->{authdomain}) {
$domain = $doment->{authdomain};
} else {
$domain = $::XCATSITEVALS{domain};
}
my $componentxml = '<component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'."\r\n<Identification>\r\n<JoinDomain>".$domain."</JoinDomain>\r\n";
if ($ou) {
$componentxml .= "<MachineObjectOU>".$ou."</MachineObjectOU>\r\n";
}
if ($prejoin) {
my $adinfo = machinepassword(wantref=>1); #TODO: needs rearranging in non prejoin case
#a note, MS is incorrect when they document unsecure join as " UnsecureJoin is performed, by using a null session with a pre-existing account. This means there is no authentication to the domain controller when configuring the machine account; it is done anonymously".
#the more informative bit is http://technet.microsoft.com/en-us/library/cc730845%28v=ws.10%29.aspx which says of 'securejoin': this method is actually less secure because the credentials reside in the ImageUnattend.xml file in plain text.
#xCAT is generating a one-time password that is kept as limited as is feasible for the deployment strategy
#in theory, a domain join will either fail of the one-time password is compromised and changed, or domain
#join will invalidate any 'snooped' one time password
$componentxml .= "<MachinePassword>".$adinfo->{password}."</MachinePassword>\n<UnsecureJoin>true</UnsecureJoin>\n";
} else { #this is the pass-through credentials case, currrently inaccessible until TODO, this must be used
#with care as used incorrectly, an LDAP manager account is at high risk of compromise
my $adminuser;
my $adminpass;
if ($doment and $doment->{adminuser}) {
$adminuser = $doment->{adminuser};
}
if ($doment and $doment->{adminpassword}) {
$adminpass = $doment->{adminpassword};
}
unless ($adminuser and $adminpass) {
my $passtab = xCAT::Table->new('passwd',-create=>0);
unless ($passtab) { sendmsg([1,"Error authenticating to Active Directory"],$node); return; }
my @adpents = $passtab->getAttribs({key=>'activedirectory'},['username','password','authdomain']);
my $adpent;
foreach $adpent (@adpents) {
if ($adpent and $adpent->{authdomain} and $adpent->{authdomain} ne $domain) { next; }
if ($adpent and $adpent->{username} and $adpent->{password}) {
$adminuser = $adpent->{username};
$adminpass = $adpent->{password};
last;
}
}
}
unless ($adminuser and $adminpass) { die "Missing active directory admin auth data from passwd table" }
$componentxml .= "<Credentials><Domain>".$domain."</Domain>\r\n<Username>".$adminuser."</Username>\r\n<Password>".$adminpass."</Password>\r\n</Credentials>\r\n";
}
$componentxml .= "</Identification>\r\n</component>\r\n";
}
sub get_win_prodkey {
my $osvariant = shift;
my $keytab = xCAT::Table->new("prodkey",-create=>0);
my $keyent;
if ($keytab) {
my @keyents = $keytab->getNodeAttribs($node,[qw/product key/]);
foreach my $tkey (@keyents) {
if ($tkey->{product} eq $osvariant) {
$keyent = $tkey;
last;
} elsif (not $tkey->{product}) {
$keyent = $tkey;
}
}
unless ($keyent) {
$keyent = $keytab->getAttribs({product=>$osvariant},"key");
}
}
if ($keyent) {
return "<ProductKey><WillShowUI>OnError</WillShowUI><Key>".$keyent->{key}."</Key></ProductKey>";
}
if ($xCAT::WinUtils::kmskeymap{$osvariant}) {
return "<ProductKey><WillShowUI>OnError</WillShowUI><Key>".$xCAT::WinUtils::kmskeymap{$osvariant}."</Key></ProductKey>";
}
return ""; #in the event that we have no specified key and no KMS key, then try with no key, user may have used some other mechanism
}
sub managed_address_mode {
return $::XCATSITEVALS{managedaddressmode};
}
sub esxipv6setup {
if (not $::XCATSITEVALS{managedaddressmode} or $::XCATSITEVALS{managedaddressmode} =~ /v4/) { return ""; } # blank line for ipv4 schemes
my $v6addr;
if ($::XCATSITEVALS{managedaddressmode} eq "autoula") {
my $hoststab;
my $mactab = xCAT::Table->new('mac',-create=>0);
my $ent = $mactab->getNodeAttribs($node,['mac'],prefetchcache=>1);
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); }
$v6addr = autoulaaddress($suffix);
$hoststab->setNodeAttribs($node,{ip=>$v6addr});
} else {
my $hoststab = xCAT::Table->new('hosts',-create=>0);
unless ($hoststab) { die "unable to proceed, no hosts table to read from" }
my $ent = $hoststab->getNodeAttribs($node,["ip"],prefetchcache=>1);
unless ($ent and $ent->{ip}) { die "no hosts table entry with viable IP in hosts table for $node" }
$v6addr = $ent->{ip};
unless ($v6addr =~ /:/) { die "incorrect format for static ipv6 in hosts table for $node" }
}
return 'esxcfg-vmknic -i '.$v6addr.'/64 "Management Network"'."#ESXISTATICV6\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'],prefetchcache=>1);
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;
} elsif ($::XCATSITEVALS{managedaddressmode} =~ /static/) {
return "#KSNET static unsupported";
} 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 {
my %funargs = @_;
if ($lastmachinepassdata->{password}) { #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
if ($funargs{wantref}) {
return $lastmachinepassdata;
}
return $lastmachinepassdata->{password};
}
my $passdata;
my $domaintab = xCAT::Table->new('domain');
$ENV{HOME}='/etc/xcat';
$ENV{LDAPRC}='ad.ldaprc';
my $ou;
my $domain;
if ($domaintab) {
my $ouent = $domaintab->getNodeAttribs($node,['ou','authdomain'],prefetchcache=>1);
if ($ouent and $ouent->{ou}) {
$ou = $ouent->{ou};
}
if ($ouent and $ouent->{authdomain}) {
$domain = $ouent->{authdomain};
}
}
$passdata->{ou}=$ou;
#my $sitetab = xCAT::Table->new('site');
#unless ($sitetab) {
# return "ERROR: unable to open site table";
#}
#(my $et) = $sitetab->getAttribs({key=>"domain"},'value');
unless ($domain) {
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 or in domain.authdomain for $node";
}
}
$passdata->{domain}=$domain;
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 @adpents = $passtab->getAttribs({key=>'activedirectory'},['username','password','authdomain']);
my $adpent;
my $username;
my $password;
foreach $adpent (@adpents) {
if ($adpent and $adpent->{authdomain} and $adpent->{authdomain} ne $domain) { next; }
if ($adpent and $adpent->{username} and $adpent->{password}) {
$username = $adpent->{username};
$password = $adpent->{password};
last;
}
}
unless ($username and $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");
my $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;
}
}
$passdata->{dc} = $server;
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 {
$passdata->{password}=$data->{password};
$lastmachinepassdata=$passdata;
if ($funargs{wantref}) {
return $passdata;
}
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 = "<pattern>";
$pkge = "</pattern>";
} else {
$pkgb = "<package>";
$pkge = "</package>";
}
}
while(<INCLUDE>) {
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(@_);
# 1 - MD5, 5 - SHA256, 6 - SHA512
unless (($result =~ /^\$1\$/) || ($result =~ /^\$5\$/) || ($result =~ /^\$6\$/)) {
$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} ) ) {
if( !defined( $::GLOBAL_TAB_HASH{$table}{$key}) ) {
return "''";
}
if( defined($::GLOBAL_TAB_HASH{$table}{$key}{$field}) ) {
return "'".$::GLOBAL_TAB_HASH{$table}{$key}{$field}."'";
} else {
return "''";
}
}
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(<TAB>) {
# 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;
%::GLOBAL_SN_HASH: getservicenode() will get all the nodes in the servicenode table. And the
result will store in the %::GLOBAL_SN_HASH. The fortmac of the value setting
looks like:
$::GLOBAL_SN_HASH{$servicenod1} = 1;
Error:
none
Example:
Comments:
none
=cut
#-----------------------------------------------------------------------------
my $os;
my $profile;
my $arch;
my $provmethod;
my $mn;
%::GLOBAL_TAB_HASH;
%::GLOBAL_SN_HASH;
%::GLOBAL_TABDUMP_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;
}
$mn = xCAT::Utils->noderangecontainsMn(@$nodes);
my $inc;
my $t_inc;
my %table;
my @tabs;
my %dump_results;
#First load input into memory..
while (<$inh>) {
my $line = $_;
if ($line !~/^##/ ) {
$t_inc.=$line;
}
if( $line =~ /#TABLE:([^:]+):([^:]+):([^#]+)#/ ) {
my $tabname=$1;
my $key=$2;
my $attrib = $3;
$table{$tabname}{$key}{$attrib} = 1;
}
if( $line =~ /^tabdump\(([\w]+)\)/) {
my $tabname = $1;
if( $tabname !~ /^(auditlog|bootparams|chain|deps|domain|eventlog|firmware|hypervisor|iscsi|kvm_nodedata|mac|nics|ipmi|mp|ppc|ppcdirect|site|websrv|zvm|statelite|rack|hosts|prodkey|switch|node)/) {
push @tabs, $tabname;
}
}
}
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);
#print Dumper(\@tabs);
dump_all_attribs_in_tabs(\@tabs,\%::GLOBAL_TABDUMP_HASH, $callback);
#print Dumper(\%::GLOBAL_TABDUMP_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();
# For AIX, get the password and cryptmethod for system root
my $aixrootpasswdvars = getAIXPasswdVars();
#%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();
getservicenode();
#print Dumper(\%::GLOBAL_SN_HASH);
#
my $scriptdir = "$tftpdir/mypostscripts/";
if( ! (-d $scriptdir )) {
mkdir($scriptdir,0777);
}
my $postfix;
my @entries = xCAT::TableUtils->get_site_attribute("precreatemypostscripts");
if ($entries[0] ) {
$entries[0] =~ tr/a-z/A-Z/;
if ($entries[0] !~ /^(1|YES)$/ ) {
$postfix="tmp";
}
} else {
$postfix="tmp";
}
foreach my $n (@$nodes ) {
$node = $n;
$inc = $t_inc;
my $script;
my $scriptfile;
if( defined( $postfix ) ) {
$scriptfile = "$tftpdir/mypostscripts/mypostscript.$node.tmp";
} else {
$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;
my $noderesent;
if( defined( $::GLOBAL_TAB_HASH{noderes}) && defined( $::GLOBAL_TAB_HASH{noderes}{$node}) ) {
$master = $::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster};
$noderesent = $::GLOBAL_TAB_HASH{noderes}{$node};
}
if( !defined($master) ) {
$::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster} = $::XCATSITEVALS{master};
}
#get the node type, service node or compute node
my $nodetype = getNodeType($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;
if($provmethod !~ /^install$|^netboot$|^statelite$/){ # using imagename
$osimgname = $provmethod;
}
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:([^:]+):([^:]+):([^:]+):BLANKOKAY#/tabdb($1,$2,$3,1)/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/#AIX_ROOT_PW_VARS_EXPORT#/$aixrootpasswdvars/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/tabdump\(([\w]+)\)/tabdump($1)/eg;
$inc =~ s/#Subroutine:([^:]+)::([^:]+)::([^:]+):([^#]+)#/subroutine($1,$2,$3,$4)/eg;
print $script $inc;
close($script_fp{$node});
}
undef(%::GLOBAL_TAB_HASH);
undef(%::GLOBAL_SN_HASH);
undef(%::GLOBAL_TABDUMP_HASH);
return 0;
}
sub getservicenode
{
# 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};
$::GLOBAL_SN_HASH{$node}=1
}
return 0;
}
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 $result;
if ( $node =~ /^$mn$/) {
$result="MN";
return $result;
}
# see if this is a service or compute node?
if ($::GLOBAL_SN_HASH{$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 getAIXPasswdVars
{
my $result;
if ($^O =~ /^aix/i) {
require xCAT::PPCdb;
my $et = xCAT::PPCdb::get_usr_passwd('system', 'root');
if ($et and defined($et->{'password'}))
{
$result .= "ROOTPW=" . $et->{'password'} . "\n";
$result .= "export ROOTPW\n";
}
if ($et and defined($et->{'cryptmethod'}))
{
$result .= "CRYPTMETHOD=" . $et->{'cryptmethod'} . "\n";
$result .= "export CRYPTMETHOD\n";
}
}
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') {
if( $tabname =~ /^noderes$/ ) {
@attribs = (@attribs, "netboot", "tftpdir"); ## add the attribs which will be needed in other place.
}
$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} ) ) {
$::GLOBAL_TAB_HASH{noderes}{$node}{xcatmaster} = $::XCATSITEVALS{master};
}
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};
}
#if the values are not got, we will set them to '';
foreach my $attrib (@attribs) {
if( !defined($::GLOBAL_TAB_HASH{$tabname}) || !defined($::GLOBAL_TAB_HASH{$tabname}{$node}) || !defined($::GLOBAL_TAB_HASH{$tabname}{$node}{$attrib})) {
$::GLOBAL_TAB_HASH{$tabname}{$node}{$attrib} = '';
}
}
}
}
$tabh->close;
#}
}
}
}
sub dump_all_attribs_in_tabs
{
my $tabs = shift;
my $result = shift;
my $callback = shift;
my $rsp;
my $tab;
foreach $tab (@$tabs) {
my $ptab = xCAT::Table->new("$tab");
unless ($ptab) {
push @{$rsp->{data}},
"Unable to open $tab table";
xCAT::MsgUtils->message("E", $rsp, $callback);
return undef;
}
my $tabdetails = xCAT::Table->getTableSchema($tab);
my $cols = $tabdetails->{cols};
my $recs = $ptab->getAllEntries();
my $sum = @$recs;
$tab =~ tr/a-z/A-Z/;
my $res = "$tab"."_LINES=$sum\n";
$res .= "export $tab"."_LINES\n";
my $num = 0;
my $rec;
foreach $rec (@$recs) {
my $attrib;
$num++;
my $values;
my $t;
foreach $attrib (@$cols) {
my $val = $rec->{$attrib};
# We use "||" as the delimiter of the attribute=value pair in each line.
# Uses could put special characters in the comments attribute.
# So we put the comments attribute as the last in the list.
# The parsing could consider everything after "comments=" as the comments value, regardless of whether or not it had "||" in it.
if( $attrib =~ /^comments$/) {
$t = $val;
} else {
$values .="$attrib=$val||";
if( $attrib =~ /^disable$/) {
$values .="comments=$t";
}
}
}
$values="$tab"."_LINE$num=\'$values\'\n";
$values .="export $tab"."_LINE$num\n";
$res .= $values;
}
$tab =~ tr/A-Z/a-z/;
$result->{$tab} = $res;
}
}
sub tabdump
{
my $tab =shift;
my $value= $::GLOBAL_TABDUMP_HASH{$tab};
return $value;
}
1;