[Kimchi-devel] [PATCH 07/23] refactor model: Create a separated model for debug report resource

Aline Manera alinefm at linux.vnet.ibm.com
Wed Jan 29 23:34:48 UTC 2014


From: Aline Manera <alinefm at br.ibm.com>

The model implementation for config and its sub-resources were added to
model_/debugreports.py
The idea is create a separated model implementation for each resource.

Signed-off-by: Aline Manera <alinefm at br.ibm.com>
---
 src/kimchi/model_/debugreports.py |  167 +++++++++++++++++++++++++++++++++++++
 src/kimchi/utils.py               |   19 ++++-
 2 files changed, 182 insertions(+), 4 deletions(-)
 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..39402aa
--- /dev/null
+++ b/src/kimchi/model_/debugreports.py
@@ -0,0 +1,167 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Aline Manera <alinefm at 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 fnmatch
+import glob
+import logging
+import os
+import shutil
+import subprocess
+import time
+
+from kimchi import config
+from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.model_.tasks import TaskModel
+from kimchi.utils import add_task, kimchi_log
+
+
+class DebugReportsModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+        self.task = TaskModel(**kargs)
+
+    def create(self, params):
+        ident = params['name']
+        taskid = self._gen_debugreport_file(ident)
+        return self.task.lookup(taskid)
+
+    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
+
+    def _gen_debugreport_file(self, name):
+        gen_cmd = self.get_system_report_tool()
+
+        if gen_cmd is not None:
+            return add_task('', gen_cmd, self.objstore, name)
+
+        raise OperationFailed("debugreport tool not found")
+
+    @staticmethod
+    def sosreport_generate(cb, name):
+        command = 'sosreport --batch --name "%s"' % name
+        try:
+            retcode = subprocess.call(command, shell=True,
+                                      stdout=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)
+
+            return
+
+        except OSError:
+            raise
+
+        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)
+
+    @staticmethod
+    def get_system_report_tool():
+        # Please add new possible debug report command here
+        # and implement the report generating function
+        # based on the new report command
+        report_tools = ({'cmd': 'sosreport --help',
+                         'fn': DebugReportsModel.sosreport_generate},)
+
+        # check if the command can be found by shell one by one
+        for helper_tool in 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
+
+
+class DebugReportModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def lookup(self, name):
+        path = config.get_debugreports_path()
+        file_pattern = os.path.join(path, name)
+        file_pattern = file_pattern + '.*'
+        try:
+            file_target = glob.glob(file_pattern)[0]
+        except IndexError:
+            raise NotFoundError('no such report')
+
+        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 delete(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('no such report')
+
+        os.remove(file_target)
+
+
+class DebugReportContentModel(object):
+    def __init__(self, **kargs):
+        self._debugreport = DebugReportModel()
+
+    def lookup(self, name):
+        return self._debugreport.lookup(name)
diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py
index 59500dd..30c09d4 100644
--- a/src/kimchi/utils.py
+++ b/src/kimchi/utils.py
@@ -25,17 +25,28 @@ import cherrypy
 import os
 import subprocess
 import urllib2
-
+from threading import Timer
 
 from cherrypy.lib.reprconf import Parser
-from kimchi.exception import TimeoutExpired
-
 
 from kimchi import config
-from threading import Timer
+from kimchi.asynctask import AsyncTask
 
 
 kimchi_log = cherrypy.log.error_log
+task_id = 0
+
+
+def get_next_task_id():
+    global task_id
+    task_id += 1
+    return task_id
+
+
+def add_task(target_uri, fn, objstore, opaque=None):
+    id = get_next_task_id()
+    AsyncTask(id, target_uri, fn, objstore, opaque)
+    return id
 
 
 def is_digit(value):
-- 
1.7.10.4




More information about the Kimchi-devel mailing list