1 /*****************************************************************************
2 * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
3 *****************************************************************************
4 * change header parameters of an rrd
5 *****************************************************************************
6 * $Id$
7 * $Log$
8 * Revision 1.6 2004/05/26 22:11:12 oetiker
9 * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
10 *
11 * Revision 1.5 2002/02/01 20:34:49 oetiker
12 * fixed version number and date/time
13 *
14 * Revision 1.4 2001/08/22 22:29:07 jake
15 * Contents of this patch:
16 * (1) Adds/revises documentation for rrd tune in rrd_tool.c and pod files.
17 * (2) Moves some initialization code from rrd_create.c to rrd_hw.c.
18 * (3) Adds another pass to smoothing for SEASONAL and DEVSEASONAL RRAs.
19 * This pass computes the coefficients as deviations from an average; the
20 * average is added the baseline coefficient of HWPREDICT. Statistical texts
21 * suggest this to preserve algorithm stability. It will not invalidate
22 * RRD files created and smoothed with the old code.
23 * (4) Adds the aberrant-reset flag to rrd tune. This operation, which is
24 * specified for a single data source, causes the holt-winters algorithm to
25 * forget everthing it has learned and start over.
26 * (5) Fixes a few out-of-date code comments.
27 *
28 * Revision 1.3 2001/03/07 21:21:54 oetiker
29 * complete rewrite of rrdgraph documentation. This also includs info
30 * on upcomming/planned changes to the rrdgraph interface and functionality
31 * -- Alex van den Bogaerdt <alex@slot.hollandcasino.nl>
32 *
33 * Revision 1.2 2001/03/04 13:01:55 oetiker
34 * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
35 * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
36 * This is backwards compatible! But new files using the Aberrant stuff are not readable
37 * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
38 * -- Jake Brutlag <jakeb@corp.webtv.net>
39 *
40 *****************************************************************************/
42 #include "rrd_tool.h"
43 #include "rrd_rpncalc.h"
44 #include "rrd_hw.h"
46 int set_hwarg(
47 rrd_t *rrd,
48 enum cf_en cf,
49 enum rra_par_en rra_par,
50 char *arg);
51 int set_deltaarg(
52 rrd_t *rrd,
53 enum rra_par_en rra_par,
54 char *arg);
55 int set_windowarg(
56 rrd_t *rrd,
57 enum rra_par_en,
58 char *arg);
60 int rrd_tune(
61 int argc,
62 char **argv)
63 {
64 rrd_t rrd;
65 int matches;
66 int optcnt = 0;
67 long ds;
68 char ds_nam[DS_NAM_SIZE];
69 char ds_new[DS_NAM_SIZE];
70 long heartbeat;
71 double min;
72 double max;
73 char dst[DST_SIZE];
74 rrd_file_t *rrd_file;
76 optind = 0;
77 opterr = 0; /* initialize getopt */
80 rrd_file = rrd_open(argv[1], &rrd, RRD_READWRITE);
81 if (rrd_file == NULL) {
82 return -1;
83 }
85 while (1) {
86 static struct option long_options[] = {
87 {"heartbeat", required_argument, 0, 'h'},
88 {"minimum", required_argument, 0, 'i'},
89 {"maximum", required_argument, 0, 'a'},
90 {"data-source-type", required_argument, 0, 'd'},
91 {"data-source-rename", required_argument, 0, 'r'},
92 /* added parameter tuning options for aberrant behavior detection */
93 {"deltapos", required_argument, 0, 'p'},
94 {"deltaneg", required_argument, 0, 'n'},
95 {"window-length", required_argument, 0, 'w'},
96 {"failure-threshold", required_argument, 0, 'f'},
97 {"alpha", required_argument, 0, 'x'},
98 {"beta", required_argument, 0, 'y'},
99 {"gamma", required_argument, 0, 'z'},
100 {"gamma-deviation", required_argument, 0, 'v'},
101 {"aberrant-reset", required_argument, 0, 'b'},
102 {0, 0, 0, 0}
103 };
104 int option_index = 0;
105 int opt;
107 opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
108 long_options, &option_index);
109 if (opt == EOF)
110 break;
112 optcnt++;
113 switch (opt) {
114 case 'h':
115 if ((matches =
116 sscanf(optarg, DS_NAM_FMT ":%ld", ds_nam,
117 &heartbeat)) != 2) {
118 rrd_set_error("invalid arguments for heartbeat");
119 rrd_free(&rrd);
120 close(rrd_file->fd);
121 return -1;
122 }
123 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
124 rrd_free(&rrd);
125 close(rrd_file->fd);
126 return -1;
127 }
128 rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
129 break;
131 case 'i':
132 if ((matches =
133 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &min)) < 1) {
134 rrd_set_error("invalid arguments for minimum ds value");
135 rrd_free(&rrd);
136 close(rrd_file->fd);
137 return -1;
138 }
139 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
140 rrd_free(&rrd);
141 close(rrd_file->fd);
142 return -1;
143 }
145 if (matches == 1)
146 min = DNAN;
147 rrd.ds_def[ds].par[DS_min_val].u_val = min;
148 break;
150 case 'a':
151 if ((matches =
152 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &max)) < 1) {
153 rrd_set_error("invalid arguments for maximum ds value");
154 rrd_free(&rrd);
155 close(rrd_file->fd);
156 return -1;
157 }
158 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
159 rrd_free(&rrd);
160 close(rrd_file->fd);
161 return -1;
162 }
163 if (matches == 1)
164 max = DNAN;
165 rrd.ds_def[ds].par[DS_max_val].u_val = max;
166 break;
168 case 'd':
169 if ((matches =
170 sscanf(optarg, DS_NAM_FMT ":" DST_FMT, ds_nam, dst)) != 2) {
171 rrd_set_error("invalid arguments for data source type");
172 rrd_free(&rrd);
173 close(rrd_file->fd);
174 return -1;
175 }
176 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
177 rrd_free(&rrd);
178 close(rrd_file->fd);
179 return -1;
180 }
181 if ((int) dst_conv(dst) == -1) {
182 rrd_free(&rrd);
183 close(rrd_file->fd);
184 return -1;
185 }
186 strncpy(rrd.ds_def[ds].dst, dst, DST_SIZE - 1);
187 rrd.ds_def[ds].dst[DST_SIZE - 1] = '\0';
189 rrd.pdp_prep[ds].last_ds[0] = 'U';
190 rrd.pdp_prep[ds].last_ds[1] = 'N';
191 rrd.pdp_prep[ds].last_ds[2] = 'K';
192 rrd.pdp_prep[ds].last_ds[3] = 'N';
193 rrd.pdp_prep[ds].last_ds[4] = '\0';
195 break;
196 case 'r':
197 if ((matches =
198 sscanf(optarg, DS_NAM_FMT ":" DS_NAM_FMT, ds_nam,
199 ds_new)) != 2) {
200 rrd_set_error("invalid arguments for data source type");
201 rrd_free(&rrd);
202 close(rrd_file->fd);
203 return -1;
204 }
205 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
206 rrd_free(&rrd);
207 close(rrd_file->fd);
208 return -1;
209 }
210 strncpy(rrd.ds_def[ds].ds_nam, ds_new, DS_NAM_SIZE - 1);
211 rrd.ds_def[ds].ds_nam[DS_NAM_SIZE - 1] = '\0';
212 break;
213 case 'p':
214 if (set_deltaarg(&rrd, RRA_delta_pos, optarg)) {
215 rrd_free(&rrd);
216 return -1;
217 }
218 break;
219 case 'n':
220 if (set_deltaarg(&rrd, RRA_delta_neg, optarg)) {
221 rrd_free(&rrd);
222 return -1;
223 }
224 break;
225 case 'f':
226 if (set_windowarg(&rrd, RRA_failure_threshold, optarg)) {
227 rrd_free(&rrd);
228 return -1;
229 }
230 break;
231 case 'w':
232 if (set_windowarg(&rrd, RRA_window_len, optarg)) {
233 rrd_free(&rrd);
234 return -1;
235 }
236 break;
237 case 'x':
238 if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_alpha, optarg)) {
239 rrd_free(&rrd);
240 return -1;
241 }
242 break;
243 case 'y':
244 if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_beta, optarg)) {
245 rrd_free(&rrd);
246 return -1;
247 }
248 break;
249 case 'z':
250 if (set_hwarg(&rrd, CF_SEASONAL, RRA_seasonal_gamma, optarg)) {
251 rrd_free(&rrd);
252 return -1;
253 }
254 break;
255 case 'v':
256 if (set_hwarg(&rrd, CF_DEVSEASONAL, RRA_seasonal_gamma, optarg)) {
257 rrd_free(&rrd);
258 return -1;
259 }
260 break;
261 case 'b':
262 if (sscanf(optarg, DS_NAM_FMT, ds_nam) != 1) {
263 rrd_set_error("invalid argument for aberrant-reset");
264 rrd_free(&rrd);
265 close(rrd_file->fd);
266 return -1;
267 }
268 if ((ds = ds_match(&rrd, ds_nam)) == -1) {
269 /* ds_match handles it own errors */
270 rrd_free(&rrd);
271 close(rrd_file->fd);
272 return -1;
273 }
274 reset_aberrant_coefficients(&rrd, rrd_file, (unsigned long) ds);
275 if (rrd_test_error()) {
276 rrd_free(&rrd);
277 close(rrd_file->fd);
278 return -1;
279 }
280 break;
281 case '?':
282 if (optopt != 0)
283 rrd_set_error("unknown option '%c'", optopt);
284 else
285 rrd_set_error("unknown option '%s'", argv[optind - 1]);
286 rrd_free(&rrd);
287 close(rrd_file->fd);
288 return -1;
289 }
290 }
291 if (optcnt > 0) {
292 rrd_seek(rrd_file, 0, SEEK_SET);
293 rrd_write(rrd_file, rrd.stat_head, sizeof(stat_head_t) * 1);
294 rrd_write(rrd_file, rrd.ds_def,
295 sizeof(ds_def_t) * rrd.stat_head->ds_cnt);
296 /* need to write rra_defs for RRA parameter changes */
297 rrd_write(rrd_file, rrd.rra_def,
298 sizeof(rra_def_t) * rrd.stat_head->rra_cnt);
299 } else {
300 int i;
302 for (i = 0; i < (int) rrd.stat_head->ds_cnt; i++)
303 if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
304 printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
305 rrd.ds_def[i].ds_nam,
306 rrd.ds_def[i].dst,
307 rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
308 rrd.ds_def[i].par[DS_min_val].u_val,
309 rrd.ds_def[i].par[DS_max_val].u_val);
310 } else {
311 char *buffer = NULL;
313 rpn_compact2str((rpn_cdefds_t *) &
314 (rrd.ds_def[i].par[DS_cdef]), rrd.ds_def,
315 &buffer);
316 printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,
317 rrd.ds_def[i].dst, buffer);
318 free(buffer);
319 }
320 }
321 close(rrd_file->fd);
322 rrd_free(&rrd);
323 return 0;
324 }
326 int set_hwarg(
327 rrd_t *rrd,
328 enum cf_en cf,
329 enum rra_par_en rra_par,
330 char *arg)
331 {
332 double param;
333 unsigned long i;
334 signed short rra_idx = -1;
336 /* read the value */
337 param = atof(arg);
338 if (param <= 0.0 || param >= 1.0) {
339 rrd_set_error("Holt-Winters parameter must be between 0 and 1");
340 return -1;
341 }
342 /* does the appropriate RRA exist? */
343 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
344 if (cf_conv(rrd->rra_def[i].cf_nam) == cf) {
345 rra_idx = i;
346 break;
347 }
348 }
349 if (rra_idx == -1) {
350 rrd_set_error("Holt-Winters RRA does not exist in this RRD");
351 return -1;
352 }
354 /* set the value */
355 rrd->rra_def[rra_idx].par[rra_par].u_val = param;
356 return 0;
357 }
359 int set_deltaarg(
360 rrd_t *rrd,
361 enum rra_par_en rra_par,
362 char *arg)
363 {
364 rrd_value_t param;
365 unsigned long i;
366 signed short rra_idx = -1;
368 param = atof(arg);
369 if (param < 0.1) {
370 rrd_set_error("Parameter specified is too small");
371 return -1;
372 }
373 /* does the appropriate RRA exist? */
374 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
375 if (cf_conv(rrd->rra_def[i].cf_nam) == CF_FAILURES) {
376 rra_idx = i;
377 break;
378 }
379 }
380 if (rra_idx == -1) {
381 rrd_set_error("Failures RRA does not exist in this RRD");
382 return -1;
383 }
385 /* set the value */
386 rrd->rra_def[rra_idx].par[rra_par].u_val = param;
387 return 0;
388 }
390 int set_windowarg(
391 rrd_t *rrd,
392 enum rra_par_en rra_par,
393 char *arg)
394 {
395 unsigned long param;
396 unsigned long i, cdp_idx;
397 signed short rra_idx = -1;
399 /* read the value */
400 param = atoi(arg);
401 if (param < 1 || param > MAX_FAILURES_WINDOW_LEN) {
402 rrd_set_error("Parameter must be between %d and %d",
403 1, MAX_FAILURES_WINDOW_LEN);
404 return -1;
405 }
406 /* does the appropriate RRA exist? */
407 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
408 if (cf_conv(rrd->rra_def[i].cf_nam) == CF_FAILURES) {
409 rra_idx = i;
410 break;
411 }
412 }
413 if (rra_idx == -1) {
414 rrd_set_error("Failures RRA does not exist in this RRD");
415 return -1;
416 }
418 /* set the value */
419 rrd->rra_def[rra_idx].par[rra_par].u_cnt = param;
421 /* erase existing violations */
422 for (i = 0; i < rrd->stat_head->ds_cnt; i++) {
423 cdp_idx = rra_idx * (rrd->stat_head->ds_cnt) + i;
424 erase_violations(rrd, cdp_idx, rra_idx);
425 }
426 return 0;
427 }