1 # Copyright (c) 2022 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """VPP execution bundle."""
16 from logging import getLogger
17 from re import fullmatch, sub
21 from .constants import Constants
25 r"(?P<thread_id>\d+)\s"
26 r"(?P<thread_name>\S+)\s.*"
27 r"(?P<thread_lcore>\d+).*"
34 r"(?P<state>\S+\s\S+|\S+)\s+"
36 r"(?P<vectors>\d+)\s+"
37 r"(?P<suspends>\d+)\s+"
39 r"(?P<vectors_calls>\S+)"
42 r"Time\s\S+,\s\d+\ssec\sinternal\snode\svector\srate\s"
43 r"(?P<rate>\S+)\sloops/sec\s"
51 r"(?P<counter>\S+\s\S+|\S+)\s+"
56 r"(?P<counter>\S+\s\S+|\S+)\s+"
59 M_NODE_COUNTERS_THREAD = (
61 r"(?P<thread_id>\d+)\s\("
62 r"(?P<thread_name>\S+)\):\s*"
68 r"(?P<reason>(\S+\s)+)\s+"
69 r"(?P<severity>\S+)\s+"
73 r"\s*per-thread\s+context\s+switches.*"
76 r"(?P<thread_name>\S+)\s+\("
77 r"(?P<thread_id>\S+)\)\s+\S+\s+"
78 r"(?P<context_switches>[\d\.]+)"
81 r"\s*per-thread\s+page\s+faults.*"
84 r"(?P<thread_name>\S+)\s+\("
85 r"(?P<thread_id>\S+)\)\s+\S+\s+"
86 r"(?P<minor_page_faults>[\d\.]+)\s+"
87 r"(?P<major_page_faults>[\d\.]+)"
91 r"(?P<thread_name>\S+)\s+\("
92 r"(?P<thread_id>\d+)\)\s*"
95 r"\s*instructions/packet,\s+cycles/packet\s+and\s+IPC.*"
99 r"(?P<node_name>\S+)\s+"
100 r"(?P<calls>[\d\.]+)\s+"
101 r"(?P<packets>[\d\.]+)\s+"
102 r"(?P<packets_per_call>[\d\.]+)\s+"
103 r"(?P<clocks_per_packets>[\d\.]+)\s+"
104 r"(?P<instructions_per_packets>[\d\.]+)\s+"
108 r"\s*cache\s+hits\s+and\s+misses.*"
112 r"(?P<node_name>\S+)\s+"
113 r"(?P<l1_hit>[\d\.]+)\s+"
114 r"(?P<l1_miss>[\d\.]+)\s+"
115 r"(?P<l2_hit>[\d\.]+)\s+"
116 r"(?P<l2_miss>[\d\.]+)\s+"
117 r"(?P<l3_hit>[\d\.]+)\s+"
118 r"(?P<l3_miss>[\d\.]+)"
121 r"\s*load\s+operations.*"
125 r"(?P<node_name>\S+)\s+"
126 r"(?P<calls>[\d\.]+)\s+"
127 r"(?P<packets>[\d\.]+)\s+"
128 r"(?P<one>[\d\.]+)\s+"
129 r"(?P<two>[\d\.]+)\s+"
130 r"(?P<three>[\d\.]+)"
133 r"\s*Branches,\s+branches\s+taken\s+and\s+mis-predictions.*"
137 r"(?P<node_name>\S+)\s+"
138 r"(?P<branches_per_call>[\d\.]+)\s+"
139 r"(?P<branches_per_packet>[\d\.]+)\s+"
140 r"(?P<taken_per_call>[\d\.]+)\s+"
141 r"(?P<taken_per_packet>[\d\.]+)\s+"
142 r"(?P<mis_predictions>[\d\.]+)"
145 r"\s*memory\s+reads\s+and\s+writes\s+per\s+memory\s+controller.*"
150 r"(?P<runtime>[\d\.]+)\s+"
151 r"(?P<reads_mbs>[\d\.]+)\s+"
152 r"(?P<writes_mbs>[\d\.]+)\s+"
153 r"(?P<total_mbs>[\d\.]+)"
159 Creates a VPP object. This is the main object for defining a VPP program,
160 and interacting with its output.
162 def __init__(self, program, serializer, hook):
164 Initialize Bundle VPP class.
166 :param program: VPP instructions.
167 :param serializer: Metric serializer.
168 :param hook: VPP CLI socket.
170 :type serializer: Serializer
174 self.code = program["code"]
175 self.metrics = program["metrics"]
176 self.cli_command_list = list()
177 self.cli_replies_list = list()
178 self.serializer = serializer
181 def attach(self, duration):
183 Attach events to VPP.
185 :param duration: Trial duration.
188 for command in self.code.splitlines():
189 self.cli_command_list.append(
190 command.format(duration=duration, socket=self.hook)
198 def fetch_data(self):
200 Fetch data by invoking subprocess calls.
202 for command in self.cli_command_list:
204 getLogger(__name__).info(command)
205 replies = subprocess.getoutput(command)
206 except (AssertionError, AttributeError):
207 getLogger("console_stderr").error(
208 f"Failed when executing command: {command}"
210 sys.exit(Constants.err_vpp_execute)
212 self.cli_replies_list.append(replies)
213 replies = sub(r"\x1b[^m]*m", "", replies)
215 getLogger(__name__).info(replies)
217 getLogger(__name__).info("<no reply>")
218 self.serializer.create(metrics=self.metrics)
220 def process_data(self):
222 Post process command reply.
224 for command in zip(self.cli_command_list, self.cli_replies_list):
225 self_fn = command[0].replace(
226 f"-s {self.hook} ", "").replace(" ", "_")
227 self_method_list = [meth for meth in dir(self)
228 if callable(getattr(self, meth)) and
229 meth.startswith('__') is False]
230 if self_fn not in self_method_list:
233 self_fn = getattr(self, self_fn)
235 except AttributeError:
237 except (KeyError, ValueError, TypeError) as exc:
238 getLogger("console_stderr").error(
239 f"Failed when processing data. Error message {exc}"
241 sys.exit(Constants.err_telemetry_process)
243 def vppctl_show_interface(self, reply):
245 Parse the show interface output.
247 :param reply: VPP reply.
250 for line in reply.splitlines():
253 if fullmatch(M_INT_BEGIN, line):
254 ifc = fullmatch(M_INT_BEGIN, line).groupdict()
255 metric = ifc["counter"].replace(" ", "_").replace("-", "_")
256 item["name"] = metric
257 item["value"] = ifc["count"]
258 if fullmatch(M_INT_CONT, line):
259 ifc_cnt = fullmatch(M_INT_CONT, line).groupdict()
260 metric = ifc_cnt["counter"].replace(" ", "_").replace("-", "_")
261 item["name"] = metric
262 item["value"] = ifc_cnt["count"]
263 if fullmatch(M_INT_BEGIN, line) or fullmatch(M_INT_CONT, line):
264 labels["name"] = ifc["name"]
265 labels["index"] = ifc["index"]
266 item["labels"] = labels
267 self.serializer.serialize(
268 metric=metric, labels=labels, item=item
271 def vppctl_show_runtime(self, reply):
273 Parse the show runtime output.
275 :param reply: VPP reply.
278 for line in reply.splitlines():
279 if fullmatch(M_RUN_THREAD, line):
280 thread = fullmatch(M_RUN_THREAD, line).groupdict()
281 if fullmatch(M_RUN_NODES, line):
282 nodes = fullmatch(M_RUN_NODES, line).groupdict()
283 for metric in self.serializer.metric_registry:
286 item["name"] = metric
287 labels["name"] = nodes["name"]
288 labels["state"] = nodes["state"]
290 labels["thread_name"] = thread["thread_name"]
291 labels["thread_id"] = thread["thread_id"]
292 labels["thread_lcore"] = thread["thread_lcore"]
293 except UnboundLocalError:
294 labels["thread_name"] = "vpp_main"
295 labels["thread_id"] = "0"
296 labels["thread_lcore"] = "0"
297 item["labels"] = labels
298 item["value"] = nodes[metric]
299 self.serializer.serialize(
300 metric=metric, labels=labels, item=item
303 def vppctl_show_node_counters_verbose(self, reply):
305 Parse the show node conuter output.
307 :param reply: VPP reply.
310 for line in reply.splitlines():
311 if fullmatch(M_NODE_COUNTERS_THREAD, line):
312 thread = fullmatch(M_NODE_COUNTERS_THREAD, line).groupdict()
313 if fullmatch(M_NODE_COUNTERS, line):
314 nodes = fullmatch(M_NODE_COUNTERS, line).groupdict()
315 for metric in self.serializer.metric_registry_registry:
318 item["name"] = metric
319 labels["name"] = nodes["name"]
320 labels["reason"] = nodes["reason"]
321 labels["severity"] = nodes["severity"]
323 labels["thread_name"] = thread["thread_name"]
324 labels["thread_id"] = thread["thread_id"]
325 except UnboundLocalError:
326 labels["thread_name"] = "vpp_main"
327 labels["thread_id"] = "0"
328 item["labels"] = labels
329 item["value"] = nodes["count"]
330 self.serializer.serialize(
331 metric=metric, labels=labels, item=item
334 def vppctl_show_perfmon_statistics(self, reply):
336 Parse the perfmon output.
338 :param reply: VPP reply.
341 def perfmon_threads(reply, regex_threads):
342 for line in reply.splitlines():
343 if fullmatch(regex_threads, line):
344 threads = fullmatch(regex_threads, line).groupdict()
345 for metric in self.serializer.metric_registry:
348 item["name"] = metric
349 labels["name"] = threads["thread_name"]
350 labels["id"] = threads["thread_id"]
351 item["labels"] = labels
352 item["value"] = threads[metric]
353 self.serializer.serialize(
354 metric=metric, labels=labels, item=item
357 def perfmon_nodes(reply, regex_threads, regex_nodes):
358 for line in reply.splitlines():
359 if fullmatch(regex_threads, line):
360 thread = fullmatch(regex_threads, line).groupdict()
361 if fullmatch(regex_nodes, line):
362 node = fullmatch(regex_nodes, line).groupdict()
363 for metric in self.serializer.metric_registry:
366 item["name"] = metric
367 labels["name"] = node["node_name"]
368 labels["thread_name"] = thread["thread_name"]
369 labels["thread_id"] = thread["thread_id"]
370 item["labels"] = labels
371 item["value"] = node[metric]
372 self.serializer.serialize(
373 metric=metric, labels=labels, item=item
376 def perfmon_system(reply, regex_line):
377 for line in reply.splitlines():
378 if fullmatch(regex_line, line):
379 name = fullmatch(regex_line, line).groupdict()
380 for metric in self.serializer.metric_registry:
383 item["name"] = metric
384 labels["name"] = name["name"]
385 item["labels"] = labels
386 item["value"] = name[metric]
387 self.serializer.serialize(
388 metric=metric, labels=labels, item=item
391 reply = sub(r"\x1b[^m]*m", "", reply)
393 if fullmatch(M_PMB_CS_HEADER, reply.splitlines()[0]):
394 perfmon_threads(reply, M_PMB_CS)
395 if fullmatch(M_PMB_PF_HEADER, reply.splitlines()[0]):
396 perfmon_threads(reply, M_PMB_PF)
397 if fullmatch(M_PMB_IC_HEADER, reply.splitlines()[0]):
398 perfmon_nodes(reply, M_PMB_THREAD, M_PMB_IC_NODE)
399 if fullmatch(M_PMB_CM_HEADER, reply.splitlines()[0]):
400 perfmon_nodes(reply, M_PMB_THREAD, M_PMB_CM_NODE)
401 if fullmatch(M_PMB_LO_HEADER, reply.splitlines()[0]):
402 perfmon_nodes(reply, M_PMB_THREAD, M_PMB_LO_NODE)
403 if fullmatch(M_PMB_BM_HEADER, reply.splitlines()[0]):
404 perfmon_nodes(reply, M_PMB_THREAD, M_PMB_BM_NODE)
405 if fullmatch(M_PMB_MB_HEADER, reply.splitlines()[0]):
406 perfmon_system(reply, M_PMB_MB)
408 def vppctl_show_version(self, reply):
410 Parse the version output.
412 :param reply: VPP reply.
415 for metric in self.serializer.metric_registry:
416 version = reply.split()[1]
419 item["name"] = metric
420 labels["version"] = version
421 item["labels"] = labels
423 self.serializer.serialize(
424 metric=metric, labels=labels, item=item