summaryrefslogtreecommitdiffstats
path: root/python/mozperftest/mozperftest/metrics/notebook/perftestetl.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozperftest/mozperftest/metrics/notebook/perftestetl.py')
-rw-r--r--python/mozperftest/mozperftest/metrics/notebook/perftestetl.py167
1 files changed, 167 insertions, 0 deletions
diff --git a/python/mozperftest/mozperftest/metrics/notebook/perftestetl.py b/python/mozperftest/mozperftest/metrics/notebook/perftestetl.py
new file mode 100644
index 0000000000..bd28d9be6d
--- /dev/null
+++ b/python/mozperftest/mozperftest/metrics/notebook/perftestetl.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+import json
+import os
+import pathlib
+from collections import OrderedDict
+
+from .constant import Constant
+from .transformer import SimplePerfherderTransformer, Transformer, get_transformer
+
+
+class PerftestETL(object):
+ """Controller class for the PerftestETL."""
+
+ def __init__(
+ self,
+ file_groups,
+ config,
+ prefix,
+ logger,
+ custom_transform=None,
+ sort_files=False,
+ ):
+ """Initializes PerftestETL.
+
+ :param dict file_groups: A dict of file groupings. The value
+ of each of the dict entries is the name of the data that
+ will be produced.
+ :param str custom_transform: The class name of a custom transformer.
+ """
+ self.fmt_data = {}
+ self.file_groups = file_groups
+ self.config = config
+ self.sort_files = sort_files
+ self.const = Constant()
+ self.prefix = prefix
+ self.logger = logger
+
+ # Gather the available transformers
+ tfms_dict = self.const.predefined_transformers
+
+ # XXX NOTEBOOK_PLUGIN functionality is broken at the moment.
+ # This code block will raise an exception if it detects it in
+ # the environment.
+ plugin_path = os.getenv("NOTEBOOK_PLUGIN")
+ if plugin_path:
+ raise Exception("NOTEBOOK_PLUGIN is currently broken.")
+
+ # Initialize the requested transformer
+ if custom_transform:
+ # try to load it directly, and fallback to registry
+ try:
+ tfm_cls = get_transformer(custom_transform)
+ except ImportError:
+ tfm_cls = tfms_dict.get(custom_transform)
+
+ if tfm_cls:
+ self.transformer = Transformer(
+ files=[],
+ custom_transformer=tfm_cls(),
+ logger=self.logger,
+ prefix=self.prefix,
+ )
+ self.logger.info(f"Found {custom_transform} transformer", self.prefix)
+ else:
+ raise Exception(f"Could not get a {custom_transform} transformer.")
+ else:
+ self.transformer = Transformer(
+ files=[],
+ custom_transformer=SimplePerfherderTransformer(),
+ logger=self.logger,
+ prefix=self.prefix,
+ )
+
+ def parse_file_grouping(self, file_grouping):
+ """Handles differences in the file_grouping definitions.
+
+ It can either be a path to a folder containing the files, a list of files,
+ or it can contain settings from an artifact_downloader instance.
+
+ :param file_grouping: A file grouping entry.
+ :return: A list of files to process.
+ """
+ files = []
+ if isinstance(file_grouping, list):
+ # A list of files was provided
+ files = file_grouping
+ elif isinstance(file_grouping, dict):
+ # A dictionary of settings from an artifact_downloader instance
+ # was provided here
+ raise Exception(
+ "Artifact downloader tooling is disabled for the time being."
+ )
+ elif isinstance(file_grouping, str):
+ # Assume a path to files was given
+ filepath = file_grouping
+ newf = [f.resolve().as_posix() for f in pathlib.Path(filepath).rglob("*")]
+ files = newf
+ else:
+ raise Exception(
+ "Unknown file grouping type provided here: %s" % file_grouping
+ )
+
+ if self.sort_files:
+ if isinstance(files, list):
+ files.sort()
+ else:
+ for _, file_list in files.items():
+ file_list.sort()
+ files = OrderedDict(sorted(files.items(), key=lambda entry: entry[0]))
+
+ if not files:
+ raise Exception(
+ "Could not find any files in this configuration: %s" % file_grouping
+ )
+
+ return files
+
+ def parse_output(self):
+ # XXX Fix up this function, it should only return a directory for output
+ # not a directory or a file. Or remove it completely, it's not very useful.
+ prefix = "" if "prefix" not in self.config else self.config["prefix"]
+ filepath = f"{prefix}std-output.json"
+
+ if "output" in self.config:
+ filepath = self.config["output"]
+ if os.path.isdir(filepath):
+ filepath = os.path.join(filepath, f"{prefix}std-output.json")
+
+ return filepath
+
+ def process(self, **kwargs):
+ """Process the file groups and return the results of the requested analyses.
+
+ :return: All the results in a dictionary. The field names are the Analyzer
+ funtions that were called.
+ """
+ fmt_data = []
+
+ for name, files in self.file_groups.items():
+ files = self.parse_file_grouping(files)
+ if isinstance(files, dict):
+ raise Exception(
+ "Artifact downloader tooling is disabled for the time being."
+ )
+ else:
+ # Transform the data
+ self.transformer.files = files
+ trfm_data = self.transformer.process(name, **kwargs)
+
+ if isinstance(trfm_data, list):
+ fmt_data.extend(trfm_data)
+ else:
+ fmt_data.append(trfm_data)
+
+ self.fmt_data = fmt_data
+
+ # Write formatted data output to filepath
+ output_data_filepath = self.parse_output()
+
+ print("Writing results to %s" % output_data_filepath)
+ with open(output_data_filepath, "w") as f:
+ json.dump(self.fmt_data, f, indent=4, sort_keys=True)
+
+ return {"data": self.fmt_data, "file-output": output_data_filepath}