mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2935 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			2935 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2012 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head1
 | |
| 
 | |
|     xCAT plugin to support Profiled nodes management
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| package xCAT_plugin::profilednodes;
 | |
| 
 | |
| use strict;
 | |
| use warnings;
 | |
| use Getopt::Long qw(:config no_ignore_case);
 | |
| use Data::Dumper;
 | |
| require xCAT::Table;
 | |
| require xCAT::Utils;
 | |
| require xCAT::TableUtils;
 | |
| require xCAT::NetworkUtils;
 | |
| require xCAT::MsgUtils;
 | |
| require xCAT::CFMUtils;
 | |
| require xCAT::ProfiledNodeUtils;
 | |
| 
 | |
| # Globals.
 | |
| # These 2 global variables are for storing the parse result of hostinfo file.
 | |
| # These 2 global varialbes are set in lib xCAT::ProfiledNodeUtils->parse_nodeinfo_file.
 | |
| #%::profiledNodeAttrs;
 | |
| #@::profiledNodeObjNames;
 | |
| 
 | |
| # All database records.
 | |
| my %allhostnames;
 | |
| my %allbmcips;
 | |
| my %allmacs;
 | |
| my %allcecs;
 | |
| my %alllparids;
 | |
| my %allmacsupper;
 | |
| my %allips;
 | |
| my %allinstallips;
 | |
| my %allnicips;
 | |
| my %allracks;
 | |
| my %allchassis;
 | |
| my %allswitches;
 | |
| my %all_switchports;
 | |
| my %allvmhosts;
 | |
| 
 | |
| my @switch_records;
 | |
| my $netboot;
 | |
| 
 | |
| # The array of all chassis which is special CMM
 | |
| my %allcmmchassis;
 | |
| my %allothernics;
 | |
| 
 | |
| # Define parameters for xcat requests.
 | |
| my $request;
 | |
| my $callback;
 | |
| my $request_command;
 | |
| my $command;
 | |
| my $args;
 | |
| 
 | |
| # Put arguments in a hash.
 | |
| my %args_dict;
 | |
| my %general_arg;
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  handled_commands
 | |
| 
 | |
|     Return list of commands handled by this plugin
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub handled_commands {
 | |
|     return {
 | |
|         nodeimport        => 'profilednodes',
 | |
|         nodepurge         => 'profilednodes',
 | |
|         nodechprofile     => 'profilednodes',
 | |
|         noderegenips      => 'profilednodes',
 | |
|         noderefresh       => 'profilednodes',
 | |
|         nodediscoverstart => 'profilednodes',
 | |
|         nodediscoverstop  => 'profilednodes',
 | |
|         nodediscoverls    => 'profilednodes',
 | |
| 
 | |
|         #nodediscoverstatus => 'profilednodes',
 | |
|         nodeaddunmged => 'profilednodes',
 | |
|         nodechmac     => 'profilednodes',
 | |
|         findme        => 'profilednodes',
 | |
|     };
 | |
| }
 | |
| 
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  process_request
 | |
| 
 | |
|     Process the command.  This is the main call.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub process_request {
 | |
| 
 | |
|     $request  = shift;
 | |
|     $callback = shift;
 | |
| 
 | |
|     #$::CALLBACK = $callback;
 | |
|     $request_command = shift;
 | |
|     $command         = $request->{command}->[0];
 | |
|     $args            = $request->{arg};
 | |
| 
 | |
|     my $gereral_arg = get_general_args();
 | |
| 
 | |
|     # There is no need to acquire lock for command nodediscoverstatus, nodediscoverls and noderegenips.
 | |
|     if ($command eq "nodediscoverstatus") {
 | |
|         nodediscoverstatus();
 | |
|         return;
 | |
|     } elsif ($command eq "nodediscoverls") {
 | |
|         nodediscoverls();
 | |
|         return;
 | |
|     } elsif ($command eq "noderegenips") {
 | |
|         noderegenips();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $non_block = 1;
 | |
|     if (defined $general_arg{'blockmode'}) {
 | |
|         if ($general_arg{'blockmode'} == 1) {
 | |
|             $non_block = 0;
 | |
|         }
 | |
|     }
 | |
|     my $lock = xCAT::Utils->acquire_lock("nodemgmt", $non_block);
 | |
|     if (!$lock) {
 | |
|         setrsp_errormsg("Cannot acquire lock, another process is already running.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # These commands should make sure no discover is running.
 | |
|     if (grep { $_ eq $command } ("nodeimport", "nodepurge", "nodechprofile", "nodeaddunmged", "nodechmac")) {
 | |
|         my $discover_running = xCAT::ProfiledNodeUtils->is_discover_started();
 | |
|         if ($discover_running) {
 | |
|             my %errormsg_dict = (
 | |
|                 'nodeimport'    => 'import nodes',
 | |
|                 'nodepurge'     => 'remove nodes',
 | |
|                 'nodechprofile' => 'change profiles',
 | |
|                 'nodeaddunmged' => 'add devices',
 | |
|                 'nodechmac'     => 'change MAC address'
 | |
|             );
 | |
| 
 | |
|             setrsp_errormsg("Cannot $errormsg_dict{$command} while node discovery is running.");
 | |
|             xCAT::Utils->release_lock($lock, $non_block);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($command eq "nodeimport") {
 | |
|         nodeimport();
 | |
|     } elsif ($command eq "nodepurge") {
 | |
|         nodepurge();
 | |
|     } elsif ($command eq "nodechprofile") {
 | |
|         nodechprofile();
 | |
|     } elsif ($command eq "noderefresh") {
 | |
|         noderefresh();
 | |
|     } elsif ($command eq "nodediscoverstart") {
 | |
|         nodediscoverstart();
 | |
|     } elsif ($command eq "nodediscoverstop") {
 | |
|         nodediscoverstop();
 | |
|     } elsif ($command eq "findme") {
 | |
|         findme();
 | |
|     } elsif ($command eq "nodeaddunmged") {
 | |
|         nodeaddunmged();
 | |
|     } elsif ($command eq "nodechmac") {
 | |
|         nodechmac();
 | |
|     }
 | |
| 
 | |
|     xCAT::Utils->release_lock($lock, $non_block);
 | |
| }
 | |
| 
 | |
| sub get_general_args
 | |
| {
 | |
|     my ($help, $ver, $blockmode);
 | |
|     %general_arg = ();
 | |
|     @ARGV        = ();
 | |
|     if ($args) {
 | |
|         @ARGV = @$args;
 | |
|     }
 | |
|     GetOptions(
 | |
|         'h|help'    => \$help,
 | |
|         'v|version' => \$ver,
 | |
|         'b|block'   => \$blockmode,
 | |
|     );
 | |
| 
 | |
|     if ($help) {
 | |
|         $general_arg{'help'} = 1;
 | |
|     }
 | |
|     if ($ver) {
 | |
|         $general_arg{'version'} = 1;
 | |
|     }
 | |
|     if ($blockmode) {
 | |
|         $general_arg{'blockmode'} = 1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  parse_args
 | |
| 
 | |
|     Description : Parse arguments. We placed arguments into a directory %args_dict
 | |
|     Arguments   : args - args of xCAT requests.
 | |
|     Returns     : undef - parse succeed.
 | |
|                   A string - parse arguments failed, the return value is error message.
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------
 | |
| 
 | |
| sub parse_args {
 | |
|     %args_dict = ();
 | |
|     foreach my $arg (@ARGV) {
 | |
|         my @argarray = split(/=/, $arg);
 | |
|         my $arglen = @argarray;
 | |
|         if ($arglen > 2) {
 | |
|             return "Illegal argument $arg specified.";
 | |
|         }
 | |
| 
 | |
|         # translate the profile names into real group names in db.
 | |
|         if ($argarray[1])
 | |
|         {
 | |
|             if ($argarray[0] eq "networkprofile") {
 | |
|                 $args_dict{ $argarray[0] } = "__NetworkProfile_" . $argarray[1];
 | |
|             } elsif ($argarray[0] eq "imageprofile") {
 | |
|                 $args_dict{ $argarray[0] } = "__ImageProfile_" . $argarray[1];
 | |
|             } elsif ($argarray[0] eq "hardwareprofile") {
 | |
|                 $args_dict{ $argarray[0] } = "__HardwareProfile_" . $argarray[1];
 | |
|             } else {
 | |
|                 $args_dict{ $argarray[0] } = $argarray[1];
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return undef;
 | |
| }
 | |
| 
 | |
| sub validate_args {
 | |
|     my $helpmsg            = shift;
 | |
|     my $enabledparamsref   = shift;
 | |
|     my $mandatoryparamsref = shift;
 | |
| 
 | |
|     if (defined $general_arg{'help'}) {
 | |
|         if ($general_arg{'help'} == 1) {
 | |
|             my %process_help_commands = (
 | |
|                 'nodediscoverstart'  => 1,
 | |
|                 'nodediscoverstop'   => 1,
 | |
|                 'nodediscoverls'     => 1,
 | |
|                 'nodediscoverstatus' => 1,
 | |
|             );
 | |
| 
 | |
|             # do not process help message for these noddiscover* commands, cover them in seqdiscovery.pm
 | |
|             unless ($process_help_commands{$command} == 1) {
 | |
|                 setrsp_infostr($helpmsg);
 | |
|                 return 0;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $parseret = parse_args();
 | |
|     if ($parseret) {
 | |
|         setrsp_errormsg($parseret);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     # If specified the nodrange= arg, we asume that the sequential discovery will be started
 | |
|     if (defined $args_dict{'noderange'}) {
 | |
| 
 | |
|         # This is a sequential discovery request, just return to make sequential to handle it
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     # Mandatory arguments.
 | |
|     my @mandatoryparams = ();
 | |
|     if ($mandatoryparamsref) {
 | |
|         @mandatoryparams = @$mandatoryparamsref;
 | |
|     }
 | |
| 
 | |
|     if (@mandatoryparams) {
 | |
|         my $profiledis;
 | |
|         foreach (@mandatoryparams) {
 | |
|             if (exists($args_dict{$_})) {
 | |
| 
 | |
|                 # this is for profile discovery
 | |
|                 $profiledis = 1;
 | |
|                 last;
 | |
|             }
 | |
|         }
 | |
|         unless ($profiledis) {
 | |
| 
 | |
|             # Not see the nodrange and 'networkprofile', 'imageprofile', 'hostnameformat'
 | |
|             # return to make sequential discovery to display help message
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     foreach (@mandatoryparams) {
 | |
|         if (!exists($args_dict{$_})) {
 | |
|             setrsp_errormsg("For profile discovery, the $_ option must be specified.");
 | |
|             setrsp_infostr($helpmsg);
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Make sure the specified parameters are valid ones.
 | |
|     my @enabledparams = ();
 | |
|     if ($enabledparamsref) {
 | |
|         @enabledparams = @$enabledparamsref;
 | |
|     }
 | |
| 
 | |
|     foreach my $argname (keys %args_dict) {
 | |
|         if (!grep { $_ eq $argname } @enabledparams) {
 | |
|             setrsp_errormsg("Illegal attribute $argname specified.");
 | |
|             setrsp_infostr($helpmsg);
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3 nodeimport
 | |
| 
 | |
|     Description :
 | |
|     Create profiled nodes by importing hostinfo file.
 | |
|     This sub maps to request "nodeimport", we need to call this command from CLI like following steps:
 | |
|     # ln -s /opt/xcat/bin/xcatclientnnr /opt/xcat/bin/nodeimport
 | |
|     # nodeimport file=/root/hostinfo.file networkprofile=network_cn imageprofile=rhel63_cn hardwareprofile=ipmi groups=group1,group2
 | |
| 
 | |
|     The hostinfo file should be written like: (MAC address is mandatory attribute)
 | |
|     # Auto generate hostname for this node entry.
 | |
|     __hostname__:
 | |
|        mac=11:11:11:11:11:11
 | |
|     # Specified hostname node.
 | |
|     node01:
 | |
|        mac=22:22:22:22:22:22
 | |
| 
 | |
|     After this call finished, the compute node's info will be updated automatically in /etc/hosts, dns config, dhcp config, TFTP config...
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodeimport {
 | |
| 
 | |
|     # Parse arges.
 | |
|     xCAT::MsgUtils->message('S', "Import profiled nodes through hostinfo file.");
 | |
| 
 | |
|     my $helpmsg = "nodeimport: create profiled nodes by importing hostinfo file.
 | |
| Usage:
 | |
| \tnodeimport file=<hostinfo file> networkprofile=<networkprofile> imageprofile=<imageprofile> hostnameformat=<hostnameformat> [hardwareprofile=<hardwareprofile>] [groups=<groups>]
 | |
| \tnodeimport [-h|--help]
 | |
| \tnodeimport {-v|--version}";
 | |
| 
 | |
|     my @enabledparams = ('file', 'groups', 'networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat');
 | |
|     my @mandatoryparams = ('file', 'networkprofile', 'imageprofile', 'hostnameformat');
 | |
| 
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams, \@mandatoryparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!(-e $args_dict{'file'})) {
 | |
|         setrsp_errormsg("Node information file does not exist.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # validate hostnameformat:
 | |
|     my $nameformattype = xCAT::ProfiledNodeUtils->get_hostname_format_type($args_dict{'hostnameformat'});
 | |
|     if ($nameformattype eq "unknown") {
 | |
|         setrsp_errormsg("Invalid node name format: $args_dict{'hostnameformat'}");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Validate if profile consistent
 | |
|     my $imageprofile    = $args_dict{'imageprofile'};
 | |
|     my $networkprofile  = $args_dict{'networkprofile'};
 | |
|     my $hardwareprofile = $args_dict{'hardwareprofile'};
 | |
|     my ($returncode, $errmsg) = xCAT::ProfiledNodeUtils->check_profile_consistent($imageprofile, $networkprofile, $hardwareprofile);
 | |
|     if (not $returncode) {
 | |
|         setrsp_errormsg($errmsg);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Get the netboot attribute for node
 | |
|     my ($retcode, $retval) = xCAT::ProfiledNodeUtils->get_netboot_attr($imageprofile, $hardwareprofile);
 | |
|     if (not $retcode) {
 | |
|         setrsp_errormsg($retval);
 | |
|         return;
 | |
|     }
 | |
|     $netboot = $retval;
 | |
| 
 | |
|     # Get database records: all hostnames, all ips, all racks...
 | |
|     xCAT::MsgUtils->message('S', "Getting database records.");
 | |
|     my $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('nodelist', 'node');
 | |
|     %allhostnames = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
 | |
|     %allbmcips = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('mac', 'mac');
 | |
|     %allmacs = %$recordsref;
 | |
| 
 | |
|     # Get all FSP ip address
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ppc', 'hcp');
 | |
|     my %allfspips = %$recordsref;
 | |
| 
 | |
|     # Get all switches name
 | |
|     $recordsref  = xCAT::ProfiledNodeUtils->get_db_switches();
 | |
|     %allswitches = %$recordsref;
 | |
| 
 | |
|     # Get all switches_switchport
 | |
|     $recordsref      = xCAT::ProfiledNodeUtils->get_db_switchports();
 | |
|     %all_switchports = %$recordsref;
 | |
| 
 | |
|     # MAC records looks like: "01:02:03:04:05:0E!node5.01:02:03:05:0F!node6-eth1". We want to get the real mac addres.
 | |
|     foreach (keys %allmacs) {
 | |
|         my @hostentries = split(/\|/, $_);
 | |
|         foreach my $hostandmac (@hostentries) {
 | |
|             my ($macstr, $machostname) = split("!", $hostandmac);
 | |
|             $allmacs{$macstr} = 0;
 | |
|         }
 | |
|     }
 | |
|     %allmacsupper = ();
 | |
|     foreach (keys %allmacs) {
 | |
|         $allmacsupper{ uc($_) } = 0;
 | |
|     }
 | |
| 
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('hosts', 'ip');
 | |
|     %allinstallips = %$recordsref;
 | |
|     $recordsref    = xCAT::NetworkUtils->get_all_nicips(1);
 | |
|     %allips        = %$recordsref;
 | |
| 
 | |
|     # Merge all BMC IPs and install IPs into allips.
 | |
|     %allips = (%allips, %allbmcips, %allinstallips, %allfspips);
 | |
| 
 | |
|     # Get all CEC names
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_all_cecs(1);
 | |
|     %allcecs    = %$recordsref;
 | |
| 
 | |
|     # Get all LPAR ids
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_all_lparids(\%allcecs);
 | |
|     %alllparids = %$recordsref;
 | |
| 
 | |
|     # Get all vm hosts/hypervisiors
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_all_vmhosts();
 | |
|     %allvmhosts = %$recordsref;
 | |
| 
 | |
|     #TODO: can not use getallnode to get rack infos.
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_rack(1);
 | |
|     %allracks      = %$recordsref;
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_chassis(1);
 | |
|     %allchassis    = %$recordsref;
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_chassis(1, 'cmm');
 | |
|     %allcmmchassis = %$recordsref;
 | |
| 
 | |
|     @switch_records = ();
 | |
| 
 | |
|     # Generate temporary hostnames for hosts entries in hostfile.
 | |
|     xCAT::MsgUtils->message('S', "Generate temporary hostnames.");
 | |
|     my ($retcode_read, $retstr_read) = read_and_generate_hostnames($args_dict{'file'});
 | |
|     if ($retcode_read != 0) {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_errormsg($retstr_read);
 | |
|         return;
 | |
|     }
 | |
|     my ($parse_ret, $parse_str) = xCAT::ProfiledNodeUtils->parse_nodeinfo_file($retstr_read);
 | |
|     if (!$parse_ret) {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_errormsg($parse_str);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $mac_addr_mode = 0;
 | |
|     my $switch_mode   = 0;
 | |
|     my $powerkvm_mode = 0;
 | |
| 
 | |
|     # Parse and validate the hostinfo string. The real hostnames will be generated here.
 | |
|     xCAT::MsgUtils->message('S', "Parsing hostinfo string and validate it.");
 | |
|     my ($hostinfo_dict_ref, $invalid_records_ref) = validate_node_entries();
 | |
|     my %hostinfo_dict   = %$hostinfo_dict_ref;
 | |
|     my @invalid_records = @$invalid_records_ref;
 | |
|     if (@invalid_records) {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_invalidrecords(\@invalid_records);
 | |
|         return;
 | |
|     }
 | |
|     unless (%hostinfo_dict) {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_errormsg("Cannot find node records in node information file.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # if user specified the switch, we need to add a new item into switch table
 | |
|     my @nodelist = keys %hostinfo_dict;
 | |
|     foreach my $mynode (@nodelist)
 | |
|     {
 | |
|         if (defined($hostinfo_dict{$mynode}{'mac'}))
 | |
|         {
 | |
|             $mac_addr_mode = 1;
 | |
|         }
 | |
|         if (defined($hostinfo_dict{$mynode}{'switches'}))
 | |
|         {
 | |
|             $switch_mode = 1;
 | |
|         }
 | |
|         if (defined($hostinfo_dict{$mynode}{'vmhost'}))
 | |
|         {
 | |
|             $powerkvm_mode = 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # cannot mix switch discovery with mac import
 | |
|     if (($mac_addr_mode == 1) && ($switch_mode == 1))
 | |
|     {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_errormsg("Cannot define mac import node in switch discovery hostinfo file.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Get no mac address nodes when user only defined CEC in NIF for 7R2 support.
 | |
|     my @nomacnodes = ();
 | |
|     foreach my $nomacnode (@nodelist) {
 | |
|         if (defined($hostinfo_dict{$nomacnode}{'cec'}) &&
 | |
|             not(defined($hostinfo_dict{$nomacnode}{'mac'})) &&
 | |
|             not(defined($hostinfo_dict{$nomacnode}{'switch'}))) {
 | |
|             push @nomacnodes, $nomacnode;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Create the full hostinfo dict.
 | |
|     xCAT::MsgUtils->message('S', "Generating new hostinfo string.");
 | |
|     my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_dict(\%hostinfo_dict);
 | |
|     unless ($retcode_gen) {
 | |
|         setrsp_progress("Failed to validate node information file.");
 | |
|         setrsp_errormsg($retstr_gen);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # create hosts and then call nodemgmt for node management plugins.
 | |
|     setrsp_progress("Importing nodes...");
 | |
|     setrsp_progress("Creating nodes...");
 | |
|     my $warnstr = "";
 | |
|     if (xCAT::DBobjUtils->setobjdefs(\%hostinfo_dict) != 0) {
 | |
|         $warnstr = "Warning: failed to import some nodes.";
 | |
|         setrsp_progress($warnstr);
 | |
|     }
 | |
| 
 | |
|     # create default uuid for PowerKVM nodes
 | |
|     if ($powerkvm_mode) {
 | |
|         my $vpdtab = xCAT::Table->new('vpd', -create => 1, -autocommit => 0);
 | |
|         foreach (@nodelist) {
 | |
|             my $keyhash;
 | |
|             my $updatehash;
 | |
|             $keyhash->{'node'}    = $_;
 | |
|             $updatehash->{'uuid'} = '00000000-0000-0000-0000-000000000000';
 | |
|             $vpdtab->setAttribs($keyhash, $updatehash);
 | |
|         }
 | |
|         $vpdtab->commit;
 | |
|     }
 | |
| 
 | |
|     # create switch, port, interface relationship.
 | |
|     if ($switch_mode) {
 | |
| 
 | |
|         #debug message.
 | |
|         my $swstr = Dumper(@switch_records);
 | |
|         xCAT::MsgUtils->message('S', "node-switch-port-interface relationship: @switch_records");
 | |
| 
 | |
|         my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0);
 | |
|         for my $key_n_value (@switch_records) {
 | |
|             my $keyref   = (@$key_n_value)[0];
 | |
|             my $valueref = (@$key_n_value)[1];
 | |
|             $swtab1->setAttribs($keyref, $valueref);
 | |
|         }
 | |
|         $swtab1->commit;
 | |
|     }
 | |
| 
 | |
|     # setup node provisioning status.
 | |
|     xCAT::Utils->runxcmd({ command => ["updatenodestat"], node => \@nodelist, arg => ['defined'] }, $request_command, -1, 2);
 | |
| 
 | |
|     setrsp_progress("Configuring nodes...");
 | |
|     my $retref = xCAT::Utils->runxcmd({ command => ["kitnodeadd"], node => \@nodelist, sequential => [1], macflag => [$mac_addr_mode] }, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $warnstr .= "Warning: failed to run command kitnodeadd.";
 | |
|         if ($retstrref->[1]) {
 | |
|             $warnstr .= "Details: $retstrref->[1]";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Use xcat command: getmacs <noderanges> -D to automatically get node mac address
 | |
|     # If some of nodes can not get mac address, then finally remove them with warning msg.
 | |
|     if (@nomacnodes) {
 | |
| 
 | |
|         # Sleep 10 seconds to ensure the basic node attributes are effected
 | |
|         sleep 10;
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["getmacs"], node => \@nomacnodes, arg => ['-D'] }, $request_command, 0, 2);
 | |
|         $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $warnstr .= "Warning: Can not discover MAC address by getmacs command for some node(s).";
 | |
|         }
 | |
| 
 | |
|         # Parse the output of "getmacs <noderange> -D" to filter success and failed nodes.
 | |
|         my @successnodes = ();
 | |
|         my @failednodes  = ();
 | |
|         my $nodelistref  = $retref->{'node'};
 | |
|         my $index        = 0;
 | |
|         my $name         = '';
 | |
|         my $contents     = '';
 | |
|         if ($nodelistref) {
 | |
|             foreach (@$nodelistref) {
 | |
| 
 | |
|                 # Get node name.
 | |
|                 if ($nodelistref->[$index]->{'name'}) {
 | |
|                     $name = $nodelistref->[$index]->{'name'}->[0];
 | |
|                 }
 | |
| 
 | |
|                 # Get node data contents.
 | |
|                 if ($nodelistref->[$index]->{'data'}->[0]->{'contents'}) {
 | |
|                     $contents = $nodelistref->[$index]->{'data'}->[0]->{'contents'}->[0];
 | |
|                 }
 | |
| 
 | |
|                 # Get success and failed nodes list.
 | |
|                 if (defined($name) and $contents =~ /[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}/) {
 | |
|                     push @successnodes, $name;
 | |
|                 } else {
 | |
|                     push @failednodes, $name;
 | |
|                 }
 | |
|                 $index++;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Reconfigure the nodes that MAC address discovered by getmacs command
 | |
|         if (@successnodes) {
 | |
|             $mac_addr_mode = 1;
 | |
|             my $retref = xCAT::Utils->runxcmd({ command => ["kitnodeadd"], node => \@successnodes, sequential => [1], macflag => [$mac_addr_mode] }, $request_command, 0, 2);
 | |
|             my $retstrref = parse_runxcmd_ret($retref);
 | |
|             if ($::RUNCMD_RC != 0) {
 | |
|                 $warnstr .= "Warning: failed to run command kitnodeadd.";
 | |
|                 if ($retstrref->[1]) {
 | |
|                     $warnstr .= "Details: $retstrref->[1]";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Remove these nodes that can not get mac address by xcat command: getmacs <noderange> -D.
 | |
|         if (@failednodes) {
 | |
|             my $nodermretref = xCAT::Utils->runxcmd({ command => ["noderm"], node => \@failednodes }, $request_command, 0, 2);
 | |
|             my $nodermretstrref = parse_runxcmd_ret($nodermretref);
 | |
|             if ($::RUNCMD_RC != 0) {
 | |
|                 $warnstr .= "Warning: Cannot remove some of nodes that not MAC address discovered by getmacs command.";
 | |
|                 if ($nodermretstrref->[1]) {
 | |
|                     $warnstr .= "Details: $nodermretstrref->[1]";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Push the success nodes to nodelist and remove the failed nodes from nodelist.
 | |
|         @nodelist = xCAT::CFMUtils->arrayops("U", \@nodelist, \@successnodes);
 | |
|         @failednodes = xCAT::CFMUtils->arrayops("I", \@nodelist, \@failednodes);
 | |
|         @nodelist    = xCAT::CFMUtils->arrayops("D", \@nodelist, \@failednodes);
 | |
|     }
 | |
| 
 | |
|     setrsp_progress("Imported nodes.");
 | |
| 
 | |
|     #TODO: get the real nodelist here.
 | |
|     setrsp_success(\@nodelist, $warnstr);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  nodepurge
 | |
| 
 | |
|     Description : Remove nodes. After nodes removed, their info in /etc/hosts, dhcp, dns... will be removed automatically.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodepurge {
 | |
|     my $nodes = $request->{node};
 | |
|     my $helpmsg = "nodepurge: Removes nodes from database and system configuration.
 | |
| Usage:
 | |
| \tnodepurge <noderange>
 | |
| \tnodepurge [-h|--help]
 | |
| \tnodepurge {-v|--version}";
 | |
| 
 | |
|     my $ret = validate_args($helpmsg);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
|     if (!$nodes) {
 | |
|         setrsp_infostr($helpmsg);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     xCAT::MsgUtils->message('S', "Purging nodes.");
 | |
| 
 | |
|     # For remove nodes, we should call 'nodemgmt' in front of 'noderm'
 | |
|     setrsp_progress("Configuring nodes...");
 | |
| 
 | |
|     my $warnstr = "";
 | |
|     my $retref = xCAT::Utils->runxcmd({ command => ["kitnoderemove"], node => $nodes, sequential => [1] }, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
| 
 | |
|     # runxcmd failed.
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to call kitnoderemove command.");
 | |
|         $warnstr .= "Warning: failed to call kitnoderemove command.";
 | |
|         if ($retstrref->[1]) {
 | |
|             $warnstr .= "Details: $retstrref->[1]";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     setrsp_progress("Updating DNS entries");
 | |
|     $retref = "";
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["makedns"], node => $nodes, arg => ['-d'] }, $request_command, 0, 2);
 | |
| 
 | |
|     setrsp_progress("Updating hosts entries");
 | |
|     $retref = "";
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["makehosts"], node => $nodes, arg => ['-d'] }, $request_command, 0, 2);
 | |
| 
 | |
|     setrsp_progress("Removing nodes...");
 | |
|     $retref = "";
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["noderm"], node => $nodes }, $request_command, 0, 2);
 | |
|     $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: Cannot remove all nodes. The noderm command failed to remove some of the nodes.");
 | |
|         $warnstr .= "Warning: Cannot remove all nodes. The noderm command failed to remove some of the nodes.";
 | |
|         if ($retstrref->[1]) {
 | |
|             $warnstr .= "Details: $retstrref->[1]";
 | |
|         }
 | |
|     }
 | |
|     setrsp_progress("Removed all nodes.");
 | |
|     setrsp_success($nodes, $warnstr);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  noderefresh
 | |
| 
 | |
|     Description : Re-Call kit plugins for node management
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #------------------------------------------------------
 | |
| sub noderefresh
 | |
| {
 | |
|     my $nodes   = $request->{node};
 | |
|     my $helpmsg = "noderefresh: Calls kit plugins for the nodes in the profile.
 | |
| Usage:
 | |
| \tnoderefresh <noderange>
 | |
| \tnoderefresh [-h|--help]
 | |
| \tnoderefresh {-v|--version}";
 | |
| 
 | |
|     if (!$nodes) {
 | |
|         setrsp_infostr($helpmsg);
 | |
|         return;
 | |
|     }
 | |
|     my $ret = validate_args($helpmsg);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $retref = xCAT::Utils->runxcmd({ command => ["kitnoderefresh"], node => $nodes, sequential => [1] }, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
| 
 | |
|     # runxcmd failed.
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: Failed to call kit commands.");
 | |
|     }
 | |
|     setrsp_success($nodes);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  nodechprofile
 | |
| 
 | |
|     Description : Update node profiles: imageprofile, networkprofile and hardwareprofile.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodechprofile {
 | |
|     my $nodes   = $request->{node};
 | |
|     my $helpmsg = "nodechprofile: Update node profiles for profiled nodes.
 | |
| Usage:
 | |
| \tnodechprofile <noderange> [networkprofile=<networkprofile>] [imageprofile=<imageprofile>] [hardwareprofile=<hardwareprofile>]
 | |
| \tnodechprofile [-h|--help]
 | |
| \tnodechprofile {-v|--version}";
 | |
|     if (!$nodes) {
 | |
|         setrsp_infostr($helpmsg);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile');
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     xCAT::MsgUtils->message('S', "Update nodes' profile settings.");
 | |
| 
 | |
|     # Get current templates for all nodes.
 | |
|     setrsp_progress("Getting all node groups from the database...");
 | |
|     my %groupdict;
 | |
|     my $nodelstab = xCAT::Table->new('nodelist');
 | |
|     my $nodeshashref = $nodelstab->getNodesAttribs($nodes, ['groups']);
 | |
|     my %updatenodeshash;
 | |
|     my %updatenodereshash;
 | |
| 
 | |
|     my %nodeoldprofiles = ();
 | |
|     foreach (@$nodes) {
 | |
|         my %nodecurrprofiles = ();
 | |
| 
 | |
|         # Get each node's profiles.
 | |
|         my $groupsstr = $nodeshashref->{$_}->[0]->{'groups'};
 | |
|         unless ($groupsstr) {
 | |
|             setrsp_errormsg("node $_ does not have any profiles, can not change its profiles.");
 | |
|             return;
 | |
|         }
 | |
|         my @groups = split(/,/, $groupsstr);
 | |
|         foreach my $group (@groups) {
 | |
|             if ($group =~ /__ImageProfile/) {
 | |
|                 $nodecurrprofiles{'imageprofile'} = $group;
 | |
|             } elsif ($group =~ /__NetworkProfile/) {
 | |
|                 $nodecurrprofiles{'networkprofile'} = $group;
 | |
|             } elsif ($group =~ /__HardwareProfile/) {
 | |
|                 $nodecurrprofiles{'hardwareprofile'} = $group;
 | |
|             } elsif ($group =~ /__Hypervisor/) {
 | |
|                 next;
 | |
|             } else {
 | |
|                 $nodecurrprofiles{'groups'} .= $group . ",";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # initialize node old profiles.
 | |
|         unless (%nodeoldprofiles) {
 | |
|             $nodeoldprofiles{'imageprofile'} = $nodecurrprofiles{'imageprofile'};
 | |
|             $nodeoldprofiles{'networkprofile'} = $nodecurrprofiles{'networkprofile'};
 | |
|             $nodeoldprofiles{'hardwareprofile'} = $nodecurrprofiles{'hardwareprofile'};
 | |
|         }
 | |
| 
 | |
|         # Make sure whether all nodes having same profiles.
 | |
|         if ($nodeoldprofiles{'imageprofile'} ne $nodecurrprofiles{'imageprofile'}) {
 | |
|             setrsp_errormsg("node $_ does not have same imageprofile with other nodes.");
 | |
|             return;
 | |
|         } elsif ($nodeoldprofiles{'hardwareprofile'} ne $nodecurrprofiles{'hardwareprofile'}) {
 | |
|             setrsp_errormsg("node $_ does not have same hardwareprofile with other nodes.");
 | |
|             return;
 | |
|         } elsif ($nodeoldprofiles{'networkprofile'} ne $nodecurrprofiles{'networkprofile'}) {
 | |
|             setrsp_errormsg("node $_ does not have same networkprofile with other nodes.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # Replace the old profiles name with new specified ones in args_dict
 | |
|         if ($nodecurrprofiles{'groups'}) {
 | |
|             $updatenodeshash{$_}{'groups'} = $nodecurrprofiles{'groups'};
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #fix 241844 issue, use local variable to store args_dict value
 | |
|     my $imageprofile    = undef;
 | |
|     my $networkprofile  = undef;
 | |
|     my $hardwareprofile = undef;
 | |
| 
 | |
|     if (exists $args_dict{'imageprofile'}) {
 | |
|         $imageprofile = $args_dict{'imageprofile'};
 | |
|     }
 | |
| 
 | |
|     if (exists $args_dict{'networkprofile'}) {
 | |
|         $networkprofile = $args_dict{'networkprofile'};
 | |
|     }
 | |
| 
 | |
|     if (exists $args_dict{'hardwareprofile'}) {
 | |
|         $hardwareprofile = $args_dict{'hardwareprofile'};
 | |
|     }
 | |
| 
 | |
|     # Verify whether this node is KVM hypervisor node
 | |
|     my $is_kvm_hypv = xCAT::ProfiledNodeUtils->is_kvm_hypv_node($imageprofile);
 | |
| 
 | |
|     # Get the netboot attribute for node
 | |
|     my $new_netboot = undef;
 | |
|     my $latestimgproflie = $imageprofile ? $imageprofile : $nodeoldprofiles{'imageprofile'};
 | |
|     my $latesthardwareprofile = $hardwareprofile ? $hardwareprofile : $nodeoldprofiles{'hardwareprofile'};
 | |
|     if ($latestimgproflie) {
 | |
|         my ($retcode, $retval) = xCAT::ProfiledNodeUtils->get_netboot_attr($latestimgproflie, $latesthardwareprofile);
 | |
|         if (not $retcode) {
 | |
|             setrsp_errormsg($retval);
 | |
|             return;
 | |
|         }
 | |
|         $new_netboot = $retval;
 | |
|     }
 | |
| 
 | |
|     # After checking, all nodes' profile should be same
 | |
|     # Get the new profile with specified ones in args_dict
 | |
|     my $changeflag = 0;
 | |
|     my $profile_groups;
 | |
|     my $profile_status;
 | |
|     if ($networkprofile) {
 | |
|         $profile_groups .= $networkprofile . ",";
 | |
|         if ($networkprofile ne $nodeoldprofiles{'networkprofile'}) {
 | |
|             $changeflag = 1;
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message('S', "Specified networkprofile is same with current value, ignore.");
 | |
|             $networkprofile = undef;
 | |
|         }
 | |
|     } else {
 | |
|         $profile_groups .= $nodeoldprofiles{'networkprofile'} . ",";
 | |
|     }
 | |
| 
 | |
|     if ($hardwareprofile) {
 | |
|         $profile_groups .= $hardwareprofile . ",";
 | |
|         if ($hardwareprofile ne $nodeoldprofiles{'hardwareprofile'}) {
 | |
|             $profile_status = 'defined';
 | |
|             $changeflag     = 1;
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message('S', "Specified hardwareprofile is same with current value, ignore.");
 | |
|             $hardwareprofile = undef;
 | |
|         }
 | |
|     } else {
 | |
|         if ($nodeoldprofiles{'hardwareprofile'}) {
 | |
|             $profile_groups .= $nodeoldprofiles{'hardwareprofile'} . ",";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($imageprofile) {
 | |
|         $profile_groups .= $imageprofile . ",";
 | |
|         if ($imageprofile ne $nodeoldprofiles{'imageprofile'}) {
 | |
|             $profile_status = 'defined';
 | |
|             $changeflag     = 1;
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message('S', "Specified imageprofile is same with current value, ignore.");
 | |
|             $imageprofile = undef;
 | |
|         }
 | |
|     } else {
 | |
|         $profile_groups .= $nodeoldprofiles{'imageprofile'} . ",";
 | |
|     }
 | |
| 
 | |
|     # make sure there are something changed, otherwise we should quit without any changes.
 | |
|     unless ($changeflag) {
 | |
|         setrsp_infostr("Warning: no profile changes detect.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Update nodes' attributes
 | |
|     foreach (@$nodes) {
 | |
|         $updatenodeshash{$_}{'groups'} .= $profile_groups;
 | |
|         if ($is_kvm_hypv) {
 | |
|             $updatenodeshash{$_}{'groups'} .= ",__Hypervisor_kvm";
 | |
|         }
 | |
|         if ($new_netboot) {
 | |
|             $updatenodereshash{$_}{'netboot'} = $new_netboot;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #update DataBase.
 | |
|     setrsp_progress("Updating database records...");
 | |
|     my $nodetab = xCAT::Table->new('nodelist', -create => 1);
 | |
|     $nodetab->setNodesAttribs(\%updatenodeshash);
 | |
|     $nodetab->close();
 | |
|     my $noderestab = xCAT::Table->new('noderes', -create => 1);
 | |
|     $noderestab->setNodesAttribs(\%updatenodereshash);
 | |
|     $noderestab->close();
 | |
| 
 | |
|     #update node's status:
 | |
|     if ($profile_status eq "defined") {
 | |
|         xCAT::Utils->runxcmd({ command => ["updatenodestat"], node => $nodes, arg => ['defined'] }, $request_command, -1, 2);
 | |
|     }
 | |
| 
 | |
|     my $retref;
 | |
|     my $retstrref;
 | |
| 
 | |
|     # If network profile specified. Need re-generate IPs for all nodess again.
 | |
|     # As new design, ignore BMC/FSP NIC while reinstall nodes
 | |
|     if ($networkprofile) {
 | |
|         my $newNetProfileName = $networkprofile;
 | |
|         my $oldNetProfileName = $nodeoldprofiles{'networkprofile'};
 | |
| 
 | |
|         my $newNicsRef = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs([$newNetProfileName])->{$newNetProfileName};
 | |
|         my $oldNicsRef = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs([$oldNetProfileName])->{$oldNetProfileName};
 | |
| 
 | |
|         my %updateNicsHash  = ();
 | |
|         my %reserveNicsHash = ();
 | |
|         foreach my $newNic (keys %$newNicsRef) {
 | |
|             if ($newNicsRef->{$newNic}->{'type'} ne 'BMC' and $newNicsRef->{$newNic}->{'type'} ne 'FSP') {
 | |
|                 $updateNicsHash{$newNic} = 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Add BMC/FSP as reserve NICs and not remove it form nics table
 | |
|         foreach my $oldNic (keys %$oldNicsRef) {
 | |
|             if ($oldNicsRef->{$oldNic}->{'type'} ne 'BMC' and $oldNicsRef->{$oldNic}->{'type'} ne 'FSP'){
 | |
|                 if ($oldNicsRef->{$oldNic}->{'network'} eq $newNicsRef->{$oldNic}->{'network'}){
 | |
|                     $reserveNicsHash{$oldNic} = 1;
 | |
|                     if(exists $updateNicsHash{$oldNic})
 | |
|                     {
 | |
|                         delete($updateNicsHash{$oldNic});
 | |
|                     }
 | |
|                 } else {
 | |
|                     $updateNicsHash{$oldNic} = 1;
 | |
|                 }
 | |
|             } else {
 | |
|                 $reserveNicsHash{$oldNic} = 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         my $updateNics  = join(",", keys %updateNicsHash);
 | |
|         my $reserveNics = join(",", keys %reserveNicsHash);
 | |
|         setrsp_progress("Regenerate IP addresses for nodes...");
 | |
|         $retref = "";
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["noderegenips"], node => $nodes, arg => [ "nics=$updateNics", "reservenics=$reserveNics" ], sequential => [1] }, $request_command, 0, 2);
 | |
|         $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             setrsp_progress("Warning: failed to generate IPs for nodes.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Update node's chain table if we need to re-provisioning OS...
 | |
|     # We need to re-provision OS if:
 | |
|     # hardware profile changed  or
 | |
|     # image profile changed or
 | |
|     # network profile changed.
 | |
|     if (($imageprofile) or ($networkprofile) or ($hardwareprofile)) {
 | |
|         my $nodetypetab = xCAT::Table->new('nodetype');
 | |
|         my $firstnode   = $nodes->[0];
 | |
|         my $profiles = xCAT::ProfiledNodeUtils->get_nodes_profiles([$firstnode], 1);
 | |
|         unless ($profiles) {
 | |
|             setrsp_errormsg("Can not get node profiles.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # If we have hardware changes, reconfigure everything including BMC.
 | |
|         my $chainret = 0;
 | |
|         my $chainstr = "";
 | |
|         if ($hardwareprofile) {
 | |
|             ($chainret, $chainstr) = xCAT::ProfiledNodeUtils->gen_chain_for_profiles($profiles->{$firstnode}, 1);
 | |
|         } else {
 | |
|             ($chainret, $chainstr) = xCAT::ProfiledNodeUtils->gen_chain_for_profiles($profiles->{$firstnode}, 0);
 | |
|         }
 | |
|         if ($chainret != 0) {
 | |
|             setrsp_errormsg("Failed to generate chain string for nodes.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # DB update: chain table.
 | |
|         my %chainAttr = {};
 | |
|         foreach my $node (@$nodes) {
 | |
|             $chainAttr{$node}{'chain'}     = $chainstr;
 | |
|             $chainAttr{$node}{'currchain'} = '';
 | |
|         }
 | |
|         my $chaintab = xCAT::Table->new('chain', -create => 1);
 | |
|         $chaintab->setNodesAttribs(\%chainAttr);
 | |
|         $chaintab->close();
 | |
| 
 | |
| 
 | |
|         # Run node plugins to refresh node relateive configurations.
 | |
|         $retref = {};
 | |
|         setrsp_progress("Updating DNS entries");
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["makedns"], node => $nodes, arg => ['-d'] }, $request_command, 0, 2);
 | |
|         my $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             setrsp_progress("Warning: failed to call kit commands.");
 | |
|         }
 | |
| 
 | |
|         $retref = {};
 | |
|         setrsp_progress("Updating hosts entries");
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["makehosts"], node => $nodes, arg => ['-d'] }, $request_command, 0, 2);
 | |
|         $retref    = {};
 | |
|         $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             setrsp_progress("Warning: failed to call kit commands.");
 | |
|         }
 | |
| 
 | |
|         setrsp_progress("Re-creating nodes...");
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["kitnodeadd"], node => $nodes, sequential => [1], macflag => [1] }, $request_command, 0, 2);
 | |
|         $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             setrsp_progress("Warning: failed to call kit commands.");
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     setrsp_progress("Updated the image/network/hardware profiles used by nodes.");
 | |
|     setrsp_success($nodes);
 | |
| }
 | |
| 
 | |
| #------------------------------------------------------
 | |
| 
 | |
| =head3 noderegenips
 | |
| 
 | |
|   Description: Re-generate IPs automatically for specified nodes.
 | |
|                All these nodes must be in same networkprofile.
 | |
|                If no nics specified, then re-generate IP all nics in the networkprofile.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------
 | |
| sub noderegenips
 | |
| {
 | |
|     my $nodes   = $request->{node};
 | |
|     my $helpmsg = "noderegenips: Regenerate nodes IP addresses.
 | |
| Usage:
 | |
| \tnoderegenips <noderange> [nics=<eth0,eth1...>]
 | |
| \tnoderegenips [-h|--help]
 | |
| \tnoderegenips {-v|--version}";
 | |
|     if (!$nodes) {
 | |
|         setrsp_infostr($helpmsg);
 | |
|         return;
 | |
|     }
 | |
|     my @enabledparams = ('nics', 'reservenics');
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my @updateNics     = ();
 | |
|     my @removedNics    = ();
 | |
|     my @reserveNics    = ();
 | |
|     my $netProfileName = '';
 | |
|     my $netProfileNicsRef;
 | |
|     my %freeIPsHash = ();
 | |
| 
 | |
|     # nicipsAttr and ipAttr are for storing node's nicips and ip attribute
 | |
|     my %nicipsAttr = ();
 | |
|     my %ipAttr     = ();
 | |
|     my $installnic = '';
 | |
|     xCAT::MsgUtils->message('S', "Start running noderegenips.");
 | |
| 
 | |
|     #1. Validate all nodes have same network profile.  networkprofile.
 | |
|     my $nodesProfilesRef = xCAT::ProfiledNodeUtils->get_nodes_profiles($nodes);
 | |
|     foreach my $node (keys %$nodesProfilesRef) {
 | |
|         unless ($nodesProfilesRef->{$node}->{NetworkProfile}) {
 | |
|             setrsp_errormsg("Node $node does not have a network profile.");
 | |
|             return;
 | |
|         }
 | |
|         unless ($netProfileName) {
 | |
|             $netProfileName = "__NetworkProfile_" . $nodesProfilesRef->{$node}->{NetworkProfile};
 | |
|             next;
 | |
|         }
 | |
|         if ("__NetworkProfile_" . $nodesProfilesRef->{$node}->{NetworkProfile} ne $netProfileName) {
 | |
|             setrsp_errormsg("Node $node has a different network profile with other nodes.");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #2. Get network profile nics settings.
 | |
|     $netProfileNicsRef = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs([$netProfileName]);
 | |
|     my $nicsref  = $netProfileNicsRef->{$netProfileName};
 | |
|     my @nicslist = keys %$nicsref;
 | |
| 
 | |
|     #3. validate specified nics
 | |
|     if (exists $args_dict{'nics'}) {
 | |
|         @updateNics = split(",", $args_dict{'nics'});
 | |
|     }
 | |
|     if (exists $args_dict{'reservenics'}) {
 | |
|         @reserveNics = split(",", $args_dict{'reservenics'});
 | |
|     }
 | |
|     foreach (@updateNics) {
 | |
|         unless ($netProfileNicsRef->{$netProfileName}->{$_}) {
 | |
| 
 | |
|             # We want to remove this nic from these nodes.
 | |
|             push(@removedNics, $_);
 | |
|         }
 | |
|     }
 | |
|     unless (@updateNics) {
 | |
|         @updateNics = @nicslist;
 | |
|     }
 | |
| 
 | |
|     # get install nic for these nodes.
 | |
|     my $restab = xCAT::Table->new('noderes');
 | |
|     my $installnicattr = $restab->getNodeAttribs($netProfileName, ['installnic']);
 | |
|     $installnic = $installnicattr->{'installnic'};
 | |
| 
 | |
|     #4. get all node's current database nics settings.
 | |
|     my $nodesNicsRef = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs($nodes);
 | |
| 
 | |
|     my $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
 | |
|     %allbmcips = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('hosts', 'ip');
 | |
|     %allinstallips = %$recordsref;
 | |
|     $recordsref    = xCAT::NetworkUtils->get_all_nicips(1);
 | |
|     %allips        = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ppc', 'hcp');
 | |
|     my %allfspips = %$recordsref;
 | |
|     %allips = (%allips, %allbmcips, %allinstallips, %allfspips);
 | |
| 
 | |
|     #5. free currently used IPs for all nodes.
 | |
|     foreach my $node (@$nodes) {
 | |
|         foreach my $nicname (@updateNics) {
 | |
|             my $nicip = $nodesNicsRef->{$node}->{$nicname}->{"ip"};
 | |
|             if ($nicip) {
 | |
|                 delete($allips{$nicip});
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #6. Generate new free IPs for each network.
 | |
|     my @allknownips    = keys %allips;
 | |
|     my %netFreeIPsHash = ();
 | |
|     foreach my $updnic (@updateNics) {
 | |
| 
 | |
|         #No need generate for removed nics.
 | |
|         unless (grep { $_ eq $updnic } @removedNics) {
 | |
|             my $netname = $netProfileNicsRef->{$netProfileName}->{$updnic}->{"network"};
 | |
|             if (not exists $netFreeIPsHash{$netname}) {
 | |
|                 $netFreeIPsHash{$netname} = xCAT::ProfiledNodeUtils->get_allocable_staticips_innet($netname, \@allknownips);
 | |
|             }
 | |
|             $freeIPsHash{$updnic} = $netFreeIPsHash{$netname};
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #7. Assign new free IPs for nodes and generate nicips and hosts attribute.
 | |
|     my %bmcipsAttr     = {};
 | |
|     my %fspipsAttr     = {};
 | |
|     my $provision_flag = 0;
 | |
|     my $bmc_flag       = 0;
 | |
|     my $fsp_flag       = 0;
 | |
|     foreach my $node (@$nodes) {
 | |
|         foreach my $nicname (@nicslist) {
 | |
| 
 | |
|             # Remove records from nicips for removed nics.
 | |
|             if (grep { $_ eq $nicname } @removedNics) {
 | |
|                 next;
 | |
|             }
 | |
| 
 | |
|             unless (grep { $_ eq $nicname } @updateNics) {
 | |
| 
 | |
|                 # if the nic not specified, just keep the old IP&NIC record in nics table.
 | |
|                 my $oldip = $nodesNicsRef->{$node}->{$nicname}->{"ip"};
 | |
|                 if ($oldip) {
 | |
|                     $nicipsAttr{$node}{nicips} .= $nicname . "!" . $oldip . ",";
 | |
|                 }
 | |
|             } else {
 | |
|                 my $ipsref = $freeIPsHash{$nicname};
 | |
|                 my $nextip = shift @$ipsref;
 | |
|                 unless ($nextip) {
 | |
|                     setrsp_errormsg("There are no more IP addresses available in the static network range for nic $nicname.");
 | |
|                     return;
 | |
|                 }
 | |
|                 $nicipsAttr{$node}{nicips} .= $nicname . "!" . $nextip . ",";
 | |
|                 if ($installnic eq $nicname) {
 | |
|                     $provision_flag = 1;
 | |
|                     $ipAttr{$node}{ip} = $nextip;
 | |
|                 } elsif ($nicname eq 'bmc') {
 | |
|                     $bmc_flag = 1;
 | |
|                     $bmcipsAttr{$node}{"bmc"} = $nextip;
 | |
|                 } elsif ($nicname eq 'fsp') {
 | |
|                     $fsp_flag = 1;
 | |
|                     $fspipsAttr{$node}{"hcp"} = $nextip;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Add reserve nics
 | |
|         foreach my $nicname (@reserveNics) {
 | |
|             my $count = index($nicipsAttr{$node}{nicips}, $nicname);
 | |
|             if ($count < 0) {
 | |
|                 my $oldip = $nodesNicsRef->{$node}->{$nicname}->{"ip"};
 | |
|                 if ($oldip) {
 | |
|                     $nicipsAttr{$node}{nicips} .= $nicname . "!" . $oldip . ",";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #8. Update database.
 | |
|     setrsp_progress("Updating database records...");
 | |
|     my $nicstab = xCAT::Table->new('nics', -create => 1);
 | |
|     $nicstab->setNodesAttribs(\%nicipsAttr);
 | |
|     $nicstab->close();
 | |
| 
 | |
|     # Update hosts table if provisioning NIC ip change
 | |
|     if ($provision_flag) {
 | |
|         my $hoststab = xCAT::Table->new('hosts', -create => 1);
 | |
|         $hoststab->setNodesAttribs(\%ipAttr);
 | |
|         $hoststab->close();
 | |
|     }
 | |
| 
 | |
|     # Update ipmi table if bmc NIC ip change
 | |
|     if ($bmc_flag) {
 | |
|         my $ipmitab = xCAT::Table->new('ipmi', -create => 1);
 | |
|         $ipmitab->setNodesAttribs(\%bmcipsAttr);
 | |
|         $ipmitab->close();
 | |
|     }
 | |
| 
 | |
|     # Update ppc table if fsp NIC ip change
 | |
|     if ($fsp_flag) {
 | |
|         my $ppctab = xCAT::Table->new('ppc', -create => 1);
 | |
|         $ppctab->setNodesAttribs(\%fspipsAttr);
 | |
|         $ppctab->close();
 | |
|     }
 | |
| 
 | |
|     setrsp_progress("Re-generated node's IPs for specified nics.");
 | |
|     setrsp_success($nodes);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3 nodeaddunmged
 | |
| 
 | |
|     Description : Create a node with hostname and ip address specified.
 | |
|                   This node will belong to group "__Unmanaged".
 | |
|                   Host file /etc/hosts will be updated automatically.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodeaddunmged
 | |
| {
 | |
|     xCAT::MsgUtils->message("Adding a unmanaged node.");
 | |
|     my $helpmsg = "nodeaddunmged: Creates an unmanaged node specifying the node name and IP address
 | |
| Usage:
 | |
| \tnodeaddunmged hostname<hostname> ip=<ip>
 | |
| \tnodeaddunmged [-h|--help]
 | |
| \tnodeaddunmged {-v|--version}";
 | |
| 
 | |
|     my @enabledparams = ('hostname', 'ip');
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams, \@enabledparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # validate the IP address
 | |
|     my $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
 | |
|     %allbmcips = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('hosts', 'ip');
 | |
|     %allinstallips = %$recordsref;
 | |
|     $recordsref    = xCAT::NetworkUtils->get_all_nicips(1);
 | |
|     %allips        = %$recordsref;
 | |
| 
 | |
|     %allips = (%allips, %allbmcips, %allinstallips);
 | |
| 
 | |
|     if (exists $allips{ $args_dict{'ip'} }) {
 | |
|         setrsp_errormsg("The specified IP address $args_dict{'ip'} already exists in the IP address database. You must use a different IP address.");
 | |
|         return;
 | |
|     } elsif ((xCAT::NetworkUtils->validate_ip($args_dict{'ip'}))[0]->[0]) {
 | |
|         setrsp_errormsg("The specified IP address $args_dict{'ip'} is invalid. You must use a valid IP address.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # validate hostname.
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('nodelist', 'node');
 | |
|     %allhostnames = %$recordsref;
 | |
|     if (exists $allhostnames{ $args_dict{'hostname'} }) {
 | |
|         setrsp_errormsg("The specified device name $args_dict{'hostname'} already exists. You must use a different device name.");
 | |
|         return;
 | |
|     }
 | |
|     if (!xCAT::NetworkUtils->isValidHostname($args_dict{'hostname'})) {
 | |
|         setrsp_errormsg("The specified device name $args_dict{'hostname'} is invalid. You must use a valid device name composed of 'a-z' '0-9'.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my %updatenodeshash = ();
 | |
|     $updatenodeshash{ $args_dict{'hostname'} }{'ip'} = $args_dict{'ip'};
 | |
|     my $hoststab = xCAT::Table->new('hosts', -create => 1);
 | |
|     $hoststab->setNodesAttribs(\%updatenodeshash);
 | |
|     $hoststab->close();
 | |
| 
 | |
|     %updatenodeshash = ();
 | |
|     $updatenodeshash{ $args_dict{'hostname'} }{'groups'} = "__Unmanaged";
 | |
|     my $nodetab = xCAT::Table->new('nodelist', -create => 1);
 | |
|     $nodetab->setNodesAttribs(\%updatenodeshash);
 | |
|     $nodetab->close();
 | |
| 
 | |
|     my $retref = xCAT::Utils->runxcmd({ command => ["makehosts"], node => [ $args_dict{"hostname"} ] }, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to update /etc/hosts for unmanaged node.");
 | |
|     }
 | |
| 
 | |
|     $retref = "";
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["makedns"], node => [ $args_dict{"hostname"} ] }, $request_command, 0, 2);
 | |
|     $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to update dns for unmanaged node.");
 | |
|     }
 | |
| 
 | |
|     setrsp_infostr("Created unmanaged node.");
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3 nodechmac
 | |
| 
 | |
|     Description : Change node's provisioning NIC's MAC address.
 | |
|                   And then call kits plugins for nodes.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodechmac
 | |
| {
 | |
|     xCAT::MsgUtils->message("Replacing node's mac address.");
 | |
|     my $helpmsg = "nodechmac: updates the MAC address for a provisioning network interface.
 | |
| Usage:
 | |
| \tnodechmac <node> mac=<mac>
 | |
| \tnodechmac [-h|--help]
 | |
| \tnodechmac  {-v|--version}";
 | |
| 
 | |
|     my @enabledparams = ('mac');
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams, \@enabledparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $nodelist = $request->{node};
 | |
|     my $hostname = $nodelist->[0];
 | |
| 
 | |
|     if ("__NOMAC__" eq $args_dict{"mac"}) {
 | |
| 
 | |
|         # Validate if node is bind on a switch
 | |
|         my $switch_table = xCAT::Table->new("switch");
 | |
|         my @item = $switch_table->getAttribs({ 'node' => $hostname }, 'switch', 'port');
 | |
|         my $item_num     = @item;
 | |
|         my $switch_valid = 0;
 | |
|         unless ($item[0])
 | |
|         {
 | |
|             setrsp_errormsg("Failed to replace node <$hostname>.  Switch information cannot be retrieved. Ensure that the switch is configured correctly.");
 | |
|             return;
 | |
|         } else {
 | |
|             foreach my $switch_item (@item) {
 | |
|                 if ($switch_item->{'switch'} && $switch_item->{'port'}) {
 | |
|                     $switch_valid = 1;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         unless ($switch_valid)
 | |
|         {
 | |
|             setrsp_errormsg("Failed to replace node <$hostname>. Switch information cannot be retrieved. Ensure that the switch is configured correctly.");
 | |
|             return;
 | |
|         }
 | |
|     } else {
 | |
| 
 | |
|         #Validate MAC address
 | |
|         my $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('mac', 'mac');
 | |
|         %allmacs = %$recordsref;
 | |
|         foreach (keys %allmacs) {
 | |
|             my @hostentries = split(/\|/, $_);
 | |
|             foreach my $hostandmac (@hostentries) {
 | |
|                 my ($macstr, $machostname) = split("!", $hostandmac);
 | |
|                 $allmacs{$macstr} = 0;
 | |
|             }
 | |
|         }
 | |
|         %allmacsupper = ();
 | |
|         foreach (keys %allmacs) {
 | |
|             $allmacsupper{ uc($_) } = 0;
 | |
|         }
 | |
|         if (exists $allmacsupper{ uc($args_dict{"mac"}) }) {
 | |
|             setrsp_errormsg("The specified MAC address $args_dict{'mac'} already exists. You must use a different MAC address.");
 | |
|             return;
 | |
|         } elsif (!xCAT::NetworkUtils->isValidMAC($args_dict{'mac'})) {
 | |
|             setrsp_errormsg("The specified MAC address $args_dict{'mac'} is invalid. You must use a valid MAC address.");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # re-create the chain record as updating mac may means for replacing a new brand hardware...
 | |
|     # Call Plugins.
 | |
|     my $profiles = xCAT::ProfiledNodeUtils->get_nodes_profiles([$hostname], 1);
 | |
|     unless ($profiles) {
 | |
|         setrsp_errormsg("Can not get node profiles.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     (my $chainret, my $chainstr) = xCAT::ProfiledNodeUtils->gen_chain_for_profiles($profiles->{$hostname}, 1);
 | |
|     if ($chainret != 0) {
 | |
|         setrsp_errormsg("Failed to generate chain string for nodes.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Update database records.
 | |
|     setrsp_progress("Updating database...");
 | |
| 
 | |
|     # MAC table
 | |
|     if ("__NOMAC__" eq $args_dict{"mac"})
 | |
|     {
 | |
|         my $mactab = xCAT::Table->new('mac', -create => 1);
 | |
|         my %keyhash;
 | |
|         $keyhash{'node'} = $hostname;
 | |
|         $mactab->delEntries(\%keyhash);
 | |
|         $mactab->commit();
 | |
|         $mactab->close();
 | |
|     } else {
 | |
|         my $mactab = xCAT::Table->new('mac', -create => 1);
 | |
|         $mactab->setNodeAttribs($hostname, { mac => $args_dict{'mac'} });
 | |
|         $mactab->close();
 | |
|     }
 | |
| 
 | |
|     # DB update: chain table.
 | |
|     my $chaintab = xCAT::Table->new('chain', -create => 1);
 | |
|     $chaintab->setNodeAttribs($hostname, { chain => $chainstr, currchain => '' });
 | |
|     $chaintab->close();
 | |
| 
 | |
| 
 | |
|     # Run node plugins to refresh node relateive configurations.
 | |
|     setrsp_progress("Configuring nodes...");
 | |
|     my $retref = {};
 | |
|     setrsp_progress("Updating DNS entries");
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["makedns"], node => [$hostname], arg => ['-d'] }, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to call kit commands.");
 | |
|     }
 | |
| 
 | |
|     $retref = {};
 | |
|     setrsp_progress("Updating hosts entries");
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["makehosts"], node => [$hostname], arg => ['-d'] }, $request_command, 0, 2);
 | |
|     $retref    = {};
 | |
|     $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to call kit commands.");
 | |
|     }
 | |
| 
 | |
|     if ("__NOMAC__" eq $args_dict{"mac"})
 | |
|     {
 | |
|         setrsp_progress("Updating DHCP entries");
 | |
|         $retref = xCAT::Utils->runxcmd({ command => ["makedhcp"], node => [$hostname], arg => ['-d'] }, $request_command, 0, 2);
 | |
|         $retref    = {};
 | |
|         $retstrref = parse_runxcmd_ret($retref);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             setrsp_progress("Warning: failed to call kit commands.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     setrsp_progress("Re-creating nodes...");
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["kitnodeadd"], node => [$hostname], macflag => [1] }, $request_command, 0, 2);
 | |
|     $retstrref = parse_runxcmd_ret($retref);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         setrsp_progress("Warning: failed to call kit commands.");
 | |
|     }
 | |
| 
 | |
|     # Update node's status.
 | |
|     setrsp_progress("Updating node status...");
 | |
|     xCAT::Utils->runxcmd({ command => ["updatenodestat"], node => [$hostname], arg => ['defined'] }, $request_command, -1, 2);
 | |
| 
 | |
|     setrsp_progress("Updated MAC address.");
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3 nodediscoverstart
 | |
| 
 | |
|     Description : Start profiled nodes discovery. If already started, return a failure.
 | |
|                   User should specify networkprofile, hardwareprofile,
 | |
|                   imageprofile, hostnameformat, rack, chassis, height and u so
 | |
|                   that node's IP address will be generated automatcially
 | |
|                   according to networkprofile, node's hardware settings will
 | |
|                   be set according to hardware profile, node's os settings will
 | |
|                   be set according to image profile, node's hostname will be
 | |
|                   set according to hostnameformat and rank. And other node's
 | |
|                   attribs will also be set according to rack, chassis, height and u.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodediscoverstart {
 | |
|     xCAT::MsgUtils->message("Profiled nodes discovery started.");
 | |
| 
 | |
|     my $helpmsg = "nodediscoverstart: Start profiled nodes discovery.
 | |
| Usage:
 | |
| \tnodediscoverstart networkprofile=<networkprofile> imageprofile=<imageprofile> hostnameformat=<hostnameformat> [hardwareprofile=<hardwareprofile>] [groups=<groups>] [rack=<rack>] [chassis=<chassis>] [height=<height>] [unit=<unit>] [rank=rank]
 | |
| \tnodediscoverstart [-h|--help]
 | |
| \tnodediscoverstart {-v|--version}
 | |
| ";
 | |
| 
 | |
|     my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat', 'rank', 'rack', 'chassis', 'height', 'unit', 'groups');
 | |
|     my @mandatoryparams = ('networkprofile', 'imageprofile', 'hostnameformat');
 | |
|     my $ret = validate_args($helpmsg, \@enabledparams, \@mandatoryparams);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my ($returncode, $errmsg) = xCAT::ProfiledNodeUtils->check_profile_consistent($args_dict{'imageprofile'}, $args_dict{'networkprofile'}, $args_dict{'hardwareprofile'});
 | |
|     if (not $returncode) {
 | |
|         setrsp_errormsg($errmsg);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # validate hostnameformat:
 | |
|     my $nameformattype = xCAT::ProfiledNodeUtils->get_hostname_format_type($args_dict{'hostnameformat'});
 | |
|     if ($nameformattype eq "unknown") {
 | |
|         setrsp_errormsg("Invalid node name format: $args_dict{'hostnameformat'}");
 | |
|         return;
 | |
|     } elsif ($nameformattype eq 'rack') {
 | |
|         if ((!exists $args_dict{'rack'}) && (!exists $args_dict{'chassis'})) {
 | |
|             setrsp_errormsg("Specify rack/chassis as node name format includes rack info.");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $recordsref = xCAT::ProfiledNodeUtils->get_all_rack(1);
 | |
|     %allracks   = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_all_chassis(1);
 | |
|     %allchassis = %$recordsref;
 | |
| 
 | |
|     # check rack
 | |
|     if (exists $args_dict{'rack'}) {
 | |
|         if (!exists $allracks{ $args_dict{'rack'} }) {
 | |
|             setrsp_errormsg("Specified rack $args_dict{'rack'} is not defined");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # rack must be specified with chassis or unit + height.
 | |
|         if (exists $args_dict{'chassis'}) {
 | |
|             setrsp_errormsg("Specified chassis cannot be used with rack.");
 | |
|             return;
 | |
|         } else {
 | |
| 
 | |
|             # We set default value for height and u if rack specified
 | |
|             if (!exists $args_dict{'height'}) { $args_dict{'height'} = 1 }
 | |
|             if (!exists $args_dict{'unit'})   { $args_dict{'unit'}   = 1 }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # chassis jdugement.
 | |
|     if (exists $args_dict{'chassis'}) {
 | |
|         if (!exists $allchassis{ $args_dict{'chassis'} }) {
 | |
|             setrsp_errormsg("Specified chassis $args_dict{'chassis'} is not defined.");
 | |
|             return;
 | |
|         }
 | |
|         if (exists $args_dict{'unit'} or exists $args_dict{'height'}) {
 | |
|             setrsp_errormsg("Specified chassis cannot be used with unit or height.");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # height and u must be valid numbers.
 | |
|     if (exists $args_dict{'unit'}) {
 | |
| 
 | |
|         # unit must be specified together with rack.
 | |
|         if (!exists $args_dict{'rack'}) {
 | |
|             setrsp_errormsg("Specified unit must also include specified rack");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # Not a valid number.
 | |
|         if (!($args_dict{'unit'} =~ /^\d+$/)) {
 | |
|             setrsp_errormsg("Specified unit $args_dict{'u'} is invalid");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if (exists $args_dict{'height'}) {
 | |
| 
 | |
|         # unit must be specified together with rack.
 | |
|         if (!exists $args_dict{'rack'}) {
 | |
|             setrsp_errormsg("Specified height must include specified rack.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # Not a valid number.
 | |
|         if (!($args_dict{'height'} =~ /^\d+$/)) {
 | |
|             setrsp_errormsg("Specified height $args_dict{'height'} is invalid.");
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Check the running of sequential discovery
 | |
|     my @PCMdiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover");
 | |
|     if ($PCMdiscover[0]) {
 | |
|         setrsp_errormsg("Profile Discovery cannot be run together with Sequential discovery.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Read DB to confirm the discover is not started yet.
 | |
|     my $discover_running = xCAT::ProfiledNodeUtils->is_discover_started();
 | |
|     if ($discover_running) {
 | |
|         setrsp_errormsg("Profiled nodes discovery already started.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Make sure provisioning network has a dynamic range.
 | |
|     my $provnet = xCAT::ProfiledNodeUtils->get_netprofile_provisionnet($args_dict{networkprofile});
 | |
|     if (!$provnet) {
 | |
|         setrsp_errormsg("Provisioning network not defined for network profile.");
 | |
|         return;
 | |
|     }
 | |
|     my $networkstab = xCAT::Table->new("networks");
 | |
|     my $netentry = ($networkstab->getAllAttribsWhere("netname = '$provnet'", 'ALL'))[0];
 | |
|     if (!$netentry->{'dynamicrange'}) {
 | |
|         setrsp_errormsg("Dynamic IP address range not defined for provisioning network.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # save discover args into table site.
 | |
|     my $valuestr = "";
 | |
|     foreach (keys %args_dict) {
 | |
|         if ($args_dict{$_}) {
 | |
|             $valuestr .= "$_:$args_dict{$_},";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $sitetab = xCAT::Table->new('site', -create => 1);
 | |
|     $sitetab->setAttribs({ "key" => "__PCMDiscover" }, { "value" => "$valuestr" });
 | |
|     $sitetab->close();
 | |
|     setrsp_infostr("Profiled node's discover started");
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  nodediscoverstop
 | |
| 
 | |
|     Description : Stop profiled nodes auto discover. This action will remove the
 | |
|                   dababase flags.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #------------------------------------------------------
 | |
| sub nodediscoverstop {
 | |
| 
 | |
|     my $helpmsg = "nodediscoverstop: stops node auto-discovery.
 | |
| Usage:
 | |
| \tnodediscoverstop
 | |
| \tnodediscoverstop [-h|--help]
 | |
| \tnodediscoverstop {-v|--version}";
 | |
| 
 | |
|     my $ret = validate_args($helpmsg);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Read DB to confirm the discover is started.
 | |
|     xCAT::MsgUtils->message("Stopping profiled node's discover.");
 | |
|     my $discover_running = xCAT::ProfiledNodeUtils->is_discover_started();
 | |
|     if (!$discover_running) {
 | |
| 
 | |
|         # do nothing that make sequential discovery to handle the message
 | |
|         # setrsp_errormsg("Node discovery for all nodes using profiles is not started.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # remove site table records: discover flag.
 | |
|     my $sitetab = xCAT::Table->new("site");
 | |
|     my %keyhash;
 | |
|     $keyhash{'key'} = "__PCMDiscover";
 | |
|     $sitetab->delEntries(\%keyhash);
 | |
|     $sitetab->commit();
 | |
| 
 | |
|     # Update node's attributes, remove from gruop "__PCMDiscover".
 | |
|     # we'll call rmdef so that node's groupinfo in table nodelist will be updated automatically.
 | |
|     my @nodes = xCAT::NodeRange::noderange('__PCMDiscover');
 | |
|     if (@nodes) {
 | |
| 
 | |
|         # There are some nodes discvoered.
 | |
|         my $retref = xCAT::Utils->runxcmd({ command => ["rmdef"], arg => [ "-t", "group", "-o", "__PCMDiscover" ] }, $request_command, 0, 2);
 | |
|     }
 | |
|     setrsp_infostr("Node discovery for all nodes using profiles stopped.");
 | |
| }
 | |
| 
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  nodediscoverstatus
 | |
| 
 | |
|     Description :  This function is obsoleted that the status will be displayed by sequential discovery
 | |
|                        Detect whether Profiled nodes discovery is running or not.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodediscoverstatus {
 | |
| 
 | |
|     my $helpmsg = "nodediscoverstatus: detects if node discovery is running.
 | |
| Usage:
 | |
| \tnodediscoverstatus
 | |
| \tnodediscoverstatus [-h|--help]
 | |
| \tnodediscoverstatus {-v|--version}";
 | |
| 
 | |
|     my $ret = validate_args($helpmsg);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $discover_running = xCAT::ProfiledNodeUtils->is_discover_started();
 | |
|     if ($discover_running) {
 | |
|         setrsp_progress("Node discovery for all nodes using profiles is running");
 | |
|     } else {
 | |
| 
 | |
|         # do nothing that make sequential discovery to handle the message
 | |
|         # setrsp_progress("Node discovery for all nodes using profiles is not started");
 | |
|     }
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  nodediscoverls
 | |
| 
 | |
|     Description : List all discovered profiled nodes.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub nodediscoverls {
 | |
|     my $helpmsg = "nodediscoverls: lists all discovered nodes using profiles.
 | |
| Usage:
 | |
| \tnodediscoverls
 | |
| \tnodediscoverls [-h|--help]
 | |
| \tnodediscoverls {-v|--version}";
 | |
| 
 | |
|     my $ret = validate_args($helpmsg);
 | |
|     if (!$ret) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Read DB to confirm the discover is started.
 | |
|     my $discover_running = xCAT::ProfiledNodeUtils->is_discover_started();
 | |
|     if (!$discover_running) {
 | |
| 
 | |
|         # do nothing that make sequential discovery to handle the message
 | |
|         # setrsp_errormsg("Node discovery process is not running.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my @nodes       = xCAT::NodeRange::noderange('__PCMDiscover');
 | |
|     my $mactab      = xCAT::Table->new("mac");
 | |
|     my $macsref     = $mactab->getNodesAttribs(\@nodes, ['mac']);
 | |
|     my $nodelisttab = xCAT::Table->new("nodelist");
 | |
| 
 | |
|     # Get node current provisioning status.
 | |
|     my $provisionapp = "provision";
 | |
|     my $provision_status = xCAT::TableUtils->getAppStatus(\@nodes, $provisionapp);
 | |
| 
 | |
|     my $rspentry;
 | |
|     my $i = 0;
 | |
|     foreach (@nodes) {
 | |
|         if (!$_) {
 | |
|             next;
 | |
|         }
 | |
|         $rspentry->{node}->[$i]->{"name"} = $_;
 | |
| 
 | |
|         # Only get the MAC address of provisioning NIC.
 | |
|         my @hostentries = split(/\|/, $macsref->{$_}->[0]->{"mac"});
 | |
|         foreach my $hostandmac (@hostentries) {
 | |
|             if (!$hostandmac) {
 | |
|                 next;
 | |
|             }
 | |
|             if (index($hostandmac, "!") == -1) {
 | |
|                 $rspentry->{node}->[$i]->{"mac"} = $hostandmac;
 | |
|                 last;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ($provision_status->{$_}) {
 | |
|             $rspentry->{node}->[$i]->{"status"} = $provision_status->{$_};
 | |
|         } else {
 | |
|             $rspentry->{node}->[$i]->{"status"} = "defined";
 | |
|         }
 | |
|         $i++;
 | |
|     }
 | |
|     $callback->($rspentry);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  findme
 | |
| 
 | |
|     Description : The default interface for node discovery.
 | |
|                   We must implement this method so that
 | |
|                   profiled nodes's findme request can be answered
 | |
|                   while profiled nodes discovery is running.
 | |
|     Arguments   : N/A
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub findme {
 | |
|     if (defined($request->{discoverymethod}) and defined($request->{discoverymethod}->[0]) and ($request->{discoverymethod}->[0] ne 'undef')) {
 | |
| 
 | |
|         # The findme request had been processed by other module, just return
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # re-initalize the global variable
 | |
|     %args_dict = ();
 | |
| 
 | |
|     # Read DB to confirm the discover is started.
 | |
|     my $sitetab = xCAT::Table->new('site');
 | |
|     my $stabent = $sitetab->getAttribs({ 'key' => '__PCMDiscover' }, ('value'));
 | |
|     my $sitevaluesstr;
 | |
|     if (ref $stabent) {
 | |
|         $sitevaluesstr = $stabent->{'value'};
 | |
|     }
 | |
|     unless ($sitevaluesstr) {
 | |
| 
 | |
|         #setrsp_errormsg("Profiled nodes discovery not started yet.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     xCAT::MsgUtils->message('S', "Profile Discovery: Start.\n");
 | |
| 
 | |
|     # We store node profiles in site table, key is "__PCMDiscover"
 | |
|     my @profilerecords = split(',', $sitevaluesstr);
 | |
|     foreach (@profilerecords) {
 | |
|         if ($_) {
 | |
|             my ($profilename, $profilevalue) = split(':', $_);
 | |
|             if ($profilename and $profilevalue) {
 | |
|                 $args_dict{$profilename} = $profilevalue;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     my $imageprofile    = $args_dict{'imageprofile'};
 | |
|     my $networkprofile  = $args_dict{'networkprofile'};
 | |
|     my $hardwareprofile = $args_dict{'hardwareprofile'};
 | |
| 
 | |
|     # Get the netboot attribute for node
 | |
|     my ($retcode, $retval) = xCAT::ProfiledNodeUtils->get_netboot_attr($imageprofile, $hardwareprofile);
 | |
|     if (not $retcode) {
 | |
|         setrsp_errormsg($retval);
 | |
|         return;
 | |
|     }
 | |
|     $netboot = $retval;
 | |
| 
 | |
|     # Get database records: all hostnames, all ips, all racks...
 | |
|     # To improve performance, we should initalize a daemon later??
 | |
|     xCAT::MsgUtils->message('S', "Getting database records.\n");
 | |
|     my $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('nodelist', 'node');
 | |
|     %allhostnames = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
 | |
|     %allbmcips = %$recordsref;
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('mac', 'mac');
 | |
|     %allmacs = %$recordsref;
 | |
| 
 | |
|     foreach (keys %allmacs) {
 | |
|         my @hostentries = split(/\|/, $_);
 | |
|         foreach my $hostandmac (@hostentries) {
 | |
|             my ($macstr, $machostname) = split("!", $hostandmac);
 | |
|             $allmacs{$macstr} = 0;
 | |
|         }
 | |
|     }
 | |
|     %allmacsupper = ();
 | |
|     foreach (keys %allmacs) {
 | |
|         $allmacsupper{ uc($_) } = 0;
 | |
|     }
 | |
|     $recordsref = xCAT::ProfiledNodeUtils->get_allnode_singleattrib_hash('hosts', 'ip');
 | |
|     %allinstallips = %$recordsref;
 | |
|     $recordsref    = xCAT::NetworkUtils->get_all_nicips(1);
 | |
|     %allips        = %$recordsref;
 | |
| 
 | |
|     # Merge all BMC IPs and install IPs into allips.
 | |
|     %allips = (%allips, %allbmcips, %allinstallips);
 | |
| 
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_rack(1);
 | |
|     %allracks      = %$recordsref;
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_chassis(1);
 | |
|     %allchassis    = %$recordsref;
 | |
|     $recordsref    = xCAT::ProfiledNodeUtils->get_all_chassis(1, 'cmm');
 | |
|     %allcmmchassis = %$recordsref;
 | |
| 
 | |
|     # Get discovered client IP and MAC
 | |
|     my $ip = $request->{'_xcat_clientip'};
 | |
|     xCAT::MsgUtils->message('S', "Profield nodes discover: _xcat_clientip is $ip.\n");
 | |
|     my $mac = '';
 | |
|     my $arptable;
 | |
|     if (-x "/usr/sbin/arp") {
 | |
|         $arptable = `/usr/sbin/arp -n`;
 | |
|     }
 | |
|     else {
 | |
|         $arptable = `/sbin/arp -n`;
 | |
|     }
 | |
|     my @arpents = split /\n/, $arptable;
 | |
|     foreach (@arpents) {
 | |
|         if (m/^($ip)\s+\S+\s+(\S+)\s/) {
 | |
|             $mac = $2;
 | |
|             last;
 | |
|         }
 | |
|     }
 | |
|     if (!$mac) {
 | |
|         setrsp_errormsg("Profiled nodes discover: Can not get mac address of this node.");
 | |
|         return;
 | |
|     }
 | |
|     xCAT::MsgUtils->message('S', "Profiled nodes discover: mac is $mac.\n");
 | |
|     if (exists $allmacsupper{ uc($mac) }) {
 | |
|         setrsp_errormsg("Discovered MAC $mac already exists in database.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Assign TMPHOSTS9999 as a temporary hostname, in parse_hsots_string,
 | |
|     # it will detect this and arrange a real hostname for it.
 | |
|     my $raw_hostinfo_str = "TMPHOSTS9999:\n  mac=$mac\n";
 | |
| 
 | |
|     # Append rack, chassis, unit, height into host info string.
 | |
|     foreach my $key ('rack', 'chassis', 'unit', 'height') {
 | |
|         if (exists($args_dict{$key})) {
 | |
|             $raw_hostinfo_str .= "  $key=$args_dict{$key}\n";
 | |
|         }
 | |
|     }
 | |
|     if (exists $args_dict{'unit'} and exists $args_dict{'height'}) {
 | |
| 
 | |
|         # increase start unit automatically.
 | |
|         $args_dict{'unit'} = $args_dict{'unit'} + $args_dict{'height'};
 | |
| 
 | |
|         # save discover args into table site.
 | |
|         my $valuestr = "";
 | |
|         foreach (keys %args_dict) {
 | |
|             if ($args_dict{$_}) {
 | |
|                 $valuestr .= "$_:$args_dict{$_},";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $sitetab = xCAT::Table->new('site', -create => 1);
 | |
|         $sitetab->setAttribs({ "key" => "__PCMDiscover" }, { "value" => "$valuestr" });
 | |
|         $sitetab->close();
 | |
|     }
 | |
| 
 | |
|     # For auto discovering PureFlex (x) nodes, set slotid attribute by default.
 | |
|     if (exists $args_dict{'chassis'}) {
 | |
|         if (exists $allcmmchassis{ $args_dict{'chassis'} }) {
 | |
|             $raw_hostinfo_str .= " slotid=1\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     xCAT::ProfiledNodeUtils->parse_nodeinfo_file($raw_hostinfo_str);
 | |
| 
 | |
|     my ($hostinfo_dict_ref, $invalid_records_ref) = validate_node_entries();
 | |
|     my %hostinfo_dict = %$hostinfo_dict_ref;
 | |
| 
 | |
|     # Create the full hostinfo dict
 | |
|     xCAT::MsgUtils->message('S', "Profiled nodes discover: Generating new hostinfo string.\n");
 | |
|     my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_dict($hostinfo_dict_ref);
 | |
|     unless ($retcode_gen) {
 | |
|         setrsp_errormsg($retstr_gen);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Create hosts and then call nodemgmt for node management plugins.
 | |
|     xCAT::MsgUtils->message('S', "Creating nodes...\n");
 | |
|     my $warnstr;
 | |
|     if (xCAT::DBobjUtils->setobjdefs(\%hostinfo_dict) != 0) {
 | |
|         $warnstr = "Warning: failed to import node.";
 | |
|         setrsp_progress($warnstr);
 | |
|     }
 | |
| 
 | |
|     my @nodelist = keys %hostinfo_dict;
 | |
| 
 | |
|     # setup node provisioning status.
 | |
|     xCAT::Utils->runxcmd({ command => ["updatenodestat"], node => \@nodelist, arg => ['defined'] }, $request_command, -1, 2);
 | |
| 
 | |
|     # call makehosts to get the IP by resolving the name
 | |
|     my $retref = xCAT::Utils->runxcmd({ command => ["makehosts"], node => \@nodelist, sequential => [1] }, $request_command, 0, 2);
 | |
| 
 | |
|     # call discover to notify client.
 | |
|     xCAT::MsgUtils->message('S', "Call discovered request.\n");
 | |
|     $request->{"command"}       = ["discovered"];
 | |
|     $request->{"node"}          = \@nodelist;
 | |
|     $request->{discoverymethod} = ['profile'];
 | |
|     $retref                     = "";
 | |
|     $retref = xCAT::Utils->runxcmd($request, $request_command, 0, 2);
 | |
|     my $retstrref = parse_runxcmd_ret($retref);
 | |
| 
 | |
|     xCAT::MsgUtils->message('S', "Call nodemgmt plugins.\n");
 | |
|     $retref = "";
 | |
|     $retref = xCAT::Utils->runxcmd({ command => ["kitnodeadd"], node => \@nodelist, sequential => [1] }, $request_command, 0, 2);
 | |
|     $retstrref = parse_runxcmd_ret($retref);
 | |
| 
 | |
|     # Set discovered flag.
 | |
|     my $nodegroupstr = $hostinfo_dict{ $nodelist[0] }{"groups"};
 | |
|     my $nodelstab = xCAT::Table->new('nodelist', -create => 1);
 | |
|     $nodelstab->setNodeAttribs($nodelist[0], { groups => $nodegroupstr . ",__PCMDiscover" });
 | |
|     $nodelstab->close();
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  gen_new_hostinfo_dict
 | |
| 
 | |
|     Description : Generate full hostinfo dict
 | |
|     Arguments   : hostinfo_dict_ref - The reference of hostinfo dict.
 | |
|     Returns     : (returnvalue, returnmsg)
 | |
|                   returnvalue - 0, stands for generate new hostinfo dict failed.
 | |
|                                 1, stands for generate new hostinfo dict OK.
 | |
|                   returnnmsg -  error messages if generate failed.
 | |
|                              -  OK for success cases.
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub gen_new_hostinfo_dict {
 | |
|     my $hostinfo_dict_ref = shift;
 | |
|     my %hostinfo_dict     = %$hostinfo_dict_ref;
 | |
| 
 | |
|     # Get free ips list for all networks in network profile.
 | |
|     my @allknownips = keys %allips;
 | |
|     my $netprofileattrsref = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs([ $args_dict{'networkprofile'} ])->{ $args_dict{'networkprofile'} };
 | |
|     my %netprofileattr = %$netprofileattrsref;
 | |
|     my %freeipshash;
 | |
|     foreach (keys %netprofileattr) {
 | |
|         my $netname = $netprofileattr{$_}{'network'};
 | |
|         if ($netname and (!exists $freeipshash{$netname})) {
 | |
|             $freeipshash{$netname} = xCAT::ProfiledNodeUtils->get_allocable_staticips_innet($netname, \@allknownips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Get networkprofile's installip
 | |
|     my $noderestab     = xCAT::Table->new('noderes');
 | |
|     my $networkprofile = $args_dict{'networkprofile'};
 | |
|     my $nodereshashref = $noderestab->getNodeAttribs($networkprofile, ['installnic']);
 | |
|     my %nodereshash = %$nodereshashref;
 | |
|     my $installnic  = $nodereshash{'installnic'};
 | |
| 
 | |
|     # Get node's provisioning method
 | |
|     my $provmethod = xCAT::ProfiledNodeUtils->get_imageprofile_prov_method($args_dict{'imageprofile'});
 | |
| 
 | |
|     # Generate node's chain.
 | |
|     my %nodeprofiles = ('NetworkProfile' => $args_dict{'networkprofile'},
 | |
|         'ImageProfile' => $args_dict{'imageprofile'});
 | |
|     if (defined $args_dict{'hardwareprofile'}) { $nodeprofiles{'HardwareProfile'} = $args_dict{'hardwareprofile'} }
 | |
|     (my $errcode, my $chainstr) = xCAT::ProfiledNodeUtils->gen_chain_for_profiles(\%nodeprofiles, 1);
 | |
|     if ($errcode != 0) {
 | |
|         return (0, "Failed to generate chain for nodes.");
 | |
|     }
 | |
| 
 | |
|     # start to check windows nodes, product will indicate it is a windows node:  win2k8r2.enterprise
 | |
|     my ($osvers, $osprofile) = xCAT::ProfiledNodeUtils->get_imageprofile_prov_osvers($provmethod);
 | |
|     my $product = undef;
 | |
|     if ($osvers =~ /^win/)
 | |
|     {
 | |
|         $product = "$osvers.$osprofile";
 | |
|     }
 | |
| 
 | |
| 
 | |
|     # Check whether this is Power env.
 | |
|     my $is_fsp = xCAT::ProfiledNodeUtils->is_fsp_node($args_dict{'networkprofile'});
 | |
| 
 | |
|     # Check whether this node is PowerKVM Hypervisor node
 | |
|     my $is_kvm_hypv = xCAT::ProfiledNodeUtils->is_kvm_hypv_node($args_dict{'imageprofile'});
 | |
| 
 | |
|     foreach my $item (sort(keys %hostinfo_dict)) {
 | |
| 
 | |
|         # Set Nodes's type:
 | |
|         $hostinfo_dict{$item}{"objtype"} = 'node';
 | |
| 
 | |
|         # Setup switches hash as switch table is a special one:
 | |
|         # We can not set values in table switch through hostinfo_dict,
 | |
|         # but must do that through $swtab1->setAttribs.
 | |
|         if (defined $hostinfo_dict{$item}{switches}) {
 | |
|             my @switchlist = split(/,/, $hostinfo_dict{$item}{switches});
 | |
|             foreach my $spi (@switchlist) {
 | |
|                 if ($spi) {
 | |
|                     my @spilist    = split(/!/, $spi);
 | |
|                     my %keyshash   = ();
 | |
|                     my %valueshash = ();
 | |
|                     $keyshash{'node'}        = $item;
 | |
|                     $valueshash{'interface'} = $spilist[0];
 | |
|                     $keyshash{'switch'}      = $spilist[1];
 | |
|                     $keyshash{'port'}        = $spilist[2];
 | |
|                     push @switch_records, [ \%keyshash, \%valueshash ];
 | |
|                 }
 | |
|             }
 | |
|             delete($hostinfo_dict{$item}{switches});
 | |
|         }
 | |
| 
 | |
|         # Generate IPs for other interfaces defined in MAC file.
 | |
|         my %ipshash;
 | |
|         foreach (keys %netprofileattr) {
 | |
| 
 | |
|             # Not generate IP if exists other nics
 | |
|             if (exists $allothernics{$item}->{$_}) {
 | |
|                 $ipshash{$_} = $allothernics{$item}->{$_};
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Generate IPs for not defined interfaces.
 | |
|         foreach (keys %netprofileattr) {
 | |
|             my $netname = $netprofileattr{$_}{'network'};
 | |
|             my $freeipsref;
 | |
|             if ($netname) {
 | |
|                 $freeipsref = $freeipshash{$netname};
 | |
|             }
 | |
| 
 | |
|             # Not generate other nic's ip if it is defined in file
 | |
|             if (exists $allothernics{$item}->{$_}) {
 | |
|                 next;
 | |
|             }
 | |
| 
 | |
|             # Not generate install nic ip if it is defined in file
 | |
|             if ($_ eq $installnic and exists $hostinfo_dict{$item}{"ip"}) {
 | |
|                 next;
 | |
|             }
 | |
| 
 | |
|             # If generated IP is already used, re-generate free ip
 | |
|             my $nextip = shift @$freeipsref;
 | |
|             while (exists $allips{$nextip}) {
 | |
|                 $nextip = shift @$freeipsref;
 | |
|             }
 | |
| 
 | |
|             if (!$nextip) {
 | |
|                 return 0, "There are no more IP addresses available in the static network range of network $netname for interface $_";
 | |
|             } else {
 | |
|                 $ipshash{$_}     = $nextip;
 | |
|                 $allips{$nextip} = 0;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Apply generated install nic ip to node if it is not defined in file.
 | |
|         if (!exists $hostinfo_dict{$item}{"ip"}) {
 | |
|             if (exists $ipshash{$installnic}) {
 | |
|                 $hostinfo_dict{$item}{"ip"} = $ipshash{$installnic};
 | |
|             } else {
 | |
|                 return 0, "There are no more IP addresses available in the static network range for interface $installnic";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         my $nicips = $installnic . "!" . $hostinfo_dict{$item}{"ip"};
 | |
|         foreach (keys %ipshash) {
 | |
|             if ($_ eq $installnic) { next; }
 | |
|             $nicips = "$_!$ipshash{$_},$nicips";
 | |
|         }
 | |
|         $hostinfo_dict{$item}{"nicips"} = $nicips;
 | |
| 
 | |
|         #save for windows node
 | |
|         if (defined($product) && exists($hostinfo_dict{$item}{"prodkey"}))
 | |
|         {
 | |
|             if (defined($hostinfo_dict{$item}{"prodkey"}))
 | |
|             {
 | |
|                 my $rst = xCAT::ProfiledNodeUtils->update_windows_prodkey($item, $product, $hostinfo_dict{$item}{"prodkey"});
 | |
|                 if ($rst == 1)
 | |
|                 {
 | |
|                     return 0, "Test Store windows per-node key failed for node: $item";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         $hostinfo_dict{$item}{"objtype"} = "node";
 | |
|         $hostinfo_dict{$item}{"groups"}  = "__Managed";
 | |
|         if (exists $args_dict{'networkprofile'}) { $hostinfo_dict{$item}{"groups"} .= "," . $args_dict{'networkprofile'} }
 | |
|         if (exists $args_dict{'imageprofile'}) { $hostinfo_dict{$item}{"groups"} .= "," . $args_dict{'imageprofile'} }
 | |
|         if (exists $args_dict{'hardwareprofile'}) { $hostinfo_dict{$item}{"groups"} .= "," . $args_dict{'hardwareprofile'} }
 | |
|         if (exists $args_dict{'groups'}) { $hostinfo_dict{$item}{"groups"} .= "," . $args_dict{'groups'} }
 | |
|         if ($is_kvm_hypv) { $hostinfo_dict{$item}{"groups"} .= ",__Hypervisor_kvm" }
 | |
| 
 | |
|         # xCAT limitation: slotid attribute only for power, id is for x.
 | |
|         if ((exists $hostinfo_dict{$item}{"slotid"}) && (!$is_fsp)) {
 | |
|             $hostinfo_dict{$item}{"id"} = $hostinfo_dict{$item}{"slotid"};
 | |
|             delete($hostinfo_dict{$item}{"slotid"});
 | |
|         }
 | |
| 
 | |
|         # generage mpa attribute for blades managed by CMM.
 | |
|         if (exists $hostinfo_dict{$item}{"chassis"}) {
 | |
|             my $chassisname = $hostinfo_dict{$item}{"chassis"};
 | |
|             if (exists $allcmmchassis{$chassisname}) {
 | |
|                 $hostinfo_dict{$item}{"mpa"} = $chassisname;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # generate CEC-based rack-mount Power nodes' attributes
 | |
|         # lparid is optional, if not set, set it to 1
 | |
|         if ((exists $hostinfo_dict{$item}{"cec"}) && (!$is_fsp)) {
 | |
|             $hostinfo_dict{$item}{"hcp"}    = $hostinfo_dict{$item}{"cec"};
 | |
|             $hostinfo_dict{$item}{"parent"} = $hostinfo_dict{$item}{"cec"};
 | |
|             delete($hostinfo_dict{$item}{"cec"});
 | |
| 
 | |
|             if (exists $hostinfo_dict{$item}{"lparid"}) {
 | |
|                 $hostinfo_dict{$item}{"id"} = $hostinfo_dict{$item}{"lparid"};
 | |
|                 delete($hostinfo_dict{$item}{"lparid"});
 | |
|             } else {
 | |
|                 $hostinfo_dict{$item}{"id"} = 1;
 | |
|             }
 | |
|             $hostinfo_dict{$item}{"mgt"} = "fsp";
 | |
|         }
 | |
| 
 | |
|         # Set netboot attribute for node
 | |
|         $hostinfo_dict{$item}{"netboot"} = $netboot;
 | |
| 
 | |
|         # get the chain attribute from hardwareprofile and insert it to node.
 | |
|         my $chaintab        = xCAT::Table->new('chain');
 | |
|         my $hardwareprofile = $args_dict{'hardwareprofile'};
 | |
|         my $chain = $chaintab->getNodeAttribs($hardwareprofile, ['chain']);
 | |
| 
 | |
|         $hostinfo_dict{$item}{"chain"} = $chainstr;
 | |
| 
 | |
|         if (exists $netprofileattr{"bmc"}) {    # Update BMC records.
 | |
|             $hostinfo_dict{$item}{"mgt"} = "ipmi";
 | |
| 
 | |
|             if (exists $ipshash{"bmc"}) {
 | |
|                 $hostinfo_dict{$item}{"bmc"} = $ipshash{"bmc"};
 | |
|             } else {
 | |
|                 return 0, "There are no more IP addresses available in the static network range for the BMC network.";
 | |
|             }
 | |
|         } elsif (exists $netprofileattr{"fsp"}) {    # Update FSP records
 | |
|             $hostinfo_dict{$item}{"mgt"} = "fsp";
 | |
|             $hostinfo_dict{$item}{"mpa"} = $hostinfo_dict{$item}{"chassis"};
 | |
| 
 | |
|             if (exists $ipshash{"fsp"}) {
 | |
|                 $hostinfo_dict{$item}{"hcp"} = $ipshash{"fsp"};
 | |
|             } else {
 | |
|                 return 0, "No sufficient IP addresses for FSP";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     return 1, "OK";
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  read_and_generate_hostnames
 | |
| 
 | |
|     Description : Read hostinfo file and generate temporary hostnames for no-hostname specified ones.
 | |
|     Arguments   : hostfile - the location of hostinfo file.
 | |
|     Returns     : (returnvalue, returnmsg)
 | |
|                   returnvalue - 0, stands for a failed return
 | |
|                                 1, stands for a success return
 | |
|                   returnnmsg -  error messages for failed return.
 | |
|                              -  the contents of the hostinfo string.
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub read_and_generate_hostnames {
 | |
|     my $hostfile = shift;
 | |
| 
 | |
|     # Get 10000 temprary hostnames.
 | |
|     my $freehostnamesref = xCAT::ProfiledNodeUtils->gen_numric_hostnames("TMPHOSTS", "", 4);
 | |
| 
 | |
|     # Auto generate hostnames for "__hostname__" entries.
 | |
|     open(HOSTFILE, $hostfile);
 | |
|     my $filecontent = join("", <HOSTFILE>);
 | |
| 
 | |
|     # Convert windows txt file into unix format.
 | |
|     $filecontent =~ s/\cM\cJ/\n/g;
 | |
|     while ((index $filecontent, "__hostname__:") >= 0) {
 | |
|         my $nexthost = shift @$freehostnamesref;
 | |
| 
 | |
|         # no more valid hostnames to assign.
 | |
|         if (!$nexthost) {
 | |
|             return 1, "Failed to generate a node name. There are no more valid node names available.";
 | |
|         }
 | |
| 
 | |
|         # This hostname already specified in hostinfo file.
 | |
|         if ((index $filecontent, "$nexthost:") >= 0) {
 | |
|             next;
 | |
|         }
 | |
| 
 | |
|         # This hostname should not in database.
 | |
|         if (exists $allhostnames{$nexthost}) {
 | |
|             next;
 | |
|         }
 | |
|         $filecontent =~ s/__hostname__/$nexthost/;
 | |
|     }
 | |
|     close(HOSTFILE);
 | |
|     return 0, $filecontent;
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  validate_node_entries
 | |
| 
 | |
|     Description : Validate the node entrie and generate proper attributes.
 | |
|     Arguments   : N/A
 | |
|     Returns     : (hostinfo_dict, invalid_records)
 | |
|                   hostinfo_dict -  Reference of hostinfo dict. Key are hostnames and values is an attributes dict.
 | |
|                   invalid_records - Reference of invalid records list.
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub validate_node_entries {
 | |
|     my %hostinfo_dict;
 | |
|     my @invalid_records;
 | |
| 
 | |
|     my $nameformat = $args_dict{'hostnameformat'};
 | |
| 
 | |
|     my $nameformattype = xCAT::ProfiledNodeUtils->get_hostname_format_type($nameformat);
 | |
|     my %freehostnames;
 | |
| 
 | |
|     # Record duplicated items.
 | |
|     # We should go through list @::profiledNodeObjNames first as  %::profiledNodeAttrs is just a hash,
 | |
|     # it not tells whether there are some duplicated hostnames in the hostinfo string.
 | |
|     my %hostnamedict;
 | |
|     foreach my $hostname (@::profiledNodeObjNames) {
 | |
|         if (exists $hostnamedict{$hostname}) {
 | |
|             push @invalid_records, [ $hostname, "Duplicated hostname defined" ];
 | |
|         } elsif (length($hostname) > 63) {
 | |
| 
 | |
|             # As the rule of IDN encoding, the length of hostname should less than 64 characters.
 | |
|             push @invalid_records, [ $hostname, "The length of hostname is more than 63 characters" ];
 | |
|         } else {
 | |
|             $hostnamedict{$hostname} = 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Verify each node entry.
 | |
|     my $rank = 0;
 | |
|     if (exists($args_dict{'rank'})) {
 | |
|         $rank = $args_dict{'rank'};
 | |
|     }
 | |
| 
 | |
|     # Get all nics attribute in networkprofile
 | |
|     my $networkprofile = $args_dict{networkprofile};
 | |
|     my $netprofileattrsref = xCAT::ProfiledNodeUtils->get_nodes_nic_attrs([$networkprofile])->{$networkprofile};
 | |
|     my %netprofileattr = %$netprofileattrsref;
 | |
| 
 | |
|     # Get install nic and provision network
 | |
|     my $noderestab = xCAT::Table->new('noderes');
 | |
|     my $nodereshashref = $noderestab->getNodeAttribs($networkprofile, ['installnic']);
 | |
|     my %nodereshash = %$nodereshashref;
 | |
|     my $installnic  = $nodereshash{'installnic'};
 | |
|     my $provnet     = $netprofileattr{$installnic}{"network"};
 | |
|     $noderestab->close();
 | |
| 
 | |
|     # Get all nics' static range
 | |
|     my %freeipshash = ();
 | |
|     foreach (keys %netprofileattr) {
 | |
|         my $netname = $netprofileattr{$_}{'network'};
 | |
|         if ($netname and (!exists $freeipshash{$netname})) {
 | |
|             $freeipshash{$netname} = xCAT::ProfiledNodeUtils->get_allocable_staticips_innet($netname);
 | |
|         }
 | |
|     }
 | |
|     my $freeprovipsref = $freeipshash{$provnet};
 | |
| 
 | |
|     # get all chassis's rack info.
 | |
|     my @chassislist = keys %allchassis;
 | |
|     my $chassisrackref = xCAT::ProfiledNodeUtils->get_racks_for_chassises(\@chassislist);
 | |
| 
 | |
|     foreach my $attr (@::profiledNodeObjNames) {
 | |
|         my $errmsg = validate_node_entry($attr, $::profiledNodeAttrs{$attr});
 | |
| 
 | |
|         # Check whether specified IP is in our prov network, static range.
 | |
|         if ($::profiledNodeAttrs{$attr}->{'ip'}) {
 | |
|             unless (grep { $_ eq $::profiledNodeAttrs{$attr}->{'ip'} } @$freeprovipsref) {
 | |
|                 $errmsg .= "Specified IP address $::profiledNodeAttrs{$attr}->{'ip'} not in static range of provision network $provnet";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Check nicips
 | |
|         my $nic_and_ips;
 | |
|         if ($::profiledNodeAttrs{$attr}->{'nicips'}) {
 | |
|             my ($ret, $othernicsref, $outputmsg) = xCAT::ProfiledNodeUtils->check_nicips($installnic, $netprofileattrsref, \%freeipshash, $::profiledNodeAttrs{$attr}->{'nicips'});
 | |
|             if ($ret) {
 | |
|                 $errmsg .= $outputmsg;
 | |
|             } else {
 | |
|                 $nic_and_ips = $othernicsref;
 | |
|             }
 | |
|             delete $::profiledNodeAttrs{$attr}->{'nicips'};
 | |
|         }
 | |
| 
 | |
|         # Set rack info for blades too.
 | |
|         if ($::profiledNodeAttrs{$attr}->{'chassis'}) {
 | |
|             $::profiledNodeAttrs{$attr}->{'rack'} = $chassisrackref->{ $::profiledNodeAttrs{$attr}->{'chassis'} };
 | |
|         }
 | |
|         if ($errmsg) {
 | |
|             if ($attr =~ /^TMPHOSTS/) {
 | |
|                 push @invalid_records, [ "__hostname__", $errmsg ];
 | |
|             } else {
 | |
|                 push @invalid_records, [ $attr, $errmsg ];
 | |
|             }
 | |
|             next;
 | |
|         }
 | |
| 
 | |
|         my $definedhostname = "";
 | |
| 
 | |
|         # We need generate hostnames for this entry.
 | |
|         if ($attr =~ /^TMPHOSTS/)
 | |
|         {
 | |
|             # rack + numric hostname format, we must specify rack in node's definition.
 | |
|             my $numricformat;
 | |
| 
 | |
|             # Need convert hostname format into numric format first.
 | |
|             if ($nameformattype eq "rack") {
 | |
|                 if (!exists $::profiledNodeAttrs{$attr}{"rack"}) {
 | |
|                     push @invalid_records, [ "__hostname__", "Rack information is not specified. You must enter the required rack information." ];
 | |
|                     next;
 | |
|                 }
 | |
|                 $numricformat = xCAT::ProfiledNodeUtils->rackformat_to_numricformat($nameformat, $::profiledNodeAttrs{$attr}{"rack"});
 | |
|                 if (!$numricformat) {
 | |
|                     push @invalid_records, [ "__hostname__", "The rack number of rack $::profiledNodeAttrs{$attr}{'rack'} does not match hostname format $nameformat" ];
 | |
|                 }
 | |
|             } else {
 | |
| 
 | |
|                 # pure numric hostname format
 | |
|                 $numricformat = $nameformat;
 | |
|             }
 | |
| 
 | |
|             # Generate hostnames based on numric hostname format.
 | |
|             my $hostnamelistref;
 | |
|             if (!exists $freehostnames{$numricformat}) {
 | |
|                 $hostnamelistref = xCAT::ProfiledNodeUtils->genhosts_with_numric_tmpl($numricformat, $rank, 10000);
 | |
|                 $rank = $rank + 10000;
 | |
|                 if (!@$hostnamelistref) {
 | |
|                     push @invalid_records, [ "__hostname__", "Can not generate sufficient hostnames from hostname format." ];
 | |
|                     last;
 | |
|                 } else {
 | |
|                     $freehostnames{$numricformat} = $hostnamelistref;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             $hostnamelistref = $freehostnames{$numricformat};
 | |
|             my $nexthostname = shift @$hostnamelistref;
 | |
|             while ((!$nexthostname) || exists $allhostnames{$nexthostname}) {
 | |
|                 if (!@$hostnamelistref) {
 | |
|                     $hostnamelistref = xCAT::ProfiledNodeUtils->genhosts_with_numric_tmpl($numricformat, $rank, 10000);
 | |
|                     $rank = $rank + 10000;
 | |
|                     if (!@$hostnamelistref) {
 | |
|                         push @invalid_records, [ "__hostname__", "Can not generate sufficient hostnames from hostname format." ];
 | |
|                         last;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 $nexthostname = shift @$hostnamelistref;
 | |
|             }
 | |
|             $definedhostname = $nexthostname;
 | |
|             $hostinfo_dict{$nexthostname} = $::profiledNodeAttrs{$attr};
 | |
|         } else {
 | |
|             $definedhostname = $attr;
 | |
|             $hostinfo_dict{$attr} = $::profiledNodeAttrs{$attr};
 | |
|         }
 | |
| 
 | |
|         if ($nic_and_ips) {
 | |
|             $allothernics{$definedhostname} = $nic_and_ips;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return (\%hostinfo_dict, \@invalid_records);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  validate_node_entry
 | |
| 
 | |
|     Description : Validate a node info hash.
 | |
|     Arguments   : node_name - node hostname.
 | |
|                   node_entry_ref - Reference of the node info hash.
 | |
|     Returns     : errormsg
 | |
|                       - undef: stands for no errror.
 | |
|                       - valid string: stands for the error message of validation.
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub validate_node_entry {
 | |
|     my $node_name      = shift;
 | |
|     my $node_entry_ref = shift;
 | |
|     my %node_entry     = %$node_entry_ref;
 | |
|     my $errmsg         = "";
 | |
| 
 | |
|     # duplicate hostname found in hostinfo file.
 | |
|     if (exists $allhostnames{$node_name}) {
 | |
|         $errmsg .= "Node name $node_name already exists. You must use a new node name.\n";
 | |
|     }
 | |
| 
 | |
|     # Must specify either MAC, CEC or switch + port.
 | |
|     if (exists $node_entry{"mac"} ||
 | |
|         exists $node_entry{"switches"} ||
 | |
|         exists $node_entry{"cec"}) {
 | |
|     } else {
 | |
|         $errmsg .= "MAC address, cec, switches is not specified. You must specify the MAC address, CEC name or switches.\n";
 | |
|     }
 | |
| 
 | |
|     if (!xCAT::NetworkUtils->isValidHostname($node_name)) {
 | |
|         unless ($node_name =~ /^TMPHOSTS/) {
 | |
|             $errmsg .= "Node name: $node_name is invalid. You must use a valid node name.\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # validate if node use FSP network
 | |
|     my $is_fsp = xCAT::ProfiledNodeUtils->is_fsp_node($args_dict{'networkprofile'});
 | |
| 
 | |
|     # Check whether this node is a KVM node
 | |
|     my $is_kvm = xCAT::ProfiledNodeUtils->is_kvm_node($args_dict{'hardwareprofile'});
 | |
|     if (not $node_entry{'vmhost'} and $is_kvm) {
 | |
| 
 | |
|         # Using kvm hardware profile but not define vmhost in nodeinfo file
 | |
|         $errmsg .= "No vmhost specified. Specify a vmhost and set it to the node information file if you are using the default IBM_PowerKVM_Guest hardware profile.\n";
 | |
|     }
 | |
| 
 | |
|     # validate each single value.
 | |
|     foreach (keys %node_entry) {
 | |
|         if ($_ eq "mac") {
 | |
|             if (exists $allmacsupper{ uc($node_entry{$_}) }) {
 | |
|                 $errmsg .= "MAC address $node_entry{$_} already exists in the database or in the node information file. You must use a new MAC address.\n";
 | |
|             } elsif (!xCAT::NetworkUtils->isValidMAC($node_entry{$_})) {
 | |
|                 $errmsg .= "MAC address $node_entry{$_} is invalid. You must use a valid MAC address.\n";
 | |
|             } else {
 | |
|                 $allmacs{ $node_entry{$_} } = 0;
 | |
|                 $allmacsupper{ uc($node_entry{$_}) } = 0;
 | |
|             }
 | |
|         } elsif ($_ eq "ip") {
 | |
|             if (exists $allips{ $node_entry{$_} }) {
 | |
|                 $errmsg .= "IP address $node_entry{$_} already exists in the database or in the node information file.\n";
 | |
|             } elsif ((xCAT::NetworkUtils->validate_ip($node_entry{$_}))[0]->[0]) {
 | |
|                 $errmsg .= "IP address $node_entry{$_} is invalid. You must use a valid IP address.\n";
 | |
|             } else {
 | |
| 
 | |
|                 #push the IP into allips list.
 | |
|                 $allips{ $node_entry{$_} } = 0;
 | |
|             }
 | |
|         } elsif ($_ eq "prodkey") {
 | |
| 
 | |
|             # Get node's provisioning os version
 | |
|             my $osimagename = xCAT::ProfiledNodeUtils->get_imageprofile_prov_method($args_dict{'imageprofile'});
 | |
|             my ($osvers, $profile) = xCAT::ProfiledNodeUtils->get_imageprofile_prov_osvers($osimagename);
 | |
|             if (!($osvers =~ /^win/)) {
 | |
|                 $errmsg .= "Specified Windows per-node key to a non-windows node is not acceptable\n";
 | |
|             }
 | |
| 
 | |
|             # it will handle windows pernode key
 | |
|             if (!($node_entry{$_} =~ /\w{5}-\w{5}-\w{5}-\w{5}-\w{5}/)) {
 | |
|                 $errmsg .= "Specified Windows per-node key $node_entry{$_} is not valid\n";
 | |
|             }
 | |
| 
 | |
|             #Transfer to capital
 | |
|             $node_entry{$_} = uc $node_entry{$_};
 | |
|         } elsif ($_ eq "switches") {
 | |
| 
 | |
|             # switches=switch1!1!eth0,switch2!2!eth1
 | |
|             my @interfaceslist = ();
 | |
|             my @switchlist = split(/,/, $node_entry{$_});
 | |
|             foreach my $spi (@switchlist) {
 | |
|                 if ($spi) {
 | |
|                     my @spilist = split(/!/, $spi);
 | |
|                     if (@spilist != 3) {
 | |
|                         $errmsg .= "Invalid 'switches' value $node_entry{$_} specified.\n";
 | |
|                         next;
 | |
|                     }
 | |
| 
 | |
|                     if (!exists $allswitches{ $spilist[1] }) {
 | |
|                         $errmsg .= "Specified switch $spilist[1] is not defined\n";
 | |
|                     }
 | |
| 
 | |
|                     # Not a valid number.
 | |
|                     if (!($spilist[2] =~ /^\d+$/)) {
 | |
|                         $errmsg .= "Specified port $spilist[2] is invalid\n";
 | |
|                     }
 | |
| 
 | |
|                     # now, we need to check "swith_switchport" string list to avoid duplicate config
 | |
|                     my $switch_port = $spilist[1] . "_" . $spilist[2];
 | |
|                     if (exists $all_switchports{$switch_port}) {
 | |
|                         $errmsg .= "Specified switch $spilist[1] and port $spilist[2] already exists in the database or in the node information file. You must use a new switch port.\n";
 | |
|                     } else {
 | |
| 
 | |
|                         # after checking, add this one into all_switchports
 | |
|                         $all_switchports{$switch_port} = 0;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         } elsif ($_ eq "rack") {
 | |
|             if (!exists $allracks{ $node_entry{$_} }) {
 | |
|                 $errmsg .= "Specified rack $node_entry{$_} is not defined\n";
 | |
|             }
 | |
| 
 | |
|             # rack must be specified with chassis or unit + height.
 | |
|             if (exists $node_entry{"chassis"}) {
 | |
|                 $errmsg .= "Specified rack cannot be used with chassis.\n";
 | |
|             } elsif (exists $node_entry{"height"} and exists $node_entry{"unit"}) {
 | |
|             } else {
 | |
|                 $errmsg .= "Specified rack must also specify the height and unit.\n";
 | |
|             }
 | |
|         } elsif ($_ eq "chassis") {
 | |
|             if (!exists $allchassis{ $node_entry{$_} }) {
 | |
|                 $errmsg .= "Specified chassis $node_entry{$_} is not defined\n";
 | |
|             }
 | |
| 
 | |
|             # Chassis must not be specified with unit and height.
 | |
|             if (exists $node_entry{"height"} or exists $node_entry{"unit"}) {
 | |
|                 $errmsg .= "Specified chassis cannot be used with height or unit.\n";
 | |
|             }
 | |
| 
 | |
|             # Check if this chassis is CMM. If it is, must specify slotid
 | |
|             if (exists $allcmmchassis{ $node_entry{$_} }) {
 | |
|                 if (not exists $node_entry{"slotid"}) {
 | |
|                     $errmsg .= "Specified CMM Chassis must be used with slotid";
 | |
|                 }
 | |
|             } else {
 | |
| 
 | |
|                 # If the specific chassis is not CMM chassis, but network is fsp
 | |
|                 if ($is_fsp) {
 | |
|                     $errmsg .= "Specified FSP network must be used with CMM chassis."
 | |
|                 }
 | |
|             }
 | |
|         } elsif ($_ eq "unit") {
 | |
|             if (!exists $node_entry{"rack"}) {
 | |
|                 $errmsg .= "Specified unit must be used with rack.\n";
 | |
|             }
 | |
| 
 | |
|             # Not a valid number.
 | |
|             if (!($node_entry{$_} =~ /^\d+$/)) {
 | |
|                 $errmsg .= "Specified unit $node_entry{$_} is invalid\n";
 | |
|             }
 | |
|         } elsif ($_ eq "height") {
 | |
|             if (!exists $node_entry{"rack"}) {
 | |
|                 $errmsg .= "Height must be used with rack\n";
 | |
|             }
 | |
| 
 | |
|             # Not a valid number.
 | |
|             if (!($node_entry{$_} =~ /^\d+$/)) {
 | |
|                 $errmsg .= "Specified height $node_entry{$_} is invalid\n";
 | |
|             }
 | |
|         } elsif ($_ eq "slotid") {
 | |
|             if (not exists $node_entry{"chassis"}) {
 | |
|                 $errmsg .= "Specified slotid must be used with chassis";
 | |
|             }
 | |
| 
 | |
|             # Not a valid number.
 | |
|             if (!($node_entry{$_} =~ /^[1-9]\d*$/)) {
 | |
|                 $errmsg .= "Specified slotid $node_entry{$_} is invalid";
 | |
|             }
 | |
|         } elsif ($_ eq "lparid") {
 | |
|             if (not exists $node_entry{"cec"}) {
 | |
|                 $errmsg .= "The lparid option must be used with the cec option.\n";
 | |
|             }
 | |
|         } elsif ($_ eq "cec") {
 | |
|             my $cec_name = $node_entry{"cec"};
 | |
|             my $lpar_id  = 1;
 | |
| 
 | |
|             # Check the specified CEC is existing
 | |
|             if (!exists $allcecs{ $node_entry{$_} }) {
 | |
|                 $errmsg .= "The CEC name $node_entry{$_} that is specified in the node information file is not defined in the system.\n";
 | |
|             } elsif (exists $node_entry{"lparid"}) {
 | |
|                 $lpar_id = $node_entry{"lparid"};
 | |
|             }
 | |
| 
 | |
|             if (exists $alllparids{$cec_name}{$lpar_id}) {
 | |
|                 $errmsg .= "The CEC name $cec_name and LPAR id $lpar_id already exist in the database or in the node information file. You must use a new CEC name and LPAR id.\n";
 | |
|             } else {
 | |
|                 $alllparids{$cec_name}{$lpar_id} = 0;
 | |
|             }
 | |
|         } elsif ($_ eq "nicips") {
 | |
| 
 | |
|             # Check Multi-Nic's ip
 | |
|             my $othernics = $node_entry{$_};
 | |
|             foreach my $nic_ips (split(/,/, $othernics)) {
 | |
|                 my @nic_and_ips = ();
 | |
|                 my $nic         = "";
 | |
|                 my $nic_ip      = "";
 | |
|                 if ($nic_ips =~ /!/ and $nic_ips !~ /!$/) {
 | |
|                     @nic_and_ips = split(/!/, $nic_ips);
 | |
|                     $nic_ip = $nic_and_ips[1];
 | |
|                     if (exists $allips{$nic_ip}) {
 | |
|                         $errmsg .= "IP address $nic_ip already exists in the database or in the node information file.\n";
 | |
|                     } elsif ((xCAT::NetworkUtils->validate_ip($nic_ip))[0]->[0]) {
 | |
|                         $errmsg .= "IP address $nic_ip is invalid. You must use a valid IP address.\n";
 | |
|                     } else {
 | |
| 
 | |
|                         #push the IP into allips list.
 | |
|                         $allips{$nic_ip} = 0;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         } elsif ($_ eq "vmhost") {
 | |
| 
 | |
|             # Support PowerKVM vms
 | |
|             my $vm_host = $node_entry{"vmhost"};
 | |
|             if (!exists $allvmhosts{$vm_host}) {
 | |
|                 $errmsg .= "Specified vmhost '$vm_host' is not defined in the system. Specify a correct vmhost.\n";
 | |
| 
 | |
|             }
 | |
| 
 | |
|             if (not $is_kvm) {
 | |
|                 $errmsg .= "Incorrect vmhost '$vm_host' found in node information file. vmhost must be used together with the IBM_PowerKVM_Guest hardware profile\n";
 | |
|             }
 | |
|         } else {
 | |
|             $errmsg .= "Invalid attribute $_ specified\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # push hostinfo into global dicts.
 | |
|     $allhostnames{$node_name} = 0;
 | |
|     return $errmsg;
 | |
| }
 | |
| 
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  setrsp_invalidrecords
 | |
| 
 | |
|     Description : Set response for processing invalid host records.
 | |
|     Arguments   : recordsref - Refrence of invalid nodes list.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub setrsp_invalidrecords
 | |
| {
 | |
|     my $recordsref = shift;
 | |
|     my $rsp;
 | |
| 
 | |
|     # The total number of invalid records.
 | |
|     $rsp->{error}     = ["Errors found in node information file"];
 | |
|     $rsp->{errorcode} = [2];
 | |
|     $rsp->{invalid_records_num}->[0] = scalar @$recordsref;
 | |
| 
 | |
|     # We write details of invalid records into a file.
 | |
|     my ($fh, $filename) = xCAT::ProfiledNodeUtils->get_output_filename();
 | |
|     foreach (@$recordsref) {
 | |
|         my @erroritem = @$_;
 | |
|         print $fh "nodename $erroritem[0], error:\n$erroritem[1]\n";
 | |
|     }
 | |
|     close $fh;
 | |
| 
 | |
|     #make it readable for http.
 | |
|     system("chmod +r $filename");
 | |
| 
 | |
|     # Tells the URL of the details file.
 | |
|     xCAT::MsgUtils->message('S', "Detailed response info placed in file: $filename\n");
 | |
|     $rsp->{details} = [$filename];
 | |
|     $callback->($rsp);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  setrsp_errormsg
 | |
| 
 | |
|     Description : Set response for error messages.
 | |
|     Arguments   : errormsg - Error messages.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub setrsp_errormsg
 | |
| {
 | |
|     my $errormsg = shift;
 | |
|     my $rsp;
 | |
|     xCAT::MsgUtils->message('S', "$errormsg\n");
 | |
|     $rsp->{error}     = [$errormsg];
 | |
|     $rsp->{errorcode} = [1];
 | |
|     $callback->($rsp);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  setrsp_infostr
 | |
| 
 | |
|     Description : Set response for a info string.
 | |
|     Arguments   : infostr - The info string..
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub setrsp_infostr
 | |
| {
 | |
|     my $infostr = shift;
 | |
|     my $rsp;
 | |
|     xCAT::MsgUtils->message('S', "$infostr\n");
 | |
|     $rsp->{data} = [$infostr];
 | |
|     $callback->($rsp);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  setrsp_progress
 | |
| 
 | |
|     Description : Set response for running progress
 | |
|     Arguments   : progress: the progress string.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub setrsp_progress
 | |
| {
 | |
|     my $progress = shift;
 | |
|     my $rsp;
 | |
|     xCAT::MsgUtils->message('S', "$progress");
 | |
|     $rsp->{info} = [$progress];
 | |
|     $callback->($rsp);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  setrsp_success
 | |
| 
 | |
|     Description : Set response for successfully processed nodes.
 | |
|     Arguments   : recordsref - Refrence of nodes list.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub setrsp_success
 | |
| {
 | |
|     my $recordsref = shift;
 | |
|     my $warnstr    = shift;
 | |
|     my $rsp;
 | |
| 
 | |
|     # The total number of success nodes.
 | |
|     $rsp->{success_nodes_num}->[0] = scalar @$recordsref;
 | |
|     my ($fh, $filename) = xCAT::ProfiledNodeUtils->get_output_filename();
 | |
|     foreach (@$recordsref) {
 | |
|         print $fh "success: $_\n";
 | |
|     }
 | |
|     if ($warnstr) {
 | |
|         print $fh "There are some warnings:\n$warnstr\n";
 | |
|     }
 | |
|     close $fh;
 | |
| 
 | |
|     #make it readable for http.
 | |
|     system("chmod +r $filename");
 | |
| 
 | |
|     # Tells the URL of the details file.
 | |
|     xCAT::MsgUtils->message('S', "Detailed response info placed in file: $filename\n");
 | |
|     $rsp->{details} = [$filename];
 | |
|     $callback->($rsp);
 | |
| }
 | |
| 
 | |
| #-----------------------------------------------------
 | |
| 
 | |
| =head3  parse_runxcmd_ret
 | |
| 
 | |
|     Description : Get return of runxcmd and convert it into strings.
 | |
|     Arguments   : The return reference of runxcmd
 | |
|     Return:     : [$outstr, $errstr], A reference of list, placing standard output and standard error message.
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------
 | |
| sub parse_runxcmd_ret
 | |
| {
 | |
|     my $retref = shift;
 | |
| 
 | |
|     my $msglistref;
 | |
|     my $outstr = "";
 | |
|     my $errstr = "";
 | |
|     if ($retref) {
 | |
|         if ($retref->{data}) {
 | |
|             $msglistref = $retref->{data};
 | |
|             $outstr     = Dumper(@$msglistref);
 | |
|             xCAT::MsgUtils->message('S', "Command standard output: $outstr");
 | |
|         }
 | |
|         if ($retref->{error}) {
 | |
|             $msglistref = $retref->{error};
 | |
|             $errstr     = Dumper(@$msglistref);
 | |
|             xCAT::MsgUtils->message('S', "Command error output: $errstr");
 | |
|         }
 | |
|     }
 | |
|     return [ $outstr, $errstr ];
 | |
| }
 | |
| 
 | |
| 1;
 |