diff --git a/perl-xCAT/xCAT/CIMUtils.pm b/perl-xCAT/xCAT/CIMUtils.pm new file mode 100644 index 000000000..1c49f0102 --- /dev/null +++ b/perl-xCAT/xCAT/CIMUtils.pm @@ -0,0 +1,462 @@ +#! /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('TRUEFALSEFALSETRUE'); + + 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; diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index 9ef3b006b..6cbcaa4f9 100644 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -333,12 +333,16 @@ my %usage = ( renergy [-v | --version] Power 6 server specific : - renergy noderange [-V] { all | { [savingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] } } - renergy noderange [-V] { {savingstatus}={on | off} | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } + renergy noderange [-V] { all | { [savingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] } } + renergy noderange [-V] { {savingstatus}={on | off} | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } Power 7 server specific : - renergy noderange [-V] { all | { [savingstatus] [dsavingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue] } } - renergy noderange [-V] { {savingstatus}={on | off} | {dsavingstatus}={on-norm | on-maxp | off} | {fsavingstatus}={on | off} | {ffovalue}=MHZ | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } + renergy noderange [-V] { all | { [savingstatus] [dsavingstatus] [cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] [averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue] } } + renergy noderange [-V] { {savingstatus}={on | off} | {dsavingstatus}={on-norm | on-maxp | off} | {fsavingstatus}={on | off} | {ffovalue}=MHZ | {cappingstatus}={on | off} | {cappingwatt}=watt | {cappingperc}=percentage } + + Power 8 server specific : + renergy noderange [-V] { all | [savingstatus] [dsavingstatus] [averageAC] [averageAChistory] [averageDC] [averageDChistory] [ambienttemp] [ambienttemphistory] [exhausttemp] [exhausttemphistory] [fanspeed] [fanspeedhistory] [CPUspeed] [CPUspeedhistory] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue]} + renergy noderange [-V] { savingstatus={on | off} | dsavingstatus={on-norm | on-maxp | off} | fsavingstatus={on | off} | ffovalue=MHZ } BladeCenter specific : For Management Modules: diff --git a/perl-xCAT/xCAT/Utils.pm b/perl-xCAT/xCAT/Utils.pm index 8eb1e0050..8a9e5e8fc 100755 --- a/perl-xCAT/xCAT/Utils.pm +++ b/perl-xCAT/xCAT/Utils.pm @@ -3298,13 +3298,68 @@ sub noderangecontainsMn return; # if no MN in the noderange, return nothing } + +# the MTM of P6 and P7 machine +my %MTM_P6P7 = ( + # P6 systems + '7998-60X' => 1, + '7998-61X' => 1, + '7778-23X' => 1, + '8203-E4A' => 1, + '8204-E8A' => 1, + '8234-EMA' => 1, + '9117-MMA' => 1, + '9119-FHA' => 1, + + # P7 systems + '8406-70Y' => 1, + '8406-71Y' => 1, + '7891-73X' => 1, + '7891-74X' => 1, + '8231-E2B' => 1, + '8202-E4B' => 1, + '8231-E2B' => 1, + '8205-E6B' => 1, + '8233-E8B' => 1, + '8236-E8C' => 1, + '9117-MMB' => 1, + '9179-MHB' => 1, + '9119-FHB' => 1, +); + +#----------------------------------------------------------------------------- + +=head3 isP6P7 + + Check whether a MTM is a P6 or P7 machine + Parameter: MTM of Power machine + +=cut + +#------------------------------------------------------------------------------- +sub isP6P7 +{ + my $class = shift; + my $mtm = shift; + + if ($class !~ /Utils/) { + $mtm = $class; + } + + if (defined $MTM_P6P7{$mtm} && $MTM_P6P7{$mtm} == 1) { + return 1; + } + + return 0; +} + =head3 filter_nodes ########################################################################## # Fliter the nodes to specific groups # For specific command, figure out the node lists which should be handled by blade.pm, fsp.pm or ipmi.pm -# mp group: the nodes will be handled by blade.pm -# fsp group: the nodes will be handled by fsp.pm -# bmc group: the nodes will be handled by ipmi.pm +# mp group (argument: $mpnodes): the nodes will be handled by blade.pm +# fsp group (argument: $fspnodes): the nodes will be handled by fsp.pm +# bmc group (argument: $bmcnodes): the nodes will be handled by ipmi.pm # For rspconfig network, the NGP ppc blade will be included in the group of mp, othewise in the fsp group # For getmacs -D, the NGP ppc blade will be included in the group of common fsp, otherwise in the mp group # For renergy command, NGP blade will be moved to mp group @@ -3346,13 +3401,29 @@ sub filter_nodes{ if ($ipmitab) { $ipmitabhash = $ipmitab->getNodesAttribs(\@nodes,['bmc']); } + + # get the node attributes from the nodehm table my $nodehmhash; my $nodehmtab = xCAT::Table->new("nodehm"); if ($nodehmtab) { $nodehmhash = $nodehmtab->getNodesAttribs(\@nodes,['mgt']); } - my (@mp, @ngpfsp, @ngpbmc, @commonfsp, @commonbmc, @unknow); + # get the node attributes from the nodetype table + my $nodetypehash; + my $nodetypetab = xCAT::Table->new("nodetype"); + if ($nodetypetab) { + $nodetypehash = $nodetypetab->getNodesAttribs(\@nodes, ['arch']); + } + + # get the node attributes from the vpd table + my $vpdhash, + my $vpdtab = xCAT::Table->new("vpd"); + if ($vpdtab) { + $vpdhash = $vpdtab->getNodesAttribs(\@nodes, ['mtm']); + } + + my (@mp, @ngpfsp, @ngpbmc, @commonfsp, @commonbmc, @unknow, @nonppcle, @p6p7); # if existing in both 'mpa' and 'ppc', a ngp power blade # if existing in both 'mpa' and 'ipmi', a ngp x86 blade @@ -3386,9 +3457,21 @@ sub filter_nodes{ } elsif (defined ($ppctabhash->{$_}->[0]) && defined ($ppctabhash->{$_}->[0]->{'hcp'})) { # common power node push @commonfsp, $_; + # whether is a Power 8 or higher with FSP + if (defined ($vpdhash->{$_}->[0]) && defined ($vpdhash->{$_}->[0]->{'mtm'})) { + if (isP6P7($vpdhash->{$_}->[0]->{'mtm'})) { + push @p6p7, $_; + } + } } elsif (defined ($ipmitabhash->{$_}->[0]) && defined ($ipmitabhash->{$_}->[0]->{'bmc'})) { # common bmc node push @commonbmc, $_; + # whether is a Power 8 or higher with FSP + if (defined ($nodetypehash->{$_}->[0]) && defined ($nodetypehash->{$_}->[0]->{'arch'})) { + if ($nodetypehash->{$_}->[0]->{'arch'} ne "ppc64le") { + push @nonppcle, $_; + } + } } else { push @unknow, $_; } @@ -3422,6 +3505,16 @@ sub filter_nodes{ push @{$mpnodes}, @ngpfsp; } } elsif ($cmd eq "renergy") { + # for renergy command, only the p6,p7 get to the general fsp.pm + # P8 and higher will get in the energy.pm + @{$fspnodes} = (); + push @{$fspnodes}, @p6p7; + + # for rnergy command, only the non-ppcle nodes get to the general ipmi.pm + # ppcle of P8 and higher will get in the energy.pm + @{$bmcnodes} = (); + push @{$bmcnodes}, @nonppcle; + if (grep /^(relhistogram)/, @args) { push @{$bmcnodes}, @ngpbmc; } else { diff --git a/xCAT-client/pods/man1/renergy.1.pod b/xCAT-client/pods/man1/renergy.1.pod index aec573f92..c036e9148 100644 --- a/xCAT-client/pods/man1/renergy.1.pod +++ b/xCAT-client/pods/man1/renergy.1.pod @@ -27,17 +27,34 @@ B =over 2 +B I [-V] { all | [savingstatus] [dsavingstatus] +[cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] +[averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] +[syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] +[ffoTurbo] [ffoNorm] [ffovalue]} + +B I [-V] { savingstatus={on | off} +| dsavingstatus={on-norm | on-maxp | off} +| fsavingstatus={on | off} | ffovalue=MHZ +| cappingstatus={on | off} | cappingwatt=watt +| cappingperc=percentage } + +=back + +B + +=over 2 + B I [-V] { all | [savingstatus] [dsavingstatus] -[cappingstatus] [cappingmaxmin] [cappingvalue] [cappingsoftmin] -[averageAC] [averageDC] [ambienttemp] [exhausttemp] [CPUspeed] +[averageAC] [averageAChistory] [averageDC] [averageDChistory] +[ambienttemp] [ambienttemphistory] [exhausttemp] [exhausttemphistory] +[fanspeed] [fanspeedhistory] [CPUspeed] [CPUspeedhistory] [syssbpower] [sysIPLtime] [fsavingstatus] [ffoMin] [ffoVmin] [ffoTurbo] [ffoNorm] [ffovalue]} B I [-V] { savingstatus={on | off} | dsavingstatus={on-norm | on-maxp | off} -| fsavingstatus={on | off} | ffovalue=MHZ -| cappingstatus={on | off} | cappingwatt=watt -| cappingperc=percentage } +| fsavingstatus={on | off} | ffovalue=MHZ } =back @@ -127,10 +144,11 @@ user can query and set the power saving and power capping status, and also can query the average consumed energy, the ambient and exhaust temperature, the processor frequency for a server. -B command supports IBM POWER6 and POWER7 rack-mounted servers, +B command supports IBM POWER6, POWER7 and POWER8 rack-mounted servers, BladeCenter management modules, blade servers, and iDataPlex servers. -For system p rack-mounted servers, the following specific hardware types are supported: +For I and I rack-mounted servers, the following specific hardware types are supported: I<8203-E4A>, I<8204-E8A>, I<9125-F2A>, I<8233-E8B>, I<8236-E8C>. +For I server, there's no hardware type restriction. The parameter I needs to be specified for the B command to get the target servers. The I should be a list of CEC node names, blade @@ -234,11 +252,13 @@ will get response immediately. =head1 B -For the system p nodes, the B command depends +For the I and I nodes, the B command depends on the Energy Management Plugin B to communicate with server. B can be downloaded from the IBM web site: http://www.ibm.com/support/fixcentral/. (Other Software -> EM) +NOTE: I nodes don't need this specific energy management package. + For iDataPlex nodes, the B command depends on the Energy Management Plugin B to communicate with server. This plugin must be requested from IBM. @@ -262,12 +282,14 @@ Display the version information. Verbose output. - =item B Query all energy attributes which supported by the specific type of hardware. +For I machines, will not display the attributes +for historical records. + =item B Query all energy attributes of the power domain 1 for blade @@ -282,6 +304,10 @@ management module node. Query the current ambient temperature. (Unit is centigrade) +=item B + +Query the historical records which were generated in last one hour for B. + =item B Query the total DC power available for the entire blade center chassis. @@ -298,10 +324,18 @@ averageAC is the total AC power being consumed by all modules in the chassis. It also includes power consumed by the Chassis Cooling Devices for BCH chassis. +=item B + +Query the historical records which were generated in last one hour for B. + =item B Query the average power consumed (Output). (Unit is watt) +=item B + +Query the historical records which were generated in last one hour for B. + =item B Query the Power Capabilities of the blade server. @@ -380,6 +414,10 @@ guaranteed. Query the effective processor frequency. (Unit is MHz) +=item B + +Query the historical records which were generated in last one hour for B + =item B Query the dynamic power saving status. The result should @@ -410,6 +448,21 @@ B is in turn off status. Query the current exhaust temperature. (Unit is centigrade) +=item B + +Query the historical records which were generated in last one hour for B + +=item B + +Query the fan speed for all the fans which installed in this node. (Unit is RPM - Rotations Per Minute)) + +If there are multiple fans for a node, multiple lines will be output. And a fan name in bracket will be +appended after B attribute name. + +=item B + +Query the historical records which were generated in last one hour for B. + =item B Query the minimum cpu frequency which can be set for FFO. (Fixed @@ -455,7 +508,7 @@ The ffovalue setting operation needs about 1 minute to take effect. Query the status of FFO. The result should be 'on' or 'off'. 'on' - enable; 'off' - disable. -=item B={B │ B} +=item B={B | B} Set the status of FFO. The value must be 'on' or 'off'. @@ -574,7 +627,7 @@ Query the time used from FSP standby to OS standby. =item B Query the system power consumed prior to power on. -(Unit is MHz) +(Unit is Watt) =item B @@ -595,7 +648,7 @@ center chassis. =item 1 -Query all the attributes which CEC1,CEC2 supported. +Query all attributes which CEC1,CEC2 supported. B CEC1,CEC2 all @@ -629,6 +682,43 @@ The output of the query operation: =item 2 +Query the B attribute for Power8 CEC. + +B CEC1 fanspeed + +The output of the query operation: + + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A1 00002101): 5947 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A2 00002103): 6081 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A3 00002105): 6108 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A4 00002107): 6000 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A5 00002109): 6013 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-A6 0000210B): 6013 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-E1 0000210C): 4992 RPM + CEC1: fanspeed (Fan U78CB.001.WZS00MA-E2 0000210D): 5016 RPM + +=item 3 + +Query the historical records for the B attribute. (Power8 CEC) + +B CEC1 CPUspeedhistory + +The output of the query operation: + + CEC1: CPUspeedhistory: 2027 MHZ: 20141226042900 + CEC1: CPUspeedhistory: 2027 MHZ: 20141226042930 + CEC1: CPUspeedhistory: 2244 MHZ: 20141226043000 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043030 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043100 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043130 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043200 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043230 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043300 + CEC1: CPUspeedhistory: 2393 MHZ: 20141226043330 + ... + +=item 4 + Query all the attirbutes for management module node MM1. (For chassis) B MM1 all @@ -658,7 +748,7 @@ The output of the query operation: in this power domain. mm1: thermaloutput: 9717.376000 BTU/hour -=item 3 +=item 5 Query all the attirbutes for blade server node blade1. @@ -674,7 +764,7 @@ The output of the query operation: blade1: maxCPUspeed: 4204MHZ blade1: savingstatus: off -=item 4 +=item 6 Query the attributes savingstatus, cappingstatus and CPUspeed for server CEC1. @@ -687,7 +777,7 @@ The output of the query operation: CEC1: cappingstatus: on CEC1: CPUspeed: 3621 MHz -=item 5 +=item 7 Turn on the power saving function of CEC1. @@ -698,7 +788,7 @@ The output of the setting operation: CEC1: Set savingstatus succeeded. CEC1: This setting may need some minutes to take effect. -=item 6 +=item 8 Set the power capping value base on the percentage of the max-min capping value. Here, set it to 50%. diff --git a/xCAT-server/lib/xcat/plugins/energy.pm b/xCAT-server/lib/xcat/plugins/energy.pm new file mode 100644 index 000000000..c17b10371 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/energy.pm @@ -0,0 +1,1027 @@ +#!/usr/bin/perl +# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html + +=head3 xCAT_plugin::energy + + This plugin module is used to handle the renergy command for: + FSP based Power 8 machine. + 1. mgt=fsp, mtm=(p8); 2. mgt=ipmi, arch=ppc64le; + +=cut + +package xCAT_plugin::energy; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +use lib "$::XCATROOT/lib/perl"; +use strict; +use warnings "all"; +use Getopt::Long; + +use xCAT::Usage; +use xCAT::CIMUtils; +use xCAT::MsgUtils; +use xCAT::Table; +use xCAT::FSPUtils; +use xCAT::NetworkUtils; + +sub handled_commands { + return { + renergy => 'nodehm:mgt=ipmi|fsp', + } +} + +# The hash includes all valid attribute for quering +my %QUERY_ATTRS = ( + 'savingstatus' => 1, + 'dsavingstatus' => 1, + 'cappingstatus' => 1, + 'cappingmaxmin' => 1, + 'cappingvalue' => 1, + 'cappingsoftmin' => 1, + 'averageAC' => 1, + 'averageAChistory' => 1, + 'averageDC' => 1, + 'averageDChistory' => 1, + 'ambienttemp' => 1, + 'ambienttemphistory' => 1, + 'exhausttemp' => 1, + 'exhausttemphistory' => 1, + 'CPUspeed' => 1, + 'CPUspeedhistory' => 1, + 'fanspeed' => 1, + 'fanspeedhistory' => 1, + 'syssbpower' => 1, + 'sysIPLtime' => 1, + # for FFO, only supported when communicating to fsp directly + 'ffoMin' => 1, + 'ffoVmin' => 1, + 'ffoTurbo' => 1, + 'ffoNorm' => 1, + 'fsavingstatus' => 1, + 'ffovalue' => 1, +); + +# The hash includes all valid attribute for writting +my %SET_ATTRS = ( + 'savingstatus' => 1, + 'dsavingstatus' => 1, + 'cappingstatus' => 1, + 'cappingwatt' => 1, + 'cappingperc' => 1, + # for FFO + 'fsavingstatus' => 1, + 'ffovalue' => 1, +); + +=head3 parse_args + + DESCRIPTION: + Parse the arguments from the command line of renergy command + ARGUMENTS: + The request hash from preprocess_request or process_request + RETURN + First element: rc: 0 -success; 1 - fail + Second element: + 1. a string: a message for display; + 2. a reference to a hash: {verbose}, {query_list} and {set_pair}. + +=cut +sub parse_args { + my $request = shift; + + my $opt = (); + my $cmd = $request->{command}->[0]; + my $args = $request->{arg}; + my $nodes = $request->{node}; + + my @query_list = (); # The attributes list for query operation + my @set_pair = (); # The attribute need to be set. e.g. savingstatus=on + + my $set_flag = (); # Indicate there's setting param in the argv + my $query_flag = (); # Indicate there's param in the argv + + # set the usage subroutine + local *usage = sub { + my $add_msg = shift; + my $usage_string = xCAT::Usage->getUsage($cmd); + if ($add_msg) { + return("$add_msg\n".$usage_string); + } else { + return($usage_string); + } + }; + + # handle the arguments + if ($request->{arg}) { + @ARGV = @{$request->{arg}}; + + $Getopt::Long::ignorecase = 0; + Getopt::Long::Configure( "bundling" ); + + if (!GetOptions( 'V' => \$::VERBOSE, + 'h|help' => \$::HELP, + 'v|version' => \$::VERSION)) { + return (1, &usage()); + } + if ($::HELP && $::VERSION) { + return (1, &usage()); + } + if ($::HELP) { + return (0, &usage()); + } + + if ($::VERSION) { + my $version_string = xCAT::Usage->getVersion('renergy'); + return(0, $version_string); + } + + if ($::VERBOSE) { + $opt->{verbose} = 1; + } + } + + # if the request has node range + if ($nodes) { + # the default option is query all attributes + if ($#ARGV < 0) { + $ARGV[0] = "all"; + } + # Check the validity of the parameters of Query and Set + # Forbid to perform both query and set + foreach my $attr (@ARGV) { + my ($set_attr, $set_value) = split (/=/, $attr); + if (defined($set_value)) { + if ($query_flag) { + return (1, &usage("Cannot perform both Query and Set.")); + } + + # make sure the attribute is valid + if ($SET_ATTRS{$set_attr} != 1) { + return (1, &usage("Invalid attribute.")); + } + + if ($set_flag) { + return (1, &usage("Only supports to perform one setting at invoke.")); + } + + # make sure the value for attirbute is valid + if (($set_attr eq "savingstatus" || $set_attr eq "fsavingstatus") + && ($set_value ne "on" && $set_value ne "off")) { + return (1, &usage("Incorrect Value")); + } elsif ($set_attr eq "dsavingstatus" + && ($set_value ne "off" + && $set_value ne "on-norm" && $set_value ne "on-maxp")) { + return (1, &usage("Incorrect Value")); + } elsif ($set_attr eq "cappingstatus" + && ($set_value ne "on" && $set_value ne "off")) { + return (1, &usage("Incorrect Value")); + } elsif ( ($set_attr eq "cappingwatt" + || $set_attr eq "cappingperc" || $set_attr eq "ffovalue") + && $set_value =~ /\D/) { + return (1, &usage("Incorrect Value")); + } + + push @set_pair, $set_attr."=".$set_value; + $set_flag = 1; + } else { + if ($set_flag) { + return (1, &usage("Cannot perform both Query and Set.")); + } + + # replace the 'all' with all valid attribute + if ($attr eq "all") { + foreach my $q_attr (keys %QUERY_ATTRS) { + # do not include 'history' related attributes for all keyword + if ($q_attr =~ /history$/) { + next; + } + if (!grep (/^$q_attr$/, @query_list)) { + push @query_list, $q_attr; + } + } + } else { + # make sure the query attribute is valid + if ($QUERY_ATTRS{$attr} != 1) { + return (1, &usage("Invalid attribute.")); + } + if (!grep (/^$attr$/, @query_list)) { + push @query_list, $attr; + } + } + $query_flag = 1; + } + } + } else { + # no noderange, do nothing + return (1, &usage()); + } + + if (@query_list) { + $opt->{'query_list'} = join(',', @query_list); + } elsif (@set_pair) { + $opt->{'set_pair'} = join(',',@set_pair); + } + + return (0, $opt); +} + +sub preprocess_request +{ + my $req = shift; + my $callback = shift; + + # Exit if the packet has been preprocessed + if (defined ($req->{_xcatpreprocessed}->[0]) && $req->{_xcatpreprocessed}->[0] == 1) { return [$req]; } + + my ($rc, $args) = parse_args($req); + if ($rc) { + # error or message display happens + xCAT::MsgUtils->message("E", {error => [$args], errorcode => [$rc]}, $callback); + return []; + } else { + unless (ref($args)) { + xCAT::MsgUtils->message("I", {data => [$args]}, $callback); + return []; + } + } + + # do nothing if no query or setting required. + unless (defined ($args->{query_list}) || defined($args->{set_pair})) { + return []; + } + + # This plugin only handle the node which is 1. mgt=fsp, mtm=(p8); 2. mgt=ipmi, arch=ppc64le; + # otherwise, make other plugin to handle it + my (@mpnodes, @fspnodes, @bmcnodes, @nohandle); + xCAT::Utils->filter_nodes($req, \@mpnodes, \@fspnodes, \@bmcnodes, \@nohandle); + + # Find the nodes which are not handled by mp (@mpnodes), fsp (@fspnodes) and bmc (@bmcnods), and not in @nohandle. + # They are the one of p8_fsp nodes which should be handled by this plugin + my %tmphash = map {$_ => 1} (@mpnodes, @fspnodes, @bmcnodes, @nohandle); + my @nodes = grep {not $tmphash{$_}} @{$req->{node}}; + + # build request array + my @requests; + if (@nodes) { + my $sn = xCAT::ServiceNodeUtils->get_ServiceNode( \@nodes, 'xcat', 'MN' ); + + # Build each request for each service node + foreach my $snkey ( keys %$sn ) { + my $reqcopy = {%$req}; + $reqcopy->{node} = $sn->{$snkey}; + $reqcopy->{'_xcatdest'} = $snkey; + $reqcopy->{_xcatpreprocessed}->[0] = 1; + if (defined($args->{verbose})) { + $reqcopy->{verbose} = $args->{verbose}; + } + if (defined($args->{query_list})) { + $reqcopy->{query_list} = $args->{query_list}; + } + if (defined($args->{set_pair})) { + $reqcopy->{set_pair} = $args->{set_pair}; + } + push @requests, $reqcopy; + } + + return \@requests; + } + + return []; +} + + +sub process_request { + my $request = shift; + my $callback = shift; + my $subreq = shift; + + my $verbose; + + my $nodes = $request->{node}; + my $args = $request->{arg}; + if (defined($request->{verbose})) { + $verbose = $request->{verbose}; + } + + # get the password for the nodes + my $user_default = "admin"; + my $password_default = "admin"; + + my $ipmi_tab = xCAT::Table->new('ipmi', -create=>0); + my $ipmi_hash; + if ($ipmi_tab) { + $ipmi_hash = $ipmi_tab->getNodesAttribs($request->{node}, ['bmc', 'username','password']); + } + + my $ppc_tab = xCAT::Table->new('ppc', -create=>0); + my $ppc_hash; + my @ppc_all_entry; + my $cec2fsp; + if ($ppc_tab) { + $ppc_hash = $ppc_tab->getNodesAttribs($request->{node}, ['hcp', 'nodetype']); + } + + my $nodehm_tab = xCAT::Table->new('nodehm', -create => 0); + my $nodehm_hash; + if ($nodehm_tab) { + $nodehm_hash = $nodehm_tab->getNodesAttribs($request->{node}, ['mgt']); + } + + my $ppcdirect_tab = xCAT::Table->new('ppcdirect', -create=>0); + + foreach my $node (@{$request->{node}}) { + my $user = $user_default; + my $password = $password_default; + my $hcp_ip; + + if (defined ($nodehm_hash->{$node}->[0]->{mgt})) { + my $mgt = $nodehm_hash->{$node}->[0]->{mgt}; + + if ($mgt eq 'fsp') { + # This is Power node which is running in PowerVM mode + unless (@ppc_all_entry) { + @ppc_all_entry = $ppc_tab->getAllNodeAttribs(['node', 'parent', 'hcp', 'nodetype']); + foreach my $ppcentry (@ppc_all_entry) { + if (defined($ppcentry->{parent})) { + $cec2fsp->{$ppcentry->{parent}} .= "$ppcentry->{node},"; + } + } + } + $hcp_ip = $cec2fsp->{$node}; + + # Get the user/password for the node + if ($ppcdirect_tab) { + my $ppcdirect_hash = $ppcdirect_tab->getAttribs({hcp => $node, username => $user}, 'password'); + if ($ppcdirect_hash) { + $password = $ppcdirect_hash->{'password'}; + } + } + } elsif ($mgt eq 'ipmi') { + if (defined ($ipmi_hash->{$node}->[0]->{bmc})){ + # This is a ipmi managed node. (should be a ppcle) + $hcp_ip = $ipmi_hash->{$node}->[0]->{bmc}; + if (defined ($ipmi_hash->{$node}->[0]->{username})){ + $user = $ipmi_hash->{$node}->[0]->{username}; + $password = $ipmi_hash->{$node}->[0]->{password}; + } + } else { + xCAT::MsgUtils->message("E", {data => ["$node: Missed attribute [bmc]."]}, $callback); + return 1; + } + } else { + xCAT::MsgUtils->message("E", {data => ["$node: Support the valid mgt [fsp, ipmi]."]}, $callback); + return 1; + } + } else { + xCAT::MsgUtils->message("E", {data => ["$node: Missed important attributes [mgt] to know how to handle this node."]}, $callback); + return 1; + } + unless ($hcp_ip) { + xCAT::MsgUtils->message("E", {data => ["$node: Cannot find HCP"]}, $callback); + return 1; + } + foreach my $ip (split(',', $hcp_ip)) { + unless ($ip) { next; } + my $real_ip = xCAT::NetworkUtils->getipaddr($ip); + unless ($real_ip) { + xCAT::MsgUtils->message("E", {error => ["$node: Cannot get ip for $ip"], errorcode => 1}, $callback); + next; + } + my %args = ( + node => $node, + ip => $real_ip, + port => '5989', + method => 'POST', + user => $user, + password => $password); + + if ($verbose) { + $args{verbose} = 1; + $args{callback} = $callback; + xCAT::MsgUtils->message("I", {data => ["$node: Access hcp [$ip], user [$user], passowrd [$password]"]}, $callback); + } + # call the cim utils to connect to cim server + my $ret = run_cim ($request, $callback, \%args); + # 0 - success; 1 - cim error; 10 - ip is not pingable; 11 - this ip is a standby fsp + unless ($ret == 10 || $ret == 11) { + last; + } + } + } + +} + +=head3 query_pum + + DESCRIPTION: + Query the attribute for instance of FipS_PUMService class + + ARGUMENTS: + $http_params - refer to the HTTP_PARAMS in xCAT::CIMUtils.pm + + RETURN + $ret - return code and messages + $pum - a hash includes all the attributes + $namepath - the name path of pum instance + +=cut + +sub query_pum +{ + my $http_params = shift; + + my %cimargs = ( classname => 'FipS_PUMService' ); + my ($ret, $value, $namepath) = xCAT::CIMUtils->enum_instance($http_params, \%cimargs); + if ($ret->{rc}) { + return ($ret); + } + + # parse the return xml to get all the property of pum instance + my $pum; + if ($value && @$value) { + my $instance = $$value[0]; + foreach my $pname (keys %{$instance->{property}}) { + $pum->{$pname} = $instance->{property}->{$pname}->{value}; + } + } + + return ($ret, $pum, $namepath); +} + +=head3 query_cec_drawer + + DESCRIPTION: + Query the attribute for instance of FipS_CECDrawer class + + ARGUMENTS: + $http_params - refer to the HTTP_PARAMS in xCAT::CIMUtils.pm + + RETURN + $ret - return code and messages + $cec_drawer - a hash includes all the attributes + +=cut +sub query_cec_drawer +{ + my $http_params = shift; + + my %cimargs = ( classname => 'FipS_CECDrawer' ); + my ($ret, $value, $namepath) = xCAT::CIMUtils->enum_instance($http_params, \%cimargs); + if ($ret->{rc}) { + return ($ret); + } + + # parse the return xml to get all the property of pum instance + my $cec_drawer; + if ($value && @$value) { + my $instance = $$value[0]; + foreach my $pname (keys %{$instance->{property}}) { + $cec_drawer->{$pname} = $instance->{property}->{$pname}->{value}; + } + } else { + return ({rc => 1, msg => "Cannot find instance for FipS_CECDrawer class"}); + } + + return ($ret, $cec_drawer, $namepath); +} + +=head3 query_metric + + DESCRIPTION: + Query the attribute for instance of FipS_*metricValue class + + ARGUMENTS: + $http_params - refer to the HTTP_PARAMS in xCAT::CIMUtils.pm + $option - the specified operation + + RETURN + $ret - return code and messages + $array - a hash includes all the attributes + +=cut + +sub query_metric +{ + my $http_params = shift; + my $classname = shift; + my $matching_string = shift; + my $value_unit = shift; + if (!defined($value_unit)) { + $value_unit = 1; + } + my %cimargs = ( classname => "$classname" ); + my ($ret, $value) = xCAT::CIMUtils->enum_instance($http_params, \%cimargs); + if ($ret->{rc}) { + return $ret; + } + my ($matching_key, $matching_value) = split /:/,$matching_string; + my %instances_hash = (); + foreach my $instance (@$value) { + my $instance_element = undef; + my $timestamp = undef; + if (defined ($instance->{property}->{$matching_key}) and $instance->{property}->{$matching_key}->{value} !~ /$matching_value/) { + next; + } + if (defined ($instance->{property}->{InstanceID})) { + $instance_element = $instance->{property}->{InstanceID}->{value}; + $instance_element =~ s/ .*$//; + } + if (!defined($instance_element)) { + next; + } + + if (defined ($instance->{property}->{MeasuredElementName})) { + $instances_hash{$instance_element}->{MeasuredElementName} = $instance->{property}->{MeasuredElementName}->{value}; + } else { + next; + } + + if (defined ($instance->{property}->{TimeStamp})) { + $timestamp = $instance->{property}->{TimeStamp}->{value}; + $timestamp =~ s/\..*$//; + } + if (defined ($instance->{property}->{MetricValue})) { + if (defined($timestamp)) { + $instances_hash{$instance_element}->{MetricValue}->{$timestamp} = $instance->{property}->{MetricValue}->{value} / $value_unit; + } + } + } + + return ($ret, \%instances_hash); +} + +=head3 query_tmp + DESCRIPTION: + Require the input and output temperature +=cut +sub query_tmp +{ + &query_metric(@_, "FipS_ThermalMetricValue", "InstanceID:InletAirTemp|ExhaustAirTemp", 100); +} +=head3 query_cpuspeed + DESCRIPTION: + Require the cpuspeed history +=cut +sub query_cpuspeed +{ + &query_metric(@_, "FipS_CPUUsageMetricValue", "InstanceID:AvgCPUUsage"); +} +=head3 query_fanspeed + DESCRIPTION: + Require the fanspeed history +=cut +sub query_fanspeed +{ + &query_metric(@_, "FipS_FanSpeedMetricValue", "InstanceID:FansSpeed"); +} +=head3 query_powermetric + DESCRIPTION: + Require the AC and DC power comsume history +=cut +sub query_powermetric +{ + my $http_params = shift; + $http_params->{timeout} = 500; + my ($ret, $return_hash) = &query_metric($http_params, "FipS_PowerMetricValue", "InstanceID:AvgInputPwr"); + if ($ret->{rc}) { + return $ret; + } + my %instances = (); + foreach my $ins (keys %$return_hash) { + if ($return_hash->{$ins}->{MeasuredElementName} =~ /Power Supply/) { + foreach my $timestamp (keys %{$return_hash->{$ins}->{MetricValue}}) { + if (!exists($instances{"averageAC"}->{MetricValue}->{$timestamp})) { + $instances{"averageAC"}->{MetricValue}->{$timestamp} = $return_hash->{$ins}->{MetricValue}->{$timestamp}; + } else { + $instances{"averageAC"}->{MetricValue}->{$timestamp} += $return_hash->{$ins}->{MetricValue}->{$timestamp}; + } + } + } else { + $instances{"averageDC"}->{MetricValue} = $return_hash->{$ins}->{MetricValue}; + } + } + return ($ret, \%instances); +} + + + +=head3 run_cim + + DESCRIPTION: + Handle the Query and Setting of Energy via CIM + + ARGUMENTS: + $request + $callback + $http_params - refer to the HTTP_PARAMS in xCAT::CIMUtils.pm + + RETURN + First element: rc: 0 -success; 1 - cim error; 10 - ip is not pingable; 11 - this ip is a standby fsp + +=cut +sub run_cim +{ + my $request = shift; + my $callback = shift; + + my $http_params = shift; + my $node = $http_params->{node}; + + my @output; + my $verbose; + my $query_list; + my $set_pair; + + if (defined($request->{verbose})) { + $verbose = $request->{verbose}; + } + if (defined($request->{query_list})) { + $query_list = $request->{query_list}; + } + if (defined($request->{set_pair})) { + $set_pair = $request->{set_pair}; + } + + # Try to connect CIM Server to Enumerate CEC object; + # If cannot access this ip, return 10 for caller to connect to the next ip + my $cimargs = { + classname => 'fips_cec', + }; + $http_params->{timeout} = 5; + my ($ret, $value) = xCAT::CIMUtils->enum_instance($http_params, $cimargs); + if ($ret->{rc}) { + if ($ret->{msg} =~ /Couldn't connect to server/) { + xCAT::MsgUtils->message("E", data => ["$node: Couldn not connect to server [$http_params->{ip}]."], $callback); + return 10; + } else { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return 1; + } + } + delete $http_params->{timeout}; + # check whether the primary ip of fsp is the IP we are accessing + if (defined ($value->[0]->{property}->{PrimaryFSP_IP}->{value}) && $value->[0]->{property}->{PrimaryFSP_IP}->{value} ne $http_params->{ip}) { + # run against the standby fsp, do the next one + return 11; + } + + + # ======start to handle the query and setting====== + + # Pre-query some specific instances since some instances are common for multiple energy attributes + #my $query_pum_capabilities_flag; # set to query the instance for [FipS_PUMServiceCapabilities] + my $query_pum_flag; # set to query the instance for [FipS_PUMService] + my $query_pum_value; # the pre-queried PUM instance + my $namepath_pum; # the name path of PUM instance + + my $query_drawer_flag; # set to query the instance for [FipS_CECDrawer] + my $query_drawer_value; # the pre-queried cec drawer instance + + my %query_return_hash = (); # the hash store the returned hashes for query functions + my $query_tmp_value; # the requrest for FipS_ThermalMetricValue class + my $query_cpuspeed_value; # the request for FipS_CPUUsageMetricValue class + my $query_fanspeed_value; # the request for FipS_FanSpeedMetricValue class + my $query_powermetric_value; # the request for FipS_PowerMetricValue class + if ($query_list) { + foreach my $attr (split(',', $query_list)) { + if ($attr =~ /^(savingstatus|dsavingstatus|fsavingstatus|ffoMin|ffoVmin|ffoTurbo|ffoNorm|ffovalue)$/) { + $query_pum_flag = 1; + } elsif ($attr =~ /^(syssbpower|sysIPLtime)$/) { + $query_drawer_flag = 1; + } elsif ($attr =~ /^(ambienttemp|exhausttemp)/) { + $query_tmp_value = 1; + } elsif ($attr =~ /^CPUspeed/) { + $query_cpuspeed_value = 1; + } elsif ($attr =~ /^fanspeed/) { + $query_fanspeed_value = 1; + } elsif ($attr =~ /^(averageAC|averageDC)/) { + $query_powermetric_value = 1; + } + } + } + + if ($set_pair) { + my ($set_name, $set_value) = split('=', $set_pair); + if ($set_name =~/^(savingstatus|dsavingstatus|fsavingstatus|ffovalue)$/) { + $query_pum_flag = 1; + } + } + + # query the pre required instances + if ($query_pum_flag) { + ($ret, $query_pum_value, $namepath_pum) = query_pum($http_params); + if ($ret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($ret->{rc}); + } + } + if ($query_drawer_flag) { + ($ret, $query_drawer_value) = query_cec_drawer($http_params); + if ($ret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($ret->{rc}); + } + } + if ($query_powermetric_value) { + my ($tmpret, $tmpvalue) = query_powermetric($http_params); + if ($tmpret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($tmpret->{rc}); + } + $query_return_hash{query_powermetric} = $tmpvalue; + } + if ($query_fanspeed_value) { + $http_params->{timeout} = 200; + my ($tmpret, $tmpvalue) = query_fanspeed($http_params); + if ($tmpret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($tmpret->{rc}); + } + $query_return_hash{query_fanspeed} = $tmpvalue; + } + if ($query_cpuspeed_value) { + my ($tmpret, $tmpvalue) = query_cpuspeed($http_params); + if ($tmpret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($tmpret->{rc}); + } + $query_return_hash{query_cpuspeed} = $tmpvalue; + } + if ($query_tmp_value) { + $http_params->{timeout} = 200; + my ($tmpret, $tmpvalue) = query_tmp($http_params); + if ($tmpret->{rc}) { + xCAT::MsgUtils->message("E", {data => ["$node: $ret->{msg}"]}, $callback); + return ($tmpret->{rc}); + } + $query_return_hash{query_tmp} = $tmpvalue; + } + + # perform the query request + if ($query_list) { + foreach my $attr (split(',', $query_list)) { + # Query the power saving status + if ($attr =~ /^(savingstatus|dsavingstatus|fsavingstatus)$/) { + if ($query_pum_flag) { + if (defined ($query_pum_value->{PowerUtilizationMode})) { + # 2 = None; 3 = Dynamic; 4 = Static; 32768 = Dynamic Favor Performance; 32769 = FFO + if ($query_pum_value->{PowerUtilizationMode} eq "2") { + push @output, "$node: $attr: off"; + } elsif ($query_pum_value->{PowerUtilizationMode} eq "3") { + if ($attr eq "dsavingstatus") { + push @output, "$node: $attr: on-norm"; + } else { + push @output, "$node: $attr: off"; + } + } elsif ($query_pum_value->{PowerUtilizationMode} eq "4") { + if ($attr eq "savingstatus") { + push @output, "$node: $attr: on"; + } else { + push @output, "$node: $attr: off"; + } + } elsif ($query_pum_value->{PowerUtilizationMode} eq "32768") { + if ($attr eq "dsavingstatus") { + push @output, "$node: $attr: on-maxp"; + } else { + push @output, "$node: $attr: off"; + } + } elsif ($query_pum_value->{PowerUtilizationMode} eq "32769") { + if ($attr eq "fsavingstatus") { + push @output, "$node: $attr: on"; + } else { + push @output, "$node: $attr: off"; + } + } + } else { + push @output, "$node: $attr: na"; + } + } else { + push @output, "$node: $attr: na"; + } + } + + # Query the FFO settings + if ($attr =~ /^(ffoMin|ffoVmin|ffoTurbo|ffoNorm|ffovalue)$/) { + if ($query_pum_flag) { + if (defined ($query_pum_value->{FixedFrequencyPoints}) && defined ($query_pum_value->{FixedFrequencyPointValues})) { + my @ffo_point = split (',', $query_pum_value->{FixedFrequencyPoints}); + my @ffo_point_value = split (',', $query_pum_value->{FixedFrequencyPointValues}); + foreach my $index (0..$#ffo_point) { + if ($ffo_point[$index] eq '2' && $attr eq 'ffoNorm') { # Norminal + push @output, "$node: $attr: $ffo_point_value[$index] MHZ"; + } elsif ($ffo_point[$index] eq '3' && $attr eq 'ffoTurbo') { # Turbo + push @output, "$node: $attr: $ffo_point_value[$index] MHZ"; + } elsif ($ffo_point[$index] eq '4' && $attr eq 'ffoVmin') { # Vmin + push @output, "$node: $attr: $ffo_point_value[$index] MHZ"; + } elsif ($ffo_point[$index] eq '5' && $attr eq 'ffoMin') { # Min + push @output, "$node: $attr: $ffo_point_value[$index] MHZ"; + } + } + } else { + push @output, "$node: $attr: na"; + } + } else { + push @output, "$node: $attr: na"; + } + } + + # Query the FFO Value + if ($attr eq 'ffovalue') { + if ($query_pum_flag) { + if (defined ($query_pum_value->{FixedFrequencyOverrideFreq})) { + if ($query_pum_value->{FixedFrequencyOverrideFreq} eq '4294967295') { + push @output, "$node: $attr: 0 MHZ"; + } else { + push @output, "$node: $attr: $query_pum_value->{FixedFrequencyOverrideFreq} MHZ"; + } + } else { + push @output, "$node: $attr: na"; + } + } else { + push @output, "$node: $attr: na"; + } + + } + + # Query the attribute sysIPLtime and syssbpower + if ($attr =~ /^(syssbpower|sysIPLtime)$/) { + if ($query_drawer_flag) { + if (defined ($query_drawer_value->{AverageTimeToIPL}) && $attr eq "sysIPLtime") { + push @output, "$node: $attr: $query_drawer_value->{AverageTimeToIPL} S"; + } elsif (defined ($query_drawer_value->{StandbyPowerUtilization}) && $attr eq "syssbpower") { + push @output, "$node: $attr: $query_drawer_value->{StandbyPowerUtilization} W"; + } else { + push @output, "$node: $attr: na"; + } + } else { + push @output, "$node: $attr: na"; + } + } + + if ($attr =~ /^ambienttemp/) { + my $tmphash = $query_return_hash{query_tmp}; + foreach my $ins (keys %$tmphash) { + if ($ins =~ /InletAirTemp/) { + my @times = sort keys %{$tmphash->{$ins}->{MetricValue}}; + if ($attr eq "ambienttemp") { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$times[-1]}"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$times[-1]} C"; + } else { + foreach my $time (@times) { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$time}: $time"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$time} C: $time"; + } + } + } + } + } elsif ($attr =~ /^exhausttemp/) { + my $tmphash = $query_return_hash{query_tmp}; + foreach my $ins (keys %$tmphash) { + if ($ins =~ /ExhaustAirTemp/) { + my @times = sort keys %{$tmphash->{$ins}->{MetricValue}}; + if ($attr eq "exhausttemp") { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$times[-1]}"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$times[-1]} C"; + } else { + foreach my $time (@times) { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$time}: $time"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$time} C: $time"; + } + } + } + } + } elsif ($attr =~ /^CPUspeed/) { + my $tmphash = $query_return_hash{query_cpuspeed}; + foreach my $ins (keys %$tmphash) { + if ($ins =~ /AvgCPUUsage/) { + my @times = sort keys %{$tmphash->{$ins}->{MetricValue}}; + if ($attr eq "CPUspeed") { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$times[-1]}"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$times[-1]} MHZ"; + } else { + foreach my $time (@times) { + #push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$time}: $time"; + push @output, "$node: $attr: $tmphash->{$ins}->{MetricValue}->{$time} MHZ: $time"; + } + } + } + } + } elsif ($attr =~ /^fanspeed/) { + my $tmphash = $query_return_hash{query_fanspeed}; + foreach my $ins (keys %$tmphash) { + if ($ins =~ /FansSpeed/) { + my @times = sort keys %{$tmphash->{$ins}->{MetricValue}}; + if ($attr eq "fanspeed") { + push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$times[-1]} RPM"; + } else { + foreach my $time (@times) { + push @output, "$node: $attr ($tmphash->{$ins}->{MeasuredElementName}): $tmphash->{$ins}->{MetricValue}->{$time} RPM: $time"; + } + } + } + } + $query_fanspeed_value = 1; + } elsif ($attr =~ /^(averageAC|averageDC)/) { + my $tmpattr = $1; + my $tmphash = $query_return_hash{query_powermetric}; + my @times = sort keys %{$tmphash->{$tmpattr}->{MetricValue}}; + if ($attr =~ /history$/) { + foreach my $time (@times) { + push @output, "$node: $attr: $tmphash->{$tmpattr}->{MetricValue}->{$time} W: $time"; + } + } else { + push @output, "$node: $attr: $tmphash->{$attr}->{MetricValue}->{$times[-1]} W"; + } + } + } + } + + # Perform the setting request + if ($set_pair) { + my ($set_name, $set_value) = split('=', $set_pair); + if ($set_name =~/^(savingstatus|dsavingstatus|fsavingstatus|ffovalue)$/) { + if ($query_pum_flag) { + if (defined ($query_pum_value->{PowerUtilizationMode})) { + + # set the power saving value + my $ps_value; + if ($set_name eq "savingstatus") { + if ($set_value eq 'on') { + $ps_value = '4'; + } elsif ($set_value eq 'off') { + $ps_value = '2'; + } + } elsif ($set_name eq "dsavingstatus") { + if ($set_value eq 'on-norm') { + $ps_value = '3'; + } elsif ($set_value eq 'on-maxp') { + $ps_value = '32768'; + }elsif ($set_value eq 'off') { + $ps_value = '2'; + } + } elsif ($set_name eq "fsavingstatus") { + if ($set_value eq 'on') { + $ps_value = '32769'; + } elsif ($set_value eq 'off') { + $ps_value = '2'; + } + } + + if ($set_name eq "ffovalue") { + # set ffo value + $cimargs = { + propertyname => 'FixedFrequencyOverrideFreq', + propertyvalue => $set_value, + namepath => $namepath_pum, + }; + $ret = xCAT::CIMUtils->set_property($http_params, $cimargs); + if ($ret->{rc}) { + push @output, "$node: Set $set_name failed. [$ret->{msg}]"; + } else { + push @output, "$node: Set $set_name succeeded"; + } + } else { + # set the power saving + if ($ps_value eq $query_pum_value->{PowerUtilizationMode}) { # do nothing if it already was the correct status + push @output, "$node: Set $set_name succeeded"; + } else { + # perform the setting + $cimargs = { + propertyname => 'PowerUtilizationMode', + propertyvalue => $ps_value, + namepath => $namepath_pum, + }; + $ret = xCAT::CIMUtils->set_property($http_params, $cimargs); + if ($ret->{rc}) { + push @output, "$node: Set $set_name failed. [$ret->{msg}]"; + } else { + push @output, "$node: Set $set_name succeeded"; + } + } + } + } else { + push @output, "$node: Set $set_name failed"; + } + } else { + push @output, "$node: Set $set_name failed"; + } + } + } + + # Display the output + my $rsp; + if ($query_list && $query_list !~ /history/) { + push @{$rsp->{data}}, sort (@output); + } else { + push @{$rsp->{data}}, @output; + } + xCAT::MsgUtils->message("I", $rsp, $callback); + + return 0; +} + + +1; diff --git a/xCAT-server/lib/xcat/plugins/fsp.pm b/xCAT-server/lib/xcat/plugins/fsp.pm index 605bc82af..9726685b6 100644 --- a/xCAT-server/lib/xcat/plugins/fsp.pm +++ b/xCAT-server/lib/xcat/plugins/fsp.pm @@ -81,7 +81,7 @@ sub preprocess_request { my (@fspnodes, @nohandle); xCAT::Utils->filter_nodes($arg1, undef, \@fspnodes, undef, \@nohandle); if (@fspnodes) { - $arg1->{noderange} = \@fspnodes; + $arg1->{node} = \@fspnodes; } else { return []; } diff --git a/xCAT-server/xCAT-server.spec b/xCAT-server/xCAT-server.spec index 46fe1c6ee..17bc22eab 100644 --- a/xCAT-server/xCAT-server.spec +++ b/xCAT-server/xCAT-server.spec @@ -25,7 +25,7 @@ AutoReqProv: no # also need to fix Requires for AIX %ifos linux BuildArch: noarch -Requires: perl-IO-Socket-SSL perl-XML-Simple perl-XML-Parser perl-Digest-SHA1 +Requires: perl-IO-Socket-SSL perl-XML-Simple perl-XML-Parser perl-Digest-SHA1 perl(LWP::Protocol::https) Obsoletes: atftp-xcat %endif