
From: Aline Manera <alinefm@br.ibm.com> To avoid duplicating code in model and mockmodel, the common code related to debugreport resource was added to model_/debugreports.py and the specific code for each backend (libvirt or mock) was added to model_/libvirtbackend.py and model_/mockbackend.py Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/model_/debugreports.py | 86 +++++++++++++++++++++++++++++++++++ src/kimchi/model_/libvirtbackend.py | 85 ++++++++++++++++++++++++++++++++++ src/kimchi/model_/mockbackend.py | 27 +++++++++++ 3 files changed, 198 insertions(+) create mode 100644 src/kimchi/model_/debugreports.py diff --git a/src/kimchi/model_/debugreports.py b/src/kimchi/model_/debugreports.py new file mode 100644 index 0000000..4db1ace --- /dev/null +++ b/src/kimchi/model_/debugreports.py @@ -0,0 +1,86 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import glob +import os +import time + +from kimchi import config +from kimchi.exception import NotFoundError + +class DebugReports(object): + def __init__(self, backend): + pass + + def get_list(self): + path = config.get_debugreports_path() + file_pattern = os.path.join(path, '*.*') + file_lists = glob.glob(file_pattern) + file_lists = [os.path.split(file)[1] for file in file_lists] + name_lists = [file.split('.', 1)[0] for file in file_lists] + + return name_lists + +class DebugReport(object): + def __init__(self, backend): + self.backend = backend + + def lookup(self, name): + file_target = self._get_debugreport(name) + ctime = os.stat(file_target).st_ctime + ctime = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime(ctime)) + file_target = os.path.split(file_target)[-1] + file_target = os.path.join("/data/debugreports", file_target) + return {'file': file_target, + 'ctime': ctime} + + def create(self, params): + ident = params['name'] + taskid = self.backend.gen_debugreport_file(ident) + if taskid is None: + raise OperationFailed("Debug report tool not found.") + + with self.backend.objstore as session: + return session.get('task', str(taskid)) + + def delete(self, name): + file_target = self._get_debugreports(name) + os.remove(file_target) + + def _get_debugreport(self, name): + path = config.get_debugreports_path() + file_pattern = os.path.join(path, name + '.*') + try: + file_target = glob.glob(file_pattern)[0] + except IndexError: + raise NotFoundError("Debug report '%s' not found.") + + return file_target + +class DebugReportContent(object): + def __init__(self, backend): + self.backend = backend + + def lookup(self, name): + debugreport = DebugReports(backend) + return self.debugreport.lookup(name) diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py index 0c70116..ea46a13 100644 --- a/src/kimchi/model_/libvirtbackend.py +++ b/src/kimchi/model_/libvirtbackend.py @@ -21,8 +21,93 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import fnmatch +import glob +import logging +import os +import shutil +import subprocess + +from kimchi import config +from kimchi.asynctask import AsyncTask +from kimchi.exception import OperationFailed from kimchi.objectstore import ObjectStore +from kimchi.utils import kimchi_log class LibvirtBackend(object): def __init__(self, objstore_loc=None): self.objstore = ObjectStore(objstore_loc) + self.next_taskid = 1 + + # Please add new possible debug report command here + # and implement the report generating function + # based on the new report command + self.report_tools = ({'cmd': 'sosreport --help', + 'fn': self._sosreport_generate},) + + def gen_debugreport_file(self, name): + gen_cmd = self._get_system_report_tool() + if gen_cmd is None: + return None + + return self.add_task('', gen_cmd, name) + + def _get_system_report_tool(self): + # check if the command can be found by shell one by one + for helper_tool in self.report_tools: + try: + retcode = subprocess.call(helper_tool['cmd'], shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if retcode == 0: + return helper_tool['fn'] + except Exception, e: + kimchi_log.info('Exception running command: %s', e) + + return None + + def _sosreport_generate(self, cb, name): + command = 'sosreport --batch --name "%s"' % name + try: + retcode = subprocess.call(command, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if retcode < 0: + raise OperationFailed('Command terminated with signal') + elif retcode > 0: + raise OperationFailed('Command failed: rc = %i' % retcode) + + pattern = '/tmp/sosreport-%s-*' % name + for reportFile in glob.glob(pattern): + if not fnmatch.fnmatch(reportFile, '*.md5'): + output = reportFile + break + else: + # sosreport tends to change the name mangling rule and + # compression file format between different releases. + # It's possible to fail to match a report file even sosreport + # runs successfully. In future we might have a general name + # mangling function in kimchi to format the name before passing + # it to sosreport. Then we can delete this exception. + raise OperationFailed('Can not find generated debug report ' + 'named by %s' % pattern) + ext = output.split('.', 1)[1] + path = config.get_debugreports_path() + target = os.path.join(path, name) + target_file = '%s.%s' % (target, ext) + shutil.move(output, target_file) + os.remove('%s.md5' % output) + cb('OK', True) + except Exception, e: + # No need to call cb to update the task status here. + # The task object will catch the exception rasied here + # and update the task status there + log = logging.getLogger('Model') + log.warning('Exception in generating debug file: %s', e) + raise OperationFailed(e) + + def add_task(self, target_uri, fn, opaque=None): + id = self.next_taskid + self.next_taskid += 1 + task = AsyncTask(id, target_uri, fn, self.objstore, opaque) + return id diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py index e3c6ab9..fa60fc1 100644 --- a/src/kimchi/model_/mockbackend.py +++ b/src/kimchi/model_/mockbackend.py @@ -21,8 +21,35 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import os +import random + +from kimchi import config +from kimchi.asynctask import AsyncTask from kimchi.objectstore import ObjectStore class MockBackend(object): def __init__(self, objstore_loc=None): self.objstore = ObjectStore(objstore_loc) + self.next_taskid = 1 + + def gen_debugreport_file(self, ident): + return self.add_task('', self._create_debugreport, ident) + + def _create_debugreport(self, cb, name): + path = config.get_debugreports_path() + tmpf = os.path.join(path, name + '.tmp') + realf = os.path.join(path, name + '.txt') + length = random.randint(1000, 10000) + with open(tmpf, 'w') as fd: + while length: + fd.write('mock debug report\n') + length = length - 1 + os.rename(tmpf, realf) + cb("OK", True) + + def add_task(self, target_uri, fn, opaque=None): + id = self.next_taskid + self.next_taskid += 1 + task = AsyncTask(id, target_uri, fn, self.objstore, opaque) + return id -- 1.7.10.4