# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/lpp/bosinst/samples/NIM.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 

# @(#)51        1.1  src/bos/usr/lpp/bosinst/samples/NIM.pm, bosinst, bos720 3/29/09 15:20:43

package NIM;

=head1 NAME

NIM

=over 2

=item * NIM functions to perform higher level operations.

=item * Various startup options are available.

=over 2

=item * MySettings.pm

=item * MyConfig.pm

=item * NIM.xml content via stdin

=item * NIM.xml

=back

=back

=head1 VARIABLES

=head2 %Config

=over 2

=item * info

NIM::Info object created to capture NIM database information.

=item * images

Images defined to the module.

=item * lpars

LPARs defined to the module.

=back

=head1 FUNCTIONS

=cut

#-------------------------------------------------------------------------------
BEGIN
{
  # print "NIM BEGIN\n";

  use Hash::Util;

  use NIM::Util;
  use NIM::Info;
  use NIM::Deploy::Mksysb;
  use NIM::LPAR::AIX;
  use NIM::LPAR::VIOS;
  use Cwd;

  my $found = 0;
  if ((-e "MySettings.pm") && (-r "MySettings.pm"))
  {
    $found = 1;
    my $dir = getcwd();
    # print "loading $dir/MySettings.pm\n";
  }
  else
  {
    foreach my $path (@INC)
    {
      if ((-e "$path/MySettings.pm") && (-r "$path/MySettings.pm"))
      {
	$found = 1;
	# print "loading $path/MySettings.pm\n";
      }
    }
  }
  require MySettings if $found;

  %Config = ('info'	=> undef,
	     'images'	=> undef,
	     'lpars'	=> undef);
}

#-------------------------------------------------------------------------------
INIT
{
  # print "NIM INIT\n";

  if (is_nim_master())
  {
    warn "Mismatched NIM filesets detected\n" if (check_nim_filesets() != 0);

    $Config{'images'} = {};
    $Config{'lpars'} = {};
    
    # do not have NIM always create all possible boot images from the SPOT resources
    # when netboot_kernel=64 exists, the operation complains about aix 5.3 spots and leaves them in an invalid state
    invoke("$Cmds{'nim'} -o change -a if_prebuild=no master");

    my $config_file = NIM::Util::which_pm('MyConfig.pm');
    if (defined $config_file)
    {
      # print "loading $config_file\n";
      require MyConfig;
    }

    my $xml_file = NIM::Util::read_stdin();		# xml string from stdin
    if (! $xml_file)
    {
      $xml_file = NIM::Util::which_pm('NIM.xml');	# file name
    }
    if ($xml_file)
    {
      # print "loading $xml_file\n";
      my $input = NIM::Util::parse_xml_input_file($xml_file);
      process_xml_input($input);
    }

    if (defined $Config{'info'})
    {
      $Config{'info'}->refresh();
    }
    else
    {
      $Config{'info'} = new NIM::Info();
    }
  }

  if (! $NIM::Util::Settings{'xml_results'})
  {
    # NIM::Util::reset_sigwarn_sigdie_handlers();
  }
}

#-------------------------------------------------------------------------------

=head2 is_nim_master

=over 2

=item

C<NIM::is_nim_master()>

=back

=head3 DESCRIPTION

=over 2

=item

Queries the system to check if it is a NIM master.

=back

=head3 PARAMETERS

=head3 RETURNS

=over 2

=item * 0 - Not NIM master.

=item * 1 - Is NIM master.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub is_nim_master
{
  my $nim_info = new NIM::Info("$Lang $Cmds{'lsnim'} -lt master 2>/dev/null");
  return ($nim_info->empty() ? 0 : 1);
}

#-------------------------------------------------------------------------------
sub object_type
{
  my ($object_name) = @_;

  my @rc = ();
  eval
  {
    my $nim_info = new NIM::Info("$Lang $Cmds{'lsnim'} -l $object_name 2>/dev/null");
    @rc = $nim_info->get_attr_values($object_name, 'type');
  };
  return (($@ || ($#rc != 0)) ? '' : $rc[0]);
}

#-------------------------------------------------------------------------------

=head2 add_lpar

=over 2

=item

C<NIM::add_lpar(%args)>

=back

=head3 DESCRIPTION

=over 2

=item

Define a NIM::LPAR object to the module and the equivalent standalone machine
to NIM.  If the standalone machine is already defined in NIM, the NIM object
may be redefined with any differing attributes.

=back

=head3 PARAMETERS

=over 2

=item * %args (REQUIRED)

Hash of arguments used to specify the attributes used to define the LPAR to
NIM as a standalone machine.

=back

=head3 RETURNS

=over 2

=item * Reference to the NIM::LPAR hash object representing the LPAR.

=item * undef - If invalid LPAR specification.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub add_lpar
{
  my %args = @_;

  log_print "NIM::add_lpar(@_)\n";

  my $rc = undef;
  if (defined $args{'type'} && defined $args{'hostname'})
  {
    my $name = $args{'hostname'};
    $name =~ s/([^\.]+).*/$1/;

    log_print "\tAdding LPAR \'$name\'\n";
    if (defined $Config{'lpars'}->{$name})
    {
      $rc = $Config{'lpars'}->{$name};
      log_print "\tAn LPAR by the same name \'$name\' was already added\n";
    }
    else
    {
      my %handler =
	(
	 'aix'	=> sub { $rc = $Config{'lpars'}->{$name} = new NIM::LPAR::AIX(@_); },
	 'vios'	=> sub { $rc = $Config{'lpars'}->{$name} = new NIM::LPAR::VIOS(@_); }
	);
      
      my $code = $handler{$args{'type'}};
      if ($code)
      {
	$code->(@_);
      }
      else
      {
	warn "\tInvalid LPAR type \'$args{'type'}\' entered";
      };
    }
  }
  else
  {
    log_print "An incomplete LPAR specification encountered\n";
  }
  return $rc;
}

#-------------------------------------------------------------------------------
sub rm_lpar
{
  my ($name) = @_;

  log_print "NIM::rm_lpar($name)\n";
  if (defined $Config{'lpars'}->{$name})
  {
    # uncomment the following line to delete machine definition in nim
    # $Config{'lpars'}->{$hostname}->delete();

    delete $Config{'lpars'}->{$name};
  }
}

#-------------------------------------------------------------------------------
sub print_lpars
{
  my ($fh) = @_;

  if (! $fh)
  {
    $fh = STDOUT;
  }
  
  if (defined $Config{'lpars'})
  {
    my $num_lpars = scalar keys %{$Config{'lpars'}};
    printf $fh "NIM config - %d lpar%s\n", $num_lpars, ($num_lpars == 1 ? '' : 's');

    while (my ($name, $lpar) = each(%{$Config{'lpars'}}))
    {
      print $fh "\t$name\n";
      while (my ($key, $value) = each(%$lpar))
      {
	if ($key ne 'info')
	{
	  print $fh "\t\t$key = \'$value\'\n";
	}
      }
    }
  }
}

#-------------------------------------------------------------------------------

=head2 add_image

=over 2

=item

C<NIM::add_image(%args)>

=back

=head3 DESCRIPTION

=over 2

=item

Define a NIM::Deploy object to the module and the equivalent image object to
NIM.  If the image object is already defined in NIM, the NIM object may be
redefined with any differing attributes.

=back

=head3 PARAMETERS

=over 2

=item * %args (REQUIRED)

Hash of arguments used to specify the attributes used to define the image to
NIM.

=back

=head3 RETURNS

=over 2

=item * Reference to the NIM::Deploy hash object representing the image.

=item * undef - If invalid image specification.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub add_image
{
  my %args = @_;

  log_print "NIM::add_image(@_)\n";

  my $rc = undef;
  if (defined $args{'type'} && (defined $args{'name'} || defined $args{'location'}))
  {
    my $image = undef;
    if ($args{'type'} eq 'mksysb')
    {
      $image = new NIM::Deploy::Mksysb(@_);
      if ($image)
      {
	if ($Config{'images'}->{$image->{'name'}})
	{
	  $rc = $Config{'images'}->{$image->{'name'}};
	  log_print "\tAn image by the same name \'$name\' or \'$location\'was already added\n";
	}
	else
	{
	  log_print "\tAdded image \'$args{'name'}:$args{'location'}\'\n";
	  log_print "\tname = \'$image->{'name'}\'\n";
	  log_print "\timage = \'$image\'\n";
	  $rc = $Config{'images'}->{$image->{'name'}} = $image;
	  log_print "\timage = \'$Config{'images'}->{$image->{'name'}}\'\n";
	}
      }
      else
      {
	log_print "\tInvalid image \'$args{'name'}:$args{'location'}\' entered\n";
      }
    }
    else
    {
      log_print "\tInvalid image type \'$args{'type'}\' entered\n";
    }
  }
  else
  {
    log_print "An incomplete image specification encountered\n";
  }
  return $rc;
}

#-------------------------------------------------------------------------------
sub rm_image
{
  my ($name) = @_;

  log_print "NIM::rm_image($name)\n";
  if (defined $Config{'image'}->{$name})
  {
    delete $Config{'images'}->{$name};
  }
}

#-------------------------------------------------------------------------------
sub print_images
{
  my ($fh) = @_;

  if (! $fh)
  {
    $fh = STDOUT;
  }
  
  if (defined $Config{'images'})
  {
    my $num_images = scalar keys %{$Config{'images'}};
    printf $fh "NIM config - %d image%s\n", $num_images, ($num_images == 1 ? '' : 's');

    while (my ($name, $image) = each(%{$Config{'images'}}))
    {
      print $fh "\t$name\n";
      while (my ($key, $value) = each(%$image))
      {
	if ($key ne 'info')
	{
	  print $fh "\t\t$key = \'$value\'\n";
	}
      }
    }
  }
}

#-------------------------------------------------------------------------------
sub xml_input_file_to_lpars
{
  my ($input) = @_;
  
  my $ref_type = ref($input->{'config'}->{'lpar'});
  if ($ref_type eq 'HASH')
  {
    add_lpar(%{$input->{'config'}->{'lpar'}});
  }
  elsif ($ref_type eq 'ARRAY')
  {
    foreach my $lpar (@{$input->{'config'}->{'lpar'}})
    {
      add_lpar(%$lpar);
    }
  }
}

#-------------------------------------------------------------------------------
sub xml_input_file_to_lpar_defaults
{
  my ($input) = @_;
  
  my $ref_type = ref($input->{'settings'}->{'lpar'});
  if ($ref_type eq 'HASH')
  {
    while (my ($key, $value) = each(%{$input->{'settings'}->{'lpar'}}))
    {
      $NIM::LPAR::Defaults{$key} = $value;
    }
  }
  elsif ($ref_type eq 'ARRAY')
  {
    warn "Only a single pauto/settings/lpar element should be specified";
    foreach my $lpar (@{$input->{'settings'}->{'lpar'}})
    {
      while (my ($key, $value) = each(%$lpar))
      {
	$NIM::LPAR::Defaults{$key} = $value;
      }
    }
  }
}

#-------------------------------------------------------------------------------
sub xml_input_file_to_images
{
  my ($input) = @_;
  
  my $ref_type = ref($input->{'config'}->{'image'});
  if ($ref_type eq 'HASH')
  {
    add_image(%{$input->{'config'}->{'image'}});
  }
  elsif ($ref_type eq 'ARRAY')
  {
    foreach my $image (@{$input->{'config'}->{'image'}})
    {
      add_image(%$image);
    }
  }
  # print_images();
}

#-------------------------------------------------------------------------------
sub update_settings
{
  my ($key, $value) = @_;

  if ($key eq 'name')
  {
    $key = 'project';
  }
  
  my @hash_refs = ( \%NIM::Util::Settings, \%NIM::Info::Settings, \%NIM::LPAR::Settings, \%NIM::Deploy::Settings );
  foreach my $settings (@hash_refs)
  {
    if (defined $settings->{$key})
    {
      $settings->{$key} = $value;
    }
  }
}

#-------------------------------------------------------------------------------
sub xml_input_file_to_project_settings
{
  my ($input) = @_;

  my $ref_type = ref($input->{'settings'}->{'project'});
  if ($ref_type eq 'HASH')
  {
    while (my ($key, $value) = each(%{$input->{'settings'}->{'project'}}))
    {
      update_settings($key, $value);
    }
  }
  elsif ($ref_type eq 'ARRAY')
  {
    warn "Only a single pauto/settings/project element should be specified";
    foreach my $project (@{$input->{'settings'}->{'project'}})
    {
      while (my ($key, $value) = each(%$project))
      {
	update_settings($key, $value);
      }
    }
  }
}

#-------------------------------------------------------------------------------

=head2 process_xml_input

=over 2

=item

C<NIM::process_xml_input($xml_input)>

=back

=head3 DESCRIPTION

=over 2

=item

Parses the XML input data structure and creates the appropriate NIM objects or
module settings.

Sample xml file content:

=back

=pod

      <?xml version="1.0"?>
      <pauto>
       <settings>
         <project>
           <name>sysp</name>
           <work_dir>/var/ibm/systemp/nim</work_dir>
           <nim_res_dir>/export/nim</nim_res_dir>
           <remote_work_dir>/opt/IBM/AE</remote_work_dir>
         </project>
     
         <lpar>
           <type></type>
           <name></name>
           <profile></profile>
           <cec></cec>
           <hmc></hmc>
           <hmc_user></hmc_user>
           <vios></vios>
           <domain></domain>
           <nameservers></nameservers>
         </lpar>
       </settings>
     
       <config>
         <lpar>
           <type>aix</type>
           <name>ipv4-076</name>
           <profile></profile>
           <cec></cec>
           <hmc></hmc>
           <hmc_user></hmc_user>
           <vios></vios>
           <hostname>ipv4-076</hostname>
           <domain>austin.ibm.com</domain>
           <nameservers></nameservers>
         </lpar>
         <image>
           <type>mksysb</type>
           <name>was7_5300-07master_sysb</name>
           <location>/export/nim/mksysb/was7_5300-07_mksysb</location>
           <ae_dir>/tmp/nimext/AE</ae_dir>
           <ae_xml>/tmp/nimext/AE/AP/was.ap</ae_xml>
           <ae_cmd>sh /opt/IBM/AE/ae -a /opt/IBM/AE/AL/wasv7.al</ae_cmd>
         </image>
     
         <image>
           <type>mksysb</type>
           <name>db2-91_5300-07master_sysb</name>
           <location>/export/nim/mksysb/db2-91_5300-07_mksysb</location>
           <ae_dir>/tmp/nimext/AE</ae_dir>
           <ae_xml>/tmp/nimext/AE/AP/DB2_9.1.ap</ae_xml>
           <ae_cmd>sh /opt/IBM/AE/ae -a /opt/IBM/AE/AL/DB2_9.1.al</ae_cmd>
         </image>
     
         <image>
           <type>mksysb</type>
           <name>5300-07master_sysb</name>
           <ae_dir>/tmp/nimext/AE</ae_dir>
           <ae_xml>/tmp/nimext/AE/AP/aix.ap</ae_xml>
           <ae_cmd>sh /opt/IBM/AE/ae -a /opt/IBM/AE/AL/aix.al</ae_cmd>
         </image>
       </config>
      </pauto>

=cut

=head3 PARAMETERS

=over 2

=item * $xml_input (REQUIRED)

XML input data structure created from the XML::Simple parser.

=back

=head3 RETURNS

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub process_xml_input
{
  my ($input) = @_;

  xml_input_file_to_project_settings($input);
  xml_input_file_to_lpar_defaults($input);
  xml_input_file_to_lpars($input);
  xml_input_file_to_images($input);

  # print_lpars();
  # print_images();
}

#-------------------------------------------------------------------------------
sub check_nim_filesets
{
  log_print "NIM::check_nim_filesets()\n";

  my @client_history = `lslpp -lqc bos.sysmgt.nim.client | grep /usr/lib/objrepos`;
  my @master_history = `lslpp -lqc bos.sysmgt.nim.master | grep /usr/lib/objrepos`;

  my $client_level;
  my $master_level;

  foreach my $line (@client_history)
  {
    my @parts = split(/:/, $line);
    log_print "\tbos.sysmgt.nim.client $parts[2] $parts[4]\n"; # Level and State

    $client_level = $parts[2];
  }

  foreach my $line (@master_history)
  {
    my @parts = split(/:/, $line);
    log_print "\tbos.sysmgt.nim.master $parts[2] $parts[4]\n"; # Level and State

    $master_level = $parts[2];
  }
  my $rc = ($client_level ne $master_level) ? -1 : 0;
  log_printf("\trc = %d\n", $rc);
  return $rc;
}

1;

# __END__

=head1 AUTHOR

IBM

=head1 BUGS

Please report any bugs to the L</"AUTHOR">.

=head1 SUPPORT

=head1 ACKNOWLEDGEMENTS

=head1 SEE ALSO

=begin html

<ul>
<li><a href="NIM/Deploy.html">NIM::Deploy</a>
<li><a href="NIM/Deploy/Mksysb.html">NIM::Deploy::Mksysb</a>
<li><a href="NIM/Info.html">NIM::Info</a>
<li><a href="NIM/LPAR.html">NIM::LPAR</a>
<li><a href="NIM/LPAR/AIX.html">NIM::LPAR::AIX</a>
<li><a href="NIM/LPAR/VIOS.html">NIM::LPAR::VIOS</a>
</ul>

=end html

=cut
