1 # ==================================================================================
2 # Copyright (c) 2020 AT&T Intellectual Property.
3 # Copyright (c) 2020 Nokia
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 # ==================================================================================
18 Provides classes and methods to define and send metrics as RMR messages to a
19 central collector. Message destination(s) are controlled by the RMR routing table.
20 Message contents must comply with the JSON schema in file metric-schema.json.
23 from ctypes import c_void_p
26 from mdclogpy import Logger
27 from ricxappframe.rmr import rmr
28 from ricxappframe.metric.exceptions import EmptyReport
34 mdc_logger = Logger(name=__name__)
42 RIC_METRICS = 120 # message type
44 # Publish dict keys as constants for convenience of client code.
45 KEY_REPORTER = "reporter"
46 KEY_GENERATOR = "generator"
47 KEY_TIMESTAMP = "timestamp"
50 KEY_DATA_TYPE = "type"
51 KEY_DATA_VALUE = "value"
54 class MetricData(dict):
56 A single measurement with ID, value and (optionally) type.
63 Creates a data item with the specified members.
71 Metric value; e.g., 1.
74 Metric type; e.g., "counter".
77 self[KEY_DATA_ID] = id
78 self[KEY_DATA_VALUE] = value
79 self[KEY_DATA_TYPE] = type
82 class MetricsReport(dict):
84 A list of metric data items with identifying information.
85 At init sets the timestamp to the current system time in
86 milliseconds since the Epoch.
90 reporter: str (optional)
91 The system that reports the data
93 generator: str (optional)
94 The generator that reports the data
96 items: List of MetricData (optional)
97 The data items for the report
100 reporter: str = None,
101 generator: str = None,
104 Creates an object with the specified details and items.
107 self[KEY_REPORTER] = reporter
108 self[KEY_GENERATOR] = generator
109 self[KEY_TIMESTAMP] = int(round(time.time() * 1000))
110 self[KEY_DATA] = [] if items is None else items
115 Convenience method that adds a data item to the report.
120 A measurement to add to the report
122 self[KEY_DATA].append(data)
125 class MetricsManager:
127 Provides an API for an Xapp to build and send measurement reports
128 by sending messages via RMR routing to a metrics adapter/collector.
132 vctx: ctypes c_void_p (required)
133 Pointer to RMR context obtained by initializing RMR.
134 The context is used to allocate space and send messages.
136 reporter: str (optional)
137 The source of the measurement; e.g., a temperature probe
139 generator: str (optional)
140 The system that collected and sent the measurement; e.g., an environment monitor.
144 reporter: str = None,
145 generator: str = None):
147 Creates a metrics manager.
150 self.reporter = reporter
151 self.generator = generator
153 def create_report(self,
156 Creates a MetricsReport object with the specified metrics data items.
160 items: list (optional)
161 List of MetricData items
167 return MetricsReport(self.reporter, self.generator, items)
169 def send_report(self, msg: MetricsReport):
171 Serializes the MetricsReport dict to JSON and sends the result via RMR.
172 Raises an exception if the report has no MetricsData items.
176 msg: MetricsReport (required)
177 Dictionary with measurement data to encode and send
182 True if the send succeeded (possibly with retries), False otherwise
184 if KEY_DATA not in msg or len(msg[KEY_DATA]) == 0:
186 payload = json.dumps(msg).encode()
187 mdc_logger.debug("send_report: payload is {}".format(payload))
188 sbuf = rmr.rmr_alloc_msg(vctx=self.vctx, size=len(payload), payload=payload,
189 mtype=RIC_METRICS, gen_transaction_id=True)
191 for _ in range(0, RETRIES):
192 sbuf = rmr.rmr_send_msg(self.vctx, sbuf)
193 post_send_summary = rmr.message_summary(sbuf)
194 mdc_logger.debug("send_report: try {0} result is {1}".format(_, post_send_summary[rmr.RMR_MS_MSG_STATE]))
195 # stop trying if RMR does not indicate retry
196 if post_send_summary[rmr.RMR_MS_MSG_STATE] != rmr.RMR_ERR_RETRY:
199 rmr.rmr_free_msg(sbuf)
200 if post_send_summary[rmr.RMR_MS_MSG_STATE] != rmr.RMR_OK:
201 mdc_logger.warning("send_report: failed after {} retries".format(RETRIES))
206 def send_metrics(self, items: list):
208 Convenience method that creates a MetricsReport object with the specified
209 metrics data items and sends it to the metrics adapter/collector.
213 items: list (required)
214 List of MetricData items
219 True if the send succeeded (possibly with retries), False otherwise
221 return self.send_report(self.create_report(items))