Skip to content

Commit 3b9b320

Browse files
authored
Initial version of defining the interfaces to accept metrics (#15913)
1 parent b92af5a commit 3b9b320

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from reporter_factory import TelemetryReporterFactory
2+
from metrics import (
3+
GaugeMetric,
4+
METRIC_LABEL_TESTBED,
5+
METRIC_LABEL_TEST_BUILD,
6+
METRIC_LABEL_TEST_CASE,
7+
METRIC_LABEL_TEST_FILE,
8+
METRIC_LABEL_TEST_JOBID,
9+
METRIC_LABEL_DEVICE_ID,
10+
METRIC_LABEL_DEVICE_PSU_ID,
11+
METRIC_LABEL_DEVICE_PSU_MODEL,
12+
METRIC_LABEL_DEVICE_PSU_SERIAL
13+
)
14+
15+
16+
def main():
17+
"""
18+
19+
PSU Model Serial HW Rev Voltage (V) Current (A) Power (W) Status LED
20+
----- --------------- --------------- -------- ------------- ------------- ----------- -------- -----
21+
PSU 1 PWR-ABCD 1Z011010112349Q 01 12.09 18.38 222.00 OK green
22+
PSU 2 PWR-ABCD 1Z011010156787X 01 12.01 17.72 214.00 OK green
23+
24+
"""
25+
common_labels = {
26+
METRIC_LABEL_TESTBED: "TB-XYZ",
27+
METRIC_LABEL_TEST_BUILD: "2024.1103",
28+
METRIC_LABEL_TEST_CASE: "mock-case",
29+
METRIC_LABEL_TEST_FILE: "mock-test.py",
30+
METRIC_LABEL_TEST_JOBID: "2024_1225_0621"
31+
}
32+
33+
# Create a MetricReporterFactory and build a MetricReporter
34+
reporter = TelemetryReporterFactory.create_periodic_metrics_reporter(common_labels)
35+
36+
metric_labels = {METRIC_LABEL_DEVICE_ID: "switch-A"}
37+
38+
# Create a metric
39+
voltage = GaugeMetric(name="Voltage",
40+
description="Power supply unit voltage reading",
41+
unit="V",
42+
reporter=reporter)
43+
44+
# Create a metric
45+
current = GaugeMetric(name="Current",
46+
description="Power supply unit current reading",
47+
unit="A",
48+
reporter=reporter)
49+
50+
# Create a metric
51+
power = GaugeMetric(name="Power",
52+
description="Power supply unit power reading",
53+
unit="W",
54+
reporter=reporter)
55+
56+
# Pass metrics to the reporter
57+
metric_labels[METRIC_LABEL_DEVICE_PSU_ID] = "PSU 1"
58+
metric_labels[METRIC_LABEL_DEVICE_PSU_MODEL] = "PWR-ABCD"
59+
metric_labels[METRIC_LABEL_DEVICE_PSU_SERIAL] = "1Z011010112349Q"
60+
voltage.record(metric_labels, 12.09)
61+
current.record(metric_labels, 18.38)
62+
power.record(metric_labels, 222.00)
63+
64+
# Pass metrics to the reporter
65+
metric_labels[METRIC_LABEL_DEVICE_PSU_ID] = "PSU 2"
66+
metric_labels[METRIC_LABEL_DEVICE_PSU_MODEL] = "PWR-ABCD"
67+
metric_labels[METRIC_LABEL_DEVICE_PSU_SERIAL] = "1Z011010156787X"
68+
voltage.record(metric_labels, 12.01)
69+
current.record(metric_labels, 17.72)
70+
power.record(metric_labels, 214.00)
71+
72+
# Report all metrics at a specific timestamp
73+
reporter.report()
74+
75+
76+
if __name__ == '__main__':
77+
main()
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""
2+
This file defines the classes receiving metrics from snappi tests and processing them.
3+
"""
4+
import time
5+
6+
from copy import deepcopy
7+
from typing import Dict, Final, Union
8+
9+
# Only certain labels are allowed
10+
METRIC_LABEL_TESTBED: Final[str] = "test.testbed" # testbed name/ID
11+
METRIC_LABEL_TEST_BUILD: Final[str] = "test.os.version" # Software/build version
12+
METRIC_LABEL_TEST_CASE: Final[str] = "test.testcase" # test case name/ID
13+
METRIC_LABEL_TEST_FILE: Final[str] = "test.file" # test file name
14+
METRIC_LABEL_TEST_JOBID: Final[str] = "test.job.id" # test job ID
15+
METRIC_LABEL_DEVICE_ID: Final[str] = "device.id" # device refers to the level of switch
16+
METRIC_LABEL_DEVICE_PORT_ID: Final[str] = "device.port.id"
17+
METRIC_LABEL_DEVICE_PSU_ID: Final[str] = "device.psu.id"
18+
METRIC_LABEL_DEVICE_PSU_MODEL: Final[str] = "device.psu.model"
19+
METRIC_LABEL_DEVICE_PSU_SERIAL: Final[str] = "device.psu.serial"
20+
METRIC_LABEL_DEVICE_PSU_HW_REV: Final[str] = "device.psu.hw_rev" # hardware revision
21+
METRIC_LABEL_DEVICE_QUEUE_ID: Final[str] = "device.queue.id"
22+
METRIC_LABEL_DEVICE_QUEUE_CAST: Final[str] = "device.queue.cast" # unicast or multicast
23+
METRIC_LABEL_DEVICE_SENSOR_ID: Final[str] = "device.sensor.id"
24+
METRIC_LABEL_DEVICE_PG_ID: Final[str] = "device.pg.id" # priority group
25+
METRIC_LABEL_DEVICE_BUFFER_POOL_ID: Final[str] = "device.buffer_pool.id"
26+
27+
28+
class PeriodicMetricsReporter:
29+
def __init__(self, common_labels: Dict[str, str]):
30+
# Will be replaced with a real initializer such as OpenTelemetry
31+
self.common_labels = deepcopy(common_labels)
32+
self.metrics = []
33+
34+
def stash_record(self, new_metric: 'Metric', labels: Dict[str, str], value: Union[int, float]):
35+
# add a new periodic metric
36+
copied_labels = deepcopy(labels)
37+
self.metrics.append({"labels": copied_labels, "value": value})
38+
39+
def report(self, timestamp=time.time_ns()):
40+
"""
41+
Report metrics at a given timestamp.
42+
The input timestamp must be UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970
43+
44+
# save the metrics in a local variable and release the metrics in the object
45+
stashed_metrics = self.metrics
46+
self.metrics = []
47+
48+
"""
49+
pass
50+
51+
52+
class FinalMetricsReporter:
53+
def __init__(self, common_labels: Dict[str, str]):
54+
# Will be replaced with a real initializer such as Kusto
55+
self.common_labels = deepcopy(common_labels)
56+
self.metrics = []
57+
58+
def stash_record(self, new_metric: 'Metric', labels: Dict[str, str], value: Union[int, float]):
59+
# add a new final metric
60+
copied_labels = deepcopy(labels)
61+
self.metrics.append({"labels": copied_labels, "value": value})
62+
63+
def report(self, timestamp=time.time_ns()):
64+
"""
65+
Report metrics at a given timestamp.
66+
The input timestamp must be UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970
67+
68+
# save the metrics in a local variable and release the metrics in the object
69+
stashed_metrics = self.metrics
70+
self.metrics = []
71+
72+
"""
73+
pass
74+
75+
76+
class Metric:
77+
def __init__(self,
78+
name: str,
79+
description: str,
80+
unit: str,
81+
reporter: PeriodicMetricsReporter):
82+
"""
83+
Args:
84+
name (str): metric name (e.g., psu power, sensor temperature, port stats, etc.)
85+
description (str): brief description of the metric
86+
unit (str): metric unit (e.g., seconds, bytes)
87+
reporter (PeriodicMetricsReporter): object of PeriodicMetricsReporter
88+
"""
89+
self.name = name
90+
self.description = description
91+
self.unit = unit
92+
self.reporter = reporter
93+
94+
def __repr__(self):
95+
return (f"Metric(name={self.name!r}, "
96+
f"description={self.description!r}, "
97+
f"unit={self.unit!r}, "
98+
f"reporter={self.reporter})")
99+
100+
101+
class GaugeMetric(Metric):
102+
def __init__(self,
103+
name: str,
104+
description: str,
105+
unit: str,
106+
reporter: PeriodicMetricsReporter):
107+
# Initialize the base class
108+
super().__init__(name, description, unit, reporter)
109+
110+
def record(self, metric_labels: Dict[str, str], value: Union[int, float]):
111+
# Save the metric into the reporter
112+
self.reporter.stash_record(self, metric_labels, value)
113+
114+
def __repr__(self):
115+
return (f"GaugeMetric(name={self.name!r}, "
116+
f"description={self.description!r}, "
117+
f"unit={self.unit!r}, "
118+
f"reporter={self.reporter})")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Dict
2+
from metrics import PeriodicMetricsReporter, FinalMetricsReporter
3+
4+
5+
class TelemetryReporterFactory:
6+
def __init__(self):
7+
return
8+
9+
@staticmethod
10+
def create_periodic_metrics_reporter(common_labels: Dict[str, str]):
11+
return (PeriodicMetricsReporter(common_labels))
12+
13+
@staticmethod
14+
def create_final_metrics_reporter(common_labels: Dict[str, str]):
15+
return (FinalMetricsReporter(common_labels))

0 commit comments

Comments
 (0)