393 lines
16 KiB
Python
393 lines
16 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: rawMain.py
|
|
#
|
|
# contains raw subCommands and access functions
|
|
#
|
|
# Class RfRawMain
|
|
# - functions init, displayUsage, displayHelp, displayOperations,
|
|
# - runOperation - raw subcommand table, dispatch of operations: get, patch, post..
|
|
# - RfRawMain - called from redfishMain, enforce legal option combinations,
|
|
# and call runOperation to run System operation (sub-sub-command)
|
|
#
|
|
# Class RfRawOperations
|
|
# All of the Systems sub-command operations eg: Systems reset, setIndicatorLed, etc
|
|
# - hello - test cmd
|
|
# - httpGet -- send GET method
|
|
# - httpPatch -- send GET method
|
|
# - httpPost -- send GET method
|
|
# - httpDelete -- send GET method
|
|
# - httpHead -- send GET method
|
|
# - httpPut -- send GET method (not implemented in 0.9)
|
|
# - 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, urlparse, urlunparse
|
|
|
|
class RfRawMain():
|
|
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] raw <method> <path> ".format(rft.program))
|
|
print("")
|
|
print(" {} raw -h # for help".format(rft.program))
|
|
print(" {} raw examples #for example commands".format(rft.program))
|
|
print("")
|
|
print(" <method> is one of: GET, PATCH, POST, DELETE, HEAD, PUT")
|
|
print(" <path> is full URI path to a redfish resource--the full path following <ipaddr:port>, starting with forward slash /")
|
|
print("")
|
|
print(" Common OPTNS:")
|
|
print(" -u <user>, --user=<usernm> -- username used for remote redfish authentication")
|
|
print(" -p <passwd>, --password=<passwd> -- password used for remote redfish authentication")
|
|
print(" -t <token>, --token=<token> - redfish auth session token-for sessions across multiple calls")
|
|
print("")
|
|
print(" -r <rhost>, --rhost=<rhost> -- remote redfish service hostname or IP:port")
|
|
print(" -X <method> --request=<method> -- the http method to use. <method>={GET,PATCH,POST,DELETE,HEAD,PUT}. Default=GET")
|
|
print(" -d <data> --data=<data> -- the http request \"data\" to send on PATCH,POST,or PUT requests")
|
|
print(" -H <hdrs>, --Headers=<hdrs> -- Specify the request header list--overrides defaults. Format \"{ A:B, C:D...}\" ")
|
|
print(" -S <Secure>, --Secure=<Secure> -- When to use https: (Note: doesn't stop rhost from redirect http to https)")
|
|
|
|
|
|
def displayHelp(self,rft):
|
|
self.displayUsage(rft)
|
|
self.displayOperations(rft)
|
|
print("")
|
|
|
|
def displayOperations(self,rft):
|
|
print(" <operations / methods>:")
|
|
print(" GET -- HTTP GET method")
|
|
print(" PATCH -- HTTP PATCH method")
|
|
print(" POST -- HTTP POST method")
|
|
print(" DELETE -- HTTP DELETE method")
|
|
print(" HEAD -- HTTP HEAD method")
|
|
print(" PUT -- HTTP PUT method")
|
|
print(" examples -- example raw commands with syntax")
|
|
print(" hello -- raw hello -- debug command")
|
|
return(0)
|
|
|
|
|
|
def runOperation(self,rft):
|
|
# instantiate SystemsOperations class
|
|
op=RfRawOperations()
|
|
|
|
# dispatch table for each subcommand: "cmdName": cmdClass.cmdFunction"
|
|
operationTable = {
|
|
"GET": op.httpGet,
|
|
"PATCH": op.httpPatch,
|
|
"POST": op.httpPost,
|
|
"DELETE": op.httpDelete,
|
|
"HEAD": op.httpHead,
|
|
"PUT": op.httpPut,
|
|
"hello": op.hello,
|
|
"examples": op.examples
|
|
}
|
|
|
|
rft.printVerbose(5,"raw: runOperation: operation: {}".format(self.operation))
|
|
rft.printVerbose(5,"raw:runOperation: args: {}".format(self.args))
|
|
|
|
if self.operation in operationTable:
|
|
rft.printVerbose(5,"raw: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("raw: Invalid operation: {}".format(self.operation))
|
|
return(2,None,False,None)
|
|
|
|
|
|
|
|
def RawMain(self,rft,cmdTop=False):
|
|
rft.printVerbose(4,"RawMain: subcommand: {}".format(rft.subcommand))
|
|
|
|
if( rft.help ):
|
|
self.displayHelp(rft)
|
|
return(2,None,False,None)
|
|
|
|
args=rft.subcommandArgv[0:]
|
|
|
|
# if 2 args, only operations are: hello and examples
|
|
nonMethodOperations=("hello","examples")
|
|
if( len(args)==2):
|
|
nonMethodOp=args[1]
|
|
if( not nonMethodOp in nonMethodOperations):
|
|
rft.printErr("Syntax error: \"raw\" subcommands require 2 arguments\n")
|
|
rft.printErr("")
|
|
self.displayHelp(rft)
|
|
return(2,None,False,None)
|
|
# else all operations require two args <method> <path>
|
|
elif( len(args) < 3 ):
|
|
rft.printErr("Syntax error: \"raw\" subcommands require 2 arguments\n")
|
|
self.displayHelp(rft)
|
|
return(2,None,False,None)
|
|
|
|
self.operation=args[1] # for raw subcommand, this is the http method (eg GET)
|
|
self.args = args[1:] # now args points to the 1st argument
|
|
self.argnum =len(self.args)
|
|
|
|
rft.printVerbose(5,"raw: operation={}, args={}".format(self.operation,self.args))
|
|
|
|
|
|
# now execute the operation.
|
|
rc,r,j,d = self.runOperation(rft)
|
|
|
|
if(rc !=0 ):
|
|
rft.printVerbose(5,"raw: 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,"raw: operation exited OK")
|
|
return(rc,r,j,d)
|
|
|
|
|
|
#
|
|
# contains operations related to the Systems subCommand
|
|
#
|
|
class RfRawOperations():
|
|
def __init__(self):
|
|
self.systemsPath=None
|
|
self.systemsCollectionDict=None
|
|
|
|
# function to return API type: authenticated or unauthenticated
|
|
# authenticated APIs require authentication
|
|
# unauthenticated APIs are /redfish, /redfish/v1, /redfish/v1/odata, /redfish/v1/$metadata
|
|
def getApiType(self,rft,uri):
|
|
unauthenticatedAPIs=("/redfish", "/redfish/v1", "/redfish/v1/", "/redfish/v1/odata", "/redfish/v1/$metadata")
|
|
if not uri in unauthenticatedAPIs:
|
|
return( rft.AUTHENTICATED_API )
|
|
else:
|
|
return( rft.UNAUTHENTICATED_API )
|
|
|
|
|
|
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 raw subcommand")
|
|
return(0,None,False,None)
|
|
|
|
|
|
def httpGet(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="GET"
|
|
rft.printVerbose(4,"raw: GET: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
if cmdTop is True: prop=rft.prop
|
|
jsonData=True
|
|
if (path=="/redfish/v1/$metadata"): jsonData=False
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, prop=prop,jsonData=jsonData)
|
|
if(rc==0):
|
|
rft.printVerbose(1," raw GET:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
else:
|
|
rft.printErr("raw: Error getting response")
|
|
return(rc,r,False,None)
|
|
|
|
def httpHead(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="HEAD"
|
|
rft.printVerbose(4,"raw: HEAD: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path)
|
|
if(rc==0):
|
|
rft.printVerbose(1," raw HEAD:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,False,None)
|
|
else:
|
|
rft.printErr("raw: Error getting response")
|
|
return(rc,r,False,None)
|
|
|
|
|
|
|
|
|
|
def httpPatch(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# load patch data--verify its good json
|
|
# get the patchData from rft.requestData readin on commandline via: -d <patchData>
|
|
try:
|
|
patchData=json.loads(rft.requestData)
|
|
except ValueError:
|
|
rft.printErr("Patch: invalid Json input data:{}".format(rft.requestData))
|
|
return(5,None,False,None)
|
|
##print("patchData: {}".format(patchData))
|
|
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="PATCH"
|
|
rft.printVerbose(4,"raw: PATCH: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
#read the resource--the generic patch command needs it to get the etag
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, "GET", rootUrl, relPath=path)
|
|
if(rc!=0):
|
|
rft.printErr("raw: Error getting resource prior to patching it, aborting")
|
|
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," Systems Patch:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def httpPost(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# load post data--verify its good json
|
|
# get the postData from rft.requestData readin on commandline via: -d <patchData>
|
|
try:
|
|
postData=json.loads(rft.requestData)
|
|
except ValueError:
|
|
rft.printErr("Post: invalid Json input data:{}".format(rft.requestData))
|
|
return(5,None,False,None)
|
|
##print("postData: {}".format(postData))
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="POST"
|
|
rft.printVerbose(4,"raw: POST: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
#output the post data in json to send over the network
|
|
reqPostData=json.dumps(postData)
|
|
#Post the data
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, reqData=reqPostData)
|
|
if(rc!=0):
|
|
rft.printErr("raw: Error sending POST to resource, aborting")
|
|
return(rc,r,False,None)
|
|
|
|
|
|
if(rc==0): rft.printVerbose(1," raw POST:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def httpPut(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# load put data--verify its good json
|
|
# get the postData from rft.requestData readin on commandline via: -d <patchData>
|
|
try:
|
|
putData=json.loads(rft.requestData)
|
|
except ValueError:
|
|
rft.printErr("Put: invalid Json input data:{}".format(rft.requestData))
|
|
return(5,None,False,None)
|
|
##print("postData: {}".format(postData))
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="PUT"
|
|
rft.printVerbose(4,"raw: POST: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
#output the post data in json to send over the network
|
|
reqPutData=json.dumps(putData)
|
|
#Put the data
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path, reqData=reqPutData)
|
|
if(rc!=0):
|
|
rft.printErr("raw: Error sending PUT to resource, aborting")
|
|
return(rc,r,False,None)
|
|
|
|
|
|
if(rc==0): rft.printVerbose(1," raw PUT:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def httpDelete(self,sc,op,rft,cmdTop=False, prop=None):
|
|
rft.printVerbose(4,"{}:{}: in raw".format(rft.subcommand,sc.operation))
|
|
|
|
# we verified that we had two args in RawMain(), so we can just read the <uri> arg here
|
|
path=sc.args[1]
|
|
method="DELETE"
|
|
rft.printVerbose(4,"raw: DELETE: method:{} path:{}".format(method,path))
|
|
|
|
apiType=self.getApiType(rft,path) # UNAUTHENTICATED API or Authenticated API
|
|
|
|
# calculate rootUrl--with correct scheme, root path, and rhost IP (w/o querying rhost)
|
|
scheme=rft.getApiScheme(apiType)
|
|
scheme_tuple=[scheme, rft.rhost, "/redfish", "","",""]
|
|
rootUrl=urlunparse(scheme_tuple) # so rootUrl="http[s]://<rhost>[:<port>]/redfish"
|
|
|
|
#send DELETE to the resource path specified
|
|
rc,r,j,d=rft.rftSendRecvRequest(apiType, method, rootUrl, relPath=path)
|
|
if(rc!=0):
|
|
rft.printErr("raw: Error sending DELETE to resource, aborting")
|
|
return(rc,r,False,None)
|
|
|
|
|
|
if(rc==0): rft.printVerbose(1," raw DELETE:",skip1=True, printV12=cmdTop)
|
|
return(rc,r,j,d)
|
|
|
|
|
|
def examples(self,sc,op,rft,cmdTop=False,prop=None):
|
|
rft.printVerbose(4,"{}:{}: in operation".format(rft.subcommand,sc.operation))
|
|
print(" {} -r<ip> raw GET /redfish/v1/ # returns the root collection".format(rft.program))
|
|
|
|
return(0,None,False,None)
|
|
|
|
|
|
|
|
|
|
'''
|
|
TODO:
|
|
1. need to handle case where no patch or post data (no -d)
|
|
2. add raw PUT
|
|
CHANGES:
|
|
0.9.2: no longer call /redfish and /redfish/v1 before executing the raw api
|
|
'''
|
|
|
|
|
|
|
|
|