diff --git a/xCAT-openbmc-py/lib/python/agent/common/rest.py b/xCAT-openbmc-py/lib/python/agent/common/rest.py index 8a49016c7..cafc2e742 100644 --- a/xCAT-openbmc-py/lib/python/agent/common/rest.py +++ b/xCAT-openbmc-py/lib/python/agent/common/rest.py @@ -37,6 +37,29 @@ class RestSession(object): return response + def request_download(self, method, url, headers, file_path, using_curl=False): + + if using_curl: + response = self._download_by_curl(method, url, headers, file_path) + else: + response = self.session.request('GET', url, headers=headers) + file_handle = open(file_path, "wb") + for chunk in response.iter_content(chunk_size=1024): + if chunk: + file_handle.write(chunk) + + return response + + def _download_by_curl(self, method, url, headers, file_path): + + header_str = ' '.join([ "%s: %s" % (k, v) for k,v in headers.items() ]) + request_cmd = 'curl -J -k -b sid=%s -H "%s" -X %s -o %s %s -s' % \ + (self.cookies['sid'], header_str, method, file_path, url) + + sub = Popen(request_cmd, stdout=PIPE, shell=True) + response, err = sub.communicate() + 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) diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py b/xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py index 5bdb5bc1d..ec19b596c 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/bmcconfig.py @@ -16,11 +16,11 @@ class BmcConfigInterface(object): def dump_generate(self, task): return task.run("dump_generate") - def dump_clear(self, task, id): - return task.run("dump_clear", id) + def dump_clear(self, task, clear_arg): + return task.run("dump_clear", clear_arg) - def dump_download(self, task, id): - return task.run("dump_download", id) + def dump_download(self, task, download_arg): + return task.run("dump_download", download_arg) def dump_process(self, task): return task.run("dump_process") diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py index b7ae94bed..412ddfb37 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/executor/openbmc_bmcconfig.py @@ -5,6 +5,7 @@ # -*- coding: utf-8 -*- # from __future__ import print_function +import os, stat import gevent import time @@ -18,22 +19,157 @@ logger = logging.getLogger('xcatagent') RSPCONFIG_GET_NETINFO=['ip', 'netmask', 'gateway', 'vlan', 'ipsrc', 'hostname'] RSPCONFIG_SET_NETINFO=['ip', 'netmask', 'gateway', 'vlan'] +XCAT_LOG_DUMP_DIR = "/var/log/xcat/dump/" + class OpenBMCBmcConfigTask(ParallelNodesCommand): - + + def pre_dump_download(self, task, download_arg, **kw): + + if download_arg == 'all': + self.callback.info('Downloading all dumps...') + if not os.path.exists(XCAT_LOG_DUMP_DIR): + os.makedirs(XCAT_LOG_DUMP_DIR) + + def pre_dump_process(self, task, **kw): + + self.callback.info('Capturing BMC Diagnostic information, this will take some time...') + + def _dump_download(self, obmc, node, download_id, flag_dump_process=False): + + formatted_time = time.strftime("%Y%m%d-%H%M", time.localtime(time.time())) + dump_log_file = '%s%s_%s_dump_%s.tar.xz' % (XCAT_LOG_DUMP_DIR, formatted_time, node, download_id) + if flag_dump_process: + self.callback.info('%s: Downloading dump %s to %s' % (node, download_id, dump_log_file)) + + obmc.download_dump(download_id, dump_log_file) + if os.path.exists(dump_log_file): + grep_cmd = '/usr/bin/grep -a' + path_not_found = '"Path not found"' + check_cmd = grep_cmd + ' ' + path_not_found + ' ' + dump_log_file + grep_string = os.popen(check_cmd).readlines() + if grep_string: + result = 'Invalid dump %s was specified. Use -l option to list.' % download_id + else: + result = 'Downloaded dump %s to %s.' % (download_id, dump_log_file) + else: + result = 'Failed to download dump %s to %s.' % (download_id, dump_log_file) + return result + def dump_list(self, **kw): - return self.callback.info('dump_list') + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + dump_info = [] + try: + obmc.login() + dump_dict = obmc.list_dump_info() + + if not dump_dict: + self.callback.info('%s: No attributes returned from the BMC.' % node) + + keys = dump_dict.keys() + keys.sort() + for key in keys: + info = '[%d] Generated: %s, Size: %s' % \ + (key, dump_dict[key]['Generated'], dump_dict[key]['Size']) + dump_info += info + self.callback.info('%s: %s' % (node, info)) + + except (SelfServerException, SelfClientException) as e: + self.callback.info('%s: %s' % (node, e.message)) + + return dump_info def dump_generate(self, **kw): - return self.callback.info("dump_generate") - def dump_clear(self, id, **kw): - return self.callback.info("dump_clear id: %s" % id) + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) - def dump_download(self, id, **kw): - return self.callback.info("dump_download id: %s" % id) + dump_id = None + try: + obmc.login() + dump_id = obmc.create_dump() + if not dump_id: + self.callback.info('%s: BMC returned 200 OK but no ID was returned. Verify manually on the BMC.' % node) + else: + self.callback.info('%s: [%s] success' % (node, dump_id)) + except (SelfServerException, SelfClientException) as e: + self.callback.info('%s: %s' % (node, e.message)) + + return dump_id + + def dump_clear(self, clear_arg, **kw): + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + try: + obmc.login() + obmc.clear_dump(clear_arg) + + result = '%s: [%s] clear' % (node, clear_arg) + except (SelfServerException, SelfClientException) as e: + result = '%s: %s' % (node, e.message) + + self.callback.info(result) + + def dump_download(self, download_arg, **kw): + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + try: + obmc.login() + if download_arg != 'all': + result = self._dump_download(obmc, node, download_arg) + self.callback.info('%s: %s' % (node, result)) + return + + dump_dict = obmc.list_dump_info() + keys = dump_dict.keys() + keys.sort() + + for key in keys: + result = self._dump_download(obmc, node, str(key)) + self.callback.info('%s: %s' % (node, result)) + except SelfServerException as e: + self.callback.info('%s: %s' % (node, e.message)) def dump_process(self, **kw): - return self.callback.info("dump_process: trigger, list and download") + + node = kw['node'] + obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, + debugmode=self.debugmode, verbose=self.verbose) + + try: + obmc.login() + flag = False + dump_id = obmc.create_dump() + self.callback.info('%s: Dump requested. Target ID is %s, waiting for BMC to generate...' % (node, dump_id)) + for i in range(20): + dump_dict = obmc.list_dump_info() + if dump_id in dump_dict: + flag = True + break + if (20-i) % 8 == 0: + self.callback.info('%s: Still waiting for dump %s to be generated... ' % (node, dump_id)) + + gevent.sleep( 15 ) + + if flag: + result = self._dump_download(obmc, node, str(dump_id), flag_dump_process=True) + else: + result = 'Could not find dump %s after waiting %d seconds.' % (dump_id, 20 * 15) + + self.callback.info('%s: %s' % (node, result)) + + except SelfServerException as e: + self.callback.info('%s: %s' % (node, e.message)) def set_sshcfg(self, **kw): return self.callback.info("set_sshcfg") diff --git a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py index f1fa55057..1f9500791 100644 --- a/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py +++ b/xCAT-openbmc-py/lib/python/agent/hwctl/openbmc_client.py @@ -9,7 +9,7 @@ import requests import json import time -from common import rest +from common import utils, rest from common.exceptions import SelfClientException, SelfServerException import logging @@ -29,6 +29,23 @@ RBEACON_URLS = { }, } +DUMP_URLS = { + "clear" : { + "path" : "/dump/entry/#ID#/action/Delete", + "field" : [], + }, + "clear_all" : { + "path" : "/dump/action/DeleteAll", + "field" : [], + }, + "create" : { + "path" : "/dump/action/CreateDump", + "field" : [], + }, + "download" : "download/dump/#ID#", + "list" : "/dump/enumerate", +} + INVENTORY_URL = "/inventory/enumerate" LEDS_URL = "/led/physical/enumerate" @@ -209,6 +226,7 @@ class OpenBMCRest(object): self.session = rest.RestSession() self.root_url = HTTP_PROTOCOL + self.bmcip + PROJECT_URL + self.download_root_url = HTTP_PROTOCOL + self.bmcip + '/' def _print_record_log (self, msg, cmd): @@ -218,13 +236,16 @@ class OpenBMCRest(object): self.messager.info(localtime + ' ' + log) logger.debug(log) - def _log_request (self, method, url, headers, data=None, files=None, cmd=''): + 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 -c cjar -b cjar -X %s -H \"%s\" ' % (method, header_str) if files: msg += '-T \'%s\' %s -s' % (files, url) + elif file_path: + msg = 'curl -J -k -c cjar -b cjar -X %s -H \"%s\" %s -o %s' % \ + (method, header_str, url, file_path) elif data: if cmd == 'login': data = data.replace(self.password, "xxxxxx") @@ -273,6 +294,32 @@ class OpenBMCRest(object): self._print_record_log(error, cmd) raise SelfServerException(error) + def download(self, method, resource, file_path, headers=None, cmd=''): + + httpheaders = headers or OpenBMCRest.headers + url = resource + if not url.startswith(HTTP_PROTOCOL): + url = self.download_root_url + resource + + request_cmd = self._log_request(method, url, httpheaders, file_path=file_path, cmd=cmd) + + try: + response = self.session.request_download(method, url, httpheaders, file_path) + except SelfServerException as e: + self._print_record_log(e.message, cmd=cmd) + raise + except SelfClientException as e: + error = e.message + self._print_record_log(error, cmd=cmd) + raise + + if not response: + self._print_record_log('No response received', cmd=cmd) + return True + + self._print_record_log(str(response.status_code), cmd=cmd) + return True + def upload (self, method, resource, files, headers=None, cmd=''): httpheaders = headers or OpenBMCRest.headers @@ -511,6 +558,48 @@ class OpenBMCRest(object): data={"data": attr_info['get_data']} return self.request(method, get_url, payload=data, cmd="get_%s" % key) + def clear_dump(self, clear_arg): + + if clear_arg == 'all': + payload = { "data": DUMP_URLS['clear_all']['field'] } + self.request('POST', DUMP_URLS['clear_all']['path'], payload=payload, cmd='clear_dump_all') + else: + path = DUMP_URLS['clear']['path'].replace('#ID#', clear_arg) + payload = { "data": DUMP_URLS['clear']['field'] } + self.request('POST', path, payload=payload, cmd='clear_dump') + + def create_dump(self): + + payload = payload = { "data": DUMP_URLS['create']['field'] } + return self.request('POST', DUMP_URLS['create']['path'], payload=payload, cmd='create_dump') + + def list_dump_info(self): + + dump_data = self.request('GET', DUMP_URLS['list'], cmd='list_dump_info') + + try: + dump_dict = {} + for key, value in dump_data.items(): + if 'Size' not in value or 'Elapsed' not in value: + continue + + key_id = int(key.split('/')[-1]) + timestamp = value['Elapsed'] + gen_time = time.strftime("%m/%d/%Y %H:%M:%S", time.localtime(timestamp)) + dump_dict.update({key_id: {'Size': value['Size'], 'Generated': gen_time}}) + + return dump_dict + except KeyError: + error = 'Error: Received wrong format response: %s' % inventory_data + raise SelfServerException(error) + + def download_dump(self, download_id, file_path): + + headers = {'Content-Type': 'application/octet-stream'} + path = DUMP_URLS['download'].replace('#ID#', download_id) + self.download('GET', path, file_path, headers=headers, cmd='download_dump') + + class OpenBMCImage(object): def __init__(self, rawid, data=None): self.id = rawid.split('/')[-1] diff --git a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py index 621e7301f..f29caf871 100644 --- a/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py +++ b/xCAT-openbmc-py/lib/python/agent/xcatagent/openbmc.py @@ -724,9 +724,9 @@ class OpenBMCManager(base.BaseManager): elif opts['--generate']: DefaultBmcConfigManager().dump_generate(runner) elif opts['--clear']: - DefaultBmcConfigManager().dump_clear(runner, opts['--clear']) + DefaultBmcConfigManager().dump_clear(runner, opts['--clear'][0]) elif opts['--download']: - DefaultBmcConfigManager().dump_download(runner, opts['--download']) + DefaultBmcConfigManager().dump_download(runner, opts['--download'][0]) else: DefaultBmcConfigManager().dump_process(runner) elif opts['sshcfg']: