mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-22 19:52:03 +00:00
228 lines
7.5 KiB
Python
228 lines
7.5 KiB
Python
#!/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
|