844 lines
40 KiB
Python
844 lines
40 KiB
Python
# Copyright Notice:
|
|
# Copyright 2016 DMTF. All rights reserved.
|
|
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfishtool/blob/main/LICENSE.md
|
|
|
|
# redfishtool: Chassis.py
|
|
#
|
|
# contains Chassis subCommands and access functions
|
|
#
|
|
# Class RfSystemsMain
|
|
# - functions init, displayUsage, displayHelp, displayOperations,
|
|
# - runOperation - Chassis command table, dispatch of operation eg get, reset
|
|
# - ChassisMain - called from redfishMain, enforce legal option combinations,
|
|
# and call runOperation to run Chassis operations (sub-sub-command)
|
|
#
|
|
# Class RfChassisOperations
|
|
# All of the Chassis sub-command operations eg: get, setIndicatorLed, etc
|
|
# - hello - test cmd
|
|
# - getCollection - return the Chassis collection
|
|
# - get - get a member of a collection -or property of the member
|
|
# - list - show of list of collection members and key idetifying properties
|
|
# (Id, AssetTag, UriPath)
|
|
# - patch - raw subcommand to patch a Chassis Member, with etag support
|
|
# - setAssetTag -- patches the assetTag of Chassis instance w/ etag support
|
|
# - setIndicatorLed --sets id LED to a specified value w/ etag support
|
|
# - getPower - get Processors collection, processor instance, or all
|
|
# - getSensors - get all sensors
|
|
# - getThermal - get Ethernet collection, instance, all
|
|
# - setPowerLimit - Set the PowerLimit for a member of the powerControl array
|
|
# - getPowerReading [consumed] - get the powerControl array, or ConsumedWatts from pwrCntl[0]
|
|
# - getLogService - get LogService collection, instance, all
|
|
# - clearLog -- clears a specified log (not implemented yet)
|
|
# - examples --prints some example apis
|
|
#
|
|
from .redfishtoolTransport import RfTransport
|
|
import requests
|
|
import json
|
|
import getopt
|
|
import re
|
|
import sys
|
|
from .ServiceRoot import RfServiceRoot
|
|
from urllib.parse import urljoin
|
|
|
|
class RfChassisMain():
|
|
def __init__(self):
|
|
# operation string and remaining args
|
|
self.operation=None
|
|
self.args=None
|
|
self.argnum=0
|
|
self.nonIdCommands=None
|
|
|
|
def displayUsage(self,rft):
|
|
if(rft.quiet): return(0)
|
|
print(" Usage:")
|
|
print(" {} [OPTNS] Chassis <operation> [<args>] -- perform <operation> on the Chassis specified ".format(rft.program))
|
|
|
|
def displayHelp(self,rft):
|
|
self.displayUsage(rft)
|
|
self.displayOperations(rft)
|
|
print(" ")
|
|
|
|
def displayOperations(self,rft):
|
|
print(" <operations>:")
|
|
print(" [collection] -- get the main Chassis collection. (Default operation if no member specified)")
|
|
print(" [get] -- get the Chassis object. (Default operation if collection member specified)")
|
|
print(" list -- list information about the Chassis collection members(\"Id\", URI, and AssetTag)")
|
|
print(" patch {A: B,C: D,...} -- patch the json-formatted {prop: value...} data to the object")
|
|
print(" setAssetTag <assetTag> -- set the Chassis's asset tag ")
|
|
print(" setIndicatorLed <state> -- set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking")
|
|
print(" Power -- get the full Power resource under a specified Chassis instance.")
|
|
print(" Thermal -- get the full Thermal resource under a specified Chassis instance.")
|
|
print(" Sensors -- get all sensors.")
|
|
print("")
|
|
print(" getPowerReading [-i<indx>] [consumed]-- get powerControl resource w/ power capacity, PowerConsumed, and power limits")
|
|
print(" if \"consumed\" keyword is added, then only current usage of powerControl[indx] is returned")
|
|
print(" <indx> is the powerControl array index. default is 0. normally, 0 is the only entry")
|
|
print(" setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] -- set powerLimit control properties")
|
|
print(" <limit>=null disables power limiting. <indx> is the powerControl array indx (dflt=0)")
|
|
print("")
|
|
print(" Logs [list] -- get the Chassis \"LogServices\" collection , or list \"id\" and URI of members.")
|
|
print(" Logs [IDOPTN] -- get the member specified by IDOPTN: -i<id>, -m<prop>:<val>, -l<link>, -a #all")
|
|
print(" clearLog <id> -- clears the log defined by <id>")
|
|
print(" examples -- example commands with syntax")
|
|
print(" hello -- Chassis hello -- debug command")
|
|
return(0)
|
|
|
|
|
|
|
|
def runOperation(self,rft):
|
|
# instantiate ChassisOperations class
|
|
op=RfChassisOperations()
|
|
|
|
# dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction"
|
|
operationTable = {
|
|
"collection": op.getCollection,
|
|
"get": op.get,
|
|
"list": op.clist,
|
|
"patch": op.patch,
|
|
"setAssetTag": op.setAssetTag,
|
|
"setIndicatorLed": op.setIndicatorLed,
|
|
"Power": op.getPower,
|
|
"Thermal": op.getThermal,
|
|
"Sensors": op.getSensors,
|
|
"setPowerLimit": op.setPowerLimit,
|
|
"getPowerReading": op.getPowerReading,
|
|
"Logs": op.getLogService,
|
|
"-clearLog": op.clearLog,
|
|
"hello": op.hello,
|
|
"examples": op.examples
|
|
}
|
|
|
|
rft.printVerbose(5,"Chassis:runOperation: operation: {}".format(self.operation))
|
|
rft.printVerbose(5,"Chassis:runOperation: args: {}".format(self.args))
|
|
|
|
if self.operation in operationTable:
|
|
rft.printVerbose(5,"Chassis:runOperation: found Oper: {} in table. executing".format(rft.subcommand))
|
|
rc,r,j,d=operationTable[self.operation](self, op, rft, cmdTop=True)
|
|
return(rc,r,j,d)
|
|
|
|
else: # invalid operation
|
|
rft.printErr("Chassis: Invalid operation: {}".format(self.operation))
|
|
return(2,None,False,None)
|
|
|
|
|
|
|
|
def ChassisMain(self,rft,cmdTop=False):
|
|
rft.printVerbose(4,"ChassisMain: subcommand: {}".format(rft.subcommand))
|
|
|
|
if( rft.help ):
|
|
self.displayHelp(rft)
|
|
return(0,None,False,None)
|
|
|
|
# we will validate usage of -P and -a in action processing
|
|
# actually, if a non 'get' action is specified, -P and -a are just ignored :)
|
|
|
|
args=rft.subcommandArgv[0:]
|
|
|
|
#if no args, then if no member Id was specified (with -I|-M|-1|-F) then assume it is a "collection" operation
|
|
# if a -IM1F was specified, then assume it is a "get" operation for that member
|
|
if( len(args) < 2 ):
|
|
if( rft.IdOptnCount==0 ):
|
|
self.operation="collection"
|
|
else:
|
|
self.operation="get"
|
|
self.args= None
|
|
else:
|
|
self.operation=args[1]
|
|
self.args = args[1:] # now args points to the 1st argument
|
|
self.argnum =len(self.args)
|
|
|
|
rft.printVerbose(5,"Chassis: operation={}, args={}".format(self.operation,self.args))
|
|
|
|
# check if the command requires a collection member target -I|-M|-L|-1|-F eg sysIdoptn
|
|
nonIdCommands = ["collection", "list", "examples", "hello"]
|
|
if( ( not self.operation in nonIdCommands ) and (rft.IdOptnCount==0) ):
|
|
# default to --One if no Id option specified
|
|
rft.oneOptn = True
|
|
rft.IdOptnCount += 1
|
|
|
|
# now execute the operation.
|
|
rc,r,j,d = self.runOperation(rft)
|
|
|
|
if(rc !=0 ):
|
|
rft.printVerbose(5,"Chassis: operation returned with error: rc={}".format(rc))
|
|
return(rc,r,False,None)
|
|
|
|
#else, if here, the subcommand executed without error. Return with 0 exit code
|
|
rft.printVerbose(5,"Chassis: operation exited OK")
|
|
return(rc,r,j,d)
|
|
|
|
|
|
#
|
|
# contains operations related to the Chassis subCommand
|
|
#
|
|
class RfChassisOperations():
|
|
def __init__(self):
|
|
self.chassisPath=None
|
|
self.chassisCollectionDict=None
|
|
|
|
|
|
def hello(self,sc,op,rft,cmdTop=False):
|
|
rft.printVerbose(4,"in hello")
|
|
rft.printVerbose(4," subcmd:{}, operation:{}, args:{}".format(rft.subcommand,sc.operation,sc.args))
|
|
print("hello world from Chassis")
|
|
return(0,None,False,None)
|
|
|
|
def getCollection(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in getCollection".format(rft.subcommand,sc.operation))
|
|
|
|
# 1st get serviceRoot
|
|
svcRoot=RfServiceRoot()
|
|
rc,r,j,d = svcRoot.getServiceRoot(rft)
|
|
if( rc != 0 ):
|
|
rft.printErr("getCollection: Error getting service root, aborting")
|
|
return(rc,r,False,None)
|
|
|
|
# get the link to the Systems collection
|
|
# need to test we got good data
|
|
if (("Chassis" in d) and ("@odata.id" in d["Chassis"])):
|
|
systemsLink=d["Chassis"]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: service root does not have a Chassis link")
|
|
return(4)
|
|
|
|
rft.printVerbose(4,"Chassis:getCollection: link is: {}".format(systemsLink))
|
|
|
|
|
|
# if a -a option was entered with "Chassis" or "Chassis collection" operation,
|
|
# then return all members of the Chassis collection expanded
|
|
if((cmdTop is True) and (rft.allOptn is True) ):
|
|
collName="Chassis"
|
|
rft.printVerbose(4,"Expand Chassis collection to return ALL Chassis collection members fully expanded in response")
|
|
rc,r,j,d=rft.getAllCollectionMembers(rft, r.url, relPath=systemsLink)
|
|
if(rc==0):
|
|
rft.printVerbose(1," Get ALL {} Collection Members".format(collName,skip1=True, printV12=cmdTop))
|
|
|
|
# otherwise, just return the collection
|
|
# now read the /Chassis collection
|
|
# use the returned url as the base url to read the Chassis collection
|
|
else:
|
|
if cmdTop is True: prop=rft.prop
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=systemsLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," Chassis Collection:",skip1=True, printV12=cmdTop)
|
|
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def get(self,sc,op,rft, cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
# getCollection
|
|
rc,r,j,d=op.getCollection(sc,op, rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
collUrl=r.url
|
|
|
|
# search collection to find path to system
|
|
sysPath,rc,r,j,d=rft.getPathBy(rft, r, d, prop)
|
|
if( rc !=0 ): #if a path was not found, its an error
|
|
return(rc,r,j,d)
|
|
|
|
rft.printVerbose(4,"ChassisOperations:get: got a path, now get entries")
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
#if here, rc=0
|
|
#if sysPath returned a response but we need to extract the property do it here
|
|
if( (r is not None) and (prop is not None) ):
|
|
rc,r,j,d=rft.getPropFromDict(rft,r,d,prop)
|
|
|
|
# otherwise, we need to do a GET to get the Chassis, if -P show property, else show full response
|
|
elif( r is None ):
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=sysPath, prop=prop)
|
|
|
|
if(rc==0): rft.printVerbose(1," Chassis Resource:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def clist(self,sc,op,rft, cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
# getCollection
|
|
collName="Chassis"
|
|
rc,r,j,d=op.getCollection(sc,op, rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
#loop through the members and create the list sub-operation response
|
|
rc,r,j,d=rft.listCollection(rft, r, d, prop="AssetTag")
|
|
if(rc==0):
|
|
rft.printVerbose(1," list {} Collection member info: Id, URI, AssetTag".format(collName,skip1=True, printV12=cmdTop))
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def iterate_op(self, run_single, sc, op, rft, cmdTop=False, prop=None):
|
|
# Wrapper method to handle issuing commands to a single chassis or to all chassis in the collection
|
|
if rft.allOptn:
|
|
# Issue command to all chassis in collection
|
|
|
|
# get the list of chassis
|
|
rc, r, j, d = op.clist(sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
if rc != 0 or not j or d is None or not isinstance(d, dict) or 'Members' not in d:
|
|
rft.printErr("Unable to get list of Chassis; return code = {}, list data = {}".format(rc, d))
|
|
return rc, r, j, d
|
|
|
|
# save existing rft options
|
|
saved_allOptn = rft.allOptn
|
|
saved_Link = rft.Link
|
|
saved_gotIdOptn = rft.gotIdOptn
|
|
saved_IdOptnCount = rft.IdOptnCount
|
|
|
|
# set rft options to process a single item
|
|
rft.allOptn = False
|
|
rft.gotIdOptn = True
|
|
rft.IdOptnCount = 1
|
|
|
|
# iterate through chassis and run operation on each based on the link (@odata.id value)
|
|
members = d.get('Members')
|
|
rc, r, j, d = 8, None, False, None
|
|
for member in members:
|
|
if '@odata.id' in member:
|
|
link = member.get('@odata.id')
|
|
# set rft.Link to point to the target chassis
|
|
rft.Link = link
|
|
# perform the operation
|
|
rc, r, j, d = run_single(sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
else:
|
|
rft.printErr("No '@odata.id' found in chassis member: {}".format(member))
|
|
|
|
# restore existing rft options
|
|
rft.allOptn = saved_allOptn
|
|
rft.Link = saved_Link
|
|
rft.gotIdOptn = saved_gotIdOptn
|
|
rft.IdOptnCount = saved_IdOptnCount
|
|
return rc, r, j, d
|
|
else:
|
|
# Issue command to single specified chassis
|
|
return run_single(sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
|
|
|
|
def patch_single(self,sc,op,rft,cmdTop=False, prop=None, patchData=None, r=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
# verify we have got an argument which is the patch structure
|
|
# its in form '{ "AssetTag": <val>, "IndicatorLed": <val> }'
|
|
##print("patchData: {}".format(patchData))
|
|
if( len( sc.args) == 2):
|
|
##print("data:{}".format(sc.args[1]))
|
|
try:
|
|
patchData=json.loads(sc.args[1])
|
|
except ValueError:
|
|
rft.printErr("Patch: invalid Json input data:{}".format(sc.args[1]))
|
|
return(5,None,False,None)
|
|
##print("patchData: {}".format(patchData))
|
|
else:
|
|
rft.printErr("Patch: error: invalid argument format")
|
|
rft.printErr(" : args={}".format(sc.args))
|
|
rft.printErr(" : expect: Systems patch \"{ <prop>: <value> }\"")
|
|
return(4,None,False,None)
|
|
|
|
# read the Chassis resource
|
|
# this is used by the generic rft.patchResource() function to see if there is an etag in the response
|
|
# if an etag is in response hdr, then we must include the etag on the patch, so this get is required
|
|
# note: the format of etag header for redfish is: ETag: W/"<string>" or "<string"
|
|
rc,r,j,d=op.get(sc,op,rft)
|
|
if( rc != 0):
|
|
return(rc,r,False,None)
|
|
|
|
# now call the generic patch function to send the patch
|
|
rc,r,j,d=rft.patchResource(rft, r, patchData)
|
|
|
|
if(rc==0): rft.printVerbose(1," Chassis Patch:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def patch(self, sc, op, rft, cmdTop=False, prop=None):
|
|
return op.iterate_op(op.patch_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
|
|
|
|
def setAssetTag_single(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
propName="AssetTag"
|
|
|
|
# get the asset tag from args
|
|
if(len(sc.args) < 2 ):
|
|
rft.printErr("Error, no assetTag value specified")
|
|
rft.printErr("Syntax: {} [options] Chassis setAssetTag \"string\" ".format(rft.program))
|
|
return(8,None,False,None)
|
|
assetTag=sc.args[1]
|
|
patchData={propName: assetTag}
|
|
|
|
# get the resource to verify it includes AssetTag,
|
|
# the response will also be used by Patch() to check for etag
|
|
rc,r,j,d=op.get(sc,op,rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
if( not propName in d ):
|
|
rft.printErr("Chassis resource does not have a {} property.".format(propName))
|
|
return(8,r,False,None)
|
|
|
|
rc,r,j,d=rft.patchResource(rft, r, patchData)
|
|
if(rc==0):
|
|
rft.printVerbose(1," Chassis setAssetTag:",skip1=True, printV12=cmdTop)
|
|
assetTag={propName: d[propName]}
|
|
return(rc,r,j,assetTag)
|
|
else: return(rc,r,False,None)
|
|
|
|
|
|
def setAssetTag(self, sc, op, rft, cmdTop=False, prop=None):
|
|
return op.iterate_op(op.setAssetTag_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
|
|
|
|
def setIndicatorLed_single(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
propName="IndicatorLED"
|
|
|
|
# get the asset tag from args
|
|
validLedStates=["Lit","Blinking","Off"]
|
|
if(len(sc.args) < 2 ):
|
|
rft.printErr("Error, no ledState value specified")
|
|
rft.printErr("Syntax: {} [options] Chassis setIndicatorLed <Lit|Off|Blinking>".format(rft.program))
|
|
return(8,None,False,None)
|
|
targLedState=sc.args[1]
|
|
if not targLedState in validLedStates:
|
|
rft.printErr("Error, invalid LED state specified")
|
|
return(8,None,False,None)
|
|
patchData={propName: targLedState}
|
|
|
|
# get the resource to verify it includes IndicatorLED,
|
|
# the response will also be used by Patch() to check for etag
|
|
rc,r,j,d=op.get(sc,op,rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
if( not propName in d ):
|
|
rft.printErr("System resource does not have a {} property.".format(propName))
|
|
return(8,r,False,None)
|
|
|
|
rc,r,j,d=rft.patchResource(rft, r, patchData)
|
|
if(rc==0):
|
|
rft.printVerbose(1," Chassis setIndicatorLed:",skip1=True, printV12=cmdTop)
|
|
ledState={"IndicatorLED": d["IndicatorLED"]}
|
|
return(rc,r,j,ledState)
|
|
else: return(rc,r,False,None)
|
|
|
|
|
|
def setIndicatorLed(self, sc, op, rft, cmdTop=False, prop=None):
|
|
return op.iterate_op(op.setIndicatorLed_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
|
|
|
|
def getPower(self,sc,op, rft, cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
resName="Power"
|
|
|
|
# get the Chassis resource first
|
|
rc,r,j,d=op.get(sc, op, rft, cmdTop, resName)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the link to the Power resource under Chassis
|
|
if ((resName in d) and ("@odata.id" in d[resName])):
|
|
resLink=d[resName]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a {} link".format(resName))
|
|
return(6,None,False,None)
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop))
|
|
|
|
return(rc,r,j,d)
|
|
|
|
|
|
|
|
def getThermal(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
resName="Thermal"
|
|
# get the Chassis resource first
|
|
rc,r,j,d=op.get(sc, op, rft, cmdTop, resName)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the link to the Thermal resource under Chassis
|
|
if ((resName in d) and ("@odata.id" in d[resName])):
|
|
resLink=d[resName]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a {} link".format(resName))
|
|
return(6,None,False,None)
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop))
|
|
|
|
return(rc,r,j,d)
|
|
|
|
|
|
|
|
def getPowerReading(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
# check if there is an arg for the operation
|
|
if( sc.argnum > 1 ):
|
|
if( sc.args[1] == 'consumed' ):
|
|
prop="PowerConsumedWatts"
|
|
else:
|
|
rft.printErr("Error: getPowerReading: invalid argument: {}".format(sc.args[1]))
|
|
rft.printErr("Syntax: getPowerReading [-i<indx>] [consumed]")
|
|
return(4,None,False,None)
|
|
|
|
# get the Chassis Power resource
|
|
rc,r,j,d=op.getPower(sc,op, rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the Power Control Object
|
|
if ("PowerControl" in d ):
|
|
powerControl=d["PowerControl"]
|
|
powerControlMembers=len(powerControl)
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a PowerControl resource")
|
|
return(4,None,False,None)
|
|
if(powerControlMembers == 0 ):
|
|
rft.printErr("Error: Chassis PowerControl array is empty")
|
|
return(4,None,False,None)
|
|
|
|
# get the powerControl array index
|
|
if( rft.IdLevel2 is None ):
|
|
indx=0
|
|
else:
|
|
indxPattern="(^0$)|(^([1-9][0-9]{,2})$)" # <decimal>: 33...
|
|
indxMatch=re.search(indxPattern,rft.IdLevel2)
|
|
if( indxMatch ):
|
|
indx=int(rft.IdLevel2) # convert base10 to integer
|
|
rft.printVerbose(4,"getPowerReading: Indx={}".format(indx))
|
|
else:
|
|
rft.printErr("Error: getPowerReading: invalid PowerControl index value: {}".format(rft.IdLevel2))
|
|
rft.printErr("Chassis getPowerReading [current] [-i<indx>] --<indx> is integer 0 - 99 (default is 0)")
|
|
|
|
if( (indx+1) > powerControlMembers ):
|
|
rft.printErr("Error: specified PowerControl index does not exist: indx={}, members={}".format(indx,powerControlMembers))
|
|
return(4,None,False,None)
|
|
|
|
#if here, we have a valid indx
|
|
if(prop is not None):
|
|
if( prop in powerControl[indx] ):
|
|
respDataVal=powerControl[indx][prop]
|
|
respData={prop: respDataVal}
|
|
rft.printVerbose(1," Get Current Power consumption (PowerConsumedWatts) of PowerControl[{}] resource".format(indx,skip1=True, printV12=cmdTop))
|
|
else:
|
|
rft.printErr("Error: Property {} not not returned in PowerControl[{}] resource".format(prop,indx))
|
|
return(4,r,j,d)
|
|
else:
|
|
respData=powerControl[indx] #return the full powerControl array
|
|
rft.printVerbose(1," Chassis PowerControl[{}] array:".format(indx,skip1=True, printV12=cmdTop))
|
|
|
|
return(rc,r,j,respData)
|
|
|
|
|
|
|
|
|
|
#setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] -- set powerLimit control properties")
|
|
def setPowerLimit_single(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
# check if there is a required 2nd arg <limit>
|
|
if( sc.argnum > 1 ):
|
|
limitPattern="(^null$)|(^0$)|(^([1-9][0-9]{,5})$)" # null | 0 | 8 | 33 |344 | 34333
|
|
limitMatch=re.search(limitPattern,sc.args[1]) # sc.args[1]=<limit>
|
|
if( limitMatch ):
|
|
if(sc.args[1] == "null"):
|
|
limit=None
|
|
else:
|
|
limit=int(sc.args[1]) # keep it a string
|
|
rft.printVerbose(4,"setPowerLimit: limit={}".format(limit))
|
|
powerLimitData={"LimitInWatts": limit }
|
|
else:
|
|
rft.printErr("Error: setPowerLimit: invalid <limit> value specified: {}".format(sc.args[1]))
|
|
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --limit=null | 0-99999")
|
|
return(8,None,False,None)
|
|
else:
|
|
rft.printErr("Error: setPowerLimit: no <limit> value specified: {}")
|
|
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --limit=null | 0-99999")
|
|
return(8,None,False,None)
|
|
# check if there is an optional 3rd arg <exception>
|
|
includeException=False
|
|
validExceptionVals=("NoAction", "HardPowerOff", "LogEventOnly", "Oem")
|
|
if (sc.argnum > 2 ):
|
|
exceptionVal=sc.args[2] # sc.args[2]=<exceptionEnum>
|
|
if( not exceptionVal in validExceptionVals ):
|
|
rft.printErr("Error: setPowerLimit: invalid <limit> value specified: {}".format(sc.args[2]))
|
|
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --limit=null | 0-99999")
|
|
return(8,None,False,None)
|
|
else:
|
|
rft.printVerbose(4,"setPowerLimit: exception={}".format(exceptionVal))
|
|
powerLimitData["LimitException"]=exceptionVal
|
|
includeException=True
|
|
|
|
# check if there is an optional 4th arg <correctionTime> in milliseconds
|
|
includeCorrectionTime=False
|
|
if (sc.argnum > 3 ):
|
|
correctionPattern="(^([1-9][0-9]{,6})$)" # 1-999999 ms
|
|
correctionMatch=re.search(correctionPattern,sc.args[3]) # sc.args[3]=<correctionInMs>
|
|
if( correctionMatch ):
|
|
correctionTime=int(sc.args[3]) # keep it a string
|
|
rft.printVerbose(4,"setPowerLimit: correctionInMs={}".format(correctionTime))
|
|
powerLimitData["CorrectionInMs"]=correctionTime
|
|
includeCorrectionTime=True
|
|
else:
|
|
rft.printErr("Error: setPowerLimit: invalid <correctionTime> value specified: {}".format(sc.args[3]))
|
|
rft.printErr("Chassis setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] --correctionTime=1-999999 ms")
|
|
return(8,None,False,None)
|
|
|
|
# get the Chassis Power resource
|
|
rc,r,j,d=op.getPower(sc,op, rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the Power Control Object
|
|
if ("PowerControl" in d ):
|
|
powerControl=d["PowerControl"]
|
|
powerControlMembers=len(powerControl)
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a PowerControl resource")
|
|
return(4,None,False,None)
|
|
if(powerControlMembers == 0 ):
|
|
rft.printErr("Error: Chassis PowerControl array is empty")
|
|
return(4,None,False,None)
|
|
|
|
# get the powerControl array index
|
|
if( rft.IdLevel2 is None ):
|
|
indx=0
|
|
else:
|
|
indxPattern="(^0$)|(^([1-9][0-9]*)$)" # <decimal>: 33...
|
|
indxMatch=re.search(indxPattern,rft.IdLevel2)
|
|
if( indxMatch ):
|
|
indx=int(rft.IdLevel2) # convert base10 to integer
|
|
rft.printVerbose(4,"setPowerLimit: Indx={}".format(indx))
|
|
else:
|
|
rft.printErr("Error: setPowerLimit: invalid PowerControl index: {}".format(rft.IdLevel2))
|
|
rft.printErr("Chassis setPowerLimit [current] [-i<indx>] --<indx> is integer 0 - 99 (default is 0)")
|
|
return(8,None,False,None)
|
|
|
|
if( (indx+1) > powerControlMembers ):
|
|
rft.printErr("Error: specified PowerControl index does not exist: indx={}, members={}".format(indx,powerControlMembers))
|
|
return(8,None,False,None)
|
|
|
|
# check that this PowerControl index member has the properties
|
|
if( not "LimitInWatts" in powerControl[indx]["PowerLimit"] ):
|
|
rft.printErr("Error: setPowerLimit: LimitInWatts property is not in rhost PowerControl[{}]".format(indx))
|
|
return(8,None,False,None)
|
|
if( ( includeException is True ) and ( not "LimitException" in powerControl[indx]["PowerLimit"] ) ):
|
|
rft.printErr("Error: setPowerLimit: LimitException property is not in rhost PowerControl[{}]".format(indx))
|
|
return(8,None,False,None)
|
|
if( ( includeCorrectionTime is True ) and ( not "CorrectionInMs" in powerControl[indx]["PowerLimit"] ) ):
|
|
rft.printErr("Error: setPowerLimit: CorrectionInMs property is not in rhost PowerControl[{}]".format(indx))
|
|
return(8,None,False,None)
|
|
|
|
# now build the final full patch data
|
|
#patchData={"PowerControl"[indx]: { "PowerLimit": powerLimitData } }
|
|
powerLimitRes={ "PowerLimit": powerLimitData }
|
|
patchData={ "PowerControl": [ {} ] }
|
|
empty={}
|
|
powerControlArray=list()
|
|
for i in range (0,powerControlMembers):
|
|
powerControlArray.append(empty)
|
|
powerControlArray[indx]=powerLimitRes
|
|
patchData["PowerControl"]=powerControlArray
|
|
|
|
rft.printVerbose(4,"setPowerLimit: patchData: {}".format(json.dumps(patchData)))
|
|
|
|
#call the generic patch command to send the patch. This takes care of etag support
|
|
rc,r,j,d=rft.patchResource(rft, r, patchData)
|
|
|
|
if(rc==0):
|
|
rft.printVerbose(1," SetPowerLimit [{}]:",indx, skip1=True, printV12=cmdTop)
|
|
powerLimit={"PowerLimit": d["PowerControl"][indx]["PowerLimit"]}
|
|
return(rc,r,j,powerLimit)
|
|
|
|
else: return(rc,r,False,None)
|
|
|
|
|
|
def setPowerLimit(self, sc, op, rft, cmdTop=False, prop=None):
|
|
return op.iterate_op(op.setPowerLimit_single, sc, op, rft, cmdTop=cmdTop, prop=prop)
|
|
|
|
|
|
def getLogService(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,":{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
# get the Chassis resource
|
|
rc,r,j,d=op.get(sc,op, rft)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
collName="LogServices"
|
|
# get the link to the LogServices collection
|
|
if ((collName in d) and ("@odata.id" in d[collName])):
|
|
logLink=d[collName]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a {} link".format(collName))
|
|
return(6,None,False,None)
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
# check if there is a list arg for the operation
|
|
if( sc.argnum > 1 and sc.args[1] == 'list' ):
|
|
#get the collection
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink)
|
|
#loop through the members and create the list sub-operation response
|
|
rc,r,j,d=rft.listCollection(rft, r, d, prop="Name")
|
|
if(rc==0):
|
|
rft.printVerbose(1," list {} Collection member info: Id, URI, Name".format(collName,skip1=True, printV12=cmdTop))
|
|
|
|
# else: check if no Log was specified. If not, return the collection
|
|
elif(rft.IdLevel2OptnCount==0):
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Collection ".format(collName,skip1=True, printV12=cmdTop))
|
|
|
|
# else: check if the -a (all) option is set. If not, return the proc specific by -i or -m
|
|
# search collection to find path using getPath2
|
|
elif( rft.allOptn is not True ):
|
|
# get the LogServices collection
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=logLink, prop=prop)
|
|
collUrl=r.url
|
|
|
|
# now search for 2nd level resource and return
|
|
path2,rc,r,j,d=rft.getLevel2ResourceById(rft,r,d)
|
|
if(rc!=0):
|
|
return(rc,r,j,d)
|
|
# so rc=0
|
|
#if sysPath returned a response but we need to extract the property do it here
|
|
if( (r is not None) and (prop is not None) ):
|
|
rc,r,j,d=rft.getPropFromDict(rft,r,d,prop)
|
|
|
|
# otherwise, we need to do a GET to get the LogServices, if -P show property, else show full response
|
|
elif( r is None ):
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', collUrl, relPath=path2, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Collection Member ".format(collName,skip1=True, printV12=cmdTop))
|
|
|
|
# If '--Entries' specified, get "Entries" nav link and read it
|
|
if rc == 0 and rft.gotEntriesOptn:
|
|
if r is not None and j and isinstance(d, dict):
|
|
rft.printVerbose(1, 'getLogService: attempting to get Entries for Logs')
|
|
entries = d.get('Entries')
|
|
if entries is not None and isinstance(entries, dict):
|
|
entries_uri = entries.get('@odata.id')
|
|
if entries_uri is not None:
|
|
rc, r, j, d = rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url,
|
|
relPath=entries_uri, prop=prop)
|
|
else:
|
|
rft.printErr('getLogService: @odata.id not found in "Entries" property')
|
|
else:
|
|
rft.printErr('getLogService: "Entries" property not found in JSON payload')
|
|
else:
|
|
rft.printErr(
|
|
'Unable to fetch Entries property from previous response: response = {}, is_json = {}, type(json) = {}'
|
|
.format(r, j, type(d)))
|
|
|
|
# else, client specified the -a option requesting ALL of the LogServices members
|
|
# for logs, we will not support this. Its too much data.
|
|
else:
|
|
rft.printErr("Error: -a option not supported for LogServices")
|
|
return(6,None,False,None)
|
|
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def clearLog(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
return(8,None,False,None)
|
|
|
|
|
|
def getSensors(self,sc,op, rft, cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
|
|
sys.stdout.write("%-50s %-10s %-10s\n" % ("Sensor", "Reading", "UpperCritical"))
|
|
resName="Thermal"
|
|
# get the Chassis resource first
|
|
rc,r,j,d=op.get(sc, op, rft, cmdTop, resName)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the link to the Thermal resource under Chassis
|
|
if ((resName in d) and ("@odata.id" in d[resName])):
|
|
resLink=d[resName]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a {} link".format(resName))
|
|
return(6,None,False,None)
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop))
|
|
|
|
collection = ['Temperatures','Fans']
|
|
for collName in collection:
|
|
numOfLinks=len(d[collName])
|
|
for i in range (0,numOfLinks):
|
|
if collName== 'Fans':
|
|
sys.stdout.write("%-50s %-10s %-10s\n" % (d[collName][i]["MemberId"], d[collName][i].get("Reading","N/A"),"N/A"))
|
|
else:
|
|
sys.stdout.write("%-50s %-10s %-10s\n" % (d[collName][i]["MemberId"], d[collName][i].get("ReadingCelsius","N/A"), d[collName][i].get("UpperThresholdCritical","N/A)")))
|
|
|
|
|
|
resName="Sensors"
|
|
# get the Chassis resource first
|
|
rc,r,j,d=op.get(sc, op, rft, cmdTop, resName)
|
|
if( rc != 0): return(rc,r,False,None)
|
|
|
|
# get the link to the Thermal resource under Chassis
|
|
if ((resName in d) and ("@odata.id" in d[resName])):
|
|
resLink=d[resName]["@odata.id"]
|
|
else:
|
|
rft.printErr("Error: Chassis resource does not have a {} link".format(resName))
|
|
return(6,None,False,None)
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
|
|
rc,r,j,d=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=resLink, prop=prop)
|
|
if(rc==0):
|
|
rft.printVerbose(1," {} Resource ".format(resName,skip1=True, printV12=cmdTop))
|
|
|
|
numOfLinks=len(d["Members"])
|
|
for i in range (0,numOfLinks):
|
|
collName="Members"
|
|
rc1,r1,j1,d1=rft.rftSendRecvRequest(rft.AUTHENTICATED_API, 'GET', r.url, relPath=d[collName][i]["@odata.id"], prop=prop)
|
|
output=json.dumps(d1,indent=4)
|
|
sys.stdout.write("%-50s %-10s %-10s\n" % (d1["Id"], d1.get("Reading", "N/A"), d1.get("Thresholds", {}).get("UpperCritical",{}).get("Reading","N/A")))
|
|
|
|
|
|
return(rc,r,False,None)
|
|
|
|
|
|
def examples(self,sc,op,rft,cmdTop=False,prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
print(" {} -r<ip> Chassis # shows the Chassis collection".format(rft.program))
|
|
print(" {} -r<ip> Chassis list # lists Id, Uri, AssetTag for all Chassis".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I <id> # gets the Chassis with Id=<d>".format(rft.program))
|
|
print(" {} -r<ip> Chassis -M AssetTag:12345 # gets the Chassis with AssetTag=12345".format(rft.program))
|
|
print(" {} -r<ip> Chassis -L <sysUrl> # gets the Chassis at URI=<systemUrl".format(rft.program))
|
|
print(" {} -r<ip> Chassis -F # get the First Chassis returned (for debug)".format(rft.program))
|
|
print(" {} -r<ip> Chassis -1 # get the first Chassis and verify that there is only one system".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I <id> patch {{A: B,C: D,...}} # patch the json-formatted {{prop: value...}} data to the object".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I <id> setAssetTag <assetTag> # set the system's asset tag ".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I <id> setIndicatorLed <state> # set the indicator LED. <state>=redfish defined values: Off, Lit, Blinking".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I<Id> Power # get the full chassis Power resource".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I<Id> Thermal # get the full chassis Thermal resource".format(rft.program))
|
|
print(" {} -r<ip> Chassis -I<Id> getPowerReading[-i<indx> [consumed] # get chassis/Power powerControl[<indx>] resource".format(rft.program))
|
|
print(" # if optional \"consumed\" arg, then return only the PowerConsumedWatts prop")
|
|
print(" {} -r<ip> Chassis -L<Url> setPowerLimit [-i<indx>] <limit> [<exception> [<correctionTime>]] # set power limit".format(rft.program))
|
|
return(0,None,False,None)
|
|
|
|
|
|
|
|
|
|
'''
|
|
TODO:
|
|
1. clearlog not implemented
|
|
|
|
'''
|
|
|
|
|
|
|
|
|