"""Utility functions for Raptor"""
# 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 os
from collections.abc
import Iterable
import yaml
from logger.logger
import RaptorLogger
from mozgeckoprofiler
import view_gecko_profile
LOG = RaptorLogger(component=
"raptor-utils")
here = os.path.dirname(os.path.realpath(__file__))
external_tools_path = os.environ.get(
"EXTERNALTOOLSPATH",
None)
if external_tools_path
is not None:
# running in production via mozharness
TOOLTOOL_PATH = os.path.join(external_tools_path,
"tooltool.py")
def strtobool(value: str):
# Copied from `mach.util` since this script can run outside of a mach environment
# Reimplementation of distutils.util.strtobool
# https://docs.python.org/3.9/distutils/apiref.html#distutils.util.strtobool
true_vals = (
"y",
"yes",
"t",
"true",
"on",
"1")
false_vals = (
"n",
"no",
"f",
"false",
"off",
"0")
value = value.lower()
if value
in true_vals:
return 1
if value
in false_vals:
return 0
raise ValueError(f
'Expected one of: {", ".join(true_vals + false_vals)}')
def flatten(data, parent_dir, sep=
"/"):
"""
Converts a dictionary
with nested entries like this
{
"dict1": {
"dict2": {
"key1": value1,
"key2": value2,
...
},
...
},
...
"dict3": {
"key3": value3,
"key4": value4,
...
}
...
}
to a
"flattened" dictionary like this that has no nested entries:
{
"dict1-dict2-key1": value1,
"dict1-dict2-key2": value2,
...
"dict3-key3": value3,
"dict3-key4": value4,
...
}
:param Iterable data : json data.
:param tuple parent_dir: json fields.
:
return dict: {subtest: value}
"""
result = {}
if not data:
return result
if isinstance(data, list):
for item
in data:
for k, v
in flatten(item, parent_dir, sep=sep).items():
result.setdefault(k, []).extend(v)
elif isinstance(data, dict):
for k, v
in data.items():
current_dir = parent_dir + (k,)
subtest = sep.join(current_dir)
if isinstance(v, Iterable)
and not isinstance(v, str):
for x, y
in flatten(v, current_dir, sep=sep).items():
result.setdefault(x, []).extend(y)
elif v
or v == 0:
result.setdefault(subtest, []).append(v)
else:
result.setdefault(sep.join(parent_dir), []).append(data)
return result
def transform_platform(
str_to_transform, config_platform, config_processor=
None, mitmproxy_version=
None
):
"""Transform platform name i.e. 'mitmproxy-rel-bin-{platform}.manifest'
transforms to
'mitmproxy-rel-bin-osx.manifest'.
Also transform
'{x64}' if needed
for 64 bit / win 10
"""
if "{platform}" not in str_to_transform
and "{x64}" not in str_to_transform:
return str_to_transform
if "win" in config_platform:
platform_id =
"win"
elif config_platform ==
"mac":
# Bug 1920821
# If we are using mitmproxy 11 we need to ensure platform_id is configured
# correctly for the folder structure. Having this check also keeps the ability to
# playback on older versions which don't have ARM support
if config_processor ==
"arm" and mitmproxy_version ==
"11.0.0":
platform_id =
"osx-arm64"
else:
platform_id =
"osx"
else:
platform_id =
"linux64"
if "{platform}" in str_to_transform:
str_to_transform = str_to_transform.replace(
"{platform}", platform_id)
if "{x64}" in str_to_transform
and config_processor
is not None:
if "x86_64" in config_processor:
str_to_transform = str_to_transform.replace(
"{x64}",
"_x64")
else:
str_to_transform = str_to_transform.replace(
"{x64}",
"")
return str_to_transform
def transform_subtest(str_to_transform, subtest_name):
"""Transform subtest name i.e. 'mitm5-linux-firefox-{subtest}.manifest'
transforms to
'mitm5-linux-firefox-amazon.manifest'.
"""
if "{subtest}" not in str_to_transform:
return str_to_transform
return str_to_transform.replace(
"{subtest}", subtest_name)
def view_gecko_profile_from_raptor():
# automatically load the latest raptor gecko-profile archive in profiler.firefox.com
LOG_GECKO = RaptorLogger(component=
"raptor-view-gecko-profile")
profile_zip_path = os.environ.get(
"RAPTOR_LATEST_GECKO_PROFILE_ARCHIVE",
None)
if profile_zip_path
is None or not os.path.exists(profile_zip_path):
LOG_GECKO.info(
"No local raptor gecko profiles were found so not "
"launching profiler.firefox.com"
)
return
LOG_GECKO.info(
"Profile saved locally to: %s" % profile_zip_path)
view_gecko_profile(profile_zip_path)
def write_yml_file(yml_file, yml_data):
# write provided data to specified local yaml file
LOG.info(
"writing %s to %s" % (yml_data, yml_file))
try:
with open(yml_file,
"w")
as outfile:
yaml.dump(yml_data, outfile, default_flow_style=
False)
except Exception
as e:
LOG.critical(
"failed to write yaml file, exeption: %s" % e)
def bool_from_str(boolean_string):
return bool(strtobool(boolean_string))