mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 11:22:27 +00:00 
			
		
		
		
	xCAT baremetal driver for OpenStack
This commit is contained in:
		
							
								
								
									
										778
									
								
								xCAT-OpenStack-baremetal/lib/perl/xCAT_plugin/openstack.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										778
									
								
								xCAT-OpenStack-baremetal/lib/perl/xCAT_plugin/openstack.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,778 @@ | ||||
| # IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| package xCAT_plugin::openstack; | ||||
| BEGIN | ||||
| { | ||||
|     $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; | ||||
| } | ||||
| use lib "$::XCATROOT/lib/perl"; | ||||
| use xCAT::Utils; | ||||
| use xCAT::TableUtils; | ||||
| use xCAT::SvrUtils; | ||||
| use xCAT::NetworkUtils; | ||||
| use xCAT::Table; | ||||
| use Data::Dumper; | ||||
| use File::Path; | ||||
| use File::Copy; | ||||
| use Getopt::Long; | ||||
| Getopt::Long::Configure("bundling"); | ||||
| Getopt::Long::Configure("pass_through"); | ||||
|  | ||||
|  | ||||
| sub handled_commands { | ||||
|     return { | ||||
| 		opsaddbmnode => "openstack",  #external command | ||||
| 		opsaddimage => "openstack",   #external command | ||||
| 		deploy_ops_bm_node => "openstack",   #internal command called from the baremetal driver | ||||
| 		cleanup_ops_bm_node => "openstack",  #internal command called from the baremetal driver | ||||
|    } | ||||
| } | ||||
|  | ||||
| sub process_request { | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my $doreq = shift; | ||||
| 	my $command = $request->{command}->[0]; | ||||
| 	 | ||||
| 	if ($command eq "opsaddbmnode") { | ||||
| 		return opsaddbmnode($request, $callback, $doreq); | ||||
| 	} elsif ($command eq "opsaddimage") { | ||||
| 		return opsaddimage($request, $callback, $doreq); | ||||
| 	} elsif ($command eq "deploy_ops_bm_node") { | ||||
| 		return deploy_ops_bm_node($request, $callback, $doreq); | ||||
| 	} elsif ($command eq "cleanup_ops_bm_node") { | ||||
| 		return cleanup_ops_bm_node($request, $callback, $doreq); | ||||
| 	} else { | ||||
| 		$callback->({error=>["Unsupported command: $command."],errorcode=>[1]}); | ||||
| 		return 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  opsaddbmnode | ||||
|      This function takes the xCAT nodes and register them | ||||
|      as the OpenStack baremetal nodes | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub opsaddbmnode { | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my $doreq = shift; | ||||
| 	 | ||||
| 	@ARGV = @{$request->{arg}}; | ||||
| 	Getopt::Long::Configure("bundling"); | ||||
| 	Getopt::Long::Configure("no_pass_through"); | ||||
| 	 | ||||
| 	my $help; | ||||
| 	my $version; | ||||
| 	my $host; | ||||
| 	 | ||||
|     if(!GetOptions( | ||||
|             'h|help'      => \$help, | ||||
|             'v|version'   => \$version, | ||||
|             's=s'         => \$host, | ||||
|        )) | ||||
|     { | ||||
|         &opsaddbmnode_usage($callback); | ||||
|         return 1; | ||||
|     } | ||||
|     # display the usage if -h or --help is specified | ||||
|     if ($help) { | ||||
|         &opsaddbmnode_usage($callback); | ||||
|         return 0; | ||||
|     } | ||||
|     # display the version statement if -v or --verison is specified | ||||
|     if ($version) | ||||
|     { | ||||
|         my $rsp={}; | ||||
|         $rsp->{data}->[0]= xCAT::Utils->Version(); | ||||
|         $callback->($rsp); | ||||
|         return 0; | ||||
|     } | ||||
| 	 | ||||
| 	if (!$request->{node}) { | ||||
| 		$callback->({error=>["Please specify at least one node."],errorcode=>[1]}); | ||||
| 		return 1;   | ||||
| 	} | ||||
| 	if (!$host) { | ||||
| 		$callback->({error=>["Please specify the OpenStack compute host name with -s flag."],errorcode=>[1]}); | ||||
| 		return 1;   | ||||
| 	} | ||||
|      | ||||
|     my $nodes = $request->{node}; | ||||
|  | ||||
|     #get bmc info for the nodes | ||||
| 	my $ipmitab = xCAT::Table->new("ipmi", -create => 0); | ||||
| 	my $tmp_ipmi; | ||||
| 	if ($ipmitab) { | ||||
| 		$tmp_ipmi = $ipmitab->getNodesAttribs($nodes, ['bmc','username', 'password'], prefetchcache=>1); | ||||
| 		#print Dumper($tmp_ipmi); | ||||
| 	} else { | ||||
| 		$callback->({error=>["Cannot open the ipmi table."],errorcode=>[1]}); | ||||
| 		return 1;		 | ||||
| 	} | ||||
|     #get mac for the nodes | ||||
| 	my $mactab = xCAT::Table->new("mac", -create => 0); | ||||
| 	my $tmp_mac; | ||||
| 	if ($mactab) { | ||||
| 		$tmp_mac = $mactab->getNodesAttribs($nodes, ['mac'], prefetchcache=>1); | ||||
| 		#print Dumper($tmp_mac); | ||||
| 	} else { | ||||
| 		$callback->({error=>["Cannot open the mac table."],errorcode=>[1]}); | ||||
| 		return 1;		 | ||||
| 	} | ||||
|  | ||||
|     #get cpu, memory and disk info for the nodes | ||||
| 	my $hwinvtab = xCAT::Table->new("hwinv", -create => 0); | ||||
| 	my $tmp_hwinv; | ||||
| 	if ($hwinvtab) { | ||||
| 		$tmp_hwinv = $hwinvtab->getNodesAttribs($nodes, ['cpucount', 'memory', 'disksize'], prefetchcache=>1); | ||||
| 		#print Dumper($tmp_hwinv); | ||||
| 	} else { | ||||
| 		$callback->({error=>["Cannot open the hwinv table."],errorcode=>[1]}); | ||||
| 		return 1;		 | ||||
| 	} | ||||
|  | ||||
| 	foreach my $node (@$nodes) { | ||||
|         #collect the node infomation needed for each node, some info | ||||
|         #may not be defined in the xCAT db | ||||
| 		my ($bmc, $bmc_user, $bmc_password, $mac, $cpu, $memory, $disk); | ||||
| 		my $ref_ipmi = $tmp_ipmi->{$node}->[0];  | ||||
| 	    if ($ref_ipmi) { | ||||
| 			if (exists($ref_ipmi->{bmc})) { | ||||
| 				$bmc = $ref_ipmi->{bmc}; | ||||
| 			} | ||||
| 			if (exists($ref_ipmi->{username})) { | ||||
| 				$bmc_user = $ref_ipmi->{username}; | ||||
| 			} | ||||
| 			if (exists($ref_ipmi->{password})) { | ||||
| 				$bmc_password = $ref_ipmi->{password}; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$ref_mac = $tmp_mac->{$node}->[0];  | ||||
| 	    if ($ref_mac) { | ||||
| 			if (exists($ref_mac->{mac})) { | ||||
| 				$mac = $ref_mac->{mac}; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$ref_hwinv = $tmp_hwinv->{$node}->[0];  | ||||
| 	    if ($ref_hwinv) { | ||||
| 			if (exists($ref_hwinv->{cpucount})) { | ||||
| 				$cpu = $ref_hwinv->{cpucount}; | ||||
| 			} | ||||
| 			if (exists($ref_hwinv->{memory})) { | ||||
| 				$memory = $ref_hwinv->{memory}; | ||||
|                 #TODO: what if the unit is not in MB? We need to convert it to MB | ||||
| 				$memory =~ s/MB|mb//g; | ||||
| 			} | ||||
| 			if (exists($ref_hwinv->{disksize})) { | ||||
| 				#The format of the the disk size is: sda:250GB,sdb:250GB or just 250GB | ||||
|                 #We need to get the size of the first one | ||||
| 				$disk = $ref_hwinv->{disksize}; | ||||
| 				my @a = split(',', $disk); | ||||
| 				my @b = split(':', $a[0]); | ||||
| 				if (@b > 1) { | ||||
| 					$disk = $b[1]; | ||||
| 				} else { | ||||
| 					$disk = $b[0]; | ||||
| 				} | ||||
|                 print "a=@a, b=@b\n"; | ||||
|                 #TODO: what if the unit is not in GB? We need to convert it to MB | ||||
| 				$disk =~ s/GB|gb//g; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		#some info are mendatory | ||||
|         if (!$mac) { | ||||
| 			$callback->({error=>["Mac address is not defined in the mac table for node $node."],errorcode=>[1]}); | ||||
| 			next; | ||||
| 		} | ||||
| 		if (!$cpu) { | ||||
| 			#default cpu count is 1 | ||||
| 			$cpu = 1; | ||||
| 		} | ||||
| 		if (!$memory) { | ||||
| 			#default memory size is 1024MB=1GB | ||||
| 			$memory = 1024; | ||||
| 		} | ||||
| 		if (!$disk) { | ||||
| 			#default disk size is 1GB | ||||
| 			$disk = 1; | ||||
| 		}				 | ||||
| 		 | ||||
| 		#print "$bmc, $bmc_user, $bmc_password, $mac, $cpu, $memory, $disk\n"; | ||||
|  | ||||
| 		#call OpenStack command to add the node into the OpenStack as | ||||
|         #a baremetal node. | ||||
|         my $cmd_tmp = "nova baremetal-node-create"; | ||||
| 		if ($bmc) { | ||||
| 			#make sure it is an ip address | ||||
| 			if (($bmc !~ /\d+\.\d+\.\d+\.\d+/) && ($bmc !~ /:/)) { | ||||
| 				$bmc =  xCAT::NetworkUtils->getipaddr($bmc); | ||||
| 			} | ||||
| 			$cmd_tmp .= " --pm_address=$bmc";			 | ||||
| 		} | ||||
| 		if ($bmc_user) { | ||||
| 			$cmd_tmp .= " --pm_user=$bmc_user"; | ||||
| 		} | ||||
| 		if ($bmc_password) { | ||||
| 			$cmd_tmp .= " --pm_password=$bmc_password"; | ||||
| 		} | ||||
| 		$cmd_tmp .= " $host $cpu $memory $disk $mac"; | ||||
|   | ||||
| 		my $cmd = qq~source \~/openrc;$cmd_tmp~; | ||||
| 		#print "cmd=$cmd\n"; | ||||
| 		my $output = | ||||
| 			xCAT::InstUtils->xcmd($callback, $doreq, "xdsh", [$host], $cmd, 0); | ||||
| 		if ($::RUNCMD_RC != 0) { | ||||
| 			my $rsp; | ||||
| 			push @{$rsp->{data}}, "OpenStack creating baremetal node $node:"; | ||||
| 			push @{$rsp->{data}}, "$output"; | ||||
| 			xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  opsaddimage | ||||
|      This function takes the xCAT nodes and register them | ||||
|      as the OpenStack baremetal nodes | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub opsaddimage { | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my $doreq = shift; | ||||
| 	 | ||||
| 	@ARGV = @{$request->{arg}}; | ||||
| 	Getopt::Long::Configure("bundling"); | ||||
| 	Getopt::Long::Configure("no_pass_through"); | ||||
| 	 | ||||
| 	my $help; | ||||
| 	my $version; | ||||
| 	#my $cloud; | ||||
| 	my $ops_img_names; | ||||
|     my $controller; | ||||
| 	 | ||||
|     if(!GetOptions( | ||||
|             'h|help'      => \$help, | ||||
|             'v|version'   => \$version, | ||||
|             'c=s'         => \$controller, | ||||
| 			'n=s'         => \$ops_img_names, | ||||
|        )) | ||||
|     { | ||||
|         &opsaddimage_usage($callback); | ||||
|         return 1; | ||||
|     } | ||||
|     # display the usage if -h or --help is specified | ||||
|     if ($help) { | ||||
|         &opsaddimage_usage($callback); | ||||
|         return 0; | ||||
|     } | ||||
|     # display the version statement if -v or --verison is specified | ||||
|     if ($version) | ||||
|     { | ||||
|         my $rsp={}; | ||||
|         $rsp->{data}->[0]= xCAT::Utils->Version(); | ||||
|         $callback->($rsp); | ||||
|         return 0; | ||||
|     } | ||||
| 	 | ||||
| 	if (@ARGV ==0) { | ||||
| 		$callback->({error=>["Please specify an image name or a list of image names."],errorcode=>[1]}); | ||||
| 		return 1;   | ||||
| 	} | ||||
|  | ||||
| 	#make sure the input cloud name is valid. | ||||
| 	#if (!$cloud) { | ||||
| 	#	$callback->({error=>["Please specify the name of the cloud with -c flag."],errorcode=>[1]}); | ||||
| 	#	return 1;   | ||||
| 	#} else { | ||||
| 	#	my $cloudstab = xCAT::Table->new('clouds', -create => 0); | ||||
| 	#	my @et = $cloudstab->getAllAttribs('name', 'controller'); | ||||
| 	#	if(@et) { | ||||
| 	#		foreach my $tmp_et (@et) { | ||||
| 	#			if ($tmp_et->{name} eq $cloud) { | ||||
| 	#				if ($tmp_et->{controller}) { | ||||
| 	#					$controller = $tmp_et->{controller}; | ||||
| 	#					last; | ||||
| 	#				} else { | ||||
| 	#					$callback->({error=>["Please specify the controller in the clouds table for the cloud: $cloud."],errorcode=>[1]}); | ||||
| 	#					return 1;  	 | ||||
| 	#				} | ||||
| 	#			} | ||||
| 	#		} | ||||
| 	#	} | ||||
| 	 | ||||
| 	if (!$controller) { | ||||
| 		$callback->({error=>["Please specify the OpenStack controller node name with -c."],errorcode=>[1]}); | ||||
| 		return 1;  			 | ||||
| 	} | ||||
| 	#} | ||||
|  | ||||
| 	#make sure that the images from the command are valid image names | ||||
|     @images = split(',', $ARGV[0]); | ||||
|     @new_names = (); | ||||
| 	if ($ops_img_names) { | ||||
| 		@new_names = split(',', $ops_img_names); | ||||
| 	} | ||||
| 	#print "images=@images, new image names=@new_names, controller=$controller\n"; | ||||
|  | ||||
| 	my $image_hash = {}; | ||||
|     my $osimgtab = xCAT::Table->new('osimage', -create => 0); | ||||
|     my @et = $osimgtab->getAllAttribs('imagename'); | ||||
| 	if(@et) { | ||||
| 		foreach my $tmp_et (@et) { | ||||
| 			$image_hash->{$tmp_et->{imagename}}{'xCAT'} = 1; | ||||
| 		} | ||||
| 	} | ||||
| 	my @bad_images; | ||||
| 	foreach my $image (@images) { | ||||
| 		if (!exists($image_hash->{$image})) { | ||||
| 			push @bad_images, $image; | ||||
| 		} | ||||
| 	} | ||||
| 	if (@bad_images > 0) { | ||||
| 		$callback->({error=>["The following images cannot be found in xCAT osimage table:\n  " . join("\n  ", @bad_images) . "\n"],errorcode=>[1]}); | ||||
| 		return 1;   | ||||
| 	} | ||||
|  | ||||
| 	my $index=0; | ||||
|  	foreach my $image (@images) { | ||||
| 		my $new_name = shift(@new_names); | ||||
| 		if (!$new_name) { | ||||
| 			$new_name = $image; #the default new name is xCAT image name | ||||
| 		} | ||||
|         my $cmd_tmp = "glance image-create --name $new_name --public --disk-format qcow2 --container-format bare --property xcat_image_name=\'$image\' < /tmp/$image.qcow2"; | ||||
|  | ||||
| 		my $cmd = qq~touch /tmp/$image.qcow2;source \~/openrc;$cmd_tmp;rm /tmp/$image.qcow2~; | ||||
| 		#print "cmd=$cmd\ncontroller=$controller\n"; | ||||
| 		my $output = | ||||
| 			xCAT::InstUtils->xcmd($callback, $doreq, "xdsh", [$controller], $cmd, 0); | ||||
| 		if ($::RUNCMD_RC != 0) { | ||||
| 			my $rsp; | ||||
| 			push @{$rsp->{data}}, "OpenStack creating image $new_name:"; | ||||
| 			push @{$rsp->{data}}, "$output"; | ||||
| 			xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 		}		  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  deploy_ops_bm_node | ||||
| 	This is a internel command called by OpenStack xCAT-baremetal driver.  | ||||
| 	It prepares the node by adding the config_ops_bm_node postbootscript  | ||||
| 	to the postscript table for the node, then call nodeset and then boot  | ||||
| 	the node up. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub deploy_ops_bm_node { | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my $doreq = shift; | ||||
| 	 | ||||
| 	@ARGV = @{$request->{arg}}; | ||||
| 	Getopt::Long::Configure("bundling"); | ||||
| 	Getopt::Long::Configure("no_pass_through"); | ||||
|  | ||||
| 	my $node = $request->{node}->[0]; | ||||
| 	 | ||||
| 	my $help; | ||||
| 	my $version; | ||||
| 	my $img_name; | ||||
|     my $hostname; | ||||
| 	my $fixed_ip; | ||||
| 	my $netmask; | ||||
| 	 | ||||
|     if(!GetOptions( | ||||
|             'h|help'      => \$help, | ||||
|             'v|version'   => \$version, | ||||
|             'image=s'     => \$img_name, | ||||
| 			'host=s'      => \$hostname, | ||||
| 			'ip=s'        => \$fixed_ip, | ||||
| 			'mask=s'      => \$netmask, | ||||
|        )) | ||||
|     { | ||||
|         &deploy_ops_bm_node_usage($callback); | ||||
|         return 1; | ||||
|     } | ||||
|     # display the usage if -h or --help is specified | ||||
|     if ($help) { | ||||
|         &deploy_ops_bm_node_usage($callback); | ||||
|         return 0; | ||||
|     } | ||||
|     # display the version statement if -v or --verison is specified | ||||
|     if ($version) | ||||
|     { | ||||
|         my $rsp={}; | ||||
|         $rsp->{data}->[0]= xCAT::Utils->Version(); | ||||
|         $callback->($rsp); | ||||
|         return 0; | ||||
|     } | ||||
| 	print "node=$node, image=$img_name, host=$hostname, ip=$fixed_ip, mask=$netmask\n"; | ||||
|  | ||||
| 	#validate the image name | ||||
|     my $osimagetab = xCAT::Table->new('osimage', -create=>1); | ||||
| 	my $ref = $osimagetab->getAttribs({imagename => $img_name}, 'imagename'); | ||||
| 	if (!$ref) { | ||||
| 		$callback->({error=>["Invalid image name: $img_name."],errorcode=>[1]}); | ||||
| 		return 1;   | ||||
| 	} | ||||
|  | ||||
| 	#check if the fixed ip is within the xCAT management network. | ||||
| 	#get the master ip address for the node then check if the master ip and  | ||||
| 	#the OpenStack fixed_ip are on the same subnet. | ||||
| 	#my $same_nw = 0; | ||||
| 	#my $master = xCAT::TableUtils->GetMasterNodeName($node); | ||||
| 	#my $master_ip = xCAT::NetworkUtils->toIP($master); | ||||
| 	#if (xCAT::NetworkUtils::isInSameSubnet($master_ip, $fixed_ip, $netmask, 0)) { | ||||
| 	#	$same_nw = 1; | ||||
| 	#} | ||||
| 	    | ||||
| 	 | ||||
| 	#add config_ops_bm_node to the node's postbootscript | ||||
| 	my $script = "config_ops_bm_node $hostname $fixed_ip $netmask"; | ||||
| 	add_postscript($callback, $node, $script); | ||||
|  | ||||
|     #run nodeset  | ||||
| 	my $cmd = qq~osimage=$img_name~; | ||||
| 	my $output = xCAT::Utils->runxcmd( | ||||
| 		{command => ["nodeset"], | ||||
| 		 node    => [$node],  | ||||
| 		 arg     => [$cmd]}, | ||||
| 		$doreq, -1, 1); | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 		my $rsp; | ||||
| 		push @{$rsp->{data}}, "nodeset:"; | ||||
| 		push @{$rsp->{data}}, "$output"; | ||||
| 		xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 		return 1; | ||||
| 	}		 | ||||
|   | ||||
|     #set boot order, assuming it is ipmi nodes for now | ||||
|     # TODO: add support for system power hw. | ||||
| 	my $cmd = qq~net~; | ||||
| 	my $output = xCAT::Utils->runxcmd( | ||||
| 		{command => ["rsetboot"], | ||||
| 		 node    => [$node],  | ||||
| 		 arg     => [$cmd]}, | ||||
| 		$doreq, -1, 1); | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 		my $rsp; | ||||
| 		push @{$rsp->{data}}, "rsetboot:"; | ||||
| 		push @{$rsp->{data}}, "$output"; | ||||
| 		xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 	}		 | ||||
| 	 | ||||
|     #reboot the node | ||||
| 	my $cmd = qq~boot~; | ||||
| 	my $output = xCAT::Utils->runxcmd( | ||||
| 		{command => ["rpower"], | ||||
| 		 node    => [$node],  | ||||
| 		 arg     => [$cmd]}, | ||||
| 		$doreq, -1, 1); | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 		my $rsp; | ||||
| 		push @{$rsp->{data}}, "rpower:"; | ||||
| 		push @{$rsp->{data}}, "$output"; | ||||
| 		xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 		return 1; | ||||
| 	}		 | ||||
| } | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  cleanup_ops_bm_node | ||||
| 	This is a internel command called by OpenStack xCAT-baremetal driver. | ||||
| 	It undoes all the changes made by deploy_ops_bm_node command. It removes | ||||
| 	the config_ops_bmn_ode postbootscript from the postscript table for the  | ||||
| 	node, removes the alias ip and then power off the node. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub cleanup_ops_bm_node { | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my $doreq = shift; | ||||
| 	 | ||||
| 	@ARGV = @{$request->{arg}}; | ||||
| 	Getopt::Long::Configure("bundling"); | ||||
| 	Getopt::Long::Configure("no_pass_through"); | ||||
|  | ||||
| 	my $node = $request->{node}->[0]; | ||||
| 	 | ||||
| 	my $help; | ||||
| 	my $version; | ||||
|  	my $fixed_ip; | ||||
| 	 | ||||
|     if(!GetOptions( | ||||
|             'h|help'      => \$help, | ||||
|             'v|version'   => \$version, | ||||
| 			'ip=s'        => \$fixed_ip, | ||||
|        )) | ||||
|     { | ||||
|         &cleanup_ops_bm_node_usage($callback); | ||||
|         return 1; | ||||
|     } | ||||
|     # display the usage if -h or --help is specified | ||||
|     if ($help) { | ||||
|         &cleanup_ops_bm_node_usage($callback); | ||||
|         return 0; | ||||
|     } | ||||
|     # display the version statement if -v or --verison is specified | ||||
|     if ($version) | ||||
|     { | ||||
|         my $rsp={}; | ||||
|         $rsp->{data}->[0]= xCAT::Utils->Version(); | ||||
|         $callback->($rsp); | ||||
|         return 0; | ||||
|     } | ||||
| 	#print "node=$node, ip=$fixed_ip\n";    | ||||
| 	 | ||||
| 	#removes the config_ops_bm_node postbootscript from the postscripts table | ||||
| 	remove_postscript($callback, $node, "config_ops_bm_node"); | ||||
|  | ||||
|  | ||||
| 	#run updatenode to remove the ip alias  | ||||
| 	my $cmd = qq~-P deconfig_ops_bm_node $fixed_ip~; | ||||
| 	my $output = xCAT::Utils->runxcmd( | ||||
| 		{command => ["updatenode"], | ||||
| 		 node    => [$node],  | ||||
| 		 arg     => [$cmd]}, | ||||
| 		$doreq, -1, 1); | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 		my $rsp; | ||||
| 		push @{$rsp->{data}}, "updatenode:"; | ||||
| 		push @{$rsp->{data}}, "$output"; | ||||
| 		xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 	} | ||||
| 		 | ||||
|     #turn the node power off | ||||
| 	$ssh_ok = 0; | ||||
| 	my $cmd = qq~stat~; | ||||
| 	my $output = xCAT::Utils->runxcmd( | ||||
| 		{command => ["rpower"], | ||||
| 		 node    => [$node],  | ||||
| 		 arg     => [$cmd]}, | ||||
| 		$doreq, -1, 1); | ||||
|  | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 		my $rsp; | ||||
| 		push @{$rsp->{data}}, "rpower:"; | ||||
| 		push @{$rsp->{data}}, "$output"; | ||||
| 		xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 		return 1; | ||||
| 	}   else { | ||||
| 		if ($output !~ /: off/) { | ||||
| 			#power off the node | ||||
| 			my $cmd = qq~off~; | ||||
| 			my $output = xCAT::Utils->runxcmd( | ||||
| 				{command => ["rpower"], | ||||
| 				 node    => [$node],  | ||||
| 				 arg     => [$cmd]}, | ||||
| 				$doreq, -1, 1); | ||||
| 			if ($::RUNCMD_RC != 0) { | ||||
| 				my $rsp; | ||||
| 				push @{$rsp->{data}}, "rpower:"; | ||||
| 				push @{$rsp->{data}}, "$output"; | ||||
| 				xCAT::MsgUtils->message("E", $rsp, $callback); | ||||
| 				return 1; | ||||
| 			}		 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #------------------------------------------------------- | ||||
| =head3  add_postscript | ||||
|  | ||||
| 	It adds the 'config_ops_bm_node' postbootscript to the  | ||||
| 	postscript table for the given node. | ||||
|  | ||||
| =cut | ||||
| #------------------------------------------------------- | ||||
| sub  add_postscript { | ||||
|     my $callback=shift; | ||||
|     my $node=shift; | ||||
| 	my $script=shift; | ||||
| 	print "script=$script\n"; | ||||
|  | ||||
|     my $posttab=xCAT::Table->new("postscripts", -create =>1); | ||||
| 	my %setup_hash; | ||||
| 	my $ref = $posttab->getNodeAttribs($node,[qw(postscripts postbootscripts)]); | ||||
| 	my $found=0; | ||||
| 	if ($ref) { | ||||
| 		if (exists($ref->{postscripts})) { | ||||
| 		    my @a = split(/,/, $ref->{postscripts}); | ||||
| 		    if (grep(/^config_ops_bm_node/, @a)) { | ||||
| 				$found = 1; | ||||
| 				if (!grep(/^$script$/, @a)) { | ||||
| 					#not exact match, must replace it with the new script | ||||
| 					for (@a) { | ||||
| 						s/^config_ops_bm_node.*$/$script/; | ||||
| 					} | ||||
| 					my $new_post = join(',', @a); | ||||
| 					$setup_hash{$node}={postscripts=>"$new_post"}; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if (exists($ref->{postbootscripts})) { | ||||
| 		    my $post=$ref->{postbootscripts}; | ||||
| 		    my @old_a=split(',', $post); | ||||
| 		    if (grep(/^config_ops_bm_node/, @old_a)) { | ||||
| 				if (!grep(/^$script$/, @old_a)) { | ||||
| 					#not exact match, will replace it with new script | ||||
| 					for (@old_a) { | ||||
| 						s/^config_ops_bm_node.*$/$script/; | ||||
| 					} | ||||
| 					my $new_postboot = join(',', @old_a); | ||||
| 					$setup_hash{$node}={postbootscripts=>"$new_postboot"}; | ||||
| 				} | ||||
| 		    } else { | ||||
| 				if (! $found) { | ||||
| 					$setup_hash{$node}={postbootscripts=>"$post,$script"}; | ||||
| 				} | ||||
| 		    } | ||||
| 		} else { | ||||
|             if (! $found) { | ||||
| 				$setup_hash{$node}={postbootscripts=>"$script"}; | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		$setup_hash{$node}={postbootscripts=>"$script"}; | ||||
| 	} | ||||
|  | ||||
| 	if (keys(%setup_hash) > 0) { | ||||
| 	    $posttab->setNodesAttribs(\%setup_hash); | ||||
| 	} | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #------------------------------------------------------- | ||||
| =head3  remove_postscript | ||||
|  | ||||
| 	It removes the 'config_ops_bm_node' postbootscript from  | ||||
| 	the postscript table for the given node. | ||||
|  | ||||
| =cut | ||||
| #------------------------------------------------------- | ||||
| sub  remove_postscript { | ||||
|     my $callback=shift; | ||||
|     my $node=shift; | ||||
| 	my $script=shift; | ||||
|  | ||||
|     my $posttab=xCAT::Table->new("postscripts", -create =>1); | ||||
| 	my %setup_hash; | ||||
| 	my $ref = $posttab->getNodeAttribs($node,[qw(postscripts postbootscripts)]); | ||||
| 	my $found=0; | ||||
| 	if ($ref) { | ||||
| 		if (exists($ref->{postscripts})) { | ||||
| 		    my @old_a = split(/,/, $ref->{postscripts}); | ||||
| 		    my @new_a = grep(!/^$script/, @old_a); | ||||
| 			if (scalar(@new_a) != scalar(@old_a)) { | ||||
| 				my $new_post = join(',', @new_a); | ||||
| 				$setup_hash{$node}={postscripts=>"$new_post"}; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (exists($ref->{postbootscripts})) { | ||||
| 		    my @old_b = split(/,/, $ref->{postbootscripts}); | ||||
| 		    my @new_b = grep(!/^$script/, @old_b); | ||||
| 			if (scalar(@new_b) != scalar(@old_b)) { | ||||
| 				my $new_post = join(',', @new_b); | ||||
| 				$setup_hash{$node}={postbootscripts=>"$new_post"}; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	 | ||||
| 	if (keys(%setup_hash) > 0) { | ||||
| 	    $posttab->setNodesAttribs(\%setup_hash); | ||||
| 	} | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  opsaddbmnode_usage | ||||
| 	The usage text for opsaddbmnode command. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub opsaddbmnode_usage { | ||||
|     my $cb=shift; | ||||
|     my $rsp={}; | ||||
|  | ||||
|     $rsp->{data}->[0]= "Usage: opsaddbmnode -h"; | ||||
|     $rsp->{data}->[1]= "       opsaddbmnode -v"; | ||||
|     $rsp->{data}->[2]= "       opsaddbmnode <noderange> -s <service_host>"; | ||||
|     $cb->($rsp); | ||||
| } | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  opsaddimage_usage | ||||
| 	The usage text for opsaddimage command. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub opsaddimage_usage { | ||||
|     my $cb=shift; | ||||
|     my $rsp={}; | ||||
|  | ||||
|     $rsp->{data}->[0]= "Usage: opsaddimage -h"; | ||||
|     $rsp->{data}->[1]= "       opsaddimage -v"; | ||||
|     $rsp->{data}->[2]= "       opsaddimage <image1,image2...> [-n <new_name1,new_name2...> -c <controller>"; | ||||
|     $cb->($rsp); | ||||
| } | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3   deploy_ops_bm_node_usage | ||||
| 	The usage text for deploy_ops_bm_node command. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub deploy_ops_bm_node_usage { | ||||
|     my $cb=shift; | ||||
|     my $rsp={}; | ||||
|  | ||||
|     $rsp->{data}->[0]= "Usage: deploy_ops_bm_node -h"; | ||||
|     $rsp->{data}->[1]= "       deploy_ops_bm_node -v"; | ||||
|     $rsp->{data}->[2]= "       deploy_ops_bm_node <node> --image <image_name> --host <ops_hostname> --ip <ops_fixed_ip> --mask <netmask>"; | ||||
|     $cb->($rsp); | ||||
| } | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
|  | ||||
| =head3  cleanup_ops_bm_node_usage | ||||
| 	The usage text cleanup_ops_bm_node command. | ||||
| =cut | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| sub cleanup_ops_bm_node_usage { | ||||
|     my $cb=shift; | ||||
|     my $rsp={}; | ||||
|  | ||||
|     $rsp->{data}->[0]= "Usage: cleanup_ops_bm_node -h"; | ||||
|     $rsp->{data}->[1]= "       cleanup_ops_bm_node -v"; | ||||
|     $rsp->{data}->[2]= "       cleanup_ops_bm_node <node> [--ip <ops_fixed_ip>]"; | ||||
|     $cb->($rsp); | ||||
| } | ||||
|  | ||||
| 1; | ||||
| @@ -0,0 +1,17 @@ | ||||
| # Copyright (c) 2012 NTT DOCOMO, INC. | ||||
| # All Rights Reserved. | ||||
| # | ||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||
| #    not use this file except in compliance with the License. You may obtain | ||||
| #    a copy of the License at | ||||
| # | ||||
| #         http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| #    Unless required by applicable law or agreed to in writing, software | ||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
| #    License for the specific language governing permissions and limitations | ||||
| #    under the License. | ||||
| from xcat.openstack.baremetal import driver | ||||
|  | ||||
| BareMetalDriver = driver.xCATBareMetalDriver | ||||
| @@ -0,0 +1,255 @@ | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
| # coding=utf-8 | ||||
|  | ||||
| """ | ||||
| A driver for Bare-metal platform. | ||||
| """ | ||||
|  | ||||
| from oslo.config import cfg | ||||
|  | ||||
| from nova.compute import power_state | ||||
| from nova import context as nova_context | ||||
| from nova import exception | ||||
| from nova.openstack.common import excutils | ||||
| from nova.openstack.common.gettextutils import _ | ||||
| from nova.openstack.common import importutils | ||||
| from nova.openstack.common import jsonutils | ||||
| from nova.openstack.common import log as logging | ||||
| from nova.virt.baremetal import baremetal_states | ||||
| from nova.virt.baremetal import db | ||||
| from nova.virt.baremetal import driver as bm_driver | ||||
| from nova.virt.baremetal import utils as bm_utils | ||||
| from nova.virt import driver | ||||
| from nova.virt import firewall | ||||
| from nova.virt.libvirt import imagecache | ||||
| from xcat.openstack.baremetal import xcat_driver | ||||
| from xcat.openstack.baremetal import exception as xcat_exception | ||||
| from xcat.openstack.baremetal import power_states | ||||
|  | ||||
|  | ||||
| LOG = logging.getLogger(__name__) | ||||
| CONF = cfg.CONF | ||||
| CONF.import_opt('use_ipv6', 'nova.netconf') | ||||
|  | ||||
|  | ||||
| class xCATBareMetalDriver(bm_driver.BareMetalDriver): | ||||
|     """BareMetal hypervisor driver.""" | ||||
|  | ||||
|     def __init__(self, virtapi, read_only=False): | ||||
|         super(xCATBareMetalDriver, self).__init__(virtapi) | ||||
|         self.xcat = xcat_driver.xCAT() | ||||
|  | ||||
|     def _get_xCAT_image_name(self, image_meta): | ||||
|         prop = image_meta.get('properties') | ||||
|         xcat_image_name = prop.get('xcat_image_name') | ||||
|         if xcat_image_name: | ||||
|             return xcat_image_name | ||||
|         else: | ||||
|             raise xcat_exception.xCATInvalidImageError(image=image_meta.get('name')) | ||||
|  | ||||
|     def spawn(self, context, instance, image_meta, injected_files, | ||||
|               admin_password, network_info=None, block_device_info=None): | ||||
|         """ | ||||
|         Create a new instance/VM/domain on the virtualization platform. | ||||
|  | ||||
|         Once this successfully completes, the instance should be | ||||
|         running (power_state.RUNNING). | ||||
|  | ||||
|         If this fails, any partial instance should be completely | ||||
|         cleaned up, and the virtualization platform should be in the state | ||||
|         that it was before this call began. | ||||
|  | ||||
|         :param context: security context | ||||
|         :param instance: Instance object as returned by DB layer. | ||||
|                          This function should use the data there to guide | ||||
|                          the creation of the new instance. | ||||
|         :param image_meta: image object returned by nova.image.glance that | ||||
|                            defines the image from which to boot this instance | ||||
|         :param injected_files: User files to inject into instance. | ||||
|         :param admin_password: Administrator password to set in instance. | ||||
|         :param network_info: | ||||
|            :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` | ||||
|         :param block_device_info: Information about block devices to be | ||||
|                                   attached to the instance. | ||||
|         """ | ||||
|  	import pdb | ||||
| 	pdb.set_trace() | ||||
|         node_uuid = self._require_node(instance) | ||||
|         node = db.bm_node_associate_and_update(context, node_uuid, | ||||
|                     {'instance_uuid': instance['uuid'], | ||||
|                      'instance_name': instance['hostname'], | ||||
|                      'task_state': baremetal_states.BUILDING}) | ||||
|  | ||||
|         try: | ||||
|             self._plug_vifs(instance, network_info, context=context) | ||||
|             self._attach_block_devices(instance, block_device_info) | ||||
|             self._start_firewall(instance, network_info) | ||||
|  | ||||
|             macs = self.macs_for_instance(instance) | ||||
|             nodename = self.xcat.get_xcat_node_name(macs) | ||||
|             imagename = self._get_xCAT_image_name(image_meta) | ||||
|             hostname = instance.get('hostname') | ||||
|              | ||||
|             #get the network information for the new node | ||||
|             interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6) | ||||
|             if CONF.use_ipv6: | ||||
|                 fixed_ip = interfaces[0].get('address_v6') | ||||
|                 netmask = interfaces[0].get('netmask_v6') | ||||
|                 gateway = interfaces[0].get('gateway_v6') | ||||
|             else: | ||||
|                 fixed_ip = interfaces[0].get('address') | ||||
|                 netmask = interfaces[0].get('netmask') | ||||
|                 gateway = interfaces[0].get('gateway') | ||||
|             #convert netmask from IPAddress to unicode string | ||||
|             if netmask: | ||||
|                 netmask = unicode(netmask) | ||||
|  | ||||
|             #let xCAT install it | ||||
|             bm_driver._update_state(context, node, instance, baremetal_states.DEPLOYING) | ||||
|             self.xcat.deploy_node(nodename, imagename, hostname, fixed_ip, netmask, gateway) | ||||
|             bm_driver._update_state(context, node, instance, baremetal_states.ACTIVE) | ||||
|         except Exception as e:  | ||||
|             with excutils.save_and_reraise_exception(): | ||||
|                 LOG.error(_("Error occured while deploying instance %(instance)s " | ||||
|                             "on baremetal node %(node)s: %(error)s") % | ||||
|                           {'instance': instance['uuid'], | ||||
|                            'node': node['uuid'], | ||||
|                            'error':str(e)}) | ||||
|                 bm_driver._update_state(context, node, instance, baremetal_states.ERROR) | ||||
|  | ||||
|     def reboot(self, context, instance, network_info, reboot_type, | ||||
|                block_device_info=None, bad_volumes_callback=None): | ||||
|         """Reboot the specified instance. | ||||
|  | ||||
|         After this is called successfully, the instance's state | ||||
|         goes back to power_state.RUNNING. The virtualization | ||||
|         platform should ensure that the reboot action has completed | ||||
|         successfully even in cases in which the underlying domain/vm | ||||
|         is paused or halted/stopped. | ||||
|  | ||||
|         :param instance: Instance object as returned by DB layer. | ||||
|         :param network_info: | ||||
|            :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` | ||||
|         :param reboot_type: Either a HARD or SOFT reboot | ||||
|         :param block_device_info: Info pertaining to attached volumes | ||||
|         :param bad_volumes_callback: Function to handle any bad volumes | ||||
|             encountered | ||||
|         """ | ||||
|         try:  | ||||
|             node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid']) | ||||
|             macs = self.macs_for_instance(instance) | ||||
|             nodename = self.xcat.get_xcat_node_name(macs) | ||||
|             self.xcat.reboot_node(nodename) | ||||
|             bm_driver._update_state(context, node, instance, baremetal_states.RUNNING) | ||||
|         except xcat_exception.xCATCommandError as e:  | ||||
|             with excutils.save_and_reraise_exception(): | ||||
|                 LOG.error(_("Error occured while rebooting instance %(instance)s " | ||||
|                             "on baremetal node %(node)s: %(error)s") % | ||||
|                             {'instance': instance['uuid'], | ||||
|                              'node': node['uuid'], | ||||
|                              'error':str(e)}) | ||||
|                 bm_driver._update_state(context, node, instance, baremetal_states.ERROR) | ||||
|  | ||||
|     def destroy(self, context, instance, network_info, block_device_info=None, | ||||
|                 destroy_disks=True): | ||||
|         """Destroy (shutdown and delete) the specified instance. | ||||
|  | ||||
|         If the instance is not found (for example if networking failed), this | ||||
|         function should still succeed.  It's probably a good idea to log a | ||||
|         warning in that case. | ||||
|  | ||||
|         :param context: security context | ||||
|         :param instance: Instance object as returned by DB layer. | ||||
|         :param network_info: | ||||
|            :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` | ||||
|         :param block_device_info: Information about block devices that should | ||||
|                                   be detached from the instance. | ||||
|         :param destroy_disks: Indicates if disks should be destroyed | ||||
|         """ | ||||
|  	#import pdb | ||||
| 	#pdb.set_trace() | ||||
|         try: | ||||
|             node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid']) | ||||
|              | ||||
|         except exception.InstanceNotFound: | ||||
|             LOG.warning(_("Destroy function called on a non-existing instance %s") | ||||
|                         % instance['uuid']) | ||||
|             return | ||||
|  | ||||
|         try: | ||||
|             macs = self.macs_for_instance(instance) | ||||
|             nodename = self.xcat.get_xcat_node_name(macs) | ||||
|             interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6) | ||||
|             fixed_ip=None | ||||
|             if interfaces and interfaces[0]:  | ||||
|                 if CONF.use_ipv6: | ||||
|                     fixed_ip = interfaces[0].get('address_v6') | ||||
|                 else: | ||||
|                     fixed_ip = interfaces[0].get('address') | ||||
|             if fixed_ip: | ||||
|                 self.xcat.cleanup_node(nodename, fixed_ip) | ||||
|             else: | ||||
|                 self.xcat.cleanup_node(nodename) | ||||
|         except Exception as e: | ||||
|             #just log it and move on | ||||
|             LOG.warning(_("Destroy called with xCAT error:" + str(e))) | ||||
|  | ||||
|         try: | ||||
|             self._detach_block_devices(instance, block_device_info) | ||||
|             self._stop_firewall(instance, network_info) | ||||
|             self._unplug_vifs(instance, network_info) | ||||
|              | ||||
|             bm_driver._update_state(context, node, None, baremetal_states.DELETED) | ||||
|         except Exception as e: | ||||
|             with excutils.save_and_reraise_exception(): | ||||
|                 LOG.error(_("Error occurred while destroying instance %s: %s")  | ||||
|                           % (instance['uuid'], str(e))) | ||||
|                 bm_driver._update_state(context, node, instance, | ||||
|                                         baremetal_states.ERROR) | ||||
|  | ||||
|     def power_off(self, instance, node=None): | ||||
|         """Power off the specified instance.""" | ||||
|         macs = self.macs_for_instance(instance) | ||||
|         nodename = self.xcat.get_xcat_node_name(macs) | ||||
|         self.xcat.power_off_node(nodename) | ||||
|              | ||||
|  | ||||
|     def power_on(self, context, instance, network_info, block_device_info=None, | ||||
|                  node=None): | ||||
|         """Power on the specified instance.""" | ||||
|         macs = self.macs_for_instance(instance) | ||||
|         nodename = self.xcat.get_xcat_node_name(macs) | ||||
|         self.xcat.power_on_node(nodename) | ||||
|  | ||||
|  | ||||
|     def get_console_output(self, instance): | ||||
|         pass | ||||
|  | ||||
|     def get_info(self, instance): | ||||
|         """Get the current status of an instance, by name (not ID!) | ||||
|  | ||||
|         Returns a dict containing: | ||||
|         :state:           the running state, one of the power_state codes | ||||
|         :max_mem:         (int) the maximum memory in KBytes allowed | ||||
|         :mem:             (int) the memory in KBytes used by the domain | ||||
|         :num_cpu:         (int) the number of virtual CPUs for the domain | ||||
|         :cpu_time:        (int) the CPU time used in nanoseconds | ||||
|         """ | ||||
|  | ||||
|         node = bm_driver._get_baremetal_node_by_instance_uuid(instance['uuid']) | ||||
|         macs = self.macs_for_instance(instance) | ||||
|         nodename = self.xcat.get_xcat_node_name(macs) | ||||
|  | ||||
|         ps = self.xcat.get_node_power_state(nodename) | ||||
|         if ps == power_states.ON: | ||||
|             pstate = power_state.RUNNING | ||||
|         elif ps == power_states.OFF: | ||||
|             pstate = power_state.SHUTDOWN | ||||
|         else: | ||||
|             pstate = power_state.NOSTATE | ||||
|  | ||||
|         return {'state': pstate, | ||||
|                 'max_mem': node['memory_mb'], | ||||
|                 'mem': node['memory_mb'], | ||||
|                 'num_cpu': node['cpus'], | ||||
|                 'cpu_time': 0} | ||||
| @@ -0,0 +1,41 @@ | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
|  | ||||
| """xCAT baremtal exceptions. | ||||
| """ | ||||
|  | ||||
| import functools | ||||
| import sys | ||||
|  | ||||
| from oslo.config import cfg | ||||
| import webob.exc | ||||
|  | ||||
| from nova.openstack.common import excutils | ||||
| from nova.openstack.common.gettextutils import _ | ||||
| from nova.openstack.common import log as logging | ||||
| from nova import safe_utils | ||||
| from nova import exception as nova_exception | ||||
|  | ||||
| LOG = logging.getLogger(__name__) | ||||
|  | ||||
| class xCATException(Exception): | ||||
|     errmsg = _("xCAT general exception") | ||||
|  | ||||
|     def __init__(self, errmsg=None, **kwargs): | ||||
|         if not errmsg: | ||||
|             errmsg = self.errmsg | ||||
|             errmsg = errmsg % kwargs | ||||
|  | ||||
|         super(xCATException, self).__init__(errmsg) | ||||
|  | ||||
| class xCATCommandError(xCATException): | ||||
|     errmsg =  _("Error returned when calling xCAT command %(cmd)s" | ||||
|                 " for node %(node)s:%(error)s") | ||||
|  | ||||
| class xCATInvalidImageError(xCATException): | ||||
|     errmsg = _("The image %(image)s is not an xCAT image") | ||||
|  | ||||
| class xCATDeploymentFailure(xCATException):     | ||||
|     errmsg = _("xCAT node deployment failed for node %(node)s:%(error)s") | ||||
|  | ||||
| class xCATRebootFailure(xCATException):     | ||||
|     errmsg = _("xCAT node rebooting failed for node %(node)s:%(error)s") | ||||
| @@ -0,0 +1,9 @@ | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
|  | ||||
| """ | ||||
| Possible xCAT node power states. | ||||
| """ | ||||
|  | ||||
| OFF = 'off' | ||||
| ON = 'on' | ||||
| ERROR = 'error' | ||||
| @@ -0,0 +1,257 @@ | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
| # coding=utf-8 | ||||
|  | ||||
|  | ||||
| """ | ||||
| Baremetal xCAT power manager. | ||||
| """ | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import stat | ||||
| from oslo.config import cfg | ||||
| import datetime | ||||
|  | ||||
| from nova import context as nova_context | ||||
| from nova.virt.baremetal import baremetal_states | ||||
| from nova.openstack.common.gettextutils import _ | ||||
| from nova.openstack.common import log as logging | ||||
| from nova.openstack.common import loopingcall | ||||
| from nova.openstack.common import timeutils | ||||
| from nova import paths | ||||
| from nova import utils | ||||
| from xcat.openstack.baremetal import exception | ||||
| from xcat.openstack.baremetal import power_states | ||||
|  | ||||
| LOG = logging.getLogger(__name__) | ||||
|  | ||||
| # register configuration options | ||||
| xcat_opts = [ | ||||
|     cfg.IntOpt('deploy_timeout', | ||||
|                 help='Timeout for node deployment. Default: 0 second (unlimited)', | ||||
|                 default=0), | ||||
|     cfg.IntOpt('reboot_timeout', | ||||
|                 help='Timeout for rebooting a node. Default: 0 second (unlimited)', | ||||
|                 default=0),     | ||||
|     cfg.IntOpt('deploy_checking_interval', | ||||
|                 help='Checking interval for node deployment. Default: 10 seconds', | ||||
|                 default=10), | ||||
|     cfg.IntOpt('reboot_checking_interval', | ||||
|                 help='Checking interval for rebooting a node. Default: 5 seconds', | ||||
|                 default=5),     | ||||
|    ] | ||||
| xcat_group = cfg.OptGroup(name='xcat', | ||||
|                           title='xCAT Options') | ||||
| CONF = cfg.CONF | ||||
| CONF.register_group(xcat_group) | ||||
| CONF.register_opts(xcat_opts, xcat_group) | ||||
|  | ||||
|  | ||||
| class xCAT(object): | ||||
|     """A driver that calls xCAT funcions""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         #setup the path for xCAT commands | ||||
|         #xcatroot = os.getenv('XCATROOT', '/opt/xcat/') | ||||
|         #sys.path.append("%s/bin" % xcatroot) | ||||
|         #sys.path.append("%s/sbin" % xcatroot) | ||||
|         pass | ||||
|  | ||||
|     def _exec_xcat_command(self, command): | ||||
|         """Calls xCAT command.""" | ||||
|         args = command.split(" ") | ||||
|         out, err = utils.execute(*args, run_as_root=True) | ||||
|         LOG.debug(_("xCAT command stdout: '%(out)s', stderr: '%(err)s'"), | ||||
|                   {'out': out, 'err': err}) | ||||
|         return out, err | ||||
|  | ||||
|     def get_xcat_node_name(self, macs): | ||||
|         """Get the xcat node name given mac addressed. | ||||
|  | ||||
|         It uses the mac address to search for the node name in xCAT. | ||||
|         """ | ||||
|         for mac in macs: | ||||
|             out, err = self._exec_xcat_command("lsdef -w mac=%s" % mac) | ||||
|             if out: | ||||
|                 return out.split(" ")[0] | ||||
|          | ||||
|         errstr='No node found in xCAT with the following mac address: ' \ | ||||
|             + ','.join(macs) | ||||
|         LOG.warning(errstr) | ||||
|         raise exception.xCATCommandError(errstr) | ||||
|  | ||||
|          | ||||
|     def deploy_node(self, nodename, imagename, hostname, fixed_ip, netmask, gateway): | ||||
|         """ | ||||
|         Install the node. | ||||
|  | ||||
|         It calls xCAT command deploy_ops_bmnode which prepares the node | ||||
|         by adding the config_ops_bm_node postbootscript to the postscript | ||||
|         table for the node, then call nodeset and then boot the node up. | ||||
|         """ | ||||
|         out, err = self._exec_xcat_command( | ||||
|             "deploy_ops_bm_node %(node)s --image %(image)s" | ||||
|             " --host %(host)s --ip %(ip)s --mask %(mask)s"  | ||||
|             % {'node': nodename, | ||||
|                'image': imagename, | ||||
|                'host': hostname, | ||||
|                'ip': fixed_ip, | ||||
|                'mask': netmask, | ||||
|             }) | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT deploy_ops_bm_node" | ||||
|                        " command for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|         self._wait_for_node_deploy(nodename) | ||||
|  | ||||
|     def cleanup_node(self, nodename, fixed_ip=None): | ||||
|         """ | ||||
|         Undo all the changes made to the node by deploy_node function. | ||||
|  | ||||
|         It calls xCAT command cleanup_ops_bm_node which removes the | ||||
|         config_ops_bm_node postbootscript from the postscript table | ||||
|         for the node, removes the alias ip and then power the node off. | ||||
|         """ | ||||
|         cmd = "cleanup_ops_bm_node %s" % nodename | ||||
|         if fixed_ip: | ||||
|             cmd += " --ip %s" % fixed_ip | ||||
|         out, err = self._exec_xcat_command(cmd) | ||||
|  | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT cleanup_ops_bm_node" | ||||
|                        " command for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|  | ||||
|     def power_on_node(self, nodename): | ||||
|         """Power on the node.""" | ||||
|         state = self.get_node_power_state(nodename) | ||||
|         if state ==  power_states.ON: | ||||
|             LOG.warning(_("Powring on node called, but the node %s " | ||||
|                           "is already on") % nodename) | ||||
|         out, err = self._exec_xcat_command("rpower %s on" % nodename) | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT rpower on" | ||||
|                     " for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|         else: | ||||
|             return power_states.ON | ||||
|      | ||||
|     def power_off_node(self, nodename): | ||||
|         """Power off the node.""" | ||||
|         state = self.get_node_power_state(nodename) | ||||
|         if state ==  power_states.OFF: | ||||
|             LOG.warning(_("Powring off node called, but the node %s " | ||||
|                           "is already off") % nodename) | ||||
|         out, err = self._exec_xcat_command("rpower %s off" % nodename) | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT rpower off" | ||||
|                     " for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|         else: | ||||
|             return power_states.OFF | ||||
|  | ||||
|     def reboot_node(self, nodename): | ||||
|         """Reboot the node.""" | ||||
|         out, err = self._exec_xcat_command("rpower %s boot" % nodename) | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT rpower boot" | ||||
|                     " for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|          | ||||
|         self._wait_for_node_reboot(nodename) | ||||
|         return power_states.ON | ||||
|          | ||||
|  | ||||
|     def get_node_power_state(self, nodename): | ||||
|         out, err = self._exec_xcat_command("rpower %s stat" % nodename) | ||||
|         if err: | ||||
|             errstr = _("Error returned when calling xCAT rpower stat" | ||||
|                     " for node %s:%s") % (nodename, err) | ||||
|             LOG.warning(errstr) | ||||
|             raise exception.xCATCommandError(errstr) | ||||
|         else: | ||||
|             state = out.split(":")[1] | ||||
|             if state: | ||||
|                 state = state.strip() | ||||
|                 if state == 'on': | ||||
|                     return power_states.ON | ||||
|                 elif state == 'off': | ||||
|                     return power_states.OFF | ||||
|              | ||||
|             return power_states.ERROR | ||||
|              | ||||
|     def _wait_for_node_deploy(self, nodename): | ||||
|         """Wait for xCAT node deployment to complete.""" | ||||
|         locals = {'errstr':''} | ||||
|  | ||||
|         def _wait_for_deploy(): | ||||
|             out,err = self._exec_xcat_command("nodels %s nodelist.status" % nodename) | ||||
|             if err: | ||||
|                 locals['errstr'] = _("Error returned when quering node status" | ||||
|                            " for node %s:%s") % (nodename, err) | ||||
|                 LOG.warning(locals['errstr']) | ||||
|                 raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|             if out: | ||||
|                 node,status = out.split(": ") | ||||
|                 if status == "booted": | ||||
|                     LOG.info(_("Deployment for node %s completed.") | ||||
|                              % nodename) | ||||
|                     raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|             if (CONF.xcat.deploy_timeout and | ||||
|                     timeutils.utcnow() > expiration): | ||||
|                 locals['errstr'] = _("Timeout while waiting for" | ||||
|                            " deployment of node %s.") % nodename | ||||
|                 LOG.warning(locals['errstr']) | ||||
|                 raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|         expiration = timeutils.utcnow() + datetime.timedelta( | ||||
|                 seconds=CONF.xcat.deploy_timeout) | ||||
|         timer = loopingcall.FixedIntervalLoopingCall(_wait_for_deploy) | ||||
|         # default check every 10 seconds | ||||
|         timer.start(interval=CONF.xcat.deploy_checking_interval).wait() | ||||
|  | ||||
|         if locals['errstr']: | ||||
|             raise exception.xCATDeploymentFailure(locals['errstr']) | ||||
|  | ||||
|  | ||||
|     def _wait_for_node_reboot(self, nodename): | ||||
|         """Wait for xCAT node boot to complete.""" | ||||
|         locals = {'errstr':''} | ||||
|  | ||||
|         def _wait_for_reboot(): | ||||
|             out,err = self._exec_xcat_command("nodels %s nodelist.status" % nodename) | ||||
|             if err: | ||||
|                 locals['errstr'] = _("Error returned when quering node status" | ||||
|                            " for node %s:%s") % (nodename, err) | ||||
|                 LOG.warning(locals['errstr']) | ||||
|                 raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|             if out: | ||||
|                 node,status = out.split(": ") | ||||
|                 if status == "booted": | ||||
|                     LOG.info(_("Rebooting node %s completed.") | ||||
|                              % nodename) | ||||
|                     raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|             if (CONF.xcat.reboot_timeout and | ||||
|                     timeutils.utcnow() > expiration): | ||||
|                 locals['errstr'] = _("Timeout while waiting for" | ||||
|                            " rebooting node %s.") % nodename | ||||
|                 LOG.warning(locals['errstr']) | ||||
|                 raise loopingcall.LoopingCallDone() | ||||
|  | ||||
|         expiration = timeutils.utcnow() + datetime.timedelta( | ||||
|                 seconds=CONF.xcat.reboot_timeout) | ||||
|         timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot) | ||||
|         # default check every 5 seconds | ||||
|         timer.start(interval=CONF.xcat.reboot_checking_interval).wait() | ||||
|  | ||||
|         if locals['errstr']: | ||||
|             raise exception.xCATRebootFailure(locals['errstr']) | ||||
							
								
								
									
										75
									
								
								xCAT-OpenStack-baremetal/pods/man1/opsaddbmnode.1.pod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								xCAT-OpenStack-baremetal/pods/man1/opsaddbmnode.1.pod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| =head1 NAME | ||||
|  | ||||
| B<opsaddbmnode> - It adds xCAT baremetal nodes to an OpenStack cloud. | ||||
|  | ||||
| =head1 SYNOPSIS | ||||
|  | ||||
| B<opsaddbmnode> I<noderange> B<-s> I<service_host> | ||||
|  | ||||
| B<opsaddbmnode> [B<-h>|B<--help>] | ||||
|  | ||||
| B<opsaddbmnode> [B<-v>|B<--version>] | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| The B<opsaddbmnode> command registers xCAT nodes to an OpenStack cloud.  | ||||
|  | ||||
| An OpenStack nova baremetal node registration command takes several node attributes: | ||||
| =item BMC ip addresss, user id and password | ||||
| =item Name of nova compute host which will control this baremetal node | ||||
| =item Number of CPUs in the node | ||||
| =item Memory in the node (MB) | ||||
| =item Local hard disk in the node (GB) | ||||
| =item MAC address to provision the node | ||||
|  | ||||
| The opsaddbmnode command pulls the above baremetal node information from xCAT tables and calls "nova baremetal-node-create" to register the baremetal node with the OpenStack cloud. | ||||
|  | ||||
| Please make sure the following xCAT tables are filled with correct information for the given nodes before calling this command. | ||||
| =item ipmi (for BMC ip addresss, user id and password) | ||||
| =item mac (for MAC address) | ||||
| =item hwinv (for CPU, memory and disk info.) | ||||
|  | ||||
| =head1 Parameters | ||||
|  | ||||
| I<noderage> is a comma separated node or node group names.  | ||||
|  | ||||
|  | ||||
| =head1 OPTIONS | ||||
|  | ||||
| =over 10 | ||||
|  | ||||
| =item B<-s>    The node name of the OpenStack compute host that hosts the baremetal nodes.  | ||||
|  | ||||
| =item B<-h|--help>     Display usage message. | ||||
|  | ||||
| =item B<-v|--version>  The Command Version. | ||||
|  | ||||
| =back | ||||
|  | ||||
| =head1 RETURN VALUE | ||||
|  | ||||
| 0  The command completed successfully. | ||||
|  | ||||
| 1  An error has occurred. | ||||
|  | ||||
| =head1 EXAMPLES | ||||
|  | ||||
| =over 3 | ||||
|  | ||||
| =item 1. | ||||
|  | ||||
| To register node1, node2 and node3 to OpenStack, sv1 is the compute host. | ||||
|  | ||||
|    opsassbmnode node1,node2,node3 -s sv1 | ||||
|  | ||||
|  | ||||
| =back | ||||
|  | ||||
| =head1 FILES | ||||
|  | ||||
| /opt/xcat/bin/opadddbmnode | ||||
|  | ||||
| =head1 SEE ALSO | ||||
|  | ||||
| L<opsaddimage(1)|opsaddimage.1> | ||||
|  | ||||
							
								
								
									
										65
									
								
								xCAT-OpenStack-baremetal/pods/man1/opsaddimage.1.pod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								xCAT-OpenStack-baremetal/pods/man1/opsaddimage.1.pod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| =head1 NAME | ||||
|  | ||||
| B<opsaddimage> - It adds or removes nodes for the vlan. | ||||
|  | ||||
| =head1 SYNOPSIS | ||||
|  | ||||
| B<opsaddimage> I<image1,image2,...> B<-n> I<new_name1,new_name2,...> [B<-c> I<controller>]   | ||||
|  | ||||
| B<opsaddimage> [B<-h>|B<--help>] | ||||
|  | ||||
| B<opsaddimage> [B<-v>|B<--version>] | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| The B<opsaddimage> command adds a list of xCAT images into the OpenStack cloud.  | ||||
|  | ||||
| Under the cover, it creates a fake imgae and registers the fake image into OpenStack with command B<glance image-create>. It sets the property in the image to indicate that this is an xCAT image and also stores the original xCAT image name in the property for further reference.  | ||||
|  | ||||
| The xCAT image names can be listed using B<lsdef -t osimage> command.       | ||||
|  | ||||
| =head1 Parameters | ||||
|  | ||||
| I<image1,image1...> a comma separated xCAT images names.  | ||||
|  | ||||
|  | ||||
| =head1 OPTIONS | ||||
|  | ||||
| =over 10 | ||||
|  | ||||
| =item B<-n>    a comma separated new image names in the OpenStack. If omitted, the default is the original xCAT image nanme.   | ||||
|  | ||||
| =item B<-c>  the node name of the OpenStack controller. This node must be an xCAT managed node. | ||||
|  | ||||
| =item B<-h|--help>     Display usage message. | ||||
|  | ||||
| =item B<-v|--version>  The Command Version. | ||||
|  | ||||
| =back | ||||
|  | ||||
| =head1 RETURN VALUE | ||||
|  | ||||
| 0  The command completed successfully. | ||||
|  | ||||
| 1  An error has occurred. | ||||
|  | ||||
| =head1 EXAMPLES | ||||
|  | ||||
| =over 3 | ||||
|  | ||||
| =item 1. | ||||
|  | ||||
| To register xCAT image rhels6.3-x86_64-install-compute into OpenStack. | ||||
|  | ||||
|   opsaddimage rhels6.3-x86_64-install-compute -c sv2 | ||||
|  | ||||
| =back | ||||
|  | ||||
| =head1 FILES | ||||
|  | ||||
| /opt/xcat/bin/opsaddimage | ||||
|  | ||||
| =head1 SEE ALSO | ||||
|  | ||||
| L<opsaddbmnode(1)|opsaddbmnode.1> | ||||
|  | ||||
							
								
								
									
										187
									
								
								xCAT-OpenStack-baremetal/share/xcat/openstack/postscripts/config_ops_bm_node
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										187
									
								
								xCAT-OpenStack-baremetal/share/xcat/openstack/postscripts/config_ops_bm_node
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| #!/bin/sh  | ||||
| # IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
|  | ||||
| # xCAT post script for configuring the openstack baremetal node. | ||||
| # The format is: | ||||
| # config_ops_bm_node ops_hostname ops_ip ops_netmask | ||||
|  | ||||
|  | ||||
| get_os_type() | ||||
| { | ||||
|     #get os type | ||||
| 	str_os_type=`uname | tr 'A-Z' 'a-z'` | ||||
| 	str_temp='' | ||||
| 	if [ "$str_os_type" = "linux" ];then | ||||
| 		str_temp=`echo $OSVER | grep -E '(sles|suse)'` | ||||
| 		if [ -f "/etc/debian_version" ];then | ||||
| 			str_os_type="debian" | ||||
| 		elif [ -f "/etc/SuSE-release" -o -n "$str_temp" ];then | ||||
| 			str_os_type="sles" | ||||
| 		else | ||||
| 			str_os_type="redhat" | ||||
| 		fi | ||||
| 	else | ||||
| 		str_os_type="aix" | ||||
| 	fi | ||||
| 	echo "$str_os_type" | ||||
| } | ||||
|  | ||||
|  | ||||
| setup_ip() | ||||
| { | ||||
| 	str_os_type=$1 | ||||
|     str_if_name=$2 | ||||
|     str_v4ip=$3 | ||||
|     str_v4mask=$4 | ||||
|  | ||||
| 	ret=`ifconfig $str_if_name |grep "inet addr" 2>&1` | ||||
|     if [ $? -eq 0 ]; then | ||||
| 		old_ip=`echo $ret|cut -d':' -f2 |cut -d' ' -f1` | ||||
| 		old_mask=`echo $ret|cut -d':' -f4` | ||||
| 		#echo "old ip = $old_ip, old mask=$old_mask" | ||||
| 		if [ "$old_ip" == "$str_v4ip" -a "$old_mask" == "$str_v4mask" ]; then | ||||
| 			#if nic is up and the address is the same, then donothing | ||||
| 			#echo "do nothing" | ||||
| 			exit 0 | ||||
| 		else | ||||
| 			#bring down the nic and reconstruct it. | ||||
| 			#echo "bring down the old nic" | ||||
| 			ifconfig $str_if_name del $old_ip | ||||
|         fi | ||||
| 	fi | ||||
|  | ||||
|     if [ "$str_os_type" = "sles" ];then | ||||
|         str_conf_file="/etc/sysconfig/network/ifcfg-${str_if_name}" | ||||
| 		if [ -f $str_conf_file ]; then | ||||
| 			rm $str_conf_file | ||||
| 		fi | ||||
|         echo "DEVICE=${str_if_name}" > $str_conf_file | ||||
|         echo "BOOTPROTO=static" >> $str_conf_file | ||||
|         echo "IPADDR=${str_v4ip}" >> $str_conf_file | ||||
|         echo "NETMASK=${str_v4mask}" >> $str_conf_file | ||||
| 		echo "NETWORK=''" >> $str_conf_file | ||||
|         echo "STARTMODE=onboot" >> $str_conf_file | ||||
|         echo "USERCONTROL=no" >> $str_conf_file | ||||
| 		ifup $str_if_name | ||||
|     #debian ubuntu | ||||
|     elif [ "$str_os_type" = "debian" ];then | ||||
|         str_conf_file="/etc/network/interfaces.d/${str_if_name}" | ||||
| 		if [ -f $str_conf_file ]; then | ||||
| 			rm $str_conf_file | ||||
| 		fi | ||||
|         echo "auto ${str_if_name}" > $str_conf_file | ||||
|         echo "iface ${str_if_name} inet static" >> $str_conf_file | ||||
|         echo "  address ${str_v4ip}" >> $str_conf_file | ||||
|         echo "  netmask ${str_v4mask}" >> $str_conf_file | ||||
| 		ifconfig $str_if_name up | ||||
|     else | ||||
|         # Write the info to the ifcfg file for redhat | ||||
|         str_conf_file="/etc/sysconfig/network-scripts/ifcfg-${str_if_name}" | ||||
| 		if [ -f $str_conf_file ]; then | ||||
| 			rm $str_conf_file | ||||
| 		fi | ||||
|         echo "DEVICE=${str_if_name}" > $str_conf_file | ||||
|         echo "BOOTPROTO=static" >> $str_conf_file | ||||
|         echo "NM_CONTROLLED=no" >> $str_conf_file | ||||
|         echo "IPADDR=${str_v4ip}" >> $str_conf_file | ||||
|         echo "NETMASK=${str_v4mask}" >> $str_conf_file | ||||
|         echo "ONBOOT=yes" >> $str_conf_file | ||||
| 		ifup $str_if_name | ||||
|     fi | ||||
| } | ||||
|  | ||||
|  | ||||
| str_os_type=$(get_os_type) | ||||
| echo "os_type=$str_os_type" | ||||
| if [ "$str_os_type" = "aix" ]; then | ||||
| 	logger -t xcat "config_ops_bm_node dose not support AIX." | ||||
| 	echo "config_ops_bm_node dose not support AIX." | ||||
| 	exit 0 | ||||
| fi | ||||
|  | ||||
| #change the hostname | ||||
| if [[ -n "$1" ]]; then | ||||
|     hostname $1 | ||||
| fi | ||||
|  | ||||
| #Add the openstack ip to the node | ||||
| if [[ -n $2 ]]; then | ||||
|     ops_ip=$2 | ||||
| 	 | ||||
| 	if [[ -z $3 ]]; then | ||||
|         logger -t xcat "config_ops_bm_node: Please specify the netmask." | ||||
| 		echo "config_ops_bm_node: Please specify the netmask." | ||||
| 		exit 1 | ||||
| 	else | ||||
| 		ops_mask=$3 | ||||
| 	fi | ||||
|  | ||||
| 	#figure out the install nic | ||||
|     if [[ -n $MACADDRESS ]]; then | ||||
|         pos=0 | ||||
|         #mac has the following format: 01:02:03:04:05:0E!node5|01:02:03:05:0F!node6-eth1 | ||||
|         for x in `echo "$MACADDRESS" | tr "|" "\n"` | ||||
|         do | ||||
|             node="" | ||||
|             mac="" | ||||
|             pos=$((pos+1)) | ||||
|             i=`expr index $x !` | ||||
|             if [[ $i -gt 0 ]]; then | ||||
|                 node=`echo ${x##*!}` | ||||
|                 mac_tmp=`echo ${x%%!*}` | ||||
|             else | ||||
|                 mac_tmp=$x | ||||
|             fi | ||||
| 			 | ||||
|             if [[ $pos -eq 1 ]]; then | ||||
|                 mac1=$mac_tmp | ||||
|             fi | ||||
| 			 | ||||
|             if [[ "$PRIMARYNIC" = "$mac_tmp" ]]; then | ||||
|                 mac=$mac_tmp | ||||
|                 break | ||||
|             fi | ||||
|  | ||||
|             if [[ -z "$PRIMARYNIC" ]] || [[ "$PRIMARYNIC" = "mac" ]]; then | ||||
|                 if [[ -z $node ]] || [[ "$node" = "$NODE" ]]; then | ||||
|                     mac=$mac_tmp | ||||
|                     break | ||||
|                 fi | ||||
|             fi | ||||
|         done | ||||
|  | ||||
|         if [[ -z $mac ]]; then | ||||
|             if [[ -z "$PRIMARYNIC" ]] || [[ "$PRIMARYNIC" = "mac" ]]; then | ||||
|                 mac=$mac1 #if nothing mathes, take the first mac | ||||
|             else | ||||
|                 nic=$PRIMARYNIC #or the primary nic itself is the nic | ||||
|             fi | ||||
|         fi | ||||
|     else | ||||
|         logger -t xcat "config_ops_bm_node: no mac addresses are defined in the mac table for the node $NODE" | ||||
|         echo "config_ops_bm_node: no mac addresses are defined in the mac table for the node $NODE" | ||||
|         index=$((index+1)) | ||||
|         continue | ||||
|     fi | ||||
| 	echo "mac=$mac" | ||||
|      | ||||
|     #find the nic that has the mac | ||||
|     if [[ -z $nic ]];  then | ||||
|         #go to each nic to match the mac address | ||||
|         ret=`ifconfig |grep -i $mac 2>&1`; | ||||
|         if [ $? -eq 0 ]; then | ||||
|            nic=`echo $ret |head -n1|cut -d' ' -f 1` | ||||
|         else | ||||
|             logger -t xcat "config_ops_bm_node: The mac address for the network for $NODE is not defined." | ||||
|             echo "config_ops_bm_node: The mac address for the network for $NODE is not defined." | ||||
|         fi | ||||
|     fi | ||||
|     echo "nic=$nic" | ||||
|  | ||||
| 	#now setup the ip alias | ||||
|     setup_ip $str_os_type $nic:0 $ops_ip $ops_mask  | ||||
| fi | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,65 @@ | ||||
| #!/bin/sh  | ||||
| # IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
|  | ||||
| # xCAT post script for deconfiguring the openstack baremetal node. | ||||
| # The format is: | ||||
| # deconfig_ops_bm_node ops_ip | ||||
|  | ||||
|  | ||||
| get_os_type() | ||||
| { | ||||
|     #get os type | ||||
| 	str_os_type=`uname | tr 'A-Z' 'a-z'` | ||||
| 	str_temp='' | ||||
| 	if [ "$str_os_type" = "linux" ];then | ||||
| 		str_temp=`echo $OSVER | grep -E '(sles|suse)'` | ||||
| 		if [ -f "/etc/debian_version" ];then | ||||
| 			str_os_type="debian" | ||||
| 		elif [ -f "/etc/SuSE-release" -o -n "$str_temp" ];then | ||||
| 			str_os_type="sles" | ||||
| 		else | ||||
| 			str_os_type="redhat" | ||||
| 		fi | ||||
| 	else | ||||
| 		str_os_type="aix" | ||||
| 	fi | ||||
| 	echo "$str_os_type" | ||||
| } | ||||
|  | ||||
|  | ||||
| str_os_type=$(get_os_type) | ||||
| echo "os_type=$str_os_type" | ||||
|  | ||||
| if [ $str_os_type == "aix" ]; then | ||||
| 	logger -t xcat "deconfig_ops_bm_node dose not support AIX." | ||||
| 	echo "deconfig_ops_bm_node dose not support AIX." | ||||
| 	exit 0 | ||||
| fi | ||||
|  | ||||
| #change the hostname | ||||
| hostname $NODE | ||||
|  | ||||
| #remove the openstack ip from the node | ||||
| if [[ -n $1 ]]; then | ||||
|     ops_ip=$1 | ||||
| 	nic=$(ip addr | grep $ops_ip | awk '{print $NF}')	 | ||||
| 	echo "nic=$nic, ops_ip=$ops_ip" | ||||
|  | ||||
| 	ifconfig $nic del $ops_ip | ||||
|   | ||||
| 	#delete the configuration file | ||||
|     if [ "$str_os_type" = "sles" ]; then | ||||
|         str_conf_file="/etc/sysconfig/network/ifcfg-$nic" | ||||
|     elif [ "$str_os_type" = "debian" ]; then  #debian ubuntu | ||||
|         str_conf_file="/etc/network/interfaces.d/$nic" | ||||
|     else #redhat | ||||
|         str_conf_file="/etc/sysconfig/network-scripts/ifcfg-$nic" | ||||
|     fi | ||||
| 	if [ -f $str_conf_file ]; then | ||||
| 		rm $str_conf_file | ||||
| 	fi | ||||
| fi | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										103
									
								
								xCAT-OpenStack-baremetal/xCAT-OpenStack-baremetal.spec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								xCAT-OpenStack-baremetal/xCAT-OpenStack-baremetal.spec
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| Summary: Executables and data of the xCAT baremetal driver for OpenStack | ||||
| Name: xCAT-OpenStack-baremetal | ||||
| Version: %(cat Version) | ||||
| Release: snap%(date +"%Y%m%d%H%M") | ||||
| Epoch: 4 | ||||
| License: IBM | ||||
| Group: Applications/System | ||||
| Source: xCAT-OpenStack-baremetal-%{version}.tar.gz | ||||
| Packager: IBM Corp. | ||||
| Vendor: IBM Corp. | ||||
| Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} | ||||
| Prefix: /opt/xcat | ||||
| BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root | ||||
|  | ||||
| %ifos linux | ||||
| BuildArch: noarch | ||||
| %endif | ||||
|  | ||||
|  | ||||
| Provides: xCAT-OpenStack-baremetal = %{epoch}:%{version} | ||||
|  | ||||
| Requires: xCAT-client | ||||
|  | ||||
| %description | ||||
| xCAT-OpenStack-baremetal provides the baremetal driver for OpenStack. | ||||
|  | ||||
| %prep | ||||
| %setup -q -n xCAT-OpenStack-baremetal | ||||
| %build | ||||
|  | ||||
| # Convert pods to man pages and html pages | ||||
| ./xpod2man | ||||
|  | ||||
| %install | ||||
| # The install phase puts all of the files in the paths they should be in when the rpm is | ||||
| # installed on a system.  The RPM_BUILD_ROOT is a simulated root file system and usually | ||||
| # has a value like: /var/tmp/xCAT-OpenStack-baremetal-2.0-snap200802270932-root | ||||
| rm -rf $RPM_BUILD_ROOT | ||||
|  | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/bin | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/sbin | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/lib/perl/xCAT_plugin | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/lib/python/xcat/openstack/baremetal | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/xcat/openstack/postscripts | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/man/man1 | ||||
| mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/man1 | ||||
|  | ||||
|  | ||||
| set +x | ||||
|  | ||||
| cp -R lib/* $RPM_BUILD_ROOT/%{prefix}/lib | ||||
| cp share/xcat/openstack/postscripts/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/openstack/postscripts | ||||
|  | ||||
|  | ||||
| # These were built dynamically in the build phase | ||||
| cp share/man/man1/* $RPM_BUILD_ROOT/%{prefix}/share/man/man1 | ||||
| chmod 444 $RPM_BUILD_ROOT/%{prefix}/share/man/man1/* | ||||
|  | ||||
| # These were built dynamically during the build phase | ||||
| cp share/doc/man1/* $RPM_BUILD_ROOT/%{prefix}/share/doc/man1 | ||||
| chmod 644 $RPM_BUILD_ROOT/%{prefix}/share/doc/man1/* | ||||
|  | ||||
| # These links get made in the RPM_BUILD_ROOT/prefix area | ||||
| ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/sbin/deploy_ops_bm_node | ||||
| ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/sbin/cleanup_ops_bm_node | ||||
| ln -sf ../bin/xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/opsaddbmnode | ||||
| ln -sf ../bin/xcatclientnnr $RPM_BUILD_ROOT/%{prefix}/bin/opsaddimage | ||||
|  | ||||
| set -x | ||||
|  | ||||
|  | ||||
| %clean | ||||
| # This step does not happen until *after* the %files packaging below | ||||
| rm -rf $RPM_BUILD_ROOT | ||||
|  | ||||
| %files | ||||
| %defattr(-,root,root) | ||||
| #%doc LICENSE.html | ||||
| # Just package everything that has been copied into RPM_BUILD_ROOT | ||||
| %{prefix} | ||||
|  | ||||
|  | ||||
| %changelog | ||||
|  | ||||
| %post | ||||
| #copy the postscripts under /installl/postscripts directory on MN only | ||||
| if [ -f "/etc/xCATMN" ]; then | ||||
| 	cp $RPM_INSTALL_PREFIX0/share/xcat/openstack/postscripts/* /install/postscripts | ||||
| fi | ||||
| exit 0 | ||||
|  | ||||
| %preun | ||||
| #remove postscripts under /installl/postscripts directory on MN only | ||||
| if [ -f "/etc/xCATMN" ]; then | ||||
| 	for fn in $RPM_INSTALL_PREFIX0/share/xcat/openstack/postscripts/* | ||||
| 	do | ||||
| 		bn=`basename $fn` | ||||
| 		rm /install/postscripts/$bn | ||||
| 	done | ||||
| fi | ||||
| exit 0 | ||||
|  | ||||
|  | ||||
							
								
								
									
										213
									
								
								xCAT-OpenStack-baremetal/xpod2man
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										213
									
								
								xCAT-OpenStack-baremetal/xpod2man
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,213 @@ | ||||
| #!/usr/bin/perl | ||||
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
|  | ||||
| # First builds the xCAT summary man page from Synopsis of each man page. | ||||
| # Then converts all of the pod man pages into html (including links to each other) | ||||
|  | ||||
| # We assume that this script is run in the xCAT-vlan-2.0 dir, so everything is | ||||
| # done relative to that. | ||||
|  | ||||
| use strict; | ||||
| #use lib '.'; | ||||
| use Pod::Man; | ||||
| use Pod::Html; | ||||
|  | ||||
| my $poddir = 'pods'; | ||||
| my $mandir = 'share/man'; | ||||
| my $htmldir = 'share/doc'; | ||||
| my $cachedir = '/tmp'; | ||||
|  | ||||
| my @pods = getPodList($poddir); | ||||
| #foreach (@pods) { print "$_\n"; } exit; | ||||
|  | ||||
| # Build the cmd overview page. | ||||
| writesummarypage("$poddir/man1/xcat.1.pod", @pods); | ||||
|  | ||||
| # Build the man page for each pod. | ||||
| #mkdir($mandir) or die "Error: could not create $mandir.\n"; | ||||
| print "Converting PODs to man pages...\n"; | ||||
| foreach my $podfile (@pods) { | ||||
|     my $manfile = $podfile; | ||||
|     $manfile =~ s/^$poddir/$mandir/;      # change the beginning of the path | ||||
|     $manfile =~ s/\.pod$//;			# change the ending | ||||
|     my $mdir = $manfile; | ||||
|     $mdir =~ s|/[^/]*$||;			# get rid of the basename part | ||||
| 	if (system("mkdir -p $mdir")) { die "Error: could not create $mdir.\n"; } | ||||
| 	my ($section) = $podfile =~ /\.(\d+)\.pod$/; | ||||
|     convertpod2man($podfile, $manfile, $section); | ||||
| } | ||||
|  | ||||
| my @dummyPods = createDummyPods($poddir, \@pods); | ||||
|  | ||||
| # Build the html page for each pod. | ||||
| #mkdir($htmldir) or die "Error: could not create $htmldir.\n"; | ||||
| print "Converting PODs to HTML pages...\n"; | ||||
| # have to clear the cache, because old entries can cause a problem | ||||
| unlink("$cachedir/pod2htmd.tmp", "$cachedir/pod2htmi.tmp"); | ||||
| foreach my $podfile (@pods) { | ||||
|     my $htmlfile = $podfile; | ||||
|     $htmlfile =~ s/^$poddir/$htmldir/;      # change the beginning of the path | ||||
|     $htmlfile =~ s/\.pod$/\.html/;			# change the ending | ||||
|     my $hdir = $htmlfile; | ||||
|     $hdir =~ s|/[^/]*$||;			# get rid of the basename part | ||||
| 	if (system("mkdir -p $hdir")) { die "Error: could not create $hdir.\n"; } | ||||
|     #print "$podfile, $htmlfile, $poddir, $htmldir\n"; | ||||
|     convertpod2html($podfile, $htmlfile, $poddir, $htmldir); | ||||
| } | ||||
|  | ||||
| # Remove the dummy pods | ||||
| unlink @dummyPods; | ||||
| rmdir "$poddir/man7"; | ||||
|  | ||||
| exit; | ||||
|  | ||||
|  | ||||
| # To enable linking between the cmd man pages and the db man pages, need to: | ||||
| #	grep thru the cmd pods searching for references (L<>) to any section 5 man page | ||||
| #	if that pod does not exist, create an empty one that will satisfy pod2html | ||||
| #	keep track of all dummy pods created, so they can be removed later | ||||
| sub createDummyPods { | ||||
| 	my ($poddir, $pods) = @_; | ||||
| 	my $cmd = "grep -r -E 'L<.+\\([57]\\)\\|.+\\.[57]>' " . $poddir; | ||||
| 	#print "Running cmd: ", $cmd, "\n"; | ||||
| 	my @lines = `$cmd`; | ||||
| 	if ($?) { print "Error running:  $cmd\n"; print join('', @lines); } | ||||
| 	#my @lines; | ||||
| 	#system($cmd); | ||||
| 	my @dummyPods; | ||||
| 	foreach my $l (@lines) { | ||||
| 		#print "$l\n"; | ||||
| 		my @matches = $l =~ /L<([^\(]+)\(([57])\)\|\1\.[57]>/g;		# get all the matches in the line | ||||
| 		# The above line should create the array with every other entry being the man page name | ||||
| 		# and every other entry is the section # (5 or 7) | ||||
| 		my $cmd; | ||||
| 		while ($cmd=shift @matches) { | ||||
| 			#foreach my $m (@matches) { | ||||
| 			my $section = shift @matches; | ||||
| 			my $filename = "$poddir/man$section/$cmd.$section.pod"; | ||||
| 			#print "$filename\n"; | ||||
| 			if (!(grep /^$filename$/, @$pods) && !(grep /^$filename$/, @dummyPods)) { push @dummyPods, $filename; } | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	# Create these empty files | ||||
| 	print "Creating empty linked-to files: ", join(', ', @dummyPods), "\n"; | ||||
| 	mkdir "$poddir/man7"; | ||||
| 	foreach my $d (@dummyPods) { | ||||
| 		if (!open(TMP, ">>$d")) { warn "Could not create dummy pod file $d ($!)\n"; } | ||||
| 		else { close TMP; } | ||||
| 	} | ||||
| 	 | ||||
| 	return @dummyPods; | ||||
| } | ||||
|  | ||||
| # Recursively get the list of pod man page files. | ||||
| sub getPodList { | ||||
| 	my $poddir = shift; | ||||
| 	my @files; | ||||
|  | ||||
| 	# 1st get toplevel dir listing | ||||
| 	opendir(DIR, $poddir) or die "Error: could not read $poddir.\n"; | ||||
| 	my @topdir = grep !/^\./, readdir(DIR);		# / | ||||
| 	close(DIR); | ||||
|  | ||||
| 	# Now go thru each subdir (these are man1, man3, etc.) | ||||
| 	foreach my $mandir (@topdir) { | ||||
| 		opendir(DIR, "$poddir/$mandir") or die "Error: could not read $poddir/$mandir.\n"; | ||||
| 		my @dir = grep !/^\./, readdir(DIR);		# / | ||||
| 		close(DIR); | ||||
| 		foreach my $file (@dir) { | ||||
| 			push @files, "$poddir/$mandir/$file"; | ||||
| 		} | ||||
| 	} | ||||
| 	return sort @files; | ||||
| } | ||||
|  | ||||
|  | ||||
| # Create the xcat man page that gives a summary description of each xcat cmd. | ||||
| sub writesummarypage { | ||||
| 	my $file = shift;       # relative path file name of the man page | ||||
| 	# the rest of @_ contains the pod files that describe each cmd | ||||
|  | ||||
| 	open(FILE, ">$file") or die "Error: could not open $file for writing.\n"; | ||||
|  | ||||
| 	print FILE <<'EOS1'; | ||||
| =head1 NAME | ||||
|  | ||||
| B<xcat> - extreme Cluster Administration Tool. | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| Extreme Cluster Administration Toolkit (xCAT). xCAT is a scalable distributed computing management | ||||
| and provisioning tool that provides a unified interface for hardware control, discovery, and | ||||
| OS diskful/diskfree deployment. | ||||
|  | ||||
|  | ||||
| =head1 XCAT DATABASE | ||||
|  | ||||
| All of the cluster configuration information is in the xCAT database.  See L<xcatdb(5)|xcatdb.5> for | ||||
| descriptions of every table in the database. | ||||
|  | ||||
| =head1 XCAT COMMANDS | ||||
|  | ||||
| What follows is a short description of each xCAT command.  To get more information about a particular | ||||
| command, see its man page.  Note that the commands are listed in alphabetical order B<within each section>, | ||||
| i.e. all the commands in section 1, then the commands in section 3, etc. | ||||
|  | ||||
| =over 12 | ||||
| EOS1 | ||||
|  | ||||
| # extract the summary for each cmd from its man page | ||||
| foreach my $manpage (@_) { | ||||
| 	my ($sectionnum) = $manpage =~ /\.(\d+)\.pod$/; | ||||
| 	# Suck in the whole file, then we will parse it. | ||||
| 	open(MANPAGE, "$manpage") or die "Error: could not open $manpage for reading.\n"; | ||||
| 	my @contents = <MANPAGE>; | ||||
| 	my $wholemanpage = join('', @contents); | ||||
| 	close(MANPAGE); | ||||
| 	# This regex matches: optional space, =head1, space, title, space, cmd, space, description, newline | ||||
| 	my ($cmd, $description) = $wholemanpage =~ /^\s*=head1\s+\S+\s+(\S+)\s+(.+?)\n/si; | ||||
| 	if (!defined($cmd)) { print "Warning: $manpage is not in a recognized structure.  It will be ignored.\n"; next; } | ||||
| 	if (!defined($description)) { print "Warning: $manpage does not have a description for $cmd.  It will be ignored.\n"; next; } | ||||
| 	$cmd =~ s/^.<(.+)>$/$1/;		# if the cmd name has pod formatting around it, strip it off | ||||
| 	$description =~ s/^-\s*//;		# if the description has a leading hypen, strip it off | ||||
| 	print FILE "\n=item L<$cmd($sectionnum)|$cmd.$sectionnum>\n\n".$description."\n"; | ||||
| } | ||||
|  | ||||
| # Artificially add the xcattest cmd, because the xCAT-test rpm will add this | ||||
| print FILE "\n=item L<xcattest(1)|xcattest.1>\n\nRun automated xCAT test cases.\n"; | ||||
|  | ||||
| 	print FILE <<"EOS3"; | ||||
|  | ||||
| =back | ||||
| EOS3 | ||||
|  | ||||
| 	close FILE; | ||||
| } | ||||
|  | ||||
|  | ||||
| # Create the html page for one pod. | ||||
| sub convertpod2html { | ||||
| 	my ($podfile, $htmlfile, $poddir, $htmldir) = @_; | ||||
|  | ||||
| 	#TODO: use --css=<stylesheet> and --title=<pagetitle> to make the pages look better | ||||
| 	pod2html($podfile, | ||||
| 			"--outfile=$htmlfile", | ||||
| 			"--podpath=man1", | ||||
| 			"--podroot=$poddir", | ||||
| 			"--htmldir=$htmldir", | ||||
| 			"--recurse", | ||||
| 			"--cachedir=$cachedir", | ||||
| 			); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| # Create the man page for one pod. | ||||
| sub convertpod2man { | ||||
| 	my ($podfile, $manfile, $section) = @_; | ||||
|  | ||||
| 	my $parser = Pod::Man->new(section => $section); | ||||
|     $parser->parse_from_file($podfile, $manfile); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user