1 /*****************************************************************************
2  * RRDtool 1.2.30  Copyright by Tobi Oetiker, 1997-2009
3  *****************************************************************************
4  * change header parameters of an rrd
5  *****************************************************************************
6  * $Id: rrd_tune.c 1735 2009-01-19 14:29:11Z oetiker $
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  *****************************************************************************/
41 
42 #include "rrd_tool.h"
43 #include "rrd_rpncalc.h"
44 #include "rrd_hw.h"
45 
46 int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg);
47 int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg);
48 int set_windowarg(rrd_t *rrd,enum rra_par_en,char *arg);
49 
50 int
rrd_tune(int argc,char ** argv)51 rrd_tune(int argc, char **argv)
52 {
53     rrd_t               rrd;
54     FILE               *rrd_file;
55     int                 matches;
56     int                 optcnt = 0;
57     long                ds;
58     char                ds_nam[DS_NAM_SIZE];
59     char                ds_new[DS_NAM_SIZE];
60     long                heartbeat;
61     double              min;
62     double              max;
63     char                dst[DST_SIZE];
64     optind = 0; opterr = 0;  /* initialize getopt */
65 
66 
67     if(rrd_open(argv[1],&rrd_file,&rrd, RRD_READWRITE)==-1){
68         return -1;
69     }
70 
71 
72     while (1){
73 	static struct option long_options[] =
74 	{
75 	    {"heartbeat",        required_argument, 0, 'h'},
76 	    {"minimum",          required_argument, 0, 'i'},
77 	    {"maximum",          required_argument, 0, 'a'},
78 	    {"data-source-type", required_argument, 0, 'd'},
79 	    {"data-source-rename", required_argument, 0, 'r'},
80 	    /* added parameter tuning options for aberrant behavior detection */
81 		{"deltapos",required_argument,0,'p'},
82 		{"deltaneg",required_argument,0,'n'},
83 		{"window-length",required_argument,0,'w'},
84 		{"failure-threshold",required_argument,0,'f'},
85 	    {"alpha",required_argument,0,'x'},
86 	    {"beta",required_argument,0,'y'},
87 	    {"gamma",required_argument,0,'z'},
88 	    {"gamma-deviation",required_argument,0,'v'},
89 		{"aberrant-reset",required_argument,0,'b'},
90 	    {0,0,0,0}
91 	};
92 	int option_index = 0;
93 	int opt;
94 	opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
95 			  long_options, &option_index);
96 	if (opt == EOF)
97 	    break;
98 
99 	optcnt++;
100 	switch(opt) {
101 	case 'h':
102 	    if ((matches = sscanf(optarg, DS_NAM_FMT ":%ld",ds_nam,&heartbeat)) != 2){
103 		rrd_set_error("invalid arguments for heartbeat");
104 		rrd_free(&rrd);
105 	        fclose(rrd_file);
106 		return -1;
107 	    }
108 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
109 		rrd_free(&rrd);
110                 fclose(rrd_file);
111 		return -1;
112 	    }
113 	    rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
114 	    break;
115 
116 	case 'i':
117 	    if ((matches = sscanf(optarg,DS_NAM_FMT ":%lf",ds_nam,&min)) <1){
118 		rrd_set_error("invalid arguments for minimum ds value");
119 		rrd_free(&rrd);
120                 fclose(rrd_file);
121 		return -1;
122 	    }
123 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
124 		rrd_free(&rrd);
125                 fclose(rrd_file);
126 		return -1;
127 	    }
128 
129 	    if(matches == 1)
130 		min= DNAN;
131 	    rrd.ds_def[ds].par[DS_min_val].u_val = min;
132 	    break;
133 
134 	case 'a':
135 	    if ((matches = sscanf(optarg, DS_NAM_FMT ":%lf",ds_nam,&max)) <1){
136 		rrd_set_error("invalid arguments for maximum ds value");
137 		rrd_free(&rrd);
138 	        fclose(rrd_file);
139 		return -1;
140 	    }
141 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
142 		rrd_free(&rrd);
143 	        fclose(rrd_file);
144 		return -1;
145 	    }
146 	    if(matches == 1)
147 		max= DNAN;
148 	    rrd.ds_def[ds].par[DS_max_val].u_val = max;
149 	    break;
150 
151 	case 'd':
152 	    if ((matches = sscanf(optarg, DS_NAM_FMT ":" DST_FMT ,ds_nam,dst)) != 2){
153 		rrd_set_error("invalid arguments for data source type");
154 		rrd_free(&rrd);
155 	        fclose(rrd_file);
156 		return -1;
157 	    }
158 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
159 		rrd_free(&rrd);
160 	        fclose(rrd_file);
161 		return -1;
162 	    }
163 	    if ((int)dst_conv(dst) == -1){
164 		rrd_free(&rrd);
165 	        fclose(rrd_file);
166 		return -1;
167 	    }
168 	    strncpy(rrd.ds_def[ds].dst,dst,DST_SIZE-1);
169 	    rrd.ds_def[ds].dst[DST_SIZE-1]='\0';
170 
171 	    rrd.pdp_prep[ds].last_ds[0] = 'U';
172 	    rrd.pdp_prep[ds].last_ds[1] = 'N';
173 	    rrd.pdp_prep[ds].last_ds[2] = 'K';
174 	    rrd.pdp_prep[ds].last_ds[3] = 'N';
175 	    rrd.pdp_prep[ds].last_ds[4] = '\0';
176 
177 	    break;
178 	case 'r':
179 	    if ((matches =
180 		 sscanf(optarg,DS_NAM_FMT ":" DS_NAM_FMT , ds_nam,ds_new)) != 2){
181 		rrd_set_error("invalid arguments for data source type");
182 		rrd_free(&rrd);
183 	        fclose(rrd_file);
184 		return -1;
185 	    }
186 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
187 		rrd_free(&rrd);
188 	        fclose(rrd_file);
189 		return -1;
190 	    }
191 	    strncpy(rrd.ds_def[ds].ds_nam,ds_new,DS_NAM_SIZE-1);
192 	    rrd.ds_def[ds].ds_nam[DS_NAM_SIZE-1]='\0';
193 	    break;
194     case 'p':
195 		if (set_deltaarg(&rrd,RRA_delta_pos,optarg)) {
196 		   rrd_free(&rrd);
197 		   return -1;
198 		}
199 		break;
200 	case 'n':
201 		if (set_deltaarg(&rrd,RRA_delta_neg,optarg)) {
202 		   rrd_free(&rrd);
203 		   return -1;
204 		}
205 		break;
206 	case 'f':
207 		if (set_windowarg(&rrd,RRA_failure_threshold,optarg)) {
208 		   rrd_free(&rrd);
209 		   return -1;
210 		}
211 		break;
212 	case 'w':
213 		if (set_windowarg(&rrd,RRA_window_len,optarg)) {
214 		   rrd_free(&rrd);
215 		   return -1;
216 		}
217 		break;
218 	case 'x':
219 		if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_alpha,optarg)) {
220 		   rrd_free(&rrd);
221 		   return -1;
222 		}
223 		break;
224 	case 'y':
225 		if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_beta,optarg)) {
226 		   rrd_free(&rrd);
227 		   return -1;
228 		}
229 		break;
230 	case 'z':
231 		if (set_hwarg(&rrd,CF_SEASONAL,RRA_seasonal_gamma,optarg)) {
232 		   rrd_free(&rrd);
233 		   return -1;
234 		}
235 		break;
236 	case 'v':
237 		if (set_hwarg(&rrd,CF_DEVSEASONAL,RRA_seasonal_gamma,optarg)) {
238 		   rrd_free(&rrd);
239 		   return -1;
240 		}
241 		break;
242 	case 'b':
243 		if (sscanf(optarg,DS_NAM_FMT,ds_nam) != 1){
244 		rrd_set_error("invalid argument for aberrant-reset");
245 		rrd_free(&rrd);
246 	        fclose(rrd_file);
247 		return -1;
248 	    }
249 	    if ((ds=ds_match(&rrd,ds_nam))==-1){
250 	    /* ds_match handles it own errors */
251 		rrd_free(&rrd);
252 	        fclose(rrd_file);
253 		return -1;
254 	    }
255 	    reset_aberrant_coefficients(&rrd,rrd_file,(unsigned long) ds);
256 		if (rrd_test_error()) {
257 		   rrd_free(&rrd);
258 		   fclose(rrd_file);
259 		   return -1;
260 		}
261 		break;
262 	case '?':
263             if (optopt != 0)
264                 rrd_set_error("unknown option '%c'", optopt);
265             else
266                 rrd_set_error("unknown option '%s'",argv[optind-1]);
267 	    rrd_free(&rrd);
268             fclose(rrd_file);
269             return -1;
270         }
271     }
272 	if(optcnt>0){
273 
274 	fseek(rrd_file,0,SEEK_SET);
275 	fwrite(rrd.stat_head,
276 	       sizeof(stat_head_t),1, rrd_file);
277 	fwrite(rrd.ds_def,
278 	       sizeof(ds_def_t), rrd.stat_head->ds_cnt, rrd_file);
279 	/* need to write rra_defs for RRA parameter changes */
280 	fwrite(rrd.rra_def, sizeof(rra_def_t), rrd.stat_head->rra_cnt,
281 		   rrd_file);
282     } else {
283 	int i;
284 	for(i=0;i< (int)rrd.stat_head->ds_cnt;i++)
285 		if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
286 	    printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
287 		   rrd.ds_def[i].ds_nam,
288 		   rrd.ds_def[i].dst,
289 		   rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
290 		   rrd.ds_def[i].par[DS_min_val].u_val,
291 		   rrd.ds_def[i].par[DS_max_val].u_val);
292 		} else {
293 		char *buffer = NULL;
294 		rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),rrd.ds_def,&buffer);
295 		printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,rrd.ds_def[i].dst,buffer);
296 	    free(buffer);
297 		}
298     }
299     fclose(rrd_file);
300     rrd_free(&rrd);
301     return 0;
302 }
303 
set_hwarg(rrd_t * rrd,enum cf_en cf,enum rra_par_en rra_par,char * arg)304 int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg)
305 {
306    double param;
307    unsigned long i;
308    signed short rra_idx = -1;
309    /* read the value */
310    param = atof(arg);
311    if (param <= 0.0 || param >= 1.0)
312    {
313 	  rrd_set_error("Holt-Winters parameter must be between 0 and 1");
314 	  return -1;
315    }
316    /* does the appropriate RRA exist?  */
317    for (i =  0; i < rrd -> stat_head -> rra_cnt; ++i)
318    {
319 	  if (cf_conv(rrd -> rra_def[i].cf_nam) == cf)
320 	  {
321 		 rra_idx = i;
322 		 break;
323 	  }
324    }
325    if (rra_idx == -1)
326    {
327 	  rrd_set_error("Holt-Winters RRA does not exist in this RRD");
328 	  return -1;
329    }
330 
331    /* set the value */
332    rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
333    return 0;
334 }
335 
set_deltaarg(rrd_t * rrd,enum rra_par_en rra_par,char * arg)336 int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
337 {
338    rrd_value_t param;
339    unsigned long i;
340    signed short rra_idx = -1;
341 
342    param = atof(arg);
343    if (param < 0.1)
344    {
345 	  rrd_set_error("Parameter specified is too small");
346 	  return -1;
347    }
348    /* does the appropriate RRA exist?  */
349    for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
350    {
351 	  if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES)
352 	  {
353 		 rra_idx = i;
354 		 break;
355 	  }
356    }
357    if (rra_idx == -1)
358    {
359 	  rrd_set_error("Failures RRA does not exist in this RRD");
360 	  return -1;
361    }
362 
363    /* set the value */
364    rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
365    return 0;
366 }
367 
set_windowarg(rrd_t * rrd,enum rra_par_en rra_par,char * arg)368 int set_windowarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
369 {
370    unsigned long param;
371    unsigned long i, cdp_idx;
372    signed short rra_idx = -1;
373    /* read the value */
374    param = atoi(arg);
375    if (param < 1 || param > MAX_FAILURES_WINDOW_LEN)
376    {
377 	  rrd_set_error("Parameter must be between %d and %d",
378 		 1, MAX_FAILURES_WINDOW_LEN);
379 	  return -1;
380    }
381    /* does the appropriate RRA exist?  */
382    for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
383    {
384 	  if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES)
385 	  {
386 		 rra_idx = i;
387 		 break;
388 	  }
389    }
390    if (rra_idx == -1)
391    {
392 	  rrd_set_error("Failures RRA does not exist in this RRD");
393 	  return -1;
394    }
395 
396    /* set the value */
397    rrd -> rra_def[rra_idx].par[rra_par].u_cnt = param;
398 
399    /* erase existing violations */
400    for (i = 0; i < rrd -> stat_head -> ds_cnt; i++)
401    {
402 	  cdp_idx = rra_idx * (rrd -> stat_head -> ds_cnt) + i;
403 	  erase_violations(rrd,cdp_idx,rra_idx);
404    }
405    return 0;
406 }
407