diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index e69de29bb..4314dfe36 100755 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -0,0 +1,568 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT::Usage; +use Getopt::Long; +use xCAT::Utils; + +#------------------------------------------------------------------------------- +=head1 xCAT::Usage +=head2 Package Description + xCAT usage module. Some commands such as rpower have different implementations + for different hardware. This module holds the usage string for these kind + of commands so that the usage can be referenced from different modules. +=cut +#------------------------------------------------------------------------------- + + +my %usage = ( + "rnetboot" => +"Usage: rnetboot [-s net|hd] [-F] [-f] [-V|--verbose] [-m table.colum==expectedstatus] [-m table.colum==expectedstatus...] [-r ] [-t ] + rnetboot [-h|--help|-v|--version] + zVM specific: + rnetboot [ipl= address]", + "rpower" => +"Usage: rpower [--nodeps] [on|onstandby|off|suspend|reset|stat|state|boot] [-V|--verbose] [-m table.colum==expectedstatus][-m table.colum==expectedstatus...] [-r ] [-t ] + rpower [-h|--help|-v|--version] + KVM Virtualization specific: + rpower [boot] [ -c ] + PPC (with IVM or HMC) specific: + rpower [--nodeps] [of] [-V|--verbose] + CEC (with HMC) specific: + rpower [on|off|reset|boot|onstandby] + LPAR(with HMC) specific: + rpower [on|off|reset|stat|state|boot|of|sms|softoff] + CEC(using Direct FSP Management) specific: + rpower [on|onstandby|off|stat|state|resetsp] + Frame(using Direct FSP Management) specific: + rpower [stat|state|rackstandby|exit_rackstandby|resetsp] + LPAR(using Direct FSP Management) specific: + rpower [on|off|reset|stat|state|boot|of|sms] + Blade(using Direct FSP Management) specific: + rpower [on|onstandby|off|cycle|state|sms] + Blade(using AMM) specific: + rpower [cycle|softoff] [-V|--verbose] + zVM specific: + rpower noderange [on|off|reset|stat|softoff] + MIC specific: + rpower noderange [stat|state|on|off|reset|boot] +", + "rbeacon" => +"Usage: rbeacon [on|off|stat] [-V|--verbose] + rbeacon [-h|--help|-v|--version]", + "rvitals" => +"Usage: + Common: + rvitals [-h|--help|-v|--version] + FSP/LPAR (with HMC) specific: + rvitals noderange {temp|voltage|lcds|all} + CEC/LPAR/Frame (using Direct FSP Management)specific: + rvitals noderange {rackenv|lcds|all} + MPA specific: + rvitals noderange {temp|voltage|wattage|fanspeed|power|leds|summary|all} + Blade specific: + rvitals noderange {temp|wattage|fanspeed|leds|summary|all} + BMC specific: + rvitals noderange {temp|voltage|wattage|fanspeed|power|leds|lcds|summary|all} + MIC specific: + rvitals noderange {thermal|all}", + "reventlog" => +"Usage: reventlog [all [-s]|clear| [-s]] [-V|--verbose] + reventlog [-h|--help|-v|--version]", + "rinv" => +"Usage: + Common: + rinv [all|model|serial] [-V|--verbose] + rinv [-h|--help|-v|--version] + BMC specific: + rinv [mprom|deviceid|uuid|guid|vpd [-t]|all [-t]] + MPA specific: + rinv [firm|bios|diag|mprom|sprom|mparom|mac|mtm [-t]] + PPC specific(with HMC): + rinv [all|bus|config|serial|model|firm [-t]] + PPC specific(using Direct FSP Management): + rinv [firm] + rinv [deconfig [-x]] + Blade specific: + rinv [all|serial|mac|bios|diag|mprom|mparom|firm|mtm [-t]] + IBM Flex System Compute Node specific: + rinv [firm] + VMware specific: + rinv + zVM specific: + rinv noderange [all|config] + MIC specific: + rinv noderange [system|ver|board|core|gddr|all]", + "rsetboot" => +"Usage: rsetboot [net|hd|cd|floppy|def|stat] [-V|--verbose] + rsetboot [-h|--help|-v|--version]", + "rbootseq" => +"Usage: + Common: + rbootseq [-h|--help|-v|--version|-V|--verbose] + Blade specific: + rbootseq [hd0|hd1|hd2|hd3|net|iscsi|usbflash|floppy|none],... + PPC (using Direct FSP Management) specific: + rbootseq [hfi|net]", + "rscan" => +"Usage: rscan [-u][-w][-x|-z] [-V|--verbose] + rscan [-h|--help|-v|--version]", + "rspconfig" => +"Usage: + Common: + rspconfig [-h|--help|-v|--version|-V|--verbose] + BMC/MPA Common: + rspconfig [snmpdest|alert|community] [-V|--verbose] + rspconfig [snmpdest=|alert=|community=] + BMC specific: + rspconfig [ip|netmask|gateway|backupgateway|garp] + rspconfig [garp=] + iDataplex specific: + rspconfig [thermprofile] + rspconfig [thermprofile=] + MPA specific: + rspconfig [sshcfg|snmpcfg|pd1|pd2|network|swnet|ntp|textid|frame] + rspconfig [textid=name] + rspconfig [frame=number] + rspconfig [USERID=passwd] [updateBMC=] + rspconfig [sshcfg=| + snmpcfg=| + pd1=| + pd2=| + network=<*|[ip],[host],[gateway],[netmask]>| + swnet=<[ip],[gateway],[netmask]>| + textid=<*>| + frame=<*>| + ntp=<[ntp],[ip],[frequency],[v3]> + FSP/CEC (using ASM Interface) Specific: + rspconfig [autopower|iocap|decfg|memdecfg|procdecfg|time|date|spdump|sysdump|network|hostname] + rspconfig autopower=| + iocap=| + decfg=:,...| + memdecfg=::::id,...| + procdecfg=::id,...| + date=| + time=| + network=<*|[ip],[host],[gateway],[netmask]>| + HMC_passwd=| + admin_passwd=| + general_passwd=| + *_passwd=| + hostname=<*|hostname> + FSP/CEC (using Direct FSP Management) Specific: + rspconfig HMC_passwd= + rspconfig admin_passwd= + rspconfig general_passwd= + rspconfig *_passwd= + rspconfig [sysname] + rspconfig [sysname=<*|name>] + rspconfig [pending_power_on_side] + rspconfig [pending_power_on_side=] + rspconfig [cec_off_policy] + rspconfig [cec_off_policy=] + rspconfig [huge_page] + rspconfig [huge_page=] + rspconfig [BSR] + rspconfig [setup_failover] + rspconfig [setup_failover=] + rspconfig [force_failover] + rspconfig --resetnet + BPA/Frame (using Direct FSP Management)specific: + rspconfig HMC_passwd= + rspconfig admin_passwd= + rspconfig general_passwd= + rspconfig *_passwd= + rspconfig [frame] + rspconfig frame=<*|frame> + rspconfig [sysname] + rspconfig [sysname=<*|name>] + rspconfig [pending_power_on_side] + rspconfig [pending_power_on_side=] + rspconfig --resetnet + HMC specific: + rspconfig [sshcfg] + rspconfig [sshcfg=] + CEC|Frame(using ASM)Specific: + rspconfig [dev|celogin1] + rspconfig [dev=]| + rspconfig [celogin1=] + ", + "getmacs" => +"Usage: + Common: + getmacs [-h|--help|-v|--version] + PPC specific: + getmacs [-F filter] + getmacs [-M] + getmacs [-V| --verbose] [-f] [-d] [--arp] | [-D {[-o] [-S server] [-G gateway] [-C client] | [--noping]}] + blade specific: + getmacs [-V| --verbose] [-d] [--arp] [-i ethN|enN] +", + "mkvm" => +"Usage: + Common: + mkvm [-h|--help|-v|--version] + For PPC(with HMC) specific: + mkvm noderange -i id -l singlenode [-V|--verbose] + mkvm noderange -c destcec -p profile [-V|--verbose] + mkvm noderange --full [-V|--verbose] + PPC (using Direct FSP Management) specific: + mkvm noderange [--full] + mkvm noderange [vmcpus=min/req/max] [vmmemory=min/req/max] + [vmphyslots=drc_index1,drc_index2...] [vmothersetting=hugepage:N,bsr:N] + [vmnics=vlan1,vlan2] [vmstorage=] [--vios] + For KVM + mkvm noderange -m|--master mastername -s|--size disksize -f|--force + For zVM + mkvm noderange directory_entry_file_path + mkvm noderange source_virtual_machine pool=disk_pool pw=multi_password", + "lsvm" => +"Usage: + Common: + lsvm [-V|--verbose] + lsvm [-h|--help|-v|--version] + PPC (with HMC) specific: + lsvm [-a|--all] + PPC (using Direct FSP Management) specific: + lsvm [-l|--long] --p775 + lsvm + zVM specific: + lsvm noderange + lsvm noderange --getnetworknames + lsvm noderange --getnetwork network_name + lsvm noderange --diskpoolnames + lsvm noderange --diskpool pool_name", + "chvm" => +"Usage: + Common: + chvm [-h|--help|-v|--version] + PPC (with HMC) specific: + chvm [-p profile][-V|--verbose] + chvm = [=...] + PPC (using Direct FSP Management) specific: + chvm --p775 [-p ] + chvm --p775 -i [-m ] -r + chvm [lparname=<*|name>] + chvm [vmcpus=min/req/max] [vmmemory=min/req/max] + [vmothersetting=hugepage:N,bsr:N] + [add_physlots=drc_index1,drc_index2...] + [add_vmnics=vlan1,vlan2] [add_vmstorage=] [--vios] + chvm [del_physlots=drc_index1,drc_index2...] + chvm [del_vadapter=slotid] + VMware specific: + chvm [-a size][-d disk][-p disk][--resize disk=size][--cpus count][--mem memory] + zVM specific: + chvm noderange [--add3390 disk_pool device_address cylinders mode read_password write_password multi_password] + chvm noderange [--add3390active device_address mode] + chvm noderange [--add9336 disk_pool virtual_device block_size mode blocks read_password write_password multi_password] + chvm noderange [--adddisk2pool function region volume group] + chvm noderange [--addnic address type device_count] + chvm noderange [--addprocessor address] + chvm noderange [--addprocessoractive address type] + chvm noderange [--addvdisk userID] device_address size] + chvm noderange [--connectnic2guestlan address lan owner] + chvm noderange [--connectnic2vswitch address vswitch] + chvm noderange [--copydisk target_address source_node source_address] + chvm noderange [--dedicatedevice virtual_device real_device mode] + chvm noderange [--deleteipl] + chvm noderange [--formatdisk disk_address multi_password] + chvm noderange [--disconnectnic address] + chvm noderange [--grantvswitch VSwitch] + chvm noderange [--removedisk virtual_device] + chvm noderange [--resetsmapi] + chvm noderange [--removediskfrompool function region group] + chvm noderange [--removenic address] + chvm noderange [--removeprocessor address] + chvm noderange [--replacevs directory_entry] + chvm noderange [--setipl ipl_target load_parms parms] + chvm noderange [--setpassword password]", + "rmvm" => +"Usage: rmvm [--service][-V|--verbose] + rmvm [-h|--help|-v|--version], + rmvm [-p] [-f] + PPC (using Direct FSP Management) specific: + rmvm ", + "lsslp" => +"Usage: lsslp [-h|--help|-v|--version] + lsslp [][-V|--verbose][-i ip[,ip..]][-w][-r|-x|-z][-n][-I][-s FRAME|CEC|MM|IVM|RSA|HMC|CMM|IMM2|FSP] + [-u] [--range IPranges][-t tries][--vpdtable][-C counts][-T timeout]", + "switchdiscover" => +"Usage: switchdiscover [-h|--help|-v|--version] + switchdiscover [][-V|--verbose][-i adpt[,adpt..]][-w][-r|-x|-z][-n][-s scan_methods]", + "rflash" => +"Usage: + rflash [ -h|--help|-v|--version] + PPC (with HMC) specific: + rflash -p [--activate concurrent | disruptive][-V|--verbose] + rflash [--commit | --recover] [-V|--verbose] + PPC (using Direct FSP Management) specific: + rflash -p --activate [-d ] + rflash [--commit | --recover] [-V|--verbose] + rflash [--bpa_acdl]", + "mkhwconn" => +"Usage: + mkhwconn [-h|--help] + + PPC (with HMC) specific: + mkhwconn noderange -t [--bind] [-V|--verbose] + mkhwconn noderange -p single_hmc [-P HMC passwd] [-V|--verbose] + + PPC (using Direct FSP Management) specific: + mkhwconn noderange -t [-T tooltype] [--port port_value] + mkhwconn noderange -s [hmcnode] [-P HMC passwd] [-V|--verbose]", + "rmhwconn" => +"Usage: + rmhwconn [-h|--help] + + PPC (with HMC) specific: + rmhwconn noderange [-V|--verbose] + + PPC (using Direct FSP Management) specific: + rmhwconn noderange [-T tooltype] + rmhwconn noderange -s", + "lshwconn" => +"Usage: + lshwconn [-h|--help] + + PPC (with HMC) specific: + lshwconn noderange [-V|--verbose] + + PPC (using Direct FSP Management) specific: + lshwconn noderange [-T tooltype] + lshwconn noderange -s", + "renergy" => +"Usage: + renergy [-h | --help] + renergy [-v | --version] + + Power 6 server specific : + renergy noderange [-V] { all | { [savingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] } } + renergy noderange [-V] { {savingstatus}={on | off} | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } + + Power 7 server specific : + renergy noderange [-V] { all | { [savingstatus] [dsavingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue] } } + renergy noderange [-V] { {savingstatus}={on | off} | {dsavingstatus}={on-norm | on-maxp | off} | {fsavingstatus}={on | off} | {ffovalue}=MHZ | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } + + Power 8 server specific : + renergy noderange [-V] { all | [savingstatus] [dsavingstatus] [averageAC] [averageAChistory] [averageDC] [averageDChistory] [ambienttemp] [ambienttemphistory] [exhausttemp] [exhausttemphistory] [fanspeed] [fanspeedhistory] [CPUspeed] [CPUspeedhistory] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue]} + renergy noderange [-V] { savingstatus={on | off} | dsavingstatus={on-norm | on-maxp | off} | fsavingstatus={on | off} | ffovalue=MHZ } + + BladeCenter specific : + For Management Modules: + renergy noderange [-V] { all | pd1all | pd2all | [pd1status] [pd2status] [pd1policy] [pd2policy] [pd1powermodule1] [pd1powermodule2] [pd2powermodule1] [pd2powermodule2] [pd1avaiablepower] [pd2avaiablepower] [pd1reservedpower] [pd2reservedpower] [pd1remainpower] [pd2remainpower] [pd1inusedpower] [pd2inusedpower] [availableDC] [averageAC] [thermaloutput] [ambienttemp] [mmtemp] } + For a blade server nodes: + renergy noderange [-V] { all | [averageDC] [capability] [cappingvalue] [CPUspeed] [maxCPUspeed] [savingstatus] [dsavingstatus] } + renergy noderange [-V] { savingstatus={on | off} | dsavingstatus={on-norm | on-maxp | off} } + + Flex specific : + For Flex Management Modules: + renergy noderange [-V] { all | [powerstatus] [powerpolicy] [powermodule] [avaiablepower] [reservedpower] [remainpower] [inusedpower] [availableDC] [averageAC] [thermaloutput] [ambienttemp] [mmtemp] } + + For Flex node (power and x86): + renergy noderange [-V] { all | [averageDC] [capability] [cappingvalue] [cappingmaxmin] [cappingmax] [cappingmin] [cappingGmin] [CPUspeed] [maxCPUspeed] [savingstatus] [dsavingstatus] } + renergy noderange [-V] { cappingstatus={on | off} | cappingwatt=watt | cappingperc=percentage | savingstatus={on | off} | dsavingstatus={on-norm | on-maxp | off} } + + iDataPlex specific : + renergy noderange [-V] [ { cappingmaxmin | cappingmax | cappingmin } ] [cappingstatus] [cappingvalue] [relhistogram] + renergy noderange [-V] { cappingstatus={on | enable | off | disable} | {cappingwatt|cappingvalue}=watt }", + "updatenode" => +"Usage: + updatenode [-h|--help|-v|--version | -g|--genmypost] + or + updatenode [-V|--verbose] [-k|--security] [-s|--sn] [-t ] + or + updatenode [-V|--verbose] [-F|--sync | -f|--snsync] [-l|--user[username]] [--fanout=[fanout value]] [-S|--sw] [-t ] + [-P|--scripts [script1,script2,...]] [-s|--sn] + [-A|--updateallsw] [-c|--cmdlineonly] [-d alt_source_dir] + [attr=val [attr=val...]] + or + updatenode [-V|--verbose] [script1,script2,...] + +Options: + A list of nodes or groups. + + [-k|--security] Update the security keys and certificates for the + target nodes. + + [-F|--sync] Perform File Syncing. + + [--fanout] Allows you to assign the fanout value for the command. + See xdsh/xdcp fanout parameter in the man page. + + [-f|--snsync] Performs File Syncing to the service nodes that service + the nodes in the noderange. + + [-g|--genmypost] Will generate a new mypostscript file for the + the nodes in the noderange, if site precreatemypostscripts is 1 or YES. + + [-l|--user] User name to run the updatenode command. It overrides the + current user which is the default. + + [-S|--sw] Perform Software Maintenance. + + [-P|--scripts] Execute postscripts listed in the postscripts table or + parameters. + + [-c|--cmdlineonly] Only use AIX software maintenance information + provided on the command line. (AIX only) + + [-s|--sn] Set the server information stored on the nodes. + + [-t|--timeout] Time out in seconds to allow the command to run. Default is no timeout, + except for updatenode -k which has a 10 second default timeout. + + [-A|--updateallsw] Install or update all software contained in the source + directory. (AIX only) + + [-d ] Used to indicate a source directory other than + the standard lpp_source directory specified in the xCAT osimage + definition. (AIX only) + + [script1,script2,...] A comma separated list of postscript names. + If omitted, all the post scripts defined for the nodes will be run. + + [attr=val [attr=val...]] Specifies one or more 'attribute equals value' + pairs, separated by spaces. (AIX only)", + "lsflexnode" => +"Usage: + lsflexnode [-h|--help|-v|--version] + lsflexnode ", + "mkflexnode" => +"Usage: + mkflexnode [-h|--help|-v|--version] + mkflexnode ", + "nodeset" => +"Usage: + Common: + nodeset [-h|--help|-v|--version] + nodeset [shell|boot|runcmd=bmcsetup|iscsiboot|osimage[=]|offline]", + "rmflexnode" => +"Usage: + rmflexnode [-h|--help|-v|--version] + rmflexnode ", + "lsve" => +"Usage: + lsve [-t type] [-m manager] [-o object] + -t: dc - 'Data Center', cl - 'Cluster', sd - 'Storage Domain', nw - 'Network', tpl -'Template' + -m: FQDN (Fully Qualified Domain Name) of the rhev manager + -o: Target object to display", + "cfgve" => +"Usage: + cfgve -t dc -m manager -o object [-c -k nfs|localfs | -r] + cfgve -t cl -m manager -o object [-c -p cpu type | -r -f] + cfgve -t sd -m manager -o object [-c | -g | -s | -a | -b | -r -f] + -t: sd - 'Storage Domain', nw - 'Network', tpl -'Template' + -m: FQDN (Fully Qualified Domain Name) of the rhev manager + -o: Target object to configure + cfgve -t nw -m manager -o object [-c -d data center -n vlan ID | -a -l cluster| -b | -r] + cfgve -t tpl -m manager -o object [-r]", + "chhypervisor" => +"Usage: + chhypervisor noderange [-a | -n | -p | -e | -d | -h]", + "rmhypervisor" => +"Usage: + rmhypervisor noderange [-f | -h]", + "clonevm" => +"Usage: + clonevm noderange [-t createmaster -f | -b basemaster -d | -h]", +); +my $vers = xCAT::Utils->Version(); +my %version = ( + "rnetboot" => "$vers", + "rpower" => "$vers", + "rbeacon" => "$vers", + "rvitals" => "$vers", + "reventlog" => "$vers", + "rinv" => "$vers", + "rsetboot" => "$vers", + "rbootseq" => "$vers", + "rscan" => "$vers", + "rspconfig" => "$vers", + "getmacs" => "$vers", + "mkvm" => "$vers", + "lsvm" => "$vers", + "chvm" => "$vers", + "rmvm" => "$vers", + "lsslp" => "$vers", + "switchdiscover" => "$vers", + "rflash" => "$vers", + "renergy" => "$vers", + "lsflexnode" => "$vers", + "mkflexnode" => "$vers", + "rmflexnode" => "$vers", + "nodeset" => "$vers", + "lsve" => "$vers", + "cfgve" => "$vers", + "chhypervisor" => "$vers", + "rmhypervisor" => "$vers", + "clonevm" => "$vers", +); + +#-------------------------------------------------------------------------------- +=head3 getUsage + It returns the usage string for the given command. + Arguments: + command + Returns: + the usage string for the command. +=cut +#------------------------------------------------------------------------------- +sub getUsage { + my ($class, $command)=@_; + if (exists($usage{$command})) { return $usage{$command};} + else { return "Usage for command $command cannot be found\n"; } +} + +#-------------------------------------------------------------------------------- +=head3 getVersion + It returns the version string for the given command. + Arguments: + command + Returns: + the version string for the command. +=cut +#------------------------------------------------------------------------------- +sub getVersion { + my ($class, $command)=@_; + if (exists($version{$command})) { return $version{$command};} + else { return "Version string for command $command cannot be found\n"; } +} + +#-------------------------------------------------------------------------------- +=head3 parseCommand + This function parses the given command to see if the usage or version string + need to be returned. + Arguments: + command + arguments + Returns: + the usage or the version string for the command. The caller need to display the + string and then exit. + none, if no usage or version strings are needed. The caller can keep going. +=cut +#------------------------------------------------------------------------------- +sub parseCommand { + my $command=shift; + if ($command =~ /xCAT::Usage/) { $command=shift; } + my @exargs=@_; + + @ARGV=@exargs; + + #print "command=$command, args=@exargs, ARGV=@ARGV\n"; + + Getopt::Long::Configure('pass_through','no_ignore_case'); + + # parse the options + if(!GetOptions( + 'h|help' => \$::HELP, + 'v|version' => \$::VERSION)) { + + return ""; + } + + if ($::HELP) { return xCAT::Usage->getUsage($command); } + if ($::VERSION) { return xCAT::Usage->getVersion($command); } + + return ""; +} + diff --git a/xCAT-client/bin/genimage b/xCAT-client/bin/genimage index e69de29bb..aa61089b9 100755 --- a/xCAT-client/bin/genimage +++ b/xCAT-client/bin/genimage @@ -0,0 +1,454 @@ +#!/usr/bin/perl +BEGIN +{ + $::XCATROOT = + $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} + : -d '/opt/xcat' ? '/opt/xcat' + : '/usr'; +} +use lib "$::XCATROOT/lib/perl"; +use strict; + +use Getopt::Long; +require xCAT::MsgUtils; +require xCAT::DSHCLI; +require xCAT::Client; + +use xCAT::Utils; +use xCAT::TableUtils; +use File::Basename; + +$::XCATROOT = "/opt/xcat"; +my $os = ""; +my $profile = ""; +my $interface = ""; +my $version; +my $drivers = ""; +my $otherInterfaces = ""; +my $kernel = ""; +my @oses; # available OSes. +my @profiles; # available profiles +my $profDir; # root where you do ./genimage from +my $help; +my $match = 0; +my $imagename; +my $arch; +my $permission; +my $rootlimit; +my $tmplimit; +my $krpmver; +my $kerneldir; +my $mode; +my $interactive; +my $onlyinitrd; +my $dryrun; +my $ignorekernelchk; +my $noupdate; +#----------------------------------------------------------------------------- + +=head3 print_usage - usage message + +=cut + +#----------------------------------------------------------------------------- +sub print_usage +{ + print "Usage:\n"; + print " genimage\n\n"; + print " genimage --dryrun\n\n"; + print ' genimage [-o ] [-a ] [-p ] [-i ] [-n ] [--onlyinitrd] [-r ] [-k ] [-g ] [-m statelite] [-l rootlimitsize] [-t tmplimitsize] [--permission ] [--interactive] [--dryrun] [--noupdate] '."\n\n"; + print " --permission is used for statelite only\n"; + print " -g is used for SLES only\n\n"; + print " -m is used for urbuntu, debian and fedora12 only\n\n"; + print "Examples:\n"; + print " genimage\n"; + print " genimage --interactive\n"; + print " genimage -i eth0 -n tg3 --interactive myimagename\n"; + print " genimage myimagename\n"; +} + +if (!GetOptions( + 'a=s' => \$arch, + 'p=s' => \$profile, + 'o=s' => \$os, + 'n=s' => \$drivers, + 'i=s' => \$interface, + 'r=s' => \$otherInterfaces, + 'l=s' => \$rootlimit, + 't=s' => \$tmplimit, + 'k=s' => \$kernel, + 'g=s' => \$krpmver, + 'm=s' => \$mode, + 'permission=s' => \$permission, + 'kerneldir=s' => \$kerneldir, + 'interactive' => \$interactive, + 'onlyinitrd' => \$onlyinitrd, + 'dryrun' => \$dryrun, + 'ignorekernelchk' => \$ignorekernelchk, + 'noupdate' => \$noupdate, + 'h|help' => \$help, + 'v|version' => \$version, +)) { + &print_usage; + exit 1; +} + +if($help){ + &print_usage; + exit 0; +} + +if ($version){ + my $version = xCAT::Utils->Version(); + xCAT::MsgUtils->message("N", $version); + exit 0; + +} +if (@ARGV > 0) { + $imagename=$ARGV[0]; +} + +if ((!$imagename) && (!$profile) && (!$os) && (!$arch)) { + my $tmpimgs=`lsdef -t osimage -w provmethod=~'/statelite|netboot/' |cut -d' ' -f1`; + if ($? == 0) { + if (($tmpimgs) && ($tmpimgs !~ /^Could/)) { #Could is returned when the osimage table is empty + my @images=split('\n', $tmpimgs); + print "Do you want to re-generate an existing image from the osimage table? "; + print "[y/n] "; + my $conf = ; + chomp($conf); + if($conf ne "" && $conf !~/N|n|[Nn][Oo]/) { + $match = 0; + while(1){ + print "Available images: \n"; + foreach(sort @images){ + print " $_\n"; + } + # default is the first image + print "Which image do you want to re-generate? ["; + print $images[0]; + print "] "; + + my $img = ; + chomp($img); + if($img eq ""){ + $imagename = $images[0]; + last; + } + foreach(@images){ + if($img eq $_){ + $imagename=$img; + $match = 1; + } + } + if ($match) { + last; + } else { + print "$img is not found in the osimage table.\n"; + } + } + } + } + } +} + + +# get the install directory +my @entries = xCAT::TableUtils->get_site_attribute("installdir"); +my $installdir = $entries[0]; +chomp($installdir); +# lots of error checking to make sure it exists. +if($installdir eq ''){ + print "Could not get install directory from site table. Assuming your OSes are stored in '/install'\n"; + $installdir = "/install"; +} + +unless(-d $installdir){ + print "The directory where your OS distributions resides: $installdir does not exist. Please check site table\n"; + exit 1; +} + +if ((!$imagename) && (!$os)){ + my @dircontents = `ls $installdir`; + chomp(@dircontents); + foreach (@dircontents) { + # SL matches Scientific Linux, sl matches sles amd sled + if($_ =~ /(rhel|fedora|SL|centos|sl|suse)/){ + push @oses,$_; + } + } + + if($#oses eq -1){ + print "There are no OS repositories in $installdir. Please run copycds for the OS first.\n"; + exit 1; + } + + # now they have the OSes, make sure they select one that is available + $match = 0; + while(1){ + print "Available OSes: \n"; + foreach(@oses){ + print " $_\n"; + } + # default is the first OS cause in many cases, they'll only have 1. + print "Which OS do you want to build a image for? ["; + print $oses[0] ; + print "] "; + + $os = ; + chomp($os); + if($os eq ""){ + $os = $oses[0]; + last; + } + foreach(@oses){ + if($os eq $_){ + $match = 1; + } + } + + if($match){ + last; + }else{ + print "$os is not found in '$installdir'\n"; + } + } + + chomp($os); +} +if ($os) { print " OS: $os\n"; } + + + +### Get the Profile #### +my $osfamily = $os; +$osfamily =~ s/\d+//g; +$osfamily =~ s/\.//g; +if($osfamily =~ /rh/){ + $osfamily = "rh"; +} + +# OS version on s390x can contain 'sp', e.g. sles11sp1 +# If the $osfamily contains 'sles' and 'sp', the $osfamily = sles +if ($osfamily =~ /sles/ && $osfamily =~ /sp/) { + $osfamily = "sles"; +} + +#print "OSfamily: $osfamily\n"; +$profDir = "$::XCATROOT/share/xcat/netboot/$osfamily"; +unless(-d $profDir){ + print "Unable to find genimage script in $profDir\n"; + exit 1; +} + +if ((!$imagename) && (!$profile)){ + my $profDir2 = "$installdir/custom/netboot/$osfamily"; + my @proList = `ls $profDir/*.pkglist`; + if (-d $profDir2) { + @proList = (@proList, `ls $profDir2/*.pkglist`); + } + my %seen = (); + foreach (@proList) { + my $f = basename($_); + $f =~ s/([^\.]*).*/$1/; + chomp($f); + $seen{$f}++; + } + @profiles = sort keys %seen; + if($#profiles eq -1){ + print "There are no profiles in $::XCATROOT/share/xcat/netboot/$osfamily.\n"; + exit 1; + } + $match = 0; + while(1){ + print "Available Profiles for $os: \n"; + foreach(@profiles){ + print " $_\n"; + } + # default is the first OS cause in many cases, they'll only have 1. + print "Which profile do you want to use for $os? ["; + print "$profiles[0] "; + print "] "; + + $profile = ; + chomp($profile); + if($profile eq ""){ + $profile = $profiles[0]; + last; + } + foreach(@profiles){ + if($profile eq $_){ + $match = 1; + } + } + if($match eq 1){ + last; + } + } +} +if ($profile) { print " Profile: $profile\n"; } + +# get the interface +if ((!$imagename) && (!$interface)){ + while(1){ + print "OPTIONAL: Which specific network interface will the image boot from? []"; + $interface = ; + chomp($interface); + if($interface eq ""){ + last; + }else{ + print "You want your stateless machines to boot off of "; + print "$interface"; + print "? "; + print "[Y/n] "; + my $conf = ; + chomp($conf); + if($conf eq ""){ + last; + } + if($conf =~ /Y|y|[Yy][Ee][Ss]/){ + last; + } + } + } + if ($interface) { print " Interface: $interface\n"; } + else { print " No interface specified. The interface will be determined at network boot time.\n"; } +} + + + +print "Generating image: \n"; +my @arg; +if ($imagename) { + push @arg, "$imagename"; +} + +if ($interface) { + push @arg, "-i"; + push @arg, "$interface"; +} +if ($drivers) { + push @arg, "-n"; + push @arg, "$drivers"; +} +if ($os) { + push @arg, "-o"; + push @arg, "$os"; +} +if ($profile) { + push @arg, "-p"; + push @arg, "$profile"; +} +if ($arch) { + push @arg, "-a"; + push @arg, "$arch"; +} + +if ($kernel) { + push @arg, "-k"; + push @arg, "$kernel"; +} + +if($otherInterfaces){ + push @arg, "-r"; + push @arg, "$otherInterfaces"; +} + +if ($permission) { + push @arg, "--permission"; + push @arg, "$permission"; +} + +if ($rootlimit) { + push @arg, "-l"; + push @arg, "$rootlimit"; +} + +if($tmplimit) { + push @arg, "-t"; + push @arg, "$tmplimit"; +} + +if ($krpmver) { + push @arg, "-g"; + push @arg, "$krpmver"; +} + +if ($mode) { + push @arg, "-m"; + push @arg, "$mode"; +} + +if ($kerneldir) { + push @arg, "--kerneldir"; + push @arg, "$kerneldir"; +} + +my $tempfile="/tmp/xcat_genimage.$$"; +push @arg, "--tempfile"; #this is the file that contains the output +push @arg, "$tempfile"; + +if ($interactive) { + push @arg, "--interactive"; +} + + +if ($onlyinitrd) { + push @arg, "--onlyinitrd"; +} + +if ($dryrun) { + push @arg, "--dryrun"; +} + +if ($ignorekernelchk) { + push @arg, "--ignorekernelchk"; +} + +if ($noupdate) { + push @arg, "--noupdate"; +} + +my $cmdref; +push (@{$cmdref->{arg}}, @arg); +$cmdref->{command}->[0] = "genimage"; + +if (!$interactive) { + xCAT::Client::submit_request($cmdref, \&xCAT::Client::handle_response); + exit $xCAT::Client::EXITCODE; +} else { + if ($dryrun) { exit 0; } + #when in interactive mode, first call genimage.pm get info from DB, + xCAT::Client::submit_request($cmdref, \&xCAT::Client::handle_response); + + #then call the specific genimage under /opt/xcat/share... + if (-f $tempfile) { + my $cmdname; + #read the command name + open(FILE1, "<$tempfile"); + my @output = ; + if (@output >0) { + $cmdname=$output[0]; + } else { + close FILE1; + return 1; + } + close FILE1; + + # run the specific genimage command + #print "cmdname=$cmdname\n"; + system("$cmdname"); + + #then call genimage.pm to save the DB + my @arg1; + push @arg1, $tempfile; + my $request; + push (@{$request->{arg}}, @arg1); + $request->{command}->[0] = "saveimgdata"; + xCAT::Client::submit_request($request, \&xCAT::Client::handle_response); + exit $xCAT::Client::EXITCODE; + } else { + exit 1; + } +} + diff --git a/xCAT-server/lib/xcat/plugins/imgcapture.pm b/xCAT-server/lib/xcat/plugins/imgcapture.pm index e69de29bb..6bb88b83e 100755 --- a/xCAT-server/lib/xcat/plugins/imgcapture.pm +++ b/xCAT-server/lib/xcat/plugins/imgcapture.pm @@ -0,0 +1,846 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT_plugin::imgcapture; + +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +use lib "$::XCATROOT/lib/perl"; + +use strict; + +use Data::Dumper; # for debug purpose +use Getopt::Long; +use xCAT::MsgUtils; +use xCAT::Utils; +use xCAT::TableUtils; +use xCAT::SvrUtils; +use xCAT::Table; +use File::Path qw(mkpath); + +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + +my $verbose = 0; +my $installroot = "/install"; +my $sysclone_home = $installroot . "/sysclone"; + +sub handled_commands { + return { "imgcapture" => "imgcapture" }; +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + + my $node; + if (exists $request->{node}) { + $node = $request->{node}->[0]; + } + + $installroot = xCAT::TableUtils->getInstallDir(); + @ARGV = @{$request->{arg}} if (defined $request->{arg}); + my $argc = scalar @ARGV; + + my $usage = "Usage:\n imgcapture -t|--type {diskless|sysclone} -o|--osimage [-V | --verbose] \n imgcapture [-h|--help] \n imgcapture [-v|--version]"; + + my $os; + my $arch; + my $device; + my $profile; + my $bootif; + my $netdriver; + my $osimg; + my $help; + my $version; + my $type; + + GetOptions( + "profile|p=s" => \$profile, + "i=s" => \$bootif, + 'n=s' => \$netdriver, + 'osimage|o=s' => \$osimg, + "device|d=s" => \$device, + "help|h" => \$help, + "version|v" => \$version, + "verbose|V" => \$verbose, + "type|t=s" => \$type + ); + + if ( defined( $ARGV[0] )) { + my $rsp = {}; + $rsp->{data}->[0] = "Invalid Argument: $ARGV[0]"; + $rsp->{data}->[1] = $usage; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if($version) { + my $version = xCAT::Utils->Version(); + my $rsp = {}; + $rsp->{data}->[0] = $version; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if($help) { + my $rsp = {}; + $rsp->{data}->[0] = $usage; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if( ! $node ) { + my $rsp = {}; + $rsp->{data}->[0] = $usage; + xCAT::MsgUtils->message("D", $rsp, $callback); + return 0; + } + + if(($type =~ /sysclone/) && (!$osimg)){ + my $rsp = {}; + push @{$rsp->{data}}, "You must specify osimage name if you are using \"sysclone\"."; + push @{$rsp->{data}}, $usage; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + my $nodetypetab = xCAT::Table->new("nodetype"); + my $ref_nodetype = $nodetypetab->getNodeAttribs($node, ['os','arch','profile']); + $os = $ref_nodetype->{os}; + $arch = $ref_nodetype->{arch}; + unless($profile) { + $profile = $ref_nodetype->{profile}; + } + + # sysclone + unless($type =~ /diskless/) + { + # Handle image capture separately for s390x + if ($arch eq 's390x') { + eval { require xCAT_plugin::zvm; }; # Load z/VM plugin dynamically + xCAT_plugin::zvm->imageCapture($callback, $node, $os, $arch, $profile, $osimg, $device); + return; + } + + my $shortname = xCAT::InstUtils->myxCATname(); + + my $rc; + $rc = sysclone_configserver($shortname, $osimg, $callback, $doreq); + if($rc){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Can not configure Imager Server on $shortname.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + $rc = sysclone_prepclient($node, $shortname, $osimg, $callback, $doreq); + if($rc){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Can not prepare Golden Client on $node.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + $rc = sysclone_getimg($node, $shortname, $osimg, $callback, $doreq); + if($rc){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Can not get image $osimg from $node.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + $rc = sysclone_createosimgdef($node, $shortname, $osimg, $callback, $doreq); + if($rc){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Can not create osimage definition for $osimg on $shortname.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + return; + } + + # -i flag is required with sles genimage + if (!$bootif && $os =~ /^sles/) { + $bootif = "eth0"; + } + + # check whether the osimage exists or not + if($osimg) { + my $osimgtab=xCAT::Table->new('osimage', -create=>1); + unless($osimgtab) { + # the osimage table doesn't exist + my $rsp = {}; + $rsp->{data}->[0] = qq{Cannot open the osimage table}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + my $linuximgtab = xCAT::Table->new('linuximage', -create=>1); + unless($linuximgtab) { + # the linuximage table doesn't exist + my $rsp = {}; + $rsp->{data}->[0] = qq{Cannot open the linuximage table}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + my ($ref) = $osimgtab->getAttribs({imagename => $osimg}, 'osvers', 'osarch', 'profile'); + unless($ref) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Cannot find $osimg from the osimage table.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + my ($ref1) = $linuximgtab->getAttribs({imagename => $osimg}, 'imagename'); + unless($ref1) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Cannot find $osimg from the linuximage table}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + # make sure the "osvers" and "osarch" attributes match the node's attribute + unless($os eq $ref->{'osvers'} and $arch eq $ref->{'osarch'}) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The 'osvers' or 'osarch' attribute of the "$osimg" table doesn't match the node's attribute}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + } + + imgcapture($node, $os, $arch, $profile, $osimg, $bootif, $netdriver, $callback, $doreq); +} + +sub imgcapture { + my ($node, $os, $arch, $profile, $osimg, $bootif, $netdriver, $callback, $subreq) = @_; + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = "nodename is $node; os is $os; arch is $arch; profile is $profile"; + $rsp->{data}->[1] = "bootif is $bootif; netdriver is $netdriver"; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # make sure the "/" partion is on the disk, + my $output = xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg =>["stat / -f |grep Type"]}, $subreq, -1, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of "stat / -f |grep Type" on $node is:}; + foreach my $o (@$output) { + push @{$rsp->{data}}, $o; + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run on the $node}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # parse the output of "stat / -f |grep Type", + $output->[0] =~ m/Type:\s+(.*)$/; + my $fstype = $1; + if ($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The file type is $fstype}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # make sure the rootfs type is not nfs or tmpfs + if($fstype eq "nfs" or $fstype eq "tmpfs") { + my $rsp = {}; + $rsp->{data}->[0] = qq{This node might not be diskful Linux node, please check it.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + my $distname = $os; + while ( $distname and ( ! -r "$::XCATROOT/share/xcat/netboot/$distname/") ) { + chop($distname); + } + + unless($distname) { + $callback->({error=>["Unable to find $::XCATROOT/share/xcat/netboot directory for $os"], errorcode => [1]}); + return; + } + + my $exlistloc = xCAT::SvrUtils->get_imgcapture_exlist_file_name("$installroot/custom/netboot/$distname", $profile, $os, $arch); + unless ($exlistloc) { + $exlistloc = xCAT::SvrUtils->get_imgcapture_exlist_file_name("$::XCATROOT/share/xcat/netboot/$distname", $profile, $os, $arch); + } + + my $xcat_imgcapture_tmpfile = "/tmp/xcat_imgcapture.$$"; + + my $excludestr = "cd /; find ."; + + if($exlistloc) { + my $exlist; + open $exlist, "<", $exlistloc; + + while(<$exlist>) { + $_ =~ s/^\s+//; + chomp $_; + unless($_ =~ m{^#}) { + $excludestr .= qq{ ! -path "$_"}; + } + } + + close $exlist; + } else { + # the following directories must be exluded when capturing the image + my @default_exlist = ("./tmp/*", "./proc/*", "./sys/*", "./dev/*", "./xcatpost/*", "./install/*"); + foreach my $item (@default_exlist) { + $excludestr .= qq{ ! -path "$item"}; + } + } + + $excludestr .= " |cpio -H newc -o |gzip -c - >$xcat_imgcapture_tmpfile"; + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{The excludestr is "$excludestr"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # run the command via "xdsh" + + xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg => ["echo -n >$xcat_imgcapture_tmpfile"]}, $subreq, -1, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{running "echo -n > $xcat_imgcapture_tmpfile" on $node}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { # the xdsh command fails + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run "echo -n > $xcat_imgcapture_tmpfile" on $node}; + xCAT:MsgUtils->message("E", $rsp, $callback); + return; + } + + my $rsp = {}; + $rsp->{data}->[0] = qq{Capturing image on $node...}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{running "$excludestr" on $node via the "xdsh" command}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg => [$excludestr]}, $subreq, -1, 1); + + if($::RUNCMD_RC) { # the xdsh command fails + my $rsp = {}; + $rsp->{data}->[0] = qq{The "xdsh" command fails to run "$excludestr" on $node}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + $rsp = {}; + $rsp->{data}->[0] = qq{Transfering the image captured on $node back...}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + # copy the image captured on $node back via the "scp" command + xCAT::Utils->runcmd("scp $node:$xcat_imgcapture_tmpfile $xcat_imgcapture_tmpfile"); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Running "scp $node:$xcat_imgcapture_tmpfile $xcat_imgcapture_tmpfile"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC) { + my $rsp ={}; + $rsp->{data}->[0] = qq{The scp command fails}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + xCAT::Utils->runxcmd({command => ["xdsh" ], node => [$node], arg => ["rm -f $xcat_imgcapture_tmpfile"]}, $subreq, -1, 1); + + # extract the $xcat_imgcapture_tmpfile file to /install/netboot/$os/$arch/$profile/rootimg + my $rootimgdir = "$installroot/netboot/$os/$arch/$profile/rootimg"; + + # empty the rootimg directory before extracting the image captured on the diskful Linux node + if( -d $rootimgdir ) { + unlink $rootimgdir; + } + mkpath($rootimgdir); + + xCAT::Utils->runcmd("cd $rootimgdir; gzip -cd $xcat_imgcapture_tmpfile|cpio -idum"); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Extracting the image to $rootimgdir}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + if($::RUNCMD_RC) { + my $rsp = {}; + $rsp->{data}->[0] = qq{fails to run the "gzip -cd xx |cpio -idum" command}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{Creating the spots exluded when capturing on $node...}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + # the next step is to call "genimage" + my $platform = getplatform($os); + if( -e "$::XCATROOT/share/xcat/netboot/$platform/genimage" ) { + my $cmd; + + if( $osimg ) { + $cmd = "$::XCATROOT/bin/genimage $osimg "; + } else { + $cmd = "$::XCATROOT/share/xcat/netboot/$platform/genimage -o $os -a $arch -p $profile "; + } + + if($bootif) { + $cmd .= "-i $bootif "; + } + if($netdriver) { + $cmd .= "-n $netdriver"; + } + + my $rsp = {}; + $rsp->{data}->[0] = qq{Generating kernel and initial ramdisks...}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{"The genimage command is: $cmd"}; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + my @cmdoutput = xCAT::Utils->runcmd($cmd, 0); + if($::RUNCMD_RC) { + my $rsp = {}; + foreach (@cmdoutput) { + push @{$rsp->{data}}, $_; + } + xCAT::MsgUtils->message("E", $rsp, $callback); + unlink $xcat_imgcapture_tmpfile; + return; + } + } else { + my $rsp = {}; + $rsp->{data}->[0] = qq{Can't run the "genimage" command for $os}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + my $rsp = {}; + $rsp->{data}->[0] = qq{Done.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + unlink $xcat_imgcapture_tmpfile; + + return 0; +} + +sub getplatform { + my $os = shift; + my $platform; + if ($os =~ m/rh.*/) { + $platform = "rh"; + } elsif ($os =~ m/centos.*/) { + $platform = "centos"; + } elsif ($os =~ m/fedora.*/) { + $platform = "fedora"; + } elsif ($os =~ m/SL.*/) { + $platform = "SL"; + } elsif ($os =~ m/sles.*/) { + $platform = "sles"; + } elsif ($os =~ m/suse.*/) { + $platform = "suse"; + } + + return $platform; +} + +sub sysclone_configserver{ + my ($server, $osimage, $callback, $subreq) = @_; + + # check if systemimager is installed on the imager server + my $rsp = {}; + $rsp->{data}->[0] = qq{Checking if systemimager packages are installed on $server.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + my $cmd = "rpm -qa|grep systemimager-server"; + my $output = xCAT::Utils->runcmd("$cmd", -1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of $cmd on $server is:}; + push @{$rsp->{data}}, $output; + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC != 0) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{systemimager-server is not installed on the $server.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # update /etc/systemimager/systemimager.conf + my $rc = `sed -i "s/\\/var\\/lib\\/systemimager/\\/install\\/sysclone/g" /etc/systemimager/systemimager.conf`; + if (!(-e $sysclone_home)) + { + mkpath($sysclone_home); + } + + my $sysclone_images = $sysclone_home . "/images"; + if (!(-e $sysclone_images)) + { + mkpath($sysclone_images); + } + + my $sysclone_scripts = $sysclone_home . "/scripts"; + if (!(-e $sysclone_scripts)) + { + mkpath($sysclone_scripts); + } + + my $sysclone_overrides = $sysclone_home . "/overrides"; + if (!(-e $sysclone_overrides)) + { + mkpath($sysclone_overrides); + } + + my $imagedir; + my $osimgtab = xCAT::Table->new('osimage'); + my $entry = ($osimgtab->getAllAttribsWhere("imagename = '$osimage'", 'ALL' ))[0]; + if(!$entry){ + $imagedir = $sysclone_home . "/images/" . $osimage; + }else{ + my $osimagetab = xCAT::Table->new('linuximage'); + my $osimageentry = $osimagetab->getAttribs({imagename => $osimage}, 'rootimgdir'); + if($osimageentry){ + $imagedir = $osimageentry->{rootimgdir}; + if (!(-e $imagedir)){ + mkpath($imagedir); + } + }else{ + $imagedir = $sysclone_home . "/images/" . $osimage; + $cmd = "chdef -t osimage $osimage rootimgdir=$imagedir"; + $rc = `$cmd`; + } + } + + $imagedir =~ s/^(\/.*)\/.+\/?$/$1/; + $imagedir =~ s/\//\\\\\//g; + $imagedir = "DEFAULT_IMAGE_DIR = ".$imagedir; + + my $olddir = `more /etc/systemimager/systemimager.conf |grep DEFAULT_IMAGE_DIR`; + $olddir =~ s/\//\\\\\//g; + chomp($olddir); + + $cmd= "sed -i \"s/$olddir/$imagedir/\" /etc/systemimager/systemimager.conf"; + $rc = `$cmd`; + + # update /etc/systemimager/rsync_stubs/10header to generate new /etc/systemimager/rsyncd.conf + $rc = `sed -i "s/\\/var\\/lib\\/systemimager/\\/install\\/sysclone/g" /etc/systemimager/rsync_stubs/10header`; + $rc = `export PERL5LIB=/usr/lib/perl5/site_perl/;LANG=C si_mkrsyncd_conf`; + + return 0; +} + +sub sysclone_prepclient { + my ($node, $server, $osimage, $callback, $subreq) = @_; + + # check if systemimager is installed on the golden client + my $rsp = {}; + $rsp->{data}->[0] = qq{Checking if systemimager packages are installed on $node.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + my $cmd = "rpm -qa|grep systemimager-client"; + my $output = xCAT::Utils->runxcmd({command => ["xdsh"], node => [$node], arg =>[$cmd]}, $subreq, 0, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of $cmd on $node is:}; + foreach my $o (@$output) { + push @{$rsp->{data}}, $o; + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC != 0) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{systemimager-client is not installed on the $node.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # prepare golden client + my $rsp = {}; + $rsp->{data}->[0] = qq{Preparing osimage $osimage on $node.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + my $cmd = "export PERL5LIB=/usr/lib/perl5/site_perl/;LANG=C si_prepareclient --server $server --no-uyok --yes"; + my $output = xCAT::Utils->runxcmd( + { + command => ["xdsh"], + node => [$node], + arg =>["-s", $cmd] + }, + $subreq, 0, 1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of $cmd on $node is:}; + foreach my $o (@$output) { + push @{$rsp->{data}}, $o; + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC != 0) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{$cmd failed on the $node.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # fix systemimager bug + $cmd = qq{sed -i 's/p_name=\"(v1)\"/p_name=\"-\"/' /etc/systemimager/autoinstallscript.conf}; + $output = xCAT::Utils->runxcmd( + { + command => ["xdsh"], + node => [$node], + arg =>[$cmd] + }, + $subreq, 0, 1); + + my @nodes = ($node); + my $nodetypetab = xCAT::Table->new("nodetype"); + my $nthash = $nodetypetab->getNodesAttribs(\@nodes, ['arch']); + my $tmp = $nthash->{$node}->[0]->{arch}; + if ( $tmp eq 'ppc64'){ + $cmd = qq(if ! cat /etc/systemimager/autoinstallscript.conf |grep 'part num=\\\"1\\\"' |grep 'id=' >/dev/null ;then sed -i 's:\\(.*\\):\\1 id=\\\"41\\\" \\2:' /etc/systemimager/autoinstallscript.conf;fi); + $output = xCAT::Utils->runxcmd( + { + command => ["xdsh"], + node => [$node], + arg =>[$cmd] + }, + $subreq, 0, 1); + } + + return 0; +} + +sub sysclone_getimg{ + my ($node, $server, $osimage, $callback, $subreq) = @_; + + my $rsp = {}; + $rsp->{data}->[0] = qq{Getting osimage "$osimage" from $node to $server.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + my $cmd = "export PERL5LIB=/usr/lib/perl5/site_perl/;"; + $cmd .= "LANG=C si_getimage -golden-client $node -image $osimage -ip-assignment dhcp -post-install reboot -quiet -update-script YES"; + my $output = xCAT::Utils->runcmd($cmd, -1); + if($verbose) { + my $rsp = {}; + $rsp->{data}->[0] = qq{the output of $cmd on $server is:}; + if(ref $output){ + foreach my $o (@$output) { + push @{$rsp->{data}}, $o; + } + } else { + @{$rsp->{data}} = ($output); + } + xCAT::MsgUtils->message("D", $rsp, $callback); + } + + if($::RUNCMD_RC != 0) { #failed + my $rsp = {}; + $rsp->{data}->[0] = qq{$cmd failed on the $server.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + # use reboot in genesis + my $masterscript = $sysclone_home . "/scripts" . "/$osimage.master"; + my $rc = `sed -i "s/shutdown -r now/reboot -f/g" $masterscript`; + + #on redhat5 and centos5, the fs inode size must be 128 + my $node_osver = getOsVersion($node); + if ( $node_osver =~ /rh.*5.*/ || $node_osver =~ /centos5.*/ ) { + `sed -i "s/mke2fs/mke2fs -I 128/g" $masterscript` + } + return 0; +} + +sub sysclone_createosimgdef{ + my ($node, $server, $osimage, $callback, $subreq) = @_; + my $createnew = 0; + my %osimgdef; + + my $osimgtab = xCAT::Table->new('osimage'); + my $entry = ($osimgtab->getAllAttribsWhere("imagename = '$osimage'", 'ALL' ))[0]; + if($entry){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Using the existing osimage "$osimage" defined on $server.}; + xCAT::MsgUtils->message("I", $rsp, $callback); + return 0; + } + + # try to see if we can get the osimage def from golden client. + my $nttab = xCAT::Table->new('nodetype'); + if (!$nttab){ + my $rsp = {}; + $rsp->{data}->[0] = qq{Can not open nodebype table.}; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + my @nodes = ($node); + my $nthash = $nttab->getNodesAttribs(\@nodes, ['node', 'provmethod']); + my $tmp = $nthash->{$node}->[0]; + if (($tmp) && ($tmp->{provmethod})){ + + my %objtype; + my $oldimg = $tmp->{provmethod}; + + # see if osimage exists + $objtype{$oldimg} = 'osimage'; + my %imagedef = xCAT::DBobjUtils->getobjdefs(\%objtype, $callback); + if (!($imagedef{$oldimg}{osvers})){ # just select one attribute for test + # create new one + $createnew = 1; + }else{ + # based on the existing one + $osimgdef{$osimage} = $imagedef{$oldimg}; + + # only update a few attributes which are meanless for sysclone + $osimgdef{$osimage}{provmethod} = "sysclone"; + $osimgdef{$osimage}{template} = ""; + $osimgdef{$osimage}{otherpkglist} = ""; + $osimgdef{$osimage}{pkglist} = ""; + + if(!($imagedef{$oldimg}{rootimgdir})){ + $imagedef{$oldimg}{rootimgdir} = $sysclone_home . "/images/" . $osimage; + + my $imagedir = $imagedef{$oldimg}{rootimgdir}; + $imagedir =~ s/^(\/.*)\/.+\/?$/$1/; + $imagedir =~ s/\//\\\\\//g; + $imagedir = "DEFAULT_IMAGE_DIR = ".$imagedir; + + my $olddir = `more /etc/systemimager/systemimager.conf |grep DEFAULT_IMAGE_DIR`; + $olddir =~ s/\//\\\\\//g; + chomp($olddir); + + my $cmd= "sed -i \"s/$olddir/$imagedir/\" /etc/systemimager/systemimager.conf"; + my $rc = `$cmd`; + } + } + } else { + $createnew = 1; + } + + if($createnew){ + my $file = $sysclone_home . "/images/" . $osimage. "/etc/systemimager/boot/ARCH"; + my $cmd = "cat $file"; + my $output = xCAT::Utils->runcmd($cmd, -1); + chomp $output; + my $arch = $output; + my $osver = getOsVersion($node); + my $platform = getplatform($osver); + + # create a baic one + $osimgdef{$osimage}{objtype} = "osimage"; + $osimgdef{$osimage}{provmethod} = "sysclone"; + $osimgdef{$osimage}{profile} = "compute"; # use compute? + $osimgdef{$osimage}{imagetype} = "Linux"; + $osimgdef{$osimage}{osarch} = $arch; + $osimgdef{$osimage}{osname} = "Linux"; + $osimgdef{$osimage}{osvers} = $osver; + $osimgdef{$osimage}{osdistroname} = "$osver-$arch"; + + $osimgdef{$osimage}{rootimgdir} = $sysclone_home . "/images/" . $osimage; + my $imagedir = $osimgdef{$osimage}{rootimgdir}; + $imagedir =~ s/^(\/.*)\/.+\/?$/$1/; + $imagedir =~ s/\//\\\\\//g; + $imagedir = "DEFAULT_IMAGE_DIR = ".$imagedir; + my $olddir = `more /etc/systemimager/systemimager.conf |grep DEFAULT_IMAGE_DIR`; + $olddir =~ s/\//\\\\\//g; + chomp($olddir); + my $cmd= "sed -i \"s/$olddir/$imagedir/\" /etc/systemimager/systemimager.conf"; + my $rc = `$cmd`; + + #$osimgdef{$osimage}{pkgdir} = "/install/$osver/$arch"; + #$osimgdef{$osimage}{otherpkgdir} = "/install/post/otherpkgs/$osver/$arch"; + } + + if (xCAT::DBobjUtils->setobjdefs(\%osimgdef) != 0) + { + my $rsp; + $rsp->{data}->[0] = "Could not create xCAT definition for $osimage.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + my $rsp = {}; + $rsp->{data}->[0] = qq{The osimage definition for $osimage was created.}; + xCAT::MsgUtils->message("D", $rsp, $callback); + + return 0; +} + +sub getOsVersion { + my ($node) = @_; + + my $os = ''; + my $version = ''; + + # Get operating system + my $release = `ssh -o ConnectTimeout=2 $node "cat /etc/*release"`; + my @lines = split('\n', $release); + if (grep(/SLES|Enterprise Server/, @lines)) { + $os = 'sles'; + $version = $lines[0]; + $version =~ tr/\.//; + $version =~ s/[^0-9]*([0-9]+).*/$1/; + $os = $os . $version; + + # Append service level + $version = `echo "$release" | grep "LEVEL"`; + $version =~ tr/\.//; + $version =~ s/[^0-9]*([0-9]+).*/$1/; + $os = $os . 'sp' . $version; + } elsif (grep(/Red Hat/, @lines)) { + $os = "rh"; + $version = $lines[0]; + $version =~ s/[^0-9]*([0-9.]+).*/$1/; + if ($lines[0] =~ /AS/) { $os = 'rhas' } + elsif ($lines[0] =~ /ES/) { $os = 'rhes' } + elsif ($lines[0] =~ /WS/) { $os = 'rhws' } + elsif ($lines[0] =~ /Server/) { $os = 'rhels' } + elsif ($lines[0] =~ /Client/) { $os = 'rhel' } + #elsif (-f "/etc/fedora-release") { $os = 'rhfc' } + $os = $os . $version; + } + elsif (grep (/CentOS/, @lines)) { + $os = "centos"; + $version = $lines[0]; + $version =~ s/[^0-9]*([0-9.]+).*/$1/; + $os = $os . $version; + } + elsif (grep (/Fedora/, @lines)) { + $os = "fedora"; + $version = $lines[0]; + $version =~ s/[^0-9]*([0-9.]+).*/$1/; + $os = $os . $version; + } + + + return $os; +} +1; diff --git a/xCAT-server/lib/xcat/plugins/packimage.pm b/xCAT-server/lib/xcat/plugins/packimage.pm index e69de29bb..02af3abc3 100755 --- a/xCAT-server/lib/xcat/plugins/packimage.pm +++ b/xCAT-server/lib/xcat/plugins/packimage.pm @@ -0,0 +1,629 @@ +package xCAT_plugin::packimage; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} +use lib "$::XCATROOT/lib/perl"; +use Data::Dumper; +use xCAT::Table; +use Getopt::Long; +use File::Path; +use File::Copy; +use Cwd; +use File::Temp; +use File::Basename; +use File::Path; +#use xCAT::Utils qw(genpassword); +use xCAT::Utils; +use xCAT::TableUtils; +use xCAT::SvrUtils; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + + +my $verbose = 0; +#$verbose = 1; + +sub handled_commands { + return { + packimage => "packimage", + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $installroot = xCAT::TableUtils->getInstallDir(); + + @ARGV = @{$request->{arg}}; + my $argc = scalar @ARGV; + if ($argc == 0) { + $callback->({info=>["packimage -h \npackimage -v \npackimage imagename"]}); + return; + } + my $osver; + my $arch; + my $profile; + my $method='cpio'; + my $exlistloc; + my $syncfile; + my $rootimg_dir; + my $destdir; + my $imagename; + my $dotorrent; + + GetOptions( + "profile|p=s" => \$profile, + "arch|a=s" => \$arch, + "osver|o=s" => \$osver, + "method|m=s" => \$method, + "tracker=s" => \$dotorrent, + "help|h" => \$help, + "version|v" => \$version + ); + if ($version) { + my $version = xCAT::Utils->Version(); + $callback->({info=>[$version]}); + return; + } + if ($help) { + $callback->({info=>["packimage -h \npackimage -v \npackimage imagename"]}); + return; + } + + if (@ARGV > 0) { + $imagename=$ARGV[0]; + if ($arch or $osver or $profile) { + $callback->({error=>["-o, -p and -a options are not allowed when a image name is specified."],errorcode=>[1]}); + return; + } + # load the module in memory + eval {require("$::XCATROOT/lib/perl/xCAT/Table.pm")}; + if ($@) { + $callback->({error=>[$@],errorcode=>[1]}); + return; + } + + # get the info from the osimage and linux + my $osimagetab=xCAT::Table->new('osimage', -create=>1); + unless ($osimagetab) { + $callback->({error=>["The osimage table cannot be opened."],errorcode=>[1]}); + return; + } + my $linuximagetab=xCAT::Table->new('linuximage', -create=>1); + unless ($linuximagetab) { + $callback->({error=>["The linuximage table cannot be opened."],errorcode=>[1]}); + return; + } + (my $ref) = $osimagetab->getAttribs({imagename => $imagename}, 'osvers', 'osarch', 'profile', 'provmethod', 'synclists'); + unless ($ref) { + $callback->({error=>["Cannot find image \'$imagename\' from the osimage table."],errorcode=>[1]}); + return; + } + (my $ref1) = $linuximagetab->getAttribs({imagename => $imagename}, 'exlist', 'rootimgdir'); + unless ($ref1) { + $callback->({error=>["Cannot find $imagename from the linuximage table."],errorcode=>[1]}); + return; + } + + $osver=$ref->{'osvers'}; + $arch=$ref->{'osarch'}; + $profile=$ref->{'profile'}; + $syncfile=$ref->{'synclists'}; + my $provmethod=$ref->{'provmethod'}; + + unless ($osver and $arch and $profile and $provmethod) { + $callback->({error=>["osimage.osvers, osimage.osarch, osimage.profile and osimage.provmethod must be specified for the image $imagename in the database."],errorcode=>[1]}); + return; + } + + if ($provmethod ne 'netboot') { + $callback->({error=>["\'$imagename\' cannot be used to build diskless image. Make sure osimage.provmethod is 'netboot'."],errorcode=>[1]}); + return; + } + + $exlistloc =$ref1->{'exlist'}; + $destdir=$ref1->{'rootimgdir'}; + } else { + $provmethod="netboot"; + unless ($osver) { + $callback->({error=>["Please specify a os version with the -o flag"],errorcode=>[1]}); + return; + } + unless ($arch) { + $arch = `uname -m`; + chomp($arch); + $arch = "x86" if ($arch =~ /i.86$/); + } + + unless ($profile) { + $callback->({error=>["Please specify a profile name with -p flag"],errorcode=>[1]}); + return; + } + } + + unless ($destdir) { + $destdir="$installroot/netboot/$osver/$arch/$profile"; + } + $rootimg_dir="$destdir/rootimg"; + + my $distname = $osver; + until (-r "$::XCATROOT/share/xcat/netboot/$distname/" or not $distname) { + chop($distname); + } + unless ($distname) { + $callback->({error=>["Unable to find $::XCATROOT/share/xcat/netboot directory for $osver"],errorcode=>[1]}); + return; + } + unless ($installroot) { + $callback->({error=>["No installdir defined in site table"],errorcode=>[1]}); + return; + } + my $oldpath=cwd(); + unless ($imagename) { + $exlistloc=xCAT::SvrUtils->get_exlist_file_name("$installroot/custom/netboot/$distname", $profile, $osver, $arch); + unless ($exlistloc) { $exlistloc=xCAT::SvrUtils->get_exlist_file_name("$::XCATROOT/share/xcat/netboot/$distname", $profile, $osver, $arch); } + + #save the settings into DB, it will not update if the image already exist + my @ret = xCAT::SvrUtils->update_tables_with_diskless_image($osver, $arch, $profile, "netboot"); + unless ($ret[0] eq 0) { + $callback->({error=>["Error when updating the osimage tables: " . $ret[1]], errorcode=>[1]}); + return; + } + } + + #before generating rootimg.gz, copy $installroot/postscripts into the image at /xcatpost + if( -e "$rootimg_dir/xcatpost" ) { + system("rm -rf $rootimg_dir/xcatpost"); + } + + system("mkdir -p $rootimg_dir/xcatpost"); + system("cp -r $installroot/postscripts/* $rootimg_dir/xcatpost/"); + + #put the image name and timestamp into diskless image when it is packed. + `echo IMAGENAME="'$imagename'" > $rootimg_dir/opt/xcat/xcatinfo`; + + my $timestamp = `date`; + chomp $timestamp; + `echo TIMESTAMP="'$timestamp'" >> $rootimg_dir/opt/xcat/xcatinfo`; + + + # before generating rootimg.gz or rootimg.sfs, need to switch the rootimg to stateless mode if necessary + my $rootimg_status = 0; # 0 means stateless mode, while 1 means statelite mode + $rootimg_status = 1 if (-f "$rootimg_dir/.statelite/litefile.save"); + + my $ref_liteList; # get the litefile entries + + my @ret = xCAT::Utils->runcmd("ilitefile $osver-$arch-statelite-$profile" , 0, 1); + $ref_liteList = $ret[0]; + + my %liteHash; # create hash table for the entries in @listList + if (parseLiteFiles($ref_liteList, \%liteHash)) { + $callback->({error=>["Failed for parsing litefile table!"], errorcode=>[1]}); + return; + } + + $verbose && $callback->({data=>["rootimg_status = $rootimg_status at line " . __LINE__ ]}); + + if($rootimg_status) { + xCAT::Utils->runcmd("mkdir $rootimg_dir/.statebackup", 0, 1); + # read through the litefile table to decide which file/directory should be restore + my $defaultloc = "$rootimg_dir/.default"; + foreach my $entry (keys %liteHash) { + my @tmp = split /\s+/, $entry; + my $filename = $tmp[1]; + my $fileopt = $tmp[0]; + + if ($fileopt =~ m/link/) { + # backup them into .statebackup dirctory + # restore the files with "link" options + if ($filename =~ m/\/$/) { + chop $filename; + } + # create the parent directory if $filename's directory is not there, + my $parent = dirname $filename; + unless ( -d "$rootimg_dir/.statebackup$parent" ) { + unlink "$rootimg_dir/.statebackup$parent"; + $verbose && $callback->({data=>["mkdir -p $rootimg_dir/.statebackup$parent"]}); + xCAT::Utils->runcmd("mkdir -p $rootimg_dir/.statebackup$parent", 0, 1); + } + $verbose && $callback->({data=>["backing up the file $filename.. at line " . __LINE__ ]}); + $verbose && print "++ $defaultloc$filename ++ $rootimg_dir$filename ++ at " . __LINE__ . "\n"; + xCAT::Utils->runcmd("mv $rootimg_dir$filename $rootimg_dir/.statebackup$filename", 0, 1); + xCAT::Utils->runcmd("cp -r -a $defaultloc$filename $rootimg_dir$filename", 0, 1); + } + } + } + + # TODO: following the old genimage code, to update the stateles-only files/directories + # # another file should be /opt/xcat/xcatdsklspost, but it seems not necessary + xCAT::Utils->runcmd("mv $rootimg_dir/etc/init.d/statelite $rootimg_dir/.statebackup/statelite ", 0, 1) if ( -e "$rootimg_dir/etc/init.d/statelite"); + if ( -e "$rootimg_dir/usr/share/dracut" ) { + # currently only used for redhat families, not available for SuSE families + if ( -e "$rootimg_dir/etc/rc.sysinit.backup" ) { + xCAT::Utils->runcmd("mv $rootimg_dir/etc/rc.sysinit.backup $rootimg_dir/etc/rc.sysinit", 0, 1); + } + } + + #restore the install.netboot of xcat dracut module + if(-e "$rootimg_dir/usr/lib/dracut/modules.d/97xcat/install"){ + xCAT::Utils->runcmd("mv $rootimg_dir/usr/lib/dracut/modules.d/97xcat/install $rootimg_dir/.statebackup/install", 0, 1); + } + xCAT::Utils->runcmd("cp /opt/xcat/share/xcat/netboot/rh/dracut_033/install.netboot $rootimg_dir/usr/lib/dracut/modules.d/97xcat/install", 0, 1); + + + my $xcat_packimg_tmpfile = "/tmp/xcat_packimg.$$"; + my $excludestr = "find . -xdev "; + my $includestr; + if ($exlistloc) { + my @excludeslist = split ',', $exlistloc; + foreach my $exlistlocname ( @excludeslist ) { + my $exlist; + my $excludetext; + open($exlist,"<",$exlistlocname); + system("echo -n > $xcat_packimg_tmpfile"); + while (<$exlist>) { + $excludetext .= $_; + } + close($exlist); + + #handle the #INLCUDE# tag recursively + my $idir = dirname($exlistlocname); + my $doneincludes=0; + while (not $doneincludes) { + $doneincludes=1; + if ($excludetext =~ /#INCLUDE:[^#^\n]+#/) { + $doneincludes=0; + $excludetext =~ s/#INCLUDE:([^#^\n]+)#/include_file($1,$idir)/eg; + } + + } + + my @tmp=split("\n", $excludetext); + foreach (@tmp) { + chomp $_; + s/\s*#.*//; #-- remove comments + next if /^\s*$/; #-- skip empty lines + if (/^\+/) { + s/^\+//; #remove '+' + $includestr .= "-path '". $_ ."' -o "; + } else { + s/^\-//; #remove '-' if any + $excludestr .= "'!' -path '".$_."' -a "; + } + } + } + } + + # the files specified for statelite should be excluded + my @excludeStatelite = ("./etc/init.d/statelite", "./etc/rc.sysinit.backup", "./.statelite*", "./.default*", "./.statebackup*"); + foreach my $entry (@excludeStatelite) { + $excludestr .= "'!' -path '" . $entry . "' -a "; + } + + $excludestr =~ s/-a $//; + if ($includestr) { + $includestr =~ s/-o $//; + $includestr = "find . -xdev " . $includestr; + } + + print "\nexcludestr=$excludestr\n\n includestr=$includestr\n\n"; # debug + + # add the xCAT post scripts to the image + unless ( -d "$rootimg_dir") { + $callback->({error=>["$rootimg_dir does not exist, run genimage -o $osver -p $profile on a server with matching architecture"], errorcode=>[1]}); + return; + } + + # some rpms like atftp mount the rootimg/proc to /proc, we need to make sure rootimg/proc is free of junk + # before packaging the image + system("umount $rootimg_dir/proc"); + copybootscript($installroot, $rootimg_dir, $osver, $arch, $profile, $callback); + my $passtab = xCAT::Table->new('passwd'); + if ($passtab) { + my $pass = 'cluster'; + (my $pent) = $passtab->getAttribs({key=>'system',username=>'root'},'password'); + if ($pent and defined ($pent->{password})) { + $pass = $pent->{password}; + } + my $oldmask=umask(0077); + my $shadow; + open($shadow,"<","$rootimg_dir/etc/shadow"); + my @shadents = <$shadow>; + close($shadow); + open($shadow,">","$rootimg_dir/etc/shadow"); + # 1 - MD5, 5 - SHA256, 6 - SHA512 + unless (($pass =~ /^\$1\$/) || ($pass =~ /^\$5\$/) || ($pass =~ /^\$6\$/)) { + $pass = crypt($pass,'$1$'.xCAT::Utils::genpassword(8)); + } + print $shadow "root:$pass:13880:0:99999:7:::\n"; + foreach (@shadents) { + unless (/^root:/) { + print $shadow "$_"; + } + } + close($shadow); + umask($oldmask); + } + + # sync fils configured in the synclist to the rootimage + $syncfile = xCAT::SvrUtils->getsynclistfile(undef, $osver, $arch, $profile, "netboot", $imagename); + if (defined ($syncfile) && -f $syncfile + && -d $rootimg_dir) { + print "sync files from $syncfile to the $rootimg_dir\n"; + system("$::XCATROOT/bin/xdcp -i $rootimg_dir -F $syncfile"); + } + + my $verb = "Packing"; + + my $temppath; + my $oldmask; + unless ( -d $rootimg_dir) { + $callback->({error=>["$rootimg_dir does not exist, run genimage -o $osver -p $profile on a server with matching architecture"]}); + return; + } + $callback->({data=>["$verb contents of $rootimg_dir"]}); + unlink("$destdir/rootimg.gz"); + unlink("$destdir/rootimg.sfs"); + if ($method =~ /cpio/) { + if ( ! $exlistloc ) { + $excludestr = "find . -xdev |cpio -H newc -o | gzip -c - > ../rootimg.gz"; + }else { + chdir("$rootimg_dir"); + system("$excludestr >> $xcat_packimg_tmpfile"); + if ($includestr) { + system("$includestr >> $xcat_packimg_tmpfile"); + } + #$excludestr =~ s!-a \z!|cpio -H newc -o | gzip -c - > ../rootimg.gz!; + $excludestr = "cat $xcat_packimg_tmpfile|cpio -H newc -o | gzip -c - > ../rootimg.gz"; + } + $oldmask = umask 0077; + } elsif ($method =~ /squashfs/) { + $temppath = mkdtemp("/tmp/packimage.$$.XXXXXXXX"); + chmod 0755,$temppath; + chdir("$rootimg_dir"); + system("$excludestr >> $xcat_packimg_tmpfile"); + if ($includestr) { + system("$includestr >> $xcat_packimg_tmpfile"); + } + $excludestr = "cat $xcat_packimg_tmpfile|cpio -dump $temppath"; + } else { + $callback->({error=>["Invalid method '$method' requested"],errorcode=>[1]}); + } + chdir("$rootimg_dir"); + `$excludestr`; + if ($method =~ /cpio/) { + chmod 0644,"$destdir/rootimg.gz"; + if ($dotorrent) { + my $currdir = getcwd; + chdir($destdir); + unlink("rootimg.gz.metainfo"); + system("ctorrent -t -u $dotorrent -l 1048576 -s rootimg.gz.metainfo rootimg.gz"); + chmod 0644, "rootimg.gz.metainfo"; + chdir($currdir); + } + umask $oldmask; + } elsif ($method =~ /squashfs/) { + my $flags; + if ($arch =~ /x86/) { + $flags="-le"; + } elsif ($arch =~ /ppc/) { + $flags="-be"; + } + + if( $osver =~ /rhels/ && $osver !~ /rhels5/) { + $flags=""; + } + + if (! -x "/sbin/mksquashfs" && ! -x "/usr/bin/mksquashfs" ) { + $callback->({error=>["mksquashfs not found, squashfs-tools rpm should be installed on the management node"],errorcode=>[1]}); + return; + } + my $rc = system("mksquashfs $temppath ../rootimg.sfs $flags"); + if ($rc) { + $callback->({error=>["mksquashfs could not be run successfully"],errorcode=>[1]}); + return; + } + $rc = system("rm -rf $temppath"); + if ($rc) { + $callback->({error=>["Failed to clean up temp space"],errorcode=>[1]}); + return; + } + chmod(0644,"../rootimg.sfs"); + } + system("rm -f $xcat_packimg_tmpfile"); + + # move the files in /.statebackup back to rootimg_dir + if ($rootimg_status) { # statelite mode + foreach my $entry (keys %liteHash) { + my @tmp = split /\s+/, $entry; + my $filename = $tmp[1]; + my $fileopt = $tmp[0]; + if ($fileopt =~ m/link/) { + chop $filename if ($filename =~ m/\/$/); + xCAT::Utils->runcmd("rm -rf $rootimg_dir$filename", 0, 1); + xCAT::Utils->runcmd("mv $rootimg_dir/.statebackup$filename $rootimg_dir$filename", 0, 1); + } + } + + xCAT::Utils->runcmd("mv $rootimg_dir/.statebackup/install $rootimg_dir/usr/lib/dracut/modules.d/97xcat/install", 0, 1); + xCAT::Utils->runcmd("mv $rootimg_dir/.statebackup/statelite $rootimg_dir/etc/init.d/statelite", 0, 1); + xCAT::Utils->runcmd("rm -rf $rootimg_dir/.statebackup", 0, 1); + } + + + chdir($oldpath); +} + +########################################################### +# +# copybootscript - copy the xCAT diskless init scripts to the image +# +############################################################# +sub copybootscript { + + my $installroot = shift; + my $rootimg_dir = shift; + my $osver = shift; + my $arch = shift; + my $profile = shift; + my $callback = shift; + my @timezone = xCAT::TableUtils->get_site_attribute("timezone"); + + if ( -f "$installroot/postscripts/xcatdsklspost") { + + # copy the xCAT diskless post script to the image + mkpath("$rootimg_dir/opt/xcat"); + + copy ("$installroot/postscripts/xcatdsklspost", "$rootimg_dir/opt/xcat/xcatdsklspost"); + if($timezone[0]) { + copy ("$rootimg_dir/usr/share/zoneinfo/$timezone[0]", "$rootimg_dir/etc/localtime"); + } + + + chmod(0755,"$rootimg_dir/opt/xcat/xcatdsklspost"); + + } else { + + my $rsp; + push @{$rsp->{data}}, "Could not find the script $installroot/postscripts/xcatdsklspost.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + return 1; + } + + + #if ( -f "$installroot/postscripts/xcatpostinit") { + # copy the linux diskless init script to the image + # - & set the permissions + #copy ("$installroot/postscripts/xcatpostinit","$rootimg_dir/etc/init.d/xcatpostinit"); + + #chmod(0755,"$rootimg_dir/etc/init.d/xcatpostinit"); + + # run chkconfig + #my $chkcmd = "chroot $rootimg_dir chkconfig --add xcatpostinit"; + #symlink "/etc/init.d/xcatpostinit","$rootimg_dir/etc/rc3.d/S84xcatpostinit"; + #symlink "/etc/init.d/xcatpostinit","$rootimg_dir/etc/rc4.d/S84xcatpostinit"; + #symlink "/etc/init.d/xcatpostinit","$rootimg_dir/etc/rc5.d/S84xcatpostinit"; + #my $rc = system($chkcmd); + #if ($rc) { + #my $rsp; + # push @{$rsp->{data}}, "Could not run the chkconfig command.\n"; + # xCAT::MsgUtils->message("E", $rsp, $callback); + # return 1; + # } + #} else { + #my $rsp; + # push @{$rsp->{data}}, "Could not find the script $installroot/postscripts/xcatpostinit.\n"; + # xCAT::MsgUtils->message("E", $rsp, $callback); + # return 1; + #} + return 0; +} + +sub include_file +{ + my $file = shift; + my $idir = shift; + my @text = (); + unless ($file =~ /^\//) { + $file = $idir."/".$file; + } + + open(INCLUDE,$file) || \ + return "#INCLUDEBAD:cannot open $file#"; + + while() { + chomp($_); + s/\s+$//; #remove trailing spaces + next if /^\s*$/; #-- skip empty lines + push(@text, $_); + } + + close(INCLUDE); + + return join("\n", @text); +} + +=head3 parseLiteFiles +In the liteentry table, one directory and its sub-items (including sub-directory and entries) can co-exist; +In order to handle such a scenario, one hash is generated to show the hirarachy relationship + +For example, one array with entry names is used as the input: +my @entries = ( + "imagename bind,persistent /var/", + "imagename bind /var/tmp/", + "imagename tmpfs,rw /root/", + "imagename tmpfs,rw /root/.bashrc", + "imagename tmpfs,rw /root/test/", + "imagename bind /etc/resolv.conf", + "imagename bind /var/run/" +); +Then, one hash will generated as: +%hashentries = { + 'bind,persistent /var/' => [ + 'bind /var/tmp/', + 'bind /var/run/' + ], + 'bind /etc/resolv.conf' => undef, + 'tmpfs,rw /root/' => [ + 'tmpfs,rw /root/.bashrc', + 'tmpfs,rw /root/test/' + ] + }; + +Arguments: + one array with entrynames, + one hash to hold the entries parsed + +Returns: + 0 if sucucess + 1 if fail + +=cut + + + +sub parseLiteFiles { + my ($flref, $dhref) = @_; + my @entries = @{$flref}; + + + foreach (@entries) { + my $entry = $_; + my @str = split /\s+/, $entry; + shift @str; + $entry = join "\t", @str; + my $file = $str[1]; + chop $file if ($file =~ m{/$}); + unless (exists $dhref->{"$entry"}) { + my $parent = dirname($file); + # to see whether $parent exists in @entries or not + unless ($parent =~ m/\/$/) { + $parent .= "/"; + } + my @res = grep {$_ =~ m/\Q$parent\E$/} @entries; + my $found = scalar @res; + + if($found eq 1) { # $parent is found in @entries + # handle $res[0]; + my @tmpresentry=split /\s+/, $res[0]; + shift @tmpresentry; + $res[0] = join "\t", @tmpresentry; + chop $parent; + my @keys = keys %{$dhref}; + my $kfound = grep {$_ =~ m/\Q$res[0]\E$/} @keys; + if($kfound eq 0) { + $dhref->{$res[0]} = []; + } + push @{$dhref->{"$res[0]"}}, $entry; + }else { + $dhref->{"$entry"} = (); + } + } + } + + return 0; +} + +1;