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