Example #1
0
int rrd_create_fn(const char *file_name, rrd_t *rrd)
{
	unsigned long i, ii;
	rrd_value_t *unknown;
	int       unkn_cnt;
	rrd_file_t *rrd_file_dn;
	rrd_t     rrd_dn;
	unsigned  rrd_flags = RRD_READWRITE | RRD_CREAT;
	int ret = 0;

	if (opt_no_overwrite) {
		rrd_flags |= RRD_EXCL ;
	}

	unkn_cnt = 0;
	for (i = 0; i < rrd->stat_head->rra_cnt; i++)
		unkn_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;

	if ((rrd_file_dn = rrd_open(file_name, rrd, rrd_flags, &ret)) == NULL) {
		rrd_free2(rrd);
		return ret;
	}

	if (rrd_write(rrd_file_dn, rrd->stat_head, sizeof(stat_head_t)) < 0){
		rrd_free2(rrd);
		rrd_close(rrd_file_dn);
		return (-RRD_ERR_WRITE5);
	}

	rrd_write(rrd_file_dn, rrd->ds_def, sizeof(ds_def_t) * rrd->stat_head->ds_cnt);

	rrd_write(rrd_file_dn, rrd->rra_def,
			sizeof(rra_def_t) * rrd->stat_head->rra_cnt);

	rrd_write(rrd_file_dn, rrd->live_head, sizeof(live_head_t));

	if ((rrd->pdp_prep = (pdp_prep_t*)calloc(1, sizeof(pdp_prep_t))) == NULL) {
		rrd_free2(rrd);
		rrd_close(rrd_file_dn);
		return (-RRD_ERR_ALLOC);
	}

	strcpy(rrd->pdp_prep->last_ds, "U");

	rrd->pdp_prep->scratch[PDP_val].u_val = 0.0;
	rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt =
		rrd->live_head->last_up % rrd->stat_head->pdp_step;

	for (i = 0; i < rrd->stat_head->ds_cnt; i++)
		rrd_write(rrd_file_dn, rrd->pdp_prep, sizeof(pdp_prep_t));

	if ((rrd->cdp_prep = (cdp_prep_t*)calloc(1, sizeof(cdp_prep_t))) == NULL) {
		rrd_free2(rrd);
		rrd_close(rrd_file_dn);
		return (-RRD_ERR_ALLOC);
	}


	for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
		switch (cf_conv(rrd->rra_def[i].cf_nam)) {
			case CF_HWPREDICT:
			case CF_MHWPREDICT:
				init_hwpredict_cdp(rrd->cdp_prep);
				break;
			case CF_SEASONAL:
			case CF_DEVSEASONAL:
				init_seasonal_cdp(rrd->cdp_prep);
				break;
			case CF_FAILURES:
				/* initialize violation history to 0 */
				for (ii = 0; ii < MAX_CDP_PAR_EN; ii++) {
					/* We can zero everything out, by setting u_val to the
					 * NULL address. Each array entry in scratch is 8 bytes
					 * (a double), but u_cnt only accessed 4 bytes (long) */
					rrd->cdp_prep->scratch[ii].u_val = 0.0;
				}
				break;
			default:
				/* can not be zero because we don't know anything ... */
				rrd->cdp_prep->scratch[CDP_val].u_val = DNAN;
				/* startup missing pdp count */
				rrd->cdp_prep->scratch[CDP_unkn_pdp_cnt].u_cnt =
					((rrd->live_head->last_up -
					  rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt)
					 % (rrd->stat_head->pdp_step
						 * rrd->rra_def[i].pdp_cnt)) / rrd->stat_head->pdp_step;
				break;
		}

		for (ii = 0; ii < rrd->stat_head->ds_cnt; ii++) {
			rrd_write(rrd_file_dn, rrd->cdp_prep, sizeof(cdp_prep_t));
		}
	}

	/* now, we must make sure that the rest of the rrd
	   struct is properly initialized */

	if ((rrd->rra_ptr = (rra_ptr_t*)calloc(1, sizeof(rra_ptr_t))) == NULL) {
		rrd_free2(rrd);
		rrd_close(rrd_file_dn);
		return -RRD_ERR_ALLOC;
	}

	/* changed this initialization to be consistent with
	 * rrd_restore. With the old value (0), the first update
	 * would occur for cur_row = 1 because rrd_update increments
	 * the pointer a priori. */
	for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
		rrd->rra_ptr->cur_row = rrd_select_initial_row(rrd_file_dn, i, &rrd->rra_def[i]);
		rrd_write(rrd_file_dn, rrd->rra_ptr, sizeof(rra_ptr_t));
	}

	/* write the empty data area */
	if ((unknown = (rrd_value_t *) malloc(512 * sizeof(rrd_value_t))) == NULL) {
		rrd_free2(rrd);
		rrd_close(rrd_file_dn);
		return -RRD_ERR_ALLOC;
	}
	for (i = 0; i < 512; ++i)
		unknown[i] = DNAN;

	while (unkn_cnt > 0) {
		if(rrd_write(rrd_file_dn, unknown, sizeof(rrd_value_t) * min(unkn_cnt, 512)) < 0)
		{
			return -RRD_ERR_CREATE_WRITE;
		}

		unkn_cnt -= 512;
	}
	free(unknown);
	rrd_free2(rrd);
	if (rrd_close(rrd_file_dn) == -1) {
		return -RRD_ERR_CREATE_WRITE;
	}
	/* flush all we don't need out of the cache */
	rrd_init(&rrd_dn);
	if((rrd_file_dn = rrd_open(file_name, &rrd_dn, RRD_READONLY, &ret)) != NULL)
	{
		rrd_dontneed(rrd_file_dn, &rrd_dn);
		/* rrd_free(&rrd_dn); */
		rrd_close(rrd_file_dn);
	}
	return ret;
}
Example #2
0
/* Reset aberrant behavior model coefficients, including intercept, slope,
 * seasonal, and seasonal deviation for the specified data source. */
void reset_aberrant_coefficients(
    rrd_t *rrd,
    rrd_file_t *rrd_file,
    unsigned long ds_idx)
{
    unsigned long cdp_idx, rra_idx, i;
    unsigned long cdp_start, rra_start;
    rrd_value_t nan_buffer = DNAN;

    /* compute the offset for the cdp area */
    cdp_start = sizeof(stat_head_t) +
        rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
        rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
        sizeof(live_head_t) + rrd->stat_head->ds_cnt * sizeof(pdp_prep_t);
    /* compute the offset for the first rra */
    rra_start = cdp_start +
        (rrd->stat_head->ds_cnt) * (rrd->stat_head->rra_cnt) *
        sizeof(cdp_prep_t) + rrd->stat_head->rra_cnt * sizeof(rra_ptr_t);

    /* loop over the RRAs */
    for (rra_idx = 0; rra_idx < rrd->stat_head->rra_cnt; rra_idx++) {
        cdp_idx = rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
        switch (cf_conv(rrd->rra_def[rra_idx].cf_nam)) {
        case CF_HWPREDICT:
        case CF_MHWPREDICT:
            init_hwpredict_cdp(&(rrd->cdp_prep[cdp_idx]));
            break;
        case CF_SEASONAL:
        case CF_DEVSEASONAL:
            /* don't use init_seasonal because it will reset burn-in, which
             * means different data sources will be calling for the smoother
             * at different times. */
            rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val = DNAN;
            rrd->cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = DNAN;
            /* move to first entry of data source for this rra */
            rrd_seek(rrd_file, rra_start + ds_idx * sizeof(rrd_value_t),
                     SEEK_SET);
            /* entries for the same data source are not contiguous,
             * temporal entries are contiguous */
            for (i = 0; i < rrd->rra_def[rra_idx].row_cnt; ++i) {
                if (rrd_write(rrd_file, &nan_buffer, sizeof(rrd_value_t) * 1)
                    != sizeof(rrd_value_t) * 1) {
                    rrd_set_error
                        ("reset_aberrant_coefficients: write failed data source %lu rra %s",
                         ds_idx, rrd->rra_def[rra_idx].cf_nam);
                    return;
                }
                rrd_seek(rrd_file, (rrd->stat_head->ds_cnt - 1) *
                         sizeof(rrd_value_t), SEEK_CUR);
            }
            break;
        case CF_FAILURES:
            erase_violations(rrd, cdp_idx, rra_idx);
            break;
        default:
            break;
        }
        /* move offset to the next rra */
        rra_start += rrd->rra_def[rra_idx].row_cnt * rrd->stat_head->ds_cnt *
            sizeof(rrd_value_t);
    }
    rrd_seek(rrd_file, cdp_start, SEEK_SET);
    if (rrd_write(rrd_file, rrd->cdp_prep,
                  sizeof(cdp_prep_t) *
                  (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
        != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
                      (rrd->stat_head->ds_cnt))) {
        rrd_set_error("reset_aberrant_coefficients: cdp_prep write failed");
    }
}
Example #3
0
int rrd_tune(
    int argc,
    char **argv)
{
    rrd_t     rrd;
    int       matches;
    int       optcnt = 0;
    long      ds;
    char      ds_nam[DS_NAM_SIZE];
    char      ds_new[DS_NAM_SIZE];
    long      heartbeat;
    double    min = 0;
    double    max = 0;
    char      dst[DST_SIZE];
    int       rc = -1;
    int       opt_newstep = -1;
    rrd_file_t *rrd_file = NULL;
    char      *opt_daemon = NULL;
    char      double_str[ 12 ];
    const char *in_filename = NULL;
    struct option long_options[] = {
        {"heartbeat", required_argument, 0, 'h'},
        {"minimum", required_argument, 0, 'i'},
        {"maximum", required_argument, 0, 'a'},
        {"data-source-type", required_argument, 0, 'd'},
        {"data-source-rename", required_argument, 0, 'r'},
        /* added parameter tuning options for aberrant behavior detection */
        {"deltapos", required_argument, 0, 'p'},
        {"deltaneg", required_argument, 0, 'n'},
        {"window-length", required_argument, 0, 'w'},
        {"failure-threshold", required_argument, 0, 'f'},
        {"alpha", required_argument, 0, 'x'},
        {"beta", required_argument, 0, 'y'},
        {"gamma", required_argument, 0, 'z'},
        {"gamma-deviation", required_argument, 0, 'v'},
        {"smoothing-window", required_argument, 0, 's'},
        {"smoothing-window-deviation", required_argument, 0, 'S'},
        {"aberrant-reset", required_argument, 0, 'b'},
	// integration of rrd_modify functionality.
        {"step", required_argument, 0, 't'},
	/* unfortunately, '-d' is already taken */
        {"daemon", required_argument, 0, 'D'},
        {0, 0, 0, 0}
    };

    optind = 0;
    opterr = 0;         /* initialize getopt */

    /* before we open the input RRD, we should flush it from any caching
    daemon, because we might totally rewrite it later on */

    /* for this, we FIRST have to find the daemon, this means we must parse options twice... */
    
    while (1) {
	int option_index = 0;
	int opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
			      long_options, &option_index);
        if (opt == EOF)
            break;
	switch (opt) {
	case 'D':
            if (opt_daemon != NULL)
		free (opt_daemon);
            opt_daemon = strdup (optarg);
            if (opt_daemon == NULL)
            {
                rrd_set_error ("strdup failed.");
                return (-1);
            }
            break;
	default:
	    break;
	}
    }
    
    // connect to daemon (will take care of environment variable automatically)
    if (rrdc_connect(opt_daemon) != 0) {
    	rrd_set_error("Cannot connect to daemon");
	return 1;
    }

    if (opt_daemon) {
	free(opt_daemon);
	opt_daemon = NULL;
    }

    if (optind < 0 || optind >= argc) {
	// missing file name...
	rrd_set_error("missing file name");
	goto done;
    }
    
    /* NOTE: getopt_long reorders argv and places all NON option arguments to
    the back, starting with optind. This means the file name has travelled to
    argv[optind] */
    
    in_filename = argv[optind];
    
    if (rrdc_is_any_connected()) {
	// is it a good idea to just ignore the error ????
	rrdc_flush(in_filename);
	rrd_clear_error();
    }

    optind = 0;
    opterr = 0;         /* re-initialize getopt */
    
    rrd_init(&rrd);
    rrd_file = rrd_open(in_filename, &rrd, RRD_READWRITE | RRD_READAHEAD | RRD_READVALUES);
    if (rrd_file == NULL) {
	goto done;
    }

    while (1) {
        int       option_index = 0;
        int       opt;
        unsigned int strtod_ret_val;

        opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
                          long_options, &option_index);
        if (opt == EOF)
            break;

        optcnt++;
        switch (opt) {
        case 'h':
            if ((matches =
		 sscanf(optarg, DS_NAM_FMT ":%ld", ds_nam,
			&heartbeat)) != 2) {
                rrd_set_error("invalid arguments for heartbeat");
		goto done;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
		goto done;
            }
            rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
            break;

        case 'i':
            matches = sscanf(optarg, DS_NAM_FMT ":%[0-9.e+-]", ds_nam, double_str);
            if( matches >= 1 ) {
                strtod_ret_val = rrd_strtodbl( double_str, NULL, &min, NULL );
            }

            if ((matches < 1) || (strtod_ret_val != 2)) {
                rrd_set_error("invalid arguments for minimum ds value");
		goto done;
            }

            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
		goto done;
            }

            if (matches == 1)
                min = DNAN;
            rrd.ds_def[ds].par[DS_min_val].u_val = min;
            break;

        case 'a':
            matches = sscanf(optarg, DS_NAM_FMT ":%[0-9.e+-]", ds_nam, double_str);
            if( matches >= 1 ) {
                strtod_ret_val = rrd_strtodbl( double_str, NULL, &max, NULL );
            }

            if ((matches < 1 ) || (strtod_ret_val != 2)) {
                rrd_set_error("invalid arguments for maximum ds value");
		goto done;
            }

            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
		goto done;
            }

            if (matches == 1)
                max = DNAN;
            rrd.ds_def[ds].par[DS_max_val].u_val = max;
            break;

        case 'd':
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":" DST_FMT, ds_nam, dst)) != 2) {
                rrd_set_error("invalid arguments for data source type");
		goto done;
	    }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
		goto done;
            }
            if ((int) dst_conv(dst) == -1) {
		goto done;
            }
            /* only reset when something is changed */
            if (strncmp(rrd.ds_def[ds].dst, dst, DST_SIZE - 1) != 0) {
                strncpy(rrd.ds_def[ds].dst, dst, DST_SIZE - 1);
                rrd.ds_def[ds].dst[DST_SIZE - 1] = '\0';

                rrd.pdp_prep[ds].last_ds[0] = 'U';
                rrd.pdp_prep[ds].last_ds[1] = 'N';
                rrd.pdp_prep[ds].last_ds[2] = 'K';
                rrd.pdp_prep[ds].last_ds[3] = 'N';
                rrd.pdp_prep[ds].last_ds[4] = '\0';
            }
            break;
        case 'r':
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":" DS_NAM_FMT, ds_nam,
                        ds_new)) != 2) {
                rrd_set_error("invalid arguments for data source type");
		goto done;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
		goto done;
            }
            strncpy(rrd.ds_def[ds].ds_nam, ds_new, DS_NAM_SIZE - 1);
            rrd.ds_def[ds].ds_nam[DS_NAM_SIZE - 1] = '\0';
            break;
        case 'p':
            if (set_deltaarg(&rrd, RRA_delta_pos, optarg)) {
		goto done;
            }
            break;
        case 'n':
            if (set_deltaarg(&rrd, RRA_delta_neg, optarg)) {
		goto done;
            }
            break;
        case 'f':
            if (set_windowarg(&rrd, RRA_failure_threshold, optarg)) {
		goto done;
            }
            break;
        case 'w':
            if (set_windowarg(&rrd, RRA_window_len, optarg)) {
		goto done;
            }
            break;
        case 'x':
            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_alpha, optarg)) {
                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_alpha, optarg)) {
		    goto done;
                }
                rrd_clear_error();
            }
            break;
        case 'y':
            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_beta, optarg)) {
                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_beta, optarg)) {
		    goto done;
                }
                rrd_clear_error();
            }
            break;
        case 'z':
            if (set_hwarg(&rrd, CF_SEASONAL, RRA_seasonal_gamma, optarg)) {
		goto done;
            }
            break;
        case 'v':
            if (set_hwarg(&rrd, CF_DEVSEASONAL, RRA_seasonal_gamma, optarg)) {
		goto done;
	    }
            break;
        case 'b':
            if (sscanf(optarg, DS_NAM_FMT, ds_nam) != 1) {
                rrd_set_error("invalid argument for aberrant-reset");
		goto done;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                /* ds_match handles it own errors */
		goto done;
            }
            reset_aberrant_coefficients(&rrd, rrd_file, (unsigned long) ds);
            if (rrd_test_error()) {
		goto done;
            }
            break;
        case 's':
            if (atoi(rrd.stat_head->version) < atoi(RRD_VERSION4)) {
                strcpy(rrd.stat_head->version, RRD_VERSION4);    /* smoothing_window causes Version 4 */
            }
            if (set_hwsmootharg
                (&rrd, CF_SEASONAL, RRA_seasonal_smoothing_window, optarg)) {
		goto done;
            }
            break;
        case 'S':
            if (atoi(rrd.stat_head->version) < atoi(RRD_VERSION4)) {
                strcpy(rrd.stat_head->version, RRD_VERSION4);    /* smoothing_window causes Version 4 */
            }
            if (set_hwsmootharg
                (&rrd, CF_DEVSEASONAL, RRA_seasonal_smoothing_window,
                 optarg)) {
		goto done;
            }
            break;
	case 't':
	    opt_newstep = atoi(optarg);
	    break;
	case 'D':
	    // ignore, handled in previous argv parsing round
	    break;
        case '?':
            if (optopt != 0)
                rrd_set_error("unknown option '%c'", optopt);
            else
                rrd_set_error("unknown option '%s'", argv[optind - 1]);
	    goto done;
        }
    }
    if (optcnt > 0) {
        rrd_seek(rrd_file, 0, SEEK_SET);
        rrd_write(rrd_file, rrd.stat_head, sizeof(stat_head_t) * 1);
        rrd_write(rrd_file, rrd.ds_def,
                  sizeof(ds_def_t) * rrd.stat_head->ds_cnt);
        /* need to write rra_defs for RRA parameter changes */
        rrd_write(rrd_file, rrd.rra_def,
                  sizeof(rra_def_t) * rrd.stat_head->rra_cnt);
    }
    
    if (optind >= argc) {
        int       i;

        for (i = 0; i < (int) rrd.stat_head->ds_cnt; i++)
            if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
                printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
                       rrd.ds_def[i].ds_nam,
                       rrd.ds_def[i].dst,
                       rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
                       rrd.ds_def[i].par[DS_min_val].u_val,
                       rrd.ds_def[i].par[DS_max_val].u_val);
            } else {
                char     *buffer = NULL;

                rpn_compact2str((rpn_cdefds_t *) &
                                (rrd.ds_def[i].par[DS_cdef]), rrd.ds_def,
                                &buffer);
                printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,
                       rrd.ds_def[i].dst, buffer);
                if (buffer) free(buffer);
            }
    }

    optind = handle_modify(&rrd, in_filename, argc, argv, optind + 1, opt_newstep);
    if (optind < 0) {
	goto done;
    }
    
    rc = 0;
done:
    if (in_filename && rrdc_is_any_connected()) {
        // save any errors....
        char *e = strdup(rrd_get_error());
	// is it a good idea to just ignore the error ????
	rrdc_forget(in_filename);
	rrd_clear_error();
        
        if (e && *e) {
            rrd_set_error(e);
        }
        if (e) free(e);
        
    }
    if (rrd_file) {
	rrd_close(rrd_file);
    }
    rrd_free(&rrd);
    return rc;
}
Example #4
0
/* Smooth a periodic array with a moving average: equal weights and
 * length = 5% of the period. */
int apply_smoother(
    rrd_t *rrd,
    unsigned long rra_idx,
    unsigned long rra_start,
    rrd_file_t *rrd_file)
{
    unsigned long i, j, k;
    unsigned long totalbytes;
    rrd_value_t *rrd_values;
    unsigned long row_length = rrd->stat_head->ds_cnt;
    unsigned long row_count = rrd->rra_def[rra_idx].row_cnt;
    unsigned long offset;
    FIFOqueue **buffers;
    rrd_value_t *working_average;
    rrd_value_t *rrd_values_cpy;
    rrd_value_t *baseline;

    if (atoi(rrd->stat_head->version) >= 4) {
        offset = floor(rrd->rra_def[rra_idx].
                       par[RRA_seasonal_smoothing_window].
                       u_val / 2 * row_count);
    } else {
        offset = floor(0.05 / 2 * row_count);
    }

    if (offset == 0)
        return 0;       /* no smoothing */

    /* allocate memory */
    totalbytes = sizeof(rrd_value_t) * row_length * row_count;
    rrd_values = (rrd_value_t *) malloc(totalbytes);
    if (rrd_values == NULL) {
        rrd_set_error("apply smoother: memory allocation failure");
        return -1;
    }

    /* rra_start is at the beginning of this rra */
    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
        rrd_set_error("seek to rra %d failed", rra_start);
        free(rrd_values);
        return -1;
    }

    /* could read all data in a single block, but we need to
     * check for NA values */
    for (i = 0; i < row_count; ++i) {
        for (j = 0; j < row_length; ++j) {
            if (rrd_read
                (rrd_file, &(rrd_values[i * row_length + j]),
                 sizeof(rrd_value_t) * 1)
                != (ssize_t) (sizeof(rrd_value_t) * 1)) {
                rrd_set_error("reading value failed: %s",
                              rrd_strerror(errno));
            }
            if (isnan(rrd_values[i * row_length + j])) {
                /* can't apply smoothing, still uninitialized values */
#ifdef DEBUG
                fprintf(stderr,
                        "apply_smoother: NA detected in seasonal array: %ld %ld\n",
                        i, j);
#endif
                free(rrd_values);
                return 0;
            }
        }
    }

    /* allocate queues, one for each data source */
    buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *) * row_length);
    for (i = 0; i < row_length; ++i) {
        queue_alloc(&(buffers[i]), 2 * offset + 1);
    }
    /* need working average initialized to 0 */
    working_average = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
    baseline = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));

    /* compute sums of the first 2*offset terms */
    for (i = 0; i < 2 * offset; ++i) {
        k = MyMod(i - offset, row_count);
        for (j = 0; j < row_length; ++j) {
            queue_push(buffers[j], rrd_values[k * row_length + j]);
            working_average[j] += rrd_values[k * row_length + j];
        }
    }

    /* as we are working through the value, we have to make sure to not double
       apply the smoothing after wrapping around. so best is to copy the rrd_values first */

    rrd_values_cpy = (rrd_value_t *) calloc(row_length*row_count, sizeof(rrd_value_t));
    memcpy(rrd_values_cpy,rrd_values,sizeof(rrd_value_t)*row_length*row_count);

    /* compute moving averages */
    for (i = offset; i < row_count + offset; ++i) {
        for (j = 0; j < row_length; ++j) {
            k = MyMod(i, row_count);
            /* add a term to the sum */
            working_average[j] += rrd_values_cpy[k * row_length + j];
            queue_push(buffers[j], rrd_values_cpy[k * row_length + j]);

            /* reset k to be the center of the window */
            k = MyMod(i - offset, row_count);
            /* overwrite rdd_values entry, the old value is already
             * saved in buffers */
            rrd_values[k * row_length + j] =
                working_average[j] / (2 * offset + 1);
            baseline[j] += rrd_values[k * row_length + j];

            /* remove a term from the sum */
            working_average[j] -= queue_pop(buffers[j]);
        }
    }

    for (i = 0; i < row_length; ++i) {
        queue_dealloc(buffers[i]);
        baseline[i] /= row_count;
    }
    free(rrd_values_cpy);
    free(buffers);
    free(working_average);

    if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
        rrd_value_t (
    *init_seasonality) (
    rrd_value_t seasonal_coef,
    rrd_value_t intercept);

        switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
        case CF_HWPREDICT:
            init_seasonality = hw_additive_init_seasonality;
            break;
        case CF_MHWPREDICT:
            init_seasonality = hw_multiplicative_init_seasonality;
            break;
        default:
            rrd_set_error("apply smoother: SEASONAL rra doesn't have "
                          "valid dependency: %s",
                          rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam);
            return -1;
        }

        for (j = 0; j < row_length; ++j) {
            for (i = 0; i < row_count; ++i) {
                rrd_values[i * row_length + j] =
                    init_seasonality(rrd_values[i * row_length + j],
                                     baseline[j]);
            }
            /* update the baseline coefficient,
             * first, compute the cdp_index. */
            offset = hw_dep_idx(rrd, rra_idx) * row_length + j;
            (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val +=
                baseline[j];
        }
/* if we are not running on mmap, lets write stuff to disk now */
#ifndef HAVE_MMAP
        /* flush cdp to disk */
        if (rrd_seek(rrd_file, sizeof(stat_head_t) +
                     rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
                     rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
                     sizeof(live_head_t) +
                     rrd->stat_head->ds_cnt * sizeof(pdp_prep_t), SEEK_SET)) {
            rrd_set_error("apply_smoother: seek to cdp_prep failed");
            free(rrd_values);
            return -1;
        }
        if (rrd_write(rrd_file, rrd->cdp_prep,
                      sizeof(cdp_prep_t) *
                      (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
            != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
                          (rrd->stat_head->ds_cnt))) {
            rrd_set_error("apply_smoother: cdp_prep write failed");
            free(rrd_values);
            return -1;
        }
#endif

    }

    /* endif CF_SEASONAL */
    /* flush updated values to disk */
    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
        rrd_set_error("apply_smoother: seek to pos %d failed", rra_start);
        free(rrd_values);
        return -1;
    }
    /* write as a single block */
    if (rrd_write
        (rrd_file, rrd_values, sizeof(rrd_value_t) * row_length * row_count)
        != (ssize_t) (sizeof(rrd_value_t) * row_length * row_count)) {
        rrd_set_error("apply_smoother: write failed to %lu", rra_start);
        free(rrd_values);
        return -1;
    }

    free(rrd_values);
    free(baseline);
    return 0;
}
Example #5
0
/* Smooth a periodic array with a moving average: equal weights and
 * length = 5% of the period. */
int apply_smoother( rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
    rrd_file_t *rrd_file) {
    unsigned long i, j, k;
    unsigned long totalbytes;
    rrd_value_t *rrd_values;
    unsigned long row_length = rrd->stat_head->ds_cnt;
    unsigned long row_count = rrd->rra_def[rra_idx].row_cnt;
    unsigned long offset;
    FIFOqueue **buffers;
    rrd_value_t *working_average;
    rrd_value_t *baseline;
	int ret = 0;

    if (atoi(rrd->stat_head->version) >= 4) {
        offset = floor(rrd->rra_def[rra_idx].
                       par[RRA_seasonal_smoothing_window].
                       u_val / 2 * row_count);
    } else {
        offset = floor(0.05 / 2 * row_count);
    }

    if (offset == 0)
        return 0;       /* no smoothing */

    /* allocate memory */
    totalbytes = sizeof(rrd_value_t) * row_length * row_count;
    rrd_values = (rrd_value_t *) malloc(totalbytes);
    if (rrd_values == NULL) {
        return -RRD_ERR_MALLOC5;
    }

    /* rra_start is at the beginning of this rra */
    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
        free(rrd_values);
        return -RRD_ERR_SEEK2;
    }

    /* could read all data in a single block, but we need to
     * check for NA values */
    for (i = 0; i < row_count; ++i) {
        for (j = 0; j < row_length; ++j) {
            if (rrd_read
                (rrd_file, &(rrd_values[i * row_length + j]),
                 sizeof(rrd_value_t) * 1)
                != (ssize_t) (sizeof(rrd_value_t) * 1)) {
				ret = -RRD_ERR_READ2;
            }
            if (isnan(rrd_values[i * row_length + j])) {
                /* can't apply smoothing, still uninitialized values */
#ifdef DEBUG
                fprintf(stderr,
                        "apply_smoother: NA detected in seasonal array: %ld %ld\n",
                        i, j);
#endif
                free(rrd_values);
                return ret;
            }
        }
    }

    /* allocate queues, one for each data source */
    buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *) * row_length);
    for (i = 0; i < row_length; ++i) {
        queue_alloc(&(buffers[i]), 2 * offset + 1);
    }
    /* need working average initialized to 0 */
    working_average = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
    baseline = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));

    /* compute sums of the first 2*offset terms */
    for (i = 0; i < 2 * offset; ++i) {
        k = MyMod(i - offset, row_count);
        for (j = 0; j < row_length; ++j) {
            queue_push(buffers[j], rrd_values[k * row_length + j]);
            working_average[j] += rrd_values[k * row_length + j];
        }
    }

    /* compute moving averages */
    for (i = offset; i < row_count + offset; ++i) {
        for (j = 0; j < row_length; ++j) {
            k = MyMod(i, row_count);
            /* add a term to the sum */
            working_average[j] += rrd_values[k * row_length + j];
            queue_push(buffers[j], rrd_values[k * row_length + j]);

            /* reset k to be the center of the window */
            k = MyMod(i - offset, row_count);
            /* overwrite rdd_values entry, the old value is already
             * saved in buffers */
            rrd_values[k * row_length + j] =
                working_average[j] / (2 * offset + 1);
            baseline[j] += rrd_values[k * row_length + j];

            /* remove a term from the sum */
            working_average[j] -= queue_pop(buffers[j]);
        }
    }

    for (i = 0; i < row_length; ++i) {
        queue_dealloc(buffers[i]);
        baseline[i] /= row_count;
    }
    free(buffers);
    free(working_average);

    if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
        rrd_value_t (
    *init_seasonality) (
    rrd_value_t seasonal_coef,
    rrd_value_t intercept);

        switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
        case CF_HWPREDICT:
            init_seasonality = hw_additive_init_seasonality;
            break;
        case CF_MHWPREDICT:
            init_seasonality = hw_multiplicative_init_seasonality;
            break;
        default:
            return -RRD_ERR_DEP1;
        }

        for (j = 0; j < row_length; ++j) {
            for (i = 0; i < row_count; ++i) {
                rrd_values[i * row_length + j] =
                    init_seasonality(rrd_values[i * row_length + j],
                                     baseline[j]);
            }
            /* update the baseline coefficient,
             * first, compute the cdp_index. */
            offset = hw_dep_idx(rrd, rra_idx) * row_length + j;
            (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val +=
                baseline[j];
        }
        /* flush cdp to disk */
        if (rrd_seek(rrd_file, sizeof(stat_head_t) +
                     rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
                     rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
                     sizeof(live_head_t) +
                     rrd->stat_head->ds_cnt * sizeof(pdp_prep_t), SEEK_SET)) {
            free(rrd_values);
			return -RRD_ERR_SEEK3;
        }
        if (rrd_write(rrd_file, rrd->cdp_prep,
                      sizeof(cdp_prep_t) *
                      (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
            != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
                          (rrd->stat_head->ds_cnt))) {
            free(rrd_values);
            return -RRD_ERR_WRITE1;
        }
    }

    /* endif CF_SEASONAL */
    /* flush updated values to disk */
    if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
        free(rrd_values);
        return -RRD_ERR_SEEK4;
    }
    /* write as a single block */
    if (rrd_write
        (rrd_file, rrd_values, sizeof(rrd_value_t) * row_length * row_count)
        != (ssize_t) (sizeof(rrd_value_t) * row_length * row_count)) {
        free(rrd_values);
        return -RRD_ERR_WRITE2;
    }

    free(rrd_values);
    free(baseline);
    return 0;
}
Example #6
0
int
rrd_restore(int argc, char **argv) 
{
    rrd_t          rrd;
    char          *buf;
	char			rc = 0;
	char			force_overwrite = 0;	

    /* init rrd clean */
    optind = 0; opterr = 0;  /* initialize getopt */
    rrd_init(&rrd);
    if (argc<3) {
		rrd_set_error("usage rrdtool %s [--range-check/-r] [--force-overwrite/-f] file.xml file.rrd",argv[0]);
		return -1;
    }
	
	while (1) {
		static struct option long_options[] =
		{
			{"range-check",      required_argument, 0,  'r'},
			{"force-overwrite",	required_argument, 0,	'f'},
			{0,0,0,0}
		};
		int option_index = 0;
		int opt;
		
		
		opt = getopt_long(argc, argv, "r:f", long_options, &option_index);
		
		if (opt == EOF)
			break;
		
		switch(opt) {
		case 'r':
			rc=1;
			break;
		case 'f':
			force_overwrite=1;
			break;
		default:
			rrd_set_error("usage rrdtool %s [--range-check|-r] [--force-overwrite/-f]  file.xml file.rrd",argv[0]);
                	return -1;
			break;
		}
    }
	
    if (readfile(argv[optind],&buf,0)==-1){
      return -1;
    }
    if (xml2rrd(buf,&rrd,rc)==-1) {
	rrd_free(&rrd);
	free(buf);
	return -1;
    }
    free(buf);
    if(rrd_write(argv[optind+1],&rrd,force_overwrite)==-1){
	rrd_free(&rrd);	
	return -1;	
    };
    rrd_free(&rrd);    
    return 0;
}
int rrd_resize(
    int argc,
    char **argv)
{
    char     *infilename, outfilename[11] = "resize.rrd";
    rrd_t     rrdold, rrdnew;
    rrd_value_t buffer;
    int       version;
    unsigned long l, rra;
    long      modify;
    unsigned long target_rra;
    int       grow = 0, shrink = 0;
    char     *endptr;
    rrd_file_t *rrd_file, *rrd_out_file;

    infilename = argv[1];
    if (!strcmp(infilename, "resize.rrd")) {
        rrd_set_error("resize.rrd is a reserved name");
        return (-1);
    }
    if (argc != 5) {
        rrd_set_error("wrong number of parameters");
        return (-1);
    }

    target_rra = strtol(argv[2], &endptr, 0);

    if (!strcmp(argv[3], "GROW"))
        grow = 1;
    else if (!strcmp(argv[3], "SHRINK"))
        shrink = 1;
    else {
        rrd_set_error("I can only GROW or SHRINK");
        return (-1);
    }

    modify = strtol(argv[4], &endptr, 0);

    if ((modify < 1)) {
        rrd_set_error("Please grow or shrink with at least 1 row");
        return (-1);
    }

    if (shrink)
        modify = -modify;


    rrd_init(&rrdold);
    rrd_file = rrd_open(infilename, &rrdold, RRD_READWRITE | RRD_COPY);
    if (rrd_file == NULL) {
        rrd_free(&rrdold);
        return (-1);
    }

    if (rrd_lock(rrd_file) != 0) {
        rrd_set_error("could not lock original RRD");
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        return (-1);
    }


    if (target_rra >= rrdold.stat_head->rra_cnt) {
        rrd_set_error("no such RRA in this RRD");
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        return (-1);
    }

    if (modify < 0)
        if ((long) rrdold.rra_def[target_rra].row_cnt <= -modify) {
            rrd_set_error("This RRA is not that big");
            rrd_free(&rrdold);
            rrd_close(rrd_file);
            return (-1);
        }

    rrd_init(&rrdnew);
    /* These need to be initialised before calling rrd_open() with 
       the RRD_CREATE flag */

    if ((rrdnew.stat_head = (stat_head_t*)calloc(1, sizeof(stat_head_t))) == NULL) {
        rrd_set_error("allocating stat_head for new RRD");
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        return (-1);
    }
    memcpy(rrdnew.stat_head,rrdold.stat_head,sizeof(stat_head_t));

    if ((rrdnew.rra_def = (rra_def_t *)malloc(sizeof(rra_def_t) * rrdold.stat_head->rra_cnt)) == NULL) {
        rrd_set_error("allocating rra_def for new RRD");
        rrd_free(&rrdnew);
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        return (-1);
    }
    memcpy(rrdnew.rra_def,rrdold.rra_def,sizeof(rra_def_t) * rrdold.stat_head->rra_cnt);

    /* Set this so that the file will be created with the correct size */
    rrdnew.rra_def[target_rra].row_cnt += modify;

    rrd_out_file = rrd_open(outfilename, &rrdnew, RRD_READWRITE | RRD_CREAT);
    if (rrd_out_file == NULL) {
        rrd_set_error("Can't create '%s': %s", outfilename,
                      rrd_strerror(errno));
        rrd_free(&rrdnew);
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        return (-1);
    }
    if (rrd_lock(rrd_out_file) != 0) {
        rrd_set_error("could not lock new RRD");
        rrd_free(&rrdnew);
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        rrd_close(rrd_out_file);
        return (-1);
    }
/*XXX: do one write for those parts of header that are unchanged */
    if ((rrdnew.rra_ptr = (rra_ptr_t *)malloc(sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt)) == NULL) {
        rrd_set_error("allocating rra_ptr for new RRD");
        rrd_free(&rrdnew);
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        rrd_close(rrd_out_file);
        return (-1);
    }

    /* Put this back the way it was so that the rest of the algorithm
       below remains unchanged, it will be corrected later */
    rrdnew.rra_def[target_rra].row_cnt -= modify;

    rrdnew.ds_def = rrdold.ds_def;
    rrdnew.live_head = rrdold.live_head;
    rrdnew.pdp_prep = rrdold.pdp_prep;
    rrdnew.cdp_prep = rrdold.cdp_prep;
    memcpy(rrdnew.rra_ptr,rrdold.rra_ptr,sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt);


    version = atoi(rrdold.stat_head->version);
    switch (version) {
    case 4:
        break;        
    case 3:
        break;
    case 1:
        rrdnew.stat_head->version[3] = '3';
        break;
    default:
        rrd_set_error("Do not know how to handle RRD version %s",
                      rrdold.stat_head->version);

        rrdnew.ds_def = NULL;
        rrdnew.live_head = NULL;
        rrdnew.pdp_prep = NULL;
        rrdnew.cdp_prep = NULL;

        rrd_free(&rrdnew);
        rrd_free(&rrdold);
        rrd_close(rrd_file);
        rrd_close(rrd_out_file);
        return (-1);
        break;
    }

/* XXX: Error checking? */
    rrd_write(rrd_out_file, rrdnew.stat_head, sizeof(stat_head_t) * 1);
    rrd_write(rrd_out_file, rrdnew.ds_def,
              sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt);
    rrd_write(rrd_out_file, rrdnew.rra_def,
              sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
    rrd_write(rrd_out_file, rrdnew.live_head, sizeof(live_head_t) * 1);
    rrd_write(rrd_out_file, rrdnew.pdp_prep,
              sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt);
    rrd_write(rrd_out_file, rrdnew.cdp_prep,
              sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
              rrdnew.stat_head->rra_cnt);
    rrd_write(rrd_out_file, rrdnew.rra_ptr,
              sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);

    /* Move the CDPs from the old to the new database.
     ** This can be made (much) faster but isn't worth the effort. Clarity
     ** is much more important.
     */

    /* Move data in unmodified RRAs
     */
    l = 0;
    for (rra = 0; rra < target_rra; rra++) {
        l += rrdnew.stat_head->ds_cnt * rrdnew.rra_def[rra].row_cnt;
    }
    while (l > 0) {
        rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
        rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
        l--;
    }
    /* Move data in this RRA, either removing or adding some rows
     */
    if (modify > 0) {
        /* Adding extra rows; insert unknown values just after the
         ** current row number.
         */
        l = rrdnew.stat_head->ds_cnt *
            (rrdnew.rra_ptr[target_rra].cur_row + 1);
        while (l > 0) {
            rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
            rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
            l--;
        }
        buffer = DNAN;
        l = rrdnew.stat_head->ds_cnt * modify;
        while (l > 0) {
            rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
            l--;
        }
    } else {
        /* Removing rows. Normally this would be just after the cursor
         ** however this may also mean that we wrap to the beginning of
         ** the array.
         */
        signed long int remove_end = 0;

        remove_end =
            (rrdnew.rra_ptr[target_rra].cur_row -
             modify) % rrdnew.rra_def[target_rra].row_cnt;
        if (remove_end <=
            (signed long int) rrdnew.rra_ptr[target_rra].cur_row) {
            while (remove_end >= 0) {
                rrd_seek(rrd_file,
                         sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
                         SEEK_CUR);
                rrdnew.rra_ptr[target_rra].cur_row--;
                rrdnew.rra_def[target_rra].row_cnt--;
                remove_end--;
                modify++;
            }
            remove_end = rrdnew.rra_def[target_rra].row_cnt - 1;
        }
        for (l = 0; l <= rrdnew.rra_ptr[target_rra].cur_row; l++) {
            unsigned int tmp;

            for (tmp = 0; tmp < rrdnew.stat_head->ds_cnt; tmp++) {
                rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
                rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
            }
        }
        while (modify < 0) {
            rrd_seek(rrd_file,
                     sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
                     SEEK_CUR);
            rrdnew.rra_def[target_rra].row_cnt--;
            modify++;
        }
    }
    /* Move the rest of the CDPs
     */
    while (1) {
        ssize_t b_read;
        if ((b_read=rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1)) <= 0)
            break;
        if(rrd_out_file->pos+b_read > rrd_out_file->file_len) {
            fprintf(stderr,"WARNING: ignoring last %zu bytes\nWARNING: if you see this message multiple times for a single file you're in trouble\n", b_read);
            continue;
        }
        rrd_write(rrd_out_file, &buffer, b_read);
    }
    rrdnew.rra_def[target_rra].row_cnt += modify;
    rrd_seek(rrd_out_file,
             sizeof(stat_head_t) +
             sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt, SEEK_SET);
    rrd_write(rrd_out_file, rrdnew.rra_def,
              sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
    rrd_seek(rrd_out_file, sizeof(live_head_t), SEEK_CUR);
    rrd_seek(rrd_out_file, sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt,
             SEEK_CUR);
    rrd_seek(rrd_out_file,
             sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
             rrdnew.stat_head->rra_cnt, SEEK_CUR);
    rrd_write(rrd_out_file, rrdnew.rra_ptr,
              sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);
    rrd_close(rrd_file);    
    rrd_close(rrd_out_file);    
    rrd_free(&rrdold);

    rrdnew.ds_def = NULL;
    rrdnew.live_head = NULL;
    rrdnew.pdp_prep = NULL;
    rrdnew.cdp_prep = NULL;

    rrd_free(&rrdnew);
    return (0);
}
int rrd_tune(
    int argc,
    char **argv)
{
    rrd_t     rrd;
    int       matches;
    int       optcnt = 0;
    long      ds;
    char      ds_nam[DS_NAM_SIZE];
    char      ds_new[DS_NAM_SIZE];
    long      heartbeat;
    double    min;
    double    max;
    char      dst[DST_SIZE];
    rrd_file_t *rrd_file;
    struct option long_options[] = {
        {"heartbeat", required_argument, 0, 'h'},
        {"minimum", required_argument, 0, 'i'},
        {"maximum", required_argument, 0, 'a'},
        {"data-source-type", required_argument, 0, 'd'},
        {"data-source-rename", required_argument, 0, 'r'},
        /* added parameter tuning options for aberrant behavior detection */
        {"deltapos", required_argument, 0, 'p'},
        {"deltaneg", required_argument, 0, 'n'},
        {"window-length", required_argument, 0, 'w'},
        {"failure-threshold", required_argument, 0, 'f'},
        {"alpha", required_argument, 0, 'x'},
        {"beta", required_argument, 0, 'y'},
        {"gamma", required_argument, 0, 'z'},
        {"gamma-deviation", required_argument, 0, 'v'},
        {"smoothing-window", required_argument, 0, 's'},
        {"smoothing-window-deviation", required_argument, 0, 'S'},
        {"aberrant-reset", required_argument, 0, 'b'},
        {0, 0, 0, 0}
    };

    optind = 0;
    opterr = 0;         /* initialize getopt */


    rrd_init(&rrd);
    rrd_file = rrd_open(argv[1], &rrd, RRD_READWRITE);
    if (rrd_file == NULL) {
        rrd_free(&rrd);
        return -1;
    }

    while (1) {
        int       option_index = 0;
        int       opt;
        char     *old_locale = "";

        opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
                          long_options, &option_index);
        if (opt == EOF)
            break;

        optcnt++;
        switch (opt) {
        case 'h':
            old_locale = setlocale(LC_NUMERIC, NULL);
            setlocale(LC_NUMERIC, "C");
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":%ld", ds_nam,
                        &heartbeat)) != 2) {
                rrd_set_error("invalid arguments for heartbeat");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                setlocale(LC_NUMERIC, old_locale);
                return -1;
            }
            setlocale(LC_NUMERIC, old_locale);
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
            break;

        case 'i':
            old_locale = setlocale(LC_NUMERIC, NULL);
            setlocale(LC_NUMERIC, "C");
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &min)) < 1) {
                rrd_set_error("invalid arguments for minimum ds value");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                setlocale(LC_NUMERIC, old_locale);
                return -1;
            }
            setlocale(LC_NUMERIC, old_locale);
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }

            if (matches == 1)
                min = DNAN;
            rrd.ds_def[ds].par[DS_min_val].u_val = min;
            break;

        case 'a':
            old_locale = setlocale(LC_NUMERIC, NULL);
            setlocale(LC_NUMERIC, "C");
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":%lf", ds_nam, &max)) < 1) {
                rrd_set_error("invalid arguments for maximum ds value");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                setlocale(LC_NUMERIC, old_locale);
                return -1;
            }
            setlocale(LC_NUMERIC, old_locale);
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            if (matches == 1)
                max = DNAN;
            rrd.ds_def[ds].par[DS_max_val].u_val = max;
            break;

        case 'd':
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":" DST_FMT, ds_nam, dst)) != 2) {
                rrd_set_error("invalid arguments for data source type");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            if ((int) dst_conv(dst) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            /* only reset when something is changed */
            if (strncmp(rrd.ds_def[ds].dst, dst, DST_SIZE - 1) != 0) {
                strncpy(rrd.ds_def[ds].dst, dst, DST_SIZE - 1);
                rrd.ds_def[ds].dst[DST_SIZE - 1] = '\0';

                rrd.pdp_prep[ds].last_ds[0] = 'U';
                rrd.pdp_prep[ds].last_ds[1] = 'N';
                rrd.pdp_prep[ds].last_ds[2] = 'K';
                rrd.pdp_prep[ds].last_ds[3] = 'N';
                rrd.pdp_prep[ds].last_ds[4] = '\0';
            }
            break;
        case 'r':
            if ((matches =
                 sscanf(optarg, DS_NAM_FMT ":" DS_NAM_FMT, ds_nam,
                        ds_new)) != 2) {
                rrd_set_error("invalid arguments for data source type");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            strncpy(rrd.ds_def[ds].ds_nam, ds_new, DS_NAM_SIZE - 1);
            rrd.ds_def[ds].ds_nam[DS_NAM_SIZE - 1] = '\0';
            break;
        case 'p':
            if (set_deltaarg(&rrd, RRA_delta_pos, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'n':
            if (set_deltaarg(&rrd, RRA_delta_neg, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'f':
            if (set_windowarg(&rrd, RRA_failure_threshold, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'w':
            if (set_windowarg(&rrd, RRA_window_len, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'x':
            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_alpha, optarg)) {
                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_alpha, optarg)) {
                    rrd_free(&rrd);
                    return -1;
                }
                rrd_clear_error();
            }
            break;
        case 'y':
            if (set_hwarg(&rrd, CF_HWPREDICT, RRA_hw_beta, optarg)) {
                if (set_hwarg(&rrd, CF_MHWPREDICT, RRA_hw_beta, optarg)) {
                    rrd_free(&rrd);
                    return -1;
                }
                rrd_clear_error();
            }
            break;
        case 'z':
            if (set_hwarg(&rrd, CF_SEASONAL, RRA_seasonal_gamma, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'v':
            if (set_hwarg(&rrd, CF_DEVSEASONAL, RRA_seasonal_gamma, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'b':
            if (sscanf(optarg, DS_NAM_FMT, ds_nam) != 1) {
                rrd_set_error("invalid argument for aberrant-reset");
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            if ((ds = ds_match(&rrd, ds_nam)) == -1) {
                /* ds_match handles it own errors */
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            reset_aberrant_coefficients(&rrd, rrd_file, (unsigned long) ds);
            if (rrd_test_error()) {
                rrd_free(&rrd);
                rrd_close(rrd_file);
                return -1;
            }
            break;
        case 's':
            strcpy(rrd.stat_head->version, RRD_VERSION);    /* smoothing_window causes Version 4 */
            if (set_hwsmootharg
                (&rrd, CF_SEASONAL, RRA_seasonal_smoothing_window, optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case 'S':
            strcpy(rrd.stat_head->version, RRD_VERSION);    /* smoothing_window causes Version 4 */
            if (set_hwsmootharg
                (&rrd, CF_DEVSEASONAL, RRA_seasonal_smoothing_window,
                 optarg)) {
                rrd_free(&rrd);
                return -1;
            }
            break;
        case '?':
            if (optopt != 0)
                rrd_set_error("unknown option '%c'", optopt);
            else
                rrd_set_error("unknown option '%s'", argv[optind - 1]);
            rrd_free(&rrd);
            rrd_close(rrd_file);
            return -1;
        }
    }
    if (optcnt > 0) {
        rrd_seek(rrd_file, 0, SEEK_SET);
        rrd_write(rrd_file, rrd.stat_head, sizeof(stat_head_t) * 1);
        rrd_write(rrd_file, rrd.ds_def,
                  sizeof(ds_def_t) * rrd.stat_head->ds_cnt);
        /* need to write rra_defs for RRA parameter changes */
        rrd_write(rrd_file, rrd.rra_def,
                  sizeof(rra_def_t) * rrd.stat_head->rra_cnt);
    } else {
        int       i;

        for (i = 0; i < (int) rrd.stat_head->ds_cnt; i++)
            if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
                printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
                       rrd.ds_def[i].ds_nam,
                       rrd.ds_def[i].dst,
                       rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
                       rrd.ds_def[i].par[DS_min_val].u_val,
                       rrd.ds_def[i].par[DS_max_val].u_val);
            } else {
                char     *buffer = NULL;

                rpn_compact2str((rpn_cdefds_t *) &
                                (rrd.ds_def[i].par[DS_cdef]), rrd.ds_def,
                                &buffer);
                printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,
                       rrd.ds_def[i].dst, buffer);
                free(buffer);
            }
    }
    rrd_close(rrd_file);
    rrd_free(&rrd);
    return 0;
}