#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2005,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 

#
#########################################################################
#
# @(#)67   1.6   src/csm/install/samplescripts/provisionnode.perl, setup, csm_rfish, rfishs001b 9/5/05 21:18:40


#
# Initialize some command name
#
sub initialize
{
	$::LSHWINFO     = "/opt/csm/bin/lshwinfo";
	$::DEFINENODE   = "/opt/csm/bin/definenode";
	$::CSMSETUPKS   = "/opt/csm/bin/csmsetupks";
	$::CSMSETUPYAST = "/opt/csm/bin/csmsetupyast";
	$::INSTALLNODE  = "/opt/csm/bin/installnode";
	$::LSNODE       = "/opt/csm/bin/lsnode";
	@::BLADE_ATTRS=('ConsoleSerialDevice=NONE',
			'ConsoleSerialSpeed=9600',
			'InstallDiskType=ide');
	@::NON_BLADE_ATTRS=('ConsoleSerialDevice=ttyS0',
			'ConsoleSerialSpeed=9600');
	(@::INST_NODES,@::NO_INST_NODES);
	(@::INSTALL_NODE_ATTRS,@::NO_INSTALL_NODE_ATTRS);
}

#
# Display the usage of provisionnode
#
sub usage
{

	print(
		"Usage:\n
    provisionnode -n node_list -f nodedef_file 
      		  -H hardware_control_point[,hardware_control_point...]
      		  -p powermethod\n\n
Flags:\n
-n node_list\t
      Specify a comma or space-separated list of node 
      host names, IP addresses, or node ranges for 
      which to assign to new nodes.  Space-separated 
      lists must be inside double quotes. For inform-
      ation about specifying node ranges, see the no-
      derange man page.\n
-f nodedef_file\t
      Specify a nodedef file which contains default 
      attributes to be used in defining the nodes 
      that are being provisioned.\n
-H hardware_control_point[, hardware_control_point...]\t
      Specify a list of hardware control points 
      that will be queried to detect new nodes. 
      Multiple hardware control points are separated 
      by commas.\n
-p powermethod\t
      Specify the power method used to contact to 
      the hardware control point\n
"
	);
	exit 1;
}

#
# Analyze the arguments for provisionnode
#
sub getArgs
{
	my $tail_nodes;

	if (scalar(@ARGV) <= 0)
	{
		&usage;
	}

	#Checks case in GetOptions
	$Getopt::Long::ignorecase = 0;

	#Allows opts to be grouped (e.g. -avx)
	Getopt::Long::Configure("bundling");

	if (
		!GetOptions(
					'h|?' => \$::HELP,
					'f=s' => \$::NODEDEF,
					'H=s' => \$::HWCTLPT,
					'p=s' => \$::POWERMETHOD,
					'n=s' => \$::STARTNODE,
				   )
	   )
	{
		exit 1;
	}

	# get nodelist need to be definded
	if(scalar(@ARGV))
	{
		$tail_nodes=join(/,/,@ARGV);
	}
	$::STARTNODE .= ",".$tail_nodes;

	&usage if ($::HELP);
	&usage if (!$::NODEDEF || !$::HWCTLPT || !$::POWERMETHOD || !$::STARTNODE);
	if(!grep(/$::POWERMETHOD/,("hmc","bmc","blade")))
	{
		print("provisionnode only support these three PowerMethod: hmc, blade, bmc\n");
		exit 1;
	}
	my $nodedef=&get_file_realpath($::NODEDEF);
	if(-f $nodedef)
	{
		$::NODEDEF=$nodedef;
	}
	else
	{
		print("$::NODEDEF does not existed\n");
		exit 1;
	}
}

# 
# This sub routine is used to get the absolute path of a file.
# Arguments:
# 	opposite path of a file
# Returns:
#       $realpath       Absolute path of the file
#       "0"             file doesn't exist
sub get_file_realpath
{
        my $file=shift;
        my ($dir,$realpath,$filename);
        if(! -f $file)
        {
                return 0;
        }
        if ( $file =~ /^(.*\/)([^\/]*)$/)
        {
                $dir = $1;
                $filename = $2;
        }
        else
        {
                $dir = ".";
                $filename = $file;
        }

        my $orig_dir = `pwd`;
	chomp $orig_dir;
        chdir($dir);

        $realpath = `pwd`;
	chomp $realpath;
        $realpath .= "/".$filename;

        chdir($orig_dir);
        return ($realpath);
}

#
# Create temporary map file
#
# The function will return temporary map filename
#
#
sub create_map_file
{

	#generate map file
	my $map_file = "/tmp/MAP" . rand();
	my $cmd      = "$::LSHWINFO -c $::HWCTLPT -p $::POWERMETHOD -o $map_file";

	print("\nExecuting lshwinfo ...\n");

	if (!system("$cmd") && -f $map_file)
	{
		print("Create map file successfully.\n");
		return $map_file;
	}
	else
	{
		print("Create map file unsuccessfully.\n");
		exit 1;
	}
}

#
# Find out the relevant attrs from map file for nodes.
#
# Argument:
#	$map_file	map file name
# Returns:
# 	no returns, it calls set_node_attr to arrange nodes' attrs
#
sub analyse_map_file
{
	my $map_file = shift;
	my (@nodes,@lines,@node_attrs,@def_attrs);	
	my $line;
	my %map_attr;
	my $host;
	my @using_line;

	open(DEFAULT,$::NODEDEF) || die "Could open $::NODEDEF";
	while(<DEFAULT>)
	{
		if($_ =~ s/\s*(\S+)\s*=.*/$1/)
		{
			push(@def_attrs,$_);
		}
	}
	
	# get nodes' name
        $::STARTNODE =~ s/^\"(.*)\"$/$1/;
        $::STARTNODE =~ s/^\'(.*)\'$/$1/;
        $::STARTNODE =~ s/\s+/,/;
	@lines=`$::DEFINENODE -n $::STARTNODE -s`;
	if(scalar(@lines)==0)
	{
		print("No node was defined\n");
		exit 1;
	}

	foreach $line(@lines)
	{
		chomp $line;
		if($line =~ s/(\S+)\s*:/$1/)
		{
			push(@nodes, $line);
		}
	}
	

	open(MAP,$map_file) || die("Could not open file $map_file: $!");
	my @map_lines=<MAP>;
	close(MAP);


	# assign attrs for each node
	foreach $host (@nodes)
	{
		foreach my $map_line(@map_lines)
		{
		        # Remove comment lines
                        (grep(/^\s*#/, $map_line)) && next;

                        # Remove blank lines
                        ($map_line =~ /^\s*$/) && next;

                        # lose the newline
                        chomp $map_line;

                        # Split the line into fields
                        my (
                        $Hostname,        $PowerMethod, $HWControlPoint,
                        $HWControlNodeId, $LParID,      $HWType,
                        $HWModel,         $HWSerialNum, $DeviceType,
                        $UUID
                     	)  = split(/::/, $map_line);
			$UUID =~ s/\s+//g;
			
			$flag=0;
			if($host =~ /$Hostname/) 
			{
				$flag=1; # push it into install_node
				push(@::INUSED_LINE,$map_line);
				&set_node_attr($host,$flag,$map_line);
				last;
			}
			elsif($host =~ /$HWControlNodeId/)
			{
				$flag=2; # push it into no_install_node
				push(@::INUSED_LINE,$map_line);
				&set_node_attr($host,$flag,$map_line);
				last;
			}
			else
			{
				next;
			}
		}
		if($flag != 1 && $flag != 2)
		{
			foreach my $map_line(@map_lines)
			{
		        	# Remove comment lines
	                        (grep(/^\s*#/, $map_line)) && next;
	
        	                # Remove blank lines
                	        ($map_line =~ /^\s*$/) && next;
	
        	                # lose the newline
                	        chomp $map_line;

				if(!grep(/^$map_line$/,@::INUSED_LINE))
				{
					$flag=3; # push it into no_install_node
					push(@::INUSED_LINE,$map_line);
					&set_node_attr($host,$flag,$map_line);
					last;
				}
			}
		}
	}
}

#
# Get the short name of a host.
#
# Arguments:
#	long/short hostname or ip addr
#
# Returns:
# 	short hostname or ip addr
#        
sub getshorthost
{
        my $host = shift;
        my $shorthost = $host;
        if (!isIpaddr($host))
        {
                $shorthost =~ s/\..*$//;
        }
        return $shorthost;
}

#
# Detect whether a parameter is a valid IP address.
#
# Arguments:
#        dot qulaified IP address: e.g. 1.2.3.4
# Returns:
#        1 - if legal IP address
#        0 - if not legal IP address.
#
#
sub isIpaddr
{
        my $addr = shift;

        #print "addr=$addr\n";
        if ($addr !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
        {
                return 0;
        }

        if ($1 > 255 || $1 == 0 || $2 > 255 || $3 > 255 || $4 > 255)
        {
                return 0;
        }
        else
        {
                return 1;
        }
}

#
# Structure the attrs of the nodes into two arrays.
#
# Arguments:
# 	$host	long hostname of node
#	$flag	the type of node
#	$attr	the attrs found in map file
#	
# Returns:
# 	no returns, but it generates four global variables
# 	@::INST_NODES	
#	@::NO_INSTALL_NODES
#	@::INSTALL_NODE_ATTRS
#	@::NO_INSTALL_NODE_ATTRS
sub set_node_attr
{
	my($host,$flag,$attr)=@_;
        # Split the line into fields
        my (
        $Hostname,        $PowerMethod, $HWControlPoint,
        $HWControlNodeId, $LParID,      $HWType,
        $HWModel,         $HWSerialNum, $DeviceType,
        $UUID
        )  = split(/::/, $attr);
	$UUID =~ s/\s+//g;
	
	$Hostname=$host if($flag == 2 || $flag == 3);
	if($PowerMethod eq "hmc" || $PowerMethod eq "bmc"
		|| $PowerMethod eq "blade")
	{
		if($PowerMethod eq "blade")
		{
			@attrs=('ConsoleServerName='.$HWControlPoint,
                   		'ConsoleMethod='.$PowerMethod,
				@::BLADE_ATTRS);
		}
		else
		{
			@attrs=('ConsoleServerName='.$HWControlPoint,
                   		'ConsoleMethod='.$PowerMethod,
				@::NON_BLADE_ATTRS);
		}
	}
	else
	{
		print("provisionnode only support these three PowerMethod: hmc, blade, bmc\n");
	}
	@attrs=($host.':', @attrs,
		'Hostname='.$Hostname,
		'PowerMethod='.$PowerMethod,
		'HWControlPoint='.$HWControlPoint,
		'HWControlNodeId='.$HWControlNodeId,
		'LParID='.$LParID,
		'HWType='.$HWType,
		'HWModel='.$HWModel,
		'HWSerialNum='.$HWSerialNum,
		'UUID='.$UUID,
	);

	if($flag == 1 or $flag == 2)
	{
		unless(grep(/^$host$/,@::INST_NODES))
		{
			push(@::INST_NODES,$host);
			@::INSTALL_NODE_ATTRS=(@::INSTALL_NODE_ATTRS,@attrs);
		}
	}
	elsif($flag == 3)
	{
		unless(grep(/^$host$/,@::NO_INST_NODES))
		{
			push(@::NO_INST_NODES,$host);
			@::NO_INSTALL_NODE_ATTRS=(@::NO_INSTALL_NODE_ATTRS,@attrs);
		}
	}
}
#
# Create temporary nodedef file
#
# The function will return temporary nodedef filename
# 
# Arguments:
#	$map_file	
# Returns:
#	$node_def	nodedef file
#
sub create_nodedef
{
	my $map_file=shift;
	&analyse_map_file($map_file);
	if(scalar(@::NO_INST_NODES) > 0)
	{
		print("These nodes do not match the entries of map file, provisionnode will chose the first available \"nohostname\" entry for each one. If you agree, please press Y, or press N. \n");
		
	        loop:
		print("Press Y/N\n");
        	chomp(my $str=<STDIN>);
	        unless($str eq 'Y' || $str eq 'N')
		{
			goto loop;
		}
		
		if($str =~ /Y/)
		{
			@::NODES=(@::INST_NODES,@::NO_INST_NODES);
			@::NODE_ATTRS=('include '.$::NODEDEF, 
				@::INSTALL_NODE_ATTRS,@::NO_INSTALL_NODE_ATTRS);
		}
		else
		{
			@::NODES=(@::INST_NODES);
			@::NODE_ATTRS=('include '.$::NODEDEF,@::INSTALL_NODE_ATTRS);
		}
	}
	else
	{
		@::NODES=(@::INST_NODES);
		@::NODE_ATTRS=(@::INSTALL_NODE_ATTRS);
	}
	my $num=scalar(@::INSTALL_NODE_ATTRS);

	$::NLIST=join(',',@::NODES);

	#write to a temporary nodedef file
	my $node_def = "/tmp/DEF" . rand();
	open(DEF, ">$node_def") || die("Could not open file $node_def: $!");
	print DEF "include $::NODEDEF\n";
	foreach(@::NODE_ATTRS)
	{
		print DEF "$_\n";
	}
	close DEF;
	return $node_def;
}


#
# definenode with new nodedef file produced by create_nodedef
#
sub run_definenode
{
	my $nodedef = shift;


	my $cmd     = "$::DEFINENODE";

	# Transfer variables to defindenode
	$cmd .= " -f $nodedef" if ($nodedef);

	if(!system("$cmd"))
	{
		print("definenode is successful\n");
	}
	else
	{
		print("definenode is failed\n");
		exit 1;
	}
}

#
# Run csmsetupks/yast for definded nodes
#
sub run_csmsetup
{

	my $cmd;
	my $csmsetup_cmd;
	my $flag;

	print("Waiting for Hardware Control library initializing...\n");
	sleep(60);
	my $node=&getshorthost($::NODES[0]);
	if(!$node)
	{
		print("No node was defined\n");
		exit 1;
	}
	my $DistributionName =
	  `$::LSNODE -a InstallDistributionName |grep $node | head -n 1 |awk '{print $NF}'`;

	if ($DistributionName =~ /RedHatEL/)
	{
		$csmsetup_cmd = $::CSMSETUPKS;
		$flag         = "csmsetupks";
	}
	elsif ($DistributionName =~ /SLES/)
	{
		$csmsetup_cmd = $::CSMSETUPYAST;
		$flag         = "csmsetupyast";
	}
	else
	{
		print("Wrong InstallDistributionName attribute. \n");
		exit 1;
	}

	print("\nExecuting $flag ...\n");
	$cmd = $csmsetup_cmd . " -xn " . $::NLIST;
	print("$cmd\n");
	if(!system("$cmd 2>&1"))
	{
		print("$flag is successful \n");
	}
	else
	{
		print("\ncsmsetup is failed\n");
		exit -1;
	}

}

#
# Run installnode for defined nodes
#
sub run_installnode
{
	my $cmd = "$::INSTALLNODE -n $::NLIST";

	if(!system("$cmd 2>&1"))
	{
		print("installnode is running ...\n");
	}
	else
	{
		print("installnode is failed\n");
		exit 1;
	}
}

#----------------------------------------------------
#
# Main code
#
#----------------------------------------------------
use Getopt::Long;

my $rc;
my ($map_file, $nodedef);

&initialize;

&getArgs;

print("Creating map file ...\n");
$map_file = &create_map_file;

$nodedef = &create_nodedef($map_file);

# definenode with new nodedef file
print("\nExecuting definenode ...\n");
&run_definenode($nodedef);

# Remove temporary files
unlink($map_file);
unlink($nodedef);

# run csmsetupks/csmsetupyast
&run_csmsetup;

# Run installnode
print("\nExecuting installnode ...\n");
&run_installnode;
