git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@13877 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			375 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # 
 | |
| # © Copyright 2009 Hewlett-Packard Development Company, L.P.
 | |
| # EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| #
 | |
| 
 | |
| ## API for talking to HP Onboard Administrator
 | |
| 
 | |
| ## NOTE:
 | |
| ## All parameters are passed by name!
 | |
| ## For example:
 | |
| ## 	hpoa->new(oaAddress => '16.129.49.209');
 | |
| 
 | |
| package xCAT::hpoa;
 | |
| 
 | |
| use strict;
 | |
| 
 | |
| #use SOAP::Lite;	# hpblade.pm requires SOAP::Lite before requiring hpoa.pm, so we can check for SOAP::Lite dynamically
 | |
| use vars qw(@ISA);
 | |
| @ISA = qw(SOAP::Lite);
 | |
| 
 | |
| # Constructor
 | |
| # Input: oaAddress, the IP address of the OA
 | |
| # Output: SOAP::SOM object (SOAP response)
 | |
| sub new {
 | |
|   my $class = shift;
 | |
|   return $class if ref $class;
 | |
| 
 | |
|   my $self = $class->SUPER::new();
 | |
| 
 | |
|   my %args = @_;
 | |
| 
 | |
|   die "oaAddress is a required parameter"
 | |
|     unless defined $args{oaAddress};
 | |
| 
 | |
|   # Some info we'll need
 | |
|   $self->{HPOA_HOST} 		= $args{oaAddress}; # OA IP address
 | |
|   $self->{HPOA_KEY} 		= undef; # oaSessionKey returned by userLogIn
 | |
|   $self->{HPOA_SECURITY_XML} 	= undef; # key placed in proper XML
 | |
|   $self->{HPOA_SECURITY_HEADER} = undef; # XML translated to SOAP::Header obj
 | |
| 
 | |
|   bless($self, $class);
 | |
| 
 | |
|   # We contact the OA via this URL:
 | |
|   my $proxy = "https://". $self->{HPOA_HOST} . ":443/hpoa";
 | |
| 
 | |
|   # One of the cool things about SOAP::Lite is that almost every
 | |
|   # method returns $self.  This allows you to string together
 | |
|   # as many calls as you need, like this:
 | |
|   $self
 | |
|     # keep the XML formatted for human readability, in case
 | |
|     # we ever have to look at it (unlikely)
 | |
|     -> readable(1)
 | |
| 
 | |
|     # Need to tell SOAP about some namespaces.  I don't know if they
 | |
|     # are all necessary or not, but I got them from the hpoa.wsdl
 | |
|     -> ns("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "wsu")
 | |
|     -> ns('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', "wsse")
 | |
|     -> ns('http://www.w3.org/2001/XMLSchema-instance', 'xsi')
 | |
|     -> ns('http://www.w3.org/2003/05/soap-encoding', 'SOAP-ENC')
 | |
|     -> ns('http://www.w3.org/2003/05/soap-envelope', 'SOAP-ENV')
 | |
|     -> ns('http://www.w3.org/2001/XMLSchema', 'xsd')
 | |
|     -> default_ns("hpoa.xsd", "hpoa")
 | |
| 
 | |
|     # Inform SOAP of the OA URL
 | |
|     -> proxy($proxy);
 | |
| 
 | |
|   return $self;
 | |
| }
 | |
| 
 | |
| # Method: call
 | |
| # Input: method and a hash of method's input params (see below)
 | |
| # Output: SOAP::SOM object (SOAP response)
 | |
| #
 | |
| # All methods in the OA API end up getting called by this routine,
 | |
| # even though the user invokes them directly using the method name.
 | |
| # For example, code that looks like this:
 | |
| # 	$hpoa->userLogIn(username=>$name, password=>$pass)
 | |
| # results in this call:
 | |
| #	$hpoa->call('userLogIn', username=>$name, password=>$pass)
 | |
| sub call {
 | |
|   my ($self, $method, %args) = @_;
 | |
| 
 | |
|   #
 | |
|   # Each item of %args is of the form:
 | |
|   #    ($name => $value).
 | |
|   #
 | |
|   # $value is usually a scalar and SOAP::Lite infers a type.
 | |
|   #
 | |
|   # If the value needs to be explicitly typed, the $value should be a
 | |
|   # reference to an array of the form:
 | |
|   #    [ $scalar, $type ]
 | |
|   # This should work for any parameter that you want to explicitly
 | |
|   # type, but for some reason the OA was not having any of it the
 | |
|   # last time I tried.
 | |
|   #
 | |
|   # If the method calls for an array of values, the $value should be
 | |
|   # a reference to an array of the form:
 | |
|   #    [ $itemName, $itemArrayRef, $itemType ]
 | |
|   #
 | |
|   # If the method calls for more complicated structure, the $value
 | |
|   # should be a reference to a hash of the form:
 | |
|   #    { name1 => value1, name2 => value2 ... }
 | |
|   # The values can themselves be scalars, array refs or hash refs,
 | |
|   # which will themselves be processed recursively.
 | |
|   #
 | |
| 
 | |
|   # Put the params in a form SOAP likes.
 | |
|   my @soapargs = ();
 | |
|   while (my ($k, $v) = each %args) {
 | |
|     push @soapargs, $self->process_args($k, $v);
 | |
|   }
 | |
|   # This is required if there are no params, otherwise SOAP::Lite
 | |
|   # makes an XML construct that the OA doesn't like.
 | |
|   @soapargs = SOAP::Data->type('xml'=> undef)
 | |
|     unless @soapargs;
 | |
| 
 | |
|   # Add the security header if it's not the login method.
 | |
|   # I'm hoping that the header will be ignored by the few methods
 | |
|   # that don't require security.
 | |
|   push (@soapargs, $self->{HPOA_SECURITY_HEADER})
 | |
|     unless ($method eq 'userLogIn') || !defined $self->{HPOA_SECURITY_HEADER};
 | |
| 
 | |
|   # Make sure we're using the correct version of SOAP, but
 | |
|   # don't mess up packages that use a different version.
 | |
|   my $version = xCAT::hpoa->soapversion();
 | |
|   xCAT::hpoa->soapversion('1.2');
 | |
| 
 | |
|   # Call the method and put the response in $r
 | |
|   my $r = $self->SUPER::call($method, @soapargs);
 | |
| 
 | |
|   # Reset the SOAP version
 | |
|   xCAT::hpoa->soapversion($version);
 | |
| 
 | |
|   # If this was the login method and it was successful, then extract
 | |
|   # the session key and remember it for subsequent calls.
 | |
|   if ($method eq 'userLogIn' && !$r->fault) {
 | |
| 
 | |
|     my $key = $r->result()->{oaSessionKey};
 | |
| 
 | |
|     # Got this XML code from the HP Insight Onboard Administrator SOAP
 | |
|     # Interface Guide 0.9.7
 | |
|     my $xml = '
 | |
| <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="true">
 | |
|   <hpoa:HpOaSessionKeyToken xmlns:hpoa="hpoa.xsd">
 | |
|      <hpoa:oaSessionKey>'
 | |
|        . $key .
 | |
|     '</hpoa:oaSessionKey>
 | |
|   </hpoa:HpOaSessionKeyToken>
 | |
| </wsse:Security>';
 | |
| 
 | |
|     $self->{HPOA_KEY} 		  = $key;
 | |
|     $self->{HPOA_SECURITY_XML} 	  = $xml;
 | |
|     $self->{HPOA_SECURITY_HEADER} = SOAP::Header->type('xml' => $xml);
 | |
|   }
 | |
| 
 | |
|   # Return the response
 | |
|   return $r;
 | |
| }
 | |
| 
 | |
| ## Create the correct SOAP::Data structure for the given args
 | |
| ## $n is the argument name
 | |
| ## $v is the value and can be of the following 4 forms:
 | |
| ##   $scalar
 | |
| ##	- A scalar value.  No further processing takes place.
 | |
| ##	  Produces: <name>value</name>
 | |
| ##   [ $scalar, $type ]
 | |
| ##	- An array ref containing a scalar value and type.  No further
 | |
| ##	  will take place.
 | |
| ##	  Produces: <name type=aType>value</name>
 | |
| ##   [ $itemName, $aref, $type ]
 | |
| ##	- An array ref containing the name for the elements, the elements
 | |
| ##	  themselves in an array ref, and the type for the elements.  The
 | |
| ##	  elements themselves can be processed.
 | |
| ##	  Produces: <name>
 | |
| ##		      <item type=aType>value1</item>
 | |
| ##		      <item type=aType>value2</item>
 | |
| ##		    </name>
 | |
| ##   { $n1 => $v1, $n2 => $v2 ... }
 | |
| ##	- A hash ref containing name value pairs that can themselves
 | |
| ##	  be processed.
 | |
| ##	  Produces: <name>
 | |
| ##		      <n1>v1</n1>
 | |
| ##		      <n2>v2</n2>
 | |
| ##	 	    </name>
 | |
| 
 | |
| sub process_args {
 | |
|   my ($self, $n, $v, $t) = @_;
 | |
|   print "process args: $n => $v\n"					if 0;
 | |
| 
 | |
|   if (!ref $v) {		# untyped scalar
 | |
|     print "\nUNTYPED SCALAR: $n => $v\n"				if 0;
 | |
|     return SOAP::Data->new(name => $n, value => $v, type => '');
 | |
|   }
 | |
| 
 | |
|   if (ref $v eq 'HASH') {	# structure
 | |
|     my ($nn, $vv, @ar);
 | |
|     while (($nn, $vv) = each %$v) {
 | |
|       print "\nSTRUCTURE $n: $nn => $vv\n"				if 0;
 | |
|       unshift @ar, $self->process_args($nn, $vv);
 | |
|     }
 | |
|     return SOAP::Data->name($n => \SOAP::Data->value(@ar));
 | |
|   }
 | |
| 
 | |
|   if (ref $v eq 'ARRAY') {
 | |
| 
 | |
|     if (scalar @$v == 2) {	# typed scalar
 | |
|       my ($value, $type) = @$v;
 | |
|       print "\nTYPED SCALAR: $n => $value ($type)\n"			if 0;
 | |
|       return SOAP::Data->new(name => $n, value => $value, type => $type);
 | |
|     }
 | |
| 
 | |
|     # Else an array of values
 | |
|     my ($itemName, $aref, $type) = @$v;
 | |
|     my (@ar, $item);
 | |
|     foreach $item (@$aref) {
 | |
|       if (ref $item eq 'HASH') {
 | |
| 	print "\nSUB STRUCTURE $n: $itemName => $item ($type)\n"	if 0;
 | |
| 	unshift @ar, $self->process_args("$itemName", $item);
 | |
|       } else {
 | |
| 	print "\nARRAY $n: $itemName => $item ($type)\n"		if 0;
 | |
| 	unshift @ar, $self->process_args($itemName, [$item, $type]);
 | |
|       }
 | |
|     }
 | |
|     return SOAP::Data->name($n => \SOAP::Data->value(@ar));
 | |
|   }
 | |
| 
 | |
|   die "Unexpected input parameter value: $n => $v\n";
 | |
| }
 | |
| 
 | |
| ###
 | |
| ### Special fault info for OAs
 | |
| ###
 | |
| 
 | |
| # The OA uses it's own fault data structures.  The simple
 | |
| # fault methods provided by SOAP::Lite are usually undef.
 | |
| # The OA's fault data looks like this:
 | |
| # {
 | |
| #   'Detail' => {
 | |
| #      'faultInfo' => {
 | |
| #         'operationName' => 'userLogIn',
 | |
| #         'errorText' => 'The user could not be authenticated.',
 | |
| #         'errorCode' => '150',
 | |
| #         'errorType' => 'USER_REQUEST'
 | |
| #      }
 | |
| #   },
 | |
| #   'Reason' => {
 | |
| #      'Text' => 'User Request Error'
 | |
| #   },
 | |
| #   'Code' => {
 | |
| #      'Value' => 'SOAP-ENV:Sender'
 | |
| #   }
 | |
| #}
 | |
| #
 | |
| # In your code, you should generally check that $response->fault
 | |
| # is defined, then print $response->oaErrorMessage.
 | |
| # If you know the codes, you can act on $response->oaErrorCode
 | |
| #
 | |
| 
 | |
| # The OA's fault structure
 | |
| sub SOAP::SOM::oaFaultInfo {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   return $self->fault->{Detail}->{faultInfo}
 | |
|     if (defined $self->fault &&
 | |
| 	defined $self->fault->{Detail} &&
 | |
| 	defined $self->fault->{Detail}->{faultInfo});
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # The name of the method producing the fault
 | |
| sub SOAP::SOM::oaOperationName {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $oafi = $self->oaFaultInfo;
 | |
| 
 | |
|   return $oafi->{operationName}
 | |
|     if defined $oafi &&
 | |
|       defined $oafi->{operationName};
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # Text of the OA fault
 | |
| sub SOAP::SOM::oaErrorText {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $oafi = $self->oaFaultInfo;
 | |
| 
 | |
|   return $oafi->{errorText}
 | |
|     if defined $oafi &&
 | |
|       defined $oafi->{errorText};
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # Numeric code of the OA fault
 | |
| sub SOAP::SOM::oaErrorCode {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $oafi = $self->oaFaultInfo;
 | |
| 
 | |
|   if (defined $oafi) {
 | |
| 
 | |
|     return $oafi->{errorCode}
 | |
|       if defined $oafi->{errorCode};
 | |
| 
 | |
|     return $oafi->{internalErrorCode}
 | |
|       if defined $oafi->{internalErrorCode};
 | |
|   }
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # Bay Number of the OA fault
 | |
| sub SOAP::SOM::oaOperationBayNumber {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $oafi = $self->oaFaultInfo;
 | |
| 
 | |
|   return $oafi->{operationBayNumber}
 | |
|     if defined $oafi &&
 | |
|       defined $oafi->{operationBayNumber};
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # Sometimes there's extra fault information
 | |
| # (Haven't seen any yet!)
 | |
| sub SOAP::SOM::oaExtraFaultData {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $oafi = $self->oaFaultInfo;
 | |
| 
 | |
|   return $oafi->{extraData}
 | |
|     if defined $oafi &&
 | |
|       defined $oafi->{extraData};
 | |
| 
 | |
|   return undef;
 | |
| }
 | |
| 
 | |
| # Nicely formatted error message for human consumption.
 | |
| # Tries to use the oaErrorText and oaErrorCode, if defined,
 | |
| # else uses the reason text.
 | |
| sub SOAP::SOM::oaErrorMessage {
 | |
|   my ($self, @args) = @_;
 | |
| 
 | |
|   my $errorText  = $self->oaErrorText;
 | |
| 
 | |
|   # Reason text is either an error message from SOAP (as when
 | |
|   # the method or argument doesn't exist), or it's a formatted
 | |
|   # form of the faultInfo->errorType enumeration.
 | |
|   my $reasonText = $self->fault->{Reason}->{Text};
 | |
| 
 | |
|   return $reasonText
 | |
|     unless defined $errorText;
 | |
| 
 | |
|   my $operationName = $self->oaOperationName;
 | |
|   my $operationBay  = $self->oaOperationBayNumber;
 | |
|   my $errorCode     = $self->oaErrorCode;
 | |
|   my $extraData     = $self->oaExtraFaultData;
 | |
| 
 | |
|   my $operation = "'$operationName' call";
 | |
|   $operation .= " on bay $operationBay"
 | |
|     if $operationBay;
 | |
| 
 | |
|   my $completeText =
 | |
|     "$reasonText $errorCode during $operation: $errorText";
 | |
|   $completeText .= "\n\t$extraData" if $extraData;
 | |
| 
 | |
|   return $completeText;
 | |
| }
 | |
| 
 | |
| 1;
 |