# IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos720 src/bos/usr/lpp/bosinst/samples/NIM/Deploy/Mksysb.pm 1.1 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 2008,2009 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # @(#)54 1.1 src/bos/usr/lpp/bosinst/samples/NIM/Deploy/Mksysb.pm, bosinst, bos720 3/29/09 15:20:47 package NIM::Deploy::Mksysb; =head1 NAME NIM::Deploy::Mksysb =over 2 =item Derived from NIM::Deploy =back =head1 VARIABLES =head2 %Settings =over 2 =item * max_spots Used to indicate the maximum number of extracted SPOTs created by this module and maintained in NIM. Excessive SPOTs will be deleted from NIM. =item * remote_work_dir The directory on the installed system used for running the self-extracting NIM fb_script script. =back =head1 FUNCTIONS =cut #------------------------------------------------------------------------------- BEGIN { # print "NIM::Deploy::Mksysb BEGIN\n"; use base (NIM::Deploy); use Hash::Util; use File::Basename; use File::Copy; use NIM::Info; use NIM::Util qw(:DEFAULT !log_print !log_printf); use NIM::Deploy::Mksysb::ImageData; *Settings = \%NIM::Deploy::Settings; $Settings{'max_spots'} = 0; $Settings{'remote_work_dir'} = '/opt/IBM/AE'; *log_print = \&NIM::Deploy::log_print; *log_printf = \&NIM::Deploy::log_printf; } #------------------------------------------------------------------------------- sub resolve_mksysb { my $this = shift; my ($nim_mksysb_name, $mksysb_pn) = @_; log_print("NIM::Deploy::Mksysb::resolve_mksysb(\'$nim_mksysb_name\', \'$mksysb_pn\')\n"); my $nim_info = $this->{'info'}; my @rc = ('', '', ''); # if location is not an empty string then it takes precendence over nim resource name if ($mksysb_pn) { my $mksysbs = $nim_info->locate_mksysb_by_location($mksysb_pn); if (! $mksysbs->empty()) { $rc[0] = $mksysbs->get_first_object_name(); $rc[1] = $mksysb_pn; $rc[2] = $mksysbs->get_first_attr_value($rc[0], 'extracted_spot'); log_print "\tEquivalent NIM mksysb resource already exists: name = $rc[0] / location = $mksysb_pn\n"; } else { if (-r $mksysb_pn) { # need to create nim resource if ($nim_mksysb_name eq '') { # need to generate a name $nim_mksysb_name = $this->get_nim_name('mksysb'); } ##################################### my @m = mutex_lock($nim_mksysb_name); ##################################### $nim_info->update("$Lang $Cmds{'lsnim'} -lFt mksysb 2>/dev/null"); my $mksysbs = $nim_info->locate_mksysb_by_name($nim_mksysb_name); if ($mksysbs->empty()) { log_print "\tCreating mksysb resource: name = $nim_mksysb_name / location = $mksysb_pn\n"; invoke("$Cmds{'nim'} -o define -t mksysb -a server=$this->{'nim_master_name'} -a location=$mksysb_pn $nim_mksysb_name"); $nim_info->update("$Lang $Cmds{'lsnim'} -lFt mksysb 2>/dev/null"); $mksysbs = $nim_info->locate_mksysb_by_location($mksysb_pn); if (! $mksysbs->empty()) { $rc[0] = $mksysbs->get_first_object_name(); $rc[1] = $mksysb_pn; $rc[2] = $mksysbs->get_first_attr_value($rc[0], 'extracted_spot'); } else { log_print "\tCould not create mksysb resource: name = $nim_mksysb_name / location = $mksysb_pn\n"; $rc[0] = ''; $rc[1] = ''; $rc[2] = ''; } } else { if ($mksysbs->get_first_attr_value($nim_mksysb_name, 'location') eq $mksysb_pn) { $rc[0] = $mksysbs->get_first_object_name(); $rc[1] = $mksysb_pn; $rc[2] = $mksysbs->get_first_attr_value($rc[0], 'extracted_spot'); } else { log_print "\tA dfferent NIM mksysb resource already exists with name: $nim_mksysb_name\n"; $rc[0] = ''; $rc[1] = ''; $rc[2] = ''; } } ################ mutex_unlock @m; ################ } else { log_print "\tUnreadable mksysb: location = $mksysb_pn\n"; $rc[0] = ''; $rc[1] = ''; $rc[2] = ''; } } } elsif ($nim_mksysb_name) { my $mksysbs = $nim_info->locate_mksysb_by_name($nim_mksysb_name); if (! $mksysbs->empty()) { $rc[0] = $nim_mksysb_name; $rc[1] = $mksysbs->get_first_attr_value($rc[0], 'location'); $rc[2] = $mksysbs->get_first_attr_value($rc[0], 'extracted_spot'); log_print "\tNIM mksysb resource already exists: name = $rc[0] / location = $rc[1]\n"; } else { log_print "\tNIM mksysb resource not found for: name = $nim_mksysb_name\n"; } } return @rc; } #------------------------------------------------------------------------------- sub find_res_group_from_mksysb_name { my $this = shift; my ($nim_mksysb_name) = @_; log_print("NIM::Deploy::Mksysb::find_res_group_from_mksysb_name(\'$nim_mksysb_name\')\n"); my $rc = ''; my $re = sprintf('%s-res_group-[0-9a-fA-F]+', $Settings{'project'}); my $results = $this->{'info'}->select_with_regexp_key_values(('^name$' => "^$re\$", '^class$' => '^groups$', '^type$' => '^res_group$', '^member[1-9][0-9]*$' => "^$nim_mksysb_name\$")); if ($results->size()) { $rc = $results->get_first_object_name(); log_print("\tFound res_group: $rc\n"); } return $rc; } #------------------------------------------------------------------------------- sub get_index_from_res_group_name { my $this = shift; my ($res_group_name) = @_; my $rc = 0; my $match = sprintf('^%s-res_group-([0-9a-fA-F]+)$', $Settings{'project'}); if ($res_group_name =~ m/$match/) { $rc = hex $1; } return $rc; } #------------------------------------------------------------------------------- sub get_spot_from_res_group { my $this = shift; my ($res_group_name) = @_; log_print("NIM::Deploy::Mksysb::get_spot_from_res_group(\'$res_group_name\')\n"); my $rc = ''; my $nim_info = $this->{'info'}; my @members = $nim_info->get_resource_group_members($res_group_name); foreach my $member (@members) { my $value = $nim_info->get_first_attr_value($member, 'type'); if ($value eq 'spot') { log_print("\tFound spot: $member\n"); $rc = $member; last; } } return $rc; } #------------------------------------------------------------------------------- sub get_bosinst_data_from_res_group { my $this = shift; my ($res_group_name) = @_; log_print("NIM::Deploy::Mksysb::get_bosinst_data_from_res_group(\'$res_group_name\')\n"); my $rc = ''; my $nim_info = $this->{'info'}; my @members = $nim_info->get_resource_group_members($res_group_name); foreach my $member (@members) { my $value = $nim_info->get_first_attr_value($member, 'type'); if ($value eq 'bosinst_data') { log_print("\tFound bosinst_data: $member\n"); $rc = $member; last; } } return $rc; } #------------------------------------------------------------------------------- sub resolve_comment_tag_value { my $this = shift; my ($tag_name, $tag_value) = @_; my $nim_info = $this->{'info'}; my @comments = $nim_info->get_comments($this->{'name'}); my $commentsStr = join("\n", @comments); if ($tag_value) { $commentsStr =~ s/([\n]*)(<$tag_name>)(.*)(<\/$tag_name>)//g; if (1) { $commentsStr =~ s/[ ]*[\n]+[ ]*/\n/g; # delete spaces around newlines $commentsStr =~ s/([\n]+)$//; # delete trailing newlines $commentsStr =~ s/^([\n]+)//; # delete leading newlines $commentsStr =~ s/> *\n$tag_value"); $nim_info->set_comments($this->{'name'}, \@comments); $nim_info->update("$Lang $Cmds{'lsnim'} -lF $this->{'name'} 2>/dev/null"); } else { if ($commentsStr =~ m/(<$tag_name>)(.*)(<\/$tag_name>)/) { $tag_value = $2; } } return $tag_value; } #------------------------------------------------------------------------------- sub resolve_ae_info { my $this = shift; my ($ae_dir, $ae_xml, $ae_cmd) = @_; $ae_dir = $this->resolve_comment_tag_value('ae_dir', $ae_dir); $ae_xml = $this->resolve_comment_tag_value('ae_xml', $ae_xml); $ae_cmd = $this->resolve_comment_tag_value('ae_cmd', $ae_cmd); my @rc = ($ae_dir, $ae_xml, $ae_cmd); return @rc; } #------------------------------------------------------------------------------- =head2 new =over 2 =item C C $nim_mksysb_name, 'location' =E $mksysb_pn)> =back =head3 DESCRIPTION =over 2 =item Creates a new instance of NIM::Deploy::Mksysb. =back =head3 PARAMETERS =over 2 =item * $args{'name'} (OPTIONAL) =item * $args{'location'} (OPTIONAL) =back =head3 RETURNS =over 2 =item * Reference to the newly created object. =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub new { my $type = shift; my %args = @_; my $arg_nim_mksysb_name = $args{'name'}; my $arg_mksysb_pn = $args{'location'}; ### log_print("NIM::Deploy::Mksysb::new(\'$arg_nim_mksysb_name\', \'$arg_mksysb_pn\')\n"); ########################################## my @m = mutex_lock('NIM::Deploy::Mksysb'); ########################################## my $this = new NIM::Deploy(@_); bless $this, $type; $this->{'type'} = 'mksysb'; ### my ($nim_mksysb_name, $mksysb_pn, $extracted_spot_name) = $this->resolve_mksysb($arg_nim_mksysb_name, $arg_mksysb_pn); if ($nim_mksysb_name && $mksysb_pn) { log_print("NIM::Deploy::Mksysb::new(\'$nim_mksysb_name\', \'$mksysb_pn\')\n"); ##################################### my @n = mutex_lock($nim_mksysb_name); ##################################### ################ mutex_unlock @m; @m = @n; ################ my $nim_info = $this->{'info'}; $nim_info->refresh(); $this->{'name'} = $nim_mksysb_name; $this->{'location'} = $mksysb_pn; # ($this->{'ae_dir'}, $this->{'ae_xml'}, $this->{'ae_cmd'}) = $this->resolve_ae_info($this->{'ae_dir'}, $this->{'ae_xml'}, $this->{'ae_cmd'}); $this->{'ae_dir'} = ($this->{'ae_dir'} ? $this->{'ae_dir'} : $Defaults{'ae_dir'}); $this->{'ae_xml'} = ($this->{'ae_xml'} ? $this->{'ae_xml'} : $Defaults{'ae_xml'}); $this->{'ae_cmd'} = ($this->{'ae_cmd'} ? $this->{'ae_cmd'} : $Defaults{'ae_cmd'}); # my $res_group_name = $this->find_res_group_from_mksysb_name($nim_mksysb_name); my $spot_name = ''; my $bosinst_data_name = ''; if ($res_group_name) { log_print "\tUsing existing res_group: $res_group_name\n"; $this->{'group_index'} = $this->get_index_from_res_group_name($res_group_name); $spot_name = $this->get_spot_from_res_group($res_group_name); $bosinst_data_name = $this->get_bosinst_data_from_res_group($res_group_name); # possible that a spot does not exist for the resource group because the spot was deleted # because nim will delete resource references if the resource is deleted } if (! $bosinst_data_name) { $bosinst_data_name = $this->get_nim_name('bosinst_data'); $nim_info->update("$Lang $Cmds{'lsnim'} -lFt bosinst_data 2>/dev/null"); my $bosinst_data = $nim_info->locate_bosinst_data_by_name($bosinst_data_name); if ($bosinst_data->empty()) { my $bosinst_data_file = $this->create_bosinst_data_file($bosinst_data_name); invoke("$Cmds{'nim'} -o define -t bosinst_data -a location=$bosinst_data_file -a server=$this->{'nim_master_name'} -a group=$res_group_name $bosinst_data_name"); } } if (! $spot_name) { if ($extracted_spot_name) { $spot_name = $extracted_spot_name; log_print "\tUsing pre-existing extracted spot \'$spot_name\' from mksysb \'$nim_mksysb_name\'\n"; } else { $spot_name = $this->get_nim_name('spot'); log_print "\tCreating spot \'$spot_name\' from mksysb \'$nim_mksysb_name\'\n"; # not sure if deallocation is safe to do here - have to find out for which operation a mksysb is allocated to the nim master if (1) { # possible that the mksysb is already allocated to the nim master for some other operation # in which case, the spot can not be extracted # deallocate operation does not take the verbose attribute invoke("/usr/sbin/nim -Fo deallocate -amksysb=$nim_mksysb_name $this->{'nim_master_name'}"); } my $spot_parent_dir = $this->get_resource_dir('spot'); invoke("$Cmds{'nim'} -o define -t spot -a source=$nim_mksysb_name -a server=$this->{'nim_master_name'} -a location=$spot_parent_dir -a auto_expand=yes $spot_name"); $nim_info->update("$Lang $Cmds{'lsnim'} -lFt spot 2>/dev/null"); my $spots = $nim_info->locate_spot_by_mksysb_source($nim_mksysb_name); if (! $spots->empty()) { $spot_name = $spots->get_first_object_name(); } else { log_print "\tCould not create extracted spot resource: name = $spot_name / mksysb_source = $nim_mksysb_name\n"; $spot_name = ''; } } } # need to wait till spot is ready for use... in the case of simultaneous spot extraction for the same mksysb if ($spot_name) { my $spots = $nim_info->locate_spot_by_name($spot_name); if (! $spots->empty()) { my $i = 0; my ($locked, $rstate, $mk_netboot) = ('', '', ''); do { if ($i != 0) { sleep 60; $nim_info->update("$Lang $Cmds{'lsnim'} -lF $spot_name 2>/dev/null"); } $locked = $nim_info->get_first_attr_value($spot_name, 'locked'); $rstate = $nim_info->get_first_attr_value($spot_name, 'Rstate'); $mk_netboot = $nim_info->get_first_attr_value($spot_name, 'mk_netboot'); } while ($locked && ($mk_netboot eq 'yes') && (($rstate eq 'unavailable for use') || ($rstate eq 'verification is being performed')) && ($i++ < 10)); if ($rstate ne 'ready for use') { log_print "\tspot \'$spot_name\' is not available for use\n"; $spot_name = ''; } } } if ($res_group_name) { if (0) # prefer not to remove the res_group { log_print "\tRemoving res_group: $res_group_name\n"; invoke("$Cmds{'nim'} -o remove $res_group_name"); } else { log_print "\tRedefining res_group: $res_group_name\n"; invoke("$Cmds{'nim'} -o change -a member1=$nim_mksysb_name -a member2=$bosinst_data_name -a member3=$spot_name $res_group_name"); } } else { $this->{'group_index'} = $this->{'deploy_index'}; $res_group_name = $this->get_nim_name('res_group'); log_print "\tDefining res_group: $res_group_name\n"; invoke("$Cmds{'nim'} -o define -t res_group -a mksysb=$nim_mksysb_name -a bosinst_data=$bosinst_data_name -a spot=$spot_name $res_group_name"); } $nim_info->refresh(); if ($spot_name) { # maintain max_spots $this->delete_excess_spots($spot_name); } } else { log_print "\tUnable to resolve mksysb: name = $arg_nim_mksysb_name / location = $arg_mksysb_pn\n"; $this = (); } ################ mutex_unlock @m; ################ return $this; } #------------------------------------------------------------------------------- sub delete { my $this = shift; log_print("NIM::Deploy::Mksysb::delete()\n"); ########################################################### my @m = mutex_lock('NIM::Deploy::Mksysb', $this->{'name'}); ########################################################### my $nim_info = $this->{'info'}; my $mksysbs = $nim_info->locate_mksysb_by_name($this->{'name'}); if (! $mksysbs->empty()) { my $spot_name = $mksysbs->get_first_attr_value($this->{'name'}, 'extracted_spot'); if ($spot_name) { invoke("$Cmds{'nim'} -o remove $spot_name"); } my $res_group_name = $this->find_res_group_from_mksysb_name($this->{'name'}); if ($res_group_name) { invoke("$Cmds{'nim'} -o remove $res_group_name"); } invoke("$Cmds{'nim'} -o remove $this->{'name'}"); } $this->NIM::Deploy::delete(); ################ mutex_unlock @m; ################ } #------------------------------------------------------------------------------- sub get_nim_name { my $this = shift; my ($type) = @_; if (($type eq 'mksysb') || ($type eq 'spot') || ($type eq 'script') || ($type eq 'fb_script') || ($type eq 'script') || ($type eq 'image_data')) { } elsif ($type eq 'bosinst_data') { return sprintf("%s\-%s\-%08x", $Settings{'project'}, $type, 0); } else { return $this->NIM::Deploy::get_nim_name($type); } return ($type ? sprintf("%s\-%s\-%08x", $Settings{'project'}, $type, $this->{'deploy_index'}) : ''); } #------------------------------------------------------------------------------- sub delete_excess_spots { my $this = shift; my ($spot_name) = @_; log_print("NIM::Deploy::Mksysb::delete_excess_spots(\'$spot_name\')\n"); my $re = sprintf('%s-spot-[0-9a-fA-F]+', $Settings{'project'}); my $results = $this->{'info'}->select_with_regexp_key_values(('^name$' => "^$re\$", '^class$' => '^resources$', '^alloc_count$' => '^0$', '^type$' => '^spot$')); log_printf("\tFound %d spot%s\n", $results->size(), ($results->size() == 1) ? '' : 's'); my $spot_names = $results->get_object_names(); log_print "\tspot(s) = @$spot_names\n"; my $num_spots = $#$spot_names + 1; if ($Settings{'max_spots'} < 0) { $Settings{'max_spots'} = 0; } if ($num_spots && ($Settings{'max_spots'} < $num_spots)) { my $num_spots_to_delete = $num_spots - $Settings{'max_spots'}; if ($num_spots_to_delete < $num_spots) { @$spot_names = sort @$spot_names; # the following code was trying to take care of the case where the # spot index wrapped; however, the current spot may not be newly created # which is why this code is disabled if (0) { # find index of current spot my $index = bsearch($spot_names, $spot_name); log_print "\tspot index = $index, max index = $#$spot_names\n"; # adjust listing such that the current spot is the last in the array my $diff = $#$spot_names - $index; for (my $i = 0; $i < $diff; $i++) { unshift @$spot_names, (pop @$spot_names); } } } # delete excess spots found at the beginning of the array - i.e. the least recently created spots # this may also include the current spot log_printf("\t%d spot%s to delete\n", $num_spots_to_delete, ($num_spots_to_delete == 1 ? '' : 's')); for (my $i = 0; $i < $num_spots_to_delete; $i++) { # only spots with alloc_count = 0 can be deleted if ($spot_name ne $spot_names->[$i]) { log_print "\tDeleting $spot_names->[$i]\n"; invoke("$Cmds{'nim'} -o remove $spot_names->[$i]"); } } } else { log_print "\tNo spots to delete\n"; } } #------------------------------------------------------------------------------- sub reset_client { my $this = shift; my ($client) = @_; invoke("$Cmds{'nim'} -Fo reset $client"); for (my $i = 0; $i < 2; $i++) { # deallocate operation does not take the verbose attribute invoke("/usr/sbin/nim -Fo deallocate -asubclass=all $client"); } } #------------------------------------------------------------------------------- sub create_perl_script { my ($script_pn, $cmd_ary_ref, $embed_pn, $remote_work_dir, $compress) = @_; if ($remote_work_dir eq '') { $remote_work_dir = '.'; } open(OUTP, ">${script_pn}") or die "Can not open file \'$script_pn\'\: \$!"; print OUTP "#!/usr/bin/perl\n"; print OUTP "use strict;\n"; print OUTP "use warnings;\n"; print OUTP "use File::Path;\n"; print OUTP "if ((-e \'$remote_work_dir\') && (! -d \'$remote_work_dir\')) { `rm $remote_work_dir`; }\n"; print OUTP "if (! -e \'$remote_work_dir\') { mkpath(\'$remote_work_dir\'); }\n"; print OUTP "`rm -r $remote_work_dir/*`;\n"; # if ($embed_pn) { if ((-e $embed_pn) && (-r $embed_pn)) { my $embed_file_name = basename($embed_pn); my $embed_dir_name = dirname($embed_pn); if ($compress) { print OUTP "open(EXTRACT, \"|$Cmds{'uudecode'} -o /dev/stdout > ${remote_work_dir}/${embed_file_name}.Z\") or die \"Can not start $Cmds{'uudecode'}: \$!\";\n"; open(UUENCODE, "$Cmds{'compress'} -c ${embed_pn} | $Cmds{'uuencode'} ${embed_file_name}.Z |") || die "Can not run $Cmds{'uuencode'}: $!"; } else { print OUTP "open(EXTRACT, \"|$Cmds{'uudecode'} -o /dev/stdout > ${remote_work_dir}/${embed_file_name}\") or die \"Can not start $Cmds{'uudecode'}: \$!\";\n"; open(UUENCODE, "$Cmds{'uuencode'} ${embed_pn} ${embed_file_name} |") || die "Can not run $Cmds{'uuencode'}: $!"; } print OUTP "print EXTRACT << 'EMBED_FILE_END';\n"; while () { my $line = $_; print OUTP "$line"; } close(UUENCODE); print OUTP "EMBED_FILE_END\n;\n"; if ($compress) { print OUTP "`$Cmds{'uncompress'} ${remote_work_dir}/${embed_file_name}.Z`;\n"; } } else { print "Error: Can not read file: ${embed_pn}\n"; } } # my @cmd_ary = @$cmd_ary_ref; foreach my $cmd (@cmd_ary) { print OUTP "$cmd;\n"; } # close OUTP; chmod 0755, $script_pn, } #------------------------------------------------------------------------------- sub translate_perl_script { my ($script_pn) = @_; my $rc = "${script_pn}"; $rc =~ s/\.pl$/\.sh/; open(OUT_SCRIPT, ">$rc") or die "Can not open file \'$rc\'\: \$!"; open(IN_SCRIPT, "<$script_pn") or die "Can not open file \'$script_pn\'\: \$!"; # ncargs is the ARG/ENV list size in 4K byte blocks print OUT_SCRIPT "NCARGS_VAL=`lsattr -El sys0 -a ncargs -F 'value'`\n"; # Modify 'Characteristics of Operating System' to fix '0403-027 The parameter list is too long.' error print OUT_SCRIPT "if [ \$NCARGS_VAL -lt 1024 ]; then chdev -l sys0 -a ncargs='1024'; fi\n"; while () { my $line = $_; if ($line =~ m/^#!\/usr\/bin\/perl/) { print OUT_SCRIPT "/usr/bin/perl \\\n"; } else { chomp($line); $line =~ s/([\"\\\$`])/\\$1/g; print OUT_SCRIPT " -e \"$line\" \\\n"; } } print OUT_SCRIPT "\n"; # revert back print OUT_SCRIPT "eval chdev -l sys0 -a ncargs=\'\$NCARGS_VAL\'\n"; close(OUT_SCRIPT); close(IN_SCRIPT); unlink($script_pn); return $rc; } #------------------------------------------------------------------------------- sub create_ksh_script { my ($script_pn, $cmd_ary_ref, $embed_pn, $remote_work_dir, $compress) = @_; if ($remote_work_dir eq '') { $remote_work_dir = '.'; } open(OUTP, ">${script_pn}") or die "Can not open file \'$script_pn\'\: \$!"; print OUTP "if [[ -e \'$remote_work_dir\' && ! -d \'$remote_work_dir\' ]]; then rm $remote_work_dir; fi\n"; print OUTP "if [[ ! -e \'$remote_work_dir\' ]]; then mkdir -p \'$remote_work_dir\'; fi\n"; print OUTP "rm -r $remote_work_dir/*\n"; # if ($embed_pn) { if ((-e $embed_pn) && (-r $embed_pn)) { my $embed_file_name = basename($embed_pn); my $embed_dir_name = dirname($embed_pn); if ($compress) { print OUTP "$Cmds{'uudecode'} -o /dev/stdout > ${remote_work_dir}/${embed_file_name}.Z << 'EMBED_FILE_END'\n"; open(UUENCODE, "$Cmds{'compress'} -c ${embed_pn} | $Cmds{'uuencode'} ${embed_file_name}.Z |") || die "Can not run $Cmds{'uuencode'}: $!"; } else { print OUTP "$Cmds{'uudecode'} -o /dev/stdout > ${remote_work_dir}/${embed_file_name} << 'EMBED_FILE_END'\n"; open(UUENCODE, "$Cmds{'uuencode'} ${embed_pn} ${embed_file_name} |") || die "Can not run $Cmds{'uuencode'}: $!"; } while () { my $line = $_; print OUTP "$line"; } close(UUENCODE); print OUTP "EMBED_FILE_END\n\n"; if ($compress) { print OUTP "$Cmds{'uncompress'} ${remote_work_dir}/${embed_file_name}.Z\n"; } } else { print "Error: Can not read file: ${embed_pn}\n"; } } # my @cmd_ary = @$cmd_ary_ref; foreach my $cmd (@cmd_ary) { print OUTP "$cmd\n"; } # close OUTP; chmod 0755, $script_pn, } #------------------------------------------------------------------------------- sub create_cust_script_resource { my $this = shift; my ($script_type, $cmd_ary_ref, $embed_pn, $compress) = @_; log_print("NIM::Deploy::Mksysb::create_cust_script_resource(\'$script_type\')\n"); if (($script_type eq 'fb_script') || ($script_type eq 'script')) { my $script_parent_dir = $this->get_resource_dir($script_type); my $script_name = $this->get_nim_name($script_type); my $script_pn = ''; if (0) { $script_pn = "${script_parent_dir}/${script_name}.pl"; create_perl_script($script_pn, $cmd_ary_ref, $embed_pn, $Settings{'remote_work_dir'}, $compress); $script_pn = translate_perl_script($script_pn); } else { $script_pn = "${script_parent_dir}/${script_name}.sh"; create_ksh_script($script_pn, $cmd_ary_ref, $embed_pn, $Settings{'remote_work_dir'}, $compress); } invoke("$Cmds{'nim'} -o define -t $script_type -a server=$this->{'nim_master_name'} -a location=$script_pn $script_name"); log_print("\trc = $script_name\n"); return $script_name; } log_print("\trc = \'\'\n"); return ''; } #------------------------------------------------------------------------------- sub delete_cust_script_resource { my $this = shift; my ($script_type, $script_name) = @_; my $scripts = ''; if ($script_type eq 'fb_script') { $scripts = $this->{'info'}->locate_fb_script_by_name($script_name); } elsif ($script_type eq 'script') { $scripts = $this->{'info'}->locate_script_by_name($script_name); } if ($scripts && (! $scripts->empty())) { my $location = $scripts->get_first_attr_value($script_name, 'location'); unlink($location); invoke("$Cmds{'nim'} -o remove $script_name"); } } #------------------------------------------------------------------------------- =head2 create_script_resource =over 2 =item C<$mksysb-Ecreate_script_resource($cmd_array_ref, $embed_pn)> =back =head3 DESCRIPTION =over 2 =item Creates a self-extracting NIM script resource. =back =head3 PARAMETERS =over 2 =item * $cmd_array_ref (REQUIRED) Perl commands to be invoked within the first boot script after the embedded file is extracted. =item * $embed_pn (REQUIRED) The path name of the file to embed in the first boot script. =item * $compress (OPTIONAL) Indicates whether to compress the embedded file within the first boot script. =back =head3 RETURNS =over 2 =item * The name of the NIM script resource. =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub create_script_resource { my $this = shift; my ($cmd_ary_ref, $embed_pn, $compress) = @_; return $this->create_cust_script_resource('script', $cmd_ary_ref, $embed_pn, $compress); } #------------------------------------------------------------------------------- =head2 delete_script_resource =over 2 =item C<$mksysb-Edelete_script_resource($script_name)> =back =head3 DESCRIPTION =over 2 =item Deletes a NIM script resource. =back =head3 PARAMETERS =over 2 =item * $script_name (REQUIRED) =back =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub delete_script_resource { my $this = shift; my ($script_name) = @_; $this->delete_cust_script_resource('script', $script_name); } #------------------------------------------------------------------------------- =head2 create_fb_script_resource =over 2 =item C<$mksysb-Ecreate_fb_script_resource($cmd_array_ref, $embed_pn)> =back =head3 DESCRIPTION =over 2 =item Creates a self-extracting NIM fb_script resource. =back =head3 PARAMETERS =over 2 =item * $cmd_array_ref (REQUIRED) Perl commands to be invoked within the first boot script after the embedded file is extracted. =item * $embed_pn (REQUIRED) The path name of the file to embed in the first boot script. =item * $compress (OPTIONAL) Indicates whether to compress the embedded file within the first boot script. =back =head3 RETURNS =over 2 =item * The name of the NIM fb_script resource. =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub create_fb_script_resource { my $this = shift; my ($cmd_ary_ref, $embed_pn, $compress) = @_; return $this->create_cust_script_resource('fb_script', $cmd_ary_ref, $embed_pn, $compress); } #------------------------------------------------------------------------------- =head2 delete_fb_script_resource =over 2 =item C<$mksysb-Edelete_fb_script_resource($fb_script_name)> =back =head3 DESCRIPTION =over 2 =item Deletes a NIM fb_script resource. =back =head3 PARAMETERS =over 2 =item * $fb_script_name (REQUIRED) =back =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub delete_fb_script_resource { my $this = shift; my ($fb_script_name) = @_; $this->delete_cust_script_resource('fb_script', $fb_script_name); } #------------------------------------------------------------------------------- # create bosinst.data file for non-prompted installs; otherwise would need to # rely on the one in the mksysb, if it exists #------------------------------------------------------------------------------- sub create_bosinst_data_file { my $this = shift; my ($name) = @_; my $bosinst_data_parent_dir = $this->get_resource_dir('bosinst_data'); my $rc = "$bosinst_data_parent_dir/$name"; open(BOSINST_DATA, ">$rc") or die "Can not open file \'$rc\'\: \$!"; print BOSINST_DATA << 'BOSINST_DATA_END'; control_flow: CONSOLE = Default INSTALL_METHOD = overwrite PROMPT = no EXISTING_SYSTEM_OVERWRITE = yes INSTALL_X_IF_ADAPTER = yes RUN_STARTUP = no RM_INST_ROOTS = no ERROR_EXIT = CUSTOMIZATION_FILE = TCB = no INSTALL_TYPE = BUNDLES = RECOVER_DEVICES = no BOSINST_DEBUG = no ACCEPT_LICENSES = yes DESKTOP = NONE INSTALL_DEVICES_AND_UPDATES = yes IMPORT_USER_VGS = ENABLE_64BIT_KERNEL = Default CREATE_JFS2_FS = Default ALL_DEVICES_KERNELS = yes GRAPHICS_BUNDLE = yes MOZILLA_BUNDLE = no KERBEROS_5_BUNDLE = no SERVER_BUNDLE = no REMOVE_JAVA_118 = no HARDWARE_DUMP = yes ADD_CDE = no ADD_GNOME = no ADD_KDE = no ERASE_ITERATIONS = 0 ERASE_PATTERNS = target_disk_data: LOCATION = SIZE_MB = largest HDISKNAME = locale: BOSINST_LANG = CULTURAL_CONVENTION = MESSAGES = KEYBOARD = BOSINST_DATA_END ; close BOSINST_DATA; return $rc; } #------------------------------------------------------------------------------- =head2 deploy_to_machine =over 2 =item C<$mksysb-Edeploy_to_machine($nim_machine_name, $script_name, $fb_script_name, $resolv_conf_name)> =back =head3 DESCRIPTION =over 2 =item Invokes NIM deployment of the mksysb to the target NIM defined machine. =back =head3 PARAMETERS =over 2 =item * $nim_machine_name (REQUIRED) =item * $script_name (OPTIONAL) =item * $fb_script_name (OPTIONAL) =item * $resolv_conf_name (OPTIONAL) =back =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub deploy_to_machine { my $this = shift; my ($nim_machine_name, $script_name, $fb_script_name, $resolv_conf_name, $image_data_name) = @_; log_print("NIM::Deploy::Mksysb::deploy_to_machine(\'$nim_machine_name\', \'$script_name\', \'$fb_script_name\', \'$resolv_conf_name\', \'$image_data_name\')\n"); my $accept_licenses = ''; if ($Settings{'accept_license'}) { $accept_licenses = '-a accept_licenses=yes'; } my $script = ''; if ($script_name) { $script = "-a script=$script_name"; } my $fb_script = ''; if ($fb_script_name) { $fb_script = "-a fb_script=$fb_script_name"; } my $resolv_conf = ''; if ($resolv_conf_name) { $resolv_conf = "-a resolv_conf=$resolv_conf_name"; } my $image_data = ''; if ($image_data_name) { $image_data = "-a image_data=$image_data_name"; } my $res_group_name = $this->get_nim_name('res_group'); if ($res_group_name && $nim_machine_name) { my $machines = $this->{'info'}->locate_machine_by_name($nim_machine_name); my $res_groups = $this->{'info'}->locate_resource_group_by_name($res_group_name); if ((! $machines->empty()) && (! $res_groups->empty())) { $this->reset_client($nim_machine_name); # machine should be on the same subnet as master invoke("$Cmds{'nim'} -o bos_inst -a source=mksysb -a group=$res_group_name $accept_licenses $script $fb_script $resolv_conf $image_data -a boot_client=no $nim_machine_name"); # add to machine group to maintain deployment history $this->add_to_mac_group($nim_machine_name); } } } #------------------------------------------------------------------------------- sub nim_master_oslevel { my $this = shift; log_print "NIM::Deploy::Mksysb::nim_master_oslevel()\n"; my @rc = (0, 0, 0); my $results = invoke('oslevel -q -s'); if ($#$results >= 2) { foreach my $result (@$results) { chomp($result); if ($result =~ m/^[1-9][0-9][0-9][0-9]-/) { my @levels = split('-', $result); my $version = $levels[0]; my $tl = $levels[1]; my $sp = (($levels[2] eq 'CSP') ? 0 : $levels[2]); @rc = ($version, $tl, $sp); last; } } } log_print("\trc = $rc[0]-$rc[1]-$rc[2]\n"); return @rc; } #------------------------------------------------------------------------------- sub oslevel_r { my $this = shift; log_print "NIM::Deploy::Mksysb::oslevel_r(\'$this->{'name'}\')\n"; my @rc = ('', ''); my $mksysbs = $this->{'info'}->locate_mksysb_by_name($this->{'name'}); if (! $mksysbs->empty()) { my $oslevel_r = $mksysbs->get_first_attr_value($this->{'name'}, 'oslevel_r'); if ($oslevel_r =~ m/([1-9][0-9][0-9][0-9])-([0-9][0-9])/) { @rc = ($1, $2); log_print "\trc = $rc[0]-$rc[1]\n"; } } return @rc; } #------------------------------------------------------------------------------- sub is_deployable { my $this = shift; log_print "NIM::Deploy::Mksysb::is_deployable()\n"; my ($nim_version, $nim_tl, $nim_sp) = $this->nim_master_oslevel(); my $mksysbs = $this->{'info'}->locate_mksysb_by_name($this->{'name'}); if (! $mksysbs->empty()) { my $oslevel_r = $mksysbs->get_first_attr_value($this->{'name'}, 'oslevel_r'); if ($oslevel_r =~ m/([1-9][0-9][0-9][0-9])-([0-9][0-9])/) { my $version = $1; my $tl = $2; if (($nim_version > $version) || ($nim_version == $version) && ($nim_tl >= $tl)) { log_print("\tmksysb \'$this->{'name'}\' is deployable\n"); return 1; } } } log_print("\tmksysb \'$this->{'name'}\' is not deployable\n"); return 0; } #------------------------------------------------------------------------------- sub get_ifs_supported_by_spot { my $this = shift; my ($spot_name) = @_; log_print("NIM::Deploy::Mksysb::get_ifs_supported_by_spot(\'$spot_name\')\n"); my $nim_info = $this->{'info'}; my $spots = $nim_info->locate_spot_by_name($spot_name); my @rc = (); if (! $spots->empty()) { my ($name, $attr_hash) = $spots->get_first_object(); if (defined $attr_hash->{'if_supported'}) { my $ifs_supported = $attr_hash->{'if_supported'}; if (0) { foreach my $if_supported (@$ifs_supported) { log_print("\t$if_supported\n"); } } @rc = @$ifs_supported; } } return \@rc; } #------------------------------------------------------------------------------- sub if_supported_parts { my $this = shift; my ($if_supported) = @_; my @rc = ('', '', ''); # platform, netboot_kernel, network_type if ($if_supported) { @rc = split(/[. ]/, $if_supported) } return @rc; } #------------------------------------------------------------------------------- =head2 valid_machine_netboot_kernel =over 2 =item C<$mksysb-Evalid_machine_netboot_kernel($nim_machine_name)> =back =head3 DESCRIPTION =over 2 =item Checks that the netboot kernel type of the NIM machine definition is valid for the mksysb. Hardware Support 5.1 5.2 5.3 6.1 -------------------------------------------------------------------------------- Kernel Support 32b, 64b 32b, 64b 32b, 64b 64b UP & MP UP & MP UP & MP MP MP =back =head3 PARAMETERS =over 2 =item * $nim_machine_name (REQUIRED) =back =head3 RETURNS =over 2 =item * 0 - invalid =item * 1 - valid =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub valid_machine_netboot_kernel { my $this = shift; my ($nim_machine_name) = @_; log_print("NIM::Deploy::Mksysb::valid_machine_netboot_kernel(\'$nim_machine_name\')\n"); my $machines = $this->{'info'}->locate_machine_by_name($nim_machine_name); if (! $machines->empty()) { my @oslevel = $this->oslevel_r(); my ($machine_name, $machine) = $machines->get_first_object(); my $netboot_kernel = $machine->{'netboot_kernel'}->[0]; if (($oslevel[0] < 5300) && (($netboot_kernel eq 'up') || ($netboot_kernel eq 'mp')) || ($oslevel[0] == 5300) && ($netboot_kernel eq 'mp') || ($oslevel[0] >= 6100) && (($netboot_kernel eq 'mp') || ($netboot_kernel eq '64'))) { log_print("\trc = 1\n"); return 1; } } log_print("\trc = 0\n"); return 0; } #------------------------------------------------------------------------------- =head2 machine_spot_check =over 2 =item C<$mksysb-Emachine_spot_check($nim_machine_name, $spot_name)> =back =head3 DESCRIPTION =over 2 =item Checks that the netboot kernel and the platform values of the NIM machine definition matches that of the SPOT used for the mksysb. =back =head3 PARAMETERS =over 2 =item * $nim_machine_name (REQUIRED) =item * $spot_name (OPTIONAL) =back =head3 RETURNS =over 2 =item * 0 - invalid =item * 1 - valid =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub machine_spot_check { my $this = shift; my ($nim_machine_name, $spot_name) = @_; log_print("NIM::Deploy::Mksysb::machine_spot_check(\'$nim_machine_name\', \'$spot_name\')\n"); my $machines = $this->{'info'}->locate_machine_by_name($nim_machine_name); if (! $machines->empty()) { if (! $spot_name) { my $res_group_name = $this->find_res_group_from_mksysb_name($this->{'name'}); if ($res_group_name) { $spot_name = $this->get_spot_from_res_group($res_group_name); } } if ($spot_name) { my ($machine_name, $machine) = $machines->get_first_object(); my $ifs_supported = $this->get_ifs_supported_by_spot($spot_name); foreach my $if_supported (@$ifs_supported) { my ($platform, $netboot_kernel, $network_type) = $this->if_supported_parts($if_supported); log_print("\tif_supported => $platform $netboot_kernel $network_type\n"); log_print("\tmachine $machine_name => $machine->{'platform'}->[0] $machine->{'netboot_kernel'}->[0]\n"); # eventually should perform check on network type if (($machine->{'netboot_kernel'}->[0] eq $netboot_kernel) && ($machine->{'platform'}->[0] eq $platform)) { log_print("\trc = 1\n"); return 1; } } } } log_print("\trc = 0\n"); return 0; } #------------------------------------------------------------------------------- =head2 resolve_machine_netboot_kernel =over 2 =item C<$mksysb-Eresolve_machine_netboot_kernel($nim_machine_name)> =back =head3 DESCRIPTION =over 2 =item Checks that the netboot kernel and the platform values of the NIM machine definition matches that of the SPOT used for the mksysb by calling machine_spot_check(). Modifies the netboot_kernel of the machine, if necessary. =back =head3 PARAMETERS =over 2 =item * $nim_machine_name (REQUIRED) =back =head3 RETURNS =over 2 =item * 0 - invalid =item * 1 - valid =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub resolve_machine_netboot_kernel { my $this = shift; my ($nim_machine_name) = @_; log_print("NIM::Deploy::Mksysb::resolve_machine_netboot_kernel(\'$nim_machine_name\')\n"); #################################### my @m = mutex_lock($this->{'name'}); #################################### $this->{'info'}->refresh(); my $res_group_name = $this->find_res_group_from_mksysb_name($this->{'name'}); my $spot_name = ''; my $result = 0; if ($res_group_name) { $spot_name = $this->get_spot_from_res_group($res_group_name); } if ($this->valid_machine_netboot_kernel($nim_machine_name) == 1) { $result = $this->machine_spot_check($nim_machine_name, $spot_name); if ($result == 0) { log_print("\tMachine spot check failed\n"); log_print("\tRebuilding the list of machine types and networks that must be supported by network boot images\n"); invoke("$Cmds{'nim'} -o change -a if_discover=yes $this->{'nim_master_name'}"); log_print("\tRebuilding network boot images from the spot\n"); invoke("$Cmds{'nim'} -Fo check $spot_name"); $this->{'info'}->update("$Lang $Cmds{'lsnim'} -lF $spot_name 2>/dev/null"); $result = $this->machine_spot_check($nim_machine_name, $spot_name); } } if ($result == 0) { my $ifs_supported = $this->get_ifs_supported_by_spot($spot_name); foreach my $if_supported (@$ifs_supported) { my ($platform, $netboot_kernel, $network_type) = $this->if_supported_parts($if_supported); log_print("\tChanging netboot_kernel to \'$netboot_kernel\'\n"); invoke("$Cmds{'nim'} -o change -a netboot_kernel=$netboot_kernel $nim_machine_name"); $this->{'info'}->update("$Lang $Cmds{'lsnim'} -lF $nim_machine_name 2>/dev/null"); $result = $this->machine_spot_check($nim_machine_name, $spot_name); last; } } ################ mutex_unlock @m; ################ if ($result == 1) { log_print("\trc = 1\n"); return 1; } log_print("\trc = 0\n"); return 0; } #------------------------------------------------------------------------------- sub extract_image_data_file { my $this = shift; log_print("NIM::Deploy::Mksysb::extract_image_data_file()\n"); my $rc = undef; my $result = invoke("restore -Tqf $this->{'location'} 2>/dev/null | grep ^\./image.data\$"); if ($#$result >= 0) { foreach my $file_name (@$result) { chomp($file_name); if ($file_name =~ m/^\.\/image\.data$/) { $rc = $file_name; log_print("\tfound ./image.data file\n"); } else { log_print("\t./image.data file not found\n"); } last; } if ($rc) { log_print("\trestoring ./image.data file\n"); invoke("restore -xvqf $this->{'location'} ./image.data"); } } return $rc; } #------------------------------------------------------------------------------- =head2 create_image_data_resource =over 2 =item C<$mksysb-Ecreate_image_data_resource($disk_size, $paging_space_size)> =back =head3 DESCRIPTION =over 2 =item Creates a NIM image_data resource from the image.data file extracted from the mksysb. =back =head3 PARAMETERS =over 2 =item * $disk_size (OPTIONAL) The new disk size (MB). =item * $paging_space_size (OPTIONAL) The new paging space size (MB). =back =head3 RETURNS =over 2 =item * The name of the NIM image_data resource. =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub create_image_data_resource { my $this = shift; my ($disk_size, $paging_space_size) = @_; log_print("NIM::Deploy::Mksysb::create_image_data_resource(\'$disk_size MB\', \'$paging_space_size MB\')\n"); my $image_data_name = ''; if ($disk_size || $paging_space_size) { my $file_name = $this->extract_image_data_file(); if ($file_name) { my $image_data = new NIM::Deploy::Mksysb::ImageData($file_name); $image_data->set_default_image_data_values(); # $image_data->print($NIM::Util::Settings{'log_file_handle'}); my $min_lp_delta = 4; if ($min_lp_delta > 0) { $image_data->set_min_free_space_lps_per_filesystem($min_lp_delta); # $image_data->allocated_size(); # called for logging purposes } if ($paging_space_size) { $image_data->resize_paging_space($paging_space_size); # $image_data->paging_space_size(); # called for logging purposes } if ($disk_size) { $image_data->scale_fs_down($disk_size, $min_lp_delta); # $image_data->allocated_size(); # called for logging purposes } # $image_data->print($NIM::Util::Settings{'log_file_handle'}); my $image_data_parent_dir = $this->get_resource_dir('image_data'); $image_data_name = $this->get_nim_name('image_data'); my $image_data_pn = "$image_data_parent_dir/$image_data_name"; my $fh = new FileHandle(">$image_data_pn"); if (defined $fh) { $image_data->print($fh); invoke("$Cmds{'nim'} -o define -t image_data -a server=$this->{'nim_master_name'} -a location=$image_data_pn $image_data_name"); } else { $image_data_name = ''; warn "Unable to create image_data file: $image_data_pn\n"; } } else { warn "Unable to extract the image.data file from the mksysb\n"; } } return $image_data_name; } #------------------------------------------------------------------------------- =head2 delete_image_data_resource =over 2 =item C<$mksysb-Edelete_image_data_resource($image_data_name)> =back =head3 DESCRIPTION =over 2 =item Deletes a NIM image_data resource. =back =head3 PARAMETERS =over 2 =item * $image_data_name (REQUIRED) =back =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub delete_image_data_resource { my $this = shift; my ($image_data_name) = @_; my $image_data = $this->{'info'}->locate_image_data_by_name($image_data_name); if (! $image_data->empty()) { my $location = $image_data->get_first_attr_value($image_data_name, 'location'); unlink($location); invoke("$Cmds{'nim'} -o remove $image_data_name"); } } 1; # __END__ =head1 AUTHOR IBM =head1 BUGS Please report any bugs to the L. =head1 SUPPORT =head1 ACKNOWLEDGEMENTS =head1 SEE ALSO =begin html =end html =cut my $comment = q { #------------------------------------------------------------------------------- http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.install/doc/insgdrf/addl_create_netboot_cmd_line.htm Use this information to manage network boot images to support only the defined clients and networks. To rebuild the list of machine types and networks that must be supported by network boot images in the NIM environment, perform a change operation on the NIM master with the if_discover=yes attribute: nim -o change -a if_discover=yes master To rebuild network boot images from a SPOT, perform a check operation on the SPOT with the force option: nim -Fo check spot_name If an administrator prefers to have NIM always create all possible boot images from the SPOT resources, the if_prebuild=yes attribute can be specified on the master: nim -o change -a if_prebuild=yes master To return NIM to the behavior of creating only the boot images that are required for the environment, remove the if_prebuild attribute from the master by setting it to "no": nim -o change -a if_prebuild=no master };