mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 11:22:27 +00:00 
			
		
		
		
	openbmc python refactor
This commit is contained in:
		| @@ -8,7 +8,7 @@ import json | ||||
| import os | ||||
| import socket | ||||
| import sys | ||||
| from xcatagent import utils | ||||
| from common import utils | ||||
|  | ||||
| class ClientShell(object): | ||||
|     def get_base_parser(self): | ||||
|   | ||||
							
								
								
									
										0
									
								
								xCAT-openbmc-py/lib/python/agent/common/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								xCAT-openbmc-py/lib/python/agent/common/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										58
									
								
								xCAT-openbmc-py/lib/python/agent/common/rest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								xCAT-openbmc-py/lib/python/agent/common/rest.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| from gevent.subprocess import Popen, PIPE | ||||
| import requests | ||||
| import urllib3 | ||||
| urllib3.disable_warnings() | ||||
|  | ||||
| import exceptions as xcat_exception | ||||
|  | ||||
| class RestSession(object): | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.session = requests.Session() | ||||
|         self.cookies = None | ||||
|  | ||||
|     def request(self, method, url, headers, data=None, timeout=30): | ||||
|  | ||||
|         try: | ||||
|             response = self.session.request(method, url, | ||||
|                                             data=data, | ||||
|                                             headers=headers, | ||||
|                                             verify=False, | ||||
|                                             timeout=timeout) | ||||
|         except requests.exceptions.ConnectionError: | ||||
|             raise xcat_exception.SelfServerException('Error: Failed to connect to server.') | ||||
|  | ||||
|         except requests.exceptions.Timeout: | ||||
|             raise xcat_exception.SelfServerException('Error: Timeout to connect to server') | ||||
|  | ||||
|         if not self.cookies: | ||||
|             self.cookies = requests.utils.dict_from_cookiejar(self.session.cookies) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def request_upload(self, method, url, headers, files, using_curl=True): | ||||
|         if using_curl: | ||||
|             return self._upload_by_curl(method, url, headers, files) | ||||
|  | ||||
|     def _upload_by_curl(self, method, url, headers, files): | ||||
|  | ||||
|         header_str = ' '.join([ "%s: %s" % (k, v) for k,v in headers.items() ]) | ||||
|         request_cmd = 'curl -k -b sid=%s -H "%s" -X %s -T %s %s -s' % \ | ||||
|                       (self.cookies['sid'], header_str, method, files, url) | ||||
|  | ||||
|         sub = Popen(request_cmd, stdout=PIPE, shell=True) | ||||
|         response, err = sub.communicate() | ||||
|  | ||||
|         if not response: | ||||
|             error = 'Error: Did not receive response from server after ' \ | ||||
|                     'running command \'%s\'' % request_cmd | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|         return response | ||||
							
								
								
									
										96
									
								
								xCAT-openbmc-py/lib/python/agent/common/task.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								xCAT-openbmc-py/lib/python/agent/common/task.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| #!/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 sys | ||||
| import traceback | ||||
|  | ||||
| class BaseCommand(object): | ||||
|  | ||||
|     def _validate(self, op, *args, **kw): | ||||
|         if hasattr(self, 'validate_%s' % op): | ||||
|             return getattr(self, 'validate_%s' % op)(self, *args, **kw) | ||||
|  | ||||
|     def _pre(self, op, *args, **kw): | ||||
|         if hasattr(self, 'pre_%s' % op): | ||||
|             return getattr(self, 'pre_%s' % op)(self, *args, **kw) | ||||
|  | ||||
|     def _execute(self, op, *args, **kw): | ||||
|         if hasattr(self, '%s' % op): | ||||
|             return getattr(self, '%s' % op)(self, *args, **kw) | ||||
|  | ||||
|     def _post(self, op, *args, **kw): | ||||
|         if hasattr(self, 'post_%s' % op): | ||||
|             return getattr(self, 'post_%s' % op)(self, *args, **kw) | ||||
|  | ||||
|     def run(self, op, *args, **kwargs): | ||||
|         #print 'op=%s, args=%s, kwargs=%s' % (op, args, kwargs) | ||||
|         try: | ||||
|             self._validate(op, *args, **kwargs) | ||||
|             self._pre(op, *args, **kwargs) | ||||
|             self._execute(op, *args, **kwargs) | ||||
|             self._post(op, *args, **kwargs) | ||||
|         except Exception, e: | ||||
|             # TODO: put e into log | ||||
|             print(traceback.format_exc(), file=sys.stderr) | ||||
|             return None | ||||
|  | ||||
|         return self.result() | ||||
|  | ||||
|     def result(self): | ||||
|         """Assume the result will be set by *_<op>""" | ||||
|         return True | ||||
|  | ||||
| class ParallelNodesCommand(BaseCommand): | ||||
|  | ||||
|     def __init__(self, inventory, callback=None, **kwargs): | ||||
|         """ | ||||
|         inventory: {'node1': {k1:v1, k2:v2, ...}, 'node2': ...} | ||||
|         """ | ||||
|         self.inventory = inventory | ||||
|         self.callback = callback | ||||
|         self.debugmode = kwargs.get('debugmode') | ||||
|         self.verbose = kwargs.get('verbose') | ||||
|  | ||||
|     def _execute_in_parallel(self, op, *args, **kw): | ||||
|         if not hasattr(self, '%s' % op): | ||||
|             return | ||||
|  | ||||
|         assert self.inventory and type(self.inventory) is dict | ||||
|         func = getattr(self, '%s' % op) | ||||
|         if len(self.inventory) == 1: | ||||
|             node = self.inventory.keys()[0] | ||||
|             func(*args, node=node, nodeinfo=self.inventory[node], **kw) | ||||
|             return | ||||
|  | ||||
|         pool_size = 1000 # Get it from kw later | ||||
|         gevent_pool = gevent.pool.Pool(pool_size) | ||||
|  | ||||
|         for node in self.inventory.keys(): | ||||
|             try: | ||||
|                 gevent_pool.add( gevent.spawn(func, *args, node=node, nodeinfo=self.inventory[node], **kw)) | ||||
|             except Exception, e: | ||||
|                 error = '%s: Internel Error occured in gevent' % node | ||||
|                 #print(traceback.format_exc(), file=sys.stderr) | ||||
|                 self.callback.error(error) | ||||
|  | ||||
|         gevent_pool.join() | ||||
|  | ||||
|     def run(self, op, *args, **kwargs): | ||||
|         #print 'op=%s, args=%s, kwargs=%s' % (op, args, kwargs) | ||||
|         try: | ||||
|             self._validate(op, *args, **kwargs) | ||||
|             self._pre(op, *args, **kwargs) | ||||
|             self._execute_in_parallel(op, *args, **kwargs) | ||||
|             self._post(op, *args, **kwargs) | ||||
|         except Exception, e: | ||||
|             # TODO: put e into log | ||||
|             print(traceback.format_exc(), file=sys.stderr) | ||||
|             return None | ||||
|  | ||||
|         return self.result() | ||||
| @@ -1,6 +1,7 @@ | ||||
| import struct | ||||
| import sys | ||||
| import inspect | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| def int2bytes(num): | ||||
| @@ -47,3 +48,21 @@ def update2Ddict(updata_dict, key_a, key_b, value): | ||||
|     else:  | ||||
|         updata_dict.update({key_a: {key_b: value}}) | ||||
| 
 | ||||
| class Messager(object): | ||||
|     def __init__(self, name=None): | ||||
|         self.logger = logging.getLogger(name or 'xcatagent') | ||||
| 
 | ||||
|     def info(self, msg): | ||||
|         self.logger.info(msg) | ||||
| 
 | ||||
|     def warn(self, msg): | ||||
|         self.logger.warn(msg) | ||||
| 
 | ||||
|     def error(self, msg): | ||||
|         self.logger.error(msg) | ||||
| 
 | ||||
|     def syslog(self, msg): | ||||
|         pass | ||||
| 
 | ||||
|     def update_node_attributes(self, attribute, node, data): | ||||
|         pass | ||||
							
								
								
									
										0
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										227
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/openbmc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/openbmc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,227 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| import requests | ||||
| import json | ||||
| import time | ||||
|  | ||||
| from common import rest | ||||
| from common.exceptions import SelfClientException, SelfServerException | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
| HTTP_PROTOCOL = "https://" | ||||
| PROJECT_URL = "/xyz/openbmc_project" | ||||
| PROJECT_PAYLOAD = "xyz.openbmc_project." | ||||
|  | ||||
| RPOWER_STATES = { | ||||
|     "on"        : "on", | ||||
|     "off"       : "off", | ||||
|     "Off"       : "off", | ||||
|     "softoff"   : "softoff", | ||||
|     "boot"      : "reset", | ||||
|     "reset"     : "reset", | ||||
|     "bmcreboot" : "BMC reboot", | ||||
|     "Ready"     : "BMC Ready", | ||||
|     "NotReady"  : "BMC NotReady", | ||||
|     "chassison" : "on (Chassis)", | ||||
|     "Running"   : "on", | ||||
|     "Quiesced"  : "quiesced", | ||||
| } | ||||
|  | ||||
| RPOWER_URLS = { | ||||
|     "on"        : { | ||||
|         "path"  : "/state/host0/attr/RequestedHostTransition", | ||||
|         "field" : "State.Host.Transition.On", | ||||
|     }, | ||||
|     "off"       : { | ||||
|         "path"  : "/state/chassis0/attr/RequestedPowerTransition", | ||||
|         "field" : "State.Chassis.Transition.Off", | ||||
|     }, | ||||
|     "softoff"   : { | ||||
|         "path"  : "/state/host0/attr/RequestedHostTransition", | ||||
|         "field" : "State.Host.Transition.Off", | ||||
|     }, | ||||
|     "state"     : { | ||||
|         "path"  : "/state/enumerate", | ||||
|     }, | ||||
| } | ||||
|  | ||||
| BMC_URLS = { | ||||
|     "reboot" : { | ||||
|         "path"  : "/state/bmc0/attr/RequestedBMCTransition", | ||||
|         "field" : "State.BMC.Transition.Reboot", | ||||
|     }, | ||||
|     "state"     : { | ||||
|         "path"  : "/state/bmc0/attr/CurrentBMCState", | ||||
|     }, | ||||
| } | ||||
|  | ||||
| RESULT_OK = 'ok' | ||||
| RESULT_FAIL = 'fail' | ||||
|  | ||||
| class OpenBMCRest(object): | ||||
|  | ||||
|     headers = {'Content-Type': 'application/json'} | ||||
|  | ||||
|     def __init__(self, name, **kwargs): | ||||
|  | ||||
|         #set default user/passwd | ||||
|         self.name = name | ||||
|         self.username, self.password = ('root', '0penBmc') | ||||
|  | ||||
|         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') | ||||
|         # print back to xcatd or just stdout | ||||
|         self.messager = kwargs.get('messager') | ||||
|  | ||||
|         self.session = rest.RestSession() | ||||
|         self.root_url = HTTP_PROTOCOL + self.bmcip + PROJECT_URL | ||||
|  | ||||
|     def _print_record_log (self, msg, cmd): | ||||
|  | ||||
|         if self.verbose : | ||||
|             localtime = time.asctime( time.localtime(time.time()) ) | ||||
|             log = self.name + ': [openbmc_debug] ' + cmd + ' ' + msg | ||||
|             self.messager.info(localtime + ' ' + log) | ||||
|             logger.debug(log) | ||||
|  | ||||
|     def _log_request (self, method, url, headers, data=None, files=None, cmd=''): | ||||
|  | ||||
|         header_str = ' '.join([ "%s: %s" % (k, v) for k,v in headers.items() ]) | ||||
|         msg = 'curl -k -c cjar -b cjar -X %s -H \"%s\" ' % (method, header_str) | ||||
|  | ||||
|         if files: | ||||
|             msg += '-T \'%s\' %s -s' % (files, url) | ||||
|         elif data: | ||||
|             if cmd == 'login': | ||||
|                 data = data.replace(self.password, "xxxxxx") | ||||
|             msg += '%s -d \'%s\'' % (url, data) | ||||
|         else: | ||||
|             msg += url | ||||
|  | ||||
|         self._print_record_log(msg, cmd) | ||||
|         return msg | ||||
|  | ||||
|     def handle_response (self, resp, cmd=''): | ||||
|  | ||||
|         data = resp.json() # it will raise ValueError | ||||
|         code = resp.status_code | ||||
|         if code != requests.codes.ok: | ||||
|             description = ''.join(data['data']['description']) | ||||
|             error = 'Error: [%d] %s' % (code, description) | ||||
|             self._print_record_log(error, cmd) | ||||
|             raise SelfClientException(error, code) | ||||
|  | ||||
|         self._print_record_log(data['message'], cmd) | ||||
|         return data['data'] | ||||
|  | ||||
|     def request (self, method, resource, headers=None, payload=None, timeout=30, cmd=''): | ||||
|  | ||||
|         httpheaders = headers or OpenBMCRest.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, httpheaders, data=data) | ||||
|             return self.handle_response(response, cmd=cmd) | ||||
|         except SelfServerException as e: | ||||
|             e.message = 'Error: BMC did not respond. ' \ | ||||
|                         'Validate BMC configuration and retry the command.' | ||||
|             self._print_record_log(e.message, cmd) | ||||
|             raise | ||||
|         except ValueError: | ||||
|             error = 'Error: Received wrong format response: %s' % response | ||||
|             self._print_record_log(error, cmd) | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def upload (self, method, resource, files, headers=None, cmd=''): | ||||
|  | ||||
|         httpheaders = headers or OpenBMCRest.headers | ||||
|         url = resource | ||||
|         if not url.startswith(HTTP_PROTOCOL): | ||||
|             url = self.root_url + resource | ||||
|  | ||||
|         request_cmd = self._log_request(method, url, httpheaders, files=files, cmd=cmd) | ||||
|  | ||||
|         try: | ||||
|             response = self.session.request_upload(method, url, httpheaders, files) | ||||
|         except SelfServerException: | ||||
|             self._print_record_log(error, cmd=cmd) | ||||
|             raise | ||||
|         try: | ||||
|             data = json.loads(response) | ||||
|         except ValueError: | ||||
|             error = 'Error: Received wrong format response when running command \'%s\': %s' % \ | ||||
|                     (request_cmd, response) | ||||
|             self._print_record_log(error, cmd=cmd) | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|         if data['message'] != '200 OK': | ||||
|             error = 'Error: Failed to upload update file %s : %s-%s' % \ | ||||
|                     (files, data['message'], \ | ||||
|                     ''.join(data['data']['description'])) | ||||
|             self._print_record_log(error, cmd=cmd) | ||||
|             raise SelfClientException(error, code) | ||||
|  | ||||
|         self._print_record_log(data['message'], cmd=cmd)  | ||||
|  | ||||
|         return True | ||||
|  | ||||
|     def login(self): | ||||
|  | ||||
|         payload = { "data": [ self.username, self.password ] } | ||||
|  | ||||
|         url = HTTP_PROTOCOL + self.bmcip + '/login' | ||||
|         self.request('POST', url, payload=payload, timeout=20, cmd='login') | ||||
|  | ||||
|     def list_power_states(self): | ||||
|  | ||||
|         states = self.request('GET', RPOWER_URLS['state']['path'], cmd='list_power_states') | ||||
|         #filter non used states | ||||
|         try: | ||||
|             host_stat = states[PROJECT_URL + '/state/host0']['CurrentHostState'] | ||||
|             chassis_stat = states[PROJECT_URL + '/state/chassis0']['CurrentPowerState'] | ||||
|             return {'host': host_stat.split('.')[-1], 'chassis': chassis_stat.split('.')[-1]} | ||||
|         except KeyError: | ||||
|             error = 'Error: Received wrong format response: %s' % states | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def set_power_state(self, state): | ||||
|  | ||||
|         payload = { "data": PROJECT_PAYLOAD + RPOWER_URLS[state]['field'] } | ||||
|         return self.request('PUT', RPOWER_URLS[state]['path'], payload=payload, cmd='set_power_state') | ||||
|  | ||||
|     def get_bmc_state(self): | ||||
|  | ||||
|         state = self.request('GET', BMC_URLS['state']['path'], cmd='get_bmc_state') | ||||
|         try: | ||||
|             return {'bmc': state.split('.')[-1]} | ||||
|         except KeyError: | ||||
|             error = 'Error: Received wrong format response: %s' % state | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def reboot_bmc(self, optype='warm'): | ||||
|  | ||||
|         payload = { "data": PROJECT_PAYLOAD + BMC_URLS['reboot']['field'] } | ||||
|         try: | ||||
|             self.request('PUT', BMC_URLS['reboot']['path'], payload=payload, cmd='bmc_reset') | ||||
|         except SelfServerException,SelfClientException: | ||||
|             # TODO: Need special handling for bmc reset, as it is normal bmc may return error | ||||
|             pass | ||||
							
								
								
									
										66
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/power.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/power.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| class PowerInterface(object): | ||||
|     """Interface for power-related actions.""" | ||||
|     interface_type = 'power' | ||||
|     version = '1.0' | ||||
|  | ||||
|     def get_power_state(self, task): | ||||
|         """Return the power state of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :raises: MissingParameterValue if a required parameter is missing. | ||||
|         :returns: a power state. | ||||
|         """ | ||||
|         return task.run('get_state') | ||||
|  | ||||
|     def set_power_state(self, task, power_state, timeout=None): | ||||
|         """Set the power state of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :param power_state: Any power state from :mod:`ironic.common.states`. | ||||
|         :param timeout: timeout (in seconds) positive integer (> 0) for any | ||||
|           power state. ``None`` indicates to use default timeout. | ||||
|         :raises: MissingParameterValue if a required parameter is missing. | ||||
|         """ | ||||
|         return task.run('set_state', power_state, timeout=timeout) | ||||
|  | ||||
|     def reboot(self, task, optype='boot', timeout=None): | ||||
|         """Perform a hard reboot of the task's nodes. | ||||
|  | ||||
|         Drivers are expected to properly handle case when node is powered off | ||||
|         by powering it on. | ||||
|  | ||||
|         :param task: a Task instance containing the node to act on. | ||||
|         :param timeout: timeout (in seconds) positive integer (> 0) for any | ||||
|           power state. ``None`` indicates to use default timeout. | ||||
|         """ | ||||
|         return task.run('reboot', optype, timeout=timeout) | ||||
|  | ||||
|     def get_bmc_state(self, task): | ||||
|         """Return the bmc state of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :raises: MissingParameterValue if a required parameter is missing. | ||||
|         :returns: a power state. | ||||
|         """ | ||||
|         return task.run('get_bmcstate') | ||||
|  | ||||
|     def reboot_bmc(self, task, optype='warm'): | ||||
|         """Return the bmc state of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :raises: MissingParameterValue if a required parameter is missing. | ||||
|         :returns: a power state. | ||||
|         """ | ||||
|         return task.run('reboot_bmc', optype) | ||||
|  | ||||
|  | ||||
| class DefaultPowerManager(PowerInterface): | ||||
|     """Interface for power-related actions.""" | ||||
|     pass | ||||
							
								
								
									
										175
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/task.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/task.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| #!/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 openbmc | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
|  | ||||
| POWER_STATE_DB = { | ||||
|     "on"      : "powering-on", | ||||
|     "off"     : "powering-off", | ||||
|     "softoff" : "powering-off", | ||||
|     "boot"    : "powering-on", | ||||
|     "reset"   : "powering-on", | ||||
| } | ||||
| class OpenBMCPowerTask(ParallelNodesCommand): | ||||
|     """Executor for power-related actions.""" | ||||
|  | ||||
|     def _determine_state(self, states): | ||||
|  | ||||
|         chassis_state = states.get('chassis') | ||||
|         host_state = states.get('host') | ||||
|         state = 'Unknown' | ||||
|         if chassis_state == 'Off': | ||||
|             state = chassis_state | ||||
|  | ||||
|         elif chassis_state == 'On': | ||||
|             if host_state == 'Off': | ||||
|                 state = 'chassison' | ||||
|             elif host_state in ['Quiesced', 'Running']: | ||||
|                 state = host_state | ||||
|             else: | ||||
|                 state = 'Unexpected host state=%s' % host_state | ||||
|         else: | ||||
|             state = 'Unexpected chassis state=%s' % chassis_state | ||||
|         return state | ||||
|  | ||||
|     def get_state(self, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         state = 'Unknown' | ||||
|         try: | ||||
|             obmc.login() | ||||
|             states = obmc.list_power_states() | ||||
|             state = self._determine_state(states) | ||||
|             result = '%s: %s' % (node, openbmc.RPOWER_STATES.get(state, state)) | ||||
|  | ||||
|         except SelfServerException as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|         except SelfClientException as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|  | ||||
|         self.callback.info(result) | ||||
|         return state | ||||
|  | ||||
|     def get_bmcstate(self, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         bmc_not_ready = bmc_state = 'NotReady' | ||||
|         try: | ||||
|             obmc.login() | ||||
|             state = obmc.get_bmc_state() | ||||
|             bmc_state = state.get('bmc') | ||||
|  | ||||
|             if bmc_state != 'Ready': | ||||
|                 bmc_state = bmc_not_ready | ||||
|  | ||||
|             result = '%s: %s' % (node, openbmc.RPOWER_STATES.get(bmc_state, bmc_state)) | ||||
|  | ||||
|         except SelfServerException, SelfClientException: | ||||
|             # There is no response when BMC is not ready | ||||
|             result = '%s: %s'  % (node, openbmc.RPOWER_STATES[bmc_not_ready]) | ||||
|  | ||||
|         self.callback.info(result) | ||||
|         return bmc_state | ||||
|  | ||||
|     def set_state(self, state, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         try: | ||||
|             obmc.login() | ||||
|             ret = obmc.set_power_state(state) | ||||
|             new_status = POWER_STATE_DB.get(state, '') | ||||
|  | ||||
|             result = '%s: %s' % (node, state) | ||||
|             if new_status: | ||||
|                 self.callback.update_node_attributes('status', node, new_status) | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|  | ||||
|         self.callback.info(result) | ||||
|  | ||||
|  | ||||
|     def reboot(self, optype='boot', **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         try: | ||||
|             obmc.login() | ||||
|             states = obmc.list_power_states() | ||||
|             status = self._determine_state(states) | ||||
|  | ||||
|             new_status ='' | ||||
|             if optype == 'reset' and status in ['Off', 'chassison']: | ||||
|                 status = openbmc.RPOWER_STATES['Off'] | ||||
|                 result = '%s: %s'  % (node, status) | ||||
|             else: | ||||
|                 if status not in ['Off', 'off']: | ||||
|                     obmc.set_power_state('off') | ||||
|                     self.callback.update_node_attributes('status', node, POWER_STATE_DB['off']) | ||||
|  | ||||
|                     off_flag = False | ||||
|                     start_timeStamp = int(time.time()) | ||||
|                     for i in range (0, 30): | ||||
|                         states = obmc.list_power_states() | ||||
|                         status = self._determine_state(states) | ||||
|                         if openbmc.RPOWER_STATES.get(status) == 'off': | ||||
|                             off_flag = True | ||||
|                             break | ||||
|                         gevent.sleep( 2 ) | ||||
|  | ||||
|                     end_timeStamp = int(time.time()) | ||||
|  | ||||
|                     if not off_flag: | ||||
|                         error = 'Error: Sent power-off command but state did not change ' \ | ||||
|                                  'to off after waiting %s seconds. (State= %s).' % (end_timeStamp - start_timeStamp, status) | ||||
|                         raise SelfServerException(error) | ||||
|  | ||||
|                 ret = obmc.set_power_state('on') | ||||
|                 self.callback.update_node_attributes('status', node, POWER_STATE_DB['on']) | ||||
|  | ||||
|                 result = '%s: %s'  % (node, optype) | ||||
|  | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|  | ||||
|         self.callback.info(result) | ||||
|  | ||||
|  | ||||
|     def reboot_bmc(self, optype='warm', **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         new_status = '' | ||||
|         try: | ||||
|             obmc.login() | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|         else: | ||||
|             try: | ||||
|                 obmc.reboot_bmc(optype) | ||||
|             except (SelfServerException, SelfClientException) as e: | ||||
|                 result = '%s: %s'  % (node, e.message) | ||||
|             else: | ||||
|                 result = '%s: %s'  % (node, openbmc.RPOWER_STATES['bmcreboot']) | ||||
|         self.callback.info(result) | ||||
|  | ||||
| @@ -1,10 +1,9 @@ | ||||
| from xcatagent import utils | ||||
| from common import utils | ||||
| import gevent | ||||
| from gevent.pool import Pool | ||||
|  | ||||
| MODULE_MAP = {"openbmc": "OpenBMCManager"} | ||||
|  | ||||
|  | ||||
| class BaseManager(object): | ||||
|     def __init__(self, messager, cwd): | ||||
|         self.messager = messager | ||||
|   | ||||
| @@ -1,11 +1,22 @@ | ||||
| from xcatagent import base | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| import argparse | ||||
| import os | ||||
| import time | ||||
| import sys | ||||
| import gevent | ||||
|  | ||||
| import utils | ||||
| import xcat_exception | ||||
| from common import utils | ||||
| from common import exceptions as xcat_exception | ||||
| from hwctl.task import OpenBMCPowerTask | ||||
| from hwctl.power import DefaultPowerManager | ||||
|  | ||||
| from xcatagent import base | ||||
| import openbmc_rest | ||||
|  | ||||
| HTTP_PROTOCOL = "https://" | ||||
| @@ -58,6 +69,7 @@ XCAT_LOG_RFLASH_DIR = XCAT_LOG_DIR + "/rflash/" | ||||
| FIRM_URL = PROJECT_URL + "/software/enumerate" | ||||
|  | ||||
| # global variables of rpower | ||||
| POWER_REBOOT_OPTIONS = ('boot', 'reset') | ||||
| POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff') | ||||
| POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status') | ||||
|  | ||||
| @@ -808,5 +820,55 @@ class OpenBMCManager(base.BaseManager): | ||||
|         self._summary(nodes_num, 'Firmware update') | ||||
|  | ||||
|     def rpower(self, nodeinfo, args): | ||||
|         super(OpenBMCManager, self).process_nodes_worker('openbmc', 'OpenBMC',  | ||||
|         mgr = OpenBMCManager2(self.messager, self.cwd, nodes=self.nodes) | ||||
|         mgr.debugmode = DEBUGMODE | ||||
|         mgr.rpower(nodeinfo, args) | ||||
|  | ||||
|     def _rpower(self, nodeinfo, args): | ||||
|         super(OpenBMCManager, self).process_nodes_worker('openbmc', 'OpenBMC', | ||||
|                     self.nodes, nodeinfo, 'rpower', args) | ||||
|  | ||||
| class OpenBMCManager2(base.BaseManager): | ||||
|     def __init__(self, messager, cwd, nodes=None, envs=None): | ||||
|         super(OpenBMCManager2, self).__init__(messager, cwd) | ||||
|         self.nodes = nodes | ||||
|         self.debugmode = (envs and envs.get('debugmode')) or None | ||||
|  | ||||
|     def rpower(self, nodesinfo, args): | ||||
|  | ||||
|         # 1, parse args | ||||
|         parser = argparse.ArgumentParser(description='Handle rpower operations.') | ||||
|         parser.add_argument('--action', | ||||
|                             help="rpower subcommand.") | ||||
|         parser.add_argument('-V', '--verbose', action='store_true', | ||||
|                             help="rpower verbose mode.") | ||||
|         args.insert(0,'--action') | ||||
|         opts = parser.parse_args(args) | ||||
|  | ||||
|         # 2, validate the args | ||||
|         if opts.action is None: | ||||
|             self.messager.error("Not specify the subcommand for rpower") | ||||
|             return | ||||
|  | ||||
|         if opts.action not in (POWER_GET_OPTIONS + POWER_SET_OPTIONS + POWER_REBOOT_OPTIONS): | ||||
|             self.messager.error("Not supported subcommand for rpower: %s" % opts.action) | ||||
|             return | ||||
|  | ||||
|         # 3, run the subcommands | ||||
|         runner = OpenBMCPowerTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=opts.verbose) | ||||
|         if opts.action == 'bmcstate': | ||||
|             DefaultPowerManager().get_bmc_state(runner) | ||||
|         elif opts.action == 'bmcreboot': | ||||
|             DefaultPowerManager().reboot_bmc(runner) | ||||
|         elif opts.action in POWER_GET_OPTIONS: | ||||
|             DefaultPowerManager().get_power_state(runner) | ||||
|         elif opts.action in POWER_REBOOT_OPTIONS: | ||||
|             DefaultPowerManager().reboot(runner, optype=opts.action) | ||||
|         else: | ||||
|             DefaultPowerManager().set_power_state(runner, power_state=opts.action)      | ||||
|  | ||||
|     def rflash(self, nodesinfo, args): | ||||
|         # 1, parse args | ||||
|         # 2, validate the args | ||||
|         # 3, run the subcommands | ||||
|         pass | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import requests | ||||
| import json | ||||
| import time | ||||
|  | ||||
| import rest | ||||
| import xcat_exception | ||||
| from common import rest | ||||
| from common import exceptions as xcat_exception | ||||
|  | ||||
| class OpenBMCRest: | ||||
|  | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| import requests | ||||
| from gevent.subprocess import Popen, PIPE | ||||
| import urllib3 | ||||
| urllib3.disable_warnings() | ||||
|  | ||||
| import xcat_exception | ||||
|  | ||||
| class RestSession : | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.session = requests.Session() | ||||
|         self.cookies = None | ||||
|  | ||||
|     def request (self, method, url, headers, data): | ||||
|         try: | ||||
|             response = self.session.request(method, url, | ||||
|                                             data=data, | ||||
|                                             headers=headers, | ||||
|                                             verify=False, | ||||
|                                             timeout=30) | ||||
|         except requests.exceptions.ConnectionError: | ||||
|             raise xcat_exception.SelfServerException( | ||||
|                   'Error: BMC did not respond. ' \ | ||||
|                   'Validate BMC configuration and retry the command.') | ||||
|         except requests.exceptions.Timeout: | ||||
|             raise xcat_exception.SelfServerException('Error: Timeout to connect to server') | ||||
|  | ||||
|         if not self.cookies: | ||||
|             self.cookies = requests.utils.dict_from_cookiejar(self.session.cookies) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def request_upload (self, method, url, headers, files): | ||||
|         request_cmd = 'curl -k -b sid=%s -H "%s" -X %s -T %s %s -s' % \ | ||||
|                       (self.cookies['sid'], headers, method, files, url) | ||||
|          | ||||
|         sub = Popen(request_cmd, stdout=PIPE, shell=True) | ||||
|         response, err = sub.communicate() | ||||
|   | ||||
|         return response | ||||
| @@ -9,7 +9,7 @@ import traceback | ||||
| from gevent import socket | ||||
| from gevent.server import StreamServer | ||||
| from gevent.lock import BoundedSemaphore | ||||
| from xcatagent import utils | ||||
| from common import utils | ||||
| from xcatagent import base as xcat_manager | ||||
|  | ||||
| MSG_TYPE = 'message' | ||||
| @@ -17,7 +17,7 @@ DB_TYPE  = 'db' | ||||
| LOCK_FILE = '/var/lock/xcat/agent.lock' | ||||
|  | ||||
|  | ||||
| class Messager(object): | ||||
| class XCATMessager(utils.Messager): | ||||
|     def __init__(self, sock): | ||||
|         self.sock = sock | ||||
|         self.sem = BoundedSemaphore(1) | ||||
| @@ -68,7 +68,7 @@ class Server(object): | ||||
|  | ||||
|     def _handle(self, sock, address): | ||||
|         try: | ||||
|             messager = Messager(sock) | ||||
|             messager = XCATMessager(sock) | ||||
|             buf = sock.recv(4) | ||||
|             sz = utils.bytes2int(buf) | ||||
|             buf = utils.recv_all(sock, sz) | ||||
|   | ||||
| @@ -30,11 +30,15 @@ xCAT-openbmc-py provides openbmc related functions. | ||||
| rm -rf $RPM_BUILD_ROOT | ||||
| install -d $RPM_BUILD_ROOT/%{prefix}/lib/python/agent | ||||
| install -d $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/xcatagent | ||||
| install -d $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/common | ||||
| install -d $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/hwctl | ||||
| install -m755 lib/python/agent/*.py $RPM_BUILD_ROOT/%{prefix}/lib/python/agent | ||||
| install -m644 lib/python/agent/xcatagent/*.py $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/xcatagent | ||||
| install -m644 lib/python/agent/common/*.py $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/common | ||||
| install -m644 lib/python/agent/hwctl/*.py $RPM_BUILD_ROOT/%{prefix}/lib/python/agent/hwctl | ||||
|  | ||||
| %ifnos linux | ||||
| rm -rf $RPM_BUILD_ROOT/%{prefix}/lib/python | ||||
| rm -rf $RPM_BUILD_ROOT/%{prefix}/lib/python/agent | ||||
| %endif | ||||
|  | ||||
| %clean | ||||
|   | ||||
		Reference in New Issue
	
	Block a user