Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Sun, 4 Mar 2001 13:01:56 +0000 (13:01 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Sun, 4 Mar 2001 13:01:56 +0000 (13:01 +0000)
Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
This is backwards compatible! But new files using the Aberrant stuff are not readable
by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
-- Jake Brutlag <jakeb@corp.webtv.net>

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@26 a5681a0c-68f1-0310-ab6d-d61299d08faa

20 files changed:
NEWS [new file with mode: 0644]
doc/rrdcreate.pod
doc/rrdgraph.pod
doc/rrdtool.pod
doc/rrdtune.pod
src/Makefile.am
src/fnv.h [new file with mode: 0644]
src/hash_32.c [new file with mode: 0644]
src/rrd_create.c
src/rrd_dump.c
src/rrd_format.h
src/rrd_graph.c
src/rrd_hw.c [new file with mode: 0644]
src/rrd_info.c
src/rrd_open.c
src/rrd_restore.c
src/rrd_tool.h
src/rrd_tune.c
src/rrd_update.c
src/rrdupdate.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..b9c3a64
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,18 @@
+RRDTOOL NEWS
+============
+
+In this file I am noting the Major changes to rrdtool
+for details check the cvs ChangeLog
+
+2001/03/21 Tobias Oetiker <oetiker@ee.ethz.ch>
+  Added Aberrant Patch from Jake Brutlag <jakeb@corp.webtv.net>
+  From now one, new rrd files use version tag 0002. They can
+  NOT be read by the old 1.0.x rrdtools
+
+  Jack:
+  Aberrant Behavior Detection support. A brief overview added to
+  rrdtool.pod.  Major updates to rrd_update.c, rrd_create.c. Minor update to
+  other core files.  Updated documentation: rrdcreate.pod, rrdgraph.pod,
+  rrdtune.pod.  This is backwards compatible!  See
+  http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+
index 8be0567..14b6f19 100644 (file)
@@ -10,7 +10,7 @@ B<rrdtool> B<create> I<filename>
 S<[B<--start>|B<-b> I<start time>]> 
 S<[B<--step>|B<-s> I<step>]> 
 S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<heartbeat>B<:>I<min>B<:>I<max>]>
 S<[B<--start>|B<-b> I<start time>]> 
 S<[B<--step>|B<-s> I<step>]> 
 S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<heartbeat>B<:>I<min>B<:>I<max>]>
-S<[B<RRA:>I<CF>B<:>I<xff>B<:>I<steps>B<:>I<rows>]>
+S<[B<RRA:>I<CF>B<:>I<cf arguments>]>
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -106,30 +106,174 @@ I<If information on minimal/maximal expected values is available,
 always set the min and/or max properties. This will help RRDtool in
 doing a simple sanity check on the data supplied when running update.>
 
 always set the min and/or max properties. This will help RRDtool in
 doing a simple sanity check on the data supplied when running update.>
 
-=item B<RRA:>I<CF>B<:>I<xff>B<:>I<steps>B<:>I<rows>
-
+=item B<RRA:>I<CF>B<:>I<cf arguments>
+  
 The purpose of an B<RRD> is to store data in the round robin archives
 The purpose of an B<RRD> is to store data in the round robin archives
-(B<RRA>). An archive consists of a number of data values from all the
-defined data-sources (B<DS>) and is defined with an B<RRA> line.
+(B<RRA>). An archive consists of a number of data values or statistics for 
+each of the defined data-sources (B<DS>) and is defined with an B<RRA> line.
 
 When data is entered into an B<RRD>, it is first fit into time slots of
 the length defined with the B<-s> option becoming a I<primary data point>.
 
 
 When data is entered into an B<RRD>, it is first fit into time slots of
 the length defined with the B<-s> option becoming a I<primary data point>.
 
-The data is also consolidated with the consolidation function (I<CF>)
-of the archive. The following consolidation functions are defined:
-B<AVERAGE>, B<MIN>, B<MAX>, B<LAST>.
+The data is also processed with the consolidation function (I<CF>)
+of the archive. There are several consolidation functions that consolidate
+primary data points via an aggregate function: B<AVERAGE>, B<MIN>, B<MAX>, B<LAST>.
+The format of B<RRA> line for these consolidation function is:
+
+B<RRA:>I<AVERAGE | MIN | MAX | LAST>B<:>I<xff>B<:>I<steps>B<:>I<rows>
 
 I<xff> The xfiles factor defines what part of a consolidation interval may
 be made up from I<*UNKNOWN*> data while the consolidated value is still
 regarded as known.
 
 
 I<xff> The xfiles factor defines what part of a consolidation interval may
 be made up from I<*UNKNOWN*> data while the consolidated value is still
 regarded as known.
 
-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.
+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.
 
 I<rows> defines how many generations of data values are kept in an B<RRA>.
 
 =back
 
 
 I<rows> defines how many generations of data values are kept in an B<RRA>.
 
 =back
 
+=head1 Aberrant Behaviour detection with Holt-Winters forecasting
+
+by Jake Brutlag E<lt>jakeb@corp.webtv.netE<gt>
+
+In addition to the aggregate functions, there are a set of specialized
+functions that enable B<RRDtool> to provide data smoothing (via the
+Holt-Winters forecasting algorithm), confidence bands, and the flagging
+aberrant behavior in the data source time series:
+
+=over 4
+
+=item B<RRA:>I<HWPREDICT>B<:>I<rows>B<:>I<alpha>B<:>I<beta>B<:>I<seasonal period>B<:>I<rra num>
+
+=item B<RRA:>I<SEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra num>
+
+=item B<RRA:>I<DEVSEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra num>
+
+=item B<RRA:>I<DEVPREDICT>B<:>I<rows>B<:>I<rra num>
+
+=item B<RRA:>I<FAILURES>B<:>I<rows>B<:>I<threshold>B<:>I<window length>B<:>I<rra num>
+
+=back
+
+These B<RRAs> differ from the true consolidation functions in several ways.
+First, each of the B<RRA>s is updated once for every primary data point.
+Second, these B<RRAs> are interdependent. To generate real-time confidence
+bounds, then a matched set of HWPREDICT, SEASONAL, DEVSEASONAL, and
+DEVPREDICT must exist. Generating smoothed values of the primary data points
+requires both a HWPREDICT B<RRA> and SEASONAL B<RRA>. Aberrant behavior
+detection requires FAILURES, HWPREDICT, DEVSEASONAL, and SEASONAL.
+
+The actual predicted, or smoothed, values are stored in the HWPREDICT
+B<RRA>. The predicted deviations are store in DEVPREDICT (think a standard
+deviation which can be scaled to yield a confidence band). The FAILURES
+B<RRA> stores binary indicators. A 1 marks the indexed observation a
+failure; that is, the number of confidence bounds violations in the
+preceding window of observations met or exceeded a specified threshold. An
+example of using these B<RRAs> to graph confidence bounds and failures
+appears in L<rrdgraph>.
+
+The SEASONAL and DEVSEASONAL B<RRAs> store the seasonal coefficients for the
+Holt-Winters Forecasting algorithm and the seasonal deviations respectively.
+There is one entry per observation time point in the seasonal cycle. For
+example, if primary data points are generated every five minutes, and the
+seasonal cycle is 1 day, both SEASONAL and DEVSEASONAL with have 288 rows.
+
+In order to simplify the creation for the novice user, in addition to
+supporting explicit creation the HWPREDICT, SEASONAL, DEVPREDICT,
+DEVSEASONAL, and FAILURES B<RRAs>, the B<rrdtool> create command supports
+implicit creation of the other four when HWPREDICT is specified alone and
+the final argument I<rra num> is omitted.
+
+I<rows> specifies the length of the B<RRA> prior to wrap around. Remember
+that there is a one-to-one correspondence between primary data points and
+entries in these RRAs. For the HWPREDICT CF, I<rows> should be larger than
+the I<seasonal period>. If the DEVPREDICT B<RRA> is implicity created, the
+default number of rows is the same as the HWPREDICT I<rows> argument. If the
+FAILURES B<RRA> is implicitly created, I<rows> will be set to the I<seasonal
+period> argument of the HWPREDICT B<RRA>. Of course, the B<rrdtool>
+I<resize> command is available if these defaults are not sufficient and the
+create wishes to avoid explicit creations of the other specialized function
+B<RRAs>.
+
+I<seasonal period> specifies the number of primary data points in a seasonal
+cycle. If SEASONAL and DEVSEASONAL are implicitly created, this argument for
+those B<RRAs> is set automatically to the value specified by HWPREDICT. If
+they are explicity created, the creator should verify that all three
+I<seasonal period> arguments agree.
+
+I<alpha> is the adaptation parameter of the intercept (or baseline)
+coefficient in the Holt-Winters Forecasting algorithm. See L<rrdtool> for a
+description of this algorithm. I<alpha> must lie between 0 and 1. A value
+closer to 1 means that more recent observations carry greater weight in
+predicting the baseline component of the forecast. A value closer to 0 mean
+that past history carries greater weight in predicted the baseline
+component.
+
+I<beta> is the adaption parameter of the slope (or linear trend) coefficient
+in the Holt-Winters Forecating algorihtm. I<beta> must lie between 0 and 1
+and plays the same role as I<alpha> with respect to the predicted linear
+trend.
+
+I<gamma> is the adaption parameter of the seasonal coefficients in the
+Holt-Winters Forecasting algorithm (HWPREDICT) or the adaption parameter in
+the exponential smoothing update of the seasonal deviations. It must lie
+between 0 and 1. If the SEASONAL and DEVSEASONAL B<RRAs> are created
+implicitly, they will both have the same value for I<gamma>: the value
+specified for the HWPREDICT I<alpha> argument. Note that because there is
+one seasonal coefficient (or deviation) for each time point during the
+seasonal cycle, the adaption rate is much slower than the baseline. Each
+seasonal coefficient is only updated (or adapts) when the observed value
+occurs at the offset in the seasonal cycle corresponding to that
+coefficient.
+
+If SEASONAL and DEVSEASONAL B<RRAs> are created explicity, I<gamma> need not
+be the same for both. Note that I<gamma> can also be changed via the
+B<rrdtool> I<tune> command.
+
+I<rra num> provides the links between related B<RRAs>. If HWPREDICT is
+specified alone and the other B<RRAs> created implicitly, then there is no
+need to worry about this argument. If B<RRAs> are created explicitly, then
+pay careful attention to this argument. For each B<RRA> which includes this
+argument, there is a dependency between that B<RRA> and another B<RRA>. The
+I<rra num> argument is the 1-based index in the order of B<RRA> creation
+(that is, the order they appear in the I<create> command). The dependent
+B<RRA> for each B<RRA> requiring the I<rra num> argument is listed here:
+
+=over 4
+
+=item *
+
+HWPREDICT I<rra num> is the index of the SEASONAL B<RRA>.
+
+=item * 
+
+SEASONAL I<rra num> is the index of the HWPREDICT B<RRA>.
+
+=item * 
+
+DEVPREDICT I<rra num> is the index of the DEVSEASONAL B<RRA>.
+
+=item *
+
+DEVSEASONAL I<rra num> is the index of the HWPREDICT B<RRA>.
+
+=item * 
+
+FAILURES I<rra num> is the index of the DEVSEASONAL B<RRA>.
+
+=back
+
+I<threshold> is the minimum number of violations (observed values outside
+the confidence bounds) within a window that constitutes a failure. If the
+FAILURES B<RRA> is implicitly created, the default value is 7.
+
+I<window length> is the number of time points in the window. Specify an
+integer greater than or equal to the threshold and less than or equal to 28.
+The time interval this window represents depends on the interval between
+primary data points. If the FAILURES B<RRA> is implicity created, the
+default value is 9.
+
 =head1 The HEARTBEAT and the STEP
 
 Here is an explanation by Don Baarda on the inner workings of rrdtool.
 =head1 The HEARTBEAT and the STEP
 
 Here is an explanation by Don Baarda on the inner workings of rrdtool.
@@ -232,6 +376,46 @@ every hour (12 * 300 seconds = 1 hour), for 100 days (2400 hours). The
 third and the fourth RRA's do the same with the for the maximum and
 average temperature, respectively.
 
 third and the fourth RRA's do the same with the for the maximum and
 average temperature, respectively.
 
+=head1 EXAMPLE 2
+
+C<rrdtool create monitor.rrd --step 300 
+DS:ifOutOctets:COUNTER:1800:0:4294967295
+RRA:AVERAGE:0.5:1:2016
+RRA:HWPREDICT:1440:0.1:0.0035:288>
+
+This example is a monitor of a router interface. The first B<RRA> tracks the
+traffic flow in octects; the second B<RRA> generates the specialized
+functions B<RRAs> for aberrant behavior detection. Note that the I<rra num>
+argument of HWPREDICT is missing, so the other B<RRAs> will be implicitly be
+created with default parameter values. In this example, the forecasting
+algorithm baseline adapts quickly; in fact the most recent one hour of
+observations (each at 5 minute intervals) account for 75% of the baseline
+prediction. The linear trend forecast adapts much more slowly. Observations
+made in during the last day (at 288 observations per day) account for only
+65% of the predicted linear trend. Note: these computations rely on an
+exponential smoothing formula described in a forthcoming LISA 2000 paper.
+
+The seasonal cycle is one day (288 data points at 300 second intervals), and
+the seasonal adaption paramter will be set to 0.1. The RRD file will store 5
+days (1440 data points) of forecasts and deviation predictions before wrap
+around. The file will store 1 day (a seasonal cycle) of 0-1 indicators in
+the FAILURES B<RRA>.
+
+The same RRD file and B<RRAs> are created with the following command, which explicitly
+creates all specialized function B<RRAs>.
+
+C<rrdtool create monitor.rrd --step 300 
+DS:ifOutOctets:COUNTER:1800:0:4294967295
+RRA:AVERAGE:0.5:1:2016
+RRA:HWPREDICT:1440:0.1:0.0035:288:3
+RRA:SEASONAL:288:0.1:2
+RRA:DEVPREDICT:1440:5
+RRA:DEVSEASONAL:288:0.1:2
+RRA:FAILURES:288:7:9:5>
+
+Of course, explicit creation need not replicate implicit create, a number of arguments 
+could be changed.
+
 =head1 AUTHOR
 
 Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
 =head1 AUTHOR
 
 Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
index 2e3a4f6..8e8038e 100644 (file)
@@ -41,6 +41,7 @@ S<[B<VRULE:>I<time>B<#>I<rrggbb>[B<:>I<legend>]]>
 S<[B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
 S<[B<AREA:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
 S<[B<STACK:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
 S<[B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
 S<[B<AREA:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
 S<[B<STACK:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]]>
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -472,10 +473,18 @@ B<AREA> or B<LINE?> -- you need something to stack something onto in
 the first place ;) 
 
 Note, that when you STACK onto *UNKNOWN* data, rrdtool will not draw
 the first place ;) 
 
 Note, that when you STACK onto *UNKNOWN* data, rrdtool will not draw
-any graphics ... *UNKNOWN* is not zero ... if you want it to zero
+any graphics ... *UNKNOWN* is not zero ... if you want it to be zero
 then you might want to use a CDEF argument with IF and UN functions to
 turn *UNKNOWN* into zero ...
 
 then you might want to use a CDEF argument with IF and UN functions to
 turn *UNKNOWN* into zero ...
 
+=item B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]
+
+Plot a tick mark (a vertical line) for each value of I<vname> that is
+non-zero and not *UNKNOWN*. The I<axis-fraction> argument specifies the
+length of the tick mark as a fraction of the y-axis; the default value
+is 0.1 (10% of the axis). Note that the color specification is not
+optional.
+
 =back
 
 =head1 NOTES on legend arguments
 =back
 
 =head1 NOTES on legend arguments
@@ -587,6 +596,61 @@ Note that this example assumes that your data is in the positive half of the y-a
 otherwhise you would would have to add NEGINF in order to extend the coverage
 of the rea to whole graph.
 
 otherwhise you would would have to add NEGINF in order to extend the coverage
 of the rea to whole graph.
 
+=head1 EXAMPLE 4
+
+If the specialized function B<RRAs> exist for aberrant behavior detection, they
+can be used to generate the graph of a time series with confidence bands and
+failures.
+
+   rrdtool graph example.gif \
+          DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \
+          DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \
+          DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \
+          DEF:fail=monitor.rrd:ifOutOctets:FAILURES \
+          TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \
+          CDEF:scaledobs=obs,8,* \
+          CDEF:upper=pred,dev,2,*,+ \
+          CDEF:lower=pred,dev,2,*,- \
+          CDEF:scaledupper=upper,8,* \
+          CDEF:scaledlower=lower,8,* \
+          LINE2:scaledobs#0000ff:"Average bits out" \
+          LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \
+          LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out"
+
+This example generates a graph of the data series in blue (LINE2 with the scaledobs
+virtual data source), confidence bounds in red (scaledupper and scaledlower virtual
+data sources), and potential failures (i.e. potential aberrant aberrant behavior)
+marked by vertical yellow lines (the fail data source).
+
+The raw data comes from an AVERAGE B<RRA>, the finest resolution of the observed
+time series (one consolidated data point per primary data point). The predicted
+(or smoothed) values are stored in the HWPREDICT B<RRA>. The predicted deviations
+(think standard deviation) values are stored in the DEVPREDICT B<RRA>. Finally,
+the FAILURES B<RRA> contains indicators, with 1 denoting a potential failure.
+
+All of the data is rescaled to bits (instead of Octets) by multiplying by 8.
+The confidence bounds are computed by an offset of 2 deviations both above
+and below the predicted values (the CDEFs upper and lower). Vertical lines
+indicated potential failures are graphed via the TICK graph element, which
+converts non-zero values in an B<RRA> into tick marks. Here an axis-fraction
+argument of 1.0 means the tick marks span the entire y-axis, and hence become
+vertical lines on the graph.
+
+The choice of 2 deviations (a scaling factor) matches the default used internally
+by the FAILURES B<RRA>. If the internal value is changed (see L<rrdtune>), this
+graphing command should be changed to be consistent.
+
+=head2 A note on data reduction:
+
+The B<rrdtool> I<graph> command is designed to plot data at a specified temporal
+resolution, regardless of the actually resolution of the data in the RRD file.
+This can present a problem for the specialized consolidation functions which
+maintain a one-to-one mapping between primary data points and consolidated
+data points. If a graph insists on viewing the contents of these B<RRAs> on a
+coarser temporal scale, the I<graph> command tries to do something intelligent,
+but the confidence bands and failures no longer have the same meaning and may
+be misleading.
+
 =head1 AUTHOR
 
 Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
 =head1 AUTHOR
 
 Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
index e487863..fee6d35 100644 (file)
@@ -170,6 +170,63 @@ B<RRD>s. The graphing feature is fully configurable. Size, color and
 contents of the graph can be defined freely. Check L<rrdgraph>
 for more information on this.
 
 contents of the graph can be defined freely. Check L<rrdgraph>
 for more information on this.
 
+=item Aberrant Behavior Detection
+
+by Jake Brutlag E<lt>jakeb@corp.webtv.netE<gt>
+
+The  B<rrdtool> also provides the building blocks for near real-time
+aberrant behavior detection. These components include:
+
+=over 12
+
+=item *
+
+An algorithm for predicting the values time series one time step into the future.
+
+=item *
+
+A measure of deviation between the predicted values and the observed values.
+
+=item *
+
+A mechanism to decide if and when an observed value
+or sequence of observed values is I<too deviant> from the predicted value(s).
+
+=back
+
+Each of these components is briefly described:
+
+Holt-Winters Time Series Forecasting Algorithm is an online, or incremental, 
+algorithm that adaptively predicts future observations in a time series. It's 
+forecast is the sum of three components: a baseline (or intercept), a linear 
+trend over time (or slope), and a seasonal coefficient (a periodic effect, 
+such as a daily cycle). There is one seasonal coefficient for each time point 
+in the period (cycle). After a value is observed, each of these components is 
+updated via exponential smoothing. So the algorithm learns from past values 
+and uses them to predict the future. The rate of adaptation is governed by 
+3 parameters, alpha (intercept), beta (slope), and gamma (seasonal). The prediction 
+can also be viewed as a smoothed value for the time series.
+
+The measure of deviation is a seasonal weighted absolute deviation. The term 
+I<seasonal> means deviation is measured separately for each time point in the 
+seasonal cycle. As with Holt-Winters Forecasting, deviation is predicted using 
+the measure computed from past values (but only at that point in the seasonal cycle). 
+After the value is observed, the algorithm learns from the observed value via 
+exponential smoothing. Confidence bands for the observed time series are generated 
+by scaling the sequence of predicted deviation values (we usually think of the sequence 
+as a continuous line rather than a set of discrete points).
+
+Aberrant behavior (a potential failure) is reported whenever the number of 
+times the observed value violates the confidence bands meets or exceeds a 
+specified threshold within a specified temporal window (i.e. 5 violations 
+during the past 45 minutes with a value observed every 5 mintues).
+
+This functionality is embedded in a set of related B<RRAs>. In particular, a FAILURES
+B<RRA> logs potential failures. Presumably a front-end application to B<rrdtool> can
+utilize this B<RRA> to initiate real-time alerts if that is desired.
+
+You can find a detailed description of how to set this up in L<rrdcreate>.
+
 =back
 
 =head2 REMOTE CONTROL
 =back
 
 =head2 REMOTE CONTROL
index badd5d5..f96d8d1 100644 (file)
@@ -12,6 +12,13 @@ S<[B<--minimum>|B<-i> I<ds-name>:I<min>]>
 S<[B<--maximum>|B<-a> I<ds-name>:I<max>]>
 S<[B<--data-source-type>|B<-d> I<ds-name>:I<DST>]>
 S<[B<--data-source-rename>|B<-r> I<old-name>:I<new-name>]>
 S<[B<--maximum>|B<-a> I<ds-name>:I<max>]>
 S<[B<--data-source-type>|B<-d> I<ds-name>:I<DST>]>
 S<[B<--data-source-rename>|B<-r> I<old-name>:I<new-name>]>
+S<[B<--deltapos> I<scale-value>]>
+S<[B<--deltaneg> I<scale value>]>
+S<[B<--failure-threshold> I<failure-threshold>]>
+S<[B<--window-length> I<window-length>]>
+S<[B<--alpha> I<adaption-parameter>]>
+S<[B<--beta> I<adaption-parameter>]>
+S<[B<--gamma> I<adaption-parameter>]>
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -21,11 +28,14 @@ All these tunable parameters together decide when data fed into an
 B<RRD> is to be regarded as invalid. Invalid data is entered into the 
 database as *UNKNOWN*.
 
 B<RRD> is to be regarded as invalid. Invalid data is entered into the 
 database as *UNKNOWN*.
 
-The main application of the B<tune> function is to relax the 
+One application of the B<tune> function is to relax the 
 validation rules on an B<RRD>. This allows to fill a new B<RRD> with
 data available in larger intervals than what you would normally want
 to permit.
 
 validation rules on an B<RRD>. This allows to fill a new B<RRD> with
 data available in larger intervals than what you would normally want
 to permit.
 
+A second application of the B<tune> function is to set or alter parameters
+used by the specialized function B<RRAs> for aberrant behavior detection.
+
 =over 8
 
 =item I<filename>
 =over 8
 
 =item I<filename>
@@ -51,13 +61,59 @@ Setting I<max> to 'U' will disable this limit.
 
 alter the type B<DST> of a data source.
 
 
 alter the type B<DST> of a data source.
 
-=item S<[B<--data-source-rename>|B<-r> I<old-name>:I<new-name>]>
+=item S<B<--data-source-rename>|B<-r> I<old-name>:I<new-name>>
 
 rename a data source
 
 
 rename a data source
 
+=item S<B<--deltapos> I<scale-value>>
+
+Alter the deviation scaling factor for the upper bound of the confidence band
+used internally to calculate violations for the FAILURES B<RRA>. The default
+value is 2. Note that this parameter is not related to graphing confidence
+bounds, that scale factor is specified as a CDEV argument to generate a graph with
+confidence bounds. It need agree with the value used internally by the FAILURES
+B<RRA> (although common sense dictates it should).
+
+=item S<B<--deltaneg> I<scale-value>>
+
+Alter the deviation scaling factor for the lower bound of the confidence band
+used internally to calculate violations for the FAILURES B<RRA>. The default
+value is 2. As with B<--deltapos>, this argument is unrelated to the scale
+factor chosen when graphing confidence bounds.
+
+=item S<B<--failure-threshold> I<failure-threshold>>
+
+Alter the number of confidence bound violations that constitute a failure for
+purposes of the FAILURES B<RRA>. This must be an integer less than or equal to
+the window length of the FAILURES B<RRA>. This restriction is not verified by
+the tune option, so one can reset failure-threshold and window-length 
+simultaneously. Setting this option will reset the count of violations to 0.
+
+=item S<B<--window-length> I<window-length>>
+
+Alter the number of time points in the temporal window for determining failures.
+This must be an integer greater than or equal to the window length of the
+FAILURES B<RRA> and less than or equal to 28. Setting this option will reset the
+count of violations to 0.
+
+=item S<B<--alpha> I<adaption-parameter>>
+
+Alter the intercept adaptation parameter for the Holt-Winters forecasting algorithm.
+Must be between 0 and 1.
+
+=item S<B<--beta> I<adaption-parameter>>
+
+Alter the intercept adaptation parameter for the Holt-Winters forecasting algorithm.
+Must be between 0 and 1.
+
+=item S<B<--gamma> I<adaption-parameter>>
+
+Alter the seasonal coefficient and deviation adaptation parameters the SEASONAL and
+DEVSEAONAL B<RRAs>. Must be between 0 and 1.
+
 =back
 
 =back
 
-=head1 EXAMPLE
+=head1 EXAMPLE 1
 
 C<rrdtool tune data.rrd -h in:100000 -h out:100000 -h through:100000>
 
 
 C<rrdtool tune data.rrd -h in:100000 -h out:100000 -h through:100000>
 
@@ -66,6 +122,14 @@ and 'through' to 10000 seconds which is a little over one day in data.rrd.
 This would allow to feed old data from mrtg-2.0 right into
 rrdtool without generating *UNKNOWN* entries.
 
 This would allow to feed old data from mrtg-2.0 right into
 rrdtool without generating *UNKNOWN* entries.
 
+=head1 EXAMPLE 2
+
+C<rrdtool tune monitor.rrd --window-length 5 --failure-threshold 3>
+
+If the FAILURES B<RRA> is implicitly created, the default window-length is 9 and 
+the default failure-thresold is 7. This command now defines a failure as 3 or more
+violations in a temporal window of 5 time points.
+
 =head1 AUTHOR
 
 Tobias Oetiker <oetiker@ee.ethz.ch>
 =head1 AUTHOR
 
 Tobias Oetiker <oetiker@ee.ethz.ch>
index 875c007..33f6790 100644 (file)
@@ -22,6 +22,8 @@ RRD_C_FILES =         \
        getopt1.c       \
        gifsize.c       \
        parsetime.c     \
        getopt1.c       \
        gifsize.c       \
        parsetime.c     \
+       hash_32.c       \
+        rrd_hw.c       \
        pngsize.c       \
        rrd_create.c    \
        rrd_diff.c      \
        pngsize.c       \
        rrd_create.c    \
        rrd_diff.c      \
diff --git a/src/fnv.h b/src/fnv.h
new file mode 100644 (file)
index 0000000..205e5a8
--- /dev/null
+++ b/src/fnv.h
@@ -0,0 +1,102 @@
+/*
+ * fnv - Fowler/Noll/Vo- hash code
+ *
+ * @(#) $Revision$
+ * @(#) $Id$
+ * @(#) $Source$
+ *
+ ***
+ *
+ * Fowler/Noll/Vo- hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *      Phong Vo (http://www.research.att.com/info/kpv/)
+ *      Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *      Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *      http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ *
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended.  One should use
+ *      the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *     chongo <Landon Curt Noll> /\oo/\
+ *     http://reality.sgi.com/chongo/
+ *     EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy!    :-)
+ */
+
+#if !defined(__FNV_H__)
+#define __FNV_H__
+
+
+/*
+ * 32 bit FNV-0 hash type
+ */
+typedef unsigned long Fnv32_t;
+
+
+/* 
+ * 32 bit FNV-0 zero initial basis
+ *
+ * This historic hash is not recommended.  One should use
+ * the FNV-1 hash and inital basis instead.
+ */
+#define FNV0_32_INIT ((Fnv32_t)0)
+
+
+/*
+ * 32 bit FNV-1 non-zero initial basis
+ *
+ * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
+ *
+ *              chongo <Landon Curt Noll> /\../\
+ *
+ * Note that the \'s above are not back-slashing escape characters.
+ * They are literal ASCII  backslash 0x5c characters.
+ */
+#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
+
+#endif /* __FNV_H__ */
diff --git a/src/hash_32.c b/src/hash_32.c
new file mode 100644 (file)
index 0000000..3fda164
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * hash_32 - 32 bit Fowler/Noll/Vo hash code
+ *
+ *
+ ***
+ *
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *      Phong Vo (http://www.research.att.com/info/kpv/)
+ *      Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *      Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *      http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended.  One should use
+ *      the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *     chongo <Landon Curt Noll> /\oo/\
+ *     http://reality.sgi.com/chongo/
+ *     EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy!    :-)
+ */
+
+#include <stdlib.h>
+#include "fnv.h"
+
+
+/* 
+ * 32 bit magic FNV-0 and FNV-1 prime 
+ */
+#define FNV_32_PRIME ((Fnv32_t)0x01000193)     
+
+
+/*
+ * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer
+ *
+ * input:
+ *     buf     - start of buffer to hash
+ *     len     - length of buffer in octets
+ *     hval    - previous hash value or 0 if first call
+ *
+ * returns:
+ *     32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t
+fnv_32_buf(void *buf, size_t len, Fnv32_t hval)
+{
+    unsigned char *bp = (unsigned char *)buf;  /* start of buffer */
+    unsigned char *be = bp + len;              /* beyond end of buffer */
+
+    /*
+     * FNV-1 hash each octet in the buffer
+     */
+    while (bp < be) {
+
+       /* multiply by the 32 bit FNV magic prime mod 2^64 */
+       hval *= FNV_32_PRIME;
+
+       /* xor the bottom with the current octet */
+       hval ^= (Fnv32_t)*bp++;
+    }
+
+    /* return our new hash value */
+    return hval;
+}
+
+
+/*
+ * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string
+ *
+ * input:
+ *     str     - string to hash
+ *     hval    - previous hash value or 0 if first call
+ *
+ * returns:
+ *     32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ *      argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t
+fnv_32_str(char *str, Fnv32_t hval)
+{
+    unsigned char *s = (unsigned char *)str;   /* unsigned string */
+
+    /*
+     * FNV-1 hash each octet in the buffer
+     */
+    while (*s) {
+
+       /* multiply by the 32 bit FNV magic prime mod 2^64 */
+       hval *= FNV_32_PRIME;
+
+       /* xor the bottom with the current octet */
+       hval ^= (Fnv32_t)*s++;
+    }
+
+    /* return our new hash value */
+    return hval;
+}
+
+/* a wrapper function for fnv_32_str */
+unsigned long FnvHash(char *str)
+{
+  return fnv_32_str(str,FNV1_32_INIT);
+}
index f118da6..d60e2e9 100644 (file)
@@ -6,7 +6,10 @@
 
 #include "rrd_tool.h"
 
 
 #include "rrd_tool.h"
 
+/* prototype for FnvHash */
+unsigned long FnvHash(char *str);
 
 
+/* #define DEBUG */
 int
 rrd_create(int argc, char **argv) 
 {
 int
 rrd_create(int argc, char **argv) 
 {
@@ -15,7 +18,9 @@ rrd_create(int argc, char **argv)
     time_t             last_up;
     struct time_value last_up_tv;
     char *parsetime_error = NULL;
     time_t             last_up;
     struct time_value last_up_tv;
     char *parsetime_error = NULL;
-
+    char *token;
+    unsigned short token_idx, error_flag, period=0;
+       unsigned long hashed_name;
     /* init last_up */
     last_up = time(NULL)-10;
     /* init rrd clean */
     /* init last_up */
     last_up = time(NULL)-10;
     /* init rrd clean */
@@ -34,7 +39,8 @@ rrd_create(int argc, char **argv)
 
     /* set some defaults */
     strcpy(rrd.stat_head->cookie,RRD_COOKIE);
 
     /* set some defaults */
     strcpy(rrd.stat_head->cookie,RRD_COOKIE);
-    strcpy(rrd.stat_head->version,RRD_VERSION);
+       /* assume the will be version 1 compatible */
+    strcpy(rrd.stat_head->version,"0001");
     rrd.stat_head->float_cookie = FLOAT_COOKIE;
     rrd.stat_head->ds_cnt = 0; /* this will be adjusted later */
     rrd.stat_head->rra_cnt = 0; /* ditto */
     rrd.stat_head->float_cookie = FLOAT_COOKIE;
     rrd.stat_head->ds_cnt = 0; /* this will be adjusted later */
     rrd.stat_head->rra_cnt = 0; /* ditto */
@@ -103,7 +109,12 @@ rrd_create(int argc, char **argv)
        }
     }
     rrd.live_head->last_up = last_up;
        }
     }
     rrd.live_head->last_up = last_up;
-
+       
+       /* optind points to the first non-option command line arg,
+        * in this case, the file name. */
+       /* Compute the FNV hash value (used by SEASONAL and DEVSEASONAL
+        * arrays. */
+       hashed_name = FnvHash(argv[optind]);
     for(i=optind+1;i<argc;i++){
        char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];  
        int ii;
     for(i=optind+1;i<argc;i++){
        char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];  
        int ii;
@@ -162,36 +173,217 @@ rrd_create(int argc, char **argv)
        } else if (strncmp(argv[i],"RRA:",3)==0){
            size_t old_size = sizeof(rra_def_t)*(rrd.stat_head->rra_cnt);
            if((rrd.rra_def = rrd_realloc(rrd.rra_def,
        } else if (strncmp(argv[i],"RRA:",3)==0){
            size_t old_size = sizeof(rra_def_t)*(rrd.stat_head->rra_cnt);
            if((rrd.rra_def = rrd_realloc(rrd.rra_def,
-                                     old_size+sizeof(rra_def_t)))==NULL){
-               rrd_set_error("allocating rrd.rra_def");
-               rrd_free(&rrd);
-               return(-1);     
+                                     old_size+sizeof(rra_def_t)))==NULL)
+           {
+                  rrd_set_error("allocating rrd.rra_def");
+                  rrd_free(&rrd);
+                  return(-1);  
            }
            memset(&rrd.rra_def[rrd.stat_head->rra_cnt], 0, sizeof(rra_def_t));
            }
            memset(&rrd.rra_def[rrd.stat_head->rra_cnt], 0, sizeof(rra_def_t));
-           if (sscanf(&argv[i][4],
-                      CF_NAM_FMT ":%lf:%lu:%lu",
-                      rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam,
-                      &rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val,
-                      &rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt,
-                      &rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt) == 4){
-               if(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) == -1){
-                   rrd_free(&rrd);
-                   return (-1);
+
+           token = strtok(&argv[i][4],":");
+           token_idx = error_flag = 0;
+           while (token != NULL)
+           {
+             switch(token_idx)
+             {
+             case 0:
+               if (sscanf(token,CF_NAM_FMT,
+                          rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) != 1)
+                 rrd_set_error("Failed to parse CF name");
+               switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+               {
+               case CF_HWPREDICT:
+                 /* initialize some parameters */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].u_val = 0.1;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].u_val = 1.0/288;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt = 
+                   rrd.stat_head -> rra_cnt;
+                 /* need to mark the file version */
+          strcpy(rrd.stat_head->version,RRD_VERSION);
+                 break;
+               case CF_DEVSEASONAL:
+               case CF_SEASONAL:
+                 /* initialize some parameters */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_gamma].u_val = 0.1;
+                 /* fall through */
+               case CF_DEVPREDICT:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt = -1;
+                 break;
+               case CF_FAILURES:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_delta_pos].u_val = 2.0;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_delta_neg].u_val = 2.0;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt = 3;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt = 2;
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt = -1;
+                 break;
+               /* invalid consolidation function */
+               case -1:
+                 rrd_set_error("Unrecognized consolidation function %s",
+                        rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+               default:
+                 break;
                }
                }
-               if (rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val<0.0 ||
-                   rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val>=1.0) {
-                   rrd_set_error("the xff must always be >= 0 and < 1");
-                   rrd_free(&rrd);
-                   return (-1);
+               /* default: 1 pdp per cdp */ 
+               rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt = 1;
+               break;
+             case 1:
+               switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+               {
+               case CF_HWPREDICT:
+               case CF_DEVSEASONAL:
+               case CF_SEASONAL:
+               case CF_DEVPREDICT:
+               case CF_FAILURES:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+                 break;
+               default:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val = atof(token);
+                 if (rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val<0.0 ||
+                     rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val>=1.0)
+                   rrd_set_error("Invalid xff: must be between 0 and 1");
+                 break;
                }
                }
-               rrd.stat_head->rra_cnt++;                       
-           }
-           else {  
-               rrd_set_error("can't parse argument '%s'",argv[i]);
+               break;
+             case 2:
+               switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+               {
+               case CF_HWPREDICT:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].u_val = atof(token);
+                 if (atof(token) <= 0.0 || atof(token) >= 1.0)
+                   rrd_set_error("Invalid alpha: must be between 0 and 1");
+                 break;
+               case CF_DEVSEASONAL:
+               case CF_SEASONAL:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_gamma].u_val = 
+                       atof(token);
+                 if (atof(token) <= 0.0 || atof(token) >= 1.0)
+                   rrd_set_error("Invalid gamma: must be between 0 and 1");
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_smooth_idx].u_cnt
+                       = hashed_name % rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt; 
+                 break;
+           case CF_FAILURES:
+                 /* specifies the # of violations that constitutes the failure threshold */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt =
+                       atoi(token);
+                 if (atoi(token) < 1 || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+                       rrd_set_error("Failure threshold is out of range %d, %d",1,
+                         MAX_FAILURES_WINDOW_LEN);
+                 break;
+               case CF_DEVPREDICT:
+                 /* specifies the index (1-based) of CF_DEVSEASONAL array
+                  * associated with this CF_DEVPREDICT array. */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+                   atoi(token) - 1;
+                 break;
+               default:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt = atoi(token);
+                 break;
+               }
+               break;
+             case 3:
+               switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+               {
+               case CF_HWPREDICT:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].u_val = atof(token);
+                 if (atof(token) < 0.0 || atof(token) > 1.0)
+                   rrd_set_error("Invalid beta: must be between 0 and 1");
+                 break;
+               case CF_DEVSEASONAL:
+               case CF_SEASONAL:
+                 /* specifies the index (1-based) of CF_HWPREDICT array
+                  * associated with this CF_DEVSEASONAL or CF_SEASONAL array. 
+                  * */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+                   atoi(token) - 1;
+                 break;
+               case CF_FAILURES:
+                 /* specifies the window length */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt =
+                       atoi(token);
+                 if (atoi(token) < 1 || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+                       rrd_set_error("Window length is out of range %d, %d",1,
+                          MAX_FAILURES_WINDOW_LEN);
+                 /* verify that window length exceeds the failure threshold */
+                 if (rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt <
+                         rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt)
+                   rrd_set_error("Window length is shorter than the failure threshold");
+                 break;
+               case CF_DEVPREDICT:
+                 /* shouldn't be any more arguments */
+                 rrd_set_error("Unexpected extra argument for consolidation function DEVPREDICT");
+                 break;
+               default:
+                 rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+                 break;
+               }
+               break;
+             case 4:
+               switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+               {
+               case CF_FAILURES:
+                 /* specifies the index (1-based) of CF_DEVSEASONAL array
+                  * associated with this CF_DEVFAILURES array. */
+                 rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+                   atoi(token) - 1;
+                 break;
+               case CF_HWPREDICT:
+                 /* length of the associated CF_SEASONAL and CF_DEVSEASONAL arrays. */
+                 period = atoi(token);
+          if (period > rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt)
+                   rrd_set_error("Length of seasonal cycle exceeds length of HW prediction array");
+                 break;
+               default:
+                 /* shouldn't be any more arguments */
+                 rrd_set_error("Unexpected extra argument for consolidation function %s",
+                               rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+                  break;
+               }
+               break;
+             case 5:
+               /* If we are here, this must be a CF_HWPREDICT RRA.
+                * Specifies the index (1-based) of CF_SEASONAL array
+                * associated with this CF_HWPREDICT array. If this argument 
+                * is missing, then the CF_SEASONAL, CF_DEVSEASONAL, CF_DEVPREDICT,
+                * CF_FAILURES.
+                * arrays are created automatically. */
+               rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+                 atoi(token) - 1;
+               break;
+             default:
+               /* should never get here */
+               rrd_set_error("Unknown error");
+               break;
+             } /* end switch */
+             if (rrd_test_error())
+             {
+               /* all errors are unrecoverable */
                rrd_free(&rrd);
                rrd_free(&rrd);
-               return (-1);            
+               return (-1);
+             }
+             token = strtok(NULL,":");
+             token_idx++;
+           } /* end while */
+#ifdef DEBUG
+           fprintf(stderr,"Creating RRA CF: %s, dep idx %lu, current idx %lu\n",
+                   rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam,
+                   rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt, 
+                   rrd.stat_head -> rra_cnt);
+#endif
+           /* should we create CF_SEASONAL, CF_DEVSEASONAL, and CF_DEVPREDICT? */
+           if (cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) == CF_HWPREDICT
+               && rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt 
+               == rrd.stat_head -> rra_cnt)
+           {
+#ifdef DEBUG
+             fprintf(stderr,"Creating HW contingent RRAs\n");
+#endif
+             if (create_hw_contingent_rras(&rrd,period,hashed_name) == -1) {
+               rrd_free(&rrd);
+               return -1;
+             }
            }
            }
-
+           rrd.stat_head->rra_cnt++;                   
        } else {
            rrd_set_error("can't parse argument '%s'",argv[i]);
            rrd_free(&rrd);
        } else {
            rrd_set_error("can't parse argument '%s'",argv[i]);
            rrd_free(&rrd);
@@ -214,6 +406,75 @@ rrd_create(int argc, char **argv)
     return rrd_create_fn(argv[optind],&rrd);
 }
 
     return rrd_create_fn(argv[optind],&rrd);
 }
 
+/* Create the CF_DEVPREDICT, CF_DEVSEASONAL, CF_SEASONAL, and CF_FAILURES RRAs
+ * associated with a CF_HWPREDICT RRA. */
+int
+create_hw_contingent_rras(rrd_t *rrd, unsigned short period, unsigned long hashed_name)
+{
+   size_t old_size;
+   rra_def_t* current_rra;
+   
+   /* save index to CF_HWPREDICT */
+   unsigned long hw_index = rrd -> stat_head -> rra_cnt;
+   /* advance the pointer */
+   (rrd -> stat_head -> rra_cnt)++;                    
+   /* allocate the memory for the 4 contingent RRAs */
+   old_size = sizeof(rra_def_t)*(rrd -> stat_head->rra_cnt);
+   if ((rrd -> rra_def = rrd_realloc(rrd -> rra_def,
+       old_size+4*sizeof(rra_def_t)))==NULL)
+   {
+     rrd_set_error("allocating rrd.rra_def");
+     return(-1);       
+   }
+   /* clear memory */
+   memset(&(rrd -> rra_def[rrd -> stat_head->rra_cnt]), 0, 4*sizeof(rra_def_t));
+
+   /* create the CF_SEASONAL RRA */
+   current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+   strcpy(current_rra -> cf_nam,"SEASONAL");
+   current_rra -> row_cnt = period;
+   current_rra -> par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+   current_rra -> pdp_cnt = 1;
+   current_rra -> par[RRA_seasonal_gamma].u_val = 
+     rrd -> rra_def[hw_index].par[RRA_hw_alpha].u_val;
+   current_rra -> par[RRA_dependent_rra_idx].u_cnt = hw_index; 
+   rrd -> rra_def[hw_index].par[RRA_dependent_rra_idx].u_cnt = rrd -> stat_head -> rra_cnt;
+
+   /* create the CF_DEVSEASONAL RRA */
+   (rrd -> stat_head -> rra_cnt)++; 
+   current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+   strcpy(current_rra -> cf_nam,"DEVSEASONAL");
+   current_rra -> row_cnt = period;
+   current_rra -> par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+   current_rra -> pdp_cnt = 1;
+   current_rra -> par[RRA_seasonal_gamma].u_val = 
+     rrd -> rra_def[hw_index].par[RRA_hw_alpha].u_val;
+   current_rra -> par[RRA_dependent_rra_idx].u_cnt = hw_index; 
+   
+   /* create the CF_DEVPREDICT RRA */
+   (rrd -> stat_head -> rra_cnt)++; 
+   current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+   strcpy(current_rra -> cf_nam,"DEVPREDICT");
+   current_rra -> row_cnt = (rrd -> rra_def[hw_index]).row_cnt;
+   current_rra -> pdp_cnt = 1;
+   current_rra -> par[RRA_dependent_rra_idx].u_cnt 
+     = hw_index + 2; /* DEVSEASONAL */
+
+   /* create the CF_FAILURES RRA */
+   (rrd -> stat_head -> rra_cnt)++; 
+   current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+   strcpy(current_rra -> cf_nam,"FAILURES");
+   current_rra -> row_cnt = period; 
+   current_rra -> pdp_cnt = 1;
+   current_rra -> par[RRA_delta_pos].u_val = 2.0;
+   current_rra -> par[RRA_delta_neg].u_val = 2.0;
+   current_rra -> par[RRA_failure_threshold].u_cnt = 7;
+   current_rra -> par[RRA_window_len].u_cnt = 9;
+   current_rra -> par[RRA_dependent_rra_idx].u_cnt = 
+        hw_index + 2; /* DEVSEASONAL */
+   return 0;
+}
+
 /* create and empty rrd file according to the specs given */
 
 int
 /* create and empty rrd file according to the specs given */
 
 int
@@ -266,21 +527,50 @@ rrd_create_fn(char *file_name, rrd_t *rrd)
        return(-1);
     }
 
        return(-1);
     }
 
-    /* can not be zero because we don't know nothing ... */
-    rrd->cdp_prep->scratch[CDP_val].u_val = DNAN;
-    for(i=0; i < rrd->stat_head->rra_cnt; i++) {
-
-       /* 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;  
 
 
-
-       for(ii=0; ii < rrd->stat_head->ds_cnt; ii++) {
-           fwrite( rrd->cdp_prep,sizeof(cdp_prep_t),1,rrd_file);
-       }
+    for(i=0; i < rrd->stat_head->rra_cnt; i++) {
+       switch (cf_conv(rrd->rra_def[i].cf_nam))
+          {
+                 case CF_HWPREDICT:
+             rrd->cdp_prep->scratch[CDP_hw_intercept].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_hw_last_intercept].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_hw_slope].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_hw_last_slope].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_null_count].u_cnt = 1; 
+             rrd->cdp_prep->scratch[CDP_last_null_count].u_cnt = 1;
+                        break;
+                 case CF_SEASONAL:
+                 case CF_DEVSEASONAL:
+             rrd->cdp_prep->scratch[CDP_hw_seasonal].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_hw_last_seasonal].u_val = DNAN;
+             rrd->cdp_prep->scratch[CDP_init_seasonal].u_cnt = 1; 
+                        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++) 
+          {
+             fwrite( rrd->cdp_prep,sizeof(cdp_prep_t),1,rrd_file);
+          }
     }
 
     /* now, we must make sure that the rest of the rrd
     }
 
     /* now, we must make sure that the rest of the rrd
@@ -292,13 +582,16 @@ rrd_create_fn(char *file_name, rrd_t *rrd)
        fclose(rrd_file);
        return(-1);
     }
        fclose(rrd_file);
        return(-1);
     }
-
-    rrd->rra_ptr->cur_row = 0;
-    for(i=0; i <rrd->stat_head->rra_cnt; i++)
-       fwrite( rrd->rra_ptr,
-               sizeof(rra_ptr_t), 1,rrd_file);
-
-
+       /* 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->rra_def[i].row_cnt - 1;
+          fwrite( rrd->rra_ptr, sizeof(rra_ptr_t),1,rrd_file);
+       }
 
     /* write the empty data area */
     for(i=0; 
 
     /* write the empty data area */
     for(i=0; 
index 95b5c52..a25fbe9 100644 (file)
@@ -5,8 +5,15 @@
  *****************************************************************************
  * $Id$
  * $Log$
  *****************************************************************************
  * $Id$
  * $Log$
- * Revision 1.1  2001/02/25 22:25:05  oetiker
- * Initial revision
+ * Revision 1.2  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
  *
  *****************************************************************************/
 
  *
  *****************************************************************************/
 
@@ -24,14 +31,15 @@ rrd_dump(int argc, char **argv)
     long         rra_base, rra_start, rra_next;
     FILE                  *in_file;
     rrd_t             rrd;
     long         rra_base, rra_start, rra_next;
     FILE                  *in_file;
     rrd_t             rrd;
-
+    unival value;
 
     if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
        return(-1);
     }
 
     if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
        return(-1);
     }
+
     puts("<!-- Round Robin Database Dump -->");
     puts("<rrd>");
     puts("<!-- Round Robin Database Dump -->");
     puts("<rrd>");
-    printf("\t<version> %s </version>\n",rrd.stat_head->version);
+    printf("\t<version> %s </version>\n",RRD_VERSION);
     printf("\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
 #if HAVE_STRFTIME
     strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
     printf("\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
 #if HAVE_STRFTIME
     strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
@@ -83,22 +91,56 @@ rrd_dump(int argc, char **argv)
                       * sizeof(rrd_value_t));
        printf("\t<rra>\n");
        printf("\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
                       * sizeof(rrd_value_t));
        printf("\t<rra>\n");
        printf("\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
-       printf("\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n",
+       printf("\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n\n",
               rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt
               *rrd.stat_head->pdp_step);
               rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt
               *rrd.stat_head->pdp_step);
-       printf("\t\t<xff> %0.10e </xff>\n\n",rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+       /* added support for RRA parameters */
+       printf("\t\t<params>");
+       for (ii = 0; ii < MAX_RRA_PAR_EN; ii++)
+       {
+          value = rrd.rra_def[i].par[ii];
+          if (ii == RRA_dependent_rra_idx ||
+                  ii == RRA_seasonal_smooth_idx ||
+                  ii == RRA_failure_threshold)
+                 printf("<value> %lu </value>", value.u_cnt);
+          else {
+                 if (isnan(value.u_val)) {
+                     printf("<value> NaN </value>");
+                 } else {
+                     printf("<value> %0.10e </value>", value.u_val);
+                 }
+          }
+       }
+       printf("\t\t</params>\n");
        printf("\t\t<cdp_prep>\n");
        for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
        printf("\t\t<cdp_prep>\n");
        for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
-           double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
-           printf("\t\t\t<ds>");       
-           if (isnan(value)){
-             printf("<value> NaN </value>");
-           } else {
-             printf("<value> %0.10e </value>", value);
-           }
-           printf("  <unknown_datapoints> %lu </unknown_datapoints>",
-                   rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
-          printf("</ds>\n");    
+#if 0
+               double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
+#endif 
+               printf("\t\t\t<ds>");
+               /* added support for exporting all CDP parameters */
+               for (iii=0; iii < MAX_CDP_PAR_EN ; iii++)
+               {
+                  value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt + ii].scratch[iii];
+           /* handle integer values as a special case */
+                  if (cf_conv(rrd.rra_def[i].cf_nam) == CF_FAILURES ||
+                          iii == CDP_unkn_pdp_cnt || 
+                      iii == CDP_null_count ||
+                      iii == CDP_last_null_count)
+                    printf("<value> %lu </value>", value.u_cnt);
+                  else {       
+                    if (isnan(value.u_val)) {
+                      printf("<value> NaN </value>");
+                    } else {
+                      printf("<value> %0.10e </value>", value.u_val);
+                    }
+                  }
+               }
+#if 0 
+               printf("  <unknown_datapoints> %lu </unknown_datapoints>",
+                      rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+#endif
+        printf("</ds>\n");      
         }
        printf("\t\t</cdp_prep>\n");
 
         }
        printf("\t\t</cdp_prep>\n");
 
index dde8bb9..3528362 100644 (file)
@@ -19,7 +19,7 @@
  *****************************************************************************/
 
 #define RRD_COOKIE    "RRD"
  *****************************************************************************/
 
 #define RRD_COOKIE    "RRD"
-#define RRD_VERSION   "0001"
+#define RRD_VERSION   "0002"
 #define FLOAT_COOKIE  8.642135E130
 
 #if defined(WIN32)
 #define FLOAT_COOKIE  8.642135E130
 
 #if defined(WIN32)
@@ -143,7 +143,7 @@ enum ds_param_en {   DS_mrhb_cnt=0,       /* minimum required heartbeat. A
                                           * least every ds_mrhb seconds,
                                           * otherwise it is regarded dead and
                                           * will be set to UNKNOWN */             
                                           * least every ds_mrhb seconds,
                                           * otherwise it is regarded dead and
                                           * will be set to UNKNOWN */             
-                    DS_min_val,          /* the processed input of a ds must */
+                            DS_min_val,          /* the processed input of a ds must */
                      DS_max_val };        /* be between max_val and min_val
                                           * both can be set to UNKNOWN if you
                                           * do not care. Data outside the limits
                      DS_max_val };        /* be between max_val and min_val
                                           * both can be set to UNKNOWN if you
                                           * do not care. Data outside the limits
@@ -168,11 +168,67 @@ typedef struct ds_def_t {
 enum cf_en           { CF_AVERAGE=0,     /* data consolidation functions */ 
                        CF_MINIMUM, 
                        CF_MAXIMUM,
 enum cf_en           { CF_AVERAGE=0,     /* data consolidation functions */ 
                        CF_MINIMUM, 
                        CF_MAXIMUM,
-                       CF_LAST};
-
-enum rra_par_en {   RRA_cdp_xff_val=0};   /* what part of the consolidated 
-                                           datapoint may be unknown, while 
-                                           still a valid entry in goes into the rra */
+                       CF_LAST,
+                                          CF_HWPREDICT, 
+                                          /* An array of predictions using the seasonal 
+                                               * Holt-Winters algorithm. Requires an RRA of type
+                                               * CF_SEASONAL for this data source. */
+                                          CF_SEASONAL,
+                                          /* An array of seasonal effects. Requires an RRA of
+                                               * type CF_HWPREDICT for this data source. */
+                                          CF_DEVPREDICT,
+                                          /* An array of deviation predictions based upon
+                                               * smoothed seasonal deviations. Requires an RRA of
+                                               * type CF_DEVSEASONAL for this data source. */
+                                          CF_DEVSEASONAL,
+                                          /* An array of smoothed seasonal deviations. Requires
+                                               * an RRA of type CF_HWPREDICT for this data source.
+                                               * */
+                                          CF_FAILURES};
+                                          /* A binary array of failure indicators: 1 indicates
+                                               * that the number of violations in the prescribed
+                                               * window exceeded the prescribed threshold. */
+
+#define MAX_RRA_PAR_EN 10
+enum rra_par_en {   RRA_cdp_xff_val=0,  /* what part of the consolidated
+                     * datapoint must be known, to produce a
+                                        * valid entry in the rra */
+                                       RRA_hw_alpha,
+                                       /* exponential smoothing parameter for the intercept in
+                                        * the Holt-Winters prediction algorithm. */
+                                       RRA_hw_beta,
+                                       /* exponential smoothing parameter for the slope in
+                                        * the Holt-Winters prediction algorithm. */
+                                       RRA_dependent_rra_idx,
+                                       /* For CF_HWPREDICT: index of the RRA with the seasonal 
+                                        * effects of the Holt-Winters algorithm (of type
+                                        * CF_SEASONAL).
+                                        * For CF_DEVPREDICT: index of the RRA with the seasonal
+                                        * deviation predictions (of type CF_DEVSEASONAL).
+                                        * For CF_SEASONAL: index of the RRA with the Holt-Winters
+                                        * intercept and slope coefficient (of type CF_HWPREDICT).
+                                        * For CF_DEVSEASONAL: index of the RRA with the 
+                                        * Holt-Winters prediction (of type CF_HWPREDICT).
+                                        * For CF_FAILURES: index of the CF_DEVSEASONAL array.
+                                        * */
+                                       RRA_seasonal_smooth_idx,
+                                       /* For CF_SEASONAL and CF_DEVSEASONAL:
+                                        * an integer between 0 and row_count - 1 which
+                                        * is index in the seasonal cycle for applying
+                                        * the period smoother. */
+                                   RRA_failure_threshold,
+                                       /* For CF_FAILURES, number of violations within the last
+                                        * window required to mark a failure. */
+                    RRA_seasonal_gamma = RRA_hw_alpha,
+                                       /* exponential smoothing parameter for seasonal effects.
+                                        * */
+                    RRA_delta_pos = RRA_hw_alpha,
+                    RRA_delta_neg = RRA_hw_beta,
+                                       /* confidence bound scaling parameters for the
+                                        * the FAILURES RRA. */
+                    RRA_window_len = RRA_seasonal_smooth_idx};
+                                       /* For CF_FAILURES, the length of the window for measuring
+                                        * failures. */
                        
 #define CF_NAM_FMT    "%19[A-Z]"
 #define CF_NAM_SIZE   20
                        
 #define CF_NAM_FMT    "%19[A-Z]"
 #define CF_NAM_SIZE   20
@@ -183,7 +239,7 @@ typedef struct rra_def_t {
     unsigned long    pdp_cnt;            /* how many primary data points are
                                          * required for a consolidated data
                                          * point?*/
     unsigned long    pdp_cnt;            /* how many primary data points are
                                          * required for a consolidated data
                                          * point?*/
-    unival           par[10];            /* index see rra_param_en */
+    unival           par[MAX_RRA_PAR_EN];            /* index see rra_param_en */
 
 } rra_def_t;
 
 
 } rra_def_t;
 
@@ -239,15 +295,56 @@ typedef struct pdp_prep_t{
 /****************************************************************************
  * POS 6: cdp_prep_t (* rra_cnt * ds_cnt )      data prep area for cdp values
  ****************************************************************************/
 /****************************************************************************
  * POS 6: cdp_prep_t (* rra_cnt * ds_cnt )      data prep area for cdp values
  ****************************************************************************/
-enum cdp_par_en {  CDP_val=0,          /* the base_interval is always an
-                                         * average */
-                  CDP_unkn_pdp_cnt };       /* how many unknown pdp were
-                                                 * integrated. This and the cdp_xff
-                                           will decide if this is going to
-                                           be a UNKNOWN or a valid value */
+#define MAX_CDP_PAR_EN 10
+#define MAX_CDP_FAILURES_IDX 8 
+/* max CDP scratch entries avail to record violations for a FAILURES RRA */
+#define MAX_FAILURES_WINDOW_LEN 28
+enum cdp_par_en {  CDP_val=0,          
+                   /* the base_interval is always an
+                                       * average */
+                          CDP_unkn_pdp_cnt,       
+                                  /* how many unknown pdp were
+                           * integrated. This and the cdp_xff
+                                       * will decide if this is going to
+                                       * be a UNKNOWN or a valid value */
+                                  CDP_hw_intercept,
+                                  /* Current intercept coefficient for the Holt-Winters
+                                       * prediction algorithm. */
+                                  CDP_hw_last_intercept,
+                                  /* Last iteration intercept coefficient for the Holt-Winters
+                                       * prediction algorihtm. */
+                                  CDP_hw_slope,
+                                  /* Current slope coefficient for the Holt-Winters
+                                       * prediction algorithm. */
+                                  CDP_hw_last_slope,
+                                  /* Last iteration slope coeffient. */
+                                  CDP_null_count,
+                                  /* Number of sequential Unknown (DNAN) values + 1 preceding
+                                   * the current prediction.
+                                       * */
+                                  CDP_last_null_count,
+                                  /* Last iteration count of Unknown (DNAN) values. */
+                                  CDP_primary_val = 8,
+                                  /* optimization for bulk updates: the value of the first CDP
+                                       * value to be written in the bulk update. */
+                                  CDP_secondary_val = 9,
+                                  /* optimization for bulk updates: the value of subsequent
+                                       * CDP values to be written in the bulk update. */
+                   CDP_hw_seasonal = CDP_hw_intercept,
+                   /* Current seasonal coefficient for the Holt-Winters
+                    * prediction algorithm. This is stored in CDP prep to avoid
+                    * redundant seek operations. */
+                   CDP_hw_last_seasonal = CDP_hw_last_intercept,
+                   /* Last iteration seasonal coeffient. */
+                   CDP_seasonal_deviation = CDP_hw_intercept,
+                   CDP_last_seasonal_deviation = CDP_hw_last_intercept,
+                   CDP_init_seasonal = CDP_null_count};
+                   /* init_seasonal is a flag which when > 0, forces smoothing updates
+                    * to occur when rra_ptr.cur_row == 0 */
 
 typedef struct cdp_prep_t{
 
 typedef struct cdp_prep_t{
-    unival         scratch[10];          /* contents according to cdp_par_en *
+    unival         scratch[MAX_CDP_PAR_EN];          
+                                                                                /* contents according to cdp_par_en *
                                           * init state should be NAN */
 
 } cdp_prep_t;
                                           * init state should be NAN */
 
 } cdp_prep_t;
@@ -283,7 +380,7 @@ typedef struct rrd_t {
  * Consolidated Data Points organized in Round Robin Archives.
  ****************************************************************************
  ****************************************************************************
  * Consolidated Data Points organized in Round Robin Archives.
  ****************************************************************************
  ****************************************************************************
+
  *RRA 0
  (0,0) .................... ( ds_cnt -1 , 0)
  .
  *RRA 0
  (0,0) .................... ( ds_cnt -1 , 0)
  .
index 136d3df..f42acaf 100644 (file)
@@ -35,7 +35,7 @@ enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
 
 
 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
 
 
 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
-           GF_LINE2,GF_LINE3,GF_AREA,GF_STACK, GF_DEF, GF_CDEF };
+           GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,GF_DEF, GF_CDEF};
 
 enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
            OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD,
 
 enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
            OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD,
@@ -346,6 +346,7 @@ enum gf_en gf_conv(char *string){
     conv_if(LINE3,GF_LINE3)
     conv_if(AREA,GF_AREA)
     conv_if(STACK,GF_STACK)
     conv_if(LINE3,GF_LINE3)
     conv_if(AREA,GF_AREA)
     conv_if(STACK,GF_STACK)
+       conv_if(TICK,GF_TICK)
     conv_if(DEF,GF_DEF)
     conv_if(CDEF,GF_CDEF)
     
     conv_if(DEF,GF_DEF)
     conv_if(CDEF,GF_CDEF)
     
@@ -687,12 +688,18 @@ reduce_data(
                if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
                else {
                    switch (cf) {
                if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
                else {
                    switch (cf) {
+                        case CF_HWPREDICT:
+                        case CF_DEVSEASONAL:
+                        case CF_DEVPREDICT:
+                        case CF_SEASONAL:
                        case CF_AVERAGE:
                            newval += srcptr[i*(*ds_cnt)+col];
                            break;
                        case CF_MINIMUM:
                            newval = min (newval,srcptr[i*(*ds_cnt)+col]);
                            break;
                        case CF_AVERAGE:
                            newval += srcptr[i*(*ds_cnt)+col];
                            break;
                        case CF_MINIMUM:
                            newval = min (newval,srcptr[i*(*ds_cnt)+col]);
                            break;
+                        case CF_FAILURES: 
+                        /* an interval contains a failure if any subintervals contained a failure */
                        case CF_MAXIMUM:
                            newval = max (newval,srcptr[i*(*ds_cnt)+col]);
                            break;
                        case CF_MAXIMUM:
                            newval = max (newval,srcptr[i*(*ds_cnt)+col]);
                            break;
@@ -704,11 +711,16 @@ reduce_data(
            }
            if (validval == 0){newval = DNAN;} else{
                switch (cf) {
            }
            if (validval == 0){newval = DNAN;} else{
                switch (cf) {
-                   case CF_AVERAGE:            
-                       newval /= validval;
+                    case CF_HWPREDICT:
+                    case CF_DEVSEASONAL:
+                    case CF_DEVPREDICT:
+                    case CF_SEASONAL:
+                    case CF_AVERAGE:                
+                        newval /= validval;
                        break;
                    case CF_MINIMUM:
                        break;
                    case CF_MINIMUM:
-                   case CF_MAXIMUM:
+                    case CF_FAILURES:
+                   case CF_MAXIMUM:
                    case CF_LAST:
                        break;
                }
                    case CF_LAST:
                        break;
                }
@@ -1389,6 +1401,7 @@ data_proc( image_desc_t *im ){
         (im->gdes[i].gf==GF_LINE2) ||
         (im->gdes[i].gf==GF_LINE3) ||
         (im->gdes[i].gf==GF_AREA) ||
         (im->gdes[i].gf==GF_LINE2) ||
         (im->gdes[i].gf==GF_LINE3) ||
         (im->gdes[i].gf==GF_AREA) ||
+        (im->gdes[i].gf==GF_TICK) ||
         (im->gdes[i].gf==GF_STACK)){
        if((im->gdes[i].p_data = malloc((im->xsize +1)
                                        * sizeof(rrd_value_t)))==NULL){
         (im->gdes[i].gf==GF_STACK)){
        if((im->gdes[i].p_data = malloc((im->xsize +1)
                                        * sizeof(rrd_value_t)))==NULL){
@@ -1411,6 +1424,7 @@ data_proc( image_desc_t *im ){
            case GF_LINE2:
            case GF_LINE3:
            case GF_AREA:
            case GF_LINE2:
            case GF_LINE3:
            case GF_AREA:
+               case GF_TICK:
                paintval = 0.0;
            case GF_STACK:
                vidx = im->gdes[ii].vidx;
                paintval = 0.0;
            case GF_STACK:
                vidx = im->gdes[ii].vidx;
@@ -1430,7 +1444,8 @@ data_proc( image_desc_t *im ){
                if (! isnan(value)) {
                  paintval += value;
                  im->gdes[ii].p_data[i] = paintval;
                if (! isnan(value)) {
                  paintval += value;
                  im->gdes[ii].p_data[i] = paintval;
-                 if (finite(paintval)){
+                 /* GF_TICK: the data values are not relevant for min and max */
+                 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
                   if (isnan(minval) || paintval <  minval)
                     minval = paintval;
                   if (isnan(maxval) || paintval >  maxval)
                   if (isnan(minval) || paintval <  minval)
                     minval = paintval;
                   if (isnan(maxval) || paintval >  maxval)
@@ -1669,6 +1684,10 @@ print_calc(image_desc_t *im, char ***prdata)
                }
 
                switch (im->gdes[i].cf){
                }
 
                switch (im->gdes[i].cf){
+               case CF_HWPREDICT:
+               case CF_DEVPREDICT:
+               case CF_DEVSEASONAL:
+               case CF_SEASONAL:
                case CF_AVERAGE:
                    validsteps++;
                    printval += im->gdes[vidx].data[ii];
                case CF_AVERAGE:
                    validsteps++;
                    printval += im->gdes[vidx].data[ii];
@@ -1676,6 +1695,7 @@ print_calc(image_desc_t *im, char ***prdata)
                case CF_MINIMUM:
                    printval = min( printval, im->gdes[vidx].data[ii]);
                    break;
                case CF_MINIMUM:
                    printval = min( printval, im->gdes[vidx].data[ii]);
                    break;
+           case CF_FAILURES:
                case CF_MAXIMUM:
                    printval = max( printval, im->gdes[vidx].data[ii]);
                    break;
                case CF_MAXIMUM:
                    printval = max( printval, im->gdes[vidx].data[ii]);
                    break;
@@ -1683,7 +1703,7 @@ print_calc(image_desc_t *im, char ***prdata)
                    printval = im->gdes[vidx].data[ii];
                }
            }
                    printval = im->gdes[vidx].data[ii];
                }
            }
-           if (im->gdes[i].cf ==  CF_AVERAGE) {
+           if (im->gdes[i].cf ==  CF_AVERAGE || im -> gdes[i].cf > CF_LAST) {
                if (validsteps > 1) {
                    printval = (printval / validsteps);
                }
                if (validsteps > 1) {
                    printval = (printval / validsteps);
                }
@@ -1738,6 +1758,7 @@ print_calc(image_desc_t *im, char ***prdata)
        case GF_LINE2:
        case GF_LINE3:
        case GF_AREA:
        case GF_LINE2:
        case GF_LINE3:
        case GF_AREA:
+       case GF_TICK:
        case GF_STACK:
        case GF_HRULE:
        case GF_VRULE:
        case GF_STACK:
        case GF_HRULE:
        case GF_VRULE:
@@ -2423,7 +2444,7 @@ MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
 
 int lazy_check(image_desc_t *im){
     FILE *fd = NULL;
 
 int lazy_check(image_desc_t *im){
     FILE *fd = NULL;
-    int size = 1;
+       int size = 1;
     struct stat  gifstat;
     
     if (im->lazy == 0) return 0; /* no lazy option */
     struct stat  gifstat;
     
     if (im->lazy == 0) return 0; /* no lazy option */
@@ -2438,11 +2459,11 @@ int lazy_check(image_desc_t *im){
       return 0; /* the file does not exist */
     switch (im->imgformat) {
     case IF_GIF:
       return 0; /* the file does not exist */
     switch (im->imgformat) {
     case IF_GIF:
-       size = GifSize(fd,&(im->xgif),&(im->ygif));
-       break;
+          size = GifSize(fd,&(im->xgif),&(im->ygif));
+          break;
     case IF_PNG:
     case IF_PNG:
-       size = PngSize(fd,&(im->xgif),&(im->ygif));
-       break;
+          size = PngSize(fd,&(im->xgif),&(im->ygif));
+          break;
     }
     fclose(fd);
     return size;
     }
     fclose(fd);
     return size;
@@ -2592,7 +2613,22 @@ graph_paint(image_desc_t *im, char ***calcpr)
        case GF_COMMENT:
        case GF_HRULE:
        case GF_VRULE:
        case GF_COMMENT:
        case GF_HRULE:
        case GF_VRULE:
-         break;
+               break;
+       case GF_TICK:
+               for (ii = 0; ii < im->xsize; ii++)
+               {
+                  if (!isnan(im->gdes[i].p_data[ii]) && 
+                          im->gdes[i].p_data[ii] > 0.0)
+                  { 
+                         /* generate a tick */
+                         gdImageLine(gif, im -> xorigin + ii, 
+                                im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
+                                im -> xorigin + ii, 
+                                im -> yorigin,
+                                im -> gdes[i].col.i);
+                  }
+               }
+               break;
        case GF_LINE1:
        case GF_LINE2:
        case GF_LINE3:
        case GF_LINE1:
        case GF_LINE2:
        case GF_LINE3:
@@ -3181,6 +3217,48 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
                return -1;
            }
            break;
                return -1;
            }
            break;
+       case GF_TICK:
+           if((scancount=sscanf(
+               &argv[i][argstart],
+               "%29[^:#]#%2x%2x%2x:%lf:%n",
+               varname,
+               &col_red,
+               &col_green,
+               &col_blue,
+               &(im.gdes[im.gdes_c-1].yrule),
+               &strstart))>=1)
+               {
+               im.gdes[im.gdes_c-1].col.red = col_red;
+               im.gdes[im.gdes_c-1].col.green = col_green;
+               im.gdes[im.gdes_c-1].col.blue = col_blue;
+               if(strstart <= 0){
+                   im.gdes[im.gdes_c-1].legend[0] = '\0';
+               } else { 
+                   scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+               }
+               if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
+                   im_free(&im);
+                   rrd_set_error("unknown variable '%s'",varname);
+                   return -1;
+               }
+               if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
+               {
+                   im_free(&im);
+                   rrd_set_error("Tick mark scaling factor out of range");
+                   return -1;
+               }
+               if (scancount < 4)
+                  im.gdes[im.gdes_c-1].col.red = -1;           
+           if (scancount < 5) 
+                  /* default tick marks: 10% of the y-axis */
+                  im.gdes[im.gdes_c-1].yrule = 0.1;
+
+               } else {
+                  im_free(&im);
+                  rrd_set_error("can't parse '%s'",&argv[i][argstart]);
+                  return -1;
+               } /* endif sscanf */
+               break;
        case GF_STACK:
            if(linepass == 0){
                im_free(&im);
        case GF_STACK:
            if(linepass == 0){
                im_free(&im);
diff --git a/src/rrd_hw.c b/src/rrd_hw.c
new file mode 100644 (file)
index 0000000..85b4fd7
--- /dev/null
@@ -0,0 +1,714 @@
+/*****************************************************************************
+ * RRDtool 1.0.21  Copyright Tobias Oetiker, 1997 - 2000
+ *****************************************************************************
+ * rrd_hw.c
+ *****************************************************************************
+ * Initial version by Jake Brutlag, WebTV Networks, 5/1/00
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+/* #define DEBUG */
+
+unsigned long MyMod(signed long val, unsigned long mod);
+
+int update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                 unsigned long ds_idx, unsigned short CDP_scratch_idx);
+int update_seasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                                unsigned long ds_idx, unsigned short CDP_scratch_idx, 
+                                rrd_value_t *seasonal_coef);
+int update_devpredict(rrd_t *rrd, unsigned long cdp_idx, 
+                                 unsigned long rra_idx, unsigned long ds_idx, unsigned short CDP_scratch_idx);
+int update_devseasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                      unsigned long ds_idx, unsigned short CDP_scratch_idx, 
+                                  rrd_value_t *seasonal_dev);
+int update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                               unsigned long ds_idx, unsigned short CDP_scratch_idx);
+
+int
+update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                 unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+   rrd_value_t prediction, seasonal_coef;
+   unsigned long dependent_rra_idx, seasonal_cdp_idx;
+   unival *coefs = rrd -> cdp_prep[cdp_idx].scratch;
+   rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+
+   /* save coefficients from current prediction */
+   coefs[CDP_hw_last_intercept].u_val = coefs[CDP_hw_intercept].u_val;
+   coefs[CDP_hw_last_slope].u_val = coefs[CDP_hw_slope].u_val;
+   coefs[CDP_last_null_count].u_cnt = coefs[CDP_null_count].u_cnt;
+
+   /* retrieve the current seasonal coef */
+   dependent_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+   seasonal_cdp_idx = dependent_rra_idx*(rrd -> stat_head -> ds_cnt) + ds_idx;
+   if (dependent_rra_idx < rra_idx)
+         seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+   else
+         seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+   
+   /* compute the prediction */
+   if (isnan(coefs[CDP_hw_intercept].u_val) || isnan(coefs[CDP_hw_slope].u_val)
+      || isnan(seasonal_coef))
+   {
+     prediction = DNAN;
+      
+     /* bootstrap initialization of slope and intercept */
+     if (isnan(coefs[CDP_hw_intercept].u_val) &&
+        !isnan(coefs[CDP_scratch_idx].u_val)) 
+     {
+#ifdef DEBUG
+       fprintf(stderr,"Initialization of slope/intercept\n");
+#endif
+       coefs[CDP_hw_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+       coefs[CDP_hw_last_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+       /* initialize the slope to 0 */
+       coefs[CDP_hw_slope].u_val = 0.0;
+       coefs[CDP_hw_last_slope].u_val = 0.0;
+       /* initialize null count to 1 */
+       coefs[CDP_null_count].u_cnt = 1;
+       coefs[CDP_last_null_count].u_cnt = 1;
+     }
+     /* if seasonal coefficient is NA, then don't update intercept, slope */
+   } else {
+     prediction = coefs[CDP_hw_intercept].u_val + 
+       (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt)
+       + seasonal_coef;
+#ifdef DEBUG
+     fprintf(stderr,"computed prediction: %f\n",prediction);
+#endif
+     if (isnan(coefs[CDP_scratch_idx].u_val))
+     {
+       /* NA value, no updates of intercept, slope;
+           * increment the null count */
+       (coefs[CDP_null_count].u_cnt)++;
+     } else {
+#ifdef DEBUG
+       fprintf(stderr,"Updating intercept, slope\n");
+#endif
+       /* update the intercept */
+       coefs[CDP_hw_intercept].u_val = (current_rra -> par[RRA_hw_alpha].u_val)*
+               (coefs[CDP_scratch_idx].u_val - seasonal_coef) +
+               (1 - current_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+               + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+       /* update the slope */
+       coefs[CDP_hw_slope].u_val = (current_rra -> par[RRA_hw_beta].u_val)*
+               (coefs[CDP_hw_intercept].u_val - coefs[CDP_hw_last_intercept].u_val) +
+               (1 - current_rra -> par[RRA_hw_beta].u_val)*(coefs[CDP_hw_slope].u_val);
+       /* reset the null count */
+       coefs[CDP_null_count].u_cnt = 1;
+     }
+   }
+
+   /* store the prediction for writing */
+   coefs[CDP_scratch_idx].u_val = prediction;
+   return 0;
+}
+
+int
+lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+                               FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef)
+{
+   unsigned long pos_tmp;
+   /* rra_ptr[].cur_row points to the rra row to be written; this function
+       * reads cur_row + offset */
+   unsigned long row_idx = rrd -> rra_ptr[rra_idx].cur_row + offset;
+   /* handle wrap around */
+   if (row_idx >= rrd -> rra_def[rra_idx].row_cnt)
+     row_idx = row_idx % (rrd -> rra_def[rra_idx].row_cnt);
+
+   /* rra_start points to the appropriate rra block in the file */
+   /* compute the pointer to the appropriate location in the file */
+   pos_tmp = rra_start + (row_idx)*(rrd -> stat_head -> ds_cnt)*sizeof(rrd_value_t);
+
+   /* allocate memory if need be */
+   if (*seasonal_coef == NULL)
+         *seasonal_coef = 
+            (rrd_value_t *) malloc((rrd -> stat_head -> ds_cnt)*sizeof(rrd_value_t));
+   if (*seasonal_coef == NULL) {
+         rrd_set_error("memory allocation failure: seasonal coef");
+         return -1;
+   }
+
+   if (!fseek(rrd_file,pos_tmp,SEEK_SET))
+   {
+      if (fread(*seasonal_coef,sizeof(rrd_value_t),rrd->stat_head->ds_cnt,rrd_file)
+                 == rrd -> stat_head -> ds_cnt)
+         {
+                /* success! */
+         /* we can safely ignore the rule requiring a seek operation between read
+          * and write, because this read moves the file pointer to somewhere
+          * in the file other than the next write location.
+          * */
+                return 0;
+         } else {
+            rrd_set_error("read operation failed in lookup_seasonal(): %lu\n",pos_tmp);
+         }
+   } else {
+         rrd_set_error("seek operation failed in lookup_seasonal(): %lu\n",pos_tmp);
+   }
+   
+   return -1;
+}
+
+int
+update_seasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                                unsigned long ds_idx, unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef)
+{
+/* TODO: extract common if subblocks in the wake of I/O optimization */
+   rrd_value_t intercept, seasonal;
+   rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+   rra_def_t *hw_rra = &(rrd -> rra_def[current_rra -> par[RRA_dependent_rra_idx].u_cnt]);
+   /* obtain cdp_prep index for HWPREDICT */
+   unsigned long hw_cdp_idx = (current_rra -> par[RRA_dependent_rra_idx].u_cnt)
+      * (rrd -> stat_head -> ds_cnt) + ds_idx;
+   unival *coefs = rrd -> cdp_prep[hw_cdp_idx].scratch;
+
+   /* update seasonal coefficient in cdp prep areas */
+   seasonal = rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val;
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = seasonal;
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val =
+         seasonal_coef[ds_idx];
+
+   /* update seasonal value for disk */
+   if (current_rra -> par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+         /* associated HWPREDICT has already been updated */
+         /* check for possible NA values */
+      if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+         {
+                /* no update, store the old value unchanged,
+                 * doesn't matter if it is NA */
+            rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = seasonal;
+         } else if (isnan(coefs[CDP_hw_last_intercept].u_val) 
+                    || isnan(coefs[CDP_hw_last_slope].u_val))
+         {
+                /* this should never happen, as HWPREDICT was already updated */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val= DNAN;
+         } else if (isnan(seasonal))
+         {
+                /* initialization: intercept is not currently being updated */
+#ifdef DEBUG
+                fprintf(stderr,"Initialization of seasonal coef %lu\n",
+                       rrd -> rra_ptr[rra_idx].cur_row);
+#endif
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val 
+                       -= coefs[CDP_hw_last_intercept].u_val; 
+         } else {
+                intercept = coefs[CDP_hw_intercept].u_val;
+#ifdef DEBUG
+                fprintf(stderr,
+                       "Updating seasonal, params: gamma %f, new intercept %f, old seasonal %f\n",
+                       current_rra -> par[RRA_seasonal_gamma].u_val,
+                       intercept, seasonal);
+#endif
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                       (current_rra -> par[RRA_seasonal_gamma].u_val)*
+                       (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+                       (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*seasonal;
+         }
+   else {
+         /* SEASONAL array is updated first, which means the new intercept
+          * hasn't be computed; so we compute it here. */
+
+         /* check for possible NA values */
+      if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+         {
+                /* no update, simple store the old value unchanged,
+                 * doesn't matter if it is NA */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =  seasonal;
+         } else if (isnan(coefs[CDP_hw_intercept].u_val) 
+                    || isnan(coefs[CDP_hw_slope].u_val))
+         {
+                /* Initialization of slope and intercept will occur.
+                 * force seasonal coefficient to 0. */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val= 0.0;
+         } else if (isnan(seasonal))
+         {
+                /* initialization: intercept will not be updated
+                 * CDP_hw_intercept = CDP_hw_last_intercept; just need to 
+                 * subtract this baseline value. */
+#ifdef DEBUG
+                fprintf(stderr,"Initialization of seasonal coef %lu\n",
+                       rrd -> rra_ptr[rra_idx].cur_row);
+#endif
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val -= coefs[CDP_hw_intercept].u_val; 
+         } else {
+                /* Note that we must get CDP_scratch_idx from SEASONAL array, as CDP_scratch_idx
+                 * for HWPREDICT array will be DNAN. */
+            intercept = (hw_rra -> par[RRA_hw_alpha].u_val)*
+                   (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - seasonal)
+                   + (1 - hw_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+                   + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                  (current_rra -> par[RRA_seasonal_gamma].u_val)*
+                  (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+                  (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*seasonal;
+         }
+   }
+#ifdef DEBUG
+   fprintf(stderr,"seasonal coefficient set= %f\n",
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+#endif
+   return 0;
+}
+
+int
+update_devpredict(rrd_t *rrd, unsigned long cdp_idx, 
+                                 unsigned long rra_idx, unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+   /* there really isn't any "update" here; the only reason this information
+    * is stored separately from DEVSEASONAL is to preserve deviation predictions
+    * for a longer duration than one seasonal cycle. */
+   unsigned long seasonal_cdp_idx = (rrd -> rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+      * (rrd -> stat_head -> ds_cnt) + ds_idx;
+
+   if (rrd -> rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+   {
+         /* associated DEVSEASONAL array already updated */
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+                = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+   } else {
+         /* associated DEVSEASONAL not yet updated */
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+                = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+   }
+   return 0;
+}
+
+int
+update_devseasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                      unsigned long ds_idx, unsigned short CDP_scratch_idx, 
+                                  rrd_value_t *seasonal_dev)
+{
+   rrd_value_t prediction = 0, seasonal_coef = DNAN;
+   rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+   /* obtain cdp_prep index for HWPREDICT */
+   unsigned long hw_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+   unsigned long hw_cdp_idx = hw_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+   unsigned long seasonal_cdp_idx;
+   unival *coefs = rrd -> cdp_prep[hw_cdp_idx].scratch;
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val =
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+   /* retrieve the next seasonal deviation value, could be NA */
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val =
+         seasonal_dev[ds_idx];
+
+   /* retrieve the current seasonal_coef (not to be confused with the
+       * current seasonal deviation). Could make this more readable by introducing
+       * some wrapper functions. */
+   seasonal_cdp_idx = (rrd -> rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+         *(rrd -> stat_head -> ds_cnt) + ds_idx;
+   if (rrd -> rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+         /* SEASONAL array already updated */
+         seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+   else
+         /* SEASONAL array not yet updated */
+         seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+   
+   /* compute the abs value of the difference between the prediction and
+       * observed value */
+   if (hw_rra_idx < rra_idx)
+   {
+         /* associated HWPREDICT has already been updated */
+         if (isnan(coefs[CDP_hw_last_intercept].u_val) ||
+             isnan(coefs[CDP_hw_last_slope].u_val) ||
+             isnan(seasonal_coef))
+         {
+                /* one of the prediction values is uinitialized */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+                return 0;
+         } else {
+            prediction = coefs[CDP_hw_last_intercept].u_val + 
+                  (coefs[CDP_hw_last_slope].u_val)*(coefs[CDP_last_null_count].u_cnt)
+                  + seasonal_coef;
+         }
+   } else {
+         /* associated HWPREDICT has NOT been updated */
+         if (isnan(coefs[CDP_hw_intercept].u_val) ||
+             isnan(coefs[CDP_hw_slope].u_val) ||
+             isnan(seasonal_coef))
+         {
+                /* one of the prediction values is uinitialized */
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+                return 0;
+         } else {
+            prediction = coefs[CDP_hw_intercept].u_val + 
+                  (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt) 
+                  + seasonal_coef;
+         }
+   }
+
+   if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+   {
+      /* no update, store existing value unchanged, doesn't
+          * matter if it is NA */
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+                rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+   } else if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val))
+   {
+         /* initialization */
+#ifdef DEBUG
+         fprintf(stderr,"Initialization of seasonal deviation\n");
+#endif
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+            abs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+   } else {
+         /* exponential smoothing update */
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+           (rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)*
+           abs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)
+           + (1 -  rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)*
+           (rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val);
+   }
+   return 0;
+}
+
+/* Check for a failure based on a threshold # of violations within the specified
+ * window. */
+int 
+update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx, 
+                               unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+   /* detection of a violation depends on 3 RRAs:
+       * HWPREDICT, SEASONAL, and DEVSEASONAL */
+   rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+   unsigned long dev_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+   rra_def_t *dev_rra = &(rrd -> rra_def[dev_rra_idx]);
+   unsigned long hw_rra_idx = dev_rra -> par[RRA_dependent_rra_idx].u_cnt;
+   rra_def_t *hw_rra =  &(rrd -> rra_def[hw_rra_idx]);
+   unsigned long seasonal_rra_idx = hw_rra -> par[RRA_dependent_rra_idx].u_cnt;
+   unsigned long temp_cdp_idx;
+   rrd_value_t deviation = DNAN;
+   rrd_value_t seasonal_coef = DNAN;
+   rrd_value_t prediction = DNAN;
+   char violation = 0; 
+   unsigned short violation_cnt = 0, i;
+   char *violations_array;
+
+   /* usual checks to determine the order of the RRAs */
+   temp_cdp_idx = dev_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+   if (rra_idx < seasonal_rra_idx)
+   {
+         /* DEVSEASONAL not yet updated */
+         deviation = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+   } else {
+         /* DEVSEASONAL already updated */
+         deviation = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+   }
+   if (!isnan(deviation)) {
+
+   temp_cdp_idx = seasonal_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+   if (rra_idx < seasonal_rra_idx)
+   {
+         /* SEASONAL not yet updated */
+         seasonal_coef = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+   } else {
+         /* SEASONAL already updated */
+         seasonal_coef = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+   }
+   /* in this code block, we know seasonal coef is not DNAN, because deviation is not
+       * null */
+
+   temp_cdp_idx = hw_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+   if (rra_idx < hw_rra_idx)
+   {
+         /* HWPREDICT not yet updated */
+         prediction = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_intercept].u_val + 
+            (rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_slope].u_val)
+                *(rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_null_count].u_cnt)
+                + seasonal_coef;
+   } else {
+         /* HWPREDICT already updated */
+         prediction = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_intercept].u_val + 
+            (rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_slope].u_val)
+                *(rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_last_null_count].u_cnt)
+                + seasonal_coef;
+   }
+
+   /* determine if the observed value is a violation */
+   if (!isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+   {
+         if (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val > prediction + 
+                (current_rra -> par[RRA_delta_pos].u_val)*deviation
+            || rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val < prediction - 
+                (current_rra -> par[RRA_delta_neg].u_val)*deviation)
+                violation = 1;
+   } else {
+         violation = 1; /* count DNAN values as violations */
+   }
+
+   }
+
+   /* determine if a failure has occurred and update the failure array */
+   violation_cnt = violation;
+   /* WARNING: this cast makes XML files non-portable across platforms,
+       * because an array of longs on disk is treated as an array of chars
+       * in memory. */
+   violations_array = (char *) ((void *) rrd -> cdp_prep[cdp_idx].scratch);
+   for (i = current_rra -> par[RRA_window_len].u_cnt; i > 1; i--)
+   {
+         /* shift */
+         violations_array[i-1] = violations_array[i-2]; 
+         violation_cnt += violations_array[i-1];
+   }
+   violations_array[0] = violation;
+
+   if (violation_cnt < current_rra -> par[RRA_failure_threshold].u_cnt)
+         /* not a failure */
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 0.0;
+   else
+         rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 1.0;
+
+   return (rrd-> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+}
+
+/* For the specified CDP prep area and the FAILURES RRA,
+ * erase all history of past violations.
+ */
+void
+erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx)
+{
+   unsigned short i;
+   char *violations_array;
+   /* check that rra_idx is a CF_FAILURES array */
+   if (cf_conv(rrd -> rra_def[rra_idx].cf_nam) != CF_FAILURES)
+   {
+#ifdef DEBUG
+         fprintf(stderr,"erase_violations called for non-FAILURES RRA: %s\n",
+            rrd -> rra_def[rra_idx].cf);
+#endif
+      return;
+   }
+
+#ifdef DEBUG
+   fprintf(stderr,"scratch buffer before erase:\n");
+   for (i = 0; i < MAX_CDP_PAR_EN; i++)
+   {
+         fprintf(stderr,"%lu ", rrd -> cdp_prep[cdp_idx].scratch[i].u_cnt);
+   }
+   fprintf(stderr,"\n");
+#endif
+
+   /* WARNING: this cast makes XML files non-portable across platforms,
+       * because an array of longs on disk is treated as an array of chars
+       * in memory. */
+   violations_array = (char *) ((void *) rrd -> cdp_prep[cdp_idx].scratch);
+   /* erase everything in the part of the CDP scratch array that will be
+       * used to store violations for the current window */
+   for (i = rrd -> rra_def[rra_idx].par[RRA_window_len].u_cnt; i > 0; i--)
+   {
+         violations_array[i-1] = 0;
+   }
+#ifdef DEBUG
+   fprintf(stderr,"scratch buffer after erase:\n");
+   for (i = 0; i < MAX_CDP_PAR_EN; i++)
+   {
+         fprintf(stderr,"%lu ", rrd -> cdp_prep[cdp_idx].scratch[i].u_cnt);
+   }
+   fprintf(stderr,"\n");
+#endif
+}
+
+/* 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,
+               FILE *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;
+
+   offset = floor(0.025*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 (fseek(rrd_file,rra_start,SEEK_SET))
+   {
+         rrd_set_error("seek to rra %d failed", rra_start);
+         free(rrd_values);
+         return -1;
+   }
+   fflush(rrd_file);
+   /* 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)
+         {
+                fread(&(rrd_values[i*row_length + j]),sizeof(rrd_value_t),1,rrd_file);
+                /* should check fread for errors... */
+                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));
+
+   /* 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);
+
+            /* remove a term from the sum */
+            working_average[j] -= queue_pop(buffers[j]);
+         }     
+   } 
+
+   for (i = 0; i < row_length; ++i)
+   {
+         queue_dealloc(buffers[i]);
+   }
+   free(buffers);
+   free(working_average);
+
+   /* flush updated values to disk */
+   fflush(rrd_file);
+   if (fseek(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 (fwrite(rrd_values,sizeof(rrd_value_t),row_length*row_count,rrd_file)
+       != row_length*row_count)
+   {
+         rrd_set_error("apply_smoother: write failed to %lu",rra_start);
+         free(rrd_values);
+         return -1;
+   }
+
+   fflush(rrd_file);
+   free(rrd_values);
+   return 0;
+}
+
+int
+update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf, 
+                 unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx, 
+                 unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef)
+{
+   rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = pdp_val;
+   switch (current_cf) {
+   case CF_AVERAGE:
+   default:
+        return 0;
+   case CF_HWPREDICT:
+     return update_hwpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   case CF_DEVPREDICT:
+        return update_devpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   case CF_SEASONAL:
+     return update_seasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+   case CF_DEVSEASONAL:
+     return update_devseasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+   case CF_FAILURES:
+     return update_failures(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+   }
+   return -1;
+}
+
+unsigned long MyMod(signed long val, unsigned long mod)
+{
+   unsigned long new_val;
+   if (val < 0)
+     new_val = ((unsigned long) abs(val)) % mod;
+   else
+     new_val = (val % mod);
+   
+   if (val < 0) 
+     return (mod - new_val);
+   else
+     return (new_val);
+}
+
+/* a standard fixed-capacity FIF0 queue implementation
+ * No overflow checking is performed. */
+int queue_alloc(FIFOqueue **q,int capacity)
+{
+   *q = (FIFOqueue *) malloc(sizeof(FIFOqueue));
+   if (*q == NULL) return -1;
+   (*q) -> queue = (rrd_value_t *) malloc(sizeof(rrd_value_t)*capacity);
+   if ((*q) -> queue == NULL)
+   {
+         free(*q);
+         return -1;
+   }
+   (*q) -> capacity = capacity;
+   (*q) -> head = capacity;
+   (*q) -> tail = 0;
+   return 0;
+}
+
+int queue_isempty(FIFOqueue *q)
+{
+   return (q -> head % q -> capacity == q -> tail);
+}
+
+void queue_push(FIFOqueue *q, rrd_value_t value)
+{
+   q -> queue[(q -> tail)++] = value;
+   q -> tail = q -> tail % q -> capacity;
+}
+
+rrd_value_t queue_pop(FIFOqueue *q)
+{
+   q -> head = q -> head % q -> capacity;
+   return q -> queue[(q -> head)++];
+}
+
+void queue_dealloc(FIFOqueue *q)
+{
+   free(q -> queue);
+   free(q);
+}
index 37c43db..9f030e7 100644 (file)
@@ -67,6 +67,7 @@ rrd_info(int argc, char **argv) {
     rrd_t        rrd;
     info_t       *data,*cd;
     infoval      info;
     rrd_t        rrd;
     info_t       *data,*cd;
     infoval      info;
+       enum cf_en   current_cf;
 
     if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
        return(NULL);
 
     if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
        return(NULL);
@@ -113,6 +114,7 @@ rrd_info(int argc, char **argv) {
     for(i=0;i<rrd.stat_head->rra_cnt;i++){
        info.u_str=rrd.rra_def[i].cf_nam;
        cd=push(cd,sprintf_alloc("rra[%d].cf",         i),  RD_I_STR,   info);
     for(i=0;i<rrd.stat_head->rra_cnt;i++){
        info.u_str=rrd.rra_def[i].cf_nam;
        cd=push(cd,sprintf_alloc("rra[%d].cf",         i),  RD_I_STR,   info);
+       current_cf = cf_conv(rrd.rra_def[i].cf_nam);
 
        info.u_cnt=rrd.rra_def[i].row_cnt;
        cd=push(cd,sprintf_alloc("rra[%d].rows",i),  RD_I_CNT,   info);
 
        info.u_cnt=rrd.rra_def[i].row_cnt;
        cd=push(cd,sprintf_alloc("rra[%d].rows",i),  RD_I_CNT,   info);
@@ -120,23 +122,69 @@ rrd_info(int argc, char **argv) {
        info.u_cnt=rrd.rra_def[i].pdp_cnt;
        cd=push(cd,sprintf_alloc("rra[%d].pdp_per_row",i),  RD_I_CNT,   info);
 
        info.u_cnt=rrd.rra_def[i].pdp_cnt;
        cd=push(cd,sprintf_alloc("rra[%d].pdp_per_row",i),  RD_I_CNT,   info);
 
-        info.u_val=rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
-        cd=push(cd,sprintf_alloc("rra[%d].xff",i),  RD_I_VAL,   info);
-        
+       switch(current_cf)
+       {
+          case CF_HWPREDICT:
+                 info.u_val=rrd.rra_def[i].par[RRA_hw_alpha].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].alpha",i),RD_I_VAL,info);
+                 info.u_val=rrd.rra_def[i].par[RRA_hw_beta].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].beta",i),RD_I_VAL,info);
+                 break;
+          case CF_SEASONAL:
+          case CF_DEVSEASONAL:
+                 info.u_val=rrd.rra_def[i].par[RRA_seasonal_gamma].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].gamma",i),RD_I_VAL,info);
+                 break;
+          case CF_FAILURES:
+                 info.u_val=rrd.rra_def[i].par[RRA_delta_pos].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].delta_pos",i),RD_I_VAL,info);
+                 info.u_val=rrd.rra_def[i].par[RRA_delta_neg].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].delta_neg",i),RD_I_VAL,info);
+                 info.u_cnt=rrd.rra_def[i].par[RRA_failure_threshold].u_cnt;
+                 cd=push(cd,sprintf_alloc("rra[%d].failure_threshold",i),RD_I_CNT,info);
+                 info.u_cnt=rrd.rra_def[i].par[RRA_window_len].u_cnt;
+                 cd=push(cd,sprintf_alloc("rra[%d].window_length",i),RD_I_CNT,info);
+                 break;
+          case CF_DEVPREDICT:
+                 break;
+          default:
+                 info.u_val=rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
+                 cd=push(cd,sprintf_alloc("rra[%d].xff",i),RD_I_VAL,info);
+                 break;
+       }
+
        for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
            info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
            cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info);
        for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
            info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
            cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info);
-
+        switch(current_cf)
+               {
+               case CF_HWPREDICT:
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
+           cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].intercept",i,ii), RD_I_VAL, info);
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val;
+           cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].slope",i,ii), RD_I_VAL, info);
+           info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
+           cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].NaN_count",i,ii), RD_I_CNT, info);
+                  break;
+               case CF_SEASONAL:
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_seasonal].u_val;
+           cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].seasonal",i,ii), RD_I_VAL, info);
+                  break;
+               case CF_DEVSEASONAL:
+           info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_seasonal_deviation].u_val;
+           cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].deviation",i,ii), RD_I_VAL, info);
+                  break;
+               case CF_DEVPREDICT:
+               case CF_FAILURES:
+                  break;
+               default:
            info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
            cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].unknown_datapoints",i,ii), RD_I_CNT, info);
            info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
            cd=push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].unknown_datapoints",i,ii), RD_I_CNT, info);
+                  break;
         }
     }
         }
     }
-    rrd_free(&rrd);
+       }
+       rrd_free(&rrd);
     return(data);
 
 }
     return(data);
 
 }
-
-
-
-
-
index 1a3496d..7b546f5 100644 (file)
@@ -5,6 +5,13 @@
  *****************************************************************************
  * $Id$
  * $Log$
  *****************************************************************************
  * $Id$
  * $Log$
+ * Revision 1.3  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
  * Revision 1.2  2001/03/04 10:29:20  oetiker
  * fixed filedescriptor leak
  * -- Mike Franusich <mike@franusich.com>
  * Revision 1.2  2001/03/04 10:29:20  oetiker
  * fixed filedescriptor leak
  * -- Mike Franusich <mike@franusich.com>
@@ -45,6 +52,15 @@ rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr)
        rrd_set_error("opening '%s': %s",file_name, strerror(errno));
        return (-1);
     }
        rrd_set_error("opening '%s': %s",file_name, strerror(errno));
        return (-1);
     }
+/*
+       if (rdwr == RRD_READWRITE)
+       {
+          if (setvbuf((*in_file),NULL,_IONBF,2)) {
+                 rrd_set_error("failed to disable the stream buffer\n");
+                 return (-1);
+          }
+       }
+*/
     
 #define MYFREAD(MYVAR,MYVART,MYCNT) \
     if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\
     
 #define MYFREAD(MYVAR,MYVART,MYCNT) \
     if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\
@@ -63,7 +79,7 @@ rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr)
            fclose(*in_file);
            return(-1);}
 
            fclose(*in_file);
            return(-1);}
 
-       if (strncmp(rrd->stat_head->version,RRD_VERSION,5) != 0){
+        if (atoi(rrd->stat_head->version) > atoi(RRD_VERSION)){
            rrd_set_error("can't handle RRD file version %s",
                        rrd->stat_head->version);
            free(rrd->stat_head);
            rrd_set_error("can't handle RRD file version %s",
                        rrd->stat_head->version);
            free(rrd->stat_head);
index 4e2b6c3..f1d1fad 100644 (file)
@@ -99,7 +99,7 @@ int read_tag(char **buf, char *tag, char *format, void *value){
 int xml2rrd(char* buf, rrd_t* rrd, char rc){
   /* pass 1 identify number of RRAs  */
   char *ptr,*ptr2,*ptr3; /* walks thought the buffer */
 int xml2rrd(char* buf, rrd_t* rrd, char rc){
   /* pass 1 identify number of RRAs  */
   char *ptr,*ptr2,*ptr3; /* walks thought the buffer */
-  long rows=0,mempool=0,i=0;
+  long rows=0,mempool=0,i=0,ii;
   xml_lc(buf); /* lets lowercase all active parts of the xml */
   ptr=buf;
   ptr2=buf;
   xml_lc(buf); /* lets lowercase all active parts of the xml */
   ptr=buf;
   ptr2=buf;
@@ -115,6 +115,14 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){
 
   strcpy(rrd->stat_head->cookie,RRD_COOKIE);
   read_tag(&ptr,"version","%4[0-9]",rrd->stat_head->version);
 
   strcpy(rrd->stat_head->cookie,RRD_COOKIE);
   read_tag(&ptr,"version","%4[0-9]",rrd->stat_head->version);
+  /* added primitive version checking */
+  if (atoi(rrd -> stat_head -> version) < 2)
+  {
+    rrd_set_error("Incompatible file version, detected version %s, required version %s\n",
+                 rrd -> stat_head -> version, RRD_VERSION);
+    free(rrd -> stat_head);
+    return -1;
+  }
   rrd->stat_head->float_cookie = FLOAT_COOKIE;
   rrd->stat_head->ds_cnt = 0;
   rrd->stat_head->rra_cnt = 0;
   rrd->stat_head->float_cookie = FLOAT_COOKIE;
   rrd->stat_head->ds_cnt = 0;
   rrd->stat_head->rra_cnt = 0;
@@ -187,21 +195,47 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){
       if(cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == -1) return -1;
 
       read_tag(&ptr2,"pdp_per_row","%lu",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].pdp_cnt));
       if(cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == -1) return -1;
 
       read_tag(&ptr2,"pdp_per_row","%lu",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].pdp_cnt));
-      read_tag(&ptr2,"xff","%lf",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[RRA_cdp_xff_val].u_val));
-      if(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[RRA_cdp_xff_val].u_val > 1 ||
-         rrd->rra_def[rrd->stat_head->rra_cnt-1].par[RRA_cdp_xff_val].u_val < 0)
-          return -1;
-      
+      /* add support to read RRA parameters */
+      eat_tag(&ptr2, "params");
+      for (i = 0; i < MAX_RRA_PAR_EN; i++)
+      {
+       if (i == RRA_dependent_rra_idx || 
+               i == RRA_seasonal_smooth_idx ||
+               i == RRA_failure_threshold)
+         read_tag(&ptr2, "value","%u",
+                  &(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[i].u_cnt));
+       else
+         read_tag(&ptr2, "value","%lf",
+                  &(rrd->rra_def[rrd->stat_head->rra_cnt-1].par[i].u_val));
+      }
+      eat_tag(&ptr2, "/params");
       eat_tag(&ptr2,"cdp_prep");
       eat_tag(&ptr2,"cdp_prep");
-      for(i=0;i<rrd->stat_head->ds_cnt;i++){
-          eat_tag(&ptr2,"ds");
-          read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt*
-                                                      (rrd->stat_head->rra_cnt-1)
-                                                      +i].scratch[CDP_val].u_val));
-          read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
-                                                     *(rrd->stat_head->rra_cnt-1)
-                                                     +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
-          eat_tag(&ptr2,"/ds");
+      for(i=0;i<rrd->stat_head->ds_cnt;i++)
+      {
+       eat_tag(&ptr2,"ds");
+       /* add suport to read CDP parameters */
+       for (ii = 0; ii < MAX_CDP_PAR_EN; ii++)
+       {
+      /* handle integer values as a special case */
+      if (cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == CF_FAILURES ||
+                 ii == CDP_unkn_pdp_cnt || 
+                 ii == CDP_null_count ||
+             ii == CDP_last_null_count)
+         {
+           read_tag(&ptr2,"value","%lu",
+                    &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rrd->stat_head->rra_cnt-1)
+                    +i].scratch[ii].u_cnt));
+         } else {
+           read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt*
+                    (rrd->stat_head->rra_cnt-1) +i].scratch[ii].u_val));
+         }
+
+#if 0
+         read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+                  *(rrd->stat_head->rra_cnt-1) +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
+#endif
+       } /* end for */
+       eat_tag(&ptr2,"/ds");
       }
       eat_tag(&ptr2,"/cdp_prep");
       rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt=0;
       }
       eat_tag(&ptr2,"/cdp_prep");
       rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt=0;
@@ -257,6 +291,9 @@ int xml2rrd(char* buf, rrd_t* rrd, char rc){
   }
 
   for(i=0; i <rrd->stat_head->rra_cnt; i++) {
   }
 
   for(i=0; i <rrd->stat_head->rra_cnt; i++) {
+         /* last row in the xml file is the most recent; as
+          * rrd_update increments the current row pointer, set cur_row
+          * here to the last row. */
       rrd->rra_ptr[i].cur_row = rrd->rra_def[i].row_cnt-1;
   }
   if (ptr==NULL)
       rrd->rra_ptr[i].cur_row = rrd->rra_def[i].row_cnt-1;
   }
   if (ptr==NULL)
@@ -379,3 +416,20 @@ rrd_restore(int argc, char **argv)
     rrd_free(&rrd);    
     return 0;
 }
     rrd_free(&rrd);    
     return 0;
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index 789f072..2795665 100644 (file)
@@ -5,8 +5,15 @@
  *****************************************************************************
  * $Id$
  * $Log$
  *****************************************************************************
  * $Id$
  * $Log$
- * Revision 1.1  2001/02/25 22:25:06  oetiker
- * Initial revision
+ * Revision 1.2  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:06  oetiker
+ * checkin
  *
  *****************************************************************************/
 #ifdef  __cplusplus
  *
  *****************************************************************************/
 #ifdef  __cplusplus
@@ -153,11 +160,36 @@ enum dst_en dst_conv(char *string);
 long ds_match(rrd_t *rrd,char *ds_nam);
 double rrd_diff(char *a, char *b);
 
 long ds_match(rrd_t *rrd,char *ds_nam);
 double rrd_diff(char *a, char *b);
 
+/* functions added for aberrant behavior detection.
+ * implemented for the most part in rrd_hw.c */
+int update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf,
+   unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx,
+   unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef);
+int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, 
+   unsigned long hashed_name);
+int lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+   FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef);
+void erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx);
+int apply_smoother(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+   FILE *rrd_file);
+
+/* a standard fixed-capacity FIFO queue implementation */
+typedef struct FIFOqueue {
+   rrd_value_t *queue;
+   int capacity, head, tail;
+} FIFOqueue;
+
+int queue_alloc(FIFOqueue **q,int capacity);
+void queue_dealloc(FIFOqueue *q);
+void queue_push(FIFOqueue *q, rrd_value_t value);
+int queue_isempty(FIFOqueue *q);
+rrd_value_t queue_pop(FIFOqueue *q);
+
+#define BURNIN_CYCLES 3
+
 #endif
 
 
 #ifdef  __cplusplus
 }
 #endif
 #endif
 
 
 #ifdef  __cplusplus
 }
 #endif
-
-
index d04a725..b45e5db 100644 (file)
@@ -5,13 +5,24 @@
  *****************************************************************************
  * $Id$
  * $Log$
  *****************************************************************************
  * $Id$
  * $Log$
- * Revision 1.1  2001/02/25 22:25:06  oetiker
- * Initial revision
+ * Revision 1.2  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:06  oetiker
+ * checkin
  *
  *****************************************************************************/
 
 #include "rrd_tool.h"
 
  *
  *****************************************************************************/
 
 #include "rrd_tool.h"
 
+int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg);
+int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg);
+int set_windowarg(rrd_t *rrd,enum rra_par_en,char *arg);
+
 int
 rrd_tune(int argc, char **argv)    
 {   
 int
 rrd_tune(int argc, char **argv)    
 {   
@@ -41,11 +52,19 @@ rrd_tune(int argc, char **argv)
            {"maximum",          required_argument, 0, 'a'},
            {"data-source-type", required_argument, 0, 'd'},
            {"data-source-rename", required_argument, 0, 'r'},
            {"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'},
            {0,0,0,0}
        };
        int option_index = 0;
        int opt;
            {0,0,0,0}
        };
        int option_index = 0;
        int opt;
-       opt = getopt_long(argc, argv, "h:i:a:d:r:", 
+       opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:", 
                          long_options, &option_index);
        if (opt == EOF)
            break;
                          long_options, &option_index);
        if (opt == EOF)
            break;
@@ -145,6 +164,48 @@ rrd_tune(int argc, char **argv)
            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;
            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)) {
+                  rrd_free(&rrd);
+                  return -1;
+               }
+               break;
+       case 'y':
+               if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_beta,optarg)) {
+                  rrd_free(&rrd);
+                  return -1;
+               }
+               break;
+       case 'z':
+               if (set_hwarg(&rrd,CF_SEASONAL,RRA_seasonal_gamma,optarg)) {
+                  rrd_free(&rrd);
+                  return -1;
+               }
+               break;
        case '?':
             if (optopt != 0)
                 rrd_set_error("unknown option '%c'", optopt);
        case '?':
             if (optopt != 0)
                 rrd_set_error("unknown option '%c'", optopt);
@@ -162,6 +223,9 @@ rrd_tune(int argc, char **argv)
               sizeof(stat_head_t),1, rrd_file);
        fwrite(rrd.ds_def,
               sizeof(ds_def_t), rrd.stat_head->ds_cnt, rrd_file);
               sizeof(stat_head_t),1, rrd_file);
        fwrite(rrd.ds_def,
               sizeof(ds_def_t), rrd.stat_head->ds_cnt, rrd_file);
+       /* need to write rra_defs for RRA parameter changes */
+       fwrite(rrd.rra_def, sizeof(rra_def_t), rrd.stat_head->rra_cnt,
+                  rrd_file);
     } else {
        int i;
        for(i=0;i< rrd.stat_head->ds_cnt;i++)
     } else {
        int i;
        for(i=0;i< rrd.stat_head->ds_cnt;i++)
@@ -177,3 +241,114 @@ rrd_tune(int argc, char **argv)
     return 0;
 }
 
     return 0;
 }
 
+int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg)
+{
+   double param;
+   unsigned long i;
+   signed short rra_idx = -1, devseasonal_idx = -1;
+   /* read the value */
+   param = atof(arg);
+   if (param <= 0.0 || param >= 1.0)
+   {
+         rrd_set_error("Holt-Winters parameter must be between 0 and 1");
+         return -1;
+   }
+   /* does the appropriate RRA exist?  */
+   for (i =  0; i < rrd -> stat_head -> rra_cnt; ++i)
+   {
+         if (cf_conv(rrd -> rra_def[i].cf_nam) == cf)
+         {
+                rra_idx = i;
+         }
+         /* find the DEVSEASONAL index separately, as it is optional */
+         if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_DEVSEASONAL)
+         {
+                devseasonal_idx = i;
+         }
+   }
+   if (rra_idx == -1) 
+   {
+         rrd_set_error("Holt-Winters RRA does not exist in this RRD");
+         return -1;
+   }
+   
+   /* set the value */
+   rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
+   if (devseasonal_idx > -1)
+         rrd -> rra_def[devseasonal_idx].par[rra_par].u_val = param;
+   return 0;
+}
+
+int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
+{
+   rrd_value_t param;
+   unsigned long i;
+   signed short rra_idx = -1;
+
+   param = atof(arg);
+   if (param < 0.1)
+   {
+         rrd_set_error("Parameter specified is too small");
+         return -1;
+   }
+   /* does the appropriate RRA exist?  */
+   for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
+   {
+         if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES) 
+         {
+                rra_idx = i;
+                break;
+         }
+   }
+   if (rra_idx == -1) 
+   {
+         rrd_set_error("Failures RRA does not exist in this RRD");
+         return -1;
+   }
+
+   /* set the value */
+   rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
+   return 0;
+}
+
+int set_windowarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
+{
+   unsigned long param;
+   unsigned long i, cdp_idx;
+   signed short rra_idx = -1;
+   /* read the value */
+   param = atoi(arg);
+   /* there are 4 chars to a long, reserve on CDP entry for future
+       * use. */
+   if (param < 1 || param > MAX_FAILURES_WINDOW_LEN)
+   {
+         rrd_set_error("Parameter must be between %d and %d",
+                1, MAX_CDP_PAR_EN - 1);
+         return -1;
+   }
+   /* does the appropriate RRA exist?  */
+   for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
+   {
+         if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES) 
+         {
+                rra_idx = i;
+                break;
+         }
+   }
+   if (rra_idx == -1) 
+   {
+         rrd_set_error("Failures RRA does not exist in this RRD");
+         return -1;
+   }
+   
+   /* set the value */
+   rrd -> rra_def[rra_idx].par[rra_par].u_cnt = param;
+
+   /* erase existing violations */
+   for (i = 0; i < rrd -> stat_head -> ds_cnt; i++)
+   {
+         cdp_idx = rra_idx * (rrd -> stat_head -> ds_cnt) + i;
+         erase_violations(rrd,cdp_idx,rra_idx);
+   }
+   return 0;
+}
index 1e5a83e..08ecb49 100644 (file)
@@ -5,6 +5,13 @@
  *****************************************************************************
  * $Id$
  * $Log$
  *****************************************************************************
  * $Id$
  * $Log$
+ * Revision 1.3  2001/03/04 13:01:55  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
  * Revision 1.2  2001/03/04 11:14:25  oetiker
  * added at-style-time@value:value syntax to rrd_update
  * --  Dave Bodenstab <imdave@mcs.net>
  * Revision 1.2  2001/03/04 11:14:25  oetiker
  * added at-style-time@value:value syntax to rrd_update
  * --  Dave Bodenstab <imdave@mcs.net>
  #include <io.h>
 #endif
 
  #include <io.h>
 #endif
 
-
 /* Prototypes */
 int LockRRD(FILE *rrd_file);
 /* Prototypes */
 int LockRRD(FILE *rrd_file);
-
+void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+    unsigned short CDP_scratch_idx, FILE *rrd_file);
 /*#define DEBUG */
 
 /*#define DEBUG */
 
+#define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
+
 
 #ifdef STANDALONE
 int 
 
 #ifdef STANDALONE
 int 
@@ -56,7 +66,8 @@ rrd_update(int argc, char **argv)
 {
 
     int              arg_i = 2;
 {
 
     int              arg_i = 2;
-    long             i,ii,iii;
+    short            j;
+    long             i,ii,iii=1;
 
     unsigned long    rra_begin;          /* byte pointer to the rra
                                          * area in the rrd file.  this
 
     unsigned long    rra_begin;          /* byte pointer to the rra
                                          * area in the rrd file.  this
@@ -81,8 +92,6 @@ rrd_update(int argc, char **argv)
                                          * was last updated */
     unsigned long    occu_pdp_age;       /* how long ago was the last
                                          * pdp_step time */
                                          * was last updated */
     unsigned long    occu_pdp_age;       /* how long ago was the last
                                          * pdp_step time */
-    unsigned long    pdp_st;             /* helper for cdp_prep 
-                                         * processing */
     rrd_value_t      *pdp_new;           /* prepare the incoming data
                                          * to be added the the
                                          * existing entry */
     rrd_value_t      *pdp_new;           /* prepare the incoming data
                                          * to be added the the
                                          * existing entry */
@@ -98,9 +107,23 @@ rrd_update(int argc, char **argv)
     rrd_t            rrd;
     time_t           current_time = time(NULL);
     char             **updvals;
     rrd_t            rrd;
     time_t           current_time = time(NULL);
     char             **updvals;
-    int              wrote_to_file = 0;
-    char             *template = NULL;          
-
+    int              schedule_smooth = 0;
+    char             *template = NULL;   
+       rrd_value_t      *seasonal_coef = NULL, *last_seasonal_coef = NULL;
+                                        /* a vector of future Holt-Winters seasonal coefs */
+    unsigned long    elapsed_pdp_st;
+                                        /* number of elapsed PDP steps since last update */
+    unsigned long    *rra_step_cnt = NULL;
+                                        /* number of rows to be updated in an RRA for a data
+                                         * value. */
+    unsigned long    start_pdp_offset;
+                                        /* number of PDP steps since the last update that
+                                         * are assigned to the first CDP to be generated
+                                         * since the last update. */
+    unsigned short   scratch_idx;
+                                        /* index into the CDP scratch array */
+    enum cf_en       current_cf;
+                                        /* numeric id of the current consolidation function */
 
     while (1) {
        static struct option long_options[] =
 
     while (1) {
        static struct option long_options[] =
@@ -123,7 +146,7 @@ rrd_update(int argc, char **argv)
 
        case '?':
            rrd_set_error("unknown option '%s'",argv[optind-1]);
 
        case '?':
            rrd_set_error("unknown option '%s'",argv[optind-1]);
-            rrd_free(&rrd);
+            rrd_free(&rrd);            
            return(-1);
        }
     }
            return(-1);
        }
     }
@@ -158,7 +181,7 @@ rrd_update(int argc, char **argv)
       rrd_free(&rrd);
       fclose(rrd_file);
       return(-1);   
       rrd_free(&rrd);
       fclose(rrd_file);
       return(-1);   
-    }
+    } 
 
     if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
        rrd_set_error("allocating updvals pointer array");
 
     if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
        rrd_set_error("allocating updvals pointer array");
@@ -248,7 +271,7 @@ rrd_update(int argc, char **argv)
        enum {atstyle, normal} timesyntax;
        struct time_value ds_tv;
         if (stepper == NULL){
        enum {atstyle, normal} timesyntax;
        struct time_value ds_tv;
         if (stepper == NULL){
-                rrd_set_error("faild duplication argv entry");
+                rrd_set_error("failed duplication argv entry");
                 free(updvals);
                 free(pdp_temp);  
                 free(tmpl_idx);
                 free(updvals);
                 free(pdp_temp);  
                 free(tmpl_idx);
@@ -329,7 +352,7 @@ rrd_update(int argc, char **argv)
        }
        
        
        }
        
        
-       /* seek to the beginning of the rrd's */
+       /* seek to the beginning of the rra's */
        if (rra_current != rra_begin) {
            if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
                rrd_set_error("seek error in rrd");
        if (rra_current != rra_begin) {
            if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
                rrd_set_error("seek error in rrd");
@@ -532,183 +555,439 @@ rrd_update(int argc, char **argv)
 #endif
            }
 
 #endif
            }
 
-
-           /* now we have to integrate this data into the cdp_prep areas */
-           /* going through the round robin archives */
-           for(i = 0;
-               i < rrd.stat_head->rra_cnt;
-               i++){
-               enum cf_en current_cf = cf_conv(rrd.rra_def[i].cf_nam);
-               /* going through all pdp_st moments which have occurred 
-                * since the last run */
-               for(pdp_st  = proc_pdp_st+rrd.stat_head->pdp_step; 
-                   pdp_st <= occu_pdp_st; 
-                   pdp_st += rrd.stat_head->pdp_step){
-
+               /* compute the number of elapsed pdp_st moments */
+               elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step;
 #ifdef DEBUG
 #ifdef DEBUG
-                   fprintf(stderr,"RRA %lu STEP %lu\n",i,pdp_st);
+               fprintf(stderr,"elapsed PDP steps: %lu\n", elapsed_pdp_st);
 #endif
 #endif
+               if (rra_step_cnt == NULL)
+               {
+                  rra_step_cnt = (unsigned long *) 
+                         malloc((rrd.stat_head->rra_cnt)* sizeof(unsigned long));
+               }
 
 
-                   if((pdp_st %
-                       (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++)
+               {
+               current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+               start_pdp_offset = rrd.rra_def[i].pdp_cnt -
+                  (proc_pdp_st / rrd.stat_head -> pdp_step) % rrd.rra_def[i].pdp_cnt;
+        if (start_pdp_offset <= elapsed_pdp_st) {
+           rra_step_cnt[i] = (elapsed_pdp_st - start_pdp_offset) / 
+                     rrd.rra_def[i].pdp_cnt + 1;
+           } else {
+                  rra_step_cnt[i] = 0;
+               }
 
 
-                       /* later on the cdp_prep values will be transferred to
-                        * the rra.  we want to be in the right place. */
-                       rrd.rra_ptr[i].cur_row++;
-                       if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
-                           /* oops ... we have to wrap the beast ... */
-                           rrd.rra_ptr[i].cur_row=0;                   
+               if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL) 
+               {
+                  /* If this is a bulk update, we need to skip ahead in the seasonal
+                       * arrays so that they will be correct for the next observed value;
+                       * note that for the bulk update itself, no update will occur to
+                       * DEVSEASONAL or SEASONAL; futhermore, HWPREDICT and DEVPREDICT will
+                       * be set to DNAN. */
+           if (rra_step_cnt[i] > 2) 
+                  {
+                         /* skip update by resetting rra_step_cnt[i],
+                          * note that this is not data source specific; this is due
+                          * to the bulk update, not a DNAN value for the specific data
+                          * source. */
+                         rra_step_cnt[i] = 0;
+              lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st, 
+                            &last_seasonal_coef);
+                     lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st + 1,
+                            &seasonal_coef);
+                  }
+               
+                 /* periodically run a smoother for seasonal effects */
+                 /* Need to use first cdp parameter buffer to track
+                  * burnin (burnin requires a specific smoothing schedule).
+                  * The CDP_init_seasonal parameter is really an RRA level,
+                  * not a data source within RRA level parameter, but the rra_def
+                  * is read only for rrd_update (not flushed to disk). */
+                 iii = i*(rrd.stat_head -> ds_cnt);
+                 if (rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt 
+                         <= BURNIN_CYCLES)
+                 {
+                    if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st 
+                                > rrd.rra_def[i].row_cnt - 1) {
+                          /* mark off one of the burnin cycles */
+                          ++(rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt);
+                      schedule_smooth = 1;
+                        }  
+                 } else {
+                        /* someone has no doubt invented a trick to deal with this
+                         * wrap around, but at least this code is clear. */
+                        if (rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt >
+                            rrd.rra_ptr[i].cur_row)
+                        {
+                                /* here elapsed_pdp_st = rra_step_cnt[i] because of 1-1
+                                 * mapping between PDP and CDP */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
 #ifdef DEBUG
 #ifdef DEBUG
-                       fprintf(stderr,"  -- RRA Preseek %ld\n",ftell(rrd_file));
+                                       fprintf(stderr,
+                                       "schedule_smooth 1: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
 #endif
 #endif
-                       /* determine if a seek is even needed. */
-                       rra_pos_tmp = rra_start +
-                               rrd.stat_head->ds_cnt*rrd.rra_ptr[i].cur_row*sizeof(rrd_value_t);
-                       if(rra_pos_tmp != rra_current) {
-                           if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
-                               rrd_set_error("seek error in rrd");
-                               break;
-                           }
-                           rra_current = rra_pos_tmp;
-                       }
+                                       schedule_smooth = 1;
+                                }
+             } else {
+                                /* can't rely on negative numbers because we are working with
+                                 * unsigned values */
+                                /* Don't need modulus here. If we've wrapped more than once, only
+                                 * one smooth is executed at the end. */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st >= rrd.rra_def[i].row_cnt
+                                       && rrd.rra_ptr[i].cur_row + elapsed_pdp_st - rrd.rra_def[i].row_cnt
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
 #ifdef DEBUG
 #ifdef DEBUG
-                       fprintf(stderr,"  -- RRA Postseek %ld\n",ftell(rrd_file));
+                                       fprintf(stderr,
+                                       "schedule_smooth 2: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
 #endif
 #endif
-                   }
+                                       schedule_smooth = 1;
+                                }
+                        }
+                 }
+
+             rra_current = ftell(rrd_file); 
+               } /* if cf is DEVSEASONAL or SEASONAL */
 
 
+        if (rrd_test_error()) break;
+
+                   /* update CDP_PREP areas */
+                   /* loop over data soures within each RRA */
                    for(ii = 0;
                        ii < rrd.stat_head->ds_cnt;
                    for(ii = 0;
                        ii < rrd.stat_head->ds_cnt;
-                       ii++){
+                       ii++)
+                       {
+                       
+                       /* iii indexes the CDP prep area for this data source within the RRA */
                        iii=i*rrd.stat_head->ds_cnt+ii;
                        iii=i*rrd.stat_head->ds_cnt+ii;
-                   
-                       /* the contents of cdp_prep[].scratch[CDP_val].u_val depends
-                        * on the consolidation function ! */
-                   
-                       if (isnan(pdp_temp[ii])){    /* pdp is unknown */
-                           rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt++;
+
+                       if (rrd.rra_def[i].pdp_cnt > 1) {
+                         
+                          if (rra_step_cnt[i] > 0) {
+                          /* If we are in this block, as least 1 CDP value will be written to
+                               * disk, this is the CDP_primary_val entry. If more than 1 value needs
+                               * to be written, then the "fill in" value is the CDP_secondary_val
+                               * entry. */
+                                 if (isnan(pdp_temp[ii]))
+                  {
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += start_pdp_offset;
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+                                 } else {
+                                        /* CDP_secondary value is the RRA "fill in" value for intermediary
+                                         * CDP data entries. No matter the CF, the value is the same because
+                                         * the average, max, min, and last of a list of identical values is
+                                         * the same, namely, the value itself. */
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = pdp_temp[ii];
+                                 }
+                     
+                                 if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
+                                     > rrd.rra_def[i].pdp_cnt*
+                                     rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
+                                 {
+                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                        /* initialize carry over */
+                                        if (current_cf == CF_AVERAGE) {
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                        } else {
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
+                                 } else {
+                                        rrd_value_t cum_val, cur_val; 
+                                    switch (current_cf) {
+                                               case CF_AVERAGE:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, 0.0);
+                                                 cur_val = IFDNAN(pdp_temp[ii],0.0);
+                          rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val =
+                                              (cum_val + cur_val) /
+                                          (rrd.rra_def[i].pdp_cnt
+                                              -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+                                                  /* initialize carry over value */
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                                  break;
+                                               case CF_MAXIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, -DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],-DINF);
 #ifdef DEBUG
 #ifdef DEBUG
-                           fprintf(stderr,"  ** UNKNOWN ADD %lu\n",
-                                   rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
 #endif
 #endif
-                       } else {
-                           if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)){
-                               /* cdp_prep is unknown when it does not
-                                * yet contain data. It can not be zero for
-                                * things like mim and max consolidation
-                                * functions */
+                                                 if (cur_val > cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_MINIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],DINF);
 #ifdef DEBUG
 #ifdef DEBUG
-                               fprintf(stderr,"  ** INIT CDP %e\n", pdp_temp[ii]);
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
 #endif
 #endif
-                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
-                           }
-                           else {
-                               switch (current_cf){
-                                   case CF_AVERAGE:                            
-                                       rrd.cdp_prep[iii].scratch[CDP_val].u_val+=pdp_temp[ii];
+                                                 if (cur_val < cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_LAST:
+                                               default:
+                                                  rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = pdp_temp[ii];
+                                                  /* initialize carry over value */
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 } /* endif meets xff value requirement for a valid value */
+                                 /* initialize carry over CDP_unkn_pdp_cnt, this must after CDP_primary_val
+                                  * is set because CDP_unkn_pdp_cnt is required to compute that value. */
+                                 if (isnan(pdp_temp[ii]))
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 
+                                               (elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt;
+                                 else
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
+               } else  /* rra_step_cnt[i]  == 0 */
+                          {
 #ifdef DEBUG
 #ifdef DEBUG
-                                       fprintf(stderr,"  ** AVERAGE %e\n", 
-                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)) {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, DNAN\n",
+                                        i,ii);
+                                 } else {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, %10.2f\n",
+                                        i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+                                 }
 #endif
 #endif
-                                       break;    
-                                   case CF_MINIMUM:
-                                       if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                 if (isnan(pdp_temp[ii])) {
+                                rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += elapsed_pdp_st;
+                                 } else if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val))
+                                 {
+                                        if (current_cf == CF_AVERAGE) {
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                        } else {
                                            rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
                                            rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
 #ifdef DEBUG
 #ifdef DEBUG
-                                       fprintf(stderr,"  ** MINIMUM %e\n", 
-                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+                                        fprintf(stderr,"Initialize CDP_val for RRA %lu DS %lu: %10.2f\n",
+                                           i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
 #endif
 #endif
-                                       break;
-                                   case CF_MAXIMUM:
-                                       if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
-                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                 } else {
+                                        switch (current_cf) {
+                                        case CF_AVERAGE:
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val += pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                               break;
+                                        case CF_MINIMUM:
+                                               if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_MAXIMUM:
+                                               if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_LAST:
+                                        default:
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 }
+                          }
+                       } else { /* rrd.rra_def[i].pdp_cnt == 1 */
+                          if (elapsed_pdp_st > 2)
+                          {
+                                  switch (current_cf) {
+                                  case CF_AVERAGE:
+                                  default:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val=pdp_temp[ii];
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val=pdp_temp[ii];
+                                         break;
+                   case CF_SEASONAL:
+                                  case CF_DEVSEASONAL:
+                                         /* need to update cached seasonal values, so they are consistent
+                                          * with the bulk update */
+                      /* WARNING: code relies on the fact that CDP_hw_last_seasonal and
+                                          * CDP_last_deviation are the same. */
+                      rrd.cdp_prep[iii].scratch[CDP_hw_last_seasonal].u_val =
+                                                last_seasonal_coef[ii];
+                                         rrd.cdp_prep[iii].scratch[CDP_hw_seasonal].u_val =
+                                                seasonal_coef[ii];
+                                         break;
+                   case CF_HWPREDICT:
+                                         /* need to update the null_count and last_null_count.
+                                          * even do this for non-DNAN pdp_temp because the
+                                          * algorithm is not learning from batch updates. */
+                                         rrd.cdp_prep[iii].scratch[CDP_null_count].u_cnt += 
+                                                elapsed_pdp_st;
+                                         rrd.cdp_prep[iii].scratch[CDP_last_null_count].u_cnt += 
+                                                elapsed_pdp_st - 1;
+                                         /* fall through */
+                                  case CF_DEVPREDICT:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+                                         break;
+                   case CF_FAILURES:
+                                         /* do not count missed bulk values as failures */
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = 0;
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = 0;
+                                         /* need to reset violations buffer.
+                                          * could do this more carefully, but for now, just
+                                          * assume a bulk update wipes away all violations. */
+                      erase_violations(&rrd, iii, i);
+                                         break;
+                                  }
+                          } 
+                       } /* endif rrd.rra_def[i].pdp_cnt == 1 */
+
+                       if (rrd_test_error()) break;
+
+                       } /* endif data sources loop */
+        } /* end RRA Loop */
+
+               /* this loop is only entered if elapsed_pdp_st < 3 */
+               for (j = elapsed_pdp_st, scratch_idx = CDP_primary_val; 
+                        j > 0 && j < 3; j--, scratch_idx = CDP_secondary_val)
+               {
+              for(i = 0, rra_start = rra_begin;
+                  i < rrd.stat_head->rra_cnt;
+              rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+                  i++)
+                  {
+                         if (rrd.rra_def[i].pdp_cnt > 1) continue;
+
+                 current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+                         if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL)
+                         {
+                        lookup_seasonal(&rrd,i,rra_start,rrd_file,
+                                   elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2),
+                               &seasonal_coef);
+                         }
+                         if (rrd_test_error()) break;
+                     /* loop over data soures within each RRA */
+                     for(ii = 0;
+                         ii < rrd.stat_head->ds_cnt;
+                         ii++)
+                         {
+                            update_aberrant_CF(&rrd,pdp_temp[ii],current_cf,
+                                       i*(rrd.stat_head->ds_cnt) + ii,i,ii,
+                                   scratch_idx, seasonal_coef);
+                         }
+           } /* end RRA Loop */
+                  if (rrd_test_error()) break;
+           } /* end elapsed_pdp_st loop */
+
+               if (rrd_test_error()) break;
+
+               /* Ready to write to disk */
+               /* Move sequentially through the file, writing one RRA at a time.
+                * Note this architecture divorces the computation of CDP with
+                * flushing updated RRA entries to disk. */
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++) {
+               /* is there anything to write for this RRA? If not, continue. */
+        if (rra_step_cnt[i] == 0) continue;
+
+               /* write the first row */
 #ifdef DEBUG
 #ifdef DEBUG
-                                       fprintf(stderr,"  ** MAXIMUM %e\n", 
-                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+        fprintf(stderr,"  -- RRA Preseek %ld\n",ftell(rrd_file));
 #endif
 #endif
-                                       break;
-                                   case CF_LAST:
-                                       rrd.cdp_prep[iii].scratch[CDP_val].u_val=pdp_temp[ii];
+           rrd.rra_ptr[i].cur_row++;
+           if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
+                  rrd.rra_ptr[i].cur_row = 0; /* wrap around */
+               /* positition on the first row */
+               rra_pos_tmp = rra_start +
+                  (rrd.stat_head->ds_cnt)*(rrd.rra_ptr[i].cur_row)*sizeof(rrd_value_t);
+               if(rra_pos_tmp != rra_current) {
+                  if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
+                     rrd_set_error("seek error in rrd");
+                     break;
+                  }
+                  rra_current = rra_pos_tmp;
+               }
 #ifdef DEBUG
 #ifdef DEBUG
-                                       fprintf(stderr,"  ** LAST %e\n", 
-                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+           fprintf(stderr,"  -- RRA Postseek %ld\n",ftell(rrd_file));
 #endif
 #endif
-                                       break;    
-                                   default:
-                                       rrd_set_error("Unknown cf %s",
-                                                     rrd.rra_def[i].cf_nam);
-                                       break;
-                               }
-                           }
-                       }
-
-
-                       /* is the data in the cdp_prep ready to go into
-                        * its rra ? */
-                       if((pdp_st % 
-                           (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
-
-                           /* prepare cdp_pref for its transition to the rra. */
-                           if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt 
-                               > rrd.rra_def[i].pdp_cnt*
-                               rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
-                               /* to much of the cdp_prep is unknown ... */
-                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
-                           else if (current_cf == CF_AVERAGE){
-                               /* for a real average we have to divide
-                                * the sum we built earlier on. While ignoring
-                                * the unknown pdps */
-                               rrd.cdp_prep[iii].scratch[CDP_val].u_val 
-                                       /= (rrd.rra_def[i].pdp_cnt
-                                           -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
-                           }
-                           /* we can write straight away, because we are
-                            * already in the right place ... */
-
+               scratch_idx = CDP_primary_val;
+               write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file);
+               if (rrd_test_error()) break;
+
+               /* write other rows of the bulk update, if any */
+               scratch_idx = CDP_secondary_val;
+               for ( ; rra_step_cnt[i] > 1; 
+                    rra_step_cnt[i]--, rrd.rra_ptr[i].cur_row++)
+               {
+                  if (rrd.rra_ptr[i].cur_row == rrd.rra_def[i].row_cnt)
+                  {
 #ifdef DEBUG
 #ifdef DEBUG
-                           fprintf(stderr,"  -- RRA WRITE VALUE %e, at %ld\n",
-                                   rrd.cdp_prep[iii].scratch[CDP_val].u_val,ftell(rrd_file));
+              fprintf(stderr,"Wraparound for RRA %s, %lu updates left\n",
+                         rrd.rra_def[i].cf_nam, rra_step_cnt[i] - 1);
 #endif
 #endif
-
-                           if(fwrite(&(rrd.cdp_prep[iii].scratch[CDP_val].u_val),
-                                     sizeof(rrd_value_t),1,rrd_file) != 1){
-                               rrd_set_error("writing rrd");
-                               break;
-                           }
-                           rra_current += sizeof(rrd_value_t);
-                           wrote_to_file = 1;
-
+                         /* wrap */
+                         rrd.rra_ptr[i].cur_row = 0;
+                         /* seek back to beginning of current rra */
+                     if (fseek(rrd_file, rra_start, SEEK_SET) != 0)
+                         {
+                        rrd_set_error("seek error in rrd");
+                        break;
+                         }
 #ifdef DEBUG
 #ifdef DEBUG
-                           fprintf(stderr,"  -- RRA WROTE new at %ld\n",ftell(rrd_file));
+                 fprintf(stderr,"  -- Wraparound Postseek %ld\n",ftell(rrd_file));
 #endif
 #endif
-
-                           /* make cdp_prep ready for the next run */
-                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
-                           rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
-                       }
-                   }
-                   /* break out of this loop if error_string has been set */
-                   if (rrd_test_error())
-                       break;
+                         rra_current = rra_start;
+                  }
+                  write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file);
                }
                }
-               /* break out of this loop if error_string has been set */
+               
                if (rrd_test_error())
                if (rrd_test_error())
-                   break;
-               /* to be able to position correctly in the next rra w move
-                * the rra_start pointer on to the next rra */
-               rra_start += rrd.rra_def[i].row_cnt
-                       *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
+                 break;
+               } /* RRA LOOP */
 
 
-           }
            /* break out of the argument parsing loop if error_string is set */
            if (rrd_test_error()){
            /* break out of the argument parsing loop if error_string is set */
            if (rrd_test_error()){
-               free(step_start);
-               break;
-           }
-       }
+                  free(step_start);
+                  break;
+           } 
+           
+       } /* endif a pdp_st has occurred */ 
        rrd.live_head->last_up = current_time;
        free(step_start);
        rrd.live_head->last_up = current_time;
        free(step_start);
-    }
+    } /* function argument loop */
 
 
+    if (seasonal_coef != NULL) free(seasonal_coef);
+    if (last_seasonal_coef != NULL) free(last_seasonal_coef);
+       if (rra_step_cnt != NULL) free(rra_step_cnt);
 
     /* if we got here and if there is an error and if the file has not been
      * written to, then close things up and return. */
 
     /* if we got here and if there is an error and if the file has not been
      * written to, then close things up and return. */
@@ -803,6 +1082,36 @@ rrd_update(int argc, char **argv)
        return(-1);
     }
 
        return(-1);
     }
 
+    /* calling the smoothing code here guarantees at most
+        * one smoothing operation per rrd_update call. Unfortunately,
+        * it is possible with bulk updates, or a long-delayed update
+        * for smoothing to occur off-schedule. This really isn't
+        * critical except during the burning cycles. */
+       if (schedule_smooth)
+       {
+#ifndef WIN32
+         rrd_file = fopen(argv[optind],"r+");
+#else
+         rrd_file = fopen(argv[optind],"rb+");
+#endif
+         rra_start = rra_begin;
+         for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
+         {
+           if (cf_conv(rrd.rra_def[i].cf_nam) == CF_DEVSEASONAL ||
+               cf_conv(rrd.rra_def[i].cf_nam) == CF_SEASONAL)
+           {
+#ifdef DEBUG
+             fprintf(stderr,"Running smoother for rra %ld\n",i);
+#endif
+             apply_smoother(&rrd,i,rra_start,rrd_file);
+             if (rrd_test_error())
+               break;
+           }
+           rra_start += rrd.rra_def[i].row_cnt
+             *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
+         }
+         fclose(rrd_file);
+       }
     rrd_free(&rrd);
     free(updvals);
     free(tmpl_idx);
     rrd_free(&rrd);
     free(updvals);
     free(tmpl_idx);
@@ -847,3 +1156,30 @@ LockRRD(FILE *rrdfile)
 
     return(stat);
 }
 
     return(stat);
 }
+
+
+void
+write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+   unsigned short CDP_scratch_idx, FILE *rrd_file)
+{
+   unsigned long ds_idx, cdp_idx;
+
+   for (ds_idx = 0; ds_idx < rrd -> stat_head -> ds_cnt; ds_idx++)
+   {
+      /* compute the cdp index */
+      cdp_idx =rra_idx * (rrd -> stat_head->ds_cnt) + ds_idx;
+#ifdef DEBUG
+         fprintf(stderr,"  -- RRA WRITE VALUE %e, at %ld CF:%s\n",
+            rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,ftell(rrd_file),
+            rrd -> rra_def[rra_idx].cf_nam);
+#endif
+
+         if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+                sizeof(rrd_value_t),1,rrd_file) != 1)
+         { 
+            rrd_set_error("writing rrd");
+                return;
+         }
+         *rra_current += sizeof(rrd_value_t);
+       }
+}
diff --git a/src/rrdupdate.c b/src/rrdupdate.c
new file mode 100644 (file)
index 0000000..cbc99a6
--- /dev/null
@@ -0,0 +1,1184 @@
+/*****************************************************************************
+ * RRDtool 1.0.33  Copyright Tobias Oetiker, 1997 - 2000
+ *****************************************************************************
+ * rrd_update.c  RRD Update Function
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.3  2001/03/04 13:01:56  oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.2  2001/03/04 11:14:25  oetiker
+ * added at-style-time@value:value syntax to rrd_update
+ * --  Dave Bodenstab <imdave@mcs.net>
+ * Revision 1.1  2001/02/25 22:25:06  oetiker
+ * Initial revision
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef WIN32
+ #include <sys/locking.h>
+ #include <sys/stat.h>
+ #include <io.h>
+#endif
+
+/* Prototypes */
+int LockRRD(FILE *rrd_file);
+void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+    unsigned short CDP_scratch_idx, FILE *rrd_file);
+/*#define DEBUG */
+
+#define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
+
+
+#ifdef STANDALONE
+int 
+main(int argc, char **argv){
+        rrd_update(argc,argv);
+        if (rrd_test_error()) {
+                printf("RRDtool 1.0.33  Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
+                        "Usage: rrdupdate filename\n"
+                        "\t\t\t[--template|-t ds-name:ds-name:...]\n"
+                        "\t\t\ttime|N:value[:value...]\n\n"
+                        "\t\t\tat-time@value[:value...]\n\n"
+                        "\t\t\t[ time:value[:value...] ..]\n\n");
+                                   
+                printf("ERROR: %s\n",rrd_get_error());
+                rrd_clear_error();                                                            
+                return 1;
+        }
+        return 0;
+}
+#endif
+
+int
+rrd_update(int argc, char **argv)
+{
+
+    int              arg_i = 2;
+    short            j;
+    long             i,ii,iii=1;
+
+    unsigned long    rra_begin;          /* byte pointer to the rra
+                                         * area in the rrd file.  this
+                                         * pointer never changes value */
+    unsigned long    rra_start;          /* byte pointer to the rra
+                                         * area in the rrd file.  this
+                                         * pointer changes as each rrd is
+                                         * processed. */
+    unsigned long    rra_current;        /* byte pointer to the current write
+                                         * spot in the rrd file. */
+    unsigned long    rra_pos_tmp;        /* temporary byte pointer. */
+    unsigned long    interval,
+       pre_int,post_int;                /* interval between this and
+                                         * the last run */
+    unsigned long    proc_pdp_st;        /* which pdp_st was the last
+                                         * to be processed */
+    unsigned long    occu_pdp_st;        /* when was the pdp_st
+                                         * before the last update
+                                         * time */
+    unsigned long    proc_pdp_age;       /* how old was the data in
+                                         * the pdp prep area when it
+                                         * was last updated */
+    unsigned long    occu_pdp_age;       /* how long ago was the last
+                                         * pdp_step time */
+    rrd_value_t      *pdp_new;           /* prepare the incoming data
+                                         * to be added the the
+                                         * existing entry */
+    rrd_value_t      *pdp_temp;          /* prepare the pdp values 
+                                         * to be added the the
+                                         * cdp values */
+
+    long             *tmpl_idx;          /* index representing the settings
+                                           transported by the template index */
+    long             tmpl_cnt = 2;       /* time and data */
+
+    FILE             *rrd_file;
+    rrd_t            rrd;
+    time_t           current_time = time(NULL);
+    char             **updvals;
+    int              schedule_smooth = 0;
+    char             *template = NULL;   
+       rrd_value_t      *seasonal_coef = NULL, *last_seasonal_coef = NULL;
+                                        /* a vector of future Holt-Winters seasonal coefs */
+    unsigned long    elapsed_pdp_st;
+                                        /* number of elapsed PDP steps since last update */
+    unsigned long    *rra_step_cnt = NULL;
+                                        /* number of rows to be updated in an RRA for a data
+                                         * value. */
+    unsigned long    start_pdp_offset;
+                                        /* number of PDP steps since the last update that
+                                         * are assigned to the first CDP to be generated
+                                         * since the last update. */
+    unsigned short   scratch_idx;
+                                        /* index into the CDP scratch array */
+    enum cf_en       current_cf;
+                                        /* numeric id of the current consolidation function */
+
+    while (1) {
+       static struct option long_options[] =
+       {
+           {"template",      required_argument, 0, 't'},
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       opt = getopt_long(argc, argv, "t:", 
+                         long_options, &option_index);
+       
+       if (opt == EOF)
+         break;
+       
+       switch(opt) {
+       case 't':
+           template = optarg;
+           break;
+
+       case '?':
+           rrd_set_error("unknown option '%s'",argv[optind-1]);
+            rrd_free(&rrd);            
+           return(-1);
+       }
+    }
+
+    /* need at least 2 arguments: filename, data. */
+    if (argc-optind < 2) {
+       rrd_set_error("Not enough arguments");
+       return -1;
+    }
+
+    if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){
+       return -1;
+    }
+    rra_current = rra_start = rra_begin = ftell(rrd_file);
+    /* This is defined in the ANSI C standard, section 7.9.5.3:
+
+        When a file is opened with udpate mode ('+' as the second
+        or third character in the ... list of mode argument
+        variables), both input and ouptut may be performed on the
+        associated stream.  However, ...  input may not be directly
+        followed by output without an intervening call to a file
+        positioning function, unless the input oepration encounters
+        end-of-file. */
+    fseek(rrd_file, 0, SEEK_CUR);
+
+    
+    /* get exclusive lock to whole file.
+     * lock gets removed when we close the file.
+     */
+    if (LockRRD(rrd_file) != 0) {
+      rrd_set_error("could not lock RRD");
+      rrd_free(&rrd);
+      fclose(rrd_file);
+      return(-1);   
+    } 
+
+    if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
+       rrd_set_error("allocating updvals pointer array");
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if ((pdp_temp = malloc(sizeof(rrd_value_t)
+                          *rrd.stat_head->ds_cnt))==NULL){
+       rrd_set_error("allocating pdp_temp ...");
+       free(updvals);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if ((tmpl_idx = malloc(sizeof(unsigned long)
+                          *(rrd.stat_head->ds_cnt+1)))==NULL){
+       rrd_set_error("allocating tmpl_idx ...");
+       free(pdp_temp);
+       free(updvals);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+    /* initialize template redirector */
+    /* default config
+       tmpl_idx[0] -> 0; (time)
+       tmpl_idx[1] -> 1; (DS 0)
+       tmpl_idx[2] -> 2; (DS 1)
+       tmpl_idx[3] -> 3; (DS 2)
+       ... */
+    for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i;
+    tmpl_cnt=rrd.stat_head->ds_cnt+1;
+    if (template) {
+       char *dsname;
+       int tmpl_len;
+       dsname = template;
+       tmpl_cnt = 1; /* the first entry is the time */
+       tmpl_len = strlen(template);
+       for(i=0;i<=tmpl_len ;i++) {
+           if (template[i] == ':' || template[i] == '\0') {
+               template[i] = '\0';
+               if (tmpl_cnt>rrd.stat_head->ds_cnt){
+                   rrd_set_error("Template contains more DS definitions than RRD");
+                   free(updvals); free(pdp_temp);
+                   free(tmpl_idx); rrd_free(&rrd);
+                   fclose(rrd_file); return(-1);
+               }
+               if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
+                   rrd_set_error("unknown DS name '%s'",dsname);
+                   free(updvals); free(pdp_temp);
+                   free(tmpl_idx); rrd_free(&rrd);
+                   fclose(rrd_file); return(-1);
+               } else {
+                 /* the first element is always the time */
+                 tmpl_idx[tmpl_cnt-1]++; 
+                 /* go to the next entry on the template */
+                 dsname = &template[i+1];
+                  /* fix the damage we did before */
+                  if (i<tmpl_len) {
+                     template[i]=':';
+                  } 
+
+               }
+           }       
+       }
+    }
+    if ((pdp_new = malloc(sizeof(rrd_value_t)
+                         *rrd.stat_head->ds_cnt))==NULL){
+       rrd_set_error("allocating pdp_new ...");
+       free(updvals);
+       free(pdp_temp);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    /* loop through the arguments. */
+    for(arg_i=optind+1; arg_i<argc;arg_i++) {
+       char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
+        char *step_start = stepper;
+       char *p;
+       char *parsetime_error = NULL;
+       enum {atstyle, normal} timesyntax;
+       struct time_value ds_tv;
+        if (stepper == NULL){
+                rrd_set_error("failed duplication argv entry");
+                free(updvals);
+                free(pdp_temp);  
+                free(tmpl_idx);
+                rrd_free(&rrd);
+                fclose(rrd_file);
+                return(-1);
+         }
+       /* initialize all ds input to unknown except the first one
+           which has always got to be set */
+       for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
+       strcpy(stepper,argv[arg_i]);
+       updvals[0]=stepper;
+       /* separate all ds elements; first must be examined separately
+          due to alternate time syntax */
+       if ((p=strchr(stepper,'@'))!=NULL) {
+           timesyntax = atstyle;
+           *p = '\0';
+           stepper = p+1;
+       } else if ((p=strchr(stepper,':'))!=NULL) {
+           timesyntax = normal;
+           *p = '\0';
+           stepper = p+1;
+       } else {
+           rrd_set_error("expected timestamp not found in data source from %s:...",
+                         argv[arg_i]);
+           free(step_start);
+           break;
+       }
+       ii=1;
+       updvals[tmpl_idx[ii]] = stepper;
+       while (*stepper) {
+           if (*stepper == ':') {
+               *stepper = '\0';
+               ii++;
+               if (ii<tmpl_cnt){                   
+                   updvals[tmpl_idx[ii]] = stepper+1;
+               }
+           }
+           stepper++;
+       }
+
+       if (ii != tmpl_cnt-1) {
+           rrd_set_error("expected %lu data source readings (got %lu) from %s:...",
+                         tmpl_cnt-1, ii, argv[arg_i]);
+           free(step_start);
+           break;
+       }
+       
+        /* get the time from the reading ... handle N */
+       if (timesyntax == atstyle) {
+            if ((parsetime_error = parsetime(updvals[0], &ds_tv))) {
+                rrd_set_error("ds time: %s: %s", updvals[0], parsetime_error );
+               free(step_start);
+               break;
+           }
+           if (ds_tv.type == RELATIVE_TO_END_TIME ||
+               ds_tv.type == RELATIVE_TO_START_TIME) {
+               rrd_set_error("specifying time relative to the 'start' "
+                              "or 'end' makes no sense here: %s",
+                             updvals[0]);
+               free(step_start);
+               break;
+           }
+
+           current_time = mktime(&ds_tv.tm) + ds_tv.offset;
+       } else if (strcmp(updvals[0],"N")==0){
+           current_time = time(NULL);
+       } else {
+           current_time = atol(updvals[0]);
+       }
+       
+       if(current_time <= rrd.live_head->last_up){
+           rrd_set_error("illegal attempt to update using time %ld when "
+                         "last update time is %ld (minimum one second step)",
+                         current_time, rrd.live_head->last_up);
+           free(step_start);
+           break;
+       }
+       
+       
+       /* seek to the beginning of the rra's */
+       if (rra_current != rra_begin) {
+           if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
+               rrd_set_error("seek error in rrd");
+               free(step_start);
+               break;
+           }
+           rra_current = rra_begin;
+       }
+       rra_start = rra_begin;
+
+       /* when was the current pdp started */
+       proc_pdp_age = rrd.live_head->last_up % rrd.stat_head->pdp_step;
+       proc_pdp_st = rrd.live_head->last_up - proc_pdp_age;
+
+       /* when did the last pdp_st occur */
+       occu_pdp_age = current_time % rrd.stat_head->pdp_step;
+       occu_pdp_st = current_time - occu_pdp_age;
+       interval = current_time - rrd.live_head->last_up;
+    
+       if (occu_pdp_st > proc_pdp_st){
+           /* OK we passed the pdp_st moment*/
+           pre_int =  occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
+                                                             * occurred before the latest
+                                                             * pdp_st moment*/
+           post_int = occu_pdp_age;                         /* how much after it */
+       } else {
+           pre_int = interval;
+           post_int = 0;
+       }
+
+#ifdef DEBUG
+       printf(
+              "proc_pdp_age %lu\t"
+              "proc_pdp_st %lu\t" 
+              "occu_pfp_age %lu\t" 
+              "occu_pdp_st %lu\t"
+              "int %lu\t"
+              "pre_int %lu\t"
+              "post_int %lu\n", proc_pdp_age, proc_pdp_st, 
+               occu_pdp_age, occu_pdp_st,
+              interval, pre_int, post_int);
+#endif
+    
+       /* process the data sources and update the pdp_prep 
+        * area accordingly */
+       for(i=0;i<rrd.stat_head->ds_cnt;i++){
+           enum dst_en dst_idx;
+           dst_idx= dst_conv(rrd.ds_def[i].dst);
+           if((updvals[i+1][0] != 'U') &&
+              rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
+              double rate = DNAN;
+              /* the data source type defines how to process the data */
+               /* pdp_temp contains rate * time ... eg the bytes
+                * transferred during the interval. Doing it this way saves
+                * a lot of math operations */
+               
+
+               switch(dst_idx){
+               case DST_COUNTER:
+               case DST_DERIVE:
+                   if(rrd.pdp_prep[i].last_ds[0] != 'U'){
+                      pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
+                      if(dst_idx == DST_COUNTER) {
+                         /* simple overflow catcher sugestet by andres kroonmaa */
+                         /* this will fail terribly for non 32 or 64 bit counters ... */
+                         /* are there any others in SNMP land ? */
+                         if (pdp_new[i] < (double)0.0 ) 
+                           pdp_new[i] += (double)4294967296.0 ;  /* 2^32 */
+                         if (pdp_new[i] < (double)0.0 ) 
+                           pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
+                      }
+                      rate = pdp_new[i] / interval;
+                   }
+                  else {
+                    pdp_new[i]= DNAN;          
+                  }
+                  break;
+               case DST_ABSOLUTE:
+                   pdp_new[i]= atof(updvals[i+1]);
+                   rate = pdp_new[i] / interval;                 
+                   break;
+               case DST_GAUGE:
+                   pdp_new[i] = atof(updvals[i+1]) * interval;
+                   rate = pdp_new[i] / interval;                  
+                   break;
+               default:
+                   rrd_set_error("rrd contains unknown DS type : '%s'",
+                                 rrd.ds_def[i].dst);
+                   break;
+               }
+               /* break out of this for loop if the error string is set */
+               if (rrd_test_error()){
+                   break;
+               }
+              /* make sure pdp_temp is neither too large or too small
+               * if any of these occur it becomes unknown ...
+               * sorry folks ... */
+              if ( ! isnan(rate) && 
+                   (( ! isnan(rrd.ds_def[i].par[DS_max_val].u_val) &&
+                        rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||     
+                   ( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
+                       rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
+                 pdp_new[i] = DNAN;
+              }               
+           } else {
+               /* no news is news all the same */
+               pdp_new[i] = DNAN;
+           }
+           
+           /* make a copy of the command line argument for the next run */
+#ifdef DEBUG
+           fprintf(stderr,
+                   "prep ds[%lu]\t"
+                   "last_arg '%s'\t"
+                   "this_arg '%s'\t"
+                   "pdp_new %10.2f\n",
+                   i,
+                   rrd.pdp_prep[i].last_ds,
+                   updvals[i+1], pdp_new[i]);
+#endif
+           if(dst_idx == DST_COUNTER || dst_idx == DST_DERIVE){
+               strncpy(rrd.pdp_prep[i].last_ds,
+                       updvals[i+1],LAST_DS_LEN-1);
+               rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
+           }
+       }
+       /* break out of the argument parsing loop if the error_string is set */
+       if (rrd_test_error()){
+           free(step_start);
+           break;
+       }
+       /* has a pdp_st moment occurred since the last run ? */
+
+       if (proc_pdp_st == occu_pdp_st){
+           /* no we have not passed a pdp_st moment. therefore update is simple */
+
+           for(i=0;i<rrd.stat_head->ds_cnt;i++){
+               if(isnan(pdp_new[i]))
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
+               else
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
+#ifdef DEBUG
+               fprintf(stderr,
+                       "NO PDP  ds[%lu]\t"
+                       "value %10.2f\t"
+                       "unkn_sec %5lu\n",
+                       i,
+                       rrd.pdp_prep[i].scratch[PDP_val].u_val,
+                       rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+           }   
+       } else {
+           /* an pdp_st has occurred. */
+
+           /* in pdp_prep[].scratch[PDP_val].u_val we have collected rate*seconds which 
+            * occurred up to the last run.        
+           pdp_new[] contains rate*seconds from the latest run.
+           pdp_temp[] will contain the rate for cdp */
+
+
+           for(i=0;i<rrd.stat_head->ds_cnt;i++){
+               /* update pdp_prep to the current pdp_st */
+               if(isnan(pdp_new[i]))
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += pre_int;
+               else
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val += 
+                       pdp_new[i]/(double)interval*(double)pre_int;
+
+               /* if too much of the pdp_prep is unknown we dump it */
+               if ((rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt 
+                    > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
+                   (occu_pdp_st-proc_pdp_st <= 
+                    rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
+                   pdp_temp[i] = DNAN;
+               } else {
+                   pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
+                       / (double)( occu_pdp_st
+                                  - proc_pdp_st
+                                  - rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+               }
+               /* make pdp_prep ready for the next run */
+               if(isnan(pdp_new[i])){
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int;
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val = 0.0;
+               } else {
+                   rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
+                   rrd.pdp_prep[i].scratch[PDP_val].u_val = 
+                       pdp_new[i]/(double)interval*(double)post_int;
+               }
+
+#ifdef DEBUG
+               fprintf(stderr,
+                       "PDP UPD ds[%lu]\t"
+                       "pdp_temp %10.2f\t"
+                       "new_prep %10.2f\t"
+                       "new_unkn_sec %5lu\n",
+                       i, pdp_temp[i],
+                       rrd.pdp_prep[i].scratch[PDP_val].u_val,
+                       rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+           }
+
+               /* compute the number of elapsed pdp_st moments */
+               elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step;
+#ifdef DEBUG
+               fprintf(stderr,"elapsed PDP steps: %lu\n", elapsed_pdp_st);
+#endif
+               if (rra_step_cnt == NULL)
+               {
+                  rra_step_cnt = (unsigned long *) 
+                         malloc((rrd.stat_head->rra_cnt)* sizeof(unsigned long));
+               }
+
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++)
+               {
+               current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+               start_pdp_offset = rrd.rra_def[i].pdp_cnt -
+                  (proc_pdp_st / rrd.stat_head -> pdp_step) % rrd.rra_def[i].pdp_cnt;
+        if (start_pdp_offset <= elapsed_pdp_st) {
+           rra_step_cnt[i] = (elapsed_pdp_st - start_pdp_offset) / 
+                     rrd.rra_def[i].pdp_cnt + 1;
+           } else {
+                  rra_step_cnt[i] = 0;
+               }
+
+               if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL) 
+               {
+                  /* If this is a bulk update, we need to skip ahead in the seasonal
+                       * arrays so that they will be correct for the next observed value;
+                       * note that for the bulk update itself, no update will occur to
+                       * DEVSEASONAL or SEASONAL; futhermore, HWPREDICT and DEVPREDICT will
+                       * be set to DNAN. */
+           if (rra_step_cnt[i] > 2) 
+                  {
+                         /* skip update by resetting rra_step_cnt[i],
+                          * note that this is not data source specific; this is due
+                          * to the bulk update, not a DNAN value for the specific data
+                          * source. */
+                         rra_step_cnt[i] = 0;
+              lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st, 
+                            &last_seasonal_coef);
+                     lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st + 1,
+                            &seasonal_coef);
+                  }
+               
+                 /* periodically run a smoother for seasonal effects */
+                 /* Need to use first cdp parameter buffer to track
+                  * burnin (burnin requires a specific smoothing schedule).
+                  * The CDP_init_seasonal parameter is really an RRA level,
+                  * not a data source within RRA level parameter, but the rra_def
+                  * is read only for rrd_update (not flushed to disk). */
+                 iii = i*(rrd.stat_head -> ds_cnt);
+                 if (rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt 
+                         <= BURNIN_CYCLES)
+                 {
+                    if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st 
+                                > rrd.rra_def[i].row_cnt - 1) {
+                          /* mark off one of the burnin cycles */
+                          ++(rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt);
+                      schedule_smooth = 1;
+                        }  
+                 } else {
+                        /* someone has no doubt invented a trick to deal with this
+                         * wrap around, but at least this code is clear. */
+                        if (rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt >
+                            rrd.rra_ptr[i].cur_row)
+                        {
+                                /* here elapsed_pdp_st = rra_step_cnt[i] because of 1-1
+                                 * mapping between PDP and CDP */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
+#ifdef DEBUG
+                                       fprintf(stderr,
+                                       "schedule_smooth 1: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+                                       schedule_smooth = 1;
+                                }
+             } else {
+                                /* can't rely on negative numbers because we are working with
+                                 * unsigned values */
+                                /* Don't need modulus here. If we've wrapped more than once, only
+                                 * one smooth is executed at the end. */
+                                if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st >= rrd.rra_def[i].row_cnt
+                                       && rrd.rra_ptr[i].cur_row + elapsed_pdp_st - rrd.rra_def[i].row_cnt
+                                       >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+                                {
+#ifdef DEBUG
+                                       fprintf(stderr,
+                                       "schedule_smooth 2: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+                    rrd.rra_ptr[i].cur_row, elapsed_pdp_st, 
+                                       rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+                                       schedule_smooth = 1;
+                                }
+                        }
+                 }
+
+             rra_current = ftell(rrd_file); 
+               } /* if cf is DEVSEASONAL or SEASONAL */
+
+        if (rrd_test_error()) break;
+
+                   /* update CDP_PREP areas */
+                   /* loop over data soures within each RRA */
+                   for(ii = 0;
+                       ii < rrd.stat_head->ds_cnt;
+                       ii++)
+                       {
+                       
+                       /* iii indexes the CDP prep area for this data source within the RRA */
+                       iii=i*rrd.stat_head->ds_cnt+ii;
+
+                       if (rrd.rra_def[i].pdp_cnt > 1) {
+                         
+                          if (rra_step_cnt[i] > 0) {
+                          /* If we are in this block, as least 1 CDP value will be written to
+                               * disk, this is the CDP_primary_val entry. If more than 1 value needs
+                               * to be written, then the "fill in" value is the CDP_secondary_val
+                               * entry. */
+                                 if (isnan(pdp_temp[ii]))
+                  {
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += start_pdp_offset;
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+                                 } else {
+                                        /* CDP_secondary value is the RRA "fill in" value for intermediary
+                                         * CDP data entries. No matter the CF, the value is the same because
+                                         * the average, max, min, and last of a list of identical values is
+                                         * the same, namely, the value itself. */
+                                        rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = pdp_temp[ii];
+                                 }
+                     
+                                 if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
+                                     > rrd.rra_def[i].pdp_cnt*
+                                     rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
+                                 {
+                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                        /* initialize carry over */
+                                        if (current_cf == CF_AVERAGE) {
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                        } else {
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
+                                 } else {
+                                        rrd_value_t cum_val, cur_val; 
+                                    switch (current_cf) {
+                                               case CF_AVERAGE:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, 0.0);
+                                                 cur_val = IFDNAN(pdp_temp[ii],0.0);
+                          rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val =
+                                              (cum_val + cur_val) /
+                                          (rrd.rra_def[i].pdp_cnt
+                                              -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+                                                  /* initialize carry over value */
+                                                  if (isnan(pdp_temp[ii])) { 
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+                                                  } else {
+                                                         rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                                ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+                                                  }
+                                                  break;
+                                               case CF_MAXIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, -DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],-DINF);
+#ifdef DEBUG
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
+#endif
+                                                 if (cur_val > cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_MINIMUM:
+                                                 cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, DINF);
+                                                 cur_val = IFDNAN(pdp_temp[ii],DINF);
+#ifdef DEBUG
+                                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+                                                         isnan(pdp_temp[ii])) {
+                                                    fprintf(stderr,
+                                                               "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+                                                               i,ii);
+                                                        exit(-1);
+                                                 }
+#endif
+                                                 if (cur_val < cum_val)
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+                                                 else
+                                                        rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+                                                 /* initialize carry over value */
+                                                 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                                 break;
+                                               case CF_LAST:
+                                               default:
+                                                  rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = pdp_temp[ii];
+                                                  /* initialize carry over value */
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 } /* endif meets xff value requirement for a valid value */
+                                 /* initialize carry over CDP_unkn_pdp_cnt, this must after CDP_primary_val
+                                  * is set because CDP_unkn_pdp_cnt is required to compute that value. */
+                                 if (isnan(pdp_temp[ii]))
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 
+                                               (elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt;
+                                 else
+                                        rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
+               } else  /* rra_step_cnt[i]  == 0 */
+                          {
+#ifdef DEBUG
+                                 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)) {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, DNAN\n",
+                                        i,ii);
+                                 } else {
+                                 fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, %10.2f\n",
+                                        i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+                                 }
+#endif
+                                 if (isnan(pdp_temp[ii])) {
+                                rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += elapsed_pdp_st;
+                                 } else if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val))
+                                 {
+                                        if (current_cf == CF_AVERAGE) {
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                        } else {
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                        }
+#ifdef DEBUG
+                                        fprintf(stderr,"Initialize CDP_val for RRA %lu DS %lu: %10.2f\n",
+                                           i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+#endif
+                                 } else {
+                                        switch (current_cf) {
+                                        case CF_AVERAGE:
+                                           rrd.cdp_prep[iii].scratch[CDP_val].u_val += pdp_temp[ii] *
+                                                  elapsed_pdp_st;
+                                               break;
+                                        case CF_MINIMUM:
+                                               if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_MAXIMUM:
+                                               if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+                                                  rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break; 
+                                        case CF_LAST:
+                                        default:
+                                               rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+                                               break;
+                                        }
+                                 }
+                          }
+                       } else { /* rrd.rra_def[i].pdp_cnt == 1 */
+                          if (elapsed_pdp_st > 2)
+                          {
+                                  switch (current_cf) {
+                                  case CF_AVERAGE:
+                                  default:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val=pdp_temp[ii];
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val=pdp_temp[ii];
+                                         break;
+                   case CF_SEASONAL:
+                                  case CF_DEVSEASONAL:
+                                         /* need to update cached seasonal values, so they are consistent
+                                          * with the bulk update */
+                      /* WARNING: code relies on the fact that CDP_hw_last_seasonal and
+                                          * CDP_last_deviation are the same. */
+                      rrd.cdp_prep[iii].scratch[CDP_hw_last_seasonal].u_val =
+                                                last_seasonal_coef[ii];
+                                         rrd.cdp_prep[iii].scratch[CDP_hw_seasonal].u_val =
+                                                seasonal_coef[ii];
+                                         break;
+                   case CF_HWPREDICT:
+                                         /* need to update the null_count and last_null_count.
+                                          * even do this for non-DNAN pdp_temp because the
+                                          * algorithm is not learning from batch updates. */
+                                         rrd.cdp_prep[iii].scratch[CDP_null_count].u_cnt += 
+                                                elapsed_pdp_st;
+                                         rrd.cdp_prep[iii].scratch[CDP_last_null_count].u_cnt += 
+                                                elapsed_pdp_st - 1;
+                                         /* fall through */
+                                  case CF_DEVPREDICT:
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+                                         break;
+                   case CF_FAILURES:
+                                         /* do not count missed bulk values as failures */
+                                 rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = 0;
+                                 rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = 0;
+                                         /* need to reset violations buffer.
+                                          * could do this more carefully, but for now, just
+                                          * assume a bulk update wipes away all violations. */
+                      erase_violations(&rrd, iii, i);
+                                         break;
+                                  }
+                          } 
+                       } /* endif rrd.rra_def[i].pdp_cnt == 1 */
+
+                       if (rrd_test_error()) break;
+
+                       } /* endif data sources loop */
+        } /* end RRA Loop */
+
+               /* this loop is only entered if elapsed_pdp_st < 3 */
+               for (j = elapsed_pdp_st, scratch_idx = CDP_primary_val; 
+                        j > 0 && j < 3; j--, scratch_idx = CDP_secondary_val)
+               {
+              for(i = 0, rra_start = rra_begin;
+                  i < rrd.stat_head->rra_cnt;
+              rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+                  i++)
+                  {
+                         if (rrd.rra_def[i].pdp_cnt > 1) continue;
+
+                 current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+                         if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL)
+                         {
+                        lookup_seasonal(&rrd,i,rra_start,rrd_file,
+                                   elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2),
+                               &seasonal_coef);
+                         }
+                         if (rrd_test_error()) break;
+                     /* loop over data soures within each RRA */
+                     for(ii = 0;
+                         ii < rrd.stat_head->ds_cnt;
+                         ii++)
+                         {
+                            update_aberrant_CF(&rrd,pdp_temp[ii],current_cf,
+                                       i*(rrd.stat_head->ds_cnt) + ii,i,ii,
+                                   scratch_idx, seasonal_coef);
+                         }
+           } /* end RRA Loop */
+                  if (rrd_test_error()) break;
+           } /* end elapsed_pdp_st loop */
+
+               if (rrd_test_error()) break;
+
+               /* Ready to write to disk */
+               /* Move sequentially through the file, writing one RRA at a time.
+                * Note this architecture divorces the computation of CDP with
+                * flushing updated RRA entries to disk. */
+           for(i = 0, rra_start = rra_begin;
+               i < rrd.stat_head->rra_cnt;
+           rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+               i++) {
+               /* is there anything to write for this RRA? If not, continue. */
+        if (rra_step_cnt[i] == 0) continue;
+
+               /* write the first row */
+#ifdef DEBUG
+        fprintf(stderr,"  -- RRA Preseek %ld\n",ftell(rrd_file));
+#endif
+           rrd.rra_ptr[i].cur_row++;
+           if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
+                  rrd.rra_ptr[i].cur_row = 0; /* wrap around */
+               /* positition on the first row */
+               rra_pos_tmp = rra_start +
+                  (rrd.stat_head->ds_cnt)*(rrd.rra_ptr[i].cur_row)*sizeof(rrd_value_t);
+               if(rra_pos_tmp != rra_current) {
+                  if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
+                     rrd_set_error("seek error in rrd");
+                     break;
+                  }
+                  rra_current = rra_pos_tmp;
+               }
+#ifdef DEBUG
+           fprintf(stderr,"  -- RRA Postseek %ld\n",ftell(rrd_file));
+#endif
+               scratch_idx = CDP_primary_val;
+               write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file);
+               if (rrd_test_error()) break;
+
+               /* write other rows of the bulk update, if any */
+               scratch_idx = CDP_secondary_val;
+               for ( ; rra_step_cnt[i] > 1; 
+                    rra_step_cnt[i]--, rrd.rra_ptr[i].cur_row++)
+               {
+                  if (rrd.rra_ptr[i].cur_row == rrd.rra_def[i].row_cnt)
+                  {
+#ifdef DEBUG
+              fprintf(stderr,"Wraparound for RRA %s, %lu updates left\n",
+                         rrd.rra_def[i].cf_nam, rra_step_cnt[i] - 1);
+#endif
+                         /* wrap */
+                         rrd.rra_ptr[i].cur_row = 0;
+                         /* seek back to beginning of current rra */
+                     if (fseek(rrd_file, rra_start, SEEK_SET) != 0)
+                         {
+                        rrd_set_error("seek error in rrd");
+                        break;
+                         }
+#ifdef DEBUG
+                 fprintf(stderr,"  -- Wraparound Postseek %ld\n",ftell(rrd_file));
+#endif
+                         rra_current = rra_start;
+                  }
+                  write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file);
+               }
+               
+               if (rrd_test_error())
+                 break;
+               } /* RRA LOOP */
+
+           /* break out of the argument parsing loop if error_string is set */
+           if (rrd_test_error()){
+                  free(step_start);
+                  break;
+           } 
+           
+       } /* endif a pdp_st has occurred */ 
+       rrd.live_head->last_up = current_time;
+       free(step_start);
+    } /* function argument loop */
+
+    if (seasonal_coef != NULL) free(seasonal_coef);
+    if (last_seasonal_coef != NULL) free(last_seasonal_coef);
+       if (rra_step_cnt != NULL) free(rra_step_cnt);
+
+    /* if we got here and if there is an error and if the file has not been
+     * written to, then close things up and return. */
+    if (rrd_test_error()) {
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    /* aargh ... that was tough ... so many loops ... anyway, its done.
+     * we just need to write back the live header portion now*/
+
+    if (fseek(rrd_file, (sizeof(stat_head_t)
+                        + sizeof(ds_def_t)*rrd.stat_head->ds_cnt 
+                        + sizeof(rra_def_t)*rrd.stat_head->rra_cnt),
+             SEEK_SET) != 0) {
+       rrd_set_error("seek rrd for live header writeback");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.live_head,
+              sizeof(live_head_t), 1, rrd_file) != 1){
+       rrd_set_error("fwrite live_head to rrd");
+       free(updvals);
+       rrd_free(&rrd);
+       free(tmpl_idx);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.pdp_prep,
+              sizeof(pdp_prep_t),
+              rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
+       rrd_set_error("ftwrite pdp_prep to rrd");
+       free(updvals);
+       rrd_free(&rrd);
+       free(tmpl_idx);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.cdp_prep,
+              sizeof(cdp_prep_t),
+              rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file) 
+       != rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
+
+       rrd_set_error("ftwrite cdp_prep to rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    if(fwrite( rrd.rra_ptr,
+              sizeof(rra_ptr_t), 
+              rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
+       rrd_set_error("fwrite rra_ptr to rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+        fclose(rrd_file);
+       return(-1);
+    }
+
+    /* OK now close the files and free the memory */
+    if(fclose(rrd_file) != 0){
+       rrd_set_error("closing rrd");
+       free(updvals);
+       free(tmpl_idx);
+       rrd_free(&rrd);
+       free(pdp_temp);
+       free(pdp_new);
+       return(-1);
+    }
+
+    /* calling the smoothing code here guarantees at most
+        * one smoothing operation per rrd_update call. Unfortunately,
+        * it is possible with bulk updates, or a long-delayed update
+        * for smoothing to occur off-schedule. This really isn't
+        * critical except during the burning cycles. */
+       if (schedule_smooth)
+       {
+#ifndef WIN32
+         rrd_file = fopen(argv[optind],"r+");
+#else
+         rrd_file = fopen(argv[optind],"rb+");
+#endif
+         rra_start = rra_begin;
+         for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
+         {
+           if (cf_conv(rrd.rra_def[i].cf_nam) == CF_DEVSEASONAL ||
+               cf_conv(rrd.rra_def[i].cf_nam) == CF_SEASONAL)
+           {
+#ifdef DEBUG
+             fprintf(stderr,"Running smoother for rra %ld\n",i);
+#endif
+             apply_smoother(&rrd,i,rra_start,rrd_file);
+             if (rrd_test_error())
+               break;
+           }
+           rra_start += rrd.rra_def[i].row_cnt
+             *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
+         }
+         fclose(rrd_file);
+       }
+    rrd_free(&rrd);
+    free(updvals);
+    free(tmpl_idx);
+    free(pdp_new);
+    free(pdp_temp);
+    return(0);
+}
+
+/*
+ * get exclusive lock to whole file.
+ * lock gets removed when we close the file
+ *
+ * returns 0 on success
+ */
+int
+LockRRD(FILE *rrdfile)
+{
+    int        rrd_fd;         /* File descriptor for RRD */
+    int                        stat;
+
+    rrd_fd = fileno(rrdfile);
+
+       {
+#ifndef WIN32    
+               struct flock    lock;
+    lock.l_type = F_WRLCK;    /* exclusive write lock */
+    lock.l_len = 0;          /* whole file */
+    lock.l_start = 0;        /* start of file */
+    lock.l_whence = SEEK_SET;   /* end of file */
+
+    stat = fcntl(rrd_fd, F_SETLK, &lock);
+#else
+               struct _stat st;
+
+               if ( _fstat( rrd_fd, &st ) == 0 ) {
+                       stat = _locking ( rrd_fd, _LK_NBLCK, st.st_size );
+               } else {
+                       stat = -1;
+               }
+#endif
+       }
+
+    return(stat);
+}
+
+
+void
+write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+   unsigned short CDP_scratch_idx, FILE *rrd_file)
+{
+   unsigned long ds_idx, cdp_idx;
+
+   for (ds_idx = 0; ds_idx < rrd -> stat_head -> ds_cnt; ds_idx++)
+   {
+      /* compute the cdp index */
+      cdp_idx =rra_idx * (rrd -> stat_head->ds_cnt) + ds_idx;
+#ifdef DEBUG
+         fprintf(stderr,"  -- RRA WRITE VALUE %e, at %ld CF:%s\n",
+            rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,ftell(rrd_file),
+            rrd -> rra_def[rra_idx].cf_nam);
+#endif
+
+         if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+                sizeof(rrd_value_t),1,rrd_file) != 1)
+         { 
+            rrd_set_error("writing rrd");
+                return;
+         }
+         *rra_current += sizeof(rrd_value_t);
+       }
+}