1/*	$OpenBSD: output-ometric.c,v 1.10 2024/04/08 14:02:13 tb Exp $ */
2/*
3 * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <err.h>
19#include <limits.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include "extern.h"
26#include "ometric.h"
27#include "version.h"
28
29static struct ometric *rpki_info, *rpki_completion_time, *rpki_duration;
30static struct ometric *rpki_repo, *rpki_obj, *rpki_ta_obj;
31static struct ometric *rpki_repo_obj, *rpki_repo_duration;
32static struct ometric *rpki_repo_state, *rpki_repo_proto;
33
34static const char * const repo_states[2] = { "failed", "synced" };
35static const char * const repo_protos[3] = { "rrdp", "rsync", "https" };
36
37static void
38set_common_stats(const struct repotalstats *in, struct ometric *metric,
39    struct olabels *ol)
40{
41	ometric_set_int_with_labels(metric, in->certs,
42	    OKV("type", "state"), OKV("cert", "valid"), ol);
43	ometric_set_int_with_labels(metric, in->certs_fail,
44	    OKV("type", "state"), OKV("cert", "failed parse"), ol);
45
46	ometric_set_int_with_labels(metric, in->mfts,
47	    OKV("type", "state"), OKV("manifest", "valid"), ol);
48	ometric_set_int_with_labels(metric, in->mfts_fail,
49	    OKV("type", "state"), OKV("manifest", "failed parse"), ol);
50
51	ometric_set_int_with_labels(metric, in->roas,
52	    OKV("type", "state"), OKV("roa", "valid"), ol);
53	ometric_set_int_with_labels(metric, in->roas_fail,
54	    OKV("type", "state"), OKV("roa", "failed parse"), ol);
55	ometric_set_int_with_labels(metric, in->roas_invalid,
56	    OKV("type", "state"), OKV("roa", "invalid"), ol);
57
58	ometric_set_int_with_labels(metric, in->aspas,
59	    OKV("type", "state"), OKV("aspa", "valid"), ol);
60	ometric_set_int_with_labels(metric, in->aspas_fail,
61	    OKV("type", "state"), OKV("aspa", "failed parse"), ol);
62	ometric_set_int_with_labels(metric, in->aspas_invalid,
63	    OKV("type", "state"), OKV("aspa", "invalid"), ol);
64
65	ometric_set_int_with_labels(metric, in->brks,
66	    OKV("type", "state"), OKV("router_key", "valid"), ol);
67	ometric_set_int_with_labels(metric, in->crls,
68	    OKV("type", "state"), OKV("crl", "valid"), ol);
69	ometric_set_int_with_labels(metric, in->gbrs,
70	    OKV("type", "state"), OKV("gbr", "valid"), ol);
71	ometric_set_int_with_labels(metric, in->taks,
72	    OKV("type", "state"), OKV("tak", "valid"), ol);
73
74	ometric_set_int_with_labels(metric, in->vrps,
75	    OKV("type", "state"), OKV("vrp", "total"), ol);
76	ometric_set_int_with_labels(metric, in->vrps_uniqs,
77	    OKV("type", "state"), OKV("vrp", "unique"), ol);
78
79	ometric_set_int_with_labels(metric, in->vaps,
80	    OKV("type", "state"), OKV("vap", "total"), ol);
81	ometric_set_int_with_labels(metric, in->vaps_uniqs,
82	    OKV("type", "state"), OKV("vap", "unique"), ol);
83	ometric_set_int_with_labels(metric, in->vaps_pas,
84	    OKV("type", "state"), OKV("vap providers", "total"), ol);
85	ometric_set_int_with_labels(metric, in->vaps_overflowed,
86	    OKV("type", "state"), OKV("vap overflowed"), ol);
87
88	ometric_set_int_with_labels(metric, in->spls,
89	    OKV("type", "state"), OKV("spl", "valid"), ol);
90	ometric_set_int_with_labels(metric, in->spls_fail,
91	    OKV("type", "state"), OKV("spl", "failed parse"), ol);
92	ometric_set_int_with_labels(metric, in->spls_invalid,
93	    OKV("type", "state"), OKV("spl", "invalid"), ol);
94
95	ometric_set_int_with_labels(metric, in->vsps,
96	    OKV("type", "state"), OKV("vsp", "total"), ol);
97	ometric_set_int_with_labels(metric, in->vsps_uniqs,
98	    OKV("type", "state"), OKV("vsp", "unique"), ol);
99}
100
101static void
102ta_stats(int id)
103{
104	struct olabels *ol;
105	const char *keys[2] = { "name", NULL };
106	const char *values[2];
107
108	values[0] = taldescs[id];
109	values[1] = NULL;
110
111	ol = olabels_new(keys, values);
112	set_common_stats(&talstats[id], rpki_ta_obj, ol);
113	olabels_free(ol);
114}
115
116static void
117repo_tal_stats(const struct repo *rp, const struct repotalstats *in, void *arg)
118{
119	struct olabels *ol;
120	const char *keys[4] = { "name", "carepo", "notify", NULL };
121	const char *values[4];
122	int talid = *(int *)arg;
123
124	values[0] = taldescs[talid];
125	repo_fetch_uris(rp, &values[1], &values[2]);
126	values[3] = NULL;
127
128	ol = olabels_new(keys, values);
129	set_common_stats(in, rpki_repo_obj, ol);
130	olabels_free(ol);
131}
132
133static void
134repo_stats(const struct repo *rp, const struct repostats *in, void *arg)
135{
136	struct olabels *ol;
137	const char *keys[3] = { "carepo", "notify", NULL };
138	const char *values[3];
139
140	repo_fetch_uris(rp, &values[0], &values[1]);
141	values[2] = NULL;
142
143	ol = olabels_new(keys, values);
144	ometric_set_timespec(rpki_repo_duration, &in->sync_time, ol);
145
146	ometric_set_int_with_labels(rpki_repo_obj, in->new_files,
147	    OKV("type", "state"), OKV("files", "new"), ol);
148	ometric_set_int_with_labels(rpki_repo_obj, in->del_files,
149	    OKV("type", "state"), OKV("files", "deleted"), ol);
150	ometric_set_int_with_labels(rpki_repo_obj, in->extra_files,
151	    OKV("type", "state"), OKV("files", "extra"), ol);
152	ometric_set_int_with_labels(rpki_repo_obj, in->del_extra_files,
153	    OKV("type", "state"), OKV("files", "deleted_extra"), ol);
154	ometric_set_int_with_labels(rpki_repo_obj, in->del_dirs,
155	    OKV("type", "state"), OKV("dirs", "deleted"), ol);
156
157	ometric_set_state(rpki_repo_state, repo_states[repo_synced(rp)], ol);
158	if (repo_synced(rp))
159		ometric_set_state(rpki_repo_proto, repo_proto(rp), ol);
160	olabels_free(ol);
161}
162
163int
164output_ometric(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks,
165    struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st)
166{
167	struct olabels *ol;
168	const char *keys[4] = { "nodename", "domainname", "release", NULL };
169	const char *values[4];
170	char hostname[HOST_NAME_MAX + 1];
171	char *domainname;
172	struct timespec now_time;
173	int rv, i;
174
175	rpki_info = ometric_new(OMT_INFO, "rpki_client",
176	    "rpki-client information");
177	rpki_completion_time = ometric_new(OMT_GAUGE,
178	    "rpki_client_job_completion_time",
179	    "end of this run as epoch timestamp");
180
181	rpki_repo = ometric_new(OMT_GAUGE, "rpki_client_repository",
182	    "total number of repositories");
183	rpki_obj = ometric_new(OMT_GAUGE, "rpki_client_objects",
184	    "total number of objects");
185
186	rpki_duration = ometric_new(OMT_GAUGE, "rpki_client_duration",
187	    "duration in seconds");
188
189	rpki_ta_obj = ometric_new(OMT_GAUGE, "rpki_client_ta_objects",
190	    "total number of objects per TAL");
191	rpki_repo_obj = ometric_new(OMT_GAUGE, "rpki_client_repository_objects",
192	    "total number of objects per repository");
193	rpki_repo_duration = ometric_new(OMT_GAUGE,
194	    "rpki_client_repository_duration",
195	    "duration used to sync this repository in seconds");
196	rpki_repo_state = ometric_new_state(repo_states,
197	    sizeof(repo_states) / sizeof(repo_states[0]),
198	    "rpki_client_repository_state",
199	    "repository state");
200	rpki_repo_proto = ometric_new_state(repo_protos,
201	    sizeof(repo_protos) / sizeof(repo_protos[0]),
202	    "rpki_client_repository_protos",
203	    "used protocol to sync repository");
204
205	/*
206	 * Dump statistics
207	 */
208	if (gethostname(hostname, sizeof(hostname)))
209		err(1, "gethostname");
210	if ((domainname = strchr(hostname, '.')))
211		*domainname++ = '\0';
212
213	values[0] = hostname;
214	values[1] = domainname;
215	values[2] = RPKI_VERSION;
216	values[3] = NULL;
217
218	ol = olabels_new(keys, values);
219	ometric_set_info(rpki_info, NULL, NULL, ol);
220	olabels_free(ol);
221
222	for (i = 0; i < talsz; i++) {
223		repo_tal_stats_collect(repo_tal_stats, i, &i);
224		ta_stats(i);
225	}
226	repo_stats_collect(repo_stats, NULL);
227	set_common_stats(&st->repo_tal_stats, rpki_obj, NULL);
228
229	ometric_set_int_with_labels(rpki_repo, st->rsync_repos,
230	    OKV("type", "state"), OKV("rsync", "synced"), NULL);
231	ometric_set_int_with_labels(rpki_repo, st->rsync_fails,
232	    OKV("type", "state"), OKV("rsync", "failed"), NULL);
233	ometric_set_int_with_labels(rpki_repo, st->http_repos,
234	    OKV("type", "state"), OKV("http", "synced"), NULL);
235	ometric_set_int_with_labels(rpki_repo, st->http_fails,
236	    OKV("type", "state"), OKV("http", "failed"), NULL);
237	ometric_set_int_with_labels(rpki_repo, st->rrdp_repos,
238	    OKV("type", "state"), OKV("rrdp", "synced"), NULL);
239	ometric_set_int_with_labels(rpki_repo, st->rrdp_fails,
240	    OKV("type", "state"), OKV("rrdp", "failed"), NULL);
241
242	ometric_set_timespec_with_labels(rpki_duration, &st->elapsed_time,
243	    OKV("type"), OKV("elapsed"), NULL);
244	ometric_set_timespec_with_labels(rpki_duration, &st->user_time,
245	    OKV("type"), OKV("user"), NULL);
246	ometric_set_timespec_with_labels(rpki_duration, &st->system_time,
247	    OKV("type"), OKV("system"), NULL);
248
249	clock_gettime(CLOCK_REALTIME, &now_time);
250	ometric_set_timespec(rpki_completion_time, &now_time, NULL);
251
252	rv = ometric_output_all(out);
253	ometric_free_all();
254
255	return rv;
256}
257