Refactor loader classes 35/27235/5
authorYujun Zhang <zhang.yujunz@zte.com.cn>
Thu, 19 Jan 2017 08:58:46 +0000 (16:58 +0800)
committerYujun Zhang <zhang.yujunz@zte.com.cn>
Sun, 29 Jan 2017 12:34:56 +0000 (20:34 +0800)
- rename BaseLoader to YamlFileLoader as base class of QTIP specs loader
- create an abstract BaseLoader
- create FileLoader for logfile collector

Change-Id: I0c992cd847fc0dce4fdd73a13c1cdbc406c84532
Signed-off-by: Yujun Zhang <zhang.yujunz@zte.com.cn>
qtip/base/constant.py
qtip/collector/base.py
qtip/collector/logfile.py
qtip/loader/base.py
qtip/loader/file.py [new file with mode: 0644]
qtip/loader/metric.py
qtip/loader/plan.py
qtip/loader/qpi.py
qtip/loader/yaml_file.py [new file with mode: 0644]
tests/data/benchmarks/plan/doctor.yaml [moved from tests/data/opt/plan/doctor.yaml with 76% similarity]
tests/unit/loader/plan_test.py

index 58cc0cc..ddd07e9 100644 (file)
@@ -64,8 +64,8 @@ class PlanProp(BaseProp):
 class CollectorProp(BaseProp):
     LOGS = 'logs'
     FILENAME = 'filename'
-    PATTERNS = 'patterns'
-    MATCH = 'match'
+    GREP = 'grep'
+    REGEX = 'regex'
     CAPTURE = 'capture'
 
 
index cd8fc79..2a25455 100644 (file)
@@ -10,3 +10,5 @@
 
 class BaseCollector(object):
     """performance metrics collector"""
+    def __init__(self, config):
+        self._config = config
index 6528ea9..19780aa 100644 (file)
@@ -9,6 +9,30 @@
 
 from base import BaseCollector
 
+from qtip.base.constant import CollectorProp as CProp
+from qtip.loader.file import FileLoader
+
 
 class LogfileCollector(BaseCollector):
     """collect performance metrics from log files"""
+
+    def __init__(self, config, paths=None):
+        super(LogfileCollector, self).__init__(config)
+        self.loader = FileLoader('.', paths)
+
+    def collect(self):
+        captured = {}
+        for item in self._config[CProp.LOGS]:
+            captured.update(self._parse_log(item))
+        return captured
+
+    def _parse_log(self, log_item):
+        captured = {}
+        # TODO(yujunz) select parser by name
+        if CProp.GREP in log_item:
+            for rule in log_item[CProp.GREP]:
+                captured.update(self._grep(log_item[CProp.FILENAME], rule))
+        return captured
+
+    def _grep(self, filename, rule):
+        return {}
index a0e5d03..abc254b 100644 (file)
@@ -7,57 +7,6 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-from collections import defaultdict
-from itertools import chain
-from os import listdir
-from os import path
-import yaml
-
-from qtip.base.error import InvalidFormat, NotFound
-from qtip.base.constant import BaseProp
-
-
-ROOT_DIR = path.join(path.dirname(__file__), path.pardir, path.pardir,
-                     'benchmarks')
-
 
 class BaseLoader(object):
-    """Abstract class of QTIP benchmark loader"""
-    RELATIVE_PATH = '.'
-    _paths = [ROOT_DIR]
-
-    def __init__(self, name, paths=None):
-        self._file = name
-        self._abspath = self._find(name, paths=paths)
-        content = defaultdict(lambda: None)
-
-        try:
-            content.update(yaml.safe_load(file(self._abspath)))
-        except yaml.YAMLError:
-            # TODO(yujunz) log yaml error
-            raise InvalidFormat(self._abspath)
-
-        self.name = content[BaseProp.NAME] or path.splitext(name)[0]
-        self.content = content
-
-    def _find(self, name, paths=None):
-        """find a benchmark in searching paths"""
-        paths = self._paths if paths is None else paths
-        for p in paths:
-            abspath = path.join(p, self.RELATIVE_PATH, name)
-            if path.exists(abspath):
-                return abspath
-        raise NotFound(name, paths)
-
-    @classmethod
-    def list_all(cls, paths=None):
-        """list all available benchmarks"""
-        paths = cls._paths if paths is None else paths
-        names = chain.from_iterable([listdir(path.join(p, cls.RELATIVE_PATH))
-                                     for p in paths])
-        for name in names:
-            item = cls(name, paths=paths)
-            yield {
-                BaseProp.NAME: name,
-                BaseProp.ABSPATH: item._abspath,
-                BaseProp.CONTENT: item.content}
+    """Abstract class of QTIP loader"""
diff --git a/qtip/loader/file.py b/qtip/loader/file.py
new file mode 100644 (file)
index 0000000..00f9481
--- /dev/null
@@ -0,0 +1,50 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corp and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from itertools import chain
+from os import listdir
+from os import path
+
+from qtip.base.constant import BaseProp
+from qtip.base.error import NotFound
+from qtip.loader.base import BaseLoader
+
+
+ROOT_DIR = path.join(path.dirname(__file__), path.pardir, path.pardir,
+                     'benchmarks')
+
+
+class FileLoader(BaseLoader):
+    RELATIVE_PATH = '.'
+    _paths = [ROOT_DIR]
+
+    def __init__(self, name, paths=None):
+        self._file = name
+        self._abspath = self._find(name, paths=paths)
+
+    def _find(self, name, paths=None):
+        """find a specification in searching paths"""
+        paths = self._paths if paths is None else paths
+        for p in paths:
+            abspath = path.join(p, self.RELATIVE_PATH, name)
+            if path.exists(abspath):
+                return abspath
+        raise NotFound(name, paths)
+
+    @classmethod
+    def list_all(cls, paths=None):
+        """list all available specification"""
+        paths = cls._paths if paths is None else paths
+        names = chain.from_iterable([listdir(path.join(p, cls.RELATIVE_PATH))
+                                     for p in paths])
+        for name in names:
+            item = cls(name, paths=paths)
+            yield {
+                BaseProp.NAME: name,
+                BaseProp.ABSPATH: item._abspath}
index 8b6fa5d..842fcdb 100644 (file)
@@ -7,10 +7,10 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-from base import BaseLoader
+from yaml_file import YamlFileLoader
 
 
-class MetricSpec(BaseLoader):
+class MetricSpec(YamlFileLoader):
     """metrics in QTIP are categorized by performance test tools, such as
     dhrystone, whetstone and etc"""
     RELATIVE_PATH = 'metric'
index c1ee00b..6f1764e 100644 (file)
@@ -9,11 +9,12 @@
 
 
 from qtip.base.constant import PlanProp
-from qtip.loader.base import BaseLoader
+from qtip.collector.logfile import LogfileCollector
+from qtip.loader.yaml_file import YamlFileLoader
 from qtip.loader.qpi import QPISpec
 
 
-class Plan(BaseLoader):
+class Plan(YamlFileLoader):
     """
     a benchmark plan is consist of configuration and a QPI list
     """
@@ -26,3 +27,7 @@ class Plan(BaseLoader):
         self.qpis = [QPISpec(qpi, paths=paths)
                      for qpi in self.content[PlanProp.QPIS]]
         self.info = self.content[PlanProp.INFO]
+        _config = self.content[PlanProp.CONFIG]
+
+        # TODO(yujunz) create collector by name
+        self.collector = LogfileCollector(_config[PlanProp.COLLECTOR], paths)
index ef6e065..73da61e 100644 (file)
@@ -7,14 +7,14 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-from base import BaseLoader
+from yaml_file import YamlFileLoader
 from metric import MetricSpec
 
 from qtip.base.constant import SpecProp
 from qtip.util.formula import Formula
 
 
-class QPISpec(BaseLoader):
+class QPISpec(YamlFileLoader):
     """
     a QPI specification defines how to calculate a performance index from
      collected metrics.
diff --git a/qtip/loader/yaml_file.py b/qtip/loader/yaml_file.py
new file mode 100644 (file)
index 0000000..f1cd461
--- /dev/null
@@ -0,0 +1,33 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corp and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from collections import defaultdict
+from os import path
+import yaml
+
+from qtip.base.error import InvalidFormat
+from qtip.base.constant import BaseProp
+from qtip.loader.file import FileLoader
+
+
+class YamlFileLoader(FileLoader):
+    """load content from yaml file"""
+
+    def __init__(self, name, paths=None):
+        super(YamlFileLoader, self).__init__(name, paths)
+        content = defaultdict(lambda: None)
+
+        try:
+            content.update(yaml.safe_load(file(self._abspath)))
+        except yaml.YAMLError:
+            # TODO(yujunz) log yaml error
+            raise InvalidFormat(self._abspath)
+
+        self.name = content[BaseProp.NAME] or path.splitext(name)[0]
+        self.content = content
similarity index 76%
rename from tests/data/opt/plan/doctor.yaml
rename to tests/data/benchmarks/plan/doctor.yaml
index 48b4c95..6c95077 100644 (file)
@@ -10,24 +10,24 @@ config:
       logs:
         - filename: doctor_consumer.log
           # 2016-12-28 03:16:05,630 consumer.py 26 INFO   doctor consumer notified at 1482894965.63
-          patterns:
-            - match: 'doctor consumer notified at \d+(\.\d+)?$'
+          grep:
+            - regex: 'doctor consumer notified at \d+(\.\d+)?$'
               capture: notified consumer
         - filename: doctor_inspector.log
           # 2016-12-28 03:16:05,299 inspector.py 76 INFO   event posted at 1482894965.3
           # 2016-12-28 03:16:05,299 inspector.py 56 INFO   doctor mark vm(<Server: doctor_vm1>) error at 1482894965.3
           # 2016-12-28 03:16:05,506 inspector.py 66 INFO   doctor mark host(overcloud-novacompute-1.ool-virtual1) down at 1482894965.51
-          patterns:
-            - match: 'event posted at \d+(\.\d+)?$'
+          grep:
+            - regex: 'event posted at \d+(\.\d+)?$'
               capture: posted event
-            - match: 'doctor mark vm\(.*\) error at \d+(\.\d+)?$'
+            - regex: 'doctor mark vm\(.*\) error at \d+(\.\d+)?$'
               capture: marked VM error
-            - match: 'doctor mark host\(.*\) down at \d+(\.\d+)?$'
+            - regex: 'doctor mark host\(.*\) down at \d+(\.\d+)?$'
               capture: marked host down
         - filename: disable_network.log
           # doctor set host down at 1482894965.164096803
-          patterns:
-            - match: 'doctor set host down at \d+(\.\d+)?$'
+          grep:
+            - regex: 'doctor set host down at \d+(\.\d+)?$'
               capture: set host down
   reporter:
     name: console
index b57bcfb..32837f8 100644 (file)
@@ -27,7 +27,7 @@ def test_init(plan):
 
 def test_list_all(benchmarks_root):
     plan_list = Plan.list_all(paths=[benchmarks_root])
-    assert len(list(plan_list)) is 1
+    assert len(list(plan_list)) is 2
     for desc in plan_list:
         assert PlanProp.NAME in desc
         assert PlanProp.CONTENT in desc