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

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

package NIM::Info;

=head1 NAME

NIM::Info

=over 2

=item

Used to access NIM information and configuration data.

=back

=head1 VARIABLES

=head2 %Settings

=over 2

=item * verbose

=back

=head1 FUNCTIONS

=cut

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

  use FileHandle;
  use NIM::Util qw(:DEFAULT !log_print !log_printf);
  use Hash::Util;

  %Settings = ('verbose'	=> 0);
}

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

  if ($Settings{'verbose'})
  {
    print_settings($NIM::Util::Settings{'log_file_handle'});
  }
}

#-------------------------------------------------------------------------------
sub print_settings
{
  my ($fh) = @_;
  if (! $fh)
  {
    $fh = STDOUT;
  }
  print $fh "NIM::Info::Settings:\n";
  while (my ($key, $value) = each(%Settings))
  {
    print $fh "\t$key = \'$value\'\n";
  }
}

#-------------------------------------------------------------------------------
sub log_print
{
  if ($Settings{'verbose'})
  {
    NIM::Util::log_print(@_);
  }
}

#-------------------------------------------------------------------------------
sub log_printf
{
  if ($Settings{'verbose'})
  {
    NIM::Util::log_printf(@_);
  }
}

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

=head2 new

=over 2

=item

C<new NIM::Info($command)>

=back

=head3 DESCRIPTION

=over 2

=item

Creates a NIM::Info hash object.  The instance of the NIM::Info object is a
snapshot of the NIM database.

=back

=head3 PARAMETERS

=over 2

=item * $command (OPTIONAL)

=back

=head3 RETURNS

=over 2

=item * Reference to the newly instantiated NIM::Info hash object.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub new
{
  my $type = shift;
  my ($command) = @_;

  $command = ((defined $command) ? $command : '');
  log_print "NIM::Info::new(\'$command\')\n";

  my $this = {};
  bless $this, $type;
  
  $this->{'command'} = $command;
  $this->{'hash'} = create_hash($command);
  # $this->print();
  
  return $this;
}

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

=head2 refresh

=over 2

=item

C<$info-E<gt>refresh($command)>

=back

=head3 DESCRIPTION

=over 2

=item

Refreshes the NIM::Info data by querying NIM again.

=back

=head3 PARAMETERS

=over 2

=item * $command (OPTIONAL)

=back

=head3 RETURNS

=over 2

=item * Reference to this instance.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub refresh
{
  my $this = shift;
  my ($command) = @_;

  $command = ((defined $command) ? $command : '');
  log_print "NIM::Info::refresh(\'$command\')\n";

  $this->{'command'} = $command;
  $this->{'hash'} = create_hash($command);

  return $this;
}

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

=head2 update

=over 2

=item

C<$info-E<gt>update($command)>

=back

=head3 DESCRIPTION

=over 2

=item

Updates the NIM::Info data by querying NIM and merging/overwriting the new
data with the data already held by this instance.

=back

=head3 PARAMETERS

=over 2

=item * $command (OPTIONAL)

=back

=head3 RETURNS

=over 2

=item * Reference to this instance.

=back

=head3 EXCEPTIONS

=cut

#-------------------------------------------------------------------------------
sub update
{
  my $this = shift;
  my ($command) = @_;

  $command = ((defined $command) ? $command : '');
  log_print "NIM::Info::update(\'$command\')\n";

  my $updates = create_hash($command);
  my $this_hash = $this->{'hash'};

  while (my ($obj_name, $attr_hash) = each(%$updates))
  {
    $this_hash->{$obj_name} = $attr_hash;

    # print "$obj_name = \n"; # yyy
    while (my ($attr_name, $attr_value) = each(%$attr_hash))
    {
      my @attr_array = @$attr_value;
      # print "\t$attr_name = @attr_array\n"; # yyy
    }
  }
  return $this;
}

#-------------------------------------------------------------------------------
sub create_hash
{
  my ($lsnim_cmd) = @_;
  if (! $lsnim_cmd)
  {
    $lsnim_cmd = "$Lang $Cmds{'lsnim'} -lF 2>/dev/null";
  }

  log_print "NIM::Info::create_hash(\'$lsnim_cmd\')\n";

  my %rc = ();
  open(LSNIM, "$lsnim_cmd |") || die "Can't run $lsnim_cmd: $!";

  my $lineno = 0;
  my $obj_name = '';
  my $key = '';

  while (<LSNIM>)
  {
    my $line = $_;
    chomp($line);
    # print "$obj_name line = \'$line\'\n";	# yyy

    $lineno++;
    if ($line =~ m/^([^\s]+):$/)
    {
      $obj_name = $1;
      $key = '';
    }
    elsif ($obj_name ne '')
    {
      if ($line =~ m/^   ([^\s]+) += *(.*)$/)
      {
	$key = $1;
	if (! defined $2)
	{
	  $2 = '';
	}

	if (! defined $rc{$obj_name}{$key})
	{
	  $rc{$obj_name}{$key} = [ $2 ];
	}
	else
	{
	  push @{$rc{$obj_name}{$key}}, ($2);
	}
      }
      elsif (($key ne '') && (defined $rc{$obj_name}{$key}) && ($line =~ m/^(.*)$/))
      {
	my $value = pop @{$rc{$obj_name}{$key}};
	$value = $value . "\n" . $1;
	push @{$rc{$obj_name}{$key}}, ($value);
      }
      else
      {
	NIM::Util::log_print "Error parsing lsnim results line ${lineno}: \'$line\'\n";
	warn "Error parsing results of $lsnim_cmd";
      }
    }
    else
    {
      NIM::Util::log_print "Error parsing lsnim results line ${lineno}: \'$line\'\n";
      warn "Error parsing results of $lsnim_cmd";
    }
  }
  close(LSNIM);
  return \%rc;
}

#-------------------------------------------------------------------------------
sub print
{
  my $this = shift;

  if (1) # not sure why this gives a GLOB reference error here so must revert to alternate method
  {
    my ($fh) = @_;
    if (! $fh)
    {
      $fh = STDOUT;
    }
    my $nim_hash = $this->{'hash'};
    
    while (my ($obj_name, $attr_hash) = each(%$nim_hash))
    {
      print $fh "$obj_name:\n";
      while (my ($key, $values) = each(%$attr_hash))
      {
	foreach my $value (@$values)
	{
	  print $fh "\t$key = $value\n";
	}
      }
    }
  }
my $comment = q {
  else
  {
    local (*fh) = @_;
    if (fh)
    {
      select fh;
    }
    else
    {
      select STDOUT;
    }
    my $nim_hash = $this->{'hash'};
    
    while (my ($obj_name, $attr_hash) = each(%$nim_hash))
    {
      print fh "$obj_name:\n";
      while (my ($key, $values) = each(%$attr_hash))
      {
	foreach my $value (@$values)
	{
	  print fh "\t$key = $value\n";
	}
      }
    }
    select STDOUT;
  }
};
}

#-------------------------------------------------------------------------------
sub size
{
  my $this = shift;
  return scalar keys %{$this->{'hash'}};
}

#-------------------------------------------------------------------------------
sub empty
{
  my $this = shift;
  return ($this->size() == 0);
}

#-------------------------------------------------------------------------------
sub get_object_names
{
  my $this = shift;
  my @names = keys %{$this->{'hash'}};
  return \@names;
}

#-------------------------------------------------------------------------------
sub get_first_object_name
{
  my $this = shift;
  my @names = keys %{$this->{'hash'}};
  
  return ((scalar @names) ? $names[0] : "");
}

#-------------------------------------------------------------------------------
sub get_objects
{
  my $this = shift;
  return $this->{'hash'};
}

#-------------------------------------------------------------------------------
sub get_first_object
{
  my $this = shift;
  my @rc = ("", {});

  while (my ($obj_name, $attr_hash) = each(%{$this->{'hash'}}))
  {
    @rc = ($obj_name, $attr_hash);
    last;
  }
  keys %{$this->{'hash'}};	# reset each iterator
  return @rc;
}

#-------------------------------------------------------------------------------
sub get_attr_values
{
  my $this = shift;
  my ($object_name, $attr_name) = @_;
  
  # log_print "NIM::Info::get_attr_values($object_name, $attr_name)\n";

  my @rc = ();
  eval
  {
    @rc = @{$this->{'hash'}->{$object_name}{$attr_name}};
  };
  return ($@ ? () : @rc);
}

#-------------------------------------------------------------------------------
sub get_first_attr_value
{
  my $this = shift;
  my ($object_name, $attr_name) = @_;
  
  my @rc = ();
  eval
  {
    if ($this->{'hash'}->{$object_name}{$attr_name})
    {
      @rc = @{$this->{'hash'}->{$object_name}{$attr_name}};
    }
  };
  return ($@ ? '' : $rc[0]);
}

#-------------------------------------------------------------------------------
sub select_with_regexp_key_values
{
  my $this = shift;
  my (%query) = @_;

  log_print "NIM::Info::select_with_regexp_key_values(@_)\n";

  my $rc = {};
  bless $rc;

  my %rc_hash = %{$this->{'hash'}};
  $rc->{'hash'} = \%rc_hash;

  while (my ($obj_name, $attr_hash) = each(%rc_hash))
  {
    my @attr_keys = keys %$attr_hash;

    my $num_keys = scalar @attr_keys;
    # log_print "\tTesting $obj_name - $num_keys\n";

    my $keep = 1;
    while (my ($query_key, $query_value) = each(%query))
    {
      my $matched = 0;
      # log_print "\tTrying to match $query_key/$query_value\n";
      if ('name' =~ $query_key)
      {
	if ($obj_name =~ $query_value)
	{
	  # log_print "\tMatched $obj_name/$query_value\n";
	  $matched = 1;
	}
      }
      else
      {
	foreach my $attr_key (@attr_keys)
	{
	  if ($attr_key =~ $query_key)
	  {
	    my $values = $attr_hash->{$attr_key};
	    foreach my $value (@$values)
	    {
	      if ($value =~ $query_value)
	      {
		# log_print "\tMatched $obj_name/$attr_key/$value\n";
		$matched = 1;
		last;
	      }
	    }
	    if ($matched)
	    {
	      last;
	    }
	  }
	}
      }
      # if can't match current sub-query, then query failed
      if ($matched == 0)
      {
	$keep = 0;
	last;
      }
    }
    if ($keep == 0)
    {
      keys %query;		# reset each iterator
      # log_print "\tDeleting $obj_name\n";
      delete $rc_hash{$obj_name};
    }
  }
  log_printf("\t%d selected\n", $rc->size());
  return $rc;
}

#-------------------------------------------------------------------------------
sub select
{
  my $this = shift;
  my (%query) = @_;

  log_print "NIM::Info::select(@_)\n";

  my $rc = {};
  bless $rc;
  
  my %rc_hash = %{$this->{'hash'}};
  $rc->{'hash'} = \%rc_hash;

  while (my ($query_key, $query_value) = each(%query))
  {
    if ($query_key eq 'name')
    {
      foreach my $obj_name (keys %rc_hash)
      {
	if (! ($obj_name =~ $query_value))
	{
	  delete $rc_hash{$obj_name};
	}
      }
    }
    else
    {
      while (my ($obj_name, $attr_hash) = each(%rc_hash))
      {
	my $keep = 0;
	if (exists $attr_hash->{$query_key})
	{
	  my $values = $attr_hash->{$query_key};

	  foreach my $value (@$values)
	  {
	    if ($value =~ $query_value)
	    {
	      $keep = 1;
	      last;
	    }
	  }
	}
	if ($keep == 0)
	{
	  delete $rc_hash{$obj_name};
	}
      }
    }
  }
  log_printf("\t%d selected\n", $rc->size());
  return $rc;
}

#-------------------------------------------------------------------------------
sub join
{
  my $this = shift;
  my (@objs) = @_;

  log_print "NIM::Info::join()\n";

  my $rc = {};
  bless $rc;

  my %rc_hash = %{$this->{'hash'}};
  $rc->{'hash'} = \%rc_hash;

  foreach my $obj (@objs)
  {
    my $hash = $obj->{'hash'};
    while (my ($obj_name, $attr_hash) = each(%$hash))
    {
      $rc_hash{$obj_name} = $attr_hash;
    }
  }
  return $rc;
}

#-------------------------------------------------------------------------------
sub locate_mksysb_by_location
{
  my $this = shift;
  my ($mksysb_pn) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^mksysb$',
			'location'	=> "^$mksysb_pn\$"));
}

#-------------------------------------------------------------------------------
sub locate_mksysb_by_name
{
  my $this = shift;
  my ($nim_mksysb_name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^mksysb$',
			'name'	=> "^$nim_mksysb_name\$"));
}

#-------------------------------------------------------------------------------
sub locate_available_mksysb_by_name_and_location
{
  my $this = shift;
  my ($nim_mksysb_name, $mksysb_pn) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^mksysb$',
			'Rstate'	=> '^ready for use$',
			'name'		=> "^$nim_mksysb_name\$",
			'location'	=> "^$mksysb_pn\$"));
}

#-------------------------------------------------------------------------------
sub locate_spot_by_location
{
  my $this = shift;
  my ($spot_dir) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^spot$',
			'location'	=> "^$spot_dir\$"));
}

#-------------------------------------------------------------------------------
sub locate_spot_by_name
{
  my $this = shift;
  my ($nim_spot_name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^spot$',
			'name'	=> "^$nim_spot_name\$"));
}

#-------------------------------------------------------------------------------
sub locate_spot_by_mksysb_source
{
  my $this = shift;
  my ($mksysb_name) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^spot$',
			'mksysb_source'	=> "^$mksysb_name\$"));
}

#-------------------------------------------------------------------------------
sub locate_available_spot_by_name_and_location
{
  my $this = shift;
  my ($nim_spot_name, $spot_dir) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^spot$',
			"name"		=> "^$nim_spot_name\$",
			'location'	=> "^$spot_dir\$",
			'Rstate'	=> '^ready for use$'));
}

#-------------------------------------------------------------------------------
sub locate_available_spots_by_oslevel_r
{
  my $this = shift;
  my ($oslevel_r) = @_;
  
  return $this->select(('class'		=> '^resources$',
			'type'		=> '^spot$',
			'oslevel_r'	=> "^$oslevel_r\$",
			'Rstate'	=> '^ready for use$'));
}

#-------------------------------------------------------------------------------
sub locate_masters
{
  my $this = shift;
  
  return $this->select(('class'	=> '^machines$',
			'type'	=> '^master$'));
}

#-------------------------------------------------------------------------------
sub locate_machine_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^machines$',
			'type'	=> '^standalone$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_resource_group_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^groups$',
			'type'	=> '^res_group$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_resource_groups_by_member
{
  my $this = shift;
  my ($member_name) = @_;
  
  return $this->select_with_regexp_key_values(('^class$'		=> '^groups$',
					       '^type$'			=> '^res_group$',
					       '^member[1-9][0-9]*$'	=> "^$member_name\$"));
}

#-------------------------------------------------------------------------------
sub get_resource_group_members
{
  my $this = shift;
  my ($name) = @_;
  
  log_print("NIM::Deploy::Mksysb::get_resource_group_members(\'$name\')\n");

  my @rc = ();
  my $groups = $this->select(('name'	=> "^$name\$",
			      'class'	=> '^groups$',
			      'type'	=> '^res_group$'));

  if (! $groups->empty())
  {
    my ($obj_name, $attr_hash) = $groups->get_first_object();
    while (my ($key, $values) = each(%$attr_hash))
    {
      if ($key =~ m/^member[1-9][0-9]*$/)
      {
	# log_print "\t\`$key\` = \`$values->[0]\`\n";
	push @rc, ($values->[0]);
      }
    }
  }
  return @rc;
}

#-------------------------------------------------------------------------------
sub locate_machine_group_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^groups$',
			'type'	=> '^mac_group$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_machine_groups_by_member
{
  my $this = shift;
  my ($member_name) = @_;
  
  return $this->select_with_regexp_key_values(('^class$'		=> '^groups$',
					       '^type$'			=> '^mac_group$',
					       '^member[1-9][0-9]*$'	=> "^$member_name\$"));
}

#-------------------------------------------------------------------------------
sub locate_bosinst_data_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^bosinst_data$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_script_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^script$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_fb_script_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^fb_script$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_image_data_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^image_data$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_resolv_conf_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'type'	=> '^resolv_conf$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub locate_resource_by_name
{
  my $this = shift;
  my ($name) = @_;
  
  return $this->select(('class'	=> '^resources$',
			'name'	=> "^$name\$"));
}

#-------------------------------------------------------------------------------
sub set_comments
{
  my $this = shift;
  my ($object_name, $comments) = @_;
  
  my $commentsStr = CORE::join("\n", @$comments);
  invoke("$Cmds{'nim'} -o change -a comments=\"$commentsStr\" $object_name");
}

#-------------------------------------------------------------------------------
sub get_comments
{
  my $this = shift;
  my ($object_name) = @_;
  
  return $this->get_attr_values($object_name, 'comments');
}

#-------------------------------------------------------------------------------
sub test
{
  my $nim_info = new NIM::Info();
  $nim_info->refresh();
  my $num_entries = $nim_info->size();
  print "## Init Results - $num_entries\n";
  $nim_info->print();

  my $results = $nim_info->select(('serves' => 'master_net_conf'));
  print "## serves => master_net_conf results\n";
  $results->print();

  my $mksysb_results = $nim_info->select(('type'	=> '^mksysb$',
					  "version"	=> '^6$',
					  'Rstate'	=> '^ready for use$'));
  print "## mksysb results\n";
  $mksysb_results->print();

  my $spot_results = $nim_info->select(('type'		=> '^spot',
					'oslevel_r'	=> '^6100-00$',
					'Rstate'	=> '^ready for use$'));
  print "## spot results\n";
  $spot_results->print();

  my $group_results = $nim_info->locate_resource_groups_by_member('6100-00master_sysb');
  print "## res_group results\n";
  $group_results->print();

  my $joined_results = $results->join(($mksysb_results));
  $num_entries = $joined_results->size();
  print "## joined results - $num_entries\n";
  $joined_results->print($joined_results);
}

1;

# __END__

=head1 AUTHOR

IBM

=head1 BUGS

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

=head1 SUPPORT

=head1 ACKNOWLEDGEMENTS

=head1 SEE ALSO

=cut
