mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 15:18:19 +00:00
9bfdadeaac
imgimport looked up the litefile entries for all images with "image" = 'ALL'. MySQL takes a double-quoted name for a string, so the lookup found nothing, and imgimport added entries that an ALL entry already covered. The lookup now quotes the column the way the database needs.
2233 lines
77 KiB
Perl
2233 lines
77 KiB
Perl
# Sumavi Inc (C) 2010
|
|
|
|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
#####################################################
|
|
# 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;
|
|
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
use strict;
|
|
use warnings;
|
|
|
|
#use xCAT::Table;
|
|
#use xCAT::Schema;
|
|
#use xCAT::NodeRange qw/noderange abbreviate_noderange/;
|
|
use xCAT::Utils;
|
|
use xCAT::TableUtils;
|
|
use Data::Dumper;
|
|
use XML::Simple;
|
|
use POSIX qw/strftime/;
|
|
use Getopt::Long;
|
|
use File::Temp;
|
|
use File::Copy;
|
|
use File::Path qw/mkpath/;
|
|
use File::Path qw/rmtree/;
|
|
use File::Basename;
|
|
use xCAT::NodeRange;
|
|
use xCAT::Schema;
|
|
use xCAT::SvrUtils;
|
|
use Cwd;
|
|
my $requestcommand;
|
|
$::VERBOSE = 0;
|
|
my $hasplugin = 0;
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
# extract the bundle, then add it to the osimage table. Basically the ying of the yang of the xexport
|
|
# function.
|
|
sub ximport {
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my %rsp; # response
|
|
my $help;
|
|
my $nodes;
|
|
my $new_profile;
|
|
my $remoteHost;
|
|
my $nozip = 0;
|
|
|
|
my $xusage = sub {
|
|
my $ec = shift;
|
|
push @{ $rsp{data} }, "imgimport: Takes in an xCAT image bundle and defines it to xCAT so you can use it";
|
|
push @{ $rsp{data} }, "Usage: ";
|
|
push @{ $rsp{data} }, "\timgimport [-h|--help]";
|
|
push @{ $rsp{data} }, "\timgimport <bundle_file_name> [-p|--postscripts <nodelist>] [-f|--profile <new_profile>] [-R|--remotehost <userid\@host>] [-n|--nozip] [-v]";
|
|
if ($ec) { $rsp{errorcode} = $ec; }
|
|
$callback->(\%rsp);
|
|
};
|
|
unless (defined($request->{arg})) { $xusage->(1); return; }
|
|
@ARGV = @{ $request->{arg} };
|
|
if ($#ARGV == -1) {
|
|
$xusage->(1);
|
|
return;
|
|
}
|
|
|
|
GetOptions(
|
|
'h|?|help' => \$help,
|
|
'v|verbose' => \$::VERBOSE,
|
|
'R|remotehost=s' => \$remoteHost,
|
|
'p|postscripts=s' => \$nodes,
|
|
'f|profile=s' => \$new_profile,
|
|
'n|nozip' => \$nozip
|
|
);
|
|
|
|
if ($help) {
|
|
$xusage->(0);
|
|
return;
|
|
}
|
|
|
|
if ($#ARGV == -1) {
|
|
# if no arguments left after processing the options, then bundle name is missing
|
|
$xusage->(1);
|
|
} else {
|
|
# first extract the bundle
|
|
my $bundle = shift @ARGV;
|
|
extract_bundle($request, $callback, $bundle, $nodes, $new_profile, $remoteHost, $nozip );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
# function to export your image. The image should already be in production, work well, and have
|
|
# no bugs. Lots of places will have problems because the image may not be in osimage table
|
|
# or they may have hardcoded things, or have post install scripts.
|
|
sub xexport {
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my %rsp; # response
|
|
my $help;
|
|
my @extra;
|
|
my $node;
|
|
my $remoteHost;
|
|
|
|
my $xusage = sub {
|
|
my $ec = shift;
|
|
push @{ $rsp{data} }, "imgexport: Creates a tarball (bundle) of an existing xCAT image";
|
|
push @{ $rsp{data} }, "Usage: ";
|
|
push @{ $rsp{data} }, "\timgexport [-h|--help]";
|
|
push @{ $rsp{data} }, "\timgexport <image_name> [destination] [[-e|--extra <file:dir> ] ... ] [-p|--postscripts <node_name>] [-R|--remotehost <userid\@host>] [-v]";
|
|
if ($ec) { $rsp{errorcode} = $ec; }
|
|
$callback->(\%rsp);
|
|
};
|
|
unless (defined($request->{arg})) { $xusage->(1); return; }
|
|
@ARGV = @{ $request->{arg} };
|
|
if ($#ARGV == -1) {
|
|
$xusage->(1);
|
|
return;
|
|
}
|
|
|
|
GetOptions(
|
|
'h|?|help' => \$help,
|
|
'p|postscripts=s' => \$node,
|
|
'e|extra=s' => \@extra,
|
|
'R|remotehost=s' => \$remoteHost,
|
|
'v|verbose' => \$::VERBOSE
|
|
);
|
|
|
|
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;
|
|
my $cwd = $request->{cwd}; #getcwd;
|
|
$cwd = $cwd->[0];
|
|
|
|
if (!defined $remoteHost) {
|
|
$callback->({ data => ["Exporting $img_name to $cwd..."] });
|
|
}
|
|
else {
|
|
$callback->({ data => ["Exporting $img_name to $remoteHost..."] });
|
|
}
|
|
|
|
|
|
# check if all files are in place
|
|
my $attrs = get_image_info($img_name, $callback, $node, @extra);
|
|
|
|
#print Dumper($attrs);
|
|
|
|
unless ($attrs) {
|
|
return 1;
|
|
}
|
|
|
|
# make manifest and tar it up.
|
|
make_bundle($img_name, $dest, $remoteHost, $attrs, $callback, $cwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# verify the image and return the values
|
|
sub get_image_info {
|
|
my $imagename = shift;
|
|
my $callback = shift;
|
|
my $node = shift;
|
|
my @extra = @_;
|
|
my $errors = 0;
|
|
my $attrs;
|
|
|
|
my $ostab = new xCAT::Table('osimage', -create => 1);
|
|
unless ($ostab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'osimage'."], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
#(my $attrs) = $ostab->getAttribs({imagename => $imagename}, 'profile', 'imagetype', 'provmethod', 'osname', 'osvers', 'osdistro', 'osarch', 'synclists');
|
|
(my $attrs0) = $ostab->getAttribs({ imagename => $imagename }, \@{ $xCAT::Schema::tabspec{osimage}->{cols} });
|
|
if (!$attrs0) {
|
|
$callback->({ error => ["Cannot find image \'$imagename\' from the osimage table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
unless ($attrs0->{provmethod}) {
|
|
$callback->({ error => ["The 'provmethod' field is not set for \'$imagename\' in the osimage table."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
unless ($attrs0->{profile}) {
|
|
$callback->({ error => ["The 'profile' field is not set for \'$imagename\' in the osimage table."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
unless ($attrs0->{osvers}) {
|
|
$callback->({ error => ["The 'osvers' field is not set for \'$imagename\' in the osimage table."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
unless ($attrs0->{osarch}) {
|
|
$callback->({ error => ["The 'osarch' field is not set for \'$imagename\' in the osimage table."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
unless ($attrs0->{provmethod} =~ /install|netboot|statelite|raw|sysclone/) {
|
|
$callback->({ error => [ "Exporting images with 'provemethod' " . $attrs0->{provmethod} . " is not supported. Hint: install, netboot, statelite, or raw" ], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
if (($attrs0->{provmethod} =~ /sysclone/) && ($attrs0->{osarch} !~ /s390x/)) {
|
|
$callback->({ error => [ "Exporting images with 'provemethod' " . $attrs0->{provmethod} . " is not supported for osarch '" . $attrs0->{osarch} . "'" ], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
#$attrs->{imagename} = $imagename;
|
|
|
|
if ($errors) {
|
|
return 0;
|
|
}
|
|
|
|
$attrs->{osimage} = $attrs0;
|
|
|
|
my $linuximagetab = new xCAT::Table('linuximage', -create => 1);
|
|
unless ($linuximagetab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'linuximage'"], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
#from linuximage table
|
|
#(my $attrs1) = $linuximagetab->getAttribs({imagename => $imagename}, 'template', 'pkglist', 'pkgdir', 'otherpkglist', 'otherpkgdir', 'exlist', 'postinstall', 'rootimgdir', 'nodebootif', 'otherifce', 'netdrivers', 'kernelver', 'permission');
|
|
(my $attrs1) = $linuximagetab->getAttribs({ imagename => $imagename }, \@{ $xCAT::Schema::tabspec{linuximage}->{cols} });
|
|
if (!$attrs1) {
|
|
$callback->({ error => ["Cannot find image \'$imagename\' from the linuximage table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
#merge attrs with attrs1
|
|
#foreach (keys %$attrs1) {
|
|
# $attrs->{$_} = $attrs1->{$_};
|
|
#}
|
|
$attrs->{linuximage} = $attrs1;
|
|
|
|
|
|
# for kit staff
|
|
|
|
if ($attrs0->{kitcomponents}) {
|
|
my $kitcomponenttab = new xCAT::Table('kitcomponent', -create => 1);
|
|
unless ($kitcomponenttab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'kitcomponent'"], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
my $kittab = new xCAT::Table('kit', -create => 1);
|
|
unless ($kittab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'kit'"], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
my $kitrepotab = new xCAT::Table('kitrepo', -create => 1);
|
|
unless ($kitrepotab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'kitrepo'"], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
my $kitlist;
|
|
my $kitrepolist;
|
|
my $kitcomplist;
|
|
my $DBname = xCAT::Utils->get_DBName; # support for DB2
|
|
foreach my $kitcomponent (split ',', $attrs0->{kitcomponents}) {
|
|
(my $kitcomphash) = $kitcomponenttab->getAttribs({ kitcompname => $kitcomponent }, 'kitname');
|
|
if (!$kitcomphash) {
|
|
$callback->({ error => ["Cannot find kitname of \'$kitcomponent\' from the kitcomponent table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
if ($kitcomphash->{kitname}) {
|
|
$kitlist->{ $kitcomphash->{kitname} } = 1;
|
|
my @kitrepohash;
|
|
if ($DBname =~ /^DB2/) {
|
|
@kitrepohash = $kitrepotab->getAllAttribsWhere("\"kitname\" = '$kitcomphash->{kitname}'", 'kitreponame');
|
|
} else {
|
|
@kitrepohash = $kitrepotab->getAllAttribsWhere("kitname = '$kitcomphash->{kitname}'", 'kitreponame');
|
|
}
|
|
foreach my $kitrepo (@kitrepohash) {
|
|
if ($kitrepo->{kitreponame}) {
|
|
$kitrepolist->{ $kitrepo->{kitreponame} } = 1;
|
|
}
|
|
}
|
|
|
|
my @kitcomponents;
|
|
if ($DBname =~ /^DB2/) {
|
|
@kitcomponents = $kitcomponenttab->getAllAttribsWhere("\"kitname\" = '$kitcomphash->{kitname}'", 'kitcompname');
|
|
} else {
|
|
@kitcomponents = $kitcomponenttab->getAllAttribsWhere("kitname = '$kitcomphash->{kitname}'", 'kitcompname');
|
|
}
|
|
foreach my $kitcomp (@kitcomponents) {
|
|
if ($kitcomp->{kitcompname}) {
|
|
$kitcomplist->{ $kitcomp->{kitcompname} } = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
foreach my $kitname (keys %$kitlist) {
|
|
(my $kitattrs) = $kittab->getAttribs({ kitname => $kitname }, \@{ $xCAT::Schema::tabspec{kit}->{cols} });
|
|
if (!$kitattrs) {
|
|
$callback->({ error => ["Cannot find kit \'$kitname\' from the kit table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
$attrs->{kit}->{$kitname} = $kitattrs;
|
|
}
|
|
|
|
foreach my $kitreponame (keys %$kitrepolist) {
|
|
(my $kitrepoattrs) = $kitrepotab->getAttribs({ kitreponame => $kitreponame }, \@{ $xCAT::Schema::tabspec{kitrepo}->{cols} });
|
|
if (!$kitrepoattrs) {
|
|
$callback->({ error => ["Cannot find kitrepo \'$kitreponame\' from the kitrepo table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
$attrs->{kitrepo}->{$kitreponame} = $kitrepoattrs;
|
|
}
|
|
|
|
foreach my $kitcompname (keys %$kitcomplist) {
|
|
(my $kitcompattrs) = $kitcomponenttab->getAttribs({ kitcompname => $kitcompname }, \@{ $xCAT::Schema::tabspec{kitcomponent}->{cols} });
|
|
if (!$kitcompattrs) {
|
|
$callback->({ error => ["Cannot find kitcomp \'$kitcompname\' from the kitcomp table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
$attrs->{kitcomp}->{$kitcompname} = $kitcompattrs;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$attrs = get_files($imagename, $callback, $attrs);
|
|
if ($#extra > -1) {
|
|
my $ex = get_extra($callback, @extra);
|
|
if ($ex) {
|
|
$attrs->{extra} = $ex;
|
|
}
|
|
}
|
|
|
|
#get postscripts
|
|
if ($node) {
|
|
$attrs = get_postscripts($node, $callback, $attrs)
|
|
}
|
|
|
|
|
|
# if we get nothing back, then we couldn't find the files. How sad, return nuthin'
|
|
return $attrs;
|
|
|
|
}
|
|
|
|
sub get_postscripts {
|
|
my $node = shift;
|
|
my $errors = 0;
|
|
my $callback = shift;
|
|
my $attrs = shift;
|
|
my @nodes = noderange($node);
|
|
if (@nodes > 0) { $node = $nodes[0]; }
|
|
else {
|
|
$callback->(
|
|
{ error => ["Unable to get postscripts, $node is not a valide node."], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
my $postscripts;
|
|
my $postbootscripts;
|
|
my $ptab = new xCAT::Table('postscripts', -create => 1);
|
|
unless ($ptab) {
|
|
$callback->(
|
|
{ error => ["Unable to open table 'postscripts'."], errorcode => 1 }
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
my $ent = $ptab->getNodeAttribs($node, [ 'postscripts', 'postbootscripts' ]);
|
|
if ($ent)
|
|
{
|
|
if ($ent->{postscripts}) { $postscripts = $ent->{postscripts}; }
|
|
if ($ent->{postbootscripts}) { $postbootscripts = $ent->{postbootscripts}; }
|
|
}
|
|
|
|
(my $attrs1) = $ptab->getAttribs({ node => "xcatdefaults" }, 'postscripts', 'postbootscripts');
|
|
if ($attrs1) {
|
|
if ($attrs1->{postscripts}) {
|
|
if ($postscripts) {
|
|
$postscripts = $attrs1->{postscripts} . ",$postscripts";
|
|
} else {
|
|
$postscripts = $attrs1->{postscripts};
|
|
}
|
|
}
|
|
if ($attrs1->{postbootscripts}) {
|
|
if ($postbootscripts) {
|
|
$postbootscripts = $attrs1->{postbootscripts} . ",$postbootscripts";
|
|
} else {
|
|
$postbootscripts = $attrs1->{postbootscripts};
|
|
}
|
|
}
|
|
|
|
}
|
|
if ($postscripts) {
|
|
$attrs->{postscripts} = $postscripts;
|
|
}
|
|
if ($postbootscripts) {
|
|
$attrs->{postbootscripts} = $postbootscripts;
|
|
}
|
|
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) {
|
|
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";
|
|
if (!$to_dir) {
|
|
if (-d $file) {
|
|
$to_dir = $file;
|
|
} else {
|
|
$to_dir = dirname($file);
|
|
}
|
|
}
|
|
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::TableUtils->getInstallDir();
|
|
unless ($installroot) {
|
|
$installroot = '/install';
|
|
}
|
|
|
|
my $provmethod = $attrs->{osimage}->{provmethod};
|
|
my $osvers = $attrs->{osimage}->{osvers};
|
|
my $arch = $attrs->{osimage}->{osarch};
|
|
my $profile = $attrs->{osimage}->{profile};
|
|
|
|
# here's the case for the install. All we need at first is the
|
|
# template. That should do it.
|
|
if ($provmethod =~ /install/) {
|
|
@arr = ("$installroot/custom/install", "$xcatroot/share/xcat/install");
|
|
|
|
#get .tmpl file
|
|
if (!$attrs->{linuximage}->{template}) {
|
|
my $template = look_for_file('tmpl', $callback, $attrs, @arr);
|
|
unless ($template) {
|
|
$callback->({ error => ["Couldn't find install template for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$callback->({ data => ["$template"] });
|
|
$attrs->{linuximage}->{template} = $template;
|
|
}
|
|
}
|
|
$attrs->{media} = "required";
|
|
}
|
|
|
|
|
|
# for stateless I need to save the
|
|
# ramdisk
|
|
# the kernel
|
|
# the rootimg.cpio.xz or rootimg.cpio.gz or rootimg.tar.xz or rootimg.tar.gz or rootimg.gz
|
|
if ($osvers !~ /esx/) {
|
|
|
|
# don't do anything because these files don't exist for ESX stateless.
|
|
if ($provmethod =~ /netboot/) {
|
|
|
|
# For 's390x', we want all of the image files
|
|
if ($arch =~ /s390x/) {
|
|
my @files;
|
|
my $dir = "$installroot/netboot/$osvers/s390x/$profile";
|
|
opendir(DIR, $dir) or $callback->({ error => ["Could not open image files in directory $dir"], errorcode => [1] });
|
|
|
|
while (my $file = readdir(DIR)) {
|
|
|
|
# We only want files in the directory that end with .img
|
|
next unless (-f "$dir/$file");
|
|
next unless ($file =~ m/\.img$/);
|
|
push(@files, "$dir/$file");
|
|
}
|
|
|
|
if (@files) {
|
|
$attrs->{rawimagefiles}->{files} = [@files];
|
|
}
|
|
|
|
closedir(DIR);
|
|
}
|
|
else {
|
|
@arr = ("$installroot/custom/netboot", "$xcatroot/share/xcat/netboot");
|
|
|
|
#get .pkglist file
|
|
if (!$attrs->{linuximage}->{pkglist}) {
|
|
|
|
# we need to get the .pkglist for this one!
|
|
my $temp = look_for_file('pkglist', $callback, $attrs, @arr);
|
|
unless ($temp) {
|
|
$callback->({ error => ["Couldn't find pkglist file for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{linuximage}->{pkglist} = $temp;
|
|
}
|
|
}
|
|
|
|
@arr = ("$installroot/netboot");
|
|
my $rootimgdir = $attrs->{linuximage}->{rootimgdir};
|
|
my $ramdisk;
|
|
my $kernel;
|
|
my $rootimg;
|
|
|
|
# look for ramdisk, kernel and rootimg.cpio.xz | rootimg.cpio.gz | rootimg.tar.xz | rootimg.tar.gz | rootimg.gz
|
|
if ($rootimgdir) {
|
|
if (-f "$rootimgdir/initrd-stateless.gz") {
|
|
$ramdisk = "$rootimgdir/initrd-stateless.gz";
|
|
}
|
|
if (-f "$rootimgdir/kernel") {
|
|
$kernel = "$rootimgdir/kernel";
|
|
}
|
|
|
|
my $compressedrootimg=xCAT::SvrUtils->searchcompressedrootimg("$rootimgdir");
|
|
$rootimg = "$rootimgdir/$compressedrootimg";
|
|
|
|
} else {
|
|
$ramdisk = look_for_file('initrd-stateless.gz', $callback, $attrs, @arr);
|
|
$kernel = look_for_file('kernel', $callback, $attrs, @arr);
|
|
|
|
my @rootimg_array = ("rootimg.cpio.xz","rootimg.cpio.gz","rootimg.tar.xz","rootimg.tar.gz","rootimg.gz");
|
|
foreach my $ra (@rootimg_array) {
|
|
$rootimg = look_for_file("$ra", $callback, $attrs, @arr);
|
|
if ($rootimg) {
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
unless ($ramdisk) {
|
|
$callback->({ error => ["Couldn't find ramdisk (initrd-stateless.gz) for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{ramdisk} = $ramdisk;
|
|
}
|
|
|
|
unless ($kernel) {
|
|
$callback->({ error => ["Couldn't find kernel (kernel) for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{kernel} = $kernel;
|
|
}
|
|
|
|
unless ($rootimg) {
|
|
$callback->({ error => ["Couldn't find compressed rootimg for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{rootimg} = $rootimg;
|
|
}
|
|
}
|
|
} elsif ($provmethod =~ /statelite/) {
|
|
@arr = ("$installroot/custom/netboot", "$xcatroot/share/xcat/netboot");
|
|
|
|
#get .pkglist file
|
|
if (!$attrs->{linuximage}->{pkglist}) {
|
|
|
|
# we need to get the .pkglist for this one!
|
|
my $temp = look_for_file('pkglist', $callback, $attrs, @arr);
|
|
unless ($temp) {
|
|
$callback->({ error => ["Couldn't find pkglist file for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{linuximage}->{pkglist} = $temp;
|
|
}
|
|
}
|
|
|
|
@arr = ("$installroot/netboot");
|
|
my $rootimgdir = $attrs->{linuximage}->{rootimgdir};
|
|
my $kernel;
|
|
my $ramdisk;
|
|
|
|
#look for kernel and ramdisk
|
|
if ($rootimgdir) {
|
|
if (-f "$rootimgdir/kernel") {
|
|
$kernel = "$rootimgdir/kernel";
|
|
}
|
|
if (-f "$rootimgdir/initrd-statelite.gz") {
|
|
$ramdisk = "$rootimgdir/initrd-statelite.gz";
|
|
}
|
|
} else {
|
|
$kernel = look_for_file('kernel', $callback, $attrs, @arr);
|
|
$ramdisk = look_for_file('initrd-statelite.gz', $callback, $attrs, @arr);
|
|
}
|
|
|
|
unless ($kernel) {
|
|
$callback->({ error => ["Couldn't find kernel (kernel) for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{kernel} = $kernel;
|
|
}
|
|
|
|
unless ($ramdisk) {
|
|
$callback->({ error => ["Couldn't find ramdisk (initrd-statelite.gz) for $imagename"], errorcode => [1] });
|
|
$errors++;
|
|
} else {
|
|
$attrs->{ramdisk} = $ramdisk;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (($provmethod =~ /raw|sysclone/) and ($arch =~ /s390x/)) {
|
|
my @files;
|
|
my $dir = "$installroot/$provmethod/$osvers/s390x/$profile";
|
|
opendir(DIR, $dir) or $callback->({ error => ["Could not open image files in directory $dir"], errorcode => [1] });
|
|
|
|
while (my $file = readdir(DIR)) {
|
|
|
|
# We only want files in the directory that end with .img
|
|
next unless (-f "$dir/$file");
|
|
next unless ($file =~ m/\.img$/);
|
|
push(@files, "$dir/$file");
|
|
}
|
|
|
|
if (@files) {
|
|
$attrs->{rawimagefiles}->{files} = [@files];
|
|
}
|
|
|
|
closedir(DIR);
|
|
}
|
|
|
|
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 $callback = shift;
|
|
my $attrs = shift;
|
|
my @dirs = @_;
|
|
my $r_file = '';
|
|
|
|
my $profile = $attrs->{osimage}->{profile};
|
|
my $arch = $attrs->{osimage}->{osarch};
|
|
my $distname = $attrs->{osimage}->{osvers};
|
|
|
|
|
|
# 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,
|
|
my $dd = $distname; # dd is distro directory, or disco dave, whichever you prefer.
|
|
if ($dd =~ /win/) { $dd = 'windows' }
|
|
until (-r "$d/$dd" or not $dd) {
|
|
$callback->({ data => ["not in $d/$dd..."] }) if $::VERBOSE;
|
|
chop($dd);
|
|
}
|
|
if ($distname && (($file eq 'tmpl') || ($file eq 'pkglist'))) {
|
|
$callback->({ data => ["looking in $d/$dd..."] }) if $::VERBOSE;
|
|
|
|
# now look for the file name: foo.rhel5.x86_64.tmpl
|
|
(-r "$d/$dd/$profile.$distname.$arch.$file") && (return "$d/$dd/$profile.$distname.$arch.$file");
|
|
|
|
# now look for the file name: foo.rhel5.tmpl
|
|
(-r "$d/$dd/$profile.$distname.$file") && (return "$d/$dd/$profile.$distname.$file");
|
|
|
|
# now look for the file name: foo.x86_64.tmpl
|
|
(-r "$d/$dd/$profile.$arch.$file") && (return "$d/$dd/$profile.$arch.$file");
|
|
|
|
# finally, look for the file name: foo.tmpl
|
|
(-r "$d/$dd/$profile.$file") && (return "$d/$dd/$profile.$file");
|
|
} 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 '';
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 make_bundle
|
|
|
|
Description : Makes the image bundle tar ball.
|
|
Arguments : Image name from the command line
|
|
Destination command line parameter, i.e. name of the tar ball. For a
|
|
remote host this can include the target directory.
|
|
Remote host parameter from the command line, if specified
|
|
Image attributes from the osimage table
|
|
callback
|
|
Current working directory
|
|
Returns : None
|
|
Example : make_bundle( $img_name, $dest, $remoteHost, $attrs, $callback, $cwd );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub make_bundle {
|
|
my $imagename = shift;
|
|
my $dest = shift;
|
|
my $remoteHost = shift;
|
|
my $attribs = shift;
|
|
my $callback = shift;
|
|
my $dir = shift;
|
|
my $rc;
|
|
|
|
# Determine the local working directory. It could be specified in the site table
|
|
# or we will default to the current working directory which was passed to this routine.
|
|
my @siteEntries = xCAT::TableUtils->get_site_attribute("tmpdir");
|
|
my $workDir = $siteEntries[0];
|
|
if (!$workDir) {
|
|
$workDir = $dir;
|
|
}
|
|
|
|
# get rid of spaces and put in underlines.
|
|
$imagename =~ s/\s+/_/g;
|
|
|
|
|
|
# Create the directory in which we collect the files prior to tarring them.
|
|
my $ttpath = mkdtemp("$workDir/imgexport.$$.XXXXXX");
|
|
$callback->({ data => ["Creating $ttpath..."] }) if $::VERBOSE;
|
|
my $tpath = "$ttpath/$imagename";
|
|
mkdir("$tpath");
|
|
chmod 0755, $tpath;
|
|
|
|
#for statelite
|
|
if ($attribs->{osimage}->{provmethod} eq 'statelite') {
|
|
|
|
#copy the rootimgdir over
|
|
my $rootimgdir = $attribs->{linuximage}->{rootimgdir};
|
|
if ($rootimgdir) {
|
|
$callback->({ data => ["Packing root image. It will take a while"] });
|
|
system("cd $rootimgdir; find rootimg |cpio -H newc -o | gzip -c - > $tpath/rootimgtree.gz");
|
|
$attribs->{'rootimgtree'} = "$rootimgdir/rootimgtree.gz";
|
|
} else {
|
|
$callback->({ error => ["Couldn't locate the root image directory. "], errorcode => [1] });
|
|
$rc = system("rm -rf $ttpath");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up temp space $ttpath"], errorcode => [1] });
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#get litefile table setting for the image
|
|
my $lftab = xCAT::Table->new("litefile", -create => 1);
|
|
if (!$lftab) {
|
|
$callback->({ error => ["Could not open the litefile table."], errorcode => [1] });
|
|
$rc = system("rm -rf $ttpath");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up temp space $ttpath"], errorcode => [1] });
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
$callback->({ data => ["Getting litefile settings"] });
|
|
my @imageInfo;
|
|
my @imagegroupsattr = ('groups');
|
|
|
|
# Check if this image contains osimage.groups attribute.
|
|
# if so, means user wants to use specific directories to this image.
|
|
my $osimagetab = xCAT::Table->new("osimage", -create => 1);
|
|
my $imagegroups = $osimagetab->getAttribs({ imagename => $imagename }, @imagegroupsattr);
|
|
if ($imagegroups and $imagegroups->{groups}) {
|
|
|
|
# get the directories with no names
|
|
push @imageInfo, $lftab->getAttribs({ image => '' }, ('file', 'options'));
|
|
|
|
# get for the image groups specific directories
|
|
push @imageInfo, $lftab->getAttribs({ image => $imagegroups->{groups} }, ('file', 'options'));
|
|
|
|
# get for the image specific directories
|
|
push @imageInfo, $lftab->getAttribs({ image => $imagename }, ('file', 'options'));
|
|
} else {
|
|
|
|
# get the directories with no names
|
|
push @imageInfo, $lftab->getAttribs({ image => '' }, ('file', 'options'));
|
|
|
|
# get the ALL directories
|
|
push @imageInfo, $lftab->getAttribs({ image => 'ALL' }, ('file', 'options'));
|
|
|
|
# get for the image specific directories
|
|
push @imageInfo, $lftab->getAttribs({ image => $imagename }, ('file', 'options'));
|
|
}
|
|
|
|
open(FILE, ">$tpath/litefile.csv") or die "Could not open $tpath/litefile.csv";
|
|
foreach (@imageInfo) {
|
|
my $file = $_->{file};
|
|
if (!$file) { next; }
|
|
my $o = $_->{options};
|
|
if (!$o) {
|
|
$o = "tmpfs";
|
|
}
|
|
print FILE "\"$imagename\",\"$file\",\"$o\",,\n";
|
|
}
|
|
close(FILE);
|
|
$attribs->{'litefile'} = "$rootimgdir/litefile.csv";
|
|
}
|
|
|
|
#print Dumper($attribs);
|
|
|
|
# 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", "ramdisk", "rootimg") {
|
|
my $filenames = $attribs->{$a};
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
foreach my $fn (@file_array) {
|
|
$callback->({ data => ["$fn"] });
|
|
if (-r $fn) {
|
|
system("cp $fn $tpath");
|
|
} else {
|
|
$callback->({ error => ["Couldn't find file $fn for $imagename. Skip."], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for my $a ("template", "pkglist", "otherpkglist", "postinstall", "exlist") {
|
|
my $filenames = $attribs->{linuximage}->{$a};
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
foreach my $fn (@file_array) {
|
|
$callback->({ data => ["$fn"] });
|
|
if (-r $fn) {
|
|
system("cp $fn $tpath");
|
|
} else {
|
|
$callback->({ error => ["Couldn't find file $fn for $imagename. Skip."], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for my $a ("synclists") {
|
|
my $filenames = $attribs->{osimage}->{$a};
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
foreach my $fn (@file_array) {
|
|
$callback->({ data => ["$fn"] });
|
|
if (-r $fn) {
|
|
system("cp $fn $tpath");
|
|
} else {
|
|
$callback->({ error => ["Couldn't find file $fn for $imagename. Skip."], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Copy kit
|
|
my @kits = keys %{ $attribs->{kit} };
|
|
foreach my $kit (@kits) {
|
|
|
|
my $values = $attribs->{kit}->{$kit};
|
|
if ($values->{kitdir}) {
|
|
my $fn = $values->{kitdir};
|
|
$callback->({ data => ["$fn"] });
|
|
if (-r $fn) {
|
|
system("cp -dr $fn $tpath");
|
|
} else {
|
|
$callback->({ error => ["Couldn't find file $fn for $imagename. Skip."], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Copy any raw image files. Multiple files can exist (used by s390x)
|
|
if ($attribs->{rawimagefiles}->{files}) {
|
|
foreach my $fromf (@{ $attribs->{rawimagefiles}->{files} }) {
|
|
$rc = system("cp $fromf $tpath");
|
|
if ($rc != 0) {
|
|
$callback->({ error => ["Unable to copy the raw image file $fromf."], errorcode => [1] });
|
|
$rc = system("rm -rf $ttpath");
|
|
if ($rc != 0) {
|
|
$callback->({ error => ["Unable to remove $ttpath."], errorcode => [1] });
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
# extra files get copied in the extra directory.
|
|
if ($attribs->{extra}) {
|
|
mkdir("$tpath/extra");
|
|
chmod 0755, "$tpath/extra";
|
|
foreach (@{ $attribs->{extra} }) {
|
|
my $fromf = $_->{src};
|
|
print " $fromf\n";
|
|
if (-d $fromf) {
|
|
print "fromf is a directory";
|
|
mkpath("$tpath/extra/$fromf");
|
|
`cp -a $fromf/* $tpath/extra/$fromf/`;
|
|
} else {
|
|
`cp $fromf $tpath/extra`;
|
|
}
|
|
}
|
|
}
|
|
|
|
# now get right below all this stuff and tar it up.
|
|
chdir($ttpath);
|
|
$callback->({ data => ["Inside $ttpath."] });
|
|
|
|
# Determine the name of the bundle that we will create.
|
|
my ($bundleName, $destDir);
|
|
if (defined $dest) {
|
|
($bundleName, $destDir) = fileparse($dest);
|
|
if ($bundleName eq '') {
|
|
$bundleName = "$imagename.tgz";
|
|
}
|
|
} else {
|
|
$bundleName = "$imagename.tgz";
|
|
}
|
|
|
|
|
|
# Determine the full file specification of the image bundle.
|
|
my $remoteDest;
|
|
my $tempBundle;
|
|
if (defined $remoteHost) {
|
|
|
|
# For a remote host, we need both a local build bundle
|
|
# and the final remote bundle file specification.
|
|
$tempBundle = mktemp("$workDir/imgexport.$$.XXXXXX");
|
|
chomp($tempBundle);
|
|
$dest = $tempBundle;
|
|
$remoteDest = "$destDir$bundleName";
|
|
}
|
|
else {
|
|
# Local imgexports go to the current working directory
|
|
$dest = "$dir/$bundleName";
|
|
}
|
|
|
|
$callback->({ data => ["Compressing $imagename bundle. Please be patient."] });
|
|
if ($::VERBOSE) {
|
|
$callback->({ data => ["tar czvf $dest . "] });
|
|
$rc = system("tar czvf $dest . ");
|
|
} else {
|
|
$rc = system("tar czf $dest . ");
|
|
}
|
|
$callback->({ data => ["Done!"] });
|
|
|
|
if ($rc) {
|
|
|
|
# An error occurred during tar to create the image bundle.
|
|
$callback->({ error => ["Failed to compress archive! (Maybe there was no space left?)"], errorcode => [1] });
|
|
if (-e $dest) {
|
|
|
|
# Remove the partially created image bundle.
|
|
$rc = system("rm $dest");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up image bundle $dest"], errorcode => [1] });
|
|
}
|
|
}
|
|
} else {
|
|
|
|
# The image bundle was created.
|
|
# If remotehost was specified then move the image bundle off xCAT MN to the remote host.
|
|
if (defined $remoteHost) {
|
|
my $remoteFile = $remoteHost . ':' . $remoteDest;
|
|
|
|
$callback->({ data => ["Moving the image bundle to the remote system location $remoteFile"] });
|
|
$rc = system("/usr/bin/scp -B $dest $remoteFile");
|
|
if ($rc) {
|
|
$callback->({ error => ["Unable to copy the image bundle $bundleName to the remote host (Maybe passwordless ssh was not setup?)"], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
|
|
# Remove the temporary image bundle if it is still around (for remote exports).
|
|
if (-e $tempBundle) {
|
|
$rc = system("rm -f $tempBundle");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up the temporary image bundle $tempBundle"], errorcode => [1] });
|
|
}
|
|
}
|
|
|
|
# Remove the directory that we used to collect the files prior to creating the tar ball.
|
|
chdir($dir);
|
|
$rc = system("rm -rf $ttpath");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up temp space $ttpath"], errorcode => [1] });
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 extract_bundle
|
|
|
|
Description : Extract the files from the image tar ball.
|
|
Arguments : Request
|
|
callback
|
|
nodes
|
|
New profile name to use for the image, if specified.
|
|
Remote host parameter from the command line
|
|
Returns : None
|
|
Example : extract_bundle( $request, $callback, $nodes, $new_profile, $remoteHost );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub extract_bundle {
|
|
my $request = shift;
|
|
|
|
#print Dumper($request);
|
|
my $callback = shift;
|
|
my $bundle = shift;
|
|
my $nodes = shift;
|
|
my $new_profile = shift;
|
|
my $remoteHost = shift;
|
|
my $nozip = shift;
|
|
|
|
my $xml;
|
|
my $data;
|
|
my $datas;
|
|
my $error = 0;
|
|
my $bundleCopy;
|
|
|
|
# Determine the current working directory.
|
|
my $dir = $request->{cwd}; #getcwd;
|
|
$dir = $dir->[0];
|
|
|
|
#print Dumper($dir);
|
|
|
|
# A working directory will hold the temporary image directory.
|
|
# It will also hold the local copy of a remote image bundle.
|
|
# The directory can be defined in the site table and if not
|
|
# defined there then we will default to using the current working
|
|
# directory.
|
|
my @siteEntries = xCAT::TableUtils->get_site_attribute("tmpdir");
|
|
my $workDir = $siteEntries[0];
|
|
if (!$workDir) {
|
|
$workDir = $dir;
|
|
}
|
|
|
|
# If we have a remote file then transfer it to the xCAT MN first.
|
|
if (defined $remoteHost) {
|
|
|
|
# Create unique copy the remote bundle in the working directory
|
|
my $remoteFile = "$remoteHost:$bundle";
|
|
$bundleCopy = `/bin/mktemp $workDir/imgimport.$$.XXXXXX`;
|
|
chomp($bundleCopy);
|
|
$callback->({ data => ["Obtaining the image bundle from the remote system $remoteFile"] });
|
|
my $rc = system("/usr/bin/scp -v -B $remoteFile $bundleCopy");
|
|
if ($rc != 0) {
|
|
$callback->({ error => ["Unable to copy the image bundle $bundle from the remote host (Maybe passwordless ssh was not setup?)"], errorcode => [1] });
|
|
$rc = system("rm -rf $bundleCopy");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to remove the local copy of the remote image bundle $bundleCopy"], errorcode => [1] });
|
|
}
|
|
return;
|
|
}
|
|
$bundle = $bundleCopy;
|
|
} else {
|
|
|
|
# When we are not doing a remote copy, we need to verify the bundle exists and find its exact location
|
|
unless (-r $bundle) {
|
|
$bundle = "$dir/$bundle";
|
|
}
|
|
|
|
unless (-r $bundle) {
|
|
$callback->({ error => ["Cannot find $bundle"], errorcode => [1] });
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ($::VERBOSE) {
|
|
if ($bundle =~ m/^\//) {
|
|
|
|
# Bundle name began with a slash
|
|
$callback->({ data => ["Bundle file is located at $bundle"] });
|
|
} else {
|
|
my $pwd = cwd();
|
|
$callback->({ data => ["Bundle file is located at $pwd/$bundle"] });
|
|
}
|
|
}
|
|
|
|
my $tpath = mkdtemp("$workDir/imgimport.$$.XXXXXX");
|
|
|
|
$callback->({ data => ["Unbundling image..."] });
|
|
my $rc;
|
|
if ($nozip) {
|
|
if ($::VERBOSE) {
|
|
$callback->({data=>["tar xvf $bundle -C $tpath"]});
|
|
$rc = system("tar xvf $bundle -C $tpath");
|
|
} else {
|
|
$rc = system("tar xf $bundle -C $tpath");
|
|
}
|
|
} else {
|
|
if ($::VERBOSE) {
|
|
$callback->({ data => ["tar zxvf $bundle -C $tpath"] });
|
|
$rc = system("tar zxvf $bundle -C $tpath");
|
|
} else {
|
|
$rc = system("tar zxf $bundle -C $tpath");
|
|
}
|
|
}
|
|
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to extract bundle $bundle"], errorcode => [1] });
|
|
|
|
# Remove the files in the untar directory so that we don't attempt to process
|
|
# a partially untarred image bundle.
|
|
system("rm -rf $tpath");
|
|
}
|
|
|
|
# get all the files in the tpath. These should be all the image names.
|
|
my @files = < $tpath/* >;
|
|
|
|
# go through each image directory. Find the XML and put it into the array. If there are any
|
|
# errors then the whole thing is over and we error and leave.
|
|
foreach my $imgdir (@files) {
|
|
unless (-r "$imgdir/manifest.xml") {
|
|
$callback->({ error => ["Failed to find manifest.xml file in image bundle"], errorcode => [1] });
|
|
last;
|
|
}
|
|
|
|
$xml = new XML::Simple;
|
|
|
|
# get the data!
|
|
# put it in an eval string so that it
|
|
$data = eval { $xml->XMLin("$imgdir/manifest.xml") };
|
|
if ($@) {
|
|
$callback->({ error => ["invalid manifest.xml file inside the bundle. Please verify the XML"], errorcode => [1] });
|
|
|
|
#my $foo = $@;
|
|
#$foo =~ s/\n//;
|
|
#$callback->({error=>[$foo],errorcode=>[1]});
|
|
#foreach($@){
|
|
# last;
|
|
#}
|
|
last;
|
|
}
|
|
|
|
#print Dumper($data);
|
|
#push @{$datas}, $data;
|
|
|
|
#support imgimport osimage exported by xCAT 2.7
|
|
manifest_adapter($data);
|
|
|
|
# now we need to import the files...
|
|
unless (verify_manifest($data, $callback)) {
|
|
$error++;
|
|
next;
|
|
}
|
|
|
|
# check media first
|
|
unless (check_media($data, $callback)) {
|
|
$error++;
|
|
next;
|
|
}
|
|
|
|
#change profile name if needed
|
|
if ($new_profile) {
|
|
$data = change_profile($data, $callback, $new_profile, $imgdir);
|
|
}
|
|
|
|
#import manifest.xml into xCAT database
|
|
unless (set_config($data, $callback)) {
|
|
$error++;
|
|
next;
|
|
}
|
|
|
|
# now place files in appropriate directories.
|
|
unless (make_files($data, $imgdir, $callback)) {
|
|
$error++;
|
|
next;
|
|
}
|
|
|
|
# put postscripts in the postsctipts table
|
|
if ($nodes) {
|
|
unless (set_postscripts($data, $callback, $nodes)) {
|
|
$error++;
|
|
next;
|
|
}
|
|
}
|
|
|
|
my $osimage = $data->{osimage}->{imagename};
|
|
$callback->({ data => ["Successfully imported the image $osimage."] });
|
|
|
|
}
|
|
|
|
# Clean up for this routine.
|
|
# Remove the temp directory used for the exploded bundle
|
|
if (-e $tpath) {
|
|
$rc = system("rm -rf $tpath");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to clean up temp space $tpath"], errorcode => [1] });
|
|
|
|
# Don't return just yet. We want the rest of the cleanup to occur.
|
|
}
|
|
}
|
|
|
|
# If this was an import from a remote host then remove the directory created for the remote files.
|
|
# We do not want to leave files hanging around that were brought from another system.
|
|
if (-e $bundleCopy) {
|
|
$rc = system("rm -rf $bundleCopy");
|
|
if ($rc) {
|
|
$callback->({ error => ["Failed to remove the local copy of the remote image bundle $bundleCopy"], errorcode => [1] });
|
|
}
|
|
}
|
|
}
|
|
|
|
sub change_profile {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $new_profile = shift;
|
|
my $srcdir = shift;
|
|
|
|
my $old_profile = $data->{osimage}->{profile};
|
|
if ($old_profile eq $new_profile) {
|
|
return $data; #do nothing if old profile is the same as the new one.
|
|
}
|
|
|
|
$data->{osimage}->{profile} = $new_profile;
|
|
my $installdir = xCAT::TableUtils->getInstallDir();
|
|
unless ($installdir) {
|
|
$installdir = '/install';
|
|
}
|
|
if ($data->{linuximage}->{rootimgdir}) {
|
|
if (($data->{osimage}->{osarch} eq "s390x") && ($data->{osimage}->{provmethod} =~ /raw|sysclone/)) {
|
|
$data->{linuximage}->{rootimgdir} = "$installdir/$data->{osimage}->{provmethod}/" . $data->{osimage}->{osvers} . "/" . $data->{osimage}->{osarch} . "/$new_profile";
|
|
} else {
|
|
$data->{linuximage}->{rootimgdir} = "$installdir/netboot/" . $data->{osimage}->{osvers} . "/" . $data->{osimage}->{osarch} . "/$new_profile";
|
|
}
|
|
|
|
for my $a ("kernel", "ramdisk", "rootimg", "rootimgtree", "litefile") {
|
|
if ($data->{$a}) {
|
|
my $fn = basename($data->{$a});
|
|
$data->{$a} = $data->{linuximage}->{rootimgdir} . "/$fn";
|
|
}
|
|
}
|
|
}
|
|
|
|
my $prov = "netboot";
|
|
if ($data->{osimage}->{provmethod} eq "install") { $prov = "install"; }
|
|
my $platform;
|
|
my $os = $data->{osimage}->{osvers};
|
|
if ($os) {
|
|
if ($os =~ /rh.*/) { $platform = "rh"; }
|
|
elsif ($os =~ /centos.*/) { $platform = "centos"; }
|
|
elsif ($os =~ /alma.*/) { $platform = "alma"; }
|
|
elsif ($os =~ /rocky.*/) { $platform = "rocky"; }
|
|
elsif ($os =~ /fedora.*/) { $platform = "fedora"; }
|
|
elsif ($os =~ /sles.*/) { $platform = "sles"; }
|
|
elsif ($os =~ /SL.*/) { $platform = "SL"; }
|
|
elsif ($os =~ /win/) { $platform = "windows"; }
|
|
elsif ($os =~ /ubuntu*/) { $platform = "ubuntu"; }
|
|
}
|
|
|
|
|
|
for my $a ("template", "pkglist", "synclists", "otherpkglist", "postinstall", "exlist") {
|
|
my $filenames = $data->{linuximage}->{$a};
|
|
if ($a eq "synclists") {
|
|
$filenames = $data->{osimage}->{$a};
|
|
}
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
my @new_file_array = ();
|
|
foreach my $old (@file_array) {
|
|
my $oldfn = basename($old);
|
|
my $olddir = dirname($old);
|
|
my $newfn;
|
|
my $newdir;
|
|
|
|
#if source file is from /opt/xcat/share...,
|
|
#then copy it to /install/custom... directory.
|
|
#Otherwise, copy the file in the same directory
|
|
if ($olddir =~ /$::XCATROOT\/share\/xcat/) {
|
|
$newdir = "$installdir/custom/$prov/$platform";
|
|
} else {
|
|
$newdir = $olddir;
|
|
}
|
|
|
|
#if the file name contains the old profile name,
|
|
#replace it with the new profile name.
|
|
#Otherwise prefix the old file name with the new profile name.
|
|
if ($oldfn =~ /$old_profile/) {
|
|
$newfn = $oldfn;
|
|
$newfn =~ s/$old_profile/$new_profile/;
|
|
} else {
|
|
$newfn = "$new_profile.$oldfn";
|
|
}
|
|
|
|
move("$srcdir/$oldfn", "$srcdir/$newfn");
|
|
push(@new_file_array, "$newdir/$newfn");
|
|
}
|
|
if ($a eq "synclists") {
|
|
$data->{osimage}->{$a} = join(',', @new_file_array);
|
|
} else {
|
|
$data->{linuximage}->{$a} = join(',', @new_file_array);
|
|
}
|
|
}
|
|
}
|
|
|
|
#change the image name
|
|
my $new_imgname = $data->{osimage}->{osvers} . "-" . $data->{osimage}->{osarch} . "-" . $data->{osimage}->{provmethod} . "-$new_profile";
|
|
$data->{osimage}->{imagename} = $new_imgname;
|
|
$data->{linuximage}->{imagename} = $new_imgname;
|
|
|
|
return $data;
|
|
}
|
|
|
|
# return 1 for true 0 for false.
|
|
# need to make sure media is copied before importing image.
|
|
sub check_media {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $rc = 0;
|
|
unless ($data->{'media'}) {
|
|
$rc = 1;
|
|
} elsif ($data->{media} eq 'required') {
|
|
my $os = $data->{osimage}->{osvers};
|
|
my $arch = $data->{osimage}->{osarch};
|
|
my $installroot = xCAT::TableUtils->getInstallDir();
|
|
unless ($installroot) {
|
|
$installroot = '/install';
|
|
}
|
|
unless (-d "$installroot/$os/$arch") {
|
|
$callback->({ error => ["This image requires that you first copy media for $os-$arch"], errorcode => [1] });
|
|
} else {
|
|
$rc = 1;
|
|
}
|
|
}
|
|
return $rc;
|
|
}
|
|
|
|
|
|
sub set_postscripts {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $nodes = shift;
|
|
|
|
$callback->({ data => ["Adding postscripts."] });
|
|
|
|
my @good_nodes = noderange($nodes);
|
|
|
|
if (@good_nodes > 0) {
|
|
my @missed = nodesmissed();
|
|
if (@missed > 0) {
|
|
$callback->({ warning => [ "The following nodes will be skipped because they are not in the nodelist table.\n " . join(',', @missed) ], errorcode => 1 });
|
|
}
|
|
} else {
|
|
$callback->({ error => ["The nodes $nodes are not defined in xCAT DB."], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
my $ptab = xCAT::Table->new('postscripts', -create => 1, -autocommit => 0);
|
|
unless ($ptab) {
|
|
$callback->({ error => ["Unable to open table 'postscripts'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
|
|
# get xcatdefaults settings
|
|
my @a1 = ();
|
|
my @a2 = ();
|
|
(my $attrs1) = $ptab->getAttribs({ node => "xcatdefaults" }, 'postscripts', 'postbootscripts');
|
|
if ($attrs1) {
|
|
if ($attrs1->{postscripts}) {
|
|
@a1 = split(',', $attrs1->{postscripts});
|
|
}
|
|
if ($attrs1->{postbootscripts}) {
|
|
@a2 = split(',', $attrs1->{postbootscripts});
|
|
}
|
|
}
|
|
|
|
#remove the script if it is already in xcatdefaults
|
|
my @a3 = ();
|
|
my @a4 = ();
|
|
my $postscripts = $data->{postscripts};
|
|
my $postbootscripts = $data->{postbootscripts};
|
|
if ($postscripts) { @a3 = split(',', $postscripts); }
|
|
if ($postbootscripts) { @a4 = split(',', $postbootscripts); }
|
|
|
|
my @a30;
|
|
my @a40;
|
|
if (@a1 > 0 && @a3 > 0) {
|
|
foreach my $tmp1 (@a3) {
|
|
if (!grep /^$tmp1$/, @a1) {
|
|
push(@a30, $tmp1);
|
|
}
|
|
}
|
|
$postscripts = join(',', @a30);
|
|
}
|
|
if (@a2 > 0 && @a4 > 0) {
|
|
foreach my $tmp2 (@a4) {
|
|
if (!grep /^$tmp2$/, @a2) {
|
|
push(@a40, $tmp2);
|
|
}
|
|
}
|
|
$postbootscripts = join(',', @a40);
|
|
}
|
|
|
|
#now save to the db
|
|
my %keyhash;
|
|
if ($postscripts || $postbootscripts) {
|
|
$keyhash{postscripts} = $postscripts;
|
|
$keyhash{postbootscripts} = $postbootscripts;
|
|
$ptab->setNodesAttribs(\@good_nodes, \%keyhash);
|
|
$ptab->commit;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
sub create_symlink {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $otherpkgdir = $data->{linuximage}->{otherpkgdir};
|
|
my @kitcomps = split(',', $data->{osimage}->{kitcomponents});
|
|
|
|
my %tabs = ();
|
|
my @tables = qw(kit kitrepo kitcomponent);
|
|
foreach my $t (@tables) {
|
|
$tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);
|
|
|
|
if (!exists($tabs{$t})) {
|
|
my %rsp;
|
|
push @{ $rsp{data} }, "Could not open xCAT table $t";
|
|
xCAT::MsgUtils->message("E", \%rsp, $callback);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (defined($otherpkgdir)) {
|
|
|
|
# Create otherpkgdir if it doesn't exist
|
|
unless (-d "$otherpkgdir") {
|
|
mkpath("$otherpkgdir");
|
|
}
|
|
|
|
if ($data and $data->{osimage} and $data->{osimage}->{kitcomponents}) {
|
|
foreach my $kitcomponent (@kitcomps) {
|
|
(my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomponent }, 'kitreponame');
|
|
if ($kitcomptable and $kitcomptable->{'kitreponame'}) {
|
|
|
|
# Create symlink if doesn't exist
|
|
unless (-d "$otherpkgdir/$kitcomptable->{'kitreponame'}") {
|
|
(my $kitrepotable) = $tabs{kitrepo}->getAttribs({ kitreponame => $kitcomptable->{'kitreponame'} }, 'kitrepodir');
|
|
if ($kitrepotable and $kitrepotable->{'kitrepodir'}) {
|
|
system("ln -sf $kitrepotable->{'kitrepodir'} $otherpkgdir/$kitcomptable->{'kitreponame'}");
|
|
} else {
|
|
$callback->({ error => ["Cannot open kitrepo table or kitrepodir do not exist"], errorcode => [1] });
|
|
next;
|
|
}
|
|
}
|
|
} else {
|
|
$callback->({ error => ["Cannot open kitcomponent table or kitreponame do not exist"], errorcode => [1] });
|
|
next;
|
|
}
|
|
}
|
|
} else {
|
|
$callback->({ warning => ["osimage table or kitcomponent do not exist"], errorcode => [1] });
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
sub set_config {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $ostab = xCAT::Table->new('osimage', -create => 1, -autocommit => 0);
|
|
my $linuxtab = xCAT::Table->new('linuximage', -create => 1, -autocommit => 0);
|
|
my $kittab = xCAT::Table->new('kit', -create => 1, -autocommit => 0);
|
|
my $kitrepotab = xCAT::Table->new('kitrepo', -create => 1, -autocommit => 0);
|
|
my $kitcomptab = xCAT::Table->new('kitcomponent', -create => 1, -autocommit => 0);
|
|
my %keyhash;
|
|
my $osimage = $data->{osimage}->{imagename};
|
|
|
|
unless ($ostab) {
|
|
$callback->({ error => ["Unable to open table 'osimage'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
unless ($linuxtab) {
|
|
$callback->({ error => ["Unable to open table 'linuximage'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
unless ($kittab) {
|
|
$callback->({ error => ["Unable to open table 'kit'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
unless ($kitrepotab) {
|
|
$callback->({ error => ["Unable to open table 'kitrepo'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
unless ($kitcomptab) {
|
|
$callback->({ error => ["Unable to open table 'kitcomponent'"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
|
|
$callback->({ data => ["Adding $osimage"] }) if $::VERBOSE;
|
|
|
|
# now we make a quick hash of what we want to put into this
|
|
my $hash_tmp = $data->{osimage};
|
|
foreach my $key (keys %$hash_tmp) {
|
|
$keyhash{$key} = $hash_tmp->{$key};
|
|
}
|
|
$ostab->setAttribs({ imagename => $osimage }, \%keyhash);
|
|
$ostab->commit;
|
|
|
|
%keyhash = ();
|
|
my $hash_tmp1 = $data->{linuximage};
|
|
foreach my $key (keys %$hash_tmp1) {
|
|
$keyhash{$key} = $hash_tmp1->{$key};
|
|
}
|
|
|
|
$linuxtab->setAttribs({ imagename => $osimage }, \%keyhash);
|
|
$linuxtab->commit;
|
|
|
|
my $kit = $data->{kit};
|
|
foreach my $k (keys %$kit) {
|
|
my $kithash = $kit->{$k};
|
|
%keyhash = ();
|
|
foreach my $key (keys %$kithash) {
|
|
$keyhash{$key} = $kithash->{$key};
|
|
}
|
|
$kittab->setAttribs({ kitname => $k }, \%keyhash);
|
|
$kittab->commit;
|
|
}
|
|
|
|
my $kitrepo = $data->{kitrepo};
|
|
foreach my $k (keys %$kitrepo) {
|
|
my $kitrepohash = $kitrepo->{$k};
|
|
%keyhash = ();
|
|
foreach my $key (keys %$kitrepohash) {
|
|
$keyhash{$key} = $kitrepohash->{$key};
|
|
}
|
|
$kitrepotab->setAttribs({ kitreponame => $k }, \%keyhash);
|
|
$kitrepotab->commit;
|
|
}
|
|
|
|
my $kitcomp = $data->{kitcomp};
|
|
foreach my $k (keys %$kitcomp) {
|
|
my $kitcomphash = $kitcomp->{$k};
|
|
%keyhash = ();
|
|
foreach my $key (keys %$kitcomphash) {
|
|
$keyhash{$key} = $kitcomphash->{$key};
|
|
}
|
|
$kitcomptab->setAttribs({ kitcompname => $k }, \%keyhash);
|
|
$kitcomptab->commit;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#an adapter to convert the manifest structure from 2.7 to 2.8
|
|
sub manifest_adapter {
|
|
my $data = shift;
|
|
|
|
if (exists($data->{osimage}) or exists($data->{linuximage})) {
|
|
return 0;
|
|
}
|
|
|
|
my %colstodel;
|
|
foreach my $col (@{ $xCAT::Schema::tabspec{osimage}->{cols} }) {
|
|
if (defined($data->{$col})) {
|
|
$colstodel{$col} = 1;
|
|
$data->{osimage}->{$col} = $data->{$col};
|
|
}
|
|
}
|
|
|
|
|
|
foreach my $col (@{ $xCAT::Schema::tabspec{linuximage}->{cols} }) {
|
|
if (defined($data->{$col})) {
|
|
$colstodel{$col} = 1;
|
|
$data->{linuximage}->{$col} = $data->{$col};
|
|
}
|
|
}
|
|
|
|
foreach my $col (keys %colstodel) {
|
|
delete($data->{$col});
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
sub verify_manifest {
|
|
my $data = shift;
|
|
my $callback = shift;
|
|
my $errors = 0;
|
|
|
|
# first make sure that the stuff is defined!
|
|
# For certain fields that are used in later construction of directory structure,
|
|
# we trim whitespace which can occur in some versions of the xml processing.
|
|
unless ($data->{osimage}->{imagename}) {
|
|
$callback->({ error => ["The 'imagename' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{imagename} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
unless ($data->{osimage}->{provmethod}) {
|
|
$callback->({ error => ["The 'provmethod' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{provmethod} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
unless ($data->{osimage}->{profile}) {
|
|
$callback->({ error => ["The 'profile' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{profile} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
unless ($data->{osimage}->{osvers}) {
|
|
$callback->({ error => ["The 'osvers' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{osvers} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
unless ($data->{osimage}->{osarch}) {
|
|
$callback->({ error => ["The 'osarch' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{osarch} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
unless ($data->{osimage}->{provmethod} =~ /install|netboot|statelite|sysclone|raw/) {
|
|
$callback->({ error => [ "Importing images with 'provmethod' " . $data->{osimage}->{provmethod} . " is not supported. Hint: install, netboot, statelite, sysclone or raw" ], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
if (($data->{osimage}->{provmethod} =~ /sysclone/) && ($data->{osimage}->{osarch} !~ /s390x/)) {
|
|
$callback->({ error => [ "Importing images with 'provemethod' " . $data->{osimage}->{provmethod} . " is not supported for osarch '" . $data->{osimage}->{osarch} . "'" ], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
$data->{osimage}->{provmethod} =~ s/^\s*(\S*)\s*$/$1/;
|
|
|
|
# if the install method is used, then we need to have certain files in place.
|
|
if ($data->{osimage}->{provmethod} =~ /install/) {
|
|
|
|
# we need to get the template for this one!
|
|
unless ($data->{linuximage}->{template}) {
|
|
$callback->({ error => ["The 'osarch' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
#$attrs->{media} = "required"; (need to do something to verify media!
|
|
|
|
} elsif ($data->{osimage}->{osvers} =~ /esx/) {
|
|
$callback->({ info => ['this is an esx image'] });
|
|
|
|
# do nothing for ESX
|
|
1;
|
|
} elsif ($data->{osimage}->{provmethod} =~ /netboot/) {
|
|
if ($data->{osimage}->{osarch} =~ /s390x/) {
|
|
if (!$data->{rawimagefiles}) {
|
|
$callback->({ error => ["The 'rawimagefiles' section is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
} elsif (!$data->{rawimagefiles}->{files}) {
|
|
$callback->({ error => ["'files' were not specified in the 'rawimagefiles' section of manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
} else {
|
|
unless ($data->{ramdisk}) {
|
|
$callback->({ error => ["The 'ramdisk' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
unless ($data->{kernel}) {
|
|
$callback->({ error => ["The 'kernel' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
unless ($data->{rootimg}) {
|
|
$callback->({ error => ["The 'rootimg' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
}
|
|
|
|
} elsif ($data->{osimage}->{provmethod} =~ /statelite/) {
|
|
unless ($data->{kernel}) {
|
|
$callback->({ error => ["The 'kernel' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
unless ($data->{ramdisk}) {
|
|
$callback->({ error => ["The 'ramdisk' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
unless ($data->{'rootimgtree'}) {
|
|
$callback->({ error => ["The 'rootimgtree' field is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
|
|
} elsif ($data->{osimage}->{provmethod} =~ /sysclone/) {
|
|
if ($data->{osimage}->{osarch} =~ /s390x/) {
|
|
if (!$data->{rawimagefiles}) {
|
|
$callback->({ error => ["The 'rawimagefiles' section is not defined in manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
} elsif (!$data->{rawimagefiles}->{files}) {
|
|
$callback->({ error => ["'files' were not specified in the 'rawimagefiles' section of manifest.xml."], errorcode => [1] });
|
|
$errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($errors) {
|
|
|
|
# we had problems, error and exit.
|
|
return 0;
|
|
}
|
|
|
|
# returning 1 means everything went good!
|
|
return 1;
|
|
}
|
|
|
|
sub make_files {
|
|
my $data = shift;
|
|
my $imgdir = shift;
|
|
my $callback = shift;
|
|
my $os = $data->{osimage}->{osvers};
|
|
my $arch = $data->{osimage}->{osarch};
|
|
my $profile = $data->{osimage}->{profile};
|
|
my $installroot = xCAT::TableUtils->getInstallDir();
|
|
unless ($installroot) {
|
|
$installroot = '/install';
|
|
}
|
|
|
|
# you'll get a hash like this for install:
|
|
#$VAR1 = {
|
|
# osimage=> {
|
|
# 'imagename' => 'Default_Stateful',
|
|
# 'provmethod' => 'install',
|
|
# 'profile' => 'all',
|
|
# 'osarch' => 'x86_64',
|
|
# 'osvers' => 'centos5.4'
|
|
# 'synclists' => '/opt/xcat/share/xcat/install/centos/all.othetpkgs.synclist',
|
|
# }
|
|
# linuxiage=> {
|
|
# 'template' => '/opt/xcat/share/xcat/install/centos/all.tmpl',
|
|
# 'pkglist' => '/opt/xcat/share/xcat/install/centos/all.pkglist',
|
|
# 'otherpkglist' => '/opt/xcat/share/xcat/install/centos/all.othetpkgs.pkglist',
|
|
# 'imagename' => 'Default_Stateful',
|
|
# }
|
|
# 'media' => 'required',
|
|
# };
|
|
|
|
# data will look something like this for netboot:
|
|
#$VAR1 = {
|
|
# 'ramdisk' => '/install/netboot/centos5.4/x86_64/compute/initrd-stateless.gz',
|
|
# 'rootimg' => '/install/netboot/centos5.4/x86_64/compute/rootimg.gz'
|
|
# 'kernel' => '/install/netboot/centos5.4/x86_64/compute/kernel',
|
|
# osimage=> {
|
|
# 'imagename' => 'Default_Stateless_1265981465',
|
|
# 'osvers' => 'centos5.4',
|
|
# 'osarch' => 'x86_64',
|
|
# 'provmethod' => 'netboot',
|
|
# 'profile' => 'compute',
|
|
# 'synclists' => '/opt/xcat/share/xcat/install/centos/compute.othetpkgs.synclist',
|
|
# }
|
|
# linuximage=> {
|
|
# 'imagename' => 'Default_Stateless_1265981465',
|
|
# 'pkglist' => '/opt/xcat/share/xcat/install/centos/compute.pkglist',
|
|
# 'otherpkglist' => '/opt/xcat/share/xcat/install/centos/compute.othetpkgs.pkglist',
|
|
# 'exlist' => '/opt/xcat/share/xcat/install/centos/compute.exlist',
|
|
# 'postinstall' => '/opt/xcat/share/xcat/install/centos/compute.postinstall',
|
|
# }
|
|
# 'extra' => [
|
|
# {
|
|
# 'dest' => '/install/custom/netboot/centos',
|
|
# 'src' => '/opt/xcat/share/xcat/netboot/centos/compute.centos5.4.pkglist'
|
|
# },
|
|
# {
|
|
# 'dest' => '/install/custom/netboot/centos',
|
|
# 'src' => '/opt/xcat/share/xcat/netboot/centos/compute.exlist'
|
|
# }
|
|
# ],
|
|
# };
|
|
# data will look something like this for statelite:
|
|
#$VAR1 = {
|
|
# 'ramdisk' => '/install/netboot/centos5.4/x86_64/compute/initrd-statelite.gz',
|
|
# 'kernel' => '/install/netboot/centos5.4/x86_64/compute/kernel',
|
|
# 'rootimgtree' => '/install/netboot/centos5.4/x86_64/compute/rootimg/rootimgtree.gz'
|
|
# osimage=> {
|
|
# 'osvers' => 'centos5.4',
|
|
# 'osarch' => 'x86_64',
|
|
# 'imagename' => 'Default_Stateless_1265981465',
|
|
# 'provmethod' => 'statelite',
|
|
# 'profile' => 'compute',
|
|
# 'synclists' => '/opt/xcat/share/xcat/install/centos/compute.othetpkgs.synclist',
|
|
# }
|
|
# linuximage=> {
|
|
# 'imagename' => 'Default_Stateless_1265981465',
|
|
# 'pkglist' => '/opt/xcat/share/xcat/install/centos/compute.pkglist',
|
|
# 'otherpkglist' => '/opt/xcat/share/xcat/install/centos/compute.othetpkgs.pkglist',
|
|
|
|
# 'exlist' => '/opt/xcat/share/xcat/install/centos/compute.exlist',
|
|
# 'postinstall' => '/opt/xcat/share/xcat/install/centos/compute.postinstall',
|
|
# }
|
|
# 'extra' => [
|
|
# {
|
|
# 'dest' => '/install/custom/netboot/centos',
|
|
# 'src' => '/opt/xcat/share/xcat/netboot/centos/compute.centos5.4.pkglist'
|
|
# },
|
|
# {
|
|
# 'dest' => '/install/custom/netboot/centos',
|
|
# 'src' => '/opt/xcat/share/xcat/netboot/centos/compute.exlist'
|
|
# }
|
|
# ],
|
|
# };
|
|
|
|
for my $a ("kernel", "ramdisk", "rootimg", "rootimgtree", "litefile") {
|
|
my $filenames = $data->{$a};
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
foreach my $fn (@file_array) {
|
|
$callback->({ data => ["$fn"] });
|
|
my $basename = basename($fn);
|
|
my $dirname = dirname($fn);
|
|
if (!-r $dirname) {
|
|
mkpath("$dirname", { verbose => 1, mode => 0755 });
|
|
}
|
|
if (-r $fn) {
|
|
$callback->({ data => [" Moving old $fn to $fn.ORIG."] });
|
|
move("$fn", "$fn.ORIG");
|
|
}
|
|
move("$imgdir/$basename", $fn);
|
|
}
|
|
}
|
|
}
|
|
|
|
for my $a ("template", "pkglist", "synclists", "otherpkglist", "postinstall", "exlist") {
|
|
my $filenames;
|
|
if ($a eq "synclists") {
|
|
$filenames = $data->{osimage}->{$a};
|
|
} else {
|
|
$filenames = $data->{linuximage}->{$a};
|
|
}
|
|
if ($filenames) {
|
|
my @file_array = split(',', $filenames);
|
|
foreach my $fn (@file_array) {
|
|
$callback->({ data => ["$fn"] });
|
|
my $basename = basename($fn);
|
|
my $dirname = dirname($fn);
|
|
if (!-r $dirname) {
|
|
mkpath("$dirname", { verbose => 1, mode => 0755 });
|
|
}
|
|
if (-r $fn) {
|
|
$callback->({ data => [" Moving old $fn to $fn.ORIG."] });
|
|
move("$fn", "$fn.ORIG");
|
|
}
|
|
move("$imgdir/$basename", $fn);
|
|
}
|
|
}
|
|
}
|
|
|
|
# unpack kit
|
|
my $k = $data->{kit};
|
|
foreach my $kit (keys %$k) {
|
|
my $fn = $k->{$kit}->{kitdir};
|
|
if ($fn) {
|
|
my $dirname = dirname($fn);
|
|
if (!-r $dirname) {
|
|
mkpath("$dirname", { verbose => 1, mode => 0755 });
|
|
}
|
|
|
|
if (-r "$dirname/$kit") {
|
|
$callback->({ data => [" Moving old $fn to $fn.ORIG."] });
|
|
move("$dirname/$kit", "$dirname/$kit.ORIG");
|
|
|
|
}
|
|
move("$imgdir/$kit", "$dirname/$kit");
|
|
|
|
#copy postscripts from kit dir to postscripts dir;
|
|
copyPostscripts($dirname, $kit, $installroot, $callback);
|
|
|
|
#copy plugin from kit to xCAT_plugin
|
|
movePlugin($dirname, $kit, $callback);
|
|
}
|
|
}
|
|
if ($hasplugin) {
|
|
|
|
# Fast restart xcatd to load the new plugins without dropping this request
|
|
system("$::XCATROOT/sbin/restartxcatd");
|
|
$hasplugin = 0;
|
|
}
|
|
|
|
|
|
#unpack the rootimgtree.gz for statelite
|
|
my $fn = $data->{'rootimgtree'};
|
|
if ($fn) {
|
|
if (-r $fn) {
|
|
my $basename = basename($fn);
|
|
my $dirname = dirname($fn);
|
|
|
|
#print "dirname=$dirname, basename=$basename\n";
|
|
$callback->({ data => ["Extracting rootimgtree.gz. It will take a while."] });
|
|
system("mkdir -p $dirname; cd $dirname; zcat $basename |cpio -idum; rm $basename");
|
|
}
|
|
}
|
|
|
|
if ($data->{extra}) {
|
|
|
|
# have to copy extras
|
|
print "copying extras\n" if $::VERBOSE;
|
|
|
|
#if its just a hash then there is only one entry.
|
|
if (ref($data->{extra}) eq 'HASH') {
|
|
my $ex = $data->{extra};
|
|
|
|
#my $f = basename($ex->{src});
|
|
my $ff = $ex->{src};
|
|
my $dest = $ex->{dest};
|
|
unless (moveExtra($callback, $ff, $dest, $imgdir)) {
|
|
return 0;
|
|
}
|
|
|
|
# if its an array go through each item.
|
|
} else {
|
|
foreach (@{ $data->{extra} }) {
|
|
|
|
#my $f = basename($_->{src});
|
|
my $ff = $_->{src};
|
|
my $dest = $_->{dest};
|
|
unless (moveExtra($callback, $ff, $dest, $imgdir)) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#litefile table for statelite
|
|
if ($data->{osimage}->{provmethod} eq 'statelite') {
|
|
$callback->({ data => ["Updating the litefile table."] });
|
|
my $fn = $data->{litefile};
|
|
if (!$fn) {
|
|
$callback->({ error => ["Could not find litefile.csv."], errorcode => [1] });
|
|
return 1;
|
|
} elsif (!-r $fn) {
|
|
$callback->({ error => ["Could not find $fn."], errorcode => [1] });
|
|
return 1;
|
|
}
|
|
|
|
my $lftab = xCAT::Table->new("litefile", -create => 1);
|
|
if (!$lftab) {
|
|
$callback->({ error => ["Could not open the litefile table."], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
|
|
#get current litefile table entries
|
|
my @entries = $lftab->getAllAttribsWhere(xCAT::Table::delimitcol('image') . " = 'ALL'", 'file', 'options');
|
|
|
|
#get the entries for image
|
|
open(FILE, "$fn") or die "Could not open $fn.";
|
|
foreach my $line (<FILE>) {
|
|
chomp($line);
|
|
print "$line\n";
|
|
my @tmp = split('"', $line);
|
|
my %keyhash;
|
|
my %updates;
|
|
my $filename = $tmp[3];
|
|
my $options = $tmp[5];
|
|
|
|
# If there is an entry with image as ALL for the same file,
|
|
# then no need to add the file name in the table
|
|
my $skip = 0;
|
|
foreach my $entry (@entries) {
|
|
if (($filename eq $entry->{'file'}) && ($options eq $entry->{'options'})) {
|
|
$skip = 1;
|
|
last;
|
|
}
|
|
}
|
|
|
|
if ($skip == 0) {
|
|
$keyhash{image} = $data->{osimage}->{imagename};
|
|
$keyhash{file} = $filename;
|
|
$updates{options} = $options;
|
|
$lftab->setAttribs(\%keyhash, \%updates);
|
|
}
|
|
}
|
|
close(FILE);
|
|
$lftab->commit;
|
|
|
|
$callback->({ data => ["The litetree and statelite tables are untouched. You can update them if needed."] });
|
|
}
|
|
|
|
# For s390x copy all image files from the root bundle directory to the repository location
|
|
if (($data->{osimage}->{osarch} =~ /s390x/) && ($data->{osimage}->{provmethod} =~ /raw|netboot|sysclone/)) {
|
|
my $reposImgDir = "$installroot/$data->{osimage}->{provmethod}/$data->{osimage}->{osvers}/$data->{osimage}->{osarch}/$data->{osimage}->{profile}";
|
|
mkpath($reposImgDir);
|
|
|
|
if ($data->{rawimagefiles}) {
|
|
$callback->({ data => ["Copying image files to $reposImgDir"] });
|
|
|
|
my $rif = $data->{rawimagefiles};
|
|
my $files = $rif->{files};
|
|
if (ref($files) eq 'ARRAY') {
|
|
foreach (@$files) {
|
|
my $old_file = basename($_);
|
|
my ($suffix) = $old_file =~ /(\.[^.]+)$/;
|
|
if ($suffix ne ".img") {
|
|
$suffix = ".img";
|
|
} else {
|
|
$suffix = "";
|
|
}
|
|
|
|
$callback->({ data => ["Moving $old_file to $reposImgDir as $old_file$suffix"] });
|
|
my $rc = move("$imgdir/$old_file", "$reposImgDir/$old_file$suffix");
|
|
if ($rc == 0) {
|
|
$callback->({ error => ["Could not move $old_file to $reposImgDir: $!\n"], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
my $old_file = basename($files);
|
|
my ($suffix) = $old_file =~ /(\.[^.]+)$/;
|
|
if ($suffix ne ".img") {
|
|
$suffix = ".img";
|
|
} else {
|
|
$suffix = "";
|
|
}
|
|
|
|
$callback->({ data => ["Moving $old_file to $reposImgDir as $old_file$suffix"] });
|
|
my $rc = move("$imgdir/$old_file", "$reposImgDir/$old_file$suffix");
|
|
if ($rc == 0) {
|
|
$callback->({ error => ["Could not move $old_file to $reposImgDir: $!\n"], errorcode => [1] });
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# return 1 meant everything was successful!
|
|
return 1;
|
|
}
|
|
|
|
sub copyPostscripts {
|
|
my $dirname = shift;
|
|
my $kit = shift;
|
|
my $installdir = shift;
|
|
my $callback = shift;
|
|
my $fenv = '\.env$';
|
|
my $fexlist = '\.exlist$';
|
|
|
|
if (-d "$dirname/$kit/other_files/") {
|
|
opendir(DIRP, "$dirname/$kit/other_files/");
|
|
foreach my $f (readdir(DIRP)) {
|
|
if (($f =~ m/^\./) || ($f =~ /$fexlist/i) || ($f =~ m/$fenv/i)) {
|
|
next;
|
|
} else {
|
|
print "$f\n";
|
|
chmod(0755, "$dirname/$kit/other_files/$f");
|
|
system("cp -rfv $dirname/$kit/other_files/$f $installdir/postscripts/");
|
|
}
|
|
}
|
|
|
|
closedir(DIRP);
|
|
}
|
|
}
|
|
|
|
sub movePlugin {
|
|
|
|
my $dirname = shift;
|
|
my $kit = shift;
|
|
my $callback = shift;
|
|
|
|
if (-d "$dirname/$kit/plugins/") {
|
|
chmod(644, "$dirname/$kit/plugins/*");
|
|
opendir(DIR, "$dirname/$kit/plugins/");
|
|
if (grep { ($_ ne '.') && ($_ ne '..') } readdir(DIR)) {
|
|
system("cp -rfv $dirname/$kit/plugins/* $::XCATROOT/lib/perl/xCAT_plugin/");
|
|
$hasplugin = 1;
|
|
}
|
|
closedir(DIR);
|
|
}
|
|
|
|
}
|
|
|
|
sub moveExtra {
|
|
my $callback = shift;
|
|
my $ff = shift;
|
|
my $dest = shift;
|
|
my $imgdir = shift;
|
|
my $f = basename($ff);
|
|
|
|
if (-d "$imgdir/extra/$ff") {
|
|
|
|
#print "This is a directory\n";
|
|
# this extra file is a directory, so we are moving the directory over.
|
|
$callback->({ data => ["$dest"] });
|
|
unless (-d $dest) {
|
|
unless (mkpath($dest)) {
|
|
$callback->({ error => ["Failed to create $dest"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# this could cause some problems. This is one of the reasons we may not want to
|
|
# allow copying of directories.
|
|
`cp -a -f $imgdir/extra/$ff/* $dest`;
|
|
if ($?) {
|
|
$callback->({ error => ["Failed to cp -a $imgdir/extra/$ff/* to $dest"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
} else {
|
|
|
|
#print "This is a file\n";
|
|
# this extra file is a file and we can just copy to the destination.
|
|
$callback->({ data => ["$dest/$f"] });
|
|
if (-r "$dest/$f") {
|
|
$callback->({ data => [" Moving old $dest/$f to $dest/$f.ORIG."] });
|
|
move("$dest/$f", "$dest/$f.ORIG");
|
|
}
|
|
`cp $imgdir/extra/$f $dest`;
|
|
if ($?) {
|
|
$callback->({ error => ["Failed to copy $imgdir/extra/$f to $dest"], errorcode => 1 });
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|