Specifies the base interval in seconds with which data will be fed
into the B<RRD>.
+A scaling factor may be present as a suffix to the integer; see
+L<"STEP, HEARTBEAT, and Rows As Durations">.
=head2 B<--no-overwrite>
I<steps> defines how many of these I<primary data points> are used to build
a I<consolidated data point> which then goes into the archive.
+See also L<"STEP, HEARTBEAT, and Rows As Durations">.
I<rows> defines how many generations of data values are kept in an B<RRA>.
Obviously, this has to be greater than zero.
+See also L<"STEP, HEARTBEAT, and Rows As Durations">.
=head1 Aberrant Behavior Detection with Holt-Winters Forecasting
primary data points. If the FAILURES B<RRA> is implicitly created, the
default value is 9.
+=head1 STEP, HEARTBEAT, and Rows As Durations
+
+Traditionally RRDtool specified PDP intervals in seconds, and most
+other values as either seconds or PDP counts. This made the
+specification for databases rather opaque; for example
+
+ rrdtool create power.rrd \
+ --start now-2h --step 1 \
+ DS:watts:GAUGE:300:0:24000 \
+ RRA:AVERAGE:0.5:1:864000 \
+ RRA:AVERAGE:0.5:60:129600 \
+ RRA:AVERAGE:0.5:3600:13392 \
+ RRA:AVERAGE:0.5:86400:3660
+
+creates a database of power values collected once per second, with a
+five minute (300 second) heartbeat, and four B<RRA>s: ten days of one
+second, 90 days of one minute, 18 months of one hour, and ten years of
+one day averages.
+
+Step, heartbeat, and PDP counts and rows may also be specified as
+durations, which are positive integers with a single-character suffix
+that specifies a scaling factor:
+
+=over
+
+=item C<s>
+
+indicates seconds
+
+=item C<m>
+
+indicates minutes. The value is multipled by 60.
+
+=item C<h>
+
+indicates hours. The value is multipled by 3600 (or C<60m>).
+
+=item C<d>
+
+indicates days. The value is multipled by 86400 (or C<24h>).
+
+=item C<w>
+
+indicates weeks. The value is multipled by 604800 (or C<7d>).
+
+=item C<M>
+
+indicates months. The value is multipled by 2678400 (or C<31d>).
+(Note this factor accommodates the maximum number of days in a month.)
+
+=item C<y>
+
+indicates years. The value is multipled by 31622400 (or C<366d>).
+(Note this factor accommodates leap years.)
+
+=back
+
+Scaled step and heartbeat values (which are natively durations in
+seconds) are used directly, while consolidation function row arguments
+are divided by their step to produce the number of rows. Note that
+the multiplication factors for months and years correspond to the
+maximum number of days in a month or year.
+
+With this feature the same specification as above can be written as:
+
+ rrdtool create power.rrd \
+ --start now-2h --step 1s \
+ DS:watts:GAUGE:5m:0:24000 \
+ RRA:AVERAGE:0.5:1s:10d \
+ RRA:AVERAGE:0.5:1m:90d \
+ RRA:AVERAGE:0.5:1h:18M \
+ RRA:AVERAGE:0.5:1d:10y
+
=head1 The HEARTBEAT and the STEP
Here is an explanation by Don Baarda on the inner workings of RRDtool.
const char *def,
ds_def_t *ds_def);
+static int convert_to_count (const char * token,
+ unsigned long * valuep,
+ unsigned long divisor)
+{
+ char * ep = NULL;
+ unsigned long int value = strtoul(token, &ep, 10);
+ switch (*ep) {
+ case 0: /* count, no conversion */
+ break;
+ case 's': /* seconds */
+ value /= divisor;
+ break;
+ case 'm': /* minutes */
+ value = (60 * value) / divisor;
+ break;
+ case 'h': /* hours */
+ value = (60 * 60 * value) / divisor;
+ break;
+ case 'd': /* days */
+ value = (24 * 60 * 60 * value) / divisor;
+ break;
+ case 'w': /* weeks */
+ value = (7 * 24 * 60 * 60 * value) / divisor;
+ break;
+ case 'M': /* months */
+ value = (31 * 24 * 60 * 60 * value) / divisor;
+ break;
+ case 'y': /* years */
+ value = (366 * 24 * 60 * 60 * value) / divisor;
+ break;
+ default:
+ return 0;
+ }
+ *valuep = value;
+ return (0 != value);
+}
+
int rrd_create(
int argc,
char **argv)
unsigned long pdp_step = 300;
rrd_time_value_t last_up_tv;
char *parsetime_error = NULL;
- long long_tmp;
int rc;
char * opt_daemon = NULL;
int opt_no_overwrite = 0;
break;
case 's':
- long_tmp = atol(optarg);
- if (long_tmp < 1) {
+ if (! convert_to_count(optarg, &pdp_step, 1)) {
rrd_set_error("step size should be no less than one second");
return (-1);
}
- pdp_step = long_tmp;
break;
case 'O':
unsigned short token_idx, error_flag, period = 0;
int cf_id = -1;
int token_min = 4;
- int row_cnt;
char *require_version = NULL;
memset(rra_def, 0, sizeof(rra_def_t));
case CF_SEASONAL:
case CF_DEVPREDICT:
case CF_FAILURES:
- row_cnt = atoi(token);
- if (row_cnt <= 0)
- rrd_set_error("Invalid row count: %i", row_cnt);
- rra_def->row_cnt = row_cnt;
+ if (! convert_to_count(token, &rra_def->row_cnt, 1))
+ rrd_set_error("Invalid row count: %s", token);
break;
default:
rra_def->par[RRA_cdp_xff_val].u_val = atof(token);
atoi(token) - 1;
break;
default:
- rra_def->pdp_cnt = atoi(token);
- if (atoi(token) < 1)
+ if (! convert_to_count(token, &rra_def->pdp_cnt, rrd->stat_head->pdp_step))
rrd_set_error("Invalid step: must be >= 1");
break;
}
("Unexpected extra argument for consolidation function DEVPREDICT");
break;
default:
- row_cnt = atoi(token);
- if (row_cnt <= 0)
- rrd_set_error("Invalid row count: %i", row_cnt);
+ if (! convert_to_count(token, &rra_def->row_cnt, rra_def->pdp_cnt))
+ rrd_set_error("Invalid row count: %s", token);
#if SIZEOF_TIME_T == 4
if ((long long) pdp_step * rra_def->pdp_cnt * row_cnt > 4294967296LL){
/* database timespan > 2**32, would overflow time_t */
rrd_set_error("The time spanned by the database is too large: must be <= 4294967296 seconds");
}
#endif
- rra_def->row_cnt = row_cnt;
break;
}
break;
{
char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];
char *old_locale;
+ int emit_failure = 1;
/*
int temp;
minstr,maxstr);
*/
old_locale = setlocale(LC_NUMERIC, "C");
+ do {
+ char numbuf[32];
+ size_t heartbeat_len;
+ char *colonp;
+
+ /* convert heartbeat as count or duration */
+ colonp = strchr(def, ':');
+ if (! colonp)
+ break;
+ heartbeat_len = colonp - def;
+ if (heartbeat_len >= sizeof(numbuf))
+ break;
+ strncpy (numbuf, def, heartbeat_len);
+ numbuf[heartbeat_len] = 0;
- if (sscanf(def, "%lu:%18[^:]:%18[^:]",
- &(ds_def->par[DS_mrhb_cnt].u_cnt),
- minstr, maxstr) == 3) {
- if (minstr[0] == 'U' && minstr[1] == 0)
- ds_def->par[DS_min_val].u_val = DNAN;
- else
- ds_def->par[DS_min_val].u_val = atof(minstr);
-
- if (maxstr[0] == 'U' && maxstr[1] == 0)
- ds_def->par[DS_max_val].u_val = DNAN;
- else
- ds_def->par[DS_max_val].u_val = atof(maxstr);
-
- if (!isnan(ds_def->par[DS_min_val].u_val) &&
- !isnan(ds_def->par[DS_max_val].u_val) &&
- ds_def->par[DS_min_val].u_val
- >= ds_def->par[DS_max_val].u_val) {
- rrd_set_error("min must be less than max in DS definition");
- setlocale(LC_NUMERIC, old_locale);
- return;
+ if (! convert_to_count(numbuf, &(ds_def->par[DS_mrhb_cnt].u_cnt), 1))
+ break;
+
+ if (sscanf(1+colonp, "%18[^:]:%18[^:]",
+ minstr, maxstr) == 2) {
+ emit_failure = 0;
+ if (minstr[0] == 'U' && minstr[1] == 0)
+ ds_def->par[DS_min_val].u_val = DNAN;
+ else
+ ds_def->par[DS_min_val].u_val = atof(minstr);
+
+ if (maxstr[0] == 'U' && maxstr[1] == 0)
+ ds_def->par[DS_max_val].u_val = DNAN;
+ else
+ ds_def->par[DS_max_val].u_val = atof(maxstr);
+
+ if (!isnan(ds_def->par[DS_min_val].u_val) &&
+ !isnan(ds_def->par[DS_max_val].u_val) &&
+ ds_def->par[DS_min_val].u_val
+ >= ds_def->par[DS_max_val].u_val) {
+ rrd_set_error("min must be less than max in DS definition");
+ break;
+ }
}
- } else {
+ } while (0);
+ if (emit_failure)
rrd_set_error("failed to parse data source %s", def);
- }
setlocale(LC_NUMERIC, old_locale);
}