prepare for the release of rrdtool-1.2.20
[rrdtool.git] / src / rrd_tune.c
1 /*****************************************************************************
2  * RRDtool 1.2.20  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  *****************************************************************************/
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
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
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
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
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 }