diff --git a/xCAT-openbmc-py/lib/python/agent/client.py b/xCAT-openbmc-py/lib/python/agent/client.py index ed5bd3e84..1c8e0448e 100755 --- a/xCAT-openbmc-py/lib/python/agent/client.py +++ b/xCAT-openbmc-py/lib/python/agent/client.py @@ -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): diff --git a/xCAT-openbmc-py/lib/python/agent/common/__init__.py b/xCAT-openbmc-py/lib/python/agent/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/xcat_exception.py b/xCAT-openbmc-py/lib/python/agent/common/exceptions.py similarity index 100% rename from xCAT-openbmc-py/lib/python/agent/xcatagent/xcat_exception.py rename to xCAT-openbmc-py/lib/python/agent/common/exceptions.py diff --git a/xCAT-openbmc-py/lib/python/agent/common/rest.py b/xCAT-openbmc-py/lib/python/agent/common/rest.py new file mode 100644 index 000000000..8a49016c7 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/common/rest.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/common/task.py b/xCAT-openbmc-py/lib/python/agent/common/task.py new file mode 100644 index 000000000..899d8fd8b --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/common/task.py @@ -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 *_""" + 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() diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/utils.py b/xCAT-openbmc-py/lib/python/agent/common/utils.py similarity index 71% rename from xCAT-openbmc-py/lib/python/agent/xcatagent/utils.py rename to xCAT-openbmc-py/lib/python/agent/common/utils.py index 183f1ebb8..c386f0f52 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/utils.py +++ b/xCAT-openbmc-py/lib/python/agent/common/utils.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/__init__.py b/xCAT-openbmc-py/lib/python/agent/hwctl/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc.py b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc.py new file mode 100644 index 000000000..d9f4922d3 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/power.py b/xCAT-openbmc-py/lib/python/agent/hwctl/power.py new file mode 100644 index 000000000..e10435b20 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/power.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/task.py b/xCAT-openbmc-py/lib/python/agent/hwctl/task.py new file mode 100644 index 000000000..53612a6b9 --- /dev/null +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/task.py @@ -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) + diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/base.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/base.py index b1ebb6aac..bf58ef628 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/base.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/base.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py index 0b301ec42..8b49b88eb 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py index 40152a118..9dcf8c99e 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc_rest.py @@ -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: diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/rest.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/rest.py deleted file mode 100644 index 35cbb449d..000000000 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/rest.py +++ /dev/null @@ -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 diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/server.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/server.py index a0b242356..86a6867de 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/server.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/server.py @@ -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) diff --git a/xCAT-openbmc-py/xCAT-openbmc-py.spec b/xCAT-openbmc-py/xCAT-openbmc-py.spec index 18bcd94ee..dcd8d8ee7 100644 --- a/xCAT-openbmc-py/xCAT-openbmc-py.spec +++ b/xCAT-openbmc-py/xCAT-openbmc-py.spec @@ -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