2 * Copyright (c) 2022 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 #include <vnet/plugin/plugin.h>
16 #include <vpp/app/version.h>
18 #include <prom/prom.h>
19 #include <vpp-api/client/stat_client.h>
20 #include <vpp/stats/stat_segment.h>
23 static prom_main_t prom_main;
26 make_stat_name (char *name)
28 prom_main_t *pm = &prom_main;
38 /* Reuse vector, instead of always allocating, when building a name. */
39 vec_reset_length (pm->name_scratch_pad);
40 pm->name_scratch_pad =
41 format (pm->name_scratch_pad, "%v%s", pm->stat_name_prefix, name);
42 return pm->name_scratch_pad;
46 dump_counter_vector_simple (stat_segment_data_t *res, u8 *s, u8 used_only)
52 name = make_stat_name (res->name);
54 for (k = 0; k < vec_len (res->simple_counter_vec); k++)
55 for (j = 0; j < vec_len (res->simple_counter_vec[k]); j++)
57 if (used_only && !res->simple_counter_vec[k][j])
61 s = format (s, "# TYPE %v counter\n", name);
64 s = format (s, "%v{thread=\"%d\",interface=\"%d\"} %lld\n", name, k, j,
65 res->simple_counter_vec[k][j]);
72 dump_counter_vector_combined (stat_segment_data_t *res, u8 *s, u8 used_only)
78 name = make_stat_name (res->name);
80 for (k = 0; k < vec_len (res->simple_counter_vec); k++)
81 for (j = 0; j < vec_len (res->combined_counter_vec[k]); j++)
83 if (used_only && !res->combined_counter_vec[k][j].packets)
87 s = format (s, "# TYPE %v_packets counter\n", name);
88 s = format (s, "# TYPE %v_bytes counter\n", name);
91 s = format (s, "%v_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
92 name, k, j, res->combined_counter_vec[k][j].packets);
93 s = format (s, "%v_bytes{thread=\"%d\",interface=\"%d\"} %lld\n", name,
94 k, j, res->combined_counter_vec[k][j].bytes);
101 dump_error_index (stat_segment_data_t *res, u8 *s, u8 used_only)
106 name = make_stat_name (res->name);
108 for (j = 0; j < vec_len (res->error_vector); j++)
110 if (used_only && !res->error_vector[j])
112 s = format (s, "# TYPE %v counter\n", name);
114 format (s, "%v{thread=\"%d\"} %lld\n", name, j, res->error_vector[j]);
121 dump_scalar_index (stat_segment_data_t *res, u8 *s, u8 used_only)
125 if (used_only && !res->scalar_value)
128 name = make_stat_name (res->name);
130 s = format (s, "# TYPE %v counter\n", name);
131 s = format (s, "%v %.2f\n", name, res->scalar_value);
137 dump_name_vector (stat_segment_data_t *res, u8 *s, u8 used_only)
142 name = make_stat_name (res->name);
144 s = format (s, "# TYPE %v_info gauge\n", name);
145 for (k = 0; k < vec_len (res->name_vector); k++)
146 s = format (s, "%v_info{index=\"%d\",name=\"%s\"} 1\n", name, k,
147 res->name_vector[k]);
153 scrape_stats_segment (u8 *s, u8 **patterns, u8 used_only)
155 stat_segment_data_t *res;
156 static u32 *stats = 0;
159 stats = stat_segment_ls (patterns);
162 res = stat_segment_dump (stats);
164 { /* Memory layout has changed */
167 stats = stat_segment_ls (patterns);
171 for (i = 0; i < vec_len (res); i++)
175 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
176 s = dump_counter_vector_simple (&res[i], s, used_only);
179 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
180 s = dump_counter_vector_combined (&res[i], s, used_only);
182 case STAT_DIR_TYPE_ERROR_INDEX:
183 s = dump_error_index (&res[i], s, used_only);
186 case STAT_DIR_TYPE_SCALAR_INDEX:
187 s = dump_scalar_index (&res[i], s, used_only);
190 case STAT_DIR_TYPE_NAME_VECTOR:
191 s = dump_name_vector (&res[i], s, used_only);
194 case STAT_DIR_TYPE_EMPTY:
198 clib_warning ("Unknown value %d\n", res[i].type);
202 stat_segment_data_free (res);
208 send_data_to_hss (hss_session_handle_t sh)
210 hss_url_handler_args_t args = {};
211 prom_main_t *pm = &prom_main;
214 args.data = vec_dup (pm->stats);
215 args.data_len = vec_len (pm->stats);
216 args.sc = HTTP_STATUS_OK;
217 args.free_vec_data = 1;
219 pm->send_data (&args);
223 send_data_to_hss_rpc (void *rpc_args)
225 send_data_to_hss (*(hss_session_handle_t *) rpc_args);
229 prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
232 uword *event_data = 0, event_type;
233 prom_main_t *pm = &prom_main;
234 hss_session_handle_t sh;
235 f64 timeout = 10000.0;
239 vlib_process_wait_for_event_or_clock (vm, timeout);
240 event_type = vlib_process_get_events (vm, (uword **) &event_data);
244 /* timeout, do nothing */
246 case PROM_SCRAPER_EVT_RUN:
247 sh.as_u64 = event_data[0];
248 vec_reset_length (pm->stats);
249 pm->stats = scrape_stats_segment (pm->stats, pm->stats_patterns,
251 session_send_rpc_evt_to_thread_force (sh.thread_index,
252 send_data_to_hss_rpc, &sh);
253 pm->last_scrape = vlib_time_now (vm);
256 clib_warning ("unexpected event %u", event_type);
260 vec_reset_length (event_data);
265 VLIB_REGISTER_NODE (prom_scraper_process_node) = {
266 .function = prom_scraper_process,
267 .type = VLIB_NODE_TYPE_PROCESS,
268 .name = "prom-scraper-process",
269 .state = VLIB_NODE_STATE_DISABLED,
273 prom_scraper_process_enable (vlib_main_t *vm)
275 prom_main_t *pm = &prom_main;
278 vlib_node_set_state (vm, prom_scraper_process_node.index,
279 VLIB_NODE_STATE_POLLING);
280 n = vlib_get_node (vm, prom_scraper_process_node.index);
281 vlib_start_process (vm, n->runtime_index);
283 pm->scraper_node_index = n->index;
287 signal_run_to_scraper (uword *args)
289 prom_main_t *pm = &prom_main;
290 ASSERT (vlib_get_thread_index () == 0);
291 vlib_process_signal_event (pm->vm, pm->scraper_node_index,
292 PROM_SCRAPER_EVT_RUN, *args);
296 prom_stats_dump (hss_url_handler_args_t *args)
298 vlib_main_t *vm = vlib_get_main ();
299 f64 now = vlib_time_now (vm);
300 prom_main_t *pm = &prom_main;
302 /* If we've recently scraped stats, return data */
303 if ((now - pm->last_scrape) < pm->min_scrape_interval)
305 send_data_to_hss (args->sh);
306 return HSS_URL_HANDLER_ASYNC;
309 if (vm->thread_index != 0)
310 vl_api_rpc_call_main_thread (signal_run_to_scraper, (u8 *) &args->sh,
313 signal_run_to_scraper (&args->sh.as_u64);
315 return HSS_URL_HANDLER_ASYNC;
319 prom_stat_patterns_add (u8 **patterns)
321 prom_main_t *pm = &prom_main;
323 u8 **pattern, **existing;
327 vec_foreach (pattern, patterns)
330 len = vec_len (*pattern);
331 vec_foreach (existing, pm->stats_patterns)
333 if (vec_len (*existing) != len)
335 if (!memcmp (*existing, *pattern, len - 1))
342 vec_add1 (pm->stats_patterns, *pattern);
347 prom_stat_patterns_free (void)
349 prom_main_t *pm = &prom_main;
352 vec_foreach (pattern, pm->stats_patterns)
354 vec_free (pm->stats_patterns);
358 prom_stat_patterns_set (u8 **patterns)
360 prom_stat_patterns_free ();
361 prom_stat_patterns_add (patterns);
365 prom_stat_patterns_get (void)
367 return prom_main.stats_patterns;
371 prom_stat_name_prefix_set (u8 *prefix)
373 prom_main_t *pm = &prom_main;
375 vec_free (pm->stat_name_prefix);
376 pm->stat_name_prefix = prefix;
380 prom_report_used_only (u8 used_only)
382 prom_main_t *pm = &prom_main;
384 pm->used_only = used_only;
388 prom_stat_segment_client_init (void)
390 stat_client_main_t *scm = &stat_client_main;
391 stat_segment_main_t *sm = &stat_segment_main;
394 size = sm->memory_size ? sm->memory_size : STAT_SEGMENT_DEFAULT_SIZE;
395 scm->memory_size = size;
396 scm->shared_header = sm->shared_header;
397 scm->directory_vector =
398 stat_segment_adjust (scm, (void *) scm->shared_header->directory_vector);
402 prom_enable (vlib_main_t *vm)
404 prom_main_t *pm = &prom_main;
406 pm->register_url = vlib_get_plugin_symbol ("http_static_plugin.so",
407 "hss_register_url_handler");
409 vlib_get_plugin_symbol ("http_static_plugin.so", "hss_session_send_data");
410 pm->register_url (prom_stats_dump, "stats.prom", HTTP_REQ_GET);
414 if (!pm->stat_name_prefix)
415 pm->stat_name_prefix = format (0, "vpp");
417 prom_scraper_process_enable (vm);
418 prom_stat_segment_client_init ();
421 static clib_error_t *
422 prom_init (vlib_main_t *vm)
424 prom_main_t *pm = &prom_main;
427 pm->min_scrape_interval = 1;
429 pm->stat_name_prefix = 0;
440 VLIB_INIT_FUNCTION (prom_init) = {
441 .runs_after = VLIB_INITS ("hss_main_init"),
444 VLIB_PLUGIN_REGISTER () = {
445 .version = VPP_BUILD_VER,
446 .description = "Prometheus Stats Exporter",
447 .default_disabled = 0,
451 * fd.io coding-style-patch-verification: ON
454 * eval: (c-set-style "gnu")