# IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos720 src/bos/usr/lpp/bosinst/samples/NIM/LPAR.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 # @(#)58 1.1 src/bos/usr/lpp/bosinst/samples/NIM/LPAR.pm, bosinst, bos720 3/29/09 15:20:53 package NIM::LPAR; =head1 NAME NIM::LPAR =over 2 =item NIM::LPAR base class. =back =head1 VARIABLES =head2 %Settings =over 2 =item * project =back =head2 %Defaults =over 2 =item * type =item * nim_type =item * platform =item * netboot_kernel =item * cable_type =item * connect =item * network_type =item * subnet_mask =item * net_settings =back =head1 FUNCTIONS =cut BEGIN { # print "NIM::LPAR BEGIN\n"; use Hash::Util; use NIM::Util; use Sys::Hostname; %Settings = ('project' => 'sysp'); # may need to default some of these values from those of the nim master's %Defaults = ('type' => 'aix', 'nim_type' => 'standalone', 'platform' => 'chrp', 'netboot_kernel' => 'mp', 'cable_type' => 'N/A', 'connect' => 'shell', 'network_type' => 'ent', 'subnet_mask' => '255.255.255.0', 'net_settings' => 'auto auto', 'hmc' => '', 'hmc_user' => ''); } #------------------------------------------------------------------------------- =head2 new =over 2 =item C =back =head3 DESCRIPTION =over 2 =item Instantiates a NIM::LPAR object. =back =head3 PARAMETERS =over 2 =item * %args =over 2 =item * type =item * name =item * profile =item * cec =item * hmc =item * hmc_user =item * host_name =item * nim_machine_name =item * nim_type =item * type =item * platform =item * netboot_kernel =item * cable_type =item * connect =item * net_settings =item * mac_addr =item * if =item * ip_addr =item * gateway =item * network_type =item * subnet_mask =item * domain =item * nameservers =back =back =head3 RETURNS =over 2 =item * Reference to the newly created hash object. =back =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub new { my $type = shift; my %args = @_; log_print("NIM::LPAR::new(@_)\n"); my $this = {}; bless $this, $type; my $nim_info = ((defined $NIM::Config{'info'}) ? $NIM::Config{'info'} : new NIM::Info()); $this->{'info'} = $nim_info; ### $this->{'type'} = (defined $args{'type'} ? $args{'type'} : $Defaults{'type'}); $this->{'name'} = (defined $args{'name'} ? $args{'name'} : ''); $this->{'profile'} = (defined $args{'profile'} ? $args{'profile'} : ''); $this->{'cec'} = (defined $args{'cec'} ? $args{'cec'} : ''); ### $this->{'hmc'} = (defined $args{'hmc'} ? $args{'hmc'} : $Defaults{'hmc'}); $this->{'hmc_user'} = (defined $args{'hmc_user'} ? $args{'hmc_user'} : $Defaults{'hmc_user'}); ### $this->{'hostname'} = (defined $args{'hostname'} ? $args{'hostname'} : ''); $this->{'nim_machine_name'} = (defined $args{'nim_machine_name'} ? $args{'nim_machine_name'} : ''); $this->{'nim_type'} = (defined $args{'nim_type'} ? $args{'nim_type'} : $Defaults{'nim_type'}); $this->{'platform'} = (defined $args{'platform'} ? $args{'platform'} : $Defaults{'platform'}); $this->{'netboot_kernel'} = (defined $args{'netboot_kernel'} ? $args{'netboot_kernel'} : $Defaults{'netboot_kernel'}); $this->{'cable_type'} = (defined $args{'cable_type'} ? $args{'cable_type'} : $Defaults{'cable_type'}); $this->{'connect'} = (defined $args{'connect'} ? $args{'connect'} : $Defaults{'connect'}); $this->{'net_settings'} = (defined $args{'net_settings'} ? $args{'net_settings'} : $Defaults{'net_settings'}); $this->{'mac_addr'} = (defined $args{'mac_addr'} ? $args{'mac_addr'} : '0'); $this->{'if'} = (defined $args{'if'} ? $args{'if'} : ''); ### $this->{'ip_addr'} = (defined $args{'ip_addr'} ? $args{'ip_addr'} : ''); $this->{'gateway'} = (defined $args{'gateway'} ? $args{'gateway'} : ''); $this->{'network_type'} = (defined $args{'network_type'} ? $args{'network_type'} : $Defaults{'network_type'}); $this->{'subnet_mask'} = (defined $args{'subnet_mask'} ? $args{'subnet_mask'} : $Defaults{'subnet_mask'}); $this->{'domain'} = (defined $args{'domain'} ? $args{'domain'} : ''); $this->{'nameservers'} = (defined $args{'nameservers'} ? $args{'nameservers'} : ''); ### $this->{'disk_size'} = (defined $args{'disk_size'} ? $args{'disk_size'} : ''); $this->{'paging_space_size'} = (defined $args{'paging_space_size'} ? $args{'paging_space_size'} : ''); ### if ($this->{'domain'} && ($this->{'hostname'} =~ m/^[^\.]+$/)) { $this->{'hostname'} = "$this->{'hostname'}.$this->{'domain'}"; } if ($this->{'hostname'} && (! $this->{'nim_machine_name'})) { $this->{'nim_machine_name'} = $this->{'hostname'}; $this->{'nim_machine_name'} =~ s/^([^\.]+).*$/$1/; } if ((! $this->{'ip_addr'}) && $this->{'hostname'}) { my ($name, $aliases, $addrtype, $length, @addr) = gethostbyname $this->{'hostname'}; my ($a, $b, $c, $d) = unpack('C4', $addr[0]); # print "$a.$b.$c.$d\n"; $this->{'ip_addr'} = "$a.$b.$c.$d"; } if ($this->{'ip_addr'} && (! $this->{'gateway'})) { $this->{'gateway'} = $this->{'ip_addr'}; $this->{'gateway'} =~ s/^([^\.]+)\.([^\.]+)\.([^\.]+)\.([^\.]+)$/$1\.$2\.$3\.1/; } if ((! $this->{'domain'}) && ($this->{'hostname'} =~ m/^[^\.]+\.(.+)$/)) { $this->{'domain'} = $1; } if (! $this->{'if'}) { $this->{'if'} = 'find_net ' . $this->{'hostname'} . ' ' . $this->{'mac_addr'}; } $this->{'update_nim'} = (defined $args{'update_nim'} ? $args{'update_nim'} : '1'); if ($this->{'update_nim'} && ($this->{'update_nim'} != 0)) { # the following is all that's needed to define a machine if a pre-existing network is found # invoke("$Cmds{'nim'} -o define -t $this->{'nim_type'} -a if1=\"$this->{'if'}\" $this->{'nim_machine_name'}"); # # net_definition values only used if a network isn't found my $nim_attrs = "-a if1=\"$this->{'if'}\" " . "-a cable_type1=$this->{'cable_type'} -a netboot_kernel=$this->{'netboot_kernel'} " . "-a net_definition=\"$this->{'network_type'} $this->{'subnet_mask'}\" " . "-a connect=$this->{'connect'} -a net_settings1=\"$this->{'net_settings'}\""; my $machines = $nim_info->locate_machine_by_name($this->{'nim_machine_name'}); if ($machines->empty()) { # create NIM machine definition invoke("$Cmds{'nim'} -o define -t $this->{'nim_type'} $nim_attrs $this->{'nim_machine_name'}"); } else { # the cpuid needs to be reset if we allow for the same nim object # to be reused for a different lpar $nim_attrs = $nim_attrs . " -a cpuid="; invoke("$Cmds{'nim'} -o change $nim_attrs $this->{'nim_machine_name'}"); } } return $this; } #------------------------------------------------------------------------------- sub delete { my $this = shift; log_print("NIM::LPAR::delete()\n"); invoke("$Cmds{'nim'} -o remove $this->{'nim_machine_name'}"); my $res_group_name = $this->delete_resources(); if ($res_group_name) { invoke("$Cmds{'nim'} -o remove $res_group_name"); } } #------------------------------------------------------------------------------- sub delete_by_name { my ($nim_machine_name) = @_; log_print("NIM::LPAR::delete_by_name(\'$nim_machine_name\')\n"); my $this = new NIM::LPAR('nim_machine_name' => $lpar_name, 'update_nim' => 0); $this->delete(); } #------------------------------------------------------------------------------- sub get_nameservers { my $this = shift; my @rc = (); my $nameservers = $this->{'nameservers'}; @rc = split(/[ \t]+/, $nameservers); return @rc; } #------------------------------------------------------------------------------- sub update_from_image_attributes { # # 'machine' can have # platform = chrp # netboot_kernel = 64 # # 'master' is a subclass of 'machine' and can also have # if_defined = chrp.64.ent # if_defined = chrp.mp.ent # # 'spot' can have # platform_defined = chrp # arch = power # if_supported = chrp.64 ent # if_supported = chrp.mp ent # my $this = shift; log_print "NIM::LPAR::update_from_image_attributes()\n"; $nim_info = $this->{'info'}; } #------------------------------------------------------------------------------- =head2 netboot =over 2 =item C<$lpar-Enetboot()> =back =head3 DESCRIPTION =over 2 =item Network boot the LPAR via its HMC. =back =head3 PARAMETERS =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub netboot { my $this = shift; log_print "NIM::LPAR::netboot($this->{'hostname'})\n"; my $host = hostname; my $nim_master_ip_addr = ''; if ($host) { my ($name, $aliases, $addrtype, $length, @addr) = gethostbyname $host; my ($a, $b, $c, $d) = unpack('C4', $addr[0]); $nim_master_ip_addr = "$a.$b.$c.$d"; } if ($this->{'hmc_user'} && $this->{'hmc'} && $this->{'network_type'} && $nim_master_ip_addr && $this->{'gateway'} && $this->{'ip_addr'} && $this->{'name'} && $this->{'profile'} && $this->{'cec'}) { # run restricted shell /usr/hmcrbin hmc commands # in restricted mode, some hmc's require that commands not be fully qualified path names - i.e. no '/'s # change boot mode to normal invoke_ssh("$this->{'hmc_user'}\@$this->{'hmc'}", "chsyscfg -r prof -m $this->{'cec'} -i \"name=$this->{'profile'},lpar_name=$this->{'name'},boot_mode=norm\""); # older versions of the HMC does not support the -K option - pre-hmcV7.3.2.0 my $snm = ''; if (0) { my $result = invoke_ssh("$this->{'hmc_user'}\@$this->{'hmc'}", "lshmc -V -F base_version"); my $hmc_version = $result->[0]; if ($hmc_version =~ m/\"V([1-9][0-9]*)\.([1-9][0-9]*)\.([1-9][0-9]*)\n\"\n$/) { if (($1 > 7) || ($1 == 7) && ($2 > 320) || ($1 == 7) && ($2 == 320) && ($3 > 0)) { $snm = "-K $this->{'subnet_mask'}"; } } } else { my $result = invoke_ssh("$this->{'hmc_user'}\@$this->{'hmc'}", "lpar_netboot --help | grep 'Subnetmask IP address' | grep -- -K"); if ($#$result >= 0) { # print "result => $result->[0]\n"; $snm = "-K $this->{'subnet_mask'}"; } } my ($speed, $duplex) = split(/[ \t]+/, $this->{'net_settings'}); if (! $speed) { $speed = "auto"; } if (! $duplex) { $duplex = "auto"; } # same as lpar_netboot -f option - not needed for the SystemU1 (550) system # invoke_ssh("$this->{'hmc_user'}\@$this->{'hmc'}", "rmvterm -m $this->{'cec'} -p $this->{'name'}"); # add -D option to perform ping test # The -f and -D options seems to be needed for the pcloud (550) system but not for SystemU1 (570) # -x -v options are for debug invoke_ssh("$this->{'hmc_user'}\@$this->{'hmc'}", "lpar_netboot -x -v -i -f -D -t $this->{'network_type'} -s $speed -d $duplex -S $nim_master_ip_addr -G $this->{'gateway'} -C $this->{'ip_addr'} $snm \"$this->{'name'}\" \"$this->{'profile'}\" \"$this->{'cec'}\""); } else { log_print "\tUnable to netboot the LPAR\n"; log_print "\t- Unspecified hmc user\n" if (! $this->{'hmc_user'}); log_print "\t- Unspecified hmc\n" if (! $this->{'hmc'}); log_print "\t- Unspecified network type\n" if (! $this->{'network_type'}); log_print "\t- Unspecified nim master\n" if (! $nim_master_ip_addr); log_print "\t- Unspecified gateway\n" if (! $this->{'gateway'}); log_print "\t- Unspecified ip address\n" if (! $this->{'ip_addr'}); log_print "\t- Unspecified partition name\n" if (! $this->{'name'}); log_print "\t- Unspecified partition profile name\n" if (! $this->{'profile'}); log_print "\t- Unspecified cec name\n" if (! $this->{'cec'}); } } #------------------------------------------------------------------------------- =head2 print_deploy_status =over 2 =item C<$lpar-Eprint_deploy_status()> =back =head3 DESCRIPTION =over 2 =item Prints the deployment status obtained via NIM machine state information. Also serializes/waits for deployment operation to complete or fail. =back =head3 PARAMETERS =head3 RETURNS =head3 EXCEPTIONS =cut #------------------------------------------------------------------------------- sub print_deploy_status { my $this = shift; log_print("NIM::LPAR::print_deploy_status()\n"); my $rc = 0; my $nim_info = $this->{'info'}; my $nim_machine_name = $this->{'nim_machine_name'}; my $keep_printing = 0; my ($prev_Cstate, $prev_Cstate_result, $prev_Mstate, $prev_info, $prev_err_info) = ('', '', '', '', ''); my $i = 0; my $repeat_count = 0; do { $nim_info->refresh("$Lang $Cmds{'lsnim'} -lF $nim_machine_name 2>/dev/null"); my $machines = $nim_info->locate_machine_by_name($nim_machine_name); if (! $machines->empty()) { my ($name, $attr_hash) = $machines->get_first_object(); select(STDOUT); $| = 1; # flush stdout on next output if ($i && ($prev_Cstate eq $attr_hash->{'Cstate'}->[0]) && ($prev_Mstate eq $attr_hash->{'Mstate'}->[0]) && ((! defined $attr_hash->{'err_info'}) && (! $prev_err_info) || (defined $attr_hash->{'err_info'}) && ($attr_hash->{'err_info'} eq $prev_err_info)) && $prev_info && (defined $attr_hash->{'info'}) && ($attr_hash->{'info'}->[0] =~ m/^BOS install [0-9]+% complete/)) { my $info = $attr_hash->{'info'}->[0]; print "\rinfo = $info"; if ($prev_info) { for (my $diff = length($prev_info) - length($info); $diff > 0; $diff--) { print ' '; } } } else { if ((defined $attr_hash->{'Cstate'}) && ($prev_Cstate ne $attr_hash->{'Cstate'}->[0]) || (defined $attr_hash->{'Mstate'}) && ($prev_Mstate ne $attr_hash->{'Mstate'}->[0]) || (defined $attr_hash->{'Cstate_result'}) && ($prev_Cstate_result ne $attr_hash->{'Cstate_result'}->[0]) || (defined $attr_hash->{'info'}) && ($prev_info ne $attr_hash->{'info'}->[0]) || (defined $attr_hash->{'err_info'}) && ($prev_err_info ne $attr_hash->{'err_info'}->[0])) { if ($i) { print "\n"; } print "==="; my @attr_keys = ('Cstate', 'Cstate_result', 'Mstate', 'info', 'err_info'); foreach my $attr_key (@attr_keys) { if (defined $attr_hash->{$attr_key}) { print "\n$attr_key = "; my $values = $attr_hash->{$attr_key}; foreach my $value (@$values) { print "$value"; } } } } } if (($i < 3) || # keep printing for a minimum of 90 seconds ($attr_hash->{'Cstate'}->[0] eq 'BOS installation has been enabled') || (($attr_hash->{'Cstate'}->[0] eq 'Base Operating System installation is being performed') || ($attr_hash->{'Cstate'}->[0] eq 'post install processing is being performed') || ($attr_hash->{'Cstate'}->[0] eq 'BOS installation has been enabled') || ($attr_hash->{'Cstate'}->[0] eq 'customization is being performed')) && ($attr_hash->{'Mstate'}->[0] eq 'in the process of booting') && ((! defined $attr_hash->{'info'}) || ($attr_hash->{'info'}->[0] ne 'prompting_for_data_at_console')) || # during reboot ($attr_hash->{'Cstate'}->[0] eq 'ready for a NIM operation') && ($attr_hash->{'Cstate_result'}->[0] ne 'failure') && (($prev_Mstate eq 'in the process of booting') || ($prev_Mstate eq 'not running')) && ($attr_hash->{'Mstate'}->[0] eq 'in the process of booting') || ($attr_hash->{'Mstate'}->[0] eq 'not running')) { $keep_printing = 1; my $new_Cstate_result = (defined $attr_hash->{'Cstate_result'}) ? $attr_hash->{'Cstate_result'}->[0] : ''; my $new_info = (defined $attr_hash->{'info'}) ? $attr_hash->{'info'}->[0] : ''; my $new_err_info = (defined $attr_hash->{'err_info'}) ? $attr_hash->{'err_info'}->[0] : ''; if (($prev_Cstate ne $attr_hash->{'Cstate'}->[0]) || ($prev_Mstate ne $attr_hash->{'Mstate'}->[0]) || ($prev_Cstate_result ne $new_Cstate_result) || ($prev_info ne $new_info) || ($prev_err_info ne $new_err_info)) { $repeat_count = 0; $prev_Cstate = $attr_hash->{'Cstate'}->[0]; $prev_Mstate = $attr_hash->{'Mstate'}->[0]; $prev_Cstate_result = $new_Cstate_result; $prev_info = $new_info; $prev_err_info = $new_err_info; } elsif ($repeat_count++ > 20) { $keep_printing = 0; } if ($keep_printing) { $i++; sleep 30; } } else { $keep_printing = 0; print "\n"; if (($attr_hash->{'Cstate'}->[0] eq 'ready for a NIM operation') && (($prev_Mstate eq 'in the process of booting') || ($prev_Mstate eq 'not running')) && ($attr_hash->{'Mstate'}->[0] eq 'currently running')) { print "===\n"; print "Installation complete!\n"; $rc = 1; } else { print "===\n"; print "Cstate = $attr_hash->{'Cstate'}->[0]\n"; print "Mstate = $attr_hash->{'Mstate'}->[0]\n"; print "prev_Mstate = $prev_Mstate\n"; } } } } while ($keep_printing); # refresh with complete nim info $nim_info->refresh(); return $rc; } #------------------------------------------------------------------------------- sub get_nim_name { my $this = shift; my ($type) = @_; if ($type ne 'res_group') { $type = ''; } return ($type ? sprintf("%s\-%s\-%s", $Settings{'project'}, $this->{'nim_machine_name'}, $type) : ''); } #------------------------------------------------------------------------------- sub add_to_res_group { my $this = shift; my ($resource_name, $resource_type) = @_; log_print("NIM::LPAR::add_to_res_group(\'$resource_name\', \'$resource_type\')\n"); if ($resource_name && $resource_type) { my $res_group_name = $this->get_nim_name('res_group'); my $res_groups = $this->{'info'}->locate_resource_group_by_name($res_group_name); if ($res_groups->empty()) { log_print "\tDefining res_group: $res_group_name\n"; invoke("$Cmds{'nim'} -o define -t res_group -a $resource_type=$resource_name $res_group_name"); } else { my ($res_group, $attr_hash) = $res_groups->get_first_object(); my $index = 0; for (my $i = 1; $index == 0; $i++) { if (! $attr_hash->{"member${i}"}) { $index = $i; } } log_print "\tRedefining res_group: $res_group_name\n"; invoke("$Cmds{'nim'} -o change -a member${index}=$resource_name $res_group_name"); } $this->{'info'}->update("$Lang $Cmds{'lsnim'} -lF $res_group_name 2>/dev/null"); } } #------------------------------------------------------------------------------- sub reset_by_name { my ($nim_machine_name) = @_; log_print("NIM::LPAR::reset_by_name(\'$nim_machine_name\')\n"); invoke("$Cmds{'nim'} -Fo reset $nim_machine_name"); for (my $i = 0; $i < 2; $i++) { # deallocate operation does not take the verbose attribute invoke("/usr/sbin/nim -Fo deallocate -asubclass=all $nim_machine_name"); } } #------------------------------------------------------------------------------- sub reset { my $this = shift; log_print("NIM::LPAR::reset()\n"); reset_by_name($this->{'nim_machine_name'}); } #------------------------------------------------------------------------------- sub allocated_resources { my $this = shift; log_print("NIM::LPAR::allocated_resources()\n"); my @rc = (); my $nim_info = $this->{'info'}; my $nim_machine_name = $this->{'nim_machine_name'}; $nim_info->refresh("$Lang $Cmds{'lsnim'} -lF $nim_machine_name 2>/dev/null"); my $machines = $nim_info->locate_machine_by_name($nim_machine_name); if (! $machines->empty()) { my ($name, $attr_hash) = $machines->get_first_object(); my @resource_types = ('mksysb', 'spot', 'fb_script', 'image_data', 'bosinst_data', 'resolv_conf'); foreach my $resource_type (@resource_types) { my $resource_list = $attr_hash->{$resource_type}; if ($resource_list) { foreach my $resource_name (@$resource_list) { push @rc, ($resource_name); } } } } log_printf("\t%d allocated resources\n", $#rc + 1); return \@rc; } #------------------------------------------------------------------------------- sub delete_resources { my $this = shift; log_print("NIM::LPAR::delete_resources()\n"); my $nim_info = $this->{'info'}; my $res_group_name = $this->get_nim_name('res_group'); $this->{'info'}->update("$Lang $Cmds{'lsnim'} -lF $res_group_name 2>/dev/null"); my $res_groups = $nim_info->locate_resource_group_by_name($res_group_name); if ($res_groups->empty()) { $res_group_name = ''; } else { my ($res_group, $attr_hash) = $res_groups->get_first_object(); my $max = 99; my @members = (); for (my $i = 1; $i <= $max; $i++) { my $member_value = $attr_hash->{"member${i}"}; if ($member_value) { my $resources = $nim_info->locate_resource_by_name($member_value->[0]); my $location = $resources->get_first_attr_value($member_value->[0], 'location'); push @members, ($member_value->[0]); if ($location && -e $location) { unlink($location); } } } $this->reset(); foreach my $member (@members) { invoke("$Cmds{'nim'} -o remove $member"); } } return $res_group_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