Implement openmetric output via bgpctl show metric command
authorclaudio <claudio@openbsd.org>
Mon, 17 Oct 2022 12:01:19 +0000 (12:01 +0000)
committerclaudio <claudio@openbsd.org>
Mon, 17 Oct 2022 12:01:19 +0000 (12:01 +0000)
This adds most of the generic code to output the metrics with labels
and implements some basic metrics. The code works but metrics may still
change.

OK tb@

usr.sbin/bgpctl/Makefile
usr.sbin/bgpctl/bgpctl.c
usr.sbin/bgpctl/bgpctl.h
usr.sbin/bgpctl/ometric.c [new file with mode: 0644]
usr.sbin/bgpctl/ometric.h [new file with mode: 0644]
usr.sbin/bgpctl/output.c
usr.sbin/bgpctl/output_json.c
usr.sbin/bgpctl/output_ometric.c [new file with mode: 0644]
usr.sbin/bgpctl/parser.c
usr.sbin/bgpctl/parser.h

index 106495d..06032dc 100644 (file)
@@ -1,9 +1,10 @@
-#      $OpenBSD: Makefile,v 1.17 2020/05/02 14:33:33 claudio Exp $
+#      $OpenBSD: Makefile,v 1.18 2022/10/17 12:01:19 claudio Exp $
 
 .PATH:         ${.CURDIR}/../bgpd
 
 PROG=  bgpctl
-SRCS=  bgpctl.c output.c output_json.c parser.c mrtparser.c util.c json.c
+SRCS=  bgpctl.c output.c output_json.c output_ometric.c parser.c \
+       mrtparser.c util.c json.c ometric.c
 CFLAGS+= -Wall
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
index e41f6c9..57613c9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpctl.c,v 1.284 2022/10/07 09:20:30 claudio Exp $ */
+/*     $OpenBSD: bgpctl.c,v 1.285 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -79,7 +79,7 @@ int
 main(int argc, char *argv[])
 {
        struct sockaddr_un       sa_un;
-       int                      fd, n, done, ch, verbose = 0;
+       int                      fd, n, done, numdone, ch, verbose = 0;
        struct imsg              imsg;
        struct network_config    net;
        struct parse_result     *res;
@@ -256,6 +256,12 @@ main(int argc, char *argv[])
        case SHOW_RIB_MEM:
                imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0);
                break;
+       case SHOW_METRIC:
+               output = &ometric_output;
+               numdone = 2;
+               imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0);
+               imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0);
+               break;
        case RELOAD:
                imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
                    res->reason, sizeof(res->reason));
@@ -366,18 +372,14 @@ main(int argc, char *argv[])
                break;
        }
 
+       output->head(res);
+
+ again:
        while (ibuf->w.queued)
-               if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+               if (msgbuf_write(&ibuf->w) <= 0)
                        err(1, "write error");
 
-       output->head(res);
-
        while (!done) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       err(1, "imsg_read error");
-               if (n == 0)
-                       errx(1, "pipe closed");
-
                while (!done) {
                        if ((n = imsg_get(ibuf, &imsg)) == -1)
                                err(1, "imsg_get error");
@@ -387,6 +389,20 @@ main(int argc, char *argv[])
                        done = show(&imsg, res);
                        imsg_free(&imsg);
                }
+
+               if (done)
+                       break;
+
+               if ((n = imsg_read(ibuf)) == -1)
+                       err(1, "imsg_read error");
+               if (n == 0)
+                       errx(1, "pipe closed");
+
+       }
+
+       if (res->action == SHOW_METRIC && --numdone > 0) {
+               done = 0;
+               goto again;
        }
 
        output->tail();
@@ -416,21 +432,29 @@ show(struct imsg *imsg, struct parse_result *res)
 
        switch (imsg->hdr.type) {
        case IMSG_CTL_SHOW_NEIGHBOR:
+               if (output->neighbor == NULL)
+                       break;
                p = imsg->data;
                output->neighbor(p, res);
                break;
        case IMSG_CTL_SHOW_TIMER:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(t))
                        errx(1, "wrong imsg len");
+               if (output->timer == NULL)
+                       break;
                memcpy(&t, imsg->data, sizeof(t));
                if (t.type > 0 && t.type < Timer_Max)
                        output->timer(&t);
                break;
        case IMSG_CTL_SHOW_INTERFACE:
+               if (output->interface == NULL)
+                       break;
                iface = imsg->data;
                output->interface(iface);
                break;
        case IMSG_CTL_SHOW_NEXTHOP:
+               if (output->nexthop == NULL)
+                       break;
                nh = imsg->data;
                output->nexthop(nh);
                break;
@@ -438,18 +462,24 @@ show(struct imsg *imsg, struct parse_result *res)
        case IMSG_CTL_SHOW_NETWORK:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf))
                        errx(1, "wrong imsg len");
+               if (output->fib == NULL)
+                       break;
                kf = imsg->data;
                output->fib(kf);
                break;
        case IMSG_CTL_SHOW_FIB_TABLES:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt))
                        errx(1, "wrong imsg len");
+               if (output->fib_table == NULL)
+                       break;
                kt = imsg->data;
                output->fib_table(kt);
                break;
        case IMSG_CTL_SHOW_RIB:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rib))
                        errx(1, "wrong imsg len");
+               if (output->rib == NULL)
+                       break;
                memcpy(&rib, imsg->data, sizeof(rib));
                aslen = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof(rib);
                asdata = imsg->data;
@@ -462,6 +492,8 @@ show(struct imsg *imsg, struct parse_result *res)
                        warnx("bad IMSG_CTL_SHOW_RIB_COMMUNITIES received");
                        break;
                }
+               if (output->communities == NULL)
+                       break;
                output->communities(imsg->data, ilen, res);
                break;
        case IMSG_CTL_SHOW_RIB_ATTR:
@@ -470,23 +502,31 @@ show(struct imsg *imsg, struct parse_result *res)
                        warnx("bad IMSG_CTL_SHOW_RIB_ATTR received");
                        break;
                }
+               if (output->attr == NULL)
+                       break;
                output->attr(imsg->data, ilen, res->flags, 0);
                break;
        case IMSG_CTL_SHOW_RIB_MEM:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(stats))
                        errx(1, "wrong imsg len");
+               if (output->rib_mem == NULL)
+                       break;
                memcpy(&stats, imsg->data, sizeof(stats));
                output->rib_mem(&stats);
                return (1);
        case IMSG_CTL_SHOW_SET:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(set))
                        errx(1, "wrong imsg len");
+               if (output->set == NULL)
+                       break;
                memcpy(&set, imsg->data, sizeof(set));
                output->set(&set);
                break;
        case IMSG_CTL_SHOW_RTR:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rtr))
                        errx(1, "wrong imsg len");
+               if (output->rtr == NULL)
+                       break;
                memcpy(&rtr, imsg->data, sizeof(rtr));
                output->rtr(&rtr);
                break;
@@ -495,6 +535,8 @@ show(struct imsg *imsg, struct parse_result *res)
                        warnx("got IMSG_CTL_RESULT with wrong len");
                        break;
                }
+               if (output->result == NULL)
+                       break;
                memcpy(&rescode, imsg->data, sizeof(rescode));
                output->result(rescode);
                return (1);
index 66a0f14..7014095 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpctl.h,v 1.16 2022/08/31 15:00:53 claudio Exp $ */
+/*     $OpenBSD: bgpctl.h,v 1.17 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
@@ -37,7 +37,7 @@ struct output {
        void    (*tail)(void);
 };
 
-extern const struct output show_output, json_output;
+extern const struct output show_output, json_output, ometric_output;
 extern const size_t pt_sizes[];
 
 #define EOL0(flag)     ((flag & F_CTL_SSV) ? ';' : '\n')
diff --git a/usr.sbin/bgpctl/ometric.c b/usr.sbin/bgpctl/ometric.c
new file mode 100644 (file)
index 0000000..54b1bc6
--- /dev/null
@@ -0,0 +1,421 @@
+/*     $OpenBSD: ometric.c,v 1.1 2022/10/17 12:01:19 claudio Exp $ */
+
+/*
+ * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ometric.h"
+
+struct olabel {
+       STAILQ_ENTRY(olabel)     entry;
+       const char              *key;
+       char                    *value;
+};
+
+struct olabels {
+       STAILQ_HEAD(, olabel)    labels;
+       struct olabels          *next;
+       int                      refcnt;
+};
+
+enum ovalue_type {
+       OVT_INTEGER,
+       OVT_DOUBLE,
+};
+
+struct ovalue {
+       STAILQ_ENTRY(ovalue)     entry;
+       struct olabels          *labels;
+       union {
+               uint64_t        i;
+               double          f;
+       }                        value;
+       enum ovalue_type         valtype;
+};
+
+STAILQ_HEAD(ovalues, ovalue);
+
+struct ometric {
+       STAILQ_ENTRY(ometric)    entry;
+       struct ovalues           vals;
+       const char              *name;
+       const char              *help;
+       const char *const       *stateset;
+       size_t                   setsize;
+       enum ometric_type        type;
+};
+
+STAILQ_HEAD(, ometric) ometrics = STAILQ_HEAD_INITIALIZER(ometrics);
+
+/*
+ * Allocate and return new ometric. The name and help string need to remain
+ * valid until the ometric is freed. Normally constant strings should be used.
+ */
+struct ometric *
+ometric_new(enum ometric_type type, const char *name, const char *help)
+{
+       struct ometric *om;
+
+       if ((om = calloc(1, sizeof(*om))) == NULL)
+               err(1, NULL);
+
+       om->name = name;
+       om->help = help;
+       om->type = type;
+       STAILQ_INIT(&om->vals);
+
+       STAILQ_INSERT_TAIL(&ometrics, om, entry);
+
+       return om;
+}
+
+/*
+ * Same as above but for a stateset. The states is an array of constant strings
+ * with statecnt elements. The states, name and help pointers need to remain
+ * valid until the ometric is freed.
+ */
+struct ometric *
+ometric_new_state(const char * const *states, size_t statecnt, const char *name,
+    const char *help)
+{
+       struct ometric *om;
+
+       if ((om = calloc(1, sizeof(*om))) == NULL)
+               err(1, NULL);
+
+       om->name = name;
+       om->help = help;
+       om->type = OMT_STATESET;
+       om->stateset = states;
+       om->setsize = statecnt;
+       STAILQ_INIT(&om->vals);
+
+       STAILQ_INSERT_TAIL(&ometrics, om, entry);
+
+       return om;
+}
+
+void
+ometric_free_all(void)
+{
+       struct ometric *om;
+       struct ovalue *ov;
+
+       while ((om = STAILQ_FIRST(&ometrics)) != NULL) {
+               STAILQ_REMOVE_HEAD(&ometrics, entry);
+               while ((ov = STAILQ_FIRST(&om->vals)) != NULL) {
+                       STAILQ_REMOVE_HEAD(&om->vals, entry);
+                       olabels_free(ov->labels);
+                       free(ov);
+               }
+               free(om);
+       }
+}
+
+static struct olabels *
+olabels_ref(struct olabels *ol)
+{
+       struct olabels *x = ol;
+
+       while (x != NULL) {
+               x->refcnt++;
+               x = x->next;
+       }
+
+       return ol;
+}
+
+/*
+ * Create a new set of labels based on keys and values arrays.
+ * keys must end in a NULL element. values needs to hold as many elements
+ * but the elements can be NULL. values are copied for the olabel but
+ * keys needs to point to constant memory.
+ */
+struct olabels *
+olabels_new(const char * const *keys, const char **values)
+{
+       struct olabels *ol;
+       struct olabel  *l;
+
+       if ((ol = malloc(sizeof(*ol))) == NULL)
+               err(1, NULL);
+       STAILQ_INIT(&ol->labels);
+       ol->refcnt = 1;
+       ol->next = NULL;
+
+       while (*keys != NULL) {
+               if (*values && **values != '\0') {
+                       if ((l = malloc(sizeof(*l))) == NULL)
+                               err(1, NULL);
+                       l->key = *keys;
+                       if ((l->value = strdup(*values)) == NULL)
+                               err(1, NULL);
+                       STAILQ_INSERT_TAIL(&ol->labels, l, entry);
+               }
+
+               keys++;
+               values++;
+       }
+
+       return ol;
+}
+
+/*
+ * Free olables once nothing uses them anymore.
+ */
+void
+olabels_free(struct olabels *ol)
+{
+       struct olabels *next;
+       struct olabel  *l;
+
+       for ( ; ol != NULL; ol = next) {
+               next = ol->next;
+
+               if (--ol->refcnt == 0) {
+                       while ((l = STAILQ_FIRST(&ol->labels)) != NULL) {
+                               STAILQ_REMOVE_HEAD(&ol->labels, entry);
+                               free(l->value);
+                               free(l);
+                       }
+                       free(ol);
+               }
+       }
+}
+
+/*
+ * Add one extra label onto the label stack. Once no longer used the
+ * value needs to be freed with olabels_free().
+ */
+static struct olabels *
+olabels_add_extra(struct olabels *ol, const char *key, const char *value)
+{
+       const char *keys[2] = { key, NULL };
+       const char *values[2] = { value, NULL };
+       struct olabels *new;
+
+       if (value == NULL || *value == '\0')
+               return ol;
+
+       new = olabels_new(keys, values);
+       new->next = olabels_ref(ol);
+
+       return new;
+}
+
+/*
+ * Output function called last.
+ */
+static const char *
+ometric_type(enum ometric_type type)
+{
+       switch (type) {
+       case OMT_GAUGE:
+               return "gauge";
+       case OMT_COUNTER:
+               return "counter";
+       case OMT_STATESET:
+               return "stateset";
+       case OMT_HISTOGRAM:
+               return "histogram";
+       case OMT_SUMMARY:
+               return "summary";
+       default:
+               return "unknown";
+       }
+}
+
+static void
+ometric_output_labels(const struct olabels *ol)
+{
+       struct olabel *l;
+       const char *comma = "";
+
+       if (ol == NULL) {
+               printf(" ");
+               return;
+       }
+
+       printf("{");
+
+       while (ol != NULL) {
+               STAILQ_FOREACH(l, &ol->labels, entry) {
+                       printf("%s%s=\"%s\"", comma, l->key, l->value);
+                       comma = ",";
+               }
+               ol = ol->next;
+       }
+
+       printf("} ");
+}
+
+static void
+ometric_output_value(const struct ovalue *ov)
+{
+       switch (ov->valtype) {
+       case OVT_INTEGER:
+               printf("%llu", ov->value.i);
+               return;
+       case OVT_DOUBLE:
+               printf("%g", ov->value.f);
+               return;
+       }
+}
+
+/*
+ * Output all metric values with TYPE and optional HELP strings.
+ */
+void
+ometric_output_all(void)
+{
+       struct ometric *om;
+       struct ovalue *ov;
+
+       STAILQ_FOREACH(om, &ometrics, entry) {
+               if (om->help)
+                       printf("# HELP %s %s\n", om->name, om->help);
+               printf("# TYPE %s %s\n", om->name, ometric_type(om->type));
+
+               STAILQ_FOREACH(ov, &om->vals, entry) {
+                       printf("%s", om->name);
+                       ometric_output_labels(ov->labels);
+                       ometric_output_value(ov);
+                       printf("\n");
+               }
+       }
+
+       printf("# EOF\n");
+}
+
+/*
+ * Value setters
+ */
+static void
+ometric_set_int_value(struct ometric *om, uint64_t val, struct olabels *ol)
+{
+       struct ovalue *ov;
+
+       if ((ov = malloc(sizeof(*ov))) == NULL)
+               err(1, NULL);
+
+       ov->value.i = val;
+       ov->valtype = OVT_INTEGER;
+       ov->labels = olabels_ref(ol);
+
+       STAILQ_INSERT_TAIL(&om->vals, ov, entry);
+}
+
+/*
+ * Set an integer value with label ol. ol can be NULL.
+ */
+void
+ometric_set_int(struct ometric *om, uint64_t val, struct olabels *ol)
+{
+       if (om->type != OMT_COUNTER && om->type != OMT_GAUGE)
+               errx(1, "%s incorrect ometric type", __func__);
+
+       ometric_set_int_value(om, val, ol);
+}
+
+/*
+ * Set a floating point value with label ol. ol can be NULL.
+ */
+void
+ometric_set_float(struct ometric *om, double val, struct olabels *ol)
+{
+       struct ovalue *ov;
+
+       if (om->type != OMT_COUNTER && om->type != OMT_GAUGE)
+               errx(1, "%s incorrect ometric type", __func__);
+
+       if ((ov = malloc(sizeof(*ov))) == NULL)
+               err(1, NULL);
+
+       ov->value.f = val;
+       ov->valtype = OVT_DOUBLE;
+       ov->labels = olabels_ref(ol);
+
+       STAILQ_INSERT_TAIL(&om->vals, ov, entry);
+}
+
+/*
+ * Add an info value (which is the value 1 but with extra key-value pairs).
+ */
+void
+ometric_set_info(struct ometric *om, const char **keys, const char **values,
+    struct olabels *ol)
+{
+       struct olabels *extra = NULL;
+
+       if (om->type != OMT_INFO)
+               errx(1, "%s incorrect ometric type", __func__);
+
+       if (keys != NULL) {
+               extra = olabels_new(keys, values);
+               extra->next = olabels_ref(ol);
+       }
+
+       ometric_set_int_value(om, 1, extra != NULL ? extra : ol);
+       olabels_free(extra);
+}
+
+/*
+ * Set a stateset to one of its states.
+ */
+void
+ometric_set_state(struct ometric *om, const char *state, struct olabels *ol)
+{
+       struct olabels *extra;
+       size_t i;
+       int val;
+
+       if (om->type != OMT_STATESET)
+               errx(1, "%s incorrect ometric type", __func__);
+
+       for (i = 0; i < om->setsize; i++) {     
+               if (strcasecmp(state, om->stateset[i]) == 0)
+                       val = 1;
+               else
+                       val = 0;
+
+               extra = olabels_add_extra(ol, om->name, om->stateset[i]);
+               ometric_set_int_value(om, val, extra);
+               olabels_free(extra);
+       }
+}
+
+/*
+ * Set a value with an extra label, the key should be a constant string while
+ * the value is copied into the extra label.
+ */
+void
+ometric_set_int_with_label(struct ometric *om, uint64_t val, const char *key,
+    const char *value, struct olabels *ol)
+{
+       struct olabels *extra;
+
+       extra = olabels_add_extra(ol, key, value);
+       ometric_set_int(om, val, extra);
+       olabels_free(extra);
+}
diff --git a/usr.sbin/bgpctl/ometric.h b/usr.sbin/bgpctl/ometric.h
new file mode 100644 (file)
index 0000000..01fc1fb
--- /dev/null
@@ -0,0 +1,49 @@
+/*     $OpenBSD: ometric.h,v 1.1 2022/10/17 12:01:19 claudio Exp $ */
+
+/*
+ * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum ometric_type {
+       OMT_UNKNOWN,
+       OMT_GAUGE,
+       OMT_COUNTER,
+       OMT_STATESET,
+       OMT_HISTOGRAM,
+       OMT_SUMMARY,
+       OMT_INFO,
+};
+
+struct ometric;
+struct olabels;
+
+struct ometric *ometric_new(enum ometric_type, const char *, const char *);
+struct ometric *ometric_new_state(const char * const *, size_t, const char *,
+                   const char *);
+void            ometric_free_all(void);
+struct olabels *olabels_new(const char * const *, const char **);
+void            olabels_free(struct olabels *);
+
+void            ometric_output_all(void);
+
+/* XXX how to pass attributes */
+/* functions to set gauge and counter metrics */
+void   ometric_set_int(struct ometric *, uint64_t, struct olabels *);
+void   ometric_set_float(struct ometric *, double, struct olabels *);
+void   ometric_set_info(struct ometric *, const char **, const char **,
+           struct olabels *); 
+void   ometric_set_state(struct ometric *, const char *, struct olabels *); 
+void   ometric_set_int_with_label(struct ometric *, uint64_t, const char *,
+           const char *, struct olabels *);
index 04b9cc2..34fe626 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output.c,v 1.29 2022/08/31 15:00:53 claudio Exp $ */
+/*     $OpenBSD: output.c,v 1.30 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -1120,5 +1120,5 @@ const struct output show_output = {
        .set = show_rib_set,
        .rtr = show_rtr,
        .result = show_result,
-       .tail = show_tail
+       .tail = show_tail,
 };
index dfd443d..6edfb86 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output_json.c,v 1.23 2022/08/31 15:00:53 claudio Exp $ */
+/*     $OpenBSD: output_json.c,v 1.24 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
@@ -1057,5 +1057,5 @@ const struct output json_output = {
        .set = json_rib_set,
        .rtr = json_rtr,
        .result = json_result,
-       .tail = json_tail
+       .tail = json_tail,
 };
diff --git a/usr.sbin/bgpctl/output_ometric.c b/usr.sbin/bgpctl/output_ometric.c
new file mode 100644 (file)
index 0000000..4f5e6e3
--- /dev/null
@@ -0,0 +1,339 @@
+/*     $OpenBSD: output_ometric.c,v 1.1 2022/10/17 12:01:19 claudio Exp $ */
+
+/*
+ * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "rde.h"
+#include "version.h"
+
+#include "bgpctl.h"
+#include "parser.h"
+#include "ometric.h"
+
+struct ometric *bgpd_info, *bgpd_scrape_time;
+struct ometric *peer_info, *peer_state, *peer_state_raw, *peer_up_time,
+                   *peer_down_time, *peer_last_read, *peer_last_write;
+struct ometric *peer_prefixes_transmit, *peer_prefixes_receive;
+struct ometric *peer_message_transmit, *peer_message_recieve;
+struct ometric *peer_update_transmit, *peer_update_pending,
+                  *peer_update_receive;
+struct ometric *peer_withdraw_transmit, *peer_withdraw_pending,
+                  *peer_withdraw_receive;
+struct ometric *peer_rr_req_transmit, *peer_rr_req_receive;
+struct ometric *peer_rr_borr_transmit, *peer_rr_borr_receive;
+struct ometric *peer_rr_eorr_transmit, *peer_rr_eorr_receive;
+struct ometric *rde_mem_size, *rde_mem_count, *rde_mem_ref_count;
+struct ometric *rde_set_size, *rde_set_count, *rde_table_count;
+
+struct timeval start_time, end_time;
+
+static void
+ometric_head(struct parse_result *arg)
+{
+       struct olabels *ol = NULL;
+       const char *keys[4] = { "nodename", "domainname", "release", NULL };
+       const char *values[4];
+       char hostname[HOST_NAME_MAX + 1];
+       char *domainname;
+
+       bgpd_info = ometric_new(OMT_INFO, "bgpd_info", "bgpd information");
+       bgpd_scrape_time = ometric_new(OMT_GAUGE, "bgpd_scrape_seconds",
+           "bgpd scrape time in seconds");
+
+       gettimeofday(&start_time, NULL);
+
+       if (gethostname(hostname, sizeof(hostname)))
+               err(1, "gethostname");
+       if ((domainname = strchr(hostname, '.')))
+               *domainname++ = '\0';
+
+       values[0] = hostname;
+       values[1] = domainname;
+       values[2] = BGPD_VERSION;
+       values[3] = NULL;
+
+       ol = olabels_new(keys, values);
+
+       ometric_set_info(bgpd_info, NULL, NULL, ol);
+
+       olabels_free(ol);
+
+       /*
+        * per neighbor stats: attrs will be remote_as, remote_addr,
+        * description and group
+        */
+       peer_info = ometric_new(OMT_INFO, "bgpd_peer_info",
+           "peer information");
+       peer_state = ometric_new_state(statenames,
+           sizeof(statenames) / sizeof(statenames[0]), "bgpd_peer_state",
+           "peer session state");
+       peer_state_raw = ometric_new(OMT_GAUGE, "bgpd_peer_state_raw",
+           "peer session state raw int value");
+       peer_up_time = ometric_new(OMT_GAUGE, "bgpd_peer_up_seconds",
+           "peer session up time in seconds");
+       peer_down_time = ometric_new(OMT_GAUGE, "bgpd_peer_down_seconds",
+           "peer session down time in seconds");
+       peer_last_read = ometric_new(OMT_GAUGE, "bgpd_peer_last_read_seconds",
+           "peer time since last read in seconds");
+       peer_last_write = ometric_new(OMT_GAUGE, "bgpd_peer_last_write_seconds",
+           "peer time since last write in seconds");
+
+       peer_prefixes_transmit = ometric_new(OMT_GAUGE,
+           "bgpd_peer_prefixes_transmit",
+           "number of prefixes sent to peer");
+       peer_prefixes_receive = ometric_new(OMT_GAUGE,
+           "bgpd_peer_prefixes_receive",
+           "number of prefixes received from peer");
+
+       peer_message_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_message_transmit_total",
+           "per message type count of tranmitted messages");
+       peer_message_recieve = ometric_new(OMT_COUNTER,
+           "bgpd_peer_message_receive_total",
+           "per message type count of received messages");
+
+       peer_update_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_update_transmit_total",
+           "number of prefixes sent as update");
+       peer_update_pending = ometric_new(OMT_COUNTER,
+           "bgpd_peer_update_pending_total",
+           "number of pending update prefixes");
+       peer_update_receive = ometric_new(OMT_COUNTER,
+           "bgpd_peer_update_receive_total",
+           "number of prefixes received as update");
+
+       peer_withdraw_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_withdraw_transmit_total",
+           "number of witdrawn prefixes sent to peer");
+       peer_withdraw_pending = ometric_new(OMT_COUNTER,
+           "bgpd_peer_withdraw_pending_total",
+           "number of pending withdrawn prefixes");
+       peer_withdraw_receive = ometric_new(OMT_COUNTER,
+           "bgpd_peer_withdraw_receive_total",
+           "number of withdrawn prefixes received from peer");
+
+       peer_rr_req_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_req_transmit_total",
+           "number of route-refresh request transmitted to peer");
+       peer_rr_req_receive = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_req_receive_total",
+           "number of route-refresh request received from peer");
+       peer_rr_borr_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_borr_transmit_total",
+           "number of ext. route-refresh BORR messages transmitted to peer");
+       peer_rr_borr_receive = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_borr_receive_total",
+           "number of ext. route-refresh BORR messages received from peer");
+       peer_rr_eorr_transmit = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_eorr_transmit_total",
+           "number of ext. route-refresh EORR messages transmitted to peer");
+       peer_rr_eorr_receive = ometric_new(OMT_COUNTER,
+           "bgpd_peer_route_refresh_eorr_receive_total",
+           "number of ext. route-refresh EORR messages received from peer");
+
+       /* RDE memory statistics */
+       rde_mem_size = ometric_new(OMT_GAUGE,
+           "bgpd_rde_memory_usage_bytes", "memory usage in bytes");
+       rde_mem_count = ometric_new(OMT_GAUGE,
+           "bgpd_rde_memory_count", "number of object in use");
+       rde_mem_ref_count = ometric_new(OMT_GAUGE,
+           "bgpd_rde_memory_reference_count", "number of references held");
+
+       rde_set_size = ometric_new(OMT_GAUGE,
+           "bgpd_rde_set_usage_bytes", "memory usage of set in bytes");
+       rde_set_count = ometric_new(OMT_GAUGE,
+           "bgpd_rde_set_count", "number of object in set");
+       rde_table_count = ometric_new(OMT_GAUGE,
+           "bgpd_rde_table_count", "number of as_set tables");
+}
+
+static void
+ometric_neighbor_stats(struct peer *p, struct parse_result *arg)
+{
+       struct olabels *ol = NULL;
+       const char *keys[5] = {
+           "remote_addr", "remote_as", "description", "group", NULL };
+       const char *values[5];
+
+       /* skip neighbor templates */
+       if (p->conf.template)
+               return;
+
+       values[0] = log_addr(&p->conf.remote_addr);
+       values[1] = log_as(p->conf.remote_as);
+       values[2] = p->conf.descr;
+       values[3] = p->conf.group;
+       values[4] = NULL;
+
+       ol = olabels_new(keys, values);
+
+       ometric_set_info(peer_info, NULL, NULL, ol);
+       ometric_set_state(peer_state, statenames[p->state], ol);
+       ometric_set_int(peer_state_raw, p->state, ol);
+
+       if (p->state == STATE_ESTABLISHED) {
+               ometric_set_int(peer_up_time,
+                   get_monotime(p->stats.last_updown), ol);
+               ometric_set_int(peer_last_read,
+                   get_monotime(p->stats.last_read), ol);
+               ometric_set_int(peer_last_write,
+                   get_monotime(p->stats.last_write), ol);
+       } else if (p->stats.last_updown != 0)
+               ometric_set_int(peer_down_time,
+                   get_monotime(p->stats.last_updown), ol);
+
+       ometric_set_int(peer_prefixes_transmit, p->stats.prefix_out_cnt, ol);
+       ometric_set_int(peer_prefixes_receive, p->stats.prefix_cnt, ol);
+
+       ometric_set_int_with_label(peer_message_transmit,
+           p->stats.msg_sent_open, "message", "open", ol);
+       ometric_set_int_with_label(peer_message_transmit,
+           p->stats.msg_sent_notification, "message", "notification", ol);
+       ometric_set_int_with_label(peer_message_transmit,
+           p->stats.msg_sent_update, "message", "update", ol);
+       ometric_set_int_with_label(peer_message_transmit,
+           p->stats.msg_sent_keepalive, "message", "keepalive", ol);
+       ometric_set_int_with_label(peer_message_transmit,
+           p->stats.msg_sent_rrefresh, "message", "route_refresh", ol);
+
+       ometric_set_int_with_label(peer_message_recieve,
+           p->stats.msg_rcvd_open, "message", "open", ol);
+       ometric_set_int_with_label(peer_message_recieve,
+           p->stats.msg_rcvd_notification, "message", "notification", ol);
+       ometric_set_int_with_label(peer_message_recieve,
+           p->stats.msg_rcvd_update, "message", "update", ol);
+       ometric_set_int_with_label(peer_message_recieve,
+           p->stats.msg_rcvd_keepalive, "message", "keepalive", ol);
+       ometric_set_int_with_label(peer_message_recieve,
+           p->stats.msg_rcvd_rrefresh, "message", "route_refresh", ol);
+
+       ometric_set_int(peer_update_transmit, p->stats.prefix_sent_update, ol);
+       ometric_set_int(peer_update_pending, p->stats.pending_update, ol);
+       ometric_set_int(peer_update_receive, p->stats.prefix_rcvd_update, ol);
+       ometric_set_int(peer_withdraw_transmit, p->stats.prefix_sent_withdraw,
+           ol);
+       ometric_set_int(peer_withdraw_pending, p->stats.pending_withdraw, ol);
+       ometric_set_int(peer_withdraw_receive, p->stats.prefix_rcvd_withdraw,
+           ol);
+
+       ometric_set_int(peer_rr_req_transmit, p->stats.refresh_sent_req, ol);
+       ometric_set_int(peer_rr_req_receive, p->stats.refresh_rcvd_req, ol);
+       ometric_set_int(peer_rr_borr_transmit, p->stats.refresh_sent_borr, ol);
+       ometric_set_int(peer_rr_borr_receive, p->stats.refresh_rcvd_borr, ol);
+       ometric_set_int(peer_rr_eorr_transmit, p->stats.refresh_sent_eorr, ol);
+       ometric_set_int(peer_rr_eorr_receive, p->stats.refresh_rcvd_eorr, ol);
+
+       olabels_free(ol);
+}
+
+static void
+ometric_rib_mem_element(const char *v, uint64_t count, uint64_t size,
+    uint64_t refs)
+{
+       if (count != UINT64_MAX)
+               ometric_set_int_with_label(rde_mem_count, count, "type", v,
+                   NULL);
+       if (size != UINT64_MAX)
+               ometric_set_int_with_label(rde_mem_size, size, "type", v, NULL);
+       if (refs != UINT64_MAX)
+               ometric_set_int_with_label(rde_mem_ref_count, refs, "type", v,
+                   NULL);
+}
+
+static void
+ometric_rib_mem(struct rde_memstats *stats)
+{
+       size_t pts = 0;
+       int i;
+
+       for (i = 0; i < AID_MAX; i++) {
+               if (stats->pt_cnt[i] == 0)
+                       continue;
+               pts += stats->pt_cnt[i] * pt_sizes[i];
+               ometric_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i],
+                   stats->pt_cnt[i] * pt_sizes[i], UINT64_MAX);
+       }
+       ometric_rib_mem_element("rib", stats->rib_cnt,
+           stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX);
+       ometric_rib_mem_element("prefix", stats->prefix_cnt,
+           stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX);
+       ometric_rib_mem_element("rde_aspath", stats->path_cnt,
+           stats->path_cnt * sizeof(struct rde_aspath),
+           stats->path_refs);
+       ometric_rib_mem_element("aspath", stats->aspath_cnt,
+           stats->aspath_size, UINT64_MAX);
+       ometric_rib_mem_element("community_entries", stats->comm_cnt,
+           stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX);
+       ometric_rib_mem_element("community", stats->comm_nmemb,
+           stats->comm_size * sizeof(struct community), stats->comm_refs);
+       ometric_rib_mem_element("attributes_entries", stats->attr_cnt,
+           stats->attr_cnt * sizeof(struct attr), stats->attr_refs);
+       ometric_rib_mem_element("attributes", stats->attr_dcnt,
+           stats->attr_data, UINT64_MAX);
+
+       ometric_rib_mem_element("total", UINT64_MAX, 
+           pts + stats->prefix_cnt * sizeof(struct prefix) +
+           stats->rib_cnt * sizeof(struct rib_entry) +
+           stats->path_cnt * sizeof(struct rde_aspath) +
+           stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
+           stats->attr_data, UINT64_MAX);
+
+       ometric_set_int(rde_table_count, stats->aset_cnt, NULL);
+       ometric_set_int_with_label(rde_set_size, stats->aset_size,
+          "type", "as_set", NULL);
+       ometric_set_int_with_label(rde_set_count, stats->aset_nmemb,
+          "type", "as_set", NULL);
+       ometric_set_int_with_label(rde_set_size, stats->pset_size,
+          "type", "prefix_set", NULL);
+       ometric_set_int_with_label(rde_set_count, stats->pset_cnt,
+          "type", "prefix_set", NULL);
+       ometric_rib_mem_element("set_total", UINT64_MAX, 
+           stats->aset_size + stats->pset_size, UINT64_MAX);
+}
+
+static void
+ometric_tail(void)
+{
+       struct timeval elapsed_time;
+       double scrape;
+
+       gettimeofday(&end_time, NULL);
+       timersub(&end_time, &start_time, &elapsed_time);
+
+       scrape = (double)elapsed_time.tv_sec +
+           (double)elapsed_time.tv_usec / 1000000;
+
+       ometric_set_float(bgpd_scrape_time, scrape, NULL);
+       ometric_output_all();
+
+       ometric_free_all();
+}
+
+const struct output ometric_output = {
+       .head = ometric_head,
+       .neighbor = ometric_neighbor_stats,
+       .rib_mem = ometric_rib_mem,
+       .tail = ometric_tail,
+};
index 00682ff..7838deb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parser.c,v 1.114 2022/08/17 15:16:12 claudio Exp $ */
+/*     $OpenBSD: parser.c,v 1.115 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -141,6 +141,7 @@ static const struct token t_show[] = {
        { KEYWORD,      "sets",         SHOW_SET,       NULL},
        { KEYWORD,      "rtr",          SHOW_RTR,       NULL},
        { KEYWORD,      "mrt",          SHOW_MRT,       t_show_mrt},
+       { KEYWORD,      "metric",       SHOW_METRIC,    NULL},
        { ENDTOKEN,     "",             NONE,           NULL}
 };
 
index 89634a2..45fea19 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parser.h,v 1.42 2022/02/06 09:52:32 claudio Exp $ */
+/*     $OpenBSD: parser.h,v 1.43 2022/10/17 12:01:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -37,6 +37,7 @@ enum actions {
        SHOW_RIB_MEM,
        SHOW_NEXTHOP,
        SHOW_INTERFACE,
+       SHOW_METRIC,
        RELOAD,
        FIB,
        FIB_COUPLE,