2 *------------------------------------------------------------------
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:
10 * http://www.apache.org/licenses/LICENSE-2.0
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 *------------------------------------------------------------------
20 #include <arpa/inet.h>
21 #include <sys/epoll.h>
30 #include <sys/socket.h>
31 #include <vpp-api/client/stat_client.h>
32 #include <vlib/vlib.h>
35 /* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */
36 #define SERVER_PORT 9482
52 dump_metrics (FILE * stream, u8 ** patterns)
54 stat_segment_data_t *res;
56 static u32 *stats = 0;
59 res = stat_segment_dump (stats);
61 { /* Memory layout has changed */
64 stats = stat_segment_ls (patterns);
68 for (i = 0; i < vec_len (res); i++)
72 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
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]);
81 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
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++)
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);
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);
99 case STAT_DIR_TYPE_SCALAR_INDEX:
100 fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
101 fformat (stream, "%s %.2f\n", prom_string (res[i].name),
102 res[i].scalar_value);
105 case STAT_DIR_TYPE_NAME_VECTOR:
106 fformat (stream, "# TYPE %s_info gauge\n",
107 prom_string (res[i].name));
108 for (k = 0; k < vec_len (res[i].name_vector); k++)
109 if (res[i].name_vector[k])
110 fformat (stream, "%s_info{index=\"%d\",name=\"%s\"} 1\n",
111 prom_string (res[i].name), k, res[i].name_vector[k]);
114 case STAT_DIR_TYPE_EMPTY:
118 fformat (stderr, "Unknown value %d\n", res[i].type);
122 stat_segment_data_free (res);
127 #define ROOTPAGE "<html><head><title>Metrics exporter</title></head><body><ul><li><a href=\"/metrics\">metrics</a></li></ul></body></html>"
128 #define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>"
131 http_handler (FILE * stream, u8 ** patterns)
133 char status[80] = { 0 };
134 if (fgets (status, sizeof (status) - 1, stream) == 0)
136 fprintf (stderr, "fgets error: %s %s\n", status, strerror (errno));
140 char *method = strtok_r (status, " \t\r\n", &saveptr);
141 if (method == 0 || strncmp (method, "GET", 4) != 0)
143 fputs ("HTTP/1.0 405 Method Not Allowed\r\n", stream);
146 char *request_uri = strtok_r (NULL, " \t", &saveptr);
147 char *protocol = strtok_r (NULL, " \t\r\n", &saveptr);
148 if (protocol == 0 || strncmp (protocol, "HTTP/1.", 7) != 0)
150 fputs ("HTTP/1.0 400 Bad Request\r\n", stream);
153 /* Read the other headers */
157 if (fgets (header, sizeof (header) - 1, stream) == 0)
159 fprintf (stderr, "fgets error: %s\n", strerror (errno));
162 if (header[0] == '\n' || header[1] == '\n')
167 if (strcmp (request_uri, "/") == 0)
169 fprintf (stream, "HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n",
170 (unsigned long) strlen (ROOTPAGE));
171 fputs (ROOTPAGE, stream);
174 if (strcmp (request_uri, "/metrics") != 0)
177 "HTTP/1.0 404 Not Found\r\nContent-Length: %lu\r\n\r\n",
178 (unsigned long) strlen (NOT_FOUND_ERROR));
179 fputs (NOT_FOUND_ERROR, stream);
182 fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream);
183 dump_metrics (stream, patterns);
187 start_listen (u16 port)
189 struct sockaddr_in6 serveraddr;
190 int addrlen = sizeof (serveraddr);
193 int listenfd = socket (AF_INET6, SOCK_STREAM, 0);
196 perror ("Failed opening socket");
201 setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (int));
204 perror ("Failed setsockopt");
209 clib_memset (&serveraddr, 0, sizeof (serveraddr));
210 serveraddr.sin6_family = AF_INET6;
211 serveraddr.sin6_port = htons (port);
212 serveraddr.sin6_addr = in6addr_any;
214 if (bind (listenfd, (struct sockaddr *) &serveraddr, addrlen) < 0)
216 fprintf (stderr, "bind() error %s\n", strerror (errno));
220 if (listen (listenfd, 1000000) != 0)
222 fprintf (stderr, "listen() error for %s\n", strerror (errno));
229 /* Socket epoll, linux-specific */
232 struct sockaddr_storage storage;
233 struct sockaddr addr;
234 struct sockaddr_in sin_addr;
235 struct sockaddr_in6 sin6_addr;
241 main (int argc, char **argv)
243 unformat_input_t _argv, *a = &_argv;
244 u8 *stat_segment_name, *pattern = 0, **patterns = 0;
245 u16 port = SERVER_PORT;
247 "%s: usage [socket-name <name>] [port <0 - 65535>] <patterns> ...\n";
250 /* Allocating 256MB heap */
251 clib_mem_init (0, 256 << 20);
253 unformat_init_command_line (a, argv);
255 stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
257 while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
259 if (unformat (a, "socket-name %s", &stat_segment_name))
261 else if (unformat (a, "port %d", &port))
263 else if (unformat (a, "%s", &pattern))
265 vec_add1 (patterns, pattern);
269 fformat (stderr, usage, argv[0]);
274 if (vec_len (patterns) == 0)
276 fformat (stderr, usage, argv[0]);
280 rv = stat_segment_connect ((char *) stat_segment_name);
283 fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
288 int fd = start_listen (port);
295 int conn_sock = accept (fd, NULL, NULL);
298 fprintf (stderr, "Accept failed: %s", strerror (errno));
303 struct sockaddr_in6 clientaddr = { 0 };
304 char address[INET6_ADDRSTRLEN];
306 getpeername (conn_sock, (struct sockaddr *) &clientaddr, &addrlen);
308 (AF_INET6, &clientaddr.sin6_addr, address, sizeof (address)))
310 fprintf (stderr, "Client address is [%s]:%d\n", address,
311 ntohs (clientaddr.sin6_port));
315 FILE *stream = fdopen (conn_sock, "r+");
318 fprintf (stderr, "fdopen error: %s\n", strerror (errno));
322 /* Single reader at the moment */
323 http_handler (stream, patterns);
327 stat_segment_disconnect ();
334 * fd.io coding-style-patch-verification: ON
337 * eval: (c-set-style "gnu")