diff --git a/redfish/connection.py b/redfish/connection.py index 4613442..c047daa 100644 --- a/redfish/connection.py +++ b/redfish/connection.py @@ -129,33 +129,42 @@ import sys class RedfishConnection(object): + """Implements basic connection handling for Redfish APIs.""" - def __init__(self, host, user_name, password): + def __init__(self, host, user_name, password, + auth_token=None, enforce_SSL=True): super(RedfishConnection, self).__init__() self.host = host self.user_name = user_name self.password = password - authen = {'Password': self.password, 'UserName': self.user_name} - self.rest_post(self.host, '/rest/v1/sessions', None, json.dumps(authen), - self.user_name, self.password) + self.auth_token = auth_token + self.enforse_SSL = enforse_SSL + # TODO: cache the token returned by this call + auth_dict = {'Password': self.password, 'UserName': self.user_name} + self.rest_post(self.host, '/rest/v1/sessions', None, + json.dumps(auth_dict), self.user_name, self.password) + # TODO: do some schema discovery here and cache the result - # XXX add members, we're going to have to cache + def _op(self, operation, suburi, request_headers=None, request_body=None): + """ + REST operation generic handler - def _op(self, operation, host, suburi, request_headers, request_body, - user_name, password, x_auth_token=None, enforce_SSL=True): - """REST operation generic handler""" + :param operation: GET, POST, etc + :param suburi: the URI path to the resource + :param request_headers: optional dict of headers + :param request_body: optional JSON body + """ - url = urlparse('https://' + host + suburi) + url = urlparse('https://' + self.host + suburi) - if request_headers is None: - request_headers = dict() + if not isinstance(request_headers, dict): request_headers = dict() # if X-Auth-Token specified, supply it instead of basic auth - if x_auth_token is not None: - request_headers['X-Auth-Token'] = x_auth_token + if self.auth_token is not None: + request_headers['X-Auth-Token'] = self.auth_token # else use user_name/password and Basic Auth - elif user_name is not None and password is not None: - request_headers['Authorization'] = "BASIC " + base64.b64encode(user_name + ":" + password) + elif self.user_name is not None and self.password is not None: + request_headers['Authorization'] = "BASIC " + base64.b64encode(self.user_name + ":" + self.password) # TODO: add support for other types of auth # TODO: think about redirects.... @@ -218,54 +227,77 @@ class RedfishConnection(object): return resp.status, headers, response + def rest_get(self, suburi, request_headers): + """REST GET - def rest_get(self, host, suburi, request_headers, user_name, password): - """Generic REST GET handler""" + :param: suburi + :param: request_headers + """ + if not isinstance(request_headers, dict): request_headers = dict() # NOTE: be prepared for various HTTP responses including 500, 404, etc. - # XXX need parameter sanitization; request_headers must be a dict or None - return self._op('GET', host, suburi, request_headers, None, user_name, password) + return self._op('GET', suburi, request_headers, None) + def rest_patch(self, suburi, request_headers, request_body): + """REST PATCH - def rest_patch(self, server, suburi, request_headers, request_body, user_name, password): - """REST PATCH""" + :param: suburi + :param: request_headers + :param: request_body + NOTE: this body is a dict, not a JSONPATCH document. + redfish does not follow IETF JSONPATCH standard + https://tools.ietf.org/html/rfc6902 + """ if not isinstance(request_headers, dict): request_headers = dict() request_headers['Content-Type'] = 'application/json' - return self._op('PATCH', server, suburi, request_headers, request_body, user_name, password) + return self._op('PATCH', suburi, request_headers, request_body) # NOTE: be prepared for various HTTP responses including 500, 404, 202 etc. + def rest_put(self, suburi, request_headers, request_body): + """REST PUT - def rest_put(self, host, suburi, request_headers, request_body, user_name, password): - """REST PUT""" + :param: suburi + :param: request_headers + :param: request_body + """ if not isinstance(request_headers, dict): request_headers = dict() request_headers['Content-Type'] = 'application/json' - return self._op('PUT', host, suburi, request_headers, request_body, user_name, password) + return self._op('PUT', suburi, request_headers, request_body) # NOTE: be prepared for various HTTP responses including 500, 404, 202 etc. - # REST POST - def rest_post(self, host, suburi, request_headers, request_body, user_name, password): + def rest_post(self, suburi, request_headers, request_body): + """REST POST + + :param: suburi + :param: request_headers + :param: request_body + """ if not isinstance(request_headers, dict): request_headers = dict() request_headers['Content-Type'] = 'application/json' - return self._op('POST', host, suburi, request_headers, request_body, user_name, password) + return self._op('POST', suburi, request_headers, request_body) # NOTE: don't assume any newly created resource is included in the response. Only the Location header matters. # the response body may be the new resource, it may be an ExtendedError, or it may be empty. - # REST DELETE - def rest_delete(self, host, suburi, request_headers, user_name, password): - return self._op('DELETE', host, suburi, request_headers, None, user_name, password) + def rest_delete(self, suburi, request_headers): + """REST DELETE + + :param: suburi + :param: request_headers + """ + if not isinstance(request_headers, dict): request_headers = dict() + return self._op('DELETE', suburi, request_headers, None) # NOTE: be prepared for various HTTP responses including 500, 404, etc. # NOTE: response may be an ExtendedError or may be empty - # this is a generator that returns collection members - def collection(self, host, collection_uri, request_headers, user_name, password): + def collection(self, collection_uri, request_headers): """ collections are of two tupes: - array of things that are fully expanded (details) - array of URLs (links) """ # get the collection - status, headers, thecollection = rest_get( - host, collection_uri, request_headers, user_name, password) + status, headers, thecollection = self.rest_get( + collection_uri, request_headers) # TODO: commment this while status < 300: diff --git a/redfish/functions.py b/redfish/functions.py index a7c158b..843d9d8 100644 --- a/redfish/functions.py +++ b/redfish/functions.py @@ -27,15 +27,8 @@ from redfish import connection class RedfishOperation(connection.RedfishConnection): - def __init__(self, host, user_name, password): - super(RedfishOperation, self).__init__(host, - user_name, password) - # XXX add members, we're going to have to cache - - # noinspection PyPep8Naming def reset_server(self): - (status, headers, system) = self.rest_get(self.host, - '/rest/v1/Systems', None, self.user_name, self.password) + (status, headers, system) = self.rest_get('/rest/v1/Systems', None) memberuri = system['links']['Member'][0]['href'] # verify expected type @@ -51,7 +44,6 @@ class RedfishOperation(connection.RedfishConnection): # perform the POST action print('POST ' + json.dumps(action) + ' to ' + memberuri) - (status, headers, response) = self.rest_post(self.host, memberuri, None, - action, self.user_name, self.password) + (status, headers, response) = self.rest_post(memberuri, None, action) print('POST response = ' + str(status)) connection.print_extended_error(response)