Родитель
ba08fc3164
Коммит
db7ccdaf4b
|
@ -4,7 +4,11 @@ operational_monitoring:
|
|||
owners:
|
||||
- msamuel@mozilla.com
|
||||
pretty_name: Operational Monitoring
|
||||
data_functions: ["compute_opmon_dimensions"]
|
||||
views:
|
||||
projects:
|
||||
type: table_view
|
||||
tables:
|
||||
- table: moz-fx-data-shared-prod.operational_monitoring_derived.projects_v1
|
||||
duet:
|
||||
glean_app: false
|
||||
owners:
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
"""Constants."""
|
||||
from typing import List, Set
|
||||
|
||||
# These are fields we don't need for opmon views/explores/dashboards
|
||||
OPMON_EXCLUDED_FIELDS: Set[str] = {
|
||||
"submission",
|
||||
"client_id",
|
||||
"build_id",
|
||||
"agg_type",
|
||||
"value",
|
||||
"histogram__VALUES",
|
||||
"histogram__bucket_count",
|
||||
"histogram__histogram_type",
|
||||
"histogram__range",
|
||||
"histogram__sum",
|
||||
}
|
||||
|
||||
# These are fields we don't need for opmon dashboards
|
||||
OPMON_DASH_EXCLUDED_FIELDS: List[str] = [
|
||||
"branch",
|
||||
"probe",
|
||||
"histogram__VALUES__key",
|
||||
"histogram__VALUES__value",
|
||||
]
|
|
@ -1,9 +1,8 @@
|
|||
"""Class to describe Operational Monitoring Dashboard."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from .. import operational_monitoring_utils
|
||||
from ..views import lookml_utils
|
||||
from .dashboard import Dashboard
|
||||
|
||||
|
@ -26,10 +25,13 @@ class OperationalMonitoringDashboard(Dashboard):
|
|||
name: str,
|
||||
layout: str,
|
||||
namespace: str,
|
||||
tables: List[Dict[str, str]],
|
||||
defn: List[Dict[str, Any]],
|
||||
):
|
||||
"""Get an instance of a FunnelAnalysisView."""
|
||||
super().__init__(title, name, layout, namespace, tables)
|
||||
"""Get an instance of a Operational Monitoring Dashboard."""
|
||||
self.dimensions = defn[0].get("dimensions", {})
|
||||
self.xaxis = defn[0]["xaxis"]
|
||||
|
||||
super().__init__(title, name, layout, namespace, defn)
|
||||
|
||||
@classmethod
|
||||
def from_dict(
|
||||
|
@ -50,7 +52,7 @@ class OperationalMonitoringDashboard(Dashboard):
|
|||
]
|
||||
return dict((label, colour) for (label, colour) in zip(series_labels, colours))
|
||||
|
||||
def to_lookml(self, bq_client, data):
|
||||
def to_lookml(self, bq_client):
|
||||
"""Get this dashboard as LookML."""
|
||||
kwargs = {
|
||||
"name": self.name,
|
||||
|
@ -60,27 +62,27 @@ class OperationalMonitoringDashboard(Dashboard):
|
|||
"dimensions": [],
|
||||
}
|
||||
|
||||
table_data = {}
|
||||
for view_data in data.get("compute_opmon_dimensions", {}).values():
|
||||
table_data.update(view_data)
|
||||
|
||||
includes = []
|
||||
graph_index = 0
|
||||
for table_defn in self.tables:
|
||||
table_name = table_defn["table"]
|
||||
if len(kwargs["dimensions"]) == 0:
|
||||
kwargs["dimensions"] = table_data[table_name]
|
||||
kwargs["dimensions"] = [
|
||||
{
|
||||
"name": name,
|
||||
"title": lookml_utils.slug_to_title(name),
|
||||
"default": info["default"],
|
||||
"options": info["options"],
|
||||
}
|
||||
for name, info in self.dimensions.items()
|
||||
]
|
||||
|
||||
xaxis = operational_monitoring_utils.get_xaxis_val(bq_client, table_name)
|
||||
|
||||
metrics = lookml_utils.get_distinct_vals(bq_client, table_name, "probe")
|
||||
explore = table_defn["explore"]
|
||||
includes.append(
|
||||
f"/looker-hub/{self.namespace}/explores/{explore}.explore.lkml"
|
||||
)
|
||||
|
||||
series_colors = self._map_series_to_colours(table_defn["branches"], explore)
|
||||
for metric in metrics:
|
||||
for metric in table_defn.get("probes", []):
|
||||
title = lookml_utils.slug_to_title(metric)
|
||||
kwargs["elements"].append(
|
||||
{
|
||||
|
@ -88,13 +90,12 @@ class OperationalMonitoringDashboard(Dashboard):
|
|||
"metric": metric,
|
||||
"explore": explore,
|
||||
"series_colors": series_colors,
|
||||
"xaxis": xaxis,
|
||||
"xaxis": self.xaxis,
|
||||
"row": int(graph_index / 2) * 10,
|
||||
"col": 0 if graph_index % 2 == 0 else 12,
|
||||
}
|
||||
)
|
||||
graph_index += 1
|
||||
|
||||
dash_lookml = lookml_utils.render_template(
|
||||
"dashboard.lkml", "dashboards", **kwargs
|
||||
)
|
||||
|
|
|
@ -40,7 +40,7 @@ class ClientCountsExplore(Explore):
|
|||
]
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Generate LookML to represent this explore."""
|
||||
return [
|
||||
|
|
|
@ -47,7 +47,7 @@ class EventsExplore(Explore):
|
|||
return EventsExplore(name, defn["views"], views_path)
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
name = self.name
|
||||
if not name.endswith("_counts"):
|
||||
|
|
|
@ -26,7 +26,7 @@ class Explore:
|
|||
return {self.name: {"type": self.type, "views": self.views}}
|
||||
|
||||
def to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Generate LookML for this explore.
|
||||
|
@ -62,7 +62,7 @@ class Explore:
|
|||
] = f"${{{base_view_name}.submission_date}} >= '2010-01-01'"
|
||||
|
||||
# We only update the first returned explore
|
||||
new_lookml = self._to_lookml(client, v1_name, data)
|
||||
new_lookml = self._to_lookml(client, v1_name)
|
||||
base_lookml.update(new_lookml[0])
|
||||
new_lookml[0] = base_lookml
|
||||
|
||||
|
@ -72,7 +72,6 @@ class Explore:
|
|||
self,
|
||||
client: bigquery.Client,
|
||||
v1_name: Optional[str],
|
||||
data: Dict = {},
|
||||
) -> List[Dict[str, Any]]:
|
||||
raise NotImplementedError("Only implemented in subclasses")
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class FunnelAnalysisExplore(Explore):
|
|||
return FunnelAnalysisExplore(name, defn["views"], views_path)
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
view_lookml = self.get_view_lookml("funnel_analysis")
|
||||
views = view_lookml["views"]
|
||||
|
|
|
@ -17,7 +17,7 @@ class GleanPingExplore(PingExplore):
|
|||
type: str = "glean_ping_explore"
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Generate LookML to represent this explore."""
|
||||
repo = next((r for r in GleanPing.get_repos() if r["name"] == v1_name))
|
||||
|
|
|
@ -16,7 +16,7 @@ class GrowthAccountingExplore(Explore):
|
|||
type: str = "growth_accounting_explore"
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Generate LookML to represent this explore."""
|
||||
return [
|
||||
|
|
|
@ -7,8 +7,7 @@ from typing import Any, Dict, Iterator, List, Optional
|
|||
|
||||
from google.cloud import bigquery
|
||||
|
||||
from .. import operational_monitoring_utils
|
||||
from ..views import View, lookml_utils
|
||||
from ..views import View
|
||||
from . import Explore
|
||||
|
||||
|
||||
|
@ -22,12 +21,15 @@ class OperationalMonitoringExplore(Explore):
|
|||
name: str,
|
||||
views: Dict[str, str],
|
||||
views_path: Path = None,
|
||||
defn: Dict[str, str] = None,
|
||||
defn: Dict[str, Any] = None,
|
||||
):
|
||||
"""Initialize OperationalMonitoringExplore."""
|
||||
super().__init__(name, views, views_path)
|
||||
if defn is not None:
|
||||
self.branches = ", ".join(defn["branches"])
|
||||
self.xaxis = defn.get("xaxis")
|
||||
self.dimensions = defn.get("dimensions", {})
|
||||
self.probes = defn.get("probes", [])
|
||||
|
||||
@staticmethod
|
||||
def from_views(views: List[View]) -> Iterator[Explore]:
|
||||
|
@ -53,40 +55,26 @@ class OperationalMonitoringExplore(Explore):
|
|||
self,
|
||||
bq_client: bigquery.Client,
|
||||
v1_name: Optional[str],
|
||||
data: Dict = {},
|
||||
) -> List[Dict[str, Any]]:
|
||||
base_view_name = self.views["base_view"]
|
||||
|
||||
namespace_data = data.get("compute_opmon_dimensions", {}).get(
|
||||
base_view_name, {}
|
||||
)
|
||||
table_name = (
|
||||
"" if len(namespace_data.keys()) == 0 else list(namespace_data.keys())[0]
|
||||
)
|
||||
dimension_data = namespace_data[table_name]
|
||||
|
||||
xaxis = operational_monitoring_utils.get_xaxis_val(bq_client, table_name)
|
||||
|
||||
filters = [
|
||||
{f"{base_view_name}.branch": self.branches},
|
||||
{f"{base_view_name}.percentile_conf": "50"},
|
||||
]
|
||||
for dimension in dimension_data:
|
||||
if "default" in dimension:
|
||||
filters.append(
|
||||
{f"{base_view_name}.{dimension['name']}": dimension["default"]}
|
||||
)
|
||||
for dimension, info in self.dimensions.items():
|
||||
if "default" in info:
|
||||
filters.append({f"{base_view_name}.{dimension}": info["default"]})
|
||||
|
||||
aggregate_tables = []
|
||||
probes = lookml_utils.get_distinct_vals(bq_client, table_name, "probe")
|
||||
for probe in probes:
|
||||
for probe in self.probes:
|
||||
filters_copy = deepcopy(filters)
|
||||
filters_copy.append({f"{base_view_name}.probe": probe})
|
||||
aggregate_tables.append(
|
||||
{
|
||||
"name": f"rollup_{probe}",
|
||||
"query": {
|
||||
"dimensions": [xaxis, "branch"],
|
||||
"dimensions": [self.xaxis, "branch"],
|
||||
"measures": ["low", "high", "percentile"],
|
||||
"filters": filters_copy,
|
||||
},
|
||||
|
|
|
@ -16,7 +16,7 @@ class PingExplore(Explore):
|
|||
type: str = "ping_explore"
|
||||
|
||||
def _to_lookml(
|
||||
self, client: bigquery.Client, v1_name: Optional[str], data: Dict = {}
|
||||
self, client: bigquery.Client, v1_name: Optional[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Generate LookML to represent this explore."""
|
||||
return [
|
||||
|
|
|
@ -8,7 +8,6 @@ import lkml
|
|||
import yaml
|
||||
from google.cloud import bigquery
|
||||
|
||||
from . import operational_monitoring_utils
|
||||
from .dashboards import DASHBOARD_TYPES
|
||||
from .explores import EXPLORE_TYPES
|
||||
from .namespaces import _get_glean_apps
|
||||
|
@ -45,7 +44,6 @@ def _generate_explores(
|
|||
v1_name: Optional[
|
||||
str
|
||||
], # v1_name for Glean explores: see: https://mozilla.github.io/probe-scraper/#tag/library
|
||||
namespace_data: dict,
|
||||
) -> Iterable[Path]:
|
||||
for explore_name, defn in explores.items():
|
||||
logging.info(f"Generating lookml for explore {explore_name} in {namespace}")
|
||||
|
@ -58,7 +56,7 @@ def _generate_explores(
|
|||
f"/looker-hub/{namespace}/views/{view}.view.lkml"
|
||||
for view in explore.get_dependent_views()
|
||||
],
|
||||
"explores": explore.to_lookml(client, v1_name, namespace_data),
|
||||
"explores": explore.to_lookml(client, v1_name),
|
||||
}
|
||||
path = out_dir / (explore_name + ".explore.lkml")
|
||||
path.write_text(FILE_HEADER + lkml.dump(file_lookml))
|
||||
|
@ -70,7 +68,6 @@ def _generate_dashboards(
|
|||
dash_dir: Path,
|
||||
namespace: str,
|
||||
dashboards: dict,
|
||||
namespace_data: dict,
|
||||
):
|
||||
for dashboard_name, dashboard_info in dashboards.items():
|
||||
logging.info(f"Generating lookml for dashboard {dashboard_name} in {namespace}")
|
||||
|
@ -78,7 +75,7 @@ def _generate_dashboards(
|
|||
namespace, dashboard_name, dashboard_info
|
||||
)
|
||||
|
||||
dashboard_lookml = dashboard.to_lookml(client, namespace_data)
|
||||
dashboard_lookml = dashboard.to_lookml(client)
|
||||
dash_path = dash_dir / f"{dashboard_name}.dashboard.lookml"
|
||||
dash_path.write_text(FILE_HEADER + dashboard_lookml)
|
||||
yield dash_path
|
||||
|
@ -95,24 +92,6 @@ def _glean_apps_to_v1_map(glean_apps):
|
|||
return {d["name"]: d["v1_name"] for d in glean_apps}
|
||||
|
||||
|
||||
def _append_data(bq_client, data_functions, view, namespace_data):
|
||||
# For now, assume that data functions are applied
|
||||
# to each table. Later it could be a mapping so only
|
||||
# certain functions are applied to certain tables.
|
||||
for function_name in data_functions:
|
||||
if function_name not in namespace_data:
|
||||
namespace_data[function_name] = {}
|
||||
|
||||
namespace_data[function_name][view.name] = {}
|
||||
|
||||
for table in view.tables:
|
||||
table_name = table["table"]
|
||||
data_function = getattr(operational_monitoring_utils, function_name)
|
||||
namespace_data[function_name][view.name][table_name] = data_function(
|
||||
bq_client, table_name
|
||||
)
|
||||
|
||||
|
||||
def _lookml(namespaces, glean_apps, target_dir):
|
||||
client = bigquery.Client()
|
||||
|
||||
|
@ -134,12 +113,6 @@ def _lookml(namespaces, glean_apps, target_dir):
|
|||
view_dir.mkdir(parents=True, exist_ok=True)
|
||||
views = list(_get_views_from_dict(lookml_objects.get("views", {}), namespace))
|
||||
|
||||
namespace_data = {}
|
||||
for view in views:
|
||||
_append_data(
|
||||
client, lookml_objects.get("data_functions", []), view, namespace_data
|
||||
)
|
||||
|
||||
logging.info(" Generating views")
|
||||
v1_name: Optional[str] = v1_mapping.get(namespace)
|
||||
for view_path in _generate_views(client, view_dir, views, v1_name):
|
||||
|
@ -150,7 +123,7 @@ def _lookml(namespaces, glean_apps, target_dir):
|
|||
explores = lookml_objects.get("explores", {})
|
||||
logging.info(" Generating explores")
|
||||
for explore_path in _generate_explores(
|
||||
client, explore_dir, namespace, explores, view_dir, v1_name, namespace_data
|
||||
client, explore_dir, namespace, explores, view_dir, v1_name
|
||||
):
|
||||
logging.info(f" ...Generating {explore_path}")
|
||||
|
||||
|
@ -159,7 +132,7 @@ def _lookml(namespaces, glean_apps, target_dir):
|
|||
dashboard_dir.mkdir(parents=True, exist_ok=True)
|
||||
dashboards = lookml_objects.get("dashboards", {})
|
||||
for dashboard_path in _generate_dashboards(
|
||||
client, dashboard_dir, namespace, dashboards, namespace_data
|
||||
client, dashboard_dir, namespace, dashboards
|
||||
):
|
||||
logging.info(f" ...Generating {dashboard_path}")
|
||||
|
||||
|
|
|
@ -11,20 +11,21 @@ from io import BytesIO
|
|||
from itertools import groupby
|
||||
from operator import itemgetter
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Union
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
import click
|
||||
import yaml
|
||||
from google.cloud import storage
|
||||
from google.cloud import bigquery
|
||||
|
||||
from generator import operational_monitoring_utils
|
||||
|
||||
from .explores import EXPLORE_TYPES
|
||||
from .views import VIEW_TYPES, View
|
||||
|
||||
PROBE_INFO_BASE_URI = "https://probeinfo.telemetry.mozilla.org"
|
||||
DEFAULT_SPOKE = "looker-spoke-default"
|
||||
OPMON_BUCKET_NAME = "operational_monitoring"
|
||||
OPMON_DATASET = "operational_monitoring"
|
||||
PROD_PROJECT = "moz-fx-data-shared-prod"
|
||||
PROJECTS_FOLDER = "projects/"
|
||||
DATA_TYPES = ["histogram", "scalar"]
|
||||
|
||||
|
||||
|
@ -32,15 +33,6 @@ def _normalize_slug(name):
|
|||
return re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
||||
|
||||
|
||||
def _download_json_file(project, bucket, filename):
|
||||
blob = bucket.get_blob(filename)
|
||||
return json.loads(blob.download_as_string()), blob.updated
|
||||
|
||||
|
||||
def _get_first(tuple_):
|
||||
return tuple_[0]
|
||||
|
||||
|
||||
def _merge_namespaces(dct, merge_dct):
|
||||
"""Recursively merge namespaces."""
|
||||
for k, _ in merge_dct.items():
|
||||
|
@ -77,66 +69,82 @@ def _get_db_views(uri):
|
|||
return views
|
||||
|
||||
|
||||
def _append_view_and_explore_for_data_type(
|
||||
om_content, project_name, table_prefix, data_type, branches, xaxis
|
||||
):
|
||||
project_data_type_id = f"{project_name}_{data_type}"
|
||||
def _get_opmon(bq_client: bigquery.Client, namespaces: Dict[str, Any]):
|
||||
om_content: Dict[str, Any] = {"views": {}, "explores": {}, "dashboards": {}}
|
||||
# get operational monitoring namespace information
|
||||
|
||||
om_content["views"][project_data_type_id] = {
|
||||
"type": f"operational_monitoring_{data_type}_view",
|
||||
"tables": [
|
||||
{
|
||||
"table": f"{PROD_PROJECT}.operational_monitoring.{table_prefix}_{data_type}",
|
||||
"xaxis": xaxis,
|
||||
}
|
||||
],
|
||||
}
|
||||
om_content["explores"][project_data_type_id] = {
|
||||
"type": "operational_monitoring_explore",
|
||||
"views": {"base_view": f"{project_name}_{data_type}"},
|
||||
"branches": branches,
|
||||
}
|
||||
opmon_namespace = namespaces["operational_monitoring"]
|
||||
views = opmon_namespace.get("views")
|
||||
|
||||
if views is None:
|
||||
print("No views defined for operational monitoring")
|
||||
return {}
|
||||
|
||||
def _get_opmon_views_explores_dashboards():
|
||||
client = storage.Client(PROD_PROJECT)
|
||||
bucket = client.get_bucket(OPMON_BUCKET_NAME)
|
||||
om_content = {"views": {}, "explores": {}, "dashboards": {}}
|
||||
projects_view = views.get("projects")
|
||||
|
||||
if projects_view is None:
|
||||
print("No projects view defined for operational monitoring")
|
||||
return {}
|
||||
|
||||
projects_table = projects_view["tables"][0]["table"]
|
||||
projects = operational_monitoring_utils.get_projects(
|
||||
bq_client, project_table=projects_table
|
||||
)
|
||||
|
||||
# Iterating over all defined operational monitoring projects
|
||||
for blob in bucket.list_blobs(prefix=PROJECTS_FOLDER):
|
||||
# The folder itself is not a project file
|
||||
if blob.name == PROJECTS_FOLDER:
|
||||
continue
|
||||
|
||||
om_project, project_last_modified = _download_json_file(
|
||||
PROD_PROJECT, bucket, blob.name
|
||||
)
|
||||
table_prefix = _normalize_slug(om_project["slug"])
|
||||
project_name = "_".join(om_project["name"].lower().split(" "))
|
||||
branches = om_project.get("branches", ["enabled", "disabled"])
|
||||
for project in projects:
|
||||
table_prefix = _normalize_slug(project["slug"])
|
||||
project_name = "_".join(project["name"].lower().split(" "))
|
||||
branches = project.get("branches", ["enabled", "disabled"])
|
||||
|
||||
for data_type in DATA_TYPES:
|
||||
_append_view_and_explore_for_data_type(
|
||||
om_content,
|
||||
project_name,
|
||||
table_prefix,
|
||||
data_type,
|
||||
branches,
|
||||
om_project["xaxis"],
|
||||
# append view and explore for data type
|
||||
project_data_type_id = f"{project_name}_{data_type}"
|
||||
table = f"{PROD_PROJECT}.{OPMON_DATASET}.{table_prefix}_{data_type}"
|
||||
dimensions = operational_monitoring_utils.get_dimension_defaults(
|
||||
bq_client, table, project["dimensions"]
|
||||
)
|
||||
|
||||
om_content["views"][project_data_type_id] = {
|
||||
"type": f"operational_monitoring_{data_type}_view",
|
||||
"tables": [
|
||||
{
|
||||
"table": table,
|
||||
"xaxis": project["xaxis"],
|
||||
"dimensions": dimensions,
|
||||
}
|
||||
],
|
||||
}
|
||||
om_content["explores"][project_data_type_id] = {
|
||||
"type": "operational_monitoring_explore",
|
||||
"views": {"base_view": f"{project_name}_{data_type}"},
|
||||
"branches": branches,
|
||||
"xaxis": project["xaxis"],
|
||||
"dimensions": dimensions,
|
||||
"probes": [
|
||||
p["name"] for p in project["probes"] if p["agg_type"] == data_type
|
||||
],
|
||||
}
|
||||
|
||||
om_content["dashboards"][project_name] = {
|
||||
"type": "operational_monitoring_dashboard",
|
||||
"tables": [
|
||||
{
|
||||
"explore": f"{project_name}_{data_type}",
|
||||
"table": f"{PROD_PROJECT}.operational_monitoring.{table_prefix}_{data_type}",
|
||||
"table": f"{PROD_PROJECT}.{OPMON_DATASET}.{table_prefix}_{data_type}",
|
||||
"branches": branches,
|
||||
"xaxis": project["xaxis"],
|
||||
"dimensions": dimensions,
|
||||
"probes": [
|
||||
p["name"]
|
||||
for p in project["probes"]
|
||||
if p["agg_type"] == data_type
|
||||
],
|
||||
}
|
||||
for data_type in DATA_TYPES
|
||||
],
|
||||
}
|
||||
|
||||
return om_content
|
||||
|
||||
|
||||
|
@ -285,9 +293,13 @@ def namespaces(custom_namespaces, generated_sql_uri, app_listings_uri, disallowl
|
|||
|
||||
if custom_namespaces is not None:
|
||||
custom_namespaces = yaml.safe_load(custom_namespaces.read()) or {}
|
||||
views_explores_dashboards = _get_opmon_views_explores_dashboards()
|
||||
custom_namespaces["operational_monitoring"].update(views_explores_dashboards)
|
||||
_merge_namespaces(namespaces, custom_namespaces)
|
||||
|
||||
# generating operational monitoring namespace, if available
|
||||
if "operational_monitoring" in custom_namespaces:
|
||||
client = bigquery.Client()
|
||||
opmon = _get_opmon(bq_client=client, namespaces=custom_namespaces)
|
||||
custom_namespaces["operational_monitoring"].update(opmon)
|
||||
_merge_namespaces(namespaces, custom_namespaces)
|
||||
|
||||
disallowed_namespaces = yaml.safe_load(disallowlist.read()) or {}
|
||||
|
||||
|
|
|
@ -1,61 +1,43 @@
|
|||
"""Utils for operational monitoring."""
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from google.api_core import exceptions
|
||||
from google.cloud import bigquery
|
||||
|
||||
from .constants import OPMON_DASH_EXCLUDED_FIELDS, OPMON_EXCLUDED_FIELDS
|
||||
from .views import lookml_utils
|
||||
|
||||
|
||||
def compute_opmon_dimensions(
|
||||
bq_client: bigquery.Client, table: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
def get_dimension_defaults(
|
||||
bq_client: bigquery.Client, table: str, dimensions: List[str]
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Compute dimensions for Operational Monitoring.
|
||||
Find default values for certain dimensions.
|
||||
|
||||
For a given Operational Monitoring dimension, find its default (most common)
|
||||
value and its top 10 most common to be used as dropdown options.
|
||||
"""
|
||||
all_dimensions = lookml_utils._generate_dimensions(bq_client, table)
|
||||
copy_excluded = OPMON_EXCLUDED_FIELDS.copy()
|
||||
copy_excluded.update(OPMON_DASH_EXCLUDED_FIELDS)
|
||||
dimensions = []
|
||||
dimension_defaults = {}
|
||||
|
||||
relevant_dimensions = [
|
||||
dimension
|
||||
for dimension in all_dimensions
|
||||
if dimension["name"] not in copy_excluded
|
||||
]
|
||||
for dimension in relevant_dimensions:
|
||||
dimension_name = dimension["name"]
|
||||
for dimension in dimensions:
|
||||
query_job = bq_client.query(
|
||||
f"""
|
||||
SELECT DISTINCT {dimension_name}, COUNT(*)
|
||||
FROM {table}
|
||||
GROUP BY 1
|
||||
ORDER BY 2 DESC
|
||||
"""
|
||||
SELECT DISTINCT {dimension} AS option, COUNT(*)
|
||||
FROM {table}
|
||||
WHERE {dimension} IS NOT NULL
|
||||
GROUP BY 1
|
||||
ORDER BY 2 DESC
|
||||
"""
|
||||
)
|
||||
|
||||
title = lookml_utils.slug_to_title(dimension_name)
|
||||
dimension_options = query_job.result().to_dataframe()[dimension_name].tolist()
|
||||
|
||||
dimension_kwarg = {
|
||||
"title": title,
|
||||
"name": dimension_name,
|
||||
}
|
||||
dimension_options = [dict(row) for row in query_job.result()]
|
||||
|
||||
if len(dimension_options) > 0:
|
||||
dimension_kwarg.update(
|
||||
{
|
||||
"default": dimension_options[0],
|
||||
"options": dimension_options[:10],
|
||||
}
|
||||
)
|
||||
dimension_defaults[dimension] = {
|
||||
"default": dimension_options[0]["option"],
|
||||
"options": [d["option"] for d in dimension_options[:10]],
|
||||
}
|
||||
|
||||
dimensions.append(dimension_kwarg)
|
||||
|
||||
return dimensions
|
||||
return dimension_defaults
|
||||
|
||||
|
||||
def get_xaxis_val(bq_client: bigquery.Client, table: str) -> str:
|
||||
|
@ -70,3 +52,21 @@ def get_xaxis_val(bq_client: bigquery.Client, table: str) -> str:
|
|||
if "build_id" in {dimension["name"] for dimension in all_dimensions}
|
||||
else "submission_date"
|
||||
)
|
||||
|
||||
|
||||
def get_projects(
|
||||
bq_client: bigquery.Client, project_table: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Select all operational monitoring projects."""
|
||||
try:
|
||||
query_job = bq_client.query(
|
||||
f"""
|
||||
SELECT *
|
||||
FROM `{project_table}`
|
||||
"""
|
||||
)
|
||||
|
||||
projects = [dict(row) for row in query_job.result()]
|
||||
except exceptions.Forbidden:
|
||||
projects = []
|
||||
return projects
|
||||
|
|
|
@ -3,10 +3,16 @@
|
|||
from textwrap import dedent
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from ..constants import OPMON_EXCLUDED_FIELDS
|
||||
from . import lookml_utils
|
||||
from .operational_monitoring_view import OperationalMonitoringView
|
||||
|
||||
ALLOWED_DIMENSIONS = {
|
||||
"branch",
|
||||
"probe",
|
||||
"histogram__VALUES__key",
|
||||
"histogram__VALUES__value",
|
||||
}
|
||||
|
||||
|
||||
class OperationalMonitoringHistogramView(OperationalMonitoringView):
|
||||
"""A view on a scalar operational monitoring table."""
|
||||
|
@ -40,12 +46,14 @@ class OperationalMonitoringHistogramView(OperationalMonitoringView):
|
|||
|
||||
reference_table = self.tables[0]["table"]
|
||||
all_dimensions = lookml_utils._generate_dimensions(bq_client, reference_table)
|
||||
additional_dimensions = [
|
||||
dimension
|
||||
for dimension in all_dimensions
|
||||
if dimension["name"] not in OPMON_EXCLUDED_FIELDS
|
||||
|
||||
filtered_dimensions = [
|
||||
d
|
||||
for d in all_dimensions
|
||||
if d["name"] in ALLOWED_DIMENSIONS
|
||||
or d["name"] in self.tables[0].get("dimensions", {}).keys()
|
||||
]
|
||||
self.dimensions.extend(additional_dimensions)
|
||||
self.dimensions.extend(filtered_dimensions)
|
||||
|
||||
return {
|
||||
"views": [
|
||||
|
|
|
@ -3,10 +3,14 @@
|
|||
from textwrap import dedent
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from ..constants import OPMON_EXCLUDED_FIELDS
|
||||
from . import lookml_utils
|
||||
from .operational_monitoring_view import OperationalMonitoringView
|
||||
|
||||
ALLOWED_DIMENSIONS = {
|
||||
"branch",
|
||||
"probe",
|
||||
}
|
||||
|
||||
|
||||
class OperationalMonitoringScalarView(OperationalMonitoringView):
|
||||
"""A view on a scalar operational monitoring table."""
|
||||
|
@ -40,12 +44,13 @@ class OperationalMonitoringScalarView(OperationalMonitoringView):
|
|||
|
||||
reference_table = self.tables[0]["table"]
|
||||
all_dimensions = lookml_utils._generate_dimensions(bq_client, reference_table)
|
||||
additional_dimensions = [
|
||||
dimension
|
||||
for dimension in all_dimensions
|
||||
if dimension["name"] not in OPMON_EXCLUDED_FIELDS
|
||||
filtered_dimensions = [
|
||||
d
|
||||
for d in all_dimensions
|
||||
if d["name"] in ALLOWED_DIMENSIONS
|
||||
or d["name"] in self.tables[0].get("dimensions", {}).keys()
|
||||
]
|
||||
self.dimensions.extend(additional_dimensions)
|
||||
self.dimensions.extend(filtered_dimensions)
|
||||
|
||||
return {
|
||||
"views": [
|
||||
|
|
|
@ -13,7 +13,7 @@ class OperationalMonitoringView(PingView):
|
|||
type: str = "operational_monitoring_view"
|
||||
percentile_ci_labels = ["percentile", "low", "high"]
|
||||
|
||||
def __init__(self, namespace: str, name: str, tables: List[Dict[str, str]]):
|
||||
def __init__(self, namespace: str, name: str, tables: List[Dict[str, Any]]):
|
||||
"""Create instance of a OperationalMonitoringView."""
|
||||
super().__init__(namespace, name, tables)
|
||||
xaxis = "build_id"
|
||||
|
|
|
@ -14,7 +14,7 @@ class PingView(View):
|
|||
type: str = "ping_view"
|
||||
allow_glean: bool = False
|
||||
|
||||
def __init__(self, namespace: str, name: str, tables: List[Dict[str, str]]):
|
||||
def __init__(self, namespace: str, name: str, tables: List[Dict[str, Any]]):
|
||||
"""Create instance of a PingView."""
|
||||
super().__init__(namespace, name, self.__class__.type, tables)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class View(object):
|
|||
|
||||
name: str
|
||||
view_type: str
|
||||
tables: List[Dict[str, str]]
|
||||
tables: List[Dict[str, Any]]
|
||||
namespace: str
|
||||
|
||||
def __init__(
|
||||
|
@ -28,7 +28,7 @@ class View(object):
|
|||
namespace: str,
|
||||
name: str,
|
||||
view_type: str,
|
||||
tables: List[Dict[str, str]],
|
||||
tables: List[Dict[str, Any]],
|
||||
**kwargs,
|
||||
):
|
||||
"""Create an instance of a view."""
|
||||
|
@ -72,7 +72,7 @@ class View(object):
|
|||
"""Check for equality with other View."""
|
||||
|
||||
def comparable_dict(d):
|
||||
return {tuple(sorted(t.items())) for t in d}
|
||||
return {tuple(sorted([(k, str(v)) for k, v in t.items()])) for t in d}
|
||||
|
||||
if isinstance(other, View):
|
||||
return (
|
||||
|
|
|
@ -43,6 +43,10 @@ def custom_namespaces(tmp_path):
|
|||
owners:
|
||||
- opmon-owner@allizom.com
|
||||
pretty_name: Operational Monitoring
|
||||
views:
|
||||
projects:
|
||||
tables:
|
||||
- table: mozdata.operational_monitoring.projects
|
||||
custom:
|
||||
connection: bigquery-oauth
|
||||
glean_app: false
|
||||
|
@ -99,35 +103,53 @@ def namespace_disallowlist(tmp_path):
|
|||
return dest.absolute()
|
||||
|
||||
|
||||
class MockBlob:
|
||||
"""Mock Blob."""
|
||||
class MockClient:
|
||||
"""Mock bigquery.Client."""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.updated = "2021-05-01"
|
||||
def query(self, query):
|
||||
class QueryJob:
|
||||
def result(self):
|
||||
print(query)
|
||||
if "os AS option" in query:
|
||||
return [
|
||||
{
|
||||
"option": "Windows",
|
||||
"count": "10",
|
||||
},
|
||||
{
|
||||
"option": "Linux",
|
||||
"count": "1",
|
||||
},
|
||||
]
|
||||
elif "cores_count AS option" in query:
|
||||
return [
|
||||
{
|
||||
"option": "4",
|
||||
"count": "10",
|
||||
},
|
||||
{
|
||||
"option": "1",
|
||||
"count": "1",
|
||||
},
|
||||
]
|
||||
else:
|
||||
return [
|
||||
{
|
||||
"slug": "op-mon",
|
||||
"name": "OpMon",
|
||||
"branches": ["enabled", "disabled"],
|
||||
"xaxis": "submission_date",
|
||||
"probes": [
|
||||
{"name": "GC_MS", "agg_type": "histogram"},
|
||||
{"name": "GC_MS_CONTENT", "agg_type": "histogram"},
|
||||
],
|
||||
"dimensions": {
|
||||
"cores_count": {"default": "4", "options": ["4", "1"]}
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
def download_as_string(self):
|
||||
return '{"slug": "test", "name": "op_mon", "xaxis": "build_id"}'
|
||||
|
||||
|
||||
class MockBucket:
|
||||
"""Mock Bucket."""
|
||||
|
||||
def list_blobs(self, prefix):
|
||||
return [MockBlob("test")]
|
||||
|
||||
def get_blob(self, filename):
|
||||
return MockBlob("test")
|
||||
|
||||
|
||||
class MockStorageClient:
|
||||
"""Mock storage.Client."""
|
||||
|
||||
def __init__(self, project_name):
|
||||
pass
|
||||
|
||||
def get_bucket(self, bucket_name):
|
||||
return MockBucket()
|
||||
return QueryJob()
|
||||
|
||||
|
||||
def add_to_tar(tar, path, content):
|
||||
|
@ -193,7 +215,7 @@ def test_namespaces_full(
|
|||
app_listings_uri,
|
||||
namespace_disallowlist,
|
||||
):
|
||||
with patch("google.cloud.storage.Client", MockStorageClient):
|
||||
with patch("google.cloud.bigquery.Client", MockClient):
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(
|
||||
namespaces,
|
||||
|
@ -385,32 +407,64 @@ def test_namespaces_full(
|
|||
},
|
||||
"operational_monitoring": {
|
||||
"dashboards": {
|
||||
"op_mon": {
|
||||
"opmon": {
|
||||
"tables": [
|
||||
{
|
||||
"explore": "op_mon_histogram",
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.test_histogram",
|
||||
"explore": "opmon_histogram",
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.op_mon_histogram",
|
||||
"branches": ["enabled", "disabled"],
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"xaxis": "submission_date",
|
||||
"probes": ["GC_MS", "GC_MS_CONTENT"],
|
||||
},
|
||||
{
|
||||
"explore": "op_mon_scalar",
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.test_scalar",
|
||||
"explore": "opmon_scalar",
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.op_mon_scalar",
|
||||
"branches": ["enabled", "disabled"],
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"xaxis": "submission_date",
|
||||
"probes": [],
|
||||
},
|
||||
],
|
||||
"type": "operational_monitoring_dashboard",
|
||||
}
|
||||
},
|
||||
"explores": {
|
||||
"op_mon_histogram": {
|
||||
"opmon_histogram": {
|
||||
"branches": ["enabled", "disabled"],
|
||||
"type": "operational_monitoring_explore",
|
||||
"views": {"base_view": "op_mon_histogram"},
|
||||
"views": {"base_view": "opmon_histogram"},
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"xaxis": "submission_date",
|
||||
"probes": ["GC_MS", "GC_MS_CONTENT"],
|
||||
},
|
||||
"op_mon_scalar": {
|
||||
"opmon_scalar": {
|
||||
"branches": ["enabled", "disabled"],
|
||||
"type": "operational_monitoring_explore",
|
||||
"views": {"base_view": "op_mon_scalar"},
|
||||
"views": {"base_view": "opmon_scalar"},
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"xaxis": "submission_date",
|
||||
"probes": [],
|
||||
},
|
||||
},
|
||||
"glean_app": False,
|
||||
|
@ -418,20 +472,32 @@ def test_namespaces_full(
|
|||
"pretty_name": "Operational Monitoring",
|
||||
"spoke": "looker-spoke-default",
|
||||
"views": {
|
||||
"op_mon_histogram": {
|
||||
"opmon_histogram": {
|
||||
"tables": [
|
||||
{
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.test_histogram",
|
||||
"xaxis": "build_id",
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.op_mon_histogram",
|
||||
"xaxis": "submission_date",
|
||||
}
|
||||
],
|
||||
"type": "operational_monitoring_histogram_view",
|
||||
},
|
||||
"op_mon_scalar": {
|
||||
"opmon_scalar": {
|
||||
"tables": [
|
||||
{
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.test_scalar",
|
||||
"xaxis": "build_id",
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
}
|
||||
},
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.op_mon_scalar",
|
||||
"xaxis": "submission_date",
|
||||
}
|
||||
],
|
||||
"type": "operational_monitoring_scalar_view",
|
||||
|
|
|
@ -16,61 +16,6 @@ from generator.views import (
|
|||
|
||||
from .utils import print_and_test
|
||||
|
||||
TABLE_HISTOGRAM = (
|
||||
"moz-fx-data-shared-prod."
|
||||
"operational_monitoring."
|
||||
"bug_1660366_pref_ongoing_fission_nightly_experiment_nightly_83_100_histogram"
|
||||
)
|
||||
|
||||
TABLE_SCALAR = (
|
||||
"moz-fx-data-shared-prod."
|
||||
"operational_monitoring."
|
||||
"bug_1660366_pref_ongoing_fission_nightly_experiment_nightly_83_100_scalar"
|
||||
)
|
||||
|
||||
DATA = {
|
||||
"compute_opmon_dimensions": {
|
||||
"fission_histogram": {
|
||||
(
|
||||
"moz-fx-data-shared-prod.operational_monitoring."
|
||||
"bug_1660366_pref_ongoing_fission_nightly_experiment_nightly_83_100_histogram"
|
||||
): [
|
||||
{
|
||||
"title": "Cores Count",
|
||||
"name": "cores_count",
|
||||
"default": "4",
|
||||
"options": ["1", "2", "3", "4", "6", "8", "10", "12", "16", "32"],
|
||||
},
|
||||
{
|
||||
"title": "Os",
|
||||
"name": "os",
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Mac", "Linux"],
|
||||
},
|
||||
]
|
||||
},
|
||||
"fission_scalar": {
|
||||
(
|
||||
"moz-fx-data-shared-prod.operational_monitoring."
|
||||
"bug_1660366_pref_ongoing_fission_nightly_experiment_nightly_83_100_scalar"
|
||||
): [
|
||||
{
|
||||
"title": "Cores Count",
|
||||
"name": "cores_count",
|
||||
"default": "4",
|
||||
"options": ["1", "2", "3", "4", "6", "8", "10", "12", "16", "32"],
|
||||
},
|
||||
{
|
||||
"title": "Os",
|
||||
"name": "os",
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Mac", "Linux"],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockClient:
|
||||
"""Mock bigquery.Client."""
|
||||
|
@ -100,7 +45,7 @@ class MockClient:
|
|||
def get_table(self, table_ref):
|
||||
"""Mock bigquery.Client.get_table."""
|
||||
|
||||
if table_ref == TABLE_HISTOGRAM:
|
||||
if "histogram" in table_ref:
|
||||
return bigquery.Table(
|
||||
table_ref,
|
||||
schema=[
|
||||
|
@ -131,7 +76,7 @@ class MockClient:
|
|||
],
|
||||
)
|
||||
|
||||
if table_ref == TABLE_SCALAR:
|
||||
if "scalar" in table_ref:
|
||||
return bigquery.Table(
|
||||
table_ref,
|
||||
schema=[
|
||||
|
@ -154,7 +99,22 @@ def operational_monitoring_histogram_view():
|
|||
return OperationalMonitoringHistogramView(
|
||||
"operational_monitoring",
|
||||
"fission_histogram",
|
||||
[{"table": TABLE_HISTOGRAM, "xaxis": "build_id"}],
|
||||
[
|
||||
{
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.bug_123_test_histogram",
|
||||
"xaxis": "build_id",
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
},
|
||||
"os": {
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Linux"],
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
@ -163,7 +123,22 @@ def operational_monitoring_scalar_view():
|
|||
return OperationalMonitoringScalarView(
|
||||
"operational_monitoring",
|
||||
"fission_scalar",
|
||||
[{"table": TABLE_SCALAR, "xaxis": "submission_date"}],
|
||||
[
|
||||
{
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.bug_123_test_scalar",
|
||||
"xaxis": "submission_date",
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
},
|
||||
"os": {
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Linux"],
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
@ -176,7 +151,21 @@ def operational_monitoring_explore(tmp_path, operational_monitoring_histogram_vi
|
|||
"fission_histogram",
|
||||
{"base_view": "fission_histogram"},
|
||||
tmp_path,
|
||||
{"branches": ["enabled", "disabled"]},
|
||||
{
|
||||
"branches": ["enabled", "disabled"],
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
},
|
||||
"os": {
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Linux"],
|
||||
},
|
||||
},
|
||||
"probes": ["GC_MS", "GC_MS_CONTENT"],
|
||||
"xaxis": "build_id",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
@ -189,10 +178,22 @@ def operational_monitoring_dashboard():
|
|||
"operational_monitoring",
|
||||
[
|
||||
{
|
||||
"table": TABLE_HISTOGRAM,
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.bug_123_test_histogram",
|
||||
"explore": "fission_histogram",
|
||||
"branches": ["enabled", "disabled"],
|
||||
}
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
},
|
||||
"os": {
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Linux"],
|
||||
},
|
||||
},
|
||||
"xaxis": "build_id",
|
||||
"probes": ["GC_MS", "GC_MS_CONTENT"],
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -203,7 +204,22 @@ def test_view_from_dict(operational_monitoring_histogram_view):
|
|||
"fission_histogram",
|
||||
{
|
||||
"type": "operational_monitoring_histogram_view",
|
||||
"tables": [{"table": TABLE_HISTOGRAM, "xaxis": "build_id"}],
|
||||
"tables": [
|
||||
{
|
||||
"table": "moz-fx-data-shared-prod.operational_monitoring.bug_123_test_histogram",
|
||||
"xaxis": "build_id",
|
||||
"dimensions": {
|
||||
"cores_count": {
|
||||
"default": "4",
|
||||
"options": ["4", "1"],
|
||||
},
|
||||
"os": {
|
||||
"default": "Windows",
|
||||
"options": ["Windows", "Linux"],
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -217,8 +233,7 @@ def test_histogram_view_lookml(operational_monitoring_histogram_view):
|
|||
{
|
||||
"name": "fission_histogram",
|
||||
"sql_table_name": (
|
||||
"moz-fx-data-shared-prod.operational_monitoring.bug_1660366_"
|
||||
"pref_ongoing_fission_nightly_experiment_nightly_83_100_histogram"
|
||||
"moz-fx-data-shared-prod.operational_monitoring.bug_123_test_histogram"
|
||||
),
|
||||
"parameters": operational_monitoring_histogram_view.parameters,
|
||||
"measures": [
|
||||
|
@ -273,7 +288,7 @@ def test_scalar_view_lookml(operational_monitoring_scalar_view):
|
|||
"sql": dedent(
|
||||
f"""
|
||||
SELECT *
|
||||
FROM `{TABLE_SCALAR}`
|
||||
FROM `{"moz-fx-data-shared-prod.operational_monitoring.bug_123_test_scalar"}`
|
||||
WHERE agg_type = "SUM"
|
||||
"""
|
||||
)
|
||||
|
@ -351,7 +366,7 @@ def test_explore_lookml(operational_monitoring_explore):
|
|||
}
|
||||
]
|
||||
|
||||
actual = operational_monitoring_explore.to_lookml(mock_bq_client, None, DATA)
|
||||
actual = operational_monitoring_explore.to_lookml(mock_bq_client, None)
|
||||
print_and_test(expected=expected, actual=actual)
|
||||
|
||||
|
||||
|
@ -460,16 +475,8 @@ def test_dashboard_lookml(operational_monitoring_dashboard):
|
|||
type: dropdown_menu
|
||||
display: inline
|
||||
options:
|
||||
- '1'
|
||||
- '2'
|
||||
- '3'
|
||||
- '4'
|
||||
- '6'
|
||||
- '8'
|
||||
- '10'
|
||||
- '12'
|
||||
- '16'
|
||||
- '32'
|
||||
- '1'
|
||||
|
||||
- title: Os
|
||||
name: Os
|
||||
|
@ -482,11 +489,10 @@ def test_dashboard_lookml(operational_monitoring_dashboard):
|
|||
display: inline
|
||||
options:
|
||||
- 'Windows'
|
||||
- 'Mac'
|
||||
- 'Linux'
|
||||
|
||||
"""
|
||||
)
|
||||
actual = operational_monitoring_dashboard.to_lookml(mock_bq_client, DATA)
|
||||
actual = operational_monitoring_dashboard.to_lookml(mock_bq_client)
|
||||
|
||||
print_and_test(expected=expected, actual=dedent(actual))
|
||||
|
|
Загрузка…
Ссылка в новой задаче