2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00

login for redfish support (#5908)

* login for redfish support

* modified to move auth class
This commit is contained in:
xuweibj 2018-12-19 16:18:28 +08:00 committed by Bin Xu
parent 6514297599
commit 4533cd9e46
5 changed files with 258 additions and 1 deletions

View File

@ -9,6 +9,7 @@ from gevent.subprocess import Popen, PIPE
import requests
import urllib3
urllib3.disable_warnings()
from requests.auth import AuthBase
import exceptions as xcat_exception
@ -17,6 +18,7 @@ class RestSession(object):
def __init__(self):
self.session = requests.Session()
self.cookies = None
self.auth = None
def request(self, method, url, headers, data=None, timeout=30):
@ -24,6 +26,7 @@ class RestSession(object):
response = self.session.request(method, url,
data=data,
headers=headers,
auth=self.auth,
verify=False,
timeout=timeout)
except requests.exceptions.ConnectionError as e:
@ -60,6 +63,9 @@ class RestSession(object):
if not self.cookies:
self.cookies = requests.utils.dict_from_cookiejar(self.session.cookies)
if not self.auth and 'X-Auth-Token' in response.headers:
self.auth = XTokenAuth(response.headers['X-Auth-Token'])
return response
def extract_server_and_port(self, message_string, format="STRING"):
@ -127,3 +133,13 @@ class RestSession(object):
raise SelfServerException(error)
return response
class XTokenAuth(AuthBase):
def __init__(self,authToken):
self.authToken=authToken
def __call__(self, auth):
auth.headers['X-Auth-Token']=self.authToken
return(auth)

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
###############################################################################
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
###############################################################################
# -*- coding: utf-8 -*-
#
from __future__ import print_function
import gevent
import time
from common.task import ParallelNodesCommand
from common.exceptions import SelfClientException, SelfServerException
from hwctl import redfish_client as redfish
import logging
logger = logging.getLogger('xcatagent')
class RedfishPowerTask(ParallelNodesCommand):
"""Executor for power-related actions."""
def get_state(self, **kw):
node = kw['node']
rf = redfish.RedfishRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback,
debugmode=self.debugmode, verbose=self.verbose)
state = 'Unknown'
try:
rf.login()
self.callback.info('%s: %s' % (node, state))
except (SelfServerException, SelfClientException) as e:
self.callback.error(e.message, node)
return state

View File

@ -0,0 +1,129 @@
#!/usr/bin/env python
###############################################################################
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
###############################################################################
# -*- coding: utf-8 -*-
#
import os
import requests
import json
import time
from common import utils, rest
from common.exceptions import SelfClientException, SelfServerException
import logging
logger = logging.getLogger('xcatagent')
HTTP_PROTOCOL = "https://"
PROJECT_URL = "/redfish/v1"
SESSION_URL = PROJECT_URL + "/SessionService/Sessions"
class RedfishRest(object):
headers = {'Content-Type': 'application/json'}
def __init__(self, name, **kwargs):
self.name = name
self.username = None
self.password = None
if 'nodeinfo' in kwargs:
for key, value in kwargs['nodeinfo'].items():
setattr(self, key, value)
if not hasattr(self, 'bmcip'):
self.bmcip = self.name
self.verbose = kwargs.get('debugmode')
self.messager = kwargs.get('messager')
self.session = rest.RestSession()
self.root_url = HTTP_PROTOCOL + self.bmcip
def _print_record_log (self, msg, cmd, error_flag=False):
if self.verbose or error_flag:
localtime = time.asctime( time.localtime(time.time()) )
log = self.name + ': [redfish_debug] ' + cmd + ' ' + msg
if self.verbose:
self.messager.info(localtime + ' ' + log)
logger.debug(log)
def _print_error_log (self, msg, cmd):
self._print_record_log(msg, cmd, True)
def _log_request (self, method, url, headers, data=None, files=None, file_path=None, cmd=''):
header_str = ' '.join([ "%s: %s" % (k, v) for k,v in headers.items() ])
msg = 'curl -k -X %s -H \"%s\" ' % (method, header_str)
if cmd != 'login':
msg += '-H \"X-Auth-Token: xxxxxx\" '
if data:
if cmd == 'login':
data = data.replace('"Password": "%s"' % self.password, '"Password": "xxxxxx"')
data = '-d \'%s\'' % data
msg += '%s %s -v' % (url, data)
else:
msg += url
self._print_record_log(msg, cmd)
return msg
def request (self, method, resource, headers=None, payload=None, timeout=30, cmd=''):
httpheaders = headers or RedfishRest.headers
url = resource
if not url.startswith(HTTP_PROTOCOL):
url = self.root_url + resource
data = None
if payload:
data=json.dumps(payload)
self._log_request(method, url, httpheaders, data=data, cmd=cmd)
try:
response = self.session.request(method, url, headers=httpheaders, data=data, timeout=timeout)
return self.handle_response(response, cmd=cmd)
except SelfServerException as e:
if cmd == 'login':
e.message = "Login to BMC failed: Can't connect to {0} {1}.".format(e.host_and_port, e.detail_msg)
else:
e.message = 'BMC did not respond. ' \
'Validate BMC configuration and retry the command.'
self._print_error_log(e.message, cmd)
raise
except ValueError:
error = 'Received wrong format response: %s' % response
self._print_error_log(error, cmd)
raise SelfServerException(error)
def handle_response (self, resp, cmd=''):
data = resp.json()
code = resp.status_code
if code != requests.codes.ok and code != requests.codes.created:
description = ''.join(data['error']['@Message.ExtendedInfo'][0]['Message'])
error = '[%d] %s' % (code, description)
self._print_error_log(error, cmd)
raise SelfClientException(error, code)
if cmd == 'login' and not 'X-Auth-Token' in resp.headers:
raise SelfServerException('Login Failed: Did not get Session Token from response')
self._print_record_log('%s %s' % (code, data['Name']), cmd)
return data
def login(self):
payload = { "UserName": self.username, "Password": self.password }
self.request('POST', SESSION_URL, payload=payload, timeout=20, cmd='login')

View File

@ -2,7 +2,8 @@ from common import utils
import gevent
from gevent.pool import Pool
MODULE_MAP = {"openbmc": "OpenBMCManager"}
MODULE_MAP = {"openbmc": "OpenBMCManager",
"redfish": "RedfishManager"}
class BaseManager(object):
def __init__(self, messager, cwd):

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
###############################################################################
# IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html
###############################################################################
# -*- coding: utf-8 -*-
#
import os
import gevent
import re
import sys
from docopt import docopt,DocoptExit
from common import utils
from common import exceptions as xcat_exception
from hwctl.executor.redfish_power import RedfishPowerTask
from hwctl.power import DefaultPowerManager
from xcatagent import base
import logging
logger = logging.getLogger('xcatagent')
try:
if not logger.handlers:
utils.enableSyslog('xcat.agent')
except:
pass
DEBUGMODE = False
VERBOSE = False
# global variables of rpower
POWER_REBOOT_OPTIONS = ('boot', 'reset')
POWER_SET_OPTIONS = ('on', 'off', 'bmcreboot', 'softoff')
POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status')
class RedfishManager(base.BaseManager):
def __init__(self, messager, cwd, nodes=None, envs=None):
super(RedfishManager, self).__init__(messager, cwd)
self.nodes = nodes
self.debugmode = (envs and envs.get('debugmode')) or None
#TODO, remove the global variable DEBUGMODE
global DEBUGMODE
DEBUGMODE = envs['debugmode']
if self.debugmode:
logger.setLevel(logging.DEBUG)
def rpower(self, nodesinfo, args):
# 1, parse args
rpower_usage = """
Usage:
rpower [-V|--verbose] [boot|bmcreboot|bmcstate|off|on|reset|softoff|stat|state|status]
Options:
-V --verbose rpower verbose mode.
"""
try:
opts=docopt(rpower_usage, argv=args)
self.verbose=opts.pop('--verbose')
action=[k for k,v in opts.items() if v][0]
except Exception as e:
# It will not be here as perl has validation for args
self.messager.error("Failed to parse arguments for rpower: %s" % args)
return
# 2, validate the args
if action not in (POWER_GET_OPTIONS + POWER_SET_OPTIONS + POWER_REBOOT_OPTIONS):
self.messager.error("Not supported subcommand for rpower: %s" % action)
return
# 3, run the subcommands
runner = RedfishPowerTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose)
DefaultPowerManager().get_power_state(runner)