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