Fawkes API  Fawkes Development Version
rrd_thread.cpp
00001 
00002 /***************************************************************************
00003  *  rrd_thread.cpp - RRD Thread
00004  *
00005  *  Created: Fri Dec 17 00:32:57 2010
00006  *  Copyright  2006-2010  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include "rrd_thread.h"
00024 
00025 #include <core/exceptions/system.h>
00026 #include <core/threading/scoped_rwlock.h>
00027 #include <utils/misc/string_conversions.h>
00028 #include <utils/system/file.h>
00029 #include <cstdio>
00030 #include <cstdlib>
00031 #include <cstdarg>
00032 #include <cstring>
00033 #include <rrd.h>
00034 
00035 using namespace fawkes;
00036 
00037 /** @class RRDThread "rrd_thread.h"
00038  * RRD Thread.
00039  * This thread maintains an active connection to RRD and provides an
00040  * aspect to access RRD to make it convenient for other threads to use
00041  * RRD.
00042  *
00043  * @author Tim Niemueller
00044  */
00045 
00046 /** Constructor. */
00047 RRDThread::RRDThread()
00048   : Thread("RRDThread", Thread::OPMODE_CONTINUOUS),
00049     AspectProviderAspect("RRDAspect", &__rrd_aspect_inifin),
00050     __rrd_aspect_inifin(this)
00051 {
00052   set_prepfin_conc_loop(true);
00053 }
00054 
00055 
00056 /** Destructor. */
00057 RRDThread::~RRDThread()
00058 {
00059 }
00060 
00061 
00062 void
00063 RRDThread::init()
00064 {
00065   __cfg_graph_interval = 30.;
00066   try {
00067     __cfg_graph_interval = config->get_float("/plugins/rrd/graph_interval");
00068   } catch (Exception &e) {}
00069 
00070   __time_wait = new TimeWait(clock, time_sec_to_usec(__cfg_graph_interval));
00071 }
00072 
00073 
00074 void
00075 RRDThread::finalize()
00076 {
00077 }
00078 
00079 
00080 void
00081 RRDThread::loop()
00082 {
00083   __time_wait->mark_start();
00084   generate_graphs();
00085   __time_wait->wait_systime();
00086 }
00087 
00088 
00089 /** Generate all graphs. */
00090 void
00091 RRDThread::generate_graphs()
00092 {
00093   ScopedRWLock lock(__graphs.rwlock(), ScopedRWLock::LOCK_READ);
00094 
00095   std::vector<fawkes::RRDGraphDefinition *>::iterator g;
00096   for (g = __graphs.begin(); g != __graphs.end(); ++g) {
00097     size_t argc = 0;
00098     const char **argv = (*g)->get_argv(argc);
00099 
00100     //logger->log_debug(name(), "rrd_graph arguments:");
00101     //for (size_t j = 0; j < argc; ++j) {
00102     //  logger->log_debug(name(), "  %zu: %s", j, argv[j]);
00103     //}
00104 
00105     rrd_clear_error();
00106     rrd_info_t *i = rrd_graph_v(argc, (char **)argv);
00107     if (i == NULL) {
00108       throw Exception("Creating graph %s (for RRD %s) failed: %s",
00109                       (*g)->get_name(), (*g)->get_rrd_def()->get_name(),
00110                       rrd_get_error());
00111     }
00112     rrd_info_free(i);
00113   }
00114 }
00115 
00116 void
00117 RRDThread::add_rrd(RRDDefinition *rrd_def)
00118 {
00119   // generate filename
00120   char *filename;
00121   if (asprintf(&filename, "%s/%s.rrd", ".", rrd_def->get_name()) == -1) {
00122     throw OutOfMemoryException("Failed to creat filename for RRD %s",
00123                                rrd_def->get_name());
00124   }
00125   rrd_def->set_filename(filename);
00126   free(filename);
00127 
00128   if (! File::exists(rrd_def->get_filename()) || rrd_def->get_recreate()) {
00129     std::string size_s = StringConversions::to_string(rrd_def->get_step_sec());
00130 
00131     // build parameter array
00132     // "create" filename --step STEP --start START DS... RRA...
00133     size_t rrd_argc = 6 + rrd_def->get_ds().size() + rrd_def->get_rra().size();
00134     const char *rrd_argv[rrd_argc];
00135     size_t i = 0;
00136     rrd_argv[i++] = "create";
00137     rrd_argv[i++] = rrd_def->get_filename();
00138     rrd_argv[i++] = "--step";
00139     rrd_argv[i++] = size_s.c_str();
00140     rrd_argv[i++] = "--start";
00141     rrd_argv[i++] = "0";
00142 
00143     std::vector<RRDDataSource>::const_iterator d;
00144     for (d = rrd_def->get_ds().begin();
00145          d != rrd_def->get_ds().end() && i < rrd_argc;
00146          ++d)
00147     {
00148       rrd_argv[i++] = d->to_string();
00149     }
00150 
00151     std::vector<RRDArchive>::const_iterator a;
00152     for (a = rrd_def->get_rra().begin();
00153          a != rrd_def->get_rra().end() && i < rrd_argc;
00154          ++a)
00155     {
00156       rrd_argv[i++] = a->to_string();
00157     }
00158 
00159     //logger->log_debug(name(), "rrd_create arguments:");
00160     //for (size_t j = 0; j < i; ++j) {
00161     //  logger->log_debug(name(), "  %zu: %s", j, rrd_argv[j]);
00162     //}
00163 
00164     // Create RRD file
00165     rrd_clear_error();
00166     if (rrd_create(i, (char **)rrd_argv) == -1) {
00167       throw Exception("Creating RRD %s failed: %s",
00168                       rrd_def->get_name(), rrd_get_error());
00169     }
00170   }
00171 
00172   ScopedRWLock lock(__rrds.rwlock());
00173   RWLockVector<fawkes::RRDDefinition *>::iterator r;
00174   for (r = __rrds.begin(); r != __rrds.end(); ++r) {
00175     if (strcmp((*r)->get_name(), rrd_def->get_name()) == 0) {
00176       throw Exception("RRD with name %s has already been registered",
00177                       rrd_def->get_name());
00178     }
00179   }
00180 
00181   rrd_def->set_rrd_manager(this);
00182   __rrds.push_back(rrd_def);
00183 }
00184 
00185 void
00186 RRDThread::remove_rrd(RRDDefinition *rrd_def)
00187 {
00188   ScopedRWLock rrds_lock(__rrds.rwlock());
00189   RWLockVector<fawkes::RRDDefinition *>::iterator r;
00190   for (r = __rrds.begin(); r != __rrds.end(); ++r) {
00191     if (strcmp((*r)->get_name(), rrd_def->get_name()) == 0) {
00192       __rrds.erase(r);
00193       break;
00194     }
00195   }
00196 
00197   ScopedRWLock graph_lock(__graphs.rwlock());
00198   bool graphs_modified = false;
00199   do {
00200     graphs_modified = false;
00201     RWLockVector<fawkes::RRDGraphDefinition *>::iterator g;
00202     for (g = __graphs.begin(); g != __graphs.end(); ++g) {
00203       if (strcmp((*g)->get_rrd_def()->get_name(), rrd_def->get_name()) == 0) {
00204         __graphs.erase(g);
00205         graphs_modified = true;
00206         break;
00207       }
00208     }
00209   } while (graphs_modified);
00210 }
00211 
00212 void
00213 RRDThread::add_graph(RRDGraphDefinition *rrd_graph_def)
00214 {
00215   // generate filename
00216   char *filename;
00217   if (asprintf(&filename, "%s/%s.png", ".",
00218                rrd_graph_def->get_name()) == -1) {
00219     throw OutOfMemoryException("Failed to create filename for PNG %s",
00220                                rrd_graph_def->get_name());
00221   }
00222   rrd_graph_def->set_filename(filename);
00223   free(filename);
00224 
00225   ScopedRWLock lock(__graphs.rwlock());
00226   RWLockVector<fawkes::RRDGraphDefinition *>::iterator g;
00227   for (g = __graphs.begin(); g != __graphs.end(); ++g) {
00228     if (strcmp((*g)->get_name(), rrd_graph_def->get_name()) == 0) {
00229       throw Exception("RRD graph with name %s has already been registered",
00230                       rrd_graph_def->get_name());
00231     }
00232   }
00233   __graphs.push_back(rrd_graph_def);
00234 }
00235 
00236 void
00237 RRDThread::add_data(const char *rrd_name, const char *format, ...)
00238 {
00239   ScopedRWLock lock(__rrds.rwlock(), ScopedRWLock::LOCK_READ);
00240 
00241   std::vector<RRDDefinition *>::const_iterator d;
00242   for (d = __rrds.begin(); d != __rrds.end(); ++d) {
00243     RRDDefinition *rrd_def = *d;
00244     if (strcmp(rrd_name, rrd_def->get_name()) == 0) {
00245       char *data;
00246       va_list arg;
00247       va_start(arg, format);
00248       if (vasprintf(&data, format, arg) == -1) {
00249         va_end(arg);
00250         throw OutOfMemoryException("Failed to create data string for %s",
00251                                    rrd_name);
00252       }
00253       va_end(arg);
00254 
00255       // filename data
00256       size_t rrd_argc = 3;
00257       const char *rrd_argv[rrd_argc];
00258       size_t i = 0;
00259       rrd_argv[i++] = "update";
00260       rrd_argv[i++] = rrd_def->get_filename();
00261       rrd_argv[i++] = data;
00262 
00263       //logger->log_debug(name(), "rrd_update arguments:");
00264       //for (size_t j = 0; j < i; ++j) {
00265       //  logger->log_debug(name(), "  %zu: %s", j, rrd_argv[j]);
00266       //}
00267 
00268       rrd_clear_error();
00269       if (rrd_update(i, (char **)rrd_argv) == -1) {
00270         free(data);
00271         throw Exception("Failed to update RRD %s: %s", rrd_name, rrd_get_error());
00272       }
00273 
00274       free(data);
00275       return;
00276     }
00277   }
00278 
00279   throw Exception("No RRD named %s registered", rrd_name);
00280 }
00281 
00282 
00283 const fawkes::RWLockVector<RRDDefinition *> &
00284 RRDThread::get_rrds() const
00285 {
00286   return __rrds;
00287 }
00288 
00289 
00290 const fawkes::RWLockVector<RRDGraphDefinition *> &
00291 RRDThread::get_graphs() const
00292 {
00293   return __graphs;
00294 }