summaryrefslogtreecommitdiff
path: root/src/libpcp_qmc/src/qmc_group.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp_qmc/src/qmc_group.cpp')
-rw-r--r--src/libpcp_qmc/src/qmc_group.cpp521
1 files changed, 521 insertions, 0 deletions
diff --git a/src/libpcp_qmc/src/qmc_group.cpp b/src/libpcp_qmc/src/qmc_group.cpp
new file mode 100644
index 0000000..d2ccb72
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_group.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1997-2005 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+
+#include <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include "qmc_group.h"
+#include "qmc_source.h"
+#include "qmc_context.h"
+#include "qmc_metric.h"
+
+int QmcGroup::tzLocal = -1;
+bool QmcGroup::tzLocalInit = false;
+QString QmcGroup::tzLocalString;
+QString QmcGroup::localHost;
+
+QmcGroup::QmcGroup(bool restrictArchives)
+{
+ my.restrictArchives = restrictArchives;
+ my.mode = PM_CONTEXT_HOST;
+ my.use = -1;
+ my.localSource = 0;
+ my.tzFlag = unknownTZ;
+ my.tzDefault = -1;
+ my.tzUser = -1;
+ my.tzGroupIndex = 0;
+ my.timeEndReal = 0.0;
+
+ // Get timezone from environment
+ if (tzLocalInit == false) {
+ char buf[MAXHOSTNAMELEN];
+ gethostname(buf, MAXHOSTNAMELEN);
+ buf[MAXHOSTNAMELEN-1] = '\0';
+ localHost = buf;
+
+ char *tz = __pmTimezone();
+ if (tz == NULL)
+ pmprintf("%s: Warning: Unable to get timezone from environment\n",
+ pmProgname);
+ else {
+ tzLocal = pmNewZone(tz);
+ if (tzLocal < 0)
+ pmprintf("%s: Warning: Timezone for localhost: %s\n",
+ pmProgname, pmErrStr(tzLocal));
+ else {
+ tzLocalString = tz;
+ my.tzDefault = tzLocal;
+ my.tzFlag = localTZ;
+ }
+ }
+ tzLocalInit = true;
+ }
+}
+
+QmcGroup::~QmcGroup()
+{
+ for (int i = 0; i < my.contexts.size(); i++)
+ if (my.contexts[i])
+ delete my.contexts[i];
+}
+
+int
+QmcGroup::use(int type, const QString &theSource, int flags)
+{
+ int sts = 0;
+ unsigned int i;
+ QString source(theSource);
+
+ if (type == PM_CONTEXT_LOCAL) {
+ for (i = 0; i < numContexts(); i++)
+ if (my.contexts[i]->source().type() == type)
+ break;
+ }
+ else if (type == PM_CONTEXT_ARCHIVE) {
+ if (source == QString::null) {
+ pmprintf("%s: Error: Archive context requires archive path\n",
+ pmProgname);
+ return PM_ERR_NOCONTEXT;
+ }
+ // This doesn't take into account {.N,.meta,.index,} ... but
+ // then again, nor does pmNewContext. More work ... useful?
+ for (i = 0; i < numContexts(); i++) {
+ if (source == my.contexts[i]->source().source())
+ break;
+ }
+ }
+ else {
+ if (source == QString::null) {
+ if (!defaultDefined()) {
+ createLocalContext();
+ if (!defaultDefined()) {
+ pmprintf("%s: Error: "
+ "Cannot connect to PMCD on localhost: %s\n",
+ pmProgname,
+ pmErrStr(my.localSource->status()));
+ return my.localSource->status();
+ }
+ }
+ source = my.contexts[0]->source().source();
+ }
+
+ for (i = 0; i < numContexts(); i++) {
+ if (source == my.contexts[i]->source().source())
+ break;
+ }
+ }
+
+ if (i == numContexts()) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: No direct match for context \"" << source
+ << "\" (type " << type << ")." << endl;
+ }
+
+ // Determine live or archive mode by the first source
+ if (i == 0)
+ my.mode = type;
+
+ // If the assumed mode differs from the requested context type
+ // we may need to map the host to an archive
+ if (my.mode != type) {
+
+ if (my.mode == PM_CONTEXT_HOST && type == PM_CONTEXT_ARCHIVE) {
+ pmprintf("%s: Error: Archive \"%s\" requested "
+ "after live mode was assumed.\n", pmProgname,
+ (const char *)source.toAscii());
+ return PM_ERR_NOCONTEXT;
+ }
+
+ // If we are in archive mode, map hosts to archives of same host
+ if (my.mode == PM_CONTEXT_ARCHIVE && type == PM_CONTEXT_HOST) {
+ QString chop1 = source.remove(PM_LOG_MAXHOSTLEN-1, INT_MAX);
+ for (i = 0; i < numContexts(); i++) {
+ QString chop2 = my.contexts[i]->source().host();
+ chop2.remove(PM_LOG_MAXHOSTLEN-1, INT_MAX);
+ if (chop1 == chop2)
+ break;
+ }
+
+ if (i == numContexts()) {
+ pmprintf("%s: Error: No archives were specified "
+ "for host \"%s\"\n", pmProgname,
+ (const char *)source.toAscii());
+ return PM_ERR_NOTARCHIVE;
+ }
+ }
+ }
+ }
+
+ if (i == numContexts()) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Creating new context for \"" << source
+ << '\"' << endl;
+ }
+
+ QmcSource *src = QmcSource::getSource(type, source, flags, false);
+ if (src == NULL) {
+ pmprintf("%s: Error: No archives were specified for host \"%s\"\n",
+ pmProgname, (const char *)source.toAscii());
+ return PM_ERR_NOTARCHIVE;
+ }
+
+ QmcContext *newContext = new QmcContext(src);
+ if (newContext->handle() < 0) {
+ sts = newContext->handle();
+ pmprintf("%s: Error: %s: %s\n", pmProgname,
+ (const char *)source.toAscii(), pmErrStr(sts));
+ delete newContext;
+ return sts;
+ }
+
+ // If we are in archive mode and are adding an archive,
+ // make sure another archive for the same host does not exist
+ if (my.restrictArchives && type == PM_CONTEXT_ARCHIVE) {
+ for (i = 0; i < numContexts(); i++)
+ // No need to restrict comparison here, both are from
+ // log labels.
+ if (my.contexts[i]->source().host() ==
+ newContext->source().host()) {
+ pmprintf("%s: Error: Archives \"%s\" and \"%s\" are from "
+ "the same host \"%s\"\n", pmProgname,
+ my.contexts[i]->source().sourceAscii(),
+ newContext->source().sourceAscii(),
+ my.contexts[i]->source().hostAscii());
+ delete newContext;
+ return PM_ERR_NOCONTEXT;
+ }
+ }
+
+ my.contexts.append(newContext);
+ my.use = my.contexts.size() - 1;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Added context " << my.use << ": "
+ << *newContext << endl;
+ }
+ }
+
+ // We found a match, do we need to use a different context?
+ else if (i != (unsigned int)my.use) {
+ my.use = i;
+ sts = useContext();
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to use context to %s: %s\n", pmProgname,
+ context()->source().sourceAscii(), pmErrStr(sts));
+ return sts;
+ }
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Using existing context " << my.use
+ << " for " << context()->source().desc() << endl;
+ }
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Using current context " << my.use
+ << " (handle = " << context()->handle() << ") for "
+ << context()->source().desc() << endl;
+ }
+
+ return context()->handle();
+}
+
+int
+QmcGroup::useTZ()
+{
+ int sts = context()->useTZ();
+
+ if (sts >= 0) {
+ my.tzDefault = context()->source().tzHandle();
+ my.tzFlag = groupTZ;
+ my.tzGroupIndex = my.use;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Using timezone of "
+ << context()->source().desc()
+ << " (" << my.tzGroupIndex << ')' << endl;
+ }
+ }
+ return sts;
+}
+
+int
+QmcGroup::useTZ(const QString &tz)
+{
+ int sts = pmNewZone(tz.toAscii());
+
+ if (sts >= 0) {
+ my.tzUser = sts;
+ my.tzUserString = tz;
+ my.tzFlag = userTZ;
+ my.tzDefault = sts;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Switching timezones to \"" << tz
+ << "\" (" << my.tzUserString << ')' << endl;
+ }
+ }
+ return sts;
+}
+
+int
+QmcGroup::useLocalTZ()
+{
+ if (tzLocal >= 0) {
+ int sts = pmUseZone(tzLocal);
+ if (sts > 0) {
+ my.tzFlag = localTZ;
+ my.tzDefault = tzLocal;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Using timezone of host \"localhost\""
+ << endl;
+ }
+ }
+ return sts;
+ }
+ return tzLocal;
+}
+
+void
+QmcGroup::defaultTZ(QString &label, QString &tz)
+{
+ if (my.tzFlag == userTZ) {
+ label = my.tzUserString;
+ tz = my.tzUserString;
+ }
+ else if (my.tzFlag == localTZ) {
+ label = localHost;
+ tz = tzLocalString;
+ }
+ else {
+ label = my.contexts[my.tzGroupIndex]->source().host();
+ tz = my.contexts[my.tzGroupIndex]->source().timezone();
+ }
+}
+
+int
+QmcGroup::useDefaultTZ()
+{
+ if (my.tzFlag == unknownTZ)
+ return -1;
+ return pmUseZone(my.tzDefault);
+}
+
+int
+QmcGroup::useDefault()
+{
+ if (numContexts() == 0)
+ createLocalContext();
+ if (numContexts() == 0)
+ return my.localSource->status();
+ my.use = 0;
+ return pmUseContext(context()->handle());
+}
+
+void
+QmcGroup::createLocalContext()
+{
+ if (numContexts() == 0) {
+ QTextStream cerr(stderr);
+ QmcSource *localSource = QmcSource::getSource(PM_CONTEXT_HOST,
+ localHost, 0, false);
+ if (localSource->status() < 0 && pmDebug & DBG_TRACE_PMC)
+ cerr << "QmcGroup::createLocalContext: Default context to "
+ << localSource->desc() << " failed: "
+ << pmErrStr(localSource->status()) << endl;
+ else if (pmDebug & DBG_TRACE_PMC)
+ cerr << "QmcGroup::createLocalContext: Default context to "
+ << localSource->desc() << endl;
+
+ QmcContext *newContext = new QmcContext(localSource);
+ if (newContext->handle() < 0) {
+ pmprintf("%s: Error: %s: %s\n", pmProgname,
+ (const char *)localHost.toAscii(), pmErrStr(newContext->handle()));
+ }
+ my.contexts.append(newContext);
+ my.use = my.contexts.size() - 1;
+ }
+}
+
+void
+QmcGroup::updateBounds()
+{
+ double newStart = DBL_MAX;
+ double newEnd = 0.0;
+ double startReal;
+ double endReal;
+ struct timeval startTv;
+ struct timeval endTv;
+
+ my.timeStart.tv_sec = 0;
+ my.timeStart.tv_usec = 0;
+ my.timeEnd = my.timeStart;
+
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ if (my.contexts[i]->handle() >= 0 &&
+ my.contexts[i]->source().type() == PM_CONTEXT_ARCHIVE) {
+ startTv = my.contexts[i]->source().start();
+ endTv = my.contexts[i]->source().end();
+ startReal = __pmtimevalToReal(&startTv);
+ endReal = __pmtimevalToReal(&endTv);
+ if (startReal < newStart)
+ newStart = startReal;
+ if (endReal > newEnd)
+ newEnd = endReal;
+ }
+ }
+
+ __pmtimevalFromReal(newStart, &my.timeStart);
+ __pmtimevalFromReal(newEnd, &my.timeEnd);
+ my.timeEndReal = newEnd;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::updateBounds: start = " << my.timeStart.tv_sec
+ << '.' << my.timeStart.tv_usec << ", end = "
+ << my.timeEnd.tv_sec << '.' << my.timeEnd.tv_usec << endl;
+ }
+}
+
+void
+QmcGroup::dump(QTextStream &stream)
+{
+ stream << "mode: ";
+ switch(my.mode) {
+ case PM_CONTEXT_LOCAL:
+ stream << "local";
+ break;
+ case PM_CONTEXT_HOST:
+ stream << "live host";
+ break;
+ case PM_CONTEXT_ARCHIVE:
+ stream << "archive";
+ break;
+ }
+
+ stream << ", timezone: ";
+ switch(my.tzFlag) {
+ case QmcGroup::localTZ:
+ stream << "local = \"" << tzLocalString;
+ break;
+ case QmcGroup::userTZ:
+ stream << "user = \"" << my.tzUserString;
+ break;
+ case QmcGroup::groupTZ:
+ stream << "group = \""
+ << my.contexts[my.tzGroupIndex]->source().timezone();
+ break;
+ case QmcGroup::unknownTZ:
+ stream << "unknown = \"???";
+ break;
+ }
+ stream << "\": " << endl;
+
+ stream << " " << numContexts() << " contexts:" << endl;
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ stream << " [" << i << "] " << *(my.contexts[i]) << endl;
+ my.contexts[i]->dumpMetrics(stream);
+ }
+}
+
+int
+QmcGroup::useContext()
+{
+ int sts = 0;
+
+ if ((context()->status() == 0) &&
+ (sts = pmUseContext(context()->handle())) < 0)
+ pmprintf("%s: Error: Unable to reuse context to %s: %s\n",
+ pmProgname, context()->source().sourceAscii(), pmErrStr(sts));
+ return sts;
+}
+
+QmcMetric *
+QmcGroup::addMetric(char const *string, double theScale, bool active)
+{
+ QmcMetric *metric = new QmcMetric(this, string, theScale, active);
+ if (metric->status() >= 0)
+ metric->context()->addMetric(metric);
+ return metric;
+}
+
+QmcMetric *
+QmcGroup::addMetric(pmMetricSpec *theMetric, double theScale, bool active)
+{
+ QmcMetric *metric = new QmcMetric(this, theMetric, theScale, active);
+ if (metric->status() >= 0)
+ metric->context()->addMetric(metric);
+ return metric;
+}
+
+int
+QmcGroup::fetch(bool update)
+{
+ int sts = 0;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::fetch: " << numContexts() << " contexts" << endl;
+ }
+
+ for (unsigned int i = 0; i < numContexts(); i++)
+ my.contexts[i]->fetch(update);
+
+ if (numContexts())
+ sts = useContext();
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::fetch: Done" << endl;
+ }
+
+ return sts;
+}
+
+int
+QmcGroup::setArchiveMode(int mode, const struct timeval *when, int interval)
+{
+ int sts, result = 0;
+
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ if (my.contexts[i]->source().type() != PM_CONTEXT_ARCHIVE)
+ continue;
+
+ sts = pmUseContext(my.contexts[i]->handle());
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to switch to context for %s: %s\n",
+ pmProgname, my.contexts[i]->source().sourceAscii(),
+ pmErrStr(sts));
+ result = sts;
+ continue;
+ }
+ sts = pmSetMode(mode, when, interval);
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to set context mode for %s: %s\n",
+ pmProgname, my.contexts[i]->source().sourceAscii(),
+ pmErrStr(sts));
+ result = sts;
+ }
+ }
+ sts = useContext();
+ if (sts < 0)
+ result = sts;
+ return result;
+}