1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Radosław Korzeniewski, MMXVIII
21 * radoslaw@korzeniewski.net, radekk@inteos.pl
22 * Inteos Sp. z o.o. http://www.inteos.pl/
23 *
24 * This is a Bacula statistics support utilities.
25 * Author: Radosław Korzeniewski, radekk@inteos.pl, Inteos Sp. z o.o.
26 */
27
28 #include "bacula.h"
29
30 /*
31 * Scans a display format parameter and return appropriate enum value.
32 *
33 * in:
34 * buf - the command argument string to check
35 * out:
36 * appropriate display_format_t value, defaults to COLLECT_SIMPLE
37 */
scandisplayformat(POOLMEM * buf)38 display_format_t scandisplayformat(POOLMEM *buf)
39 {
40 if (bstrcmp(buf, "json")){
41 return COLLECT_JSON;
42 }
43 if (bstrcmp(buf, "full")){
44 return COLLECT_FULL;
45 }
46 return COLLECT_SIMPLE;
47 };
48
49 /*
50 * Render a simple representation of the metric into a buffer.
51 *
52 * in:
53 * out - a memory buffer where render to
54 * m - a bstatmetric object to render its value
55 * out:
56 * rendered metric name and value in simple format at buf
57 */
rendermetricsimple(POOL_MEM & out,bstatmetric * m)58 void rendermetricsimple(POOL_MEM &out, bstatmetric *m)
59 {
60 POOL_MEM buf(PM_MESSAGE);
61
62 m->render_metric_value(buf);
63 Mmsg(out, "%s=%s\n", m->name, buf.c_str());
64 };
65
66 /*
67 * Render a JSON representation of the metric into a buffer.
68 *
69 * in:
70 * out - a memory buffer where render to
71 * m - a bstatmetric object to render its value
72 * out:
73 * rendered metric in JSON format at buf
74 *
75 * JSON example output implemented:
76 * [
77 * {
78 * name: "bacula.jobs.all",
79 * value: 228,
80 * type: "Integer",
81 * unit: "Jobs",
82 * description: "Number of all jobs."
83 * },
84 * ]
85 * - the array brackets are delivered outside this function
86 */
rendermetricjson(POOL_MEM & out,bstatmetric * m,int nr)87 void rendermetricjson(POOL_MEM &out, bstatmetric *m, int nr)
88 {
89 POOL_MEM buf(PM_MESSAGE);
90
91 m->render_metric_value(buf, true);
92 Mmsg(out, "%s {\n \"name\": \"%s\",\n \"value\": %s,\n \"type\": \"%s\",\n \"unit\": \"%s\",\n \"description\": \"%s\"\n }",
93 nr > 0 ? ",\n":"\n", m->name, buf.c_str(), m->metric_type_str(), m->metric_unit_str(), m->description);
94 };
95
96 /*
97 * Render a full representation of the metric into a buffer.
98 *
99 * in:
100 * out - a memory buffer where render to
101 * m - a bstatmetric object to render its value
102 * out:
103 * rendered metric at buf
104 */
rendermetricfull(POOL_MEM & out,bstatmetric * m)105 void rendermetricfull(POOL_MEM &out, bstatmetric *m)
106 {
107 POOL_MEM buf(PM_MESSAGE);
108
109 m->render_metric_value(buf);
110 Mmsg(out, "name=\"%s\" value=%s type=%s unit=%s descr=\"%s\"\n", m->name, buf.c_str(), m->metric_type_str(),
111 m->metric_unit_str(), m->description);
112 };
113
114 /*
115 * Render metric into a buffer based on display format provided.
116 *
117 * in:
118 * out - a memory buffer where render to
119 * m - a bstatmetric object to render its value
120 * format - display format enum
121 * out:
122 * rendered metric at buf
123 */
rendermetric(POOL_MEM & out,bstatmetric * m,display_format_t format,int nr)124 void rendermetric(POOL_MEM &out, bstatmetric *m, display_format_t format, int nr)
125 {
126 switch (format){
127 case COLLECT_SIMPLE:
128 rendermetricsimple(out, m);
129 break;
130 case COLLECT_JSON:
131 rendermetricjson(out, m, nr);
132 break;
133 case COLLECT_FULL:
134 rendermetricfull(out, m);
135 break;
136 }
137 };
138
139 /*
140 * Return a string representation of the display format enum.
141 */
displayformat2str(display_format_t format)142 const char *displayformat2str(display_format_t format)
143 {
144 switch (format){
145 case COLLECT_SIMPLE:
146 return "simple";
147 case COLLECT_JSON:
148 return "json";
149 case COLLECT_FULL:
150 return "full";
151 default:
152 return "simple";
153 }
154 };
155
156 /*
157 * Return a string representation of the collector status.
158 *
159 * in:
160 * res_collector - a COLLECTOR resource for collector backend
161 * out:
162 * string representation of the collector status
163 */
str_collector_status(COLLECTOR & res_collector)164 const char *str_collector_status(COLLECTOR &res_collector)
165 {
166 const char *status;
167
168 if (res_collector.valid){
169 status = res_collector.running?"running":"stopped";
170 } else {
171 status = res_collector.running?"waiting to exit":"stopped";
172 }
173 return status;
174 };
175
176 /*
177 * Return a string representation of the collector spooling status.
178 *
179 * in:
180 * res_collector - a COLLECTOR resource for collector backend
181 * out:
182 * string representation of the collector spooling status
183 */
str_collector_spooling(COLLECTOR & res_collector)184 const char *str_collector_spooling(COLLECTOR &res_collector)
185 {
186 const char *spool;
187
188 if (res_collector.spool_directory){
189 /* spooling defined */
190 switch (res_collector.spooled){
191 case BCOLLECT_SPOOL_YES:
192 spool = "in progress";
193 break;
194 case BCOLLECT_SPOOL_DESPOOL:
195 spool = "despooling now";
196 break;
197 case BCOLLECT_SPOOL_NO:
198 spool = "enabled";
199 break;
200 default:
201 spool = "unknown (enabled)";
202 }
203 } else {
204 spool = "disabled";
205 }
206 return spool;
207 };
208
209 /*
210 * A support function renders a Statistics resource status into a buffer.
211 *
212 * in:
213 * res_collector - a COLLECTOR resource class to display
214 * buf - a POLL_MEM buffer to render into
215 * out:
216 * the length of rendered text
217 */
render_collector_status(COLLECTOR & res_collector,POOL_MEM & buf)218 int render_collector_status(COLLECTOR &res_collector, POOL_MEM &buf)
219 {
220 const char *status, *spool;
221 char dt[MAX_TIME_LENGTH];
222 time_t t;
223 utime_t i;
224 int len;
225 POOL_MEM errmsg(PM_MESSAGE);
226
227 res_collector.lock();
228 status = str_collector_status(res_collector);
229 t = res_collector.timestamp;
230 i = res_collector.interval;
231 spool = str_collector_spooling(res_collector);
232 if (res_collector.errmsg && strlen(res_collector.errmsg)){
233 Mmsg(errmsg, " lasterror=%s\n", res_collector.errmsg);
234 } else {
235 pm_strcpy(errmsg, "");
236 }
237 res_collector.unlock();
238
239 bstrftime_nc(dt, sizeof(dt), t);
240 len = Mmsg(buf, "Statistics backend: %s is %s\n type=%i lasttimestamp=%s\n interval=%d secs\n spooling=%s\n%s\n",
241 res_collector.hdr.name, status,
242 res_collector.type, dt,
243 i, spool,
244 errmsg.c_str());
245 return len;
246 };
247
248 /*
249 * A support function renders a Statistics resource status into an OutputWriter for APIv2.
250 *
251 * in:
252 * res_collector - a COLLECTOR resource class to display
253 * ow - OutputWriter for apiv2
254 * out:
255 * rendered status in OutputWritter buffer
256 */
api_render_collector_status(COLLECTOR & res_collector,OutputWriter & ow)257 void api_render_collector_status(COLLECTOR &res_collector, OutputWriter &ow)
258 {
259 const char *status, *spool;
260 time_t t;
261 utime_t i;
262
263 res_collector.lock();
264 status = str_collector_status(res_collector);
265 t = res_collector.timestamp;
266 i = res_collector.interval;
267 spool = str_collector_spooling(res_collector);
268 res_collector.unlock();
269 ow.get_output(
270 OT_START_OBJ,
271 OT_STRING, "name", res_collector.hdr.name,
272 OT_STRING, "status", status,
273 OT_INT, "interval", i,
274 OT_UTIME, "lasttimestamp", t,
275 OT_STRING, "spooling", spool,
276 OT_STRING, "lasterror", NPRTB(res_collector.errmsg),
277 OT_END_OBJ,
278 OT_END
279 );
280 return;
281 };
282
283 /*
284 * Replace dot '.' character for "%32" to avoid metric level split
285 */
replace_dot_metric_name(POOL_MEM & out,const char * name)286 char *replace_dot_metric_name(POOL_MEM &out, const char *name)
287 {
288 char *p, *q;
289 POOL_MEM tmp(PM_NAME);
290
291 pm_strcpy(out, NULL);
292 pm_strcpy(tmp, name);
293 p = tmp.c_str();
294 while((q = strchr(p, '.')) != NULL){
295 /* q is the dot char and p is a start of the substring to copy */
296 *q = 0; // temporary terminate substring
297 pm_strcat(out, p);
298 pm_strcat(out, "%32");
299 // printf(">%s<", out.c_str());
300 p = q + 1; // next substring
301 }
302 pm_strcat(out, p);
303 return out.c_str();
304 };
305
306 /*
307 * Return the metric name updated with Statistics Prefix parameter if defined.
308 */
render_metric_prefix(COLLECTOR * collector,POOL_MEM & buf,bstatmetric * item)309 void render_metric_prefix(COLLECTOR *collector, POOL_MEM &buf, bstatmetric *item)
310 {
311 POOL_MEM name(PM_NAME);
312
313 if (collector && item){
314 if (collector->mangle_name){
315 replace_dot_metric_name(name, item->name);
316 } else {
317 Mmsg(name, "%s", item->name);
318 }
319 if (collector->prefix){
320 /* $prefix.$metric */
321 Mmsg(buf, "%s.%s", collector->prefix, name.c_str());
322 } else {
323 /* $metric */
324 Mmsg(buf, "%s", name.c_str());
325 }
326 Dmsg2(1500, "Statistics: %s met&prefix: %s\n", collector->hdr.name, buf.c_str());
327 }
328 };
329
330
331 #ifndef TEST_PROGRAM
332 #define TEST_PROGRAM_A
333 #endif
334
335 #ifdef TEST_PROGRAM
336 #include "unittests.h"
337
338 struct test {
339 const int nr;
340 const char *in;
341 const char *out;
342 };
343
344 static struct test tests[] = {
345 {1, "abc.def", "abc%32def"},
346 {2, "", ""},
347 {3, ".abc", "%32abc"},
348 {4, "abc.", "abc%32"},
349 {5, "abc..def", "abc%32%32def"},
350 {6, "abc..", "abc%32%32"},
351 {7, "..def", "%32%32def"},
352 {8, ".......", "%32%32%32%32%32%32%32"},
353 {0, NULL, NULL},
354 };
355
356 #define ntests ((int)(sizeof(tests)/sizeof(struct test)))
357
main()358 int main()
359 {
360 Unittests collect_test("collect_test");
361 POOL_MEM name(PM_NAME);
362 char buf[64];
363
364 for (int i = 0; i < ntests; i++) {
365 if (tests[i].nr > 0){
366 snprintf(buf, 64, "Checking mangle test: %d - '%s'", tests[i].nr, tests[i].in);
367 ok(strcmp(replace_dot_metric_name(name, tests[i].in), tests[i].out) == 0, buf);
368 }
369 }
370
371 return report();
372 }
373 #endif /* TEST_PROGRAM */
374