]> git.ipfire.org Git - thirdparty/rrdtool-1.x.git/commitdiff
Implement --template option to rrdtool create
authorPeter Stamfest <peter@stamfest.at>
Tue, 2 Sep 2014 20:44:18 +0000 (22:44 +0200)
committerPeter Stamfest <peter@stamfest.at>
Tue, 2 Sep 2014 21:56:23 +0000 (23:56 +0200)
src/rrd.h
src/rrd_client.c
src/rrd_client.h
src/rrd_create.c
src/rrd_daemon.c
tests/Makefile.am
tests/create-from-template-1 [new file with mode: 0755]

index 5eb52355ad0657ce5ef2468cd9df50cdfbc4f03b..c69659e78a8e243f55eeff70ae36455f7381fd07 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -245,6 +245,7 @@ struct rrd_t;
     time_t last_up,
     int no_overwrite,
     const char **sources,
+    const char *template,
     int argc,
     const char **argv);
     rrd_info_t *rrd_info_r(
index 9c1ae3d3e43e3af5da72a1fcab863d3d263e10b8..58a3d5b8e42f2de63b765b692b846478433fd823 100644 (file)
@@ -1203,7 +1203,7 @@ int rrdc_create (const char *filename, /* {{{ */
     int argc,
     const char **argv)
 {
-    return rrdc_create_r2(filename, pdp_step, last_up, no_overwrite, NULL, argc, argv);
+    return rrdc_create_r2(filename, pdp_step, last_up, no_overwrite, NULL, NULL, argc, argv);
 }
 
 int rrdc_create_r2(const char *filename, /* {{{ */
@@ -1211,6 +1211,7 @@ int rrdc_create_r2(const char *filename, /* {{{ */
     time_t last_up,
     int no_overwrite,
     const char **sources,
+    const char *template,
     int argc,
     const char **argv)
 {
@@ -1262,6 +1263,11 @@ int rrdc_create_r2(const char *filename, /* {{{ */
     }
   }
   
+  if (template != NULL) {
+    buffer_add_string ("-t", &buffer_ptr, &buffer_free);
+    buffer_add_string (template, &buffer_ptr, &buffer_free);
+  }
+  
   if (status != 0)
   {
     mutex_unlock (&lock);
index 4984bc5936c2d6391f259f50096dbd9ba3bbda28..18b36e726e6554c0ad82ad11b45838feef624f03 100644 (file)
@@ -65,6 +65,7 @@ int rrdc_create_r2 (const char *filename,
     time_t last_up,
     int no_overwrite,
     const char **sources,
+    const char *template,
     int argc,
     const char **argv);
 
index cfd03e0dc74de6c2796a1b3fdb8fba62b26213e3..5510b173377bf18ceb58b6b009fdb6f7e74a4415 100644 (file)
@@ -56,13 +56,14 @@ int rrd_create(
         {"step", required_argument, 0, 's'},
         {"daemon", required_argument, 0, 'd'},
         {"source", required_argument, 0, 'r'},
+        {"template", required_argument, 0, 't'},
         {"no-overwrite", no_argument, 0, 'O'},
         {0, 0, 0, 0}
     };
     int       option_index = 0;
     int       opt;
     time_t    last_up = -1;
-    unsigned long pdp_step = 300;
+    unsigned long pdp_step = 0;
     rrd_time_value_t last_up_tv;
     const char *parsetime_error = NULL;
     int       rc = -1;
@@ -70,6 +71,7 @@ int rrd_create(
     int       opt_no_overwrite = 0;
     GList * sources = NULL;
     const char **sources_array = NULL;
+    const char *template = NULL;
     
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -162,6 +164,22 @@ int rrd_create(
 
             break;
         }
+        case 't': {
+            if (template != NULL) {
+                rrd_set_error("template already set");
+                rc = -1;
+                goto done;
+            }
+            char * optcpy = strdup(optarg);
+            if (optcpy == NULL) {
+                rrd_set_error("Cannot allocate string");
+                rc = -1;
+                goto done;
+            }
+
+            template = optcpy;
+            break;
+        }
         case '?':
             if (optopt != 0)
                 rrd_set_error("unknown option '%c'", optopt);
@@ -194,14 +212,14 @@ int rrd_create(
     if (rrdc_is_connected (opt_daemon)) {
         rc = rrdc_create_r2(argv[optind],
                       pdp_step, last_up, opt_no_overwrite, 
-                      sources_array,
+                      sources_array, template,
                       argc - optind - 1, (const char **) (argv + optind + 1));
-       } else {
+    } else {
         rc = rrd_create_r2(argv[optind],
                       pdp_step, last_up, opt_no_overwrite,
-                      sources_array,
+                      sources_array, template,
                       argc - optind - 1, (const char **) (argv + optind + 1));
-       }
+    }
 done:
     if (sources_array != NULL) {
         free(sources_array);
@@ -212,6 +230,10 @@ done:
         g_list_free_full(sources, free);
         sources = NULL;
     }
+    if (template != NULL) {
+        free(template);
+        template = NULL;
+    }
     return rc;
 }
 
@@ -646,7 +668,7 @@ int rrd_create_r(
     int argc,
     const char **argv)
 {
-       return rrd_create_r2(filename,pdp_step,last_up,0, NULL, argc,argv);
+       return rrd_create_r2(filename,pdp_step,last_up,0, NULL, NULL, argc,argv);
 }
 
 
@@ -668,6 +690,7 @@ int rrd_create_r2(
     time_t last_up,
     int no_overwrite,
     const char **sources,
+    const char *template,
     int argc,
     const char **argv)
 {
@@ -716,6 +739,49 @@ int rrd_create_r2(
     rrd.rra_def = NULL;
 
     rrd.live_head->last_up = last_up > 0 ? last_up : time(NULL) - 10;
+    int last_up_set = last_up > 0;
+    
+    time_t    template_latest_last_up = 0;
+
+    if (template) {
+        rrd_t trrd;
+
+        rrd_init(&trrd);
+        rrd_file_t *tf = rrd_open(template, &trrd, RRD_READONLY | RRD_READAHEAD | RRD_READVALUES);
+        if (tf == NULL) {
+            rrd_set_error("Cannot open template RRD %s", template);
+            goto done;
+        }
+        
+        /* copy step time if not yet set */
+        
+        if (rrd.stat_head->pdp_step == 0) {
+            rrd.stat_head->pdp_step = trrd.stat_head->pdp_step;
+        }
+
+        /* copy DS and RRA definitions from template... */
+
+        rrd.stat_head->ds_cnt = trrd.stat_head->ds_cnt;
+        rrd.ds_def = malloc(sizeof(ds_def_t) * trrd.stat_head->ds_cnt);
+        rrd.stat_head->rra_cnt = trrd.stat_head->rra_cnt;
+        rrd.rra_def = malloc(sizeof(rra_def_t) * trrd.stat_head->rra_cnt);
+        
+        if (rrd.ds_def == NULL || rrd.rra_def == NULL) {
+            rrd_set_error("cannot allocate memory");
+            goto done;
+        }
+
+        template_latest_last_up = trrd.live_head->last_up;
+
+        memcpy(rrd.ds_def,  trrd.ds_def,  sizeof(ds_def_t)  * trrd.stat_head->ds_cnt);
+        memcpy(rrd.rra_def, trrd.rra_def, sizeof(rra_def_t) * trrd.stat_head->rra_cnt);
+
+        rrd_close(tf);
+    }
+    
+    if (rrd.stat_head->pdp_step == 0) {
+        rrd.stat_head->pdp_step = 300;
+    }
 
     /* optind points to the first non-option command line arg,
      * in this case, the file name. */
@@ -800,7 +866,13 @@ int rrd_create_r2(
         rrd_set_error("you must define at least one Data Source");
        goto done;
     }
-    
+
+    /* set last update time from template if not set explicitly and there 
+     * are no sources given */
+    if (!last_up_set && template_latest_last_up > 0 && sources == NULL) {
+        rrd.live_head->last_up = template_latest_last_up;
+    }
+
     rc = rrd_init_data(&rrd);
     if (rc != 0) goto done;
 
@@ -832,11 +904,12 @@ int rrd_create_r2(
     
         if (last_up == -1) {
             rrd.live_head->last_up = sources_latest_last_up;
+            last_up_set = 1;
             reset_pdp_prep(&rrd);
         }
         rrd_prefill_data(&rrd, sources_rrd_files, mappings, mappings_cnt);
     }
-    
+
     rc = write_rrd(filename, &rrd);
     
 done:
index 3fefd406c97a72f9bd1a6e0030035ae4b402bb8f..81535ef59cbc3eaf44b966f9fcab6bee6f4f4610 100644 (file)
@@ -1915,6 +1915,7 @@ static int handle_request_create (HANDLER_PROTO) /* {{{ */
   char *av[128];
   char **sources = NULL;
   int sources_length = 0;
+  char *template = NULL;
   int status;
   unsigned long step = 300;
   time_t last_up = time(NULL)-10;
@@ -1991,6 +1992,15 @@ static int handle_request_create (HANDLER_PROTO) /* {{{ */
       sources[sources_length + 1] = NULL;
       continue;
     }
+    if( ! strncmp(tok,"-t",2) ) {
+      status = buffer_get_field(&buffer, &buffer_size, &tok );
+      if (status != 0) {
+          rc = syntax_error(sock,cmd);
+          goto done;
+      }
+      template = tok;
+      continue;
+    }
     if( ! strncmp(tok,"-O",2) ) {
       no_overwrite = 1;
       continue;
@@ -2011,7 +2021,7 @@ static int handle_request_create (HANDLER_PROTO) /* {{{ */
 
   rrd_clear_error ();
   pthread_mutex_lock(&rrdfilecreate_lock);
-  status = rrd_create_r2(file,step,last_up,no_overwrite, (const char**) sources, ac,(const char **)av);
+  status = rrd_create_r2(file,step,last_up,no_overwrite, (const char**) sources, template, ac,(const char **)av);
   pthread_mutex_unlock(&rrdfilecreate_lock);
 
   if(!status) {
index 1d8136bdb75fbef3bdf188c0e8630af5098d1afd..d6717f23b2d6a40196ba73cdfe41317708fa205b 100644 (file)
@@ -3,7 +3,8 @@ TESTS = modify1 modify2 modify3 modify4 modify5 \
        rrdcreate \
        dump-restore \
        create-with-source-1 create-with-source-2 create-with-source-3 \
-       create-with-source-4 create-with-source-and-mapping-1
+       create-with-source-4 create-with-source-and-mapping-1 \
+       create-from-template-1
 EXTRA_DIST = Makefile.am \
        alltests functions \
        modify1 modify-test1.create.dump modify-test1.mod1.dump \
diff --git a/tests/create-from-template-1 b/tests/create-from-template-1
new file mode 100755 (executable)
index 0000000..a247510
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+. $(dirname $0)/functions
+
+BASE=$BASEDIR/$(basename $0)-test
+PREFIX=$BUILDDIR/$(basename $0)-test
+
+# currently, we do not properly copy cdp and pdp information, so for
+# comparison of RRD dumps, we just filter out those parts we do not
+# expect to match anyway...
+function cdp_prep_filter {
+       perl -n -e '$a=join("",<>); $a=~s,<(cdp_prep).*?</\1>,,msg ; print $a'
+}
+
+function pdp_prep_filter {
+       perl -n -e '$a=join("",<>); $a=~s,<(last_ds|value|unknown_sec).*?</\1>,,msg ; print $a'
+}
+
+function data_filter_by_time {
+        START="$1"
+        END="$2"
+#                       <!-- 2011-03-13 08:46:00 CET / 1300002360 --> <row><v>7.9666666667e+02</v>
+
+       perl -n -e 'print unless (m,/ (\d+) -->.*<row>, && $1 >= '$START' && $1 <= '$END')'
+}
+
+FIRST=1300000000
+ST=$FIRST
+
+RRA="RRA:AVERAGE:0.5:1:100 RRA:AVERAGE:0.5:5:2 RRA:MIN:0.5:5:2 RRA:MAX:0.5:5:2 RRA:LAST:0.5:5:2"
+
+rm -f ${PREFIX}*.rrd ${PREFIX}*.xml
+$RRDTOOL create ${PREFIX}ax1.rrd --start $(($ST-1)) --step 60 DS:a:GAUGE:120:0:U DS:b:COUNTER:120:0:U $RRA
+report createax1
+
+cp ${PREFIX}ax1.rrd ${PREFIX}ax1-copy.rrd
+
+UPDATEAx1=
+V=10
+for A in $(seq $ST 60 $(($ST + 3000)) ) ; do
+       UPDATEAx1="$UPDATEAx1 $A:$V"
+       V=$(($V + 20))
+       ST=$A
+done
+
+$RRDTOOL create ${PREFIX}ay1.rrd --start $ST --step 60 DS:a:GAUGE:120:0:U DS:b:COUNTER:120:0:U $RRA
+report createay1
+
+$RRDTOOL update ${PREFIX}ax1.rrd --template a $UPDATEAx1
+report update ax1
+
+$RRDTOOL create ${PREFIX}ax2.rrd --template ${PREFIX}ax1.rrd || fail "create ax2"
+
+$RRDTOOL dump ${PREFIX}ay1.rrd > ${PREFIX}ay1.xml
+$RRDTOOL dump ${PREFIX}ax2.rrd > ${PREFIX}ax2.xml
+$DIFF ${PREFIX}ay1.xml ${PREFIX}ax2.xml
+
+report "created from template matches empty RRD with same start time"
+
+$RRDTOOL create ${PREFIX}b1.rrd --step 60 DS:b:GAUGE:120:0:U  $RRA
+report createb1
+
+! $RRDTOOL create ${PREFIX}b2.rrd --template ${PREFIX}b1.rrd  DS:b:COUNTER:120:0:U 2>/dev/null
+report "create with template and name-clashing DS fails"
+
+$RRDTOOL create ${PREFIX}ax3.rrd --template ${PREFIX}ax1-copy.rrd \
+    --source ${PREFIX}ax1.rrd || fail "create ax3"
+
+$RRDTOOL dump ${PREFIX}ax1.rrd > ${PREFIX}ax1.xml
+$RRDTOOL dump ${PREFIX}ax3.rrd > ${PREFIX}ax3.xml
+$DIFF ${PREFIX}ax1.xml ${PREFIX}ax3.xml
+
+report "create from old source as template using source for prefilling equals source"
+
+rm -f ${PREFIX}*.rrd ${PREFIX}*.xml