diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index 4f197c1c..dacef586 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -161,6 +161,7 @@ class Command(object): self._gpool = pool self._bmcv4ip = None self._bmcv6ip = None + self.xauthtoken = None for addrinf in socket.getaddrinfo(bmc, 0, 0, socket.SOCK_STREAM): if addrinf[0] == socket.AF_INET: self._bmcv4ip = socket.inet_pton(addrinf[0], addrinf[-1][0]) @@ -173,12 +174,14 @@ class Command(object): self.wc.set_header('Accept-Encoding', 'gzip') self.wc.set_header('OData-Version', '4.0') overview = self.wc.grab_json_response('/redfish/v1/') - self.wc.set_basic_credentials(userid, password) self.username = userid self.password = password + self.wc.set_basic_credentials(self.username, self.password) self.wc.set_header('Content-Type', 'application/json') if 'Systems' not in overview: raise exc.PyghmiException('Redfish not ready') + if 'SessionService' in overview: + self._get_session_token(self.wc) systems = overview['Systems']['@odata.id'] res = self.wc.grab_json_response_with_status(systems) if res[1] == 401: @@ -206,6 +209,26 @@ class Command(object): self.powerurl = self.sysinfo.get('Actions', {}).get( '#ComputerSystem.Reset', {}).get('target', None) + def _get_session_token(self, wc): + # specification actually indicates we can skip straight to this url + username = self.username + password = self.password + if not isinstance(username, str): + username = username.decode() + if not isinstance(password, str): + password = password.decode() + rsp = wc.grab_rsp('/redfish/v1/SessionService/Sessions', + {'UserName': username, 'Password': password}) + rsp.read() + self.xauthtoken = rsp.getheader('X-Auth-Token') + if self.xauthtoken: + if 'Authorization' in wc.stdheaders: + del wc.stdheaders['Authorization'] + if 'Authorization' in self.wc.stdheaders: + del self.wc.stdheaders['Authorization'] + wc.stdheaders['X-Auth-Token'] = self.xauthtoken + self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken + @property def _accountserviceurl(self): sroot = self._do_web_request('/redfish/v1/') @@ -520,6 +543,17 @@ class Command(object): finally: if 'If-Match' in wc.stdheaders: del wc.stdheaders['If-Match'] + if res[1] == 401 and self.xauthtoken: + wc.set_basic_credentials(self.username, self.password) + self._get_session_token(wc) + if etag: + wc.stdheaders['If-Match'] = etag + try: + res = wc.grab_json_response_with_status(url, payload, + method=method) + finally: + if 'If-Match' in wc.stdheaders: + del wc.stdheaders['If-Match'] if res[1] < 200 or res[1] >= 300: try: info = json.loads(res[0]) @@ -1430,3 +1464,4 @@ if __name__ == '__main__': print(repr( Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'], verifycallback=lambda x: True).get_power())) + diff --git a/pyghmi/util/webclient.py b/pyghmi/util/webclient.py index f0b97a2e..e9d29946 100644 --- a/pyghmi/util/webclient.py +++ b/pyghmi/util/webclient.py @@ -272,6 +272,22 @@ class SecureHTTPConnection(httplib.HTTPConnection, object): return json.loads(body) if body else {}, rsp.status return body, rsp.status + def grab_rsp(self, url, data=None, referer=None, headers=None, method=None): + webclient = self.dupe() + if isinstance(data, dict): + data = json.dumps(data) + if data: + if not method: + method = 'POST' + webclient.request(method, url, data, referer=referer, + headers=headers) + else: + if not method: + method = 'GET' + webclient.request(method, url, referer=referer, headers=headers) + rsp = webclient.getresponse() + return rsp + def download(self, url, file): """Download a file to filename or file object @@ -390,3 +406,4 @@ class SecureHTTPConnection(httplib.HTTPConnection, object): except httplib.CannotSendRequest: self.broken = True raise +