mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-28 18:05:31 +00:00 
			
		
		
		
	adds the imgexport/imgimport command to extract/pull in images to xCAT. This initial release just has export, hope to get import done by next week
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@5659 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
		
							
								
								
									
										415
									
								
								xCAT-server/lib/xcat/plugins/imgport.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								xCAT-server/lib/xcat/plugins/imgport.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,415 @@ | ||||
| # Sumavi Inc (C) 2010 | ||||
| ##################################################### | ||||
| # imgport will export and import xCAT stateless, statelite, and diskful templates. | ||||
| # This will make it so that you can easily share your images with others. | ||||
| # All your images are belong to us! | ||||
| package xCAT_plugin::imgport; | ||||
| use strict; | ||||
| use warnings; | ||||
| use xCAT::Table; | ||||
| use xCAT::Schema; | ||||
| use Data::Dumper; | ||||
| use XML::Simple; | ||||
| use xCAT::NodeRange qw/noderange abbreviate_noderange/; | ||||
| use xCAT::Utils; | ||||
| use POSIX qw/strftime/; | ||||
| use Getopt::Long; | ||||
| use File::Temp; | ||||
| use File::Copy; | ||||
| use File::Path; | ||||
| use Cwd; | ||||
| my $requestcommand; | ||||
|  | ||||
| 1; | ||||
|  | ||||
| #some quick aliases to table/value | ||||
| my %shortnames = ( | ||||
|                   groups => [qw(nodelist groups)], | ||||
|                   tags   => [qw(nodelist groups)], | ||||
|                   mgt    => [qw(nodehm mgt)], | ||||
|                   #switch => [qw(switch switch)], | ||||
|                   ); | ||||
|  | ||||
| ##################################################### | ||||
| # Return list of commands handled by this plugin | ||||
| ##################################################### | ||||
| sub handled_commands | ||||
| { | ||||
| 	return { | ||||
| 		imgexport	=> "imgport", | ||||
| 		imgimport	=> "imgport", | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| ##################################################### | ||||
| # Process the command | ||||
| ##################################################### | ||||
| sub process_request | ||||
| { | ||||
| 	#use Getopt::Long; | ||||
| 	Getopt::Long::Configure("bundling"); | ||||
| 	#Getopt::Long::Configure("pass_through"); | ||||
| 	Getopt::Long::Configure("no_pass_through"); | ||||
|  | ||||
| 	my $request  = shift; | ||||
| 	my $callback = shift; | ||||
| 	$requestcommand = shift; | ||||
| 	my $command  = $request->{command}->[0]; | ||||
| 	my $args     = $request->{arg}; | ||||
|  | ||||
| 	if ($command eq "imgexport"){ | ||||
| 		return xexport($request, $callback); | ||||
| 	}elsif ($command eq "imgimport"){ | ||||
| 		return ximport($request, $callback); | ||||
| 	}else{ | ||||
| 		print "Error: $command not found in export\n"; | ||||
| 		$callback->({error=>["Error: $command not found in this module."],errorcode=>[1]}); | ||||
| 		#return (1, "$command not found in sumavinode"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| # return sthe mount point to the requesting node. | ||||
| sub xexport {  | ||||
| 	my $request = shift; | ||||
| 	my $callback = shift; | ||||
| 	my %rsp;	# response | ||||
| 	my $help; | ||||
| 	my @extra; | ||||
|  | ||||
| 	my $xusage = sub { | ||||
| 		my $ec = shift; | ||||
| 		push@{ $rsp{data} }, "imgexport: Creates a tarball of an existing xCAT image"; | ||||
| 		push@{ $rsp{data} }, "Usage: imgexport <image name> [directory] [[--extra <file:dir> ] ... ]"; | ||||
| 		if($ec){ $rsp{errorcode} = $ec; } | ||||
| 		$callback->(\%rsp); | ||||
| 	}; | ||||
| 	unless(defined($request->{arg})){ $xusage->(1); return; } | ||||
| 	@ARGV = @{ $request->{arg}}; | ||||
| 	if($#ARGV eq -1){ | ||||
| 		$xusage->(1); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	GetOptions( | ||||
| 		'h|?|help' => \$help, | ||||
| 		'extra=s' => \@extra | ||||
| 	); | ||||
|  | ||||
| 	if($help){ | ||||
| 		$xusage->(0); | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	# ok, we're done with all that.  Now lets actually start doing some work. | ||||
| 	my $img_name = shift @ARGV;	 | ||||
| 	my $dest = shift @ARGV; | ||||
|  | ||||
| 	$callback->( {data => ["Exporting $img_name..."]}); | ||||
| 	# check if all files are in place | ||||
| 	my $attrs = get_image_info($img_name, $callback, @extra); | ||||
| 	#print Dumper($attrs); | ||||
|  | ||||
| 	unless($attrs){ | ||||
| 		return 1; | ||||
| 	}	 | ||||
|  | ||||
| 	# make manifest and tar it up. | ||||
| 	make_bundle($img_name, $dest, $attrs, $callback); | ||||
| 	 | ||||
| } | ||||
|  | ||||
| # verify the image and return the values | ||||
| sub get_image_info { | ||||
| 	my $imagename = shift; | ||||
| 	my $callback = shift; | ||||
| 	my @extra = @_; | ||||
| 	my $errors = 0; | ||||
| 	 | ||||
| 	my $ostab = new xCAT::Table('osimage', -create=>1); | ||||
| 	unless($ostab){ | ||||
| 		$callback->( | ||||
| 			{error => ["Unable to open table 'osimage' What's going on here dude?"],errorcode=>1} | ||||
| 		); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	 | ||||
| 	(my $attrs) = $ostab->getAttribs({imagename => $imagename}, 'profile', 'provmethod', 'osvers', 'osarch' ); | ||||
| 	if (!$attrs) { | ||||
| 		$callback->({error=>["Cannot find image \'$imagename\' from the osimage table."],errorcode=>[1]}); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	unless($attrs->{provmethod}){ | ||||
| 		$callback->({error=>["The 'provmethod' field is not set for \'$imagename\' in the osimage table."],errorcode=>[1]}); | ||||
| 		$errors++; | ||||
| 	} | ||||
|  | ||||
| 	unless($attrs->{profile}){ | ||||
| 		$callback->({error=>["The 'profile' field is not set for \'$imagename\' in the osimage table."],errorcode=>[1]}); | ||||
| 		$errors++; | ||||
| 	} | ||||
|  | ||||
| 	unless($attrs->{osvers}){ | ||||
| 		$callback->({error=>["The 'osvers' field is not set for \'$imagename\' in the osimage table."],errorcode=>[1]}); | ||||
| 		$errors++; | ||||
| 	} | ||||
|  | ||||
| 	unless($attrs->{osarch}){ | ||||
| 		$callback->({error=>["The 'osarch' field is not set for \'$imagename\' in the osimage table."],errorcode=>[1]}); | ||||
| 		$errors++; | ||||
| 	} | ||||
|  | ||||
| 	unless($attrs->{provmethod} =~ /install|netboot|statelite/){ | ||||
| 		$callback->({error=>["Exporting images with 'provemethod' " . $attrs->{provmethod} . " is not supported. Hint: install, netboot, or statelite"],errorcode=>[1]}); | ||||
| 		$errors++; | ||||
| 	} | ||||
|  | ||||
| 	if($errors){ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	$attrs = get_files($imagename, $callback, $attrs); | ||||
| 	if($#extra > -1){ | ||||
| 		my $ex = get_extra($callback, @extra); | ||||
| 		if($ex){  | ||||
| 			$attrs->{extra} = $ex; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	# if we get nothing back, then we couldn't find the files.  How sad, return nuthin' | ||||
| 	return $attrs;	 | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| # returns a hash of files | ||||
| # extra { | ||||
| #	  file => dir | ||||
| #   file => dir | ||||
| # } | ||||
|  | ||||
| sub get_extra { | ||||
| 	my $callback = shift; | ||||
| 	my @extra = @_; | ||||
| 	my $extra; | ||||
|  | ||||
| 	# make sure that the extra is formatted correctly: | ||||
| 	foreach my $e (@extra){ | ||||
| 		unless($e =~ /:/){ | ||||
| 			$callback->({error=>["Extra argument $e is not formatted correctly and will be ignored.  Hint: </my/file/dir/file:to_install_dir>"],errorcode=>[1]}); | ||||
| 			next; | ||||
| 		} | ||||
| 		my ($file , $to_dir) = split(/:/, $e); | ||||
| 		unless( -r $file){ | ||||
| 			$callback->({error=>["Can not find Extra file $file.  Argument will be ignored"],errorcode=>[1]}); | ||||
| 			next; | ||||
| 		} | ||||
| 		#print "$file => $to_dir"; | ||||
| 		push @{ $extra}, { 'src' => $file, 'dest' => $to_dir }; | ||||
| 	}	 | ||||
| 	return $extra; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| # well we check to make sure the files exist and then we return them. | ||||
| sub get_files{ | ||||
| 	my $imagename = shift; | ||||
| 	my $errors = 0; | ||||
| 	my $callback = shift; | ||||
| 	my $attrs = shift;  # we'll hopefully get a reference to it and modify this variable. | ||||
| 	my @arr;	 # array of directory search paths | ||||
| 	my $template = ''; | ||||
|  | ||||
| 	# todo is XCATROOT not going to be /opt/xcat/  in normal situations?  We'll always | ||||
| 	# assume it is for now | ||||
| 	my $xcatroot = "/opt/xcat"; | ||||
|  | ||||
| 	# get the install root | ||||
| 	my $installroot = xCAT::Utils->getInstallDir(); | ||||
| 	unless($installroot){ | ||||
| 		$installroot = '/install'; | ||||
| 	} | ||||
|  | ||||
| 	my $provmethod = $attrs->{provmethod}; | ||||
|  | ||||
| 	# here's the case for the install.  All we need at first is the  | ||||
| 	# template.  That should do it. | ||||
| 	if($provmethod =~ /install/){ | ||||
| 		# we need to get the template for this one! | ||||
| 		@arr = ("$installroot/custom/install", "$xcatroot/share/xcat/install"); | ||||
| 		my $template = look_for_file('tmpl', $attrs, @arr); | ||||
| 		unless($template){ | ||||
| 			$callback->({error=>["Couldn't find install template for $imagename"],errorcode=>[1]}); | ||||
| 			$errors++; | ||||
| 		}else{ | ||||
| 			$callback->( {data => ["$template"]}); | ||||
| 			$attrs->{template} = $template; | ||||
| 		} | ||||
| 		$attrs->{media} = "required"; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	# for stateless I need to save the  | ||||
| 	# ramdisk | ||||
| 	# the kernel | ||||
| 	# the rootimg.gz | ||||
| 	if($provmethod =~ /netboot/){ | ||||
| 		@arr = ("$installroot/netboot"); | ||||
|  | ||||
| 		# look for ramdisk | ||||
| 		my $ramdisk = look_for_file('initrd.gz', $attrs, @arr); | ||||
| 		unless($ramdisk){ | ||||
| 			$callback->({error=>["Couldn't find ramdisk (initrd.gz) for  $imagename"],errorcode=>[1]}); | ||||
| 			$errors++; | ||||
| 		}else{ | ||||
| 			$callback->( {data => ["$ramdisk"]}); | ||||
| 			$attrs->{ramdisk} = $ramdisk; | ||||
| 		} | ||||
|  | ||||
| 		# look for kernel | ||||
| 		my $kernel = look_for_file('kernel', $attrs, @arr); | ||||
| 		unless($kernel){ | ||||
| 			$callback->({error=>["Couldn't find kernel (kernel) for  $imagename"],errorcode=>[1]}); | ||||
| 			$errors++; | ||||
| 		}else{ | ||||
| 			$callback->( {data => ["$kernel"]}); | ||||
| 			$attrs->{kernel} = $kernel; | ||||
| 		} | ||||
|  | ||||
| 		# look for rootimg.gz | ||||
| 		my $rootimg = look_for_file('rootimg.gz', $attrs, @arr); | ||||
| 		unless($rootimg){ | ||||
| 			$callback->({error=>["Couldn't find rootimg (rootimg.gz) for  $imagename"],errorcode=>[1]}); | ||||
| 			$errors++; | ||||
| 		}else{ | ||||
| 			$callback->( {data => ["$rootimg"]}); | ||||
| 			$attrs->{rootimg} = $rootimg; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 		 | ||||
|  | ||||
| 	if($errors){ | ||||
| 		$attrs = 0; | ||||
| 	} | ||||
| 	return $attrs; | ||||
| } | ||||
|  | ||||
|  | ||||
| # argument: | ||||
| # type of file:  This is usually the suffix of the file, or the file name. | ||||
| # attributes:  These are the paramaters you got from the osimage table in a hash. | ||||
| # @dirs:  Some search paths where we'll start looking for them. | ||||
| # then we just return a string of the full path to where the file is. | ||||
| # mostly because we just ooze awesomeness. | ||||
| sub look_for_file { | ||||
| 	my $file = shift; | ||||
| 	my $attrs = shift; | ||||
| 	my @dirs = @_; | ||||
| 	my $r_file = ''; | ||||
| 	 | ||||
| 	my $profile = $attrs->{profile}; | ||||
| 	my $arch = $attrs->{osarch}; | ||||
| 	my $distname = $attrs->{osvers}; | ||||
| 	my $dd = $distname; # dd is distro directory, or disco dave, whichever you prefer. | ||||
|  | ||||
|  | ||||
| 	# go through the directories and look for the file.  We hopefully will find it... | ||||
| 	foreach my $d (@dirs){ | ||||
| 			# widdle down rhel5.4, rhel5., rhel5, rhel, rhe, rh, r,  | ||||
| 			until(-r "$d/$dd" or not $dd){ | ||||
| 				chop($dd);	 | ||||
| 			} | ||||
| 			if($distname && $file eq 'tmpl'){		 | ||||
| 				# now look for the file name: foo.rhel5.x86_64.tmpl | ||||
| 				(-r "$d/$dd/$profile.$distname.$arch.tmpl") && (return "$d/$dd/$profile.$distname.$arch.tmpl"); | ||||
| 				 | ||||
| 				# now look for the file name: foo.rhel5.tmpl | ||||
| 				(-r "$d/$dd/$profile.$distname.tmpl") && (return "$d/$dd/$profile.$distname.tmpl"); | ||||
|  | ||||
| 				# now look for the file name: foo.x86_64.tmpl | ||||
| 				(-r "$d/$dd/$profile.$arch.tmpl") && (return "$d/$dd/$profile.$arch.tmpl"); | ||||
|  | ||||
| 				# finally, look for the file name: foo.tmpl | ||||
| 				(-r "$d/$dd/$profile.tmpl") && (return "$d/$dd/$profile.tmpl"); | ||||
| 			}else{ | ||||
| 				# this may find the ramdisk: /install/netboot/ | ||||
| 				(-r "$d/$dd/$arch/$profile/$file") && (return "$d/$dd/$arch/$profile/$file"); | ||||
| 			} | ||||
| 	} | ||||
|  | ||||
| 	# I got nothing man.  Can't find it.  Sorry 'bout that. | ||||
| 	# returning nothing: | ||||
| 	return ''; | ||||
| } | ||||
|  | ||||
|  | ||||
| # here's where we make the tarball | ||||
| sub make_bundle { | ||||
| 	my $imagename = shift; | ||||
| 	my $dest = shift; | ||||
| 	my $attribs = shift; | ||||
| 	my $callback = shift; | ||||
|  | ||||
| 	# tar ball is made in local working directory.  Sometimes doing this in /tmp  | ||||
| 	# is bad.  In the case of my development machine, the / filesystem was nearly full. | ||||
| 	# so doing it in cwd is easy and predictable. | ||||
| 	my $dir = getcwd; | ||||
| 	my $ttpath = mkdtemp("$dir/export.$$.XXXXXX"); | ||||
| 	my $tpath = "$ttpath/$imagename"; | ||||
| 	mkdir("$tpath"); | ||||
| 	chmod 0755,$tpath; | ||||
|  | ||||
| 	# make manifest.xml file.  So easy!  This is why we like XML.  I didn't like | ||||
| 	# the idea at first though. | ||||
| 	my $xml = new XML::Simple(RootName =>'xcatimage');	 | ||||
| 	open(FILE,">$tpath/manifest.xml") or die "Could not open $tpath/manifest.xml"; | ||||
| 	print FILE  $xml->XMLout($attribs, noattr => 1, xmldecl => '<?xml version="1.0">'); | ||||
| 	#print $xml->XMLout($attribs, noattr => 1, xmldecl => '<?xml version="1.0">'); | ||||
| 	close(FILE); | ||||
|  | ||||
|  | ||||
| 	# these are the only files we copy in.  (unless you have extras) | ||||
| 	for my $a ("kernel", "template", "ramdisk", "rootimg"){ | ||||
| 		if($attribs->{$a}){ | ||||
| 			copy($attribs->{$a}, $tpath); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	# extra files get copied in the extra directory. | ||||
| 	if($attribs->{extra}){ | ||||
| 		mkdir("$tpath/extra"); | ||||
| 		chmod 0755,"$tpath/extra"; | ||||
| 		foreach(@{ $attribs->{extra} }){ | ||||
| 			copy($_->{src}, "$tpath/extra"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	# now get right below all this stuff and tar it up. | ||||
| 	chdir($ttpath); | ||||
| 	unless($dest){  | ||||
| 		$dest = "$dir/$imagename.tgz"; | ||||
| 	} | ||||
|  | ||||
| 	# if no absolute path specified put it in the cwd | ||||
| 	unless($dest =~ /^\//){ | ||||
| 		$dest = "$dir/$dest";			 | ||||
| 	} | ||||
|  | ||||
| 	$callback->( {data => ["Compressing $imagename bundle.  Please be patient."]}); | ||||
| 	my $rc = system("tar czvf $dest . ");	 | ||||
| 	if($rc) { | ||||
| 		$callback->({error=>["Failed to compress archive!  (Maybe there was no space left?)"],errorcode=>[1]}); | ||||
| 		return; | ||||
| 	} | ||||
| 	chdir($dir);	 | ||||
| 	$rc = system("rm -rf $ttpath"); | ||||
| 	if ($rc) { | ||||
| 		$callback->({error=>["Failed to clean up temp space $ttpath"],errorcode=>[1]}); | ||||
| 		return; | ||||
| 	}	 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user