FD.io VPP  v19.08.3-2-gbabecb413
Vector Packet Processing
vpp_prometheus_export.c
Go to the documentation of this file.
1 /*
2  *------------------------------------------------------------------
3  * vpp_get_stats.c
4  *
5  * Copyright (c) 2018 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19 
20 #include <arpa/inet.h>
21 #include <sys/epoll.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <netdb.h>
30 #include <sys/socket.h>
32 #include <vlib/vlib.h>
33 #include <ctype.h>
34 
35 /* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */
36 #define SERVER_PORT 9482
37 
38 static char *
39 prom_string (char *s)
40 {
41  char *p = s;
42  while (*p)
43  {
44  if (!isalnum (*p))
45  *p = '_';
46  p++;
47  }
48  return s;
49 }
50 
51 static void
52 dump_metrics (FILE * stream, u8 ** patterns)
53 {
55  int i, j, k;
56  static u32 *stats = 0;
57 
58 retry:
59  res = stat_segment_dump (stats);
60  if (res == 0)
61  { /* Memory layout has changed */
62  if (stats)
63  vec_free (stats);
64  stats = stat_segment_ls (patterns);
65  goto retry;
66  }
67 
68  for (i = 0; i < vec_len (res); i++)
69  {
70  switch (res[i].type)
71  {
73  fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
74  for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
75  for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
76  fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n",
77  prom_string (res[i].name), k, j,
78  res[i].simple_counter_vec[k][j]);
79  break;
80 
82  fformat (stream, "# TYPE %s_packets counter\n",
83  prom_string (res[i].name));
84  fformat (stream, "# TYPE %s_bytes counter\n",
85  prom_string (res[i].name));
86  for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
87  for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
88  {
89  fformat (stream,
90  "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
91  prom_string (res[i].name), k, j,
92  res[i].combined_counter_vec[k][j].packets);
93  fformat (stream,
94  "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n",
95  prom_string (res[i].name), k, j,
96  res[i].combined_counter_vec[k][j].bytes);
97  }
98  break;
100  for (j = 0; j < vec_len (res[i].error_vector); j++)
101  {
102  fformat (stream, "# TYPE %s counter\n",
103  prom_string (res[i].name));
104  fformat (stream, "%s{thread=\"%d\"} %lld\n",
105  prom_string (res[i].name), j, res[i].error_vector[j]);
106  }
107  break;
108 
110  fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
111  fformat (stream, "%s %.2f\n", prom_string (res[i].name),
112  res[i].scalar_value);
113  break;
114 
115  case STAT_DIR_TYPE_EMPTY:
116  break;
117 
118  default:
119  fformat (stderr, "Unknown value %d\n", res[i].type);
120  ;
121  }
122  }
124 
125 }
126 
127 
128 #define ROOTPAGE "<html><head><title>Metrics exporter</title></head><body><ul><li><a href=\"/metrics\">metrics</a></li></ul></body></html>"
129 #define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>"
130 
131 static void
132 http_handler (FILE * stream, u8 ** patterns)
133 {
134  char status[80] = { 0 };
135  if (fgets (status, sizeof (status) - 1, stream) == 0)
136  {
137  fprintf (stderr, "fgets error: %s %s\n", status, strerror (errno));
138  return;
139  }
140  char *saveptr;
141  char *method = strtok_r (status, " \t\r\n", &saveptr);
142  if (method == 0 || strncmp (method, "GET", 4) != 0)
143  {
144  fputs ("HTTP/1.0 405 Method Not Allowed\r\n", stream);
145  return;
146  }
147  char *request_uri = strtok_r (NULL, " \t", &saveptr);
148  char *protocol = strtok_r (NULL, " \t\r\n", &saveptr);
149  if (protocol == 0 || strncmp (protocol, "HTTP/1.", 7) != 0)
150  {
151  fputs ("HTTP/1.0 400 Bad Request\r\n", stream);
152  return;
153  }
154  /* Read the other headers */
155  for (;;)
156  {
157  char header[1024];
158  if (fgets (header, sizeof (header) - 1, stream) == 0)
159  {
160  fprintf (stderr, "fgets error: %s\n", strerror (errno));
161  return;
162  }
163  if (header[0] == '\n' || header[1] == '\n')
164  {
165  break;
166  }
167  }
168  if (strcmp (request_uri, "/") == 0)
169  {
170  fprintf (stream, "HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n",
171  (unsigned long) strlen (ROOTPAGE));
172  fputs (ROOTPAGE, stream);
173  return;
174  }
175  if (strcmp (request_uri, "/metrics") != 0)
176  {
177  fprintf (stream,
178  "HTTP/1.0 404 Not Found\r\nContent-Length: %lu\r\n\r\n",
179  (unsigned long) strlen (NOT_FOUND_ERROR));
180  fputs (NOT_FOUND_ERROR, stream);
181  return;
182  }
183  fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream);
184  dump_metrics (stream, patterns);
185 }
186 
187 static int
189 {
190  struct sockaddr_in6 serveraddr;
191  int addrlen = sizeof (serveraddr);
192  int enable = 1;
193 
194  int listenfd = socket (AF_INET6, SOCK_STREAM, 0);
195  if (listenfd == -1)
196  {
197  perror ("Failed opening socket");
198  return -1;
199  }
200 
201  int rv =
202  setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (int));
203  if (rv < 0)
204  {
205  perror ("Failed setsockopt");
206  close (listenfd);
207  return -1;
208  }
209 
210  clib_memset (&serveraddr, 0, sizeof (serveraddr));
211  serveraddr.sin6_family = AF_INET6;
212  serveraddr.sin6_port = htons (port);
213  serveraddr.sin6_addr = in6addr_any;
214 
215  if (bind (listenfd, (struct sockaddr *) &serveraddr, addrlen) < 0)
216  {
217  fprintf (stderr, "bind() error %s\n", strerror (errno));
218  close (listenfd);
219  return -1;
220  }
221  if (listen (listenfd, 1000000) != 0)
222  {
223  fprintf (stderr, "listen() error for %s\n", strerror (errno));
224  close (listenfd);
225  return -1;
226  }
227  return listenfd;
228 }
229 
230 /* Socket epoll, linux-specific */
232 {
233  struct sockaddr_storage storage;
234  struct sockaddr addr;
235  struct sockaddr_in sin_addr;
236  struct sockaddr_in6 sin6_addr;
237 };
238 
239 
240 
241 int
242 main (int argc, char **argv)
243 {
244  unformat_input_t _argv, *a = &_argv;
245  u8 *stat_segment_name, *pattern = 0, **patterns = 0;
246  int rv;
247 
248  /* Allocating 32MB heap */
249  clib_mem_init (0, 32 << 20);
250 
251  unformat_init_command_line (a, argv);
252 
253  stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
254 
256  {
257  if (unformat (a, "socket-name %s", &stat_segment_name))
258  ;
259  else if (unformat (a, "%s", &pattern))
260  {
261  vec_add1 (patterns, pattern);
262  }
263  else
264  {
265  fformat (stderr,
266  "%s: usage [socket-name <name>] <patterns> ...\n",
267  argv[0]);
268  exit (1);
269  }
270  }
271 
272  if (vec_len (patterns) == 0)
273  {
274  fformat (stderr,
275  "%s: usage [socket-name <name>] <patterns> ...\n", argv[0]);
276  exit (1);
277  }
278 
279  rv = stat_segment_connect ((char *) stat_segment_name);
280  if (rv)
281  {
282  fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
283  stat_segment_name);
284  exit (1);
285  }
286 
287  int fd = start_listen (SERVER_PORT);
288  if (fd < 0)
289  {
290  exit (1);
291  }
292  for (;;)
293  {
294  int conn_sock = accept (fd, NULL, NULL);
295  if (conn_sock < 0)
296  {
297  fprintf (stderr, "Accept failed: %s", strerror (errno));
298  continue;
299  }
300  else
301  {
302  struct sockaddr_in6 clientaddr = { 0 };
303  char address[INET6_ADDRSTRLEN];
304  socklen_t addrlen;
305  getpeername (conn_sock, (struct sockaddr *) &clientaddr, &addrlen);
306  if (inet_ntop
307  (AF_INET6, &clientaddr.sin6_addr, address, sizeof (address)))
308  {
309  fprintf (stderr, "Client address is [%s]:%d\n", address,
310  ntohs (clientaddr.sin6_port));
311  }
312  }
313 
314  FILE *stream = fdopen (conn_sock, "r+");
315  if (stream == NULL)
316  {
317  fprintf (stderr, "fdopen error: %s\n", strerror (errno));
318  close (conn_sock);
319  continue;
320  }
321  /* Single reader at the moment */
322  http_handler (stream, patterns);
323  fclose (stream);
324  }
325 
327  close (fd);
328 
329  exit (0);
330 }
331 
332 /*
333  * fd.io coding-style-patch-verification: ON
334  *
335  * Local Variables:
336  * eval: (c-set-style "gnu")
337  * End:
338  */
typedef address
Definition: ip_types.api:83
int stat_segment_connect(const char *socket_name)
Definition: stat_client.c:151
a
Definition: bitmap.h:538
static int start_listen(u16 port)
Optimized string handling code, including c11-compliant "safe C library" variants.
void stat_segment_data_free(stat_segment_data_t *res)
Definition: stat_client.c:287
counter_t ** simple_counter_vec
Definition: stat_client.h:40
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
#define NOT_FOUND_ERROR
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
int i
static void dump_metrics(FILE *stream, u8 **patterns)
unsigned char u8
Definition: types.h:56
int main(int argc, char **argv)
unsigned int u32
Definition: types.h:88
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vl_api_ip_proto_t protocol
Definition: punt.api:39
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
void unformat_init_command_line(unformat_input_t *input, char *argv[])
Definition: unformat.c:1013
u16 port
Definition: punt.api:40
#define SERVER_PORT
u8 name[64]
Definition: memclnt.api:152
void stat_segment_disconnect(void)
Definition: stat_client.c:165
word fformat(FILE *f, char *fmt,...)
Definition: format.c:462
void * clib_mem_init(void *heap, uword size)
Definition: mem_dlmalloc.c:205
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
#define STAT_SEGMENT_SOCKET_FILE
Definition: stat_client.h:30
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
struct sockaddr addr
uint32_t * stat_segment_ls(uint8_t **patterns)
Definition: stat_client.c:366
stat_segment_data_t * stat_segment_dump(uint32_t *stats)
Definition: stat_client.c:401
struct sockaddr_in6 sin6_addr
static char * prom_string(char *s)
struct sockaddr_in sin_addr
static void http_handler(FILE *stream, u8 **patterns)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
#define ROOTPAGE
struct sockaddr_storage storage
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:171