463 lines
16 KiB
Perl
463 lines
16 KiB
Perl
#! /usr/bin/env perl
|
|
# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
# This package offers subroutines to access CIM server
|
|
|
|
package xCAT::CIMUtils;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use HTTP::Headers;
|
|
use HTTP::Request;
|
|
use LWP::UserAgent;
|
|
|
|
use XML::LibXML;
|
|
use Data::Dumper;
|
|
|
|
=head1 HTTP_PARAMS
|
|
|
|
A hash which includes all the parameters for accessing HTTP server. The valid parameter:
|
|
ip: The IP address of the HTTP server
|
|
user: The user to access HTTP server.
|
|
password: The password for the user.
|
|
method: The http method. (GET, PUT, POST, DELETE). Default is GET
|
|
protocol: The protocol which will be used to access HTTP server. (http/https). Default is https
|
|
format: The format of payload. Default is xml
|
|
payload: The payload of http
|
|
|
|
Example:
|
|
my %http_params = ( ip => '192.168.1.1',
|
|
port => '5989',
|
|
user => 'HMC',
|
|
password => 'admin',
|
|
method => 'POST',
|
|
protocol => 'https');
|
|
|
|
=cut
|
|
|
|
=head1 enum_instance ()
|
|
Description:
|
|
Enumerate CIM instances.
|
|
|
|
Arguments:
|
|
http_params: A reference to HTTP_PARAMS
|
|
cim_params: The CIM parameters
|
|
classname - a mandatory param to specify the class that enumerate will target to.
|
|
|
|
Return:
|
|
1 - A hash reference. The valid key includes:
|
|
rc - The return code. 0 - success. > 0 - fail.
|
|
cim_rc - The return code from CIM server.
|
|
msg - Output message.
|
|
|
|
2 - Array of instances, each instance is a hash contains lots of properties.
|
|
|
|
3 - The name path of instance
|
|
=cut
|
|
|
|
|
|
sub enum_instance
|
|
{
|
|
my $http_params = shift;
|
|
unless (ref($http_params)) {
|
|
$http_params = shift;
|
|
}
|
|
|
|
my $cim_params = shift;
|
|
|
|
# This is a mandatory parameter
|
|
unless ($cim_params->{classname}) {
|
|
return ({rc => 1, msg => "Missed the classname"});
|
|
}
|
|
|
|
unless ($cim_params->{namespace}) {
|
|
$cim_params->{namespace} = "ibmsd";
|
|
}
|
|
|
|
# prepare the CIM payload
|
|
my $tmpnode;
|
|
|
|
# create a new doc
|
|
my $doc = XML::LibXML->createDocument('1.0','UTF-8');
|
|
|
|
# create and add the root element
|
|
my $root = $doc->createElement("CIM");
|
|
$root->setAttribute("CIMVERSION", "2.0");
|
|
$root->setAttribute("DTDVERSION", "2.0");
|
|
|
|
$doc->setDocumentElement($root);
|
|
|
|
# create and add the MESSAGE element
|
|
my $message = $doc->createElement("MESSAGE");
|
|
$message->setAttribute("ID", "1000");
|
|
$message->setAttribute("PROTOCOLVERSION", "1.0");
|
|
|
|
$root->addChild($message);
|
|
|
|
# add a SIMPLE REQUEST
|
|
my $simple_request = $doc->createElement("SIMPLEREQ");
|
|
$message->addChild($simple_request);
|
|
|
|
# add an IMETHOD CALL
|
|
my $imethod_call = $doc->createElement("IMETHODCALL");
|
|
$imethod_call->setAttribute("NAME", "EnumerateInstances");
|
|
|
|
$simple_request->addChild($imethod_call);
|
|
|
|
# add the local name space path
|
|
my $localnamespacepath = $doc->createElement("LOCALNAMESPACEPATH");
|
|
$tmpnode = $doc->createElement("NAMESPACE");
|
|
$tmpnode->setAttribute("NAME", "root");
|
|
$localnamespacepath->addChild($tmpnode);
|
|
|
|
$tmpnode = $doc->createElement("NAMESPACE");
|
|
$tmpnode->setAttribute("NAME", $cim_params->{namespace});
|
|
$localnamespacepath->addChild($tmpnode);
|
|
|
|
$imethod_call->addChild($localnamespacepath);
|
|
|
|
# add the target class name
|
|
my $param_classname = $doc->createElement("IPARAMVALUE");
|
|
$param_classname->setAttribute("NAME", "ClassName");
|
|
$imethod_call->addChild($param_classname);
|
|
|
|
my $classname = $doc->createElement("CLASSNAME");
|
|
$classname->setAttribute("NAME", $cim_params->{classname});
|
|
$param_classname->addChild($classname);
|
|
|
|
# add several common parameters
|
|
$imethod_call->appendWellBalancedChunk('<IPARAMVALUE NAME="DeepInheritance"><VALUE>TRUE</VALUE></IPARAMVALUE><IPARAMVALUE NAME="LocalOnly"><VALUE>FALSE</VALUE></IPARAMVALUE><IPARAMVALUE NAME="IncludeQualifiers"><VALUE>FALSE</VALUE></IPARAMVALUE><IPARAMVALUE NAME="IncludeClassOrigin"><VALUE>TRUE</VALUE></IPARAMVALUE>');
|
|
|
|
my $payload = $doc->toString();
|
|
|
|
# generate http request
|
|
my $ret = gen_http_request($http_params, $payload);
|
|
|
|
if ($ret->{rc}) {
|
|
return $ret;
|
|
}
|
|
|
|
# send request to http server
|
|
$ret = send_http_request($http_params, $ret->{request});
|
|
if ($ret->{rc}) {
|
|
return $ret;
|
|
}
|
|
|
|
# parse the http response
|
|
my $ret_value;
|
|
my $parser = XML::LibXML->new();
|
|
my $resp_doc = $parser->parse_string($ret->{payload});
|
|
|
|
# check the error message from CIM
|
|
my $error_node = $resp_doc->getElementsByTagName("ERROR");
|
|
if ($error_node) {
|
|
my $msg = $error_node->[0]->getAttribute("DESCRIPTION");
|
|
my $errorcode = $error_node->[0]->getAttribute("CODE");
|
|
return ({rc => 1, cim_rc => $errorcode, msg => $error_node->[0]->getAttribute("DESCRIPTION")." [cim return code: $errorcode]"});
|
|
}
|
|
|
|
# get the name path of the instance, which is used to set property
|
|
my @namepath = $resp_doc->getElementsByTagName("INSTANCENAME");
|
|
my $namepath_string;
|
|
if (@namepath) {
|
|
$namepath_string = $namepath[0]->toString();
|
|
}
|
|
|
|
# get all the instance elements
|
|
my @instances = $resp_doc->getElementsByTagName("VALUE.NAMEDINSTANCE");
|
|
foreach my $instance (@instances) {
|
|
# get all the property element for each instance
|
|
my @properties = $instance->getElementsByTagName("PROPERTY");
|
|
if (my @property_arrays = $instance->getElementsByTagName("PROPERTY.ARRAY")) {
|
|
push @properties, @property_arrays;
|
|
}
|
|
my $ins_value;
|
|
foreach my $property (@properties) {
|
|
# get name, vlaue and type for each property. (only the one which has value)
|
|
if (my $pname = $property->getAttribute("NAME")) {
|
|
if (my $pvalue = $property->getAttribute("TYPE")) {
|
|
$ins_value->{property}->{$pname}->{type} = $pvalue;
|
|
}
|
|
if ($property->getElementsByTagName("VALUE.ARRAY")) {
|
|
my @nodelist = $property->getElementsByTagName("VALUE");
|
|
my @value_array = ();
|
|
foreach my $n (@nodelist) {
|
|
push @value_array, $n->textContent;
|
|
}
|
|
$ins_value->{property}->{$pname}->{value} = join(',',@value_array);
|
|
} elsif (my $node = $property->getElementsByTagName("VALUE")) {
|
|
$ins_value->{property}->{$pname}->{value} = $node->[0]->textContent;
|
|
}
|
|
}
|
|
}
|
|
push @{$ret_value}, $ins_value;
|
|
}
|
|
|
|
return ({rc =>0}, $ret_value, $namepath_string);
|
|
}
|
|
|
|
=head1 set_property ()
|
|
Description:
|
|
Set the property for an instance.
|
|
|
|
Arguments:
|
|
http_params: A reference to HTTP_PARAMS
|
|
cim_params: The CIM parameters
|
|
namepath - a mandatory param to specify the path of the instance.
|
|
It should be returned from 'enum_instance' subroutine.
|
|
|
|
Return:
|
|
1 - A hash reference. The valid key includes:
|
|
rc - The return code. 0 - success. > 0 - fail.
|
|
cim_rc - The return code from CIM server.
|
|
msg - Output message.
|
|
=cut
|
|
sub set_property
|
|
{
|
|
my $http_params = shift;
|
|
unless (ref($http_params)) {
|
|
$http_params = shift;
|
|
}
|
|
|
|
my $cim_params = shift;
|
|
|
|
# This is a mandatory parameter
|
|
unless ($cim_params->{namepath}) {
|
|
return ({rc => 1, msg => "Missed the name path for the instance"});
|
|
}
|
|
|
|
unless ($cim_params->{namespace}) {
|
|
$cim_params->{namespace} = "ibmsd";
|
|
}
|
|
|
|
# prepare the CIM payload
|
|
my $tmpnode;
|
|
|
|
# create a new doc
|
|
my $doc = XML::LibXML->createDocument('1.0','UTF-8');
|
|
|
|
# create and add the root element
|
|
my $root = $doc->createElement("CIM");
|
|
$root->setAttribute("CIMVERSION", "2.0");
|
|
$root->setAttribute("DTDVERSION", "2.0");
|
|
|
|
$doc->setDocumentElement($root);
|
|
|
|
# create and add the MESSAGE element
|
|
my $message = $doc->createElement("MESSAGE");
|
|
$message->setAttribute("ID", "1000");
|
|
$message->setAttribute("PROTOCOLVERSION", "1.0");
|
|
|
|
$root->addChild($message);
|
|
|
|
# add a SIMPLE REQUEST
|
|
my $simple_request = $doc->createElement("SIMPLEREQ");
|
|
$message->addChild($simple_request);
|
|
|
|
# add an IMETHOD CALL
|
|
my $imethod_call = $doc->createElement("IMETHODCALL");
|
|
$imethod_call->setAttribute("NAME", "SetProperty");
|
|
|
|
$simple_request->addChild($imethod_call);
|
|
|
|
# add the local name space path
|
|
my $localnamespacepath = $doc->createElement("LOCALNAMESPACEPATH");
|
|
$tmpnode = $doc->createElement("NAMESPACE");
|
|
$tmpnode->setAttribute("NAME", "root");
|
|
$localnamespacepath->addChild($tmpnode);
|
|
|
|
$tmpnode = $doc->createElement("NAMESPACE");
|
|
$tmpnode->setAttribute("NAME", $cim_params->{namespace});
|
|
$localnamespacepath->addChild($tmpnode);
|
|
|
|
$imethod_call->addChild($localnamespacepath);
|
|
|
|
# add the target property name
|
|
my $param_propertyname = $doc->createElement("IPARAMVALUE");
|
|
$param_propertyname->setAttribute("NAME", "PropertyName");
|
|
$imethod_call->addChild($param_propertyname);
|
|
|
|
$tmpnode = $doc->createElement("VALUE");
|
|
$tmpnode->appendTextNode($cim_params->{propertyname});
|
|
$param_propertyname->addChild($tmpnode);
|
|
|
|
# add the target property value
|
|
my $param_newvaluename = $doc->createElement("IPARAMVALUE");
|
|
$param_newvaluename->setAttribute("NAME", "NewValue");
|
|
$imethod_call->addChild($param_newvaluename);
|
|
|
|
$tmpnode = $doc->createElement("VALUE");
|
|
$tmpnode->appendTextNode($cim_params->{propertyvalue});
|
|
$param_newvaluename->addChild($tmpnode);
|
|
|
|
# add parameters of instance name path
|
|
my $param_namepath = $doc->createElement("IPARAMVALUE");
|
|
$param_namepath->setAttribute("NAME", "InstanceName");
|
|
$param_namepath->appendWellBalancedChunk($cim_params->{namepath});
|
|
$imethod_call->addChild($param_namepath);
|
|
|
|
my $payload = $doc->toString();
|
|
|
|
# generate http request
|
|
my $ret = gen_http_request($http_params, $payload);
|
|
|
|
if ($ret->{rc}) {
|
|
return $ret;
|
|
}
|
|
|
|
# send request to http server
|
|
$ret = send_http_request($http_params, $ret->{request});
|
|
if ($ret->{rc}) {
|
|
return $ret;
|
|
}
|
|
|
|
# parse the http response
|
|
my $ret_value;
|
|
my $parser = XML::LibXML->new();
|
|
my $resp_doc = $parser->parse_string($ret->{payload});
|
|
|
|
# check the error message from CIM
|
|
my $error_node = $resp_doc->getElementsByTagName("ERROR");
|
|
if ($error_node) {
|
|
my $msg = $error_node->[0]->getAttribute("DESCRIPTION");
|
|
my $errorcode = $error_node->[0]->getAttribute("CODE");
|
|
return ({rc => 1, cim_rc => $errorcode, msg => $error_node->[0]->getAttribute("DESCRIPTION")." [cim return code: $errorcode]"});
|
|
}
|
|
|
|
|
|
# if no http and cim error, the setting was succeeded
|
|
return ($ret);
|
|
}
|
|
|
|
=head1 gen_http_request ()
|
|
Description:
|
|
Generate a http request.
|
|
|
|
Arguments:
|
|
http_params: A reference to HTTP_PARAMS
|
|
payload: The payload for the http request. It can be null if the payload has been set in http_params.
|
|
|
|
Return:
|
|
A hash reference. The valid key includes:
|
|
rc - The return code. 0 - success. > 0 - fail.
|
|
msg - Output message
|
|
request - The generated HTTP::Request object
|
|
=cut
|
|
|
|
sub gen_http_request
|
|
{
|
|
my $http_params = shift;
|
|
my $http_payload = shift;
|
|
|
|
# check the mandatory parameters
|
|
unless (defined ($http_params->{ip}) && defined ($http_params->{port}) && defined($http_params->{user}) && defined($http_params->{password})) {
|
|
return ({rc => 1, msg => "Missed the mandatory parameters: ip, port, user or password"});
|
|
}
|
|
|
|
# set the default value for parameters
|
|
unless (defined ($http_params->{protocol})) {
|
|
$http_params->{protocol} = 'https';
|
|
}
|
|
unless (defined ($http_params->{format})) {
|
|
$http_params->{format} = 'xml';
|
|
}
|
|
unless (defined ($http_params->{method})) {
|
|
$http_params->{method} = 'GET';
|
|
}
|
|
|
|
my $payload = '';
|
|
if (defined ($http_params->{payload})) {
|
|
$payload = $http_params->{payload};
|
|
}
|
|
if (defined ($http_payload)) {
|
|
unless (ref($http_payload)) { #Todo: support payloasd to be a hash
|
|
$payload = $http_payload;
|
|
}
|
|
}
|
|
|
|
# create the http head
|
|
my $header = HTTP::Headers->new('content-type' => "application/$http_params->{format}",
|
|
'Accept' => "application/$http_params->{format}",
|
|
'User-Agent' => "xCAT/2",
|
|
'Host' => "$http_params->{ip}:$http_params->{port}");
|
|
|
|
# set the user & password
|
|
$header->authorization_basic($http_params->{user}, $http_params->{password});
|
|
|
|
# set the length of payload
|
|
my $plen = length($payload);
|
|
$header->push_header('Content-Length' => $plen);
|
|
|
|
# create the URL
|
|
my $url = "$http_params->{protocol}://$http_params->{ip}:$http_params->{port}";
|
|
my $request = HTTP::Request->new($http_params->{method}, $url, $header, $payload);
|
|
|
|
# set the http version
|
|
$request->protocol('HTTP/1.1');
|
|
|
|
return ({rc => 0, request => $request});
|
|
}
|
|
|
|
|
|
=head1 send_http_request ()
|
|
Description:
|
|
Send http request to http server and waiting for the response.
|
|
|
|
Arguments:
|
|
http_params: A reference to HTTP_PARAMS
|
|
http_request: A HTTP::Request object
|
|
|
|
Return:
|
|
A hash reference. The valid key includes:
|
|
rc - The return code. 0 - success. > 0 - fail.
|
|
http_rc - The return code of http. 200, 400 ...
|
|
msg - Output message
|
|
payload - The http response from http server
|
|
=cut
|
|
|
|
sub send_http_request
|
|
{
|
|
my $http_params = shift;
|
|
my $http_request = shift;
|
|
|
|
# Load the library LWP::Protocol::https for https support
|
|
if ($http_params->{protocol} eq 'https') {
|
|
eval { require LWP::Protocol::https};
|
|
if ($@) {
|
|
return ({rc => 1, msg => "Failed to load perl library LWP::Protocol::https"});
|
|
}
|
|
}
|
|
|
|
# create a new HTTP User Agent Object
|
|
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0});
|
|
if ($http_params->{timeout}) {
|
|
$ua->timeout($http_params->{timeout});
|
|
} else {
|
|
$ua->timeout(10); # the default timeout is 10s
|
|
}
|
|
|
|
if (defined($http_params->{verbose}) && defined ($http_params->{callback})) {
|
|
$http_params->{callback}({data => ["\n========CIM Request Start========", $http_request->as_string(), "=======CIM Request End======="]});
|
|
}
|
|
|
|
# send request and receive the response
|
|
my $response = $ua->request($http_request);
|
|
|
|
if (defined($http_params->{verbose}) && defined ($http_params->{callback}) && defined ($response->{_content})) {
|
|
$http_params->{callback}({data => ["\n========CIM Response Start========", $response->{_content}, "=======CIM Response End======="]});
|
|
}
|
|
|
|
# check the http response
|
|
if (defined ($response) && defined ($response->{_rc}) && defined ($response->{_msg})) {
|
|
if ($response->{_rc} eq "200" && $response->{_msg} eq "OK") {
|
|
return ({rc => 0, http_rc => $response->{_rc}, msg => "$response->{_msg} [http return code: $response->{_rc}]", payload => $response->{_content}});
|
|
}
|
|
}
|
|
|
|
return ({rc => 1, http_rc => $response->{_rc}, msg => $response->{_msg}});
|
|
}
|
|
|
|
|
|
1;
|