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