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

# @(#)64        1.1  src/bos/usr/lpp/bosinst/samples/nimae.pl, bosinst, bos720 3/29/09 15:21:01
#-------------------------------------------------------------------------------

=head1 NAME

nimae

=over 2

=item * NIM script to perform higher level operations.

=item * 

=begin html

Various startup options are available. See <a href="NIM.html#name">description for NIM</a>.

=end html

=back

=head1 PARAMETERS

=head4 -operation install_nimae

=over 2

=item * -source_dir (OPTIONAL)

=item * -nim_master (OPTIONAL)

=item * -nimae_dir (OPTIONAL)

=back

=head4 -operation deploy_image

=over 2

=item * -lpar (REQUIRED)

Name of the LPAR to deploy the image.

=item * -image (REQUIRED)

Name of the image to deploy.

=item * -config (OPTIONAL)

Re-configuration data (name=value) used to modify the image during activation/boot. Can be specified multiple times.

=item * -nim_master (OPTIONAL)

=item * -nimae_dir (OPTIONAL)

=item * -cp (OPTIONAL)

Files to copy to remote system. Can be specified multiple times.

=back

=head1 RETURNS

=cut

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

BEGIN
{
  push @INC, '/usr/lpp/bosinst/samples'
}

use strict;
use warnings;

use Hash::Util;
use Getopt::Long;
use File::Basename;
use File::Temp;
use FileHandle;
use Sys::Hostname;
use XML::LibXML;
use Cwd;

use NIM;
use NIM::Util;
use NIM::Deploy;

use threads;
use threads::shared;

# Getopt::Long::Configure('debug');
Getopt::Long::Configure('pass_through');

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

my $cmd_args = join(' ', @ARGV);
log_print("$0 $cmd_args\n");

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

my @initial_ARGV = @ARGV;

my %defaults = ('nimae_dir'	=> '/usr/lpp/bosinst/samples');

my %opts = ('operation'		=> undef,
	    'nim_master'	=> undef,
	    'nimae_dir'		=> undef);

GetOptions("operation=s"	=> \$opts{'operation'},
	   "nim_master=s"	=> \$opts{'nim_master'},
	   "nimae_dir=s"	=> \$opts{'nimae_dir'});

$opts{'work_dir'} = $NIM::Deploy::Settings{'work_dir'};

my $exit_status = 0;
my $ns_uri = 'http://schemas.dmtf.org/ovf/environment/1';

#-------------------------------------------------------------------------------
if ($opts{'operation'})
{
  while (my ($k, $v) = each(%defaults))
  {
    if ((defined $opts{$k}) && (! $opts{$k}))
    {
      $opts{$k} = $v;
    }
  }

  if (run_remote_cmd() == 0)
  {
    log_print("running $opts{'operation'} locally\n");

    my %handler =
      (
       'install_nimae' => sub
       {
	 # nimae -o install_nimae -nim_master ipv4-072 -source_dir . -nimae_dir /tmp/nimae
	 
	 log_print("install_nimae\n");
	 
	 $opts{'source_dir'} = undef;
	 GetOptions("source_dir=s" => \$opts{'source_dir'});
	 
	 if (! $opts{'source_dir'})
	 {
	   if ($opts{'nimae_dir'})
	   {
	     $opts{'source_dir'} = $opts{'nimae_dir'};
	   }
	   else
	   {
	     $opts{'source_dir'} = $defaults{'nimae_dir'};
	   }
	 }
	 if (! $opts{'nimae_dir'})
	 {
	   $opts{'nimae_dir'} = $defaults{'nimae_dir'};
	 }
	 if ($opts{'nim_master'} || die "-nim_master is a required option\n")
	 {
	   invoke("cd $opts{'source_dir'}; tar -chf - * | ssh root\@$opts{'nim_master'} \"export LIBPATH=.; mkdir -p $opts{'nimae_dir'}; cd $opts{'nimae_dir'}; cat | tar -xf -; export PERL5LIB=AE; AE/setup;\"");
	 }
       },

       'deploy_image' => sub
       {
	 # nimae -o deploy_image -nim_master ipv4-072 -image was7_5300-07master_sysb -lpar ipv4-073 -config .password$=mypw -nimae_dir /tmp/nimae
	 
	 log_print("deploy_image\n");
	 $exit_status = -1;
	 
	 # copies the ae dir and invokes the customize script with the key/value pairs
	 # returns the fb_script resource name
	 
	 $opts{'image'} = undef;
	 $opts{'lpar'} = undef;
	 
	 # -ae ae_dir -config key1=value1 -config key2=value2 -config 'regexp1'=value3
	 my %config = ();
	 
	 GetOptions("image=s"		=> \$opts{'image'},
		    "lpar=s"		=> \$opts{'lpar'},	# target machine
		    "config=s"		=> \%config);		# activation profile customization
	 
	 if (($opts{'image'} || die "-image is a required option\n") &&
	     ($opts{'lpar'} || die "-lpar is a required option\n"))
	 {
	   if (NIM::is_nim_master() != 0)
	   {
	     my $start_time = time();
	     
	     my $lpar_name = $opts{'lpar'};
	     my $image_name = $opts{'image'};
	     
	     my $lpar = $NIM::Config{'lpars'}->{$lpar_name};
	     my $image = $NIM::Config{'images'}->{$image_name};
	     
	     if (! $image)
	     {
	       # eventually need to account for generic - i.e. non-mksysb - images
	       if (NIM::object_type($image_name) eq 'mksysb')
	       {
		 $image = new NIM::Deploy::Mksysb('name'		=> $image_name);
	       }
	       if ($image)
	       {
		 $NIM::Config{'images'}->{$image_name} = $image;
	       }
	     }

	     if (($lpar || die "Machine \'$lpar_name\' not found\n") &&
		 ($image || die "Image \'$image_name\' not found\n"))
	     {
	       log_print("\tDeploying $image_name to $lpar_name\n");
	       
	       ##########################################################
	       my @m = mutex_lock_nb("nimae::deploy_image::$lpar_name");
	       ##########################################################

	       if ($#m >= 0)
	       {
		 ################################
		 push @m, mutex_lock($lpar_name);
		 ################################

		 # delete any previously allocated resources from earlier deployment to the same lpar
		 $lpar->delete_resources();
		 
		 # parse template xml
		 my $ae_xml = parse_ae_xml($image->{'ae_xml'});
		 
		 # expand regexp properties
		 while (my ($key, $value) = each(%config))
		 {
		   my $properties = get_ae_properties($ae_xml, $key);
		   delete $config{$key};
		   set_ae_config($properties, $value, \%config);
		 }
		 
		 # create working copy of activation engine and customized xml file
		 my ($work_ae_dir, $work_ae_xml_pn) = customize_ae($image->{'ae_dir'}, $ae_xml, $image->{'ae_cmd'}, \%config);
		 
		 #
		 
		 if ($image->{'type'} eq 'mksysb')
		 {
		   if (deploy_mksysb($lpar, $image, $work_ae_dir,
				     $image->{'ae_cmd'}, \%config,
				     $opts{'work_dir'}, $NIM::Deploy::Settings{'remote_work_dir'}))
		   {
		     $exit_status = 0;
		   }
		 }
		 else
		 {
		   warn "Unsupported image type for deploy_image operation\n";
		 }
	       }
	       else
	       {
		 warn "$lpar_name is already locked for deploy_image";
	       }
		 
	       ################
	       mutex_unlock @m;
	       ################
	     }
	   }
	   else
	   {
	     die "Not nim master";
	   }
	 }
       },

       'remove_image' => sub
       {
	 log_print("remove_image\n");
	 
	 $opts{'image'} = undef;
	 GetOptions("image=s"		=> \$opts{'image'});
	 
	 if (($opts{'image'} || die "-image is a required option\n"))
	 {
	   if (NIM::is_nim_master() != 0)
	   {
	     my $image_name = $opts{'image'};
	     my $image = $NIM::Config{'images'}->{$image_name};
	     
	     if (! $image)
	     {
	       # eventually need to account for generic - i.e. non-mksysb - images
	       if (NIM::object_type($image_name) eq 'mksysb')
	       {
		 $image = new NIM::Deploy::Mksysb('name'		=> $image_name);
	       }
	     }
	     
	     if (($image || die "Image \'$image_name\' not found\n"))
	     {
	       $image->delete();
	       delete $NIM::Config{'images'}->{$image_name};
	     }
	   }
	 }
       },
       
       'reset_lpar' => sub
       {
	 log_print("reset_lpar\n");
	 
	 $opts{'lpar'} = undef;
	 GetOptions("lpar=s"		=> \$opts{'lpar'});	# target machine
	 
	 if (($opts{'lpar'} || die "-lpar is a required option\n"))
	 {
	   if (NIM::is_nim_master() != 0)
	   {
	     my $lpar_name = $opts{'lpar'};
	     my $lpar = $NIM::Config{'lpars'}->{$lpar_name};
	     
	     ###############################
	     my @m = mutex_lock($lpar_name);
	     ###############################
  
	     if ($lpar)
	     {
	       $lpar->reset();
	     }
	     else
	     {
	       NIM::LPAR::reset_by_name($lpar_name);
	     }

	     ################
	     mutex_unlock @m;
	     ################
	   }
	 }
       },

       'remove_lpar' => sub
       {
	 # delete the NIM machine definition
	 # lpar should have already been shutdown
	 
	 log_print("remove_lpar\n");
	 
	 $opts{'lpar'} = undef;
	 GetOptions("lpar=s"		=> \$opts{'lpar'});	# target machine
	 
	 if (($opts{'lpar'} || die "-lpar is a required option\n"))
	 {
	   if (NIM::is_nim_master() != 0)
	   {
	     my $lpar_name = $opts{'lpar'};
	     my $lpar = $NIM::Config{'lpars'}->{$lpar_name};
	     
	     ###############################
	     my @m = mutex_lock($lpar_name);
	     ###############################
  
	     if ($lpar)
	     {
	       $lpar->reset();
	       $lpar->delete();
	       
	       delete $NIM::Config{'lpars'}->{$lpar_name};
	     }
	     else
	     {
	       NIM::LPAR::reset_by_name($lpar_name);
	       NIM::LPAR::delete_by_name($lpar_name);
	     }

	     ################
	     mutex_unlock @m;
	     ################
	   }
	 }
       },

       'noop' => sub
       {
	 log_print("noop\n");
       },

       'test' => sub
       {
	 log_print("test\n");

	 my %config = ();
	 GetOptions("config=s"		=> \%config);		# activation profile customization
	 
	 my $ae_xml = parse_ae_xml("AE/AP/aix.ovf");
	 
	 # expand regexp properties
	 while (my ($key, $value) = each(%config))
	 {
	   my $properties = get_ae_properties($ae_xml, $key);
	   delete $config{$key};
	   set_ae_config($properties, $value, \%config);
	 }
	 
	 # create working copy of activation engine and customized xml file
	 my ($work_ae_dir, $work_ae_xml_pn) = customize_ae("./AE", $ae_xml, '.', \%config);
	 print "$work_ae_dir\n";
	 `cp $work_ae_dir/AP/aix.ovf /tmp`;
       }
      );
    
    my $code = $handler{$opts{'operation'}};
    if ($code)
    {
      $code->();
    }
    else
    {
      die "Invalid operation: $opts{'operation'}";
    };

    my $args = join(' ', @ARGV);
    if ($args)
    {
      warn "Invalid option(s) specified: $args\n";
    }
  }
  else
  {
    log_print("done running remote command\n");
  }
}

# print "exit rc = $exit_status\n";
exit $exit_status;

#-------------------------------------------------------------------------------
sub create_tar_cmd
{
  my @rc = ('', '');
  my $dir = getcwd();
  
  my %copy_files = ();
  GetOptions("cp=s" => \%copy_files);

  if (scalar keys (%copy_files))
  {
    $rc[0] = 'tar -chf -';
    
    while (my ($pn, $src_pn) = each(%copy_files))
    {
      my $src_fn = basename($src_pn);
      my $src_dn = dirname($src_pn);

      if ($src_dn)
      {
	$rc[0] = $rc[0] . " -C $src_dn $src_fn -C $dir";
      }
      else
      {
	$rc[0] = $rc[0] . " $src_fn";
      }

      my $fn = basename($pn);
      my $dn = dirname($pn);
      
      if (($dn || $fn) && ($src_fn ne $pn))
      {
	$rc[1] = $rc[1] . "mv $src_fn $pn;";
      }
    }
  }
  return @rc;
}

#-------------------------------------------------------------------------------
# Used to pass command to a remote nim master
#-------------------------------------------------------------------------------
sub run_remote_cmd
{
  my $rc = 0;
  if ($opts{'nim_master'} && ($opts{'operation'} ne 'install_nimae'))
  {
    my $host_name = hostname();
    my ($host, $aliases, $addrtype, $length, @addr) = gethostbyname $host_name;

    my $nim;
    ($nim, $aliases, $addrtype, $length, @addr) = gethostbyname $opts{'nim_master'};

    log_print("host = $host / nim_master = $opts{'nim_master'}\n");
    log_print("nim master = $nim\n");

    my @host_parts = split('.', $host);
    my @nim_parts = split('.', $nim);

    if (($#host_parts == $#nim_parts) && ($host ne $nim) ||
	(($#host_parts == 0) || ($#nim_parts == 0)) && ($host_parts[0] ne $nim_parts[0]))
    {
      log_print("running remote command\n");

      my $cmd = basename($0);
      if ($opts{'nimae_dir'})
      {
	$cmd = $opts{'nimae_dir'} . '/' . $cmd;
      }

      @ARGV = @initial_ARGV;
      my @cmds = create_tar_cmd();
      my $tar_cmd = $cmds[0];
      my $args = join(' ', @ARGV);

      # pass along stdin
      my $input_str = NIM::Util::read_stdin();
      if ($input_str)
      {
	# $input_str =~ s/\n//g;
	$input_str =~ s/'/\\'/g;
	$input_str =~ s/"/\\"/g;
	$input_str =~ s/\$/\\\$/g;
	$input_str = "echo \'$input_str\' |";
      }

      # work dir needs to be requestor unique since this is a requestor driven request to put data and run command
      my $ssh_work_dir = "$opts{'work_dir'}/$host/$$";

      log_print("running \'ssh root\@$opts{'nim_master'} \"$cmd $args\"\' in $ssh_work_dir\n");
      if ($tar_cmd)
      {
	my $mv_cmd = $cmds[1];
	# print "$tar_cmd\n";
	# print "$mv_cmd\n";

	invoke("$tar_cmd | ssh root\@$opts{'nim_master'} \"export LIBPATH=$opts{'nimae_dir'}; mkdir -p $ssh_work_dir; cd $ssh_work_dir; cat | tar -xf -; $mv_cmd export PERL5LIB=$opts{'nimae_dir'}; $input_str $cmd $args\"");
      }
      else
      {
	invoke("ssh root\@$opts{'nim_master'} \"export LIBPATH=$opts{'nimae_dir'}; mkdir -p $ssh_work_dir; cd $ssh_work_dir; export PERL5LIB=$opts{'nimae_dir'}; $input_str $cmd $args\"");
      }
      $rc = 1;
    }
  }
  return $rc;
}

#-------------------------------------------------------------------------------
sub parse_ae_xml
{
  my ($xml_file) = @_;
  my %rc = ();
  
  $rc{'xml_file'} = $xml_file;
  $rc{'parser'} = new XML::LibXML();
  $rc{'doc'} = eval
  {
    $rc{'parser'}->parse_file($xml_file);
  };
  $rc{'doc'} || die "can not open the file: $xml_file";
  
  $rc{'fn'} = basename($xml_file);
  $rc{'dn'} = dirname($xml_file);
  
  $rc{'is_ovf'} = ($rc{'fn'} =~ /\.ovf$/);

  # debug - display info
  if (0)
  {
    while (my ($k, $v) = each(%rc))
    {
      if ($k eq 'is_ovf')
      {
	$v = ($v ? '1' : '0');
      }
      print "$k => $v\n";
    }
  }
  return \%rc;
}

#-------------------------------------------------------------------------------
sub get_ae_properties
{
  my ($ae_xml, $query, $exact_match) = @_;
  my @rc = ();
  
  $exact_match = ($exact_match ? '1' : '0');
  log_print("get_ae_properties($ae_xml->{'xml_file'}, $query, $exact_match)\n");

  my $is_ovf = $ae_xml->{'is_ovf'};
  my $doc = $ae_xml->{'doc'};

  my $rootElement = $doc->getDocumentElement();
  if ($is_ovf)
  {
    $rootElement->setNamespace($ns_uri, 'default_ns', 1);
    $rootElement->setNamespace($ns_uri, 'ovfenv_ns', 1);
  }

  log_printf("\troot element = %s, uri = %s\n", $rootElement->nodeName(), $rootElement->namespaceURI());
  for (my $nextNode = $rootElement->firstChild(); $nextNode; $nextNode = $nextNode->nextSibling())
  {
    log_printf("\tchild element = %s, uri =%s, type = %s, content = %s\n", $nextNode->nodeName(), $nextNode->namespaceURI(), $nextNode->nodeType(), $nextNode->textContent());
  }

  if ($is_ovf)
  {
    log_print("\tprocessing ovf properties\n");

    # xpath = //PropertySection/Property[@ovfenv:key='ConfigWAS.password'][@ovfenv:value]
    my $xpath_expression = "//default_ns:PropertySection/default_ns:Property[\@ovfenv_ns:key][\@ovfenv_ns:value]";

    my @nodes = $rootElement->findnodes($xpath_expression);
    # log_printf("\tchecking %d nodes\n", $#nodes + 1);

    foreach my $node (@nodes)
    {
      # my $node_name = $node->nodeName(); 
      # log_print("\tchecking node $node_name\n");

      my $attr_node = $node->getAttributeNodeNS($ns_uri, 'key');
      if ($attr_node)
      {
	my $attr_value = $attr_node->getValue();
	my $match = 0;

	# log_print "ovfenv:key = $attr_value\n";
	
	if ($exact_match)
	{
	  if ($attr_value eq $query)
	  {
	    $match = 1;
	  }
	}
	elsif ($attr_value =~ $query)
	{
	  $match = 1;
	}
	if ($match)
	{
	  # log_print "++ ovfenv:key = $attr_value\n";
	  push @rc, ($attr_value);
	}
      }
    }
  }
  else
  {
    # xpath = //configuration[@name='ConfigWAS']/parameter[@name='password'][@value]
    
    if ($exact_match)
    {
      my $last_dot = rindex($query, '.');
      
      my $config_name = substr($query, 0, $last_dot);
      my $property_name = substr($query, $last_dot + 1);
      
      my $xpath_expression = "//configuration[\@name=\'$config_name\']/parameter[\@name=\'$property_name\'][\@value]";
      
      my @nodes = $rootElement->findnodes($xpath_expression);
      if ($#nodes >= 0)
      {
	push @rc, ("${config_name}.${property_name}");
      }
    }
    else
    {
      my $xpath_expression = "//configuration[\@name]/parameter[\@name][\@value]";
      my @nodes = $rootElement->findnodes($xpath_expression);

      foreach my $node (@nodes)
      {
	my $parent = $node->parentNode;
	my $cname = $parent->getAttribute('name');
	my $pname = $node->getAttribute('name');
	
	my $name = "$cname.$pname";
	
	# print "$name =~ $query\n";
	if ($name =~ $query)
	{
	  push @rc, ($name);
	}
      }
    }
  }
  
  foreach my $property (@rc)
  {
    log_print("\t$property\n");
  }
  
  return \@rc;
}

#-------------------------------------------------------------------------------
sub set_ae_config
{
  my ($properties, $value, $rc)  = @_;

  if (! defined $rc)
  {
    $rc = {};
  }

  foreach my $property (@$properties)
  {
    $rc->{$property} = $value;
  }
  return $rc;
}

#-------------------------------------------------------------------------------
sub customize_ae
{
  my ($ae_dir, $ae_xml, $ae_cmd, $config) = @_;
  my @rc = ('', '');
  
  log_print("customize_ae($ae_dir, $ae_xml->{'xml_file'}, $ae_cmd)\n");
  
  if ($ae_dir)
  {
    if ($opts{'work_dir'})
    {
      if (-e "$opts{'work_dir'}/$$/AE")
      {
	`rm -r $opts{'work_dir'}/$$/AE`;
      }
      else
      {
	`mkdir -p $opts{'work_dir'}/$$/AE`;
      }
    }
    
    log_print("\twork dir = $opts{'work_dir'}/$$/AE\n");
    
    `cp -pr ${ae_dir}/* $opts{'work_dir'}/$$/AE`;
    if (-e "$opts{'work_dir'}/$$/AE/AP")
    {
      `rm $opts{'work_dir'}/$$/AE/AP/*`;
    }
    if (-e "$opts{'work_dir'}/$$/AE/AR")
    {
      `rm $opts{'work_dir'}/$$/AE/AR/*`;
    }
    else
    {
      `mkdir -p $opts{'work_dir'}/$$/AE/AR`;
    }
    
    my $is_ovf = $ae_xml->{'is_ovf'};
    my $doc = $ae_xml->{'doc'};
    my $fn = $ae_xml->{'fn'};

    my $rootElement = $doc->getDocumentElement();
    if ($is_ovf)
    {
      $rootElement->setNamespace($ns_uri, 'default_ns', 1);
      $rootElement->setNamespace($ns_uri, 'ovfenv_ns', 1);
    }
    while (my ($key, $value) = each(%$config))
    {
      if ($key && $value)
      {
	# log_print("\t$key\n");
	if ($is_ovf)
	{
	  # need to modify ovf environment instead of ovf envelope xml file

	  # xpath = //PropertySection/Property[@ovfenv:key='ConfigWAS.password'][@ovfenv:value]
	  my $xpath_expression = "//default_ns:PropertySection/default_ns:Property[\@ovfenv_ns:key=\'$key\'][\@ovfenv_ns:value]";
	  my @nodes = $rootElement->findnodes($xpath_expression);

	  foreach my $node (@nodes)
	  {
	    my $attr_val_node = $node->getAttributeNodeNS($ns_uri, 'value');
	    $attr_val_node->setValue($value);

	    my $node_name = $node->nodeName(); 
	    log_print("\tsetting $node_name $key=$value\n");
	  }
	}
	else
	{
	  # xpath = //configuration[@name='ConfigWAS']/parameter[@name='password'][@value]
	  
	  my $last_dot = rindex($key, '.');
	  
	  my $config_name = substr($key, 0, $last_dot);
	  my $property_name = substr($key, $last_dot + 1);
	  
	  log_print("\t$key\n");
	  
	  if ($config_name && $property_name)
	  {
	    my $xpath_expression = "//configuration[\@name=\'$config_name\']/parameter[\@name=\'$property_name\'][\@value]";
	    my @nodes = $rootElement->findnodes($xpath_expression);

	    foreach my $node (@nodes)
	    {
	      my $node_name = $node->nodeName(); 
	      log_print("\tsetting $node_name $key=$value\n");
	      
	      $node->setAttribute('value', $value);
	    }
	  }
	}
      }
    }
    
    `mkdir -p $opts{'work_dir'}/$$/AE/AP`;
    if ($is_ovf)
    {
      $doc->toFile("$opts{'work_dir'}/$$/AE/AP/ovf-env.xml", 1);
    }
    else
    {
      $doc->toFile("$opts{'work_dir'}/$$/AE/AP/$fn", 1);
    }
    @rc = ("$opts{'work_dir'}/$$/AE", "$opts{'work_dir'}/$$/AE/AP/$fn");
  }
  return @rc;
}

#-------------------------------------------------------------------------------
sub rand_passwd
{
  my $rc = '';

  my $minimum = ord('a');
  my $range = ord('z') - $minimum;

  for (my $i = 0; $i < 8; $i++)
  {
    my $random_number = int(rand($range)) + $minimum;
    $rc = $rc . chr($random_number);
  }
  # print $rc . "\n";
  return $rc;
}

#-------------------------------------------------------------------------------
sub deploy_mksysb
{
  my ($lpar, $mksysb, $ae_dir, $ae_cmd, $config, $work_dir, $remote_work_dir) = @_;

  log_print("deploy_mksysb($lpar->{'nim_machine_name'}, $mksysb->{'name'}, $ae_dir, $ae_cmd, $work_dir, $remote_work_dir)\n");

  my $rc = 0;
  my $start_time = time();

  if ($mksysb->is_deployable() == 1)
  {
    my $dir = getcwd();

    # set root password to random value to disable login with original password from the image
    # since the activation engine runs after tcpip starts up
    my $password = rand_passwd();
    log_print("\ttemporary password: $password\n");

    # need to create fb_script nim resource and return name as $rc
    
    chdir "$work_dir/$$/AE";
    invoke("tar -chf ../AE.tar *");
    chdir '..';
    
    # NIM::Deploy::Mksysb::create_fb_script_resource() needs to be changed if perl commands used instead of ksh
    my $cmd_ary_ref = undef;
    if (0)
    {
      # perl commands

      $cmd_ary_ref = [ "mkpath(\'$remote_work_dir\')",
		       "chdir \'$remote_work_dir\'",
		       '`tar -xvf AE.tar`',
		       '`rm AE.tar`',
		       '$ENV{\'LIBPATH\'} = \'.\'',
		       '`./setup`',
		       
		       "`echo \"root:$password\" | chpasswd`",

		       # "`sh ${remote_work_dir}/ae -a ${remote_work_dir}/AL/wasv7.al`"
		       "`$ae_cmd`"
	];
    }
    else
    {
      # ksh commands

      $cmd_ary_ref = [ "mkdir -p $remote_work_dir",
		       "cd $remote_work_dir",
		       'tar -xvf AE.tar',
		       'rm AE.tar',
		       'export LIBPATH=.',
		       './setup',
		       
		       "echo \"root:$password\" | chpasswd",

		       "$ae_cmd"
	];
    }

    # use to switch between using fb_script or script resource
    my $use_fb_script = 1;

    my $fb_script_name = (($use_fb_script == 1) ? $mksysb->create_fb_script_resource($cmd_ary_ref, 'AE.tar', 1) : '');
    my $script_name = (($use_fb_script == 0) ? $mksysb->create_script_resource($cmd_ary_ref, 'AE.tar', 1) : '');

    if ($fb_script_name)
    {
      $lpar->add_to_res_group($fb_script_name, 'fb_script');
    }
    if ($script_name)
    {
      $lpar->add_to_res_group($script_name, 'script');
    }

    my $resolv_conf_name = $mksysb->create_resolv_conf_resource($lpar->{'domain'}, $lpar->get_nameservers());
    $lpar->add_to_res_group($resolv_conf_name, 'resolv_conf');
    
    # extract ./image.data file from the mksysb and size to disk
    my $image_data_name = $mksysb->create_image_data_resource($lpar->{'disk_size'}, $lpar->{'paging_space_size'});
    $lpar->add_to_res_group($image_data_name, 'image_data');
    
    my $thr = undef;
    if ($mksysb->resolve_machine_netboot_kernel($lpar->{'nim_machine_name'}))
      # if ($mksysb->machine_spot_check($lpar->{'nim_machine_name'}))
    {
      $mksysb->deploy_to_machine($lpar->{'nim_machine_name'}, $script_name, $fb_script_name, $resolv_conf_name, $image_data_name);

      # some hmc lpar_netboot have a bug which command blocks till the entire bosinst operation is complete
      # lpar_netboot should exit after the network bootp but before the install
      # use threads to account for blocking behavior

      if (0)
      {
	$lpar->netboot();
      }
      else
      {
	log_print("Starting netboot thread\n");
	$thr = threads->create(sub { my ($lpar) = @_; $lpar->netboot(); }, $lpar);
      }
      
      # print status
      $rc = $lpar->print_deploy_status();
    }
  
my $comment = q {
    # getting a SIGWARN "PmmREFCNT_dec: REFCNT decremented below 0!" if the following code
    # is enabled.

    if ($thr)
    {
      log_print("Waiting for netboot thread to complete\n");
      $thr->join();
      log_print("Netboot thread completed\n");
    }
};

    if ($fb_script_name)
    {
      $mksysb->delete_fb_script_resource($fb_script_name);
    }
    if ($script_name)
    {
      $mksysb->delete_script_resource($script_name);
    }
    $mksysb->delete_resolv_conf_resource($resolv_conf_name);
    $mksysb->delete_image_data_resource($image_data_name);
    
    chdir $dir;
  }

  my $elapse_time = time() - $start_time;
  my $mins = $elapse_time /= 60;
  $mins =~ s/\..*//;
  my $secs = $elapse_time % 60;
  
  print "Elapsed time = $mins minutes + $secs seconds\n";

  return $rc;
}

# __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.html">NIM</a>
<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

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

my $comment = q {

nimae -o install_nimae -source_dir . -nim_master ipv4-072 -nimae_dir /tmp/nimae
rm /var/ibm/systemp/logs/pAutomation.log; rm -r /var/ibm/systemp/nim/[1-9]*; export LIBPATH=.; ./nimae -o deploy_image -image db2-91_5300-07master_sysb -lpar ipv4-073 -config password=mypw

};
