mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	login for redfish support (#5908)
* login for redfish support * modified to move auth class
This commit is contained in:
		| @@ -9,6 +9,7 @@ from gevent.subprocess import Popen, PIPE | ||||
| import requests | ||||
| import urllib3 | ||||
| urllib3.disable_warnings() | ||||
| from requests.auth import AuthBase | ||||
|  | ||||
| import exceptions as xcat_exception | ||||
|  | ||||
| @@ -17,6 +18,7 @@ class RestSession(object): | ||||
|     def __init__(self): | ||||
|         self.session = requests.Session() | ||||
|         self.cookies = None | ||||
|         self.auth = None | ||||
|  | ||||
|     def request(self, method, url, headers, data=None, timeout=30): | ||||
|  | ||||
| @@ -24,6 +26,7 @@ class RestSession(object): | ||||
|             response = self.session.request(method, url, | ||||
|                                             data=data, | ||||
|                                             headers=headers, | ||||
|                                             auth=self.auth, | ||||
|                                             verify=False, | ||||
|                                             timeout=timeout) | ||||
|         except requests.exceptions.ConnectionError as e: | ||||
| @@ -60,6 +63,9 @@ class RestSession(object): | ||||
|         if not self.cookies: | ||||
|             self.cookies = requests.utils.dict_from_cookiejar(self.session.cookies) | ||||
|  | ||||
|         if not self.auth and 'X-Auth-Token' in response.headers: | ||||
|             self.auth = XTokenAuth(response.headers['X-Auth-Token']) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def extract_server_and_port(self, message_string, format="STRING"): | ||||
| @@ -127,3 +133,13 @@ class RestSession(object): | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|         return response | ||||
|  | ||||
| class XTokenAuth(AuthBase): | ||||
|  | ||||
|     def __init__(self,authToken): | ||||
|  | ||||
|         self.authToken=authToken | ||||
|  | ||||
|     def __call__(self, auth): | ||||
|         auth.headers['X-Auth-Token']=self.authToken | ||||
|         return(auth) | ||||
|   | ||||
| @@ -0,0 +1,34 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| from __future__ import print_function | ||||
| import gevent | ||||
| import time | ||||
|  | ||||
| from common.task import ParallelNodesCommand | ||||
| from common.exceptions import SelfClientException, SelfServerException | ||||
| from hwctl import redfish_client as redfish | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
| class RedfishPowerTask(ParallelNodesCommand): | ||||
|     """Executor for power-related actions.""" | ||||
|  | ||||
|     def get_state(self, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         rf = redfish.RedfishRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                  debugmode=self.debugmode, verbose=self.verbose) | ||||
|  | ||||
|         state = 'Unknown' | ||||
|         try: | ||||
|             rf.login() | ||||
|             self.callback.info('%s: %s' % (node, state)) | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             self.callback.error(e.message, node) | ||||
|  | ||||
|         return state | ||||
							
								
								
									
										129
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/redfish_client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/redfish_client.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| import os | ||||
| import requests | ||||
| import json | ||||
| import time | ||||
|  | ||||
| from common import utils, rest | ||||
| from common.exceptions import SelfClientException, SelfServerException | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
| HTTP_PROTOCOL = "https://" | ||||
| PROJECT_URL = "/redfish/v1" | ||||
|  | ||||
| SESSION_URL = PROJECT_URL + "/SessionService/Sessions" | ||||
|  | ||||
| class RedfishRest(object): | ||||
|  | ||||
|     headers = {'Content-Type': 'application/json'} | ||||
|  | ||||
|     def __init__(self, name, **kwargs): | ||||
|  | ||||
|         self.name = name | ||||
|         self.username = None | ||||
|         self.password = None | ||||
|  | ||||
|         if 'nodeinfo' in kwargs: | ||||
|             for key, value in kwargs['nodeinfo'].items(): | ||||
|                 setattr(self, key, value) | ||||
|         if not hasattr(self, 'bmcip'): | ||||
|             self.bmcip = self.name | ||||
|  | ||||
|         self.verbose = kwargs.get('debugmode') | ||||
|         self.messager = kwargs.get('messager') | ||||
|  | ||||
|         self.session = rest.RestSession() | ||||
|         self.root_url = HTTP_PROTOCOL + self.bmcip | ||||
|  | ||||
|     def _print_record_log (self, msg, cmd, error_flag=False): | ||||
|  | ||||
|         if self.verbose or error_flag: | ||||
|             localtime = time.asctime( time.localtime(time.time()) ) | ||||
|             log = self.name + ': [redfish_debug] ' + cmd + ' ' + msg | ||||
|             if self.verbose: | ||||
|                 self.messager.info(localtime + ' ' + log) | ||||
|             logger.debug(log) | ||||
|  | ||||
|     def _print_error_log (self, msg, cmd): | ||||
|  | ||||
|         self._print_record_log(msg, cmd, True) | ||||
|  | ||||
|     def _log_request (self, method, url, headers, data=None, files=None, file_path=None, cmd=''): | ||||
|  | ||||
|         header_str = ' '.join([ "%s: %s" % (k, v) for k,v in headers.items() ]) | ||||
|         msg = 'curl -k -X %s -H \"%s\" ' % (method, header_str) | ||||
|  | ||||
|         if cmd != 'login': | ||||
|             msg += '-H \"X-Auth-Token: xxxxxx\" ' | ||||
|  | ||||
|         if data: | ||||
|             if cmd == 'login': | ||||
|                 data = data.replace('"Password": "%s"' % self.password, '"Password": "xxxxxx"') | ||||
|                 data = '-d \'%s\'' % data   | ||||
|             msg += '%s %s -v' % (url, data) | ||||
|         else: | ||||
|             msg += url | ||||
|  | ||||
|         self._print_record_log(msg, cmd) | ||||
|         return msg | ||||
|  | ||||
|     def request (self, method, resource, headers=None, payload=None, timeout=30, cmd=''): | ||||
|  | ||||
|         httpheaders = headers or RedfishRest.headers | ||||
|         url = resource | ||||
|         if not url.startswith(HTTP_PROTOCOL): | ||||
|             url = self.root_url + resource | ||||
|  | ||||
|         data = None | ||||
|         if payload: | ||||
|             data=json.dumps(payload) | ||||
|  | ||||
|         self._log_request(method, url, httpheaders, data=data, cmd=cmd) | ||||
|  | ||||
|         try: | ||||
|             response = self.session.request(method, url, headers=httpheaders, data=data, timeout=timeout) | ||||
|             return self.handle_response(response, cmd=cmd) | ||||
|         except SelfServerException as e: | ||||
|             if cmd == 'login': | ||||
|                 e.message = "Login to BMC failed: Can't connect to {0} {1}.".format(e.host_and_port, e.detail_msg) | ||||
|             else: | ||||
|                 e.message = 'BMC did not respond. ' \ | ||||
|                             'Validate BMC configuration and retry the command.' | ||||
|             self._print_error_log(e.message, cmd) | ||||
|             raise | ||||
|         except ValueError: | ||||
|             error = 'Received wrong format response: %s' % response | ||||
|             self._print_error_log(error, cmd) | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def handle_response (self, resp, cmd=''): | ||||
|  | ||||
|         data = resp.json() | ||||
|         code = resp.status_code | ||||
|  | ||||
|         if code != requests.codes.ok and code != requests.codes.created: | ||||
|  | ||||
|             description = ''.join(data['error']['@Message.ExtendedInfo'][0]['Message']) | ||||
|             error = '[%d] %s' % (code, description) | ||||
|             self._print_error_log(error, cmd) | ||||
|             raise SelfClientException(error, code) | ||||
|  | ||||
|         if cmd == 'login' and not 'X-Auth-Token' in resp.headers: | ||||
|             raise SelfServerException('Login Failed: Did not get Session Token from response') | ||||
|  | ||||
|         self._print_record_log('%s %s' % (code, data['Name']), cmd) | ||||
|         return data | ||||
|  | ||||
|     def login(self): | ||||
|  | ||||
|         payload = { "UserName": self.username, "Password": self.password } | ||||
|         self.request('POST', SESSION_URL, payload=payload, timeout=20, cmd='login')  | ||||
|  | ||||
| @@ -2,7 +2,8 @@ from common import utils | ||||
| import gevent | ||||
| from gevent.pool import Pool | ||||
|  | ||||
| MODULE_MAP = {"openbmc": "OpenBMCManager"} | ||||
| MODULE_MAP = {"openbmc": "OpenBMCManager", | ||||
|              "redfish": "RedfishManager"} | ||||
|  | ||||
| class BaseManager(object): | ||||
|     def __init__(self, messager, cwd): | ||||
|   | ||||
							
								
								
									
										77
									
								
								xCAT-openbmc-py/lib/python/agent/xcatagent/redfish.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								xCAT-openbmc-py/lib/python/agent/xcatagent/redfish.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| import os | ||||
| import gevent | ||||
| import re | ||||
| import sys | ||||
| from docopt import docopt,DocoptExit | ||||
|  | ||||
| from common import utils | ||||
| from common import exceptions as xcat_exception | ||||
| from hwctl.executor.redfish_power import RedfishPowerTask | ||||
| from hwctl.power import DefaultPowerManager | ||||
|  | ||||
| from xcatagent import base | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
| try: | ||||
|     if not logger.handlers: | ||||
|         utils.enableSyslog('xcat.agent') | ||||
| except: | ||||
|     pass | ||||
|  | ||||
| DEBUGMODE = False | ||||
| VERBOSE = False | ||||
|  | ||||
| # global variables of rpower | ||||
| POWER_REBOOT_OPTIONS = ('boot', 'reset') | ||||
| POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff') | ||||
| POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status') | ||||
|  | ||||
| class RedfishManager(base.BaseManager): | ||||
|     def __init__(self, messager, cwd, nodes=None, envs=None): | ||||
|         super(RedfishManager, self).__init__(messager, cwd) | ||||
|         self.nodes = nodes | ||||
|         self.debugmode = (envs and envs.get('debugmode')) or None | ||||
|         #TODO, remove the global variable DEBUGMODE | ||||
|         global DEBUGMODE | ||||
|         DEBUGMODE = envs['debugmode'] | ||||
|  | ||||
|         if self.debugmode: | ||||
|             logger.setLevel(logging.DEBUG) | ||||
|  | ||||
|     def rpower(self, nodesinfo, args): | ||||
|  | ||||
|         # 1, parse args | ||||
|         rpower_usage = """ | ||||
|         Usage: | ||||
|             rpower [-V|--verbose] [boot|bmcreboot|bmcstate|off|on|reset|softoff|stat|state|status] | ||||
|  | ||||
|         Options: | ||||
|             -V --verbose   rpower verbose mode. | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             opts=docopt(rpower_usage, argv=args) | ||||
|  | ||||
|             self.verbose=opts.pop('--verbose') | ||||
|             action=[k for k,v in opts.items() if v][0] | ||||
|         except Exception as e: | ||||
|             # It will not be here as perl has validation for args | ||||
|             self.messager.error("Failed to parse arguments for rpower: %s" % args) | ||||
|             return | ||||
|  | ||||
|         # 2, validate the args | ||||
|         if action not in (POWER_GET_OPTIONS + POWER_SET_OPTIONS + POWER_REBOOT_OPTIONS): | ||||
|             self.messager.error("Not supported subcommand for rpower: %s" % action) | ||||
|             return | ||||
|  | ||||
|         # 3, run the subcommands | ||||
|         runner = RedfishPowerTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose) | ||||
|         DefaultPowerManager().get_power_state(runner) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user