mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-21 11:12:04 +00:00
openbmc python refactor
This commit is contained in:
parent
c8ace50d21
commit
ddf4cd932c
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user