diff options
Diffstat (limited to 'src/pmgadgets')
-rw-r--r-- | src/pmgadgets/GNUmakefile | 71 | ||||
-rw-r--r-- | src/pmgadgets/global.h | 27 | ||||
-rw-r--r-- | src/pmgadgets/lex.l | 133 | ||||
-rw-r--r-- | src/pmgadgets/main.cpp | 56 | ||||
-rw-r--r-- | src/pmgadgets/parse.cpp | 1339 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets-args.sh | 381 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.cpp | 55 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.h | 40 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.info.in | 18 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.pro | 23 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.qrc | 6 | ||||
-rw-r--r-- | src/pmgadgets/pmgadgets.sh.IN | 2 | ||||
-rwxr-xr-x | src/pmgadgets/pmgcisco.sh | 251 | ||||
-rwxr-xr-x | src/pmgadgets/pmgcluster.sh | 281 | ||||
-rwxr-xr-x | src/pmgadgets/pmgshping.sh | 242 | ||||
-rwxr-xr-x | src/pmgadgets/pmgsys.py | 380 | ||||
-rw-r--r-- | src/pmgadgets/tokens.h | 60 |
17 files changed, 3365 insertions, 0 deletions
diff --git a/src/pmgadgets/GNUmakefile b/src/pmgadgets/GNUmakefile new file mode 100644 index 0000000..833bff7 --- /dev/null +++ b/src/pmgadgets/GNUmakefile @@ -0,0 +1,71 @@ +TOPDIR = ../.. +COMMAND = pmgadgets +PROJECT = $(COMMAND).pro +include $(TOPDIR)/src/include/builddefs + +WRAPPER = $(COMMAND).sh +QRCFILE = $(COMMAND).qrc +ICNFILE = $(COMMAND).icns +XMLFILE = $(COMMAND).info +HEADERS = pmgadgets.h tokens.h +SOURCES = $(HEADERS:.h=.cpp) main.cpp parse.cpp +CONFFILES = $(PROJECT) +LDIRT = $(COMMAND) $(ICNFILE) $(WRAPPER) $(SCRIPTS) $(XMLFILE) *.yy.c images + +default: build-me + +include $(BUILDRULES) + +ifeq "$(ENABLE_QT)" "true" +build-me:: images wrappers + $(QTMAKE) + $(LNMAKE) + +build-me:: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +ifeq ($(WINDOW),mac) +PKG_MAC_DIR = /Library/PCP/$(COMMAND).app/Contents +PKG_SUB_DIR = $(PKG_MAC_DIR)/MacOS +wrappers: $(WRAPPER) $(SCRIPTS) +else +PKG_SUB_DIR = $(PKG_BIN_DIR) +wrappers: $(WRAPPER) $(SCRIPTS) +endif + +$(WRAPPER): $(WRAPPER).IN + @ $(SED) -e '/\# .*/b' -e 's;PKG_BIN_DIR;$(PKG_SUB_DIR);g' < $< > $@ + +install: default + $(INSTALL) -m 755 -d $(PKG_BIN_DIR) +ifneq ($(WINDOW),mac) + $(INSTALL) -m 755 $(BINARY) $(PKG_BIN_DIR)/$(COMMAND) +endif +ifeq ($(WINDOW),mac) + $(INSTALL) -m 755 $(WRAPPER) $(PKG_BIN_DIR)/$(COMMAND) + $(call INSTALL_DIRECTORY_HIERARCHY,$(PKG_MAC_DIR),/Library) + $(INSTALL) -m 644 $(XMLFILE) $(PKG_MAC_DIR)/Info.plist + $(INSTALL) -m 644 $(MACBUILD)/PkgInfo $(PKG_MAC_DIR)/PkgInfo + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/MacOS + $(call INSTALL_QT_FRAMEWORKS,$(BINARY)) + $(INSTALL) -m 755 $(BINARY) $(PKG_MAC_DIR)/MacOS/$(COMMAND) + rm $(BINARY) + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/Resources + $(INSTALL) -m 644 $(ICNFILE) $(PKG_MAC_DIR)/Resources/$(ICNFILE) + $(call INSTALL_QT_RESOURCES,$(PKG_MAC_DIR)/Resources) +endif + +else +build-me: +install: +endif + +default_pcp: default + +install_pcp: install + +images: $(ICNFILE) + $(LN_S) $(TOPDIR)/images images + +$(ICNFILE): + $(LN_S) $(TOPDIR)/images/$(ICNFILE) $(ICNFILE) diff --git a/src/pmgadgets/global.h b/src/pmgadgets/global.h new file mode 100644 index 0000000..269d22b --- /dev/null +++ b/src/pmgadgets/global.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 1996-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _GLOBAL_H +#define _GLOBAL_H + +// Global data from resources/command line options +struct AppData { + int zoom; // Zoom factor + double delta; // Update interval (seconds) + char * defaultFont; // Default font for label gadgets +}; + +extern AppData appData; // Global options/resources + +#endif /* _GLOBAL_H */ diff --git a/src/pmgadgets/lex.l b/src/pmgadgets/lex.l new file mode 100644 index 0000000..52047b5 --- /dev/null +++ b/src/pmgadgets/lex.l @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1996-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +%{ + +#include <stdlib.h> +#include <string.h> +#include "tokens.h" + +extern unsigned lineCount; + +int dostring(); + +#ifndef FLEX_SCANNER +#ifdef output +#undef output +#define output(c) +#endif +#else +#define YY_NO_UNPUT +#endif + +%} + +%% + +[-]?[0-9]+ { + /* + * bigger than, 999,999,999 (or smaller than + * -99,999,999), let's force it to be a real, to + avoid overflow issues + */ + if (strlen(yytext) > 9) { + tokenRealVal = atof(yytext); + return(TOK_REAL); + } + tokenIntVal = atoi(yytext); + return(TOK_INTEGER); + } +[-]?[0-9]+\.[0-9]+ { + tokenRealVal = atof(yytext); + return(TOK_REAL); + } + +\( return TOK_LPAREN; +\) return TOK_RPAREN; +\[ return TOK_LBRACKET; +\] return TOK_RBRACKET; +\: return TOK_COLON; + +_line return TOK_LINE; +_label return TOK_LABEL; +_bar return TOK_BAR; +_multibar return TOK_MULTIBAR; +_bargraph return TOK_BARGRAPH; +_led return TOK_LED; + +_legend return TOK_LEGEND; +_colour return TOK_COLOUR; +_color return TOK_COLOUR; +_colourlist return TOK_COLOURLIST; +_colorlist return TOK_COLOURLIST; +_actions return TOK_ACTIONLIST; + +_update return TOK_UPDATE; +_history return TOK_HISTORY; +_noborder return TOK_NOBORDER; +_metric return TOK_METRIC; +_horizontal return TOK_HORIZONTAL; +_vertical return TOK_VERTICAL; +_metrics return TOK_METRICS; +_min return TOK_MIN; +_mainimum return TOK_MIN; +_max return TOK_MAX; +_maximum return TOK_MAX; +_default return TOK_DEFAULT; +_fixed return TOK_FIXED; + +_[a-zA-Z0-9_.] return TOK_BAD_RES_WORD; + +\# { + while (input() != '\n') + ; + nLines++; + } + +\"[^\"\n][^\"\n]*\" return dostring(); + +\n nLines++; + +[A-Za-z][A-Za-z0-9_.\-]* { + tokenStringVal = strdup(yytext); + return TOK_IDENTIFIER; + } + +[0-9\-]+[A-Za-z_.\-]+[A-Za-z0-9.\-]* { + tokenStringVal = strdup(yytext); + return TOK_IDENTIFIER; + } + +[ \t]* { } +%% + +int +yywrap(void) +{ + return 1; +} + +int +dostring(void) +{ + size_t szResult; + char* result; + + szResult = yyleng - 1; /* ignore 1st '"', 2nd clobbered by '\0' */ + result = (char*)malloc(szResult); + strncpy(result, yytext + 1, szResult - 1); + result[szResult - 1] = '\0'; + tokenStringVal = result; + return TOK_STRING; +} diff --git a/src/pmgadgets/main.cpp b/src/pmgadgets/main.cpp new file mode 100644 index 0000000..e4e6f0f --- /dev/null +++ b/src/pmgadgets/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013, Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <QApplication> +#include "pmgadgets.h" +#include "global.h" + +AppData appData; // Global options/resources + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + PmGadgets gadgets; + return gadgets.exec(); +} + +#if 0 +#include <QDesktopWidget> +#include <QCursor> +static char usage[] = + "Usage: pmgadgets [options] [message...]\n\n" + "Options:\n" + " -? | -help display this usage message\n" + " -header title set window title\n" + " -noslider do not display a text box slider\n" + " -noframe do not display a frame around the text box\n"; + + char *option; + char *filename = NULL; + char *defaultname = NULL; + + int errflag = 0; + if (errflag) { + fprintf(stderr, "%s", usage); + exit(1); + } + + if (nearmouseflag) + grid.move(QCursor::pos()); + else if (centerflag) { + int x = (a.desktop()->screenGeometry().width() / 2) - (q.width() / 2); + int y = (a.desktop()->screenGeometry().height() / 2) - (q.height() / 2); + grid.move(x > 0 ? x : 0, y > 0 ? y : 0); + } +#endif diff --git a/src/pmgadgets/parse.cpp b/src/pmgadgets/parse.cpp new file mode 100644 index 0000000..923baef --- /dev/null +++ b/src/pmgadgets/parse.cpp @@ -0,0 +1,1339 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 1996 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include <setjmp.h> +#include <values.h> +#include "global.h" +#include "tokens.h" + +#include "qed.h" +#include "qed_bar.h" +#include "qed_led.h" +#include "qed_line.h" +#include "qed_label.h" +#include "qed_legend.h" +#include "qed_gadget.h" +#include "qed_colorlist.h" +#include "qed_actionlist.h" +#include <qnumeric.h> + +extern "C" { +int yylex(); +} + +// Variables manipulated by the lexical analyser +// +unsigned nLines = 1; +int tokenIntVal; +double tokenRealVal; +char *tokenStringVal; + +// Parser variables +// +static int token; +static int eofOK = 1; +static unsigned nErrors; +static unsigned nWarnings; + +static QList<QedColorList*> colorLists; +static QList<QedLegend*> legends; +static QList<QedActionList*> actionLists; +static QList<QedGadget*> gadgets; +static QWidget *parent; + +class MetricData +{ +public: + MetricData(char* h, char* m, char* i) { host = h; metric = m; inst = i; } + char *host; // NULL => localhost + char *metric; // metric name + char *inst; // instance name (may be NULL) +}; + +int AddMetrics(double a, QList<MetricData*>&b, QedGadget *c) { return 0; /* TODO */ } + +typedef struct { + int id; + const char* string; +} TokenList; + +static TokenList tokenStrings[] = { + { TOK_LINE, "line" }, + { TOK_LABEL, "label" }, + { TOK_BAR, "bar" }, + { TOK_MULTIBAR, "multibar" }, + { TOK_BARGRAPH, "bargraph" }, + { TOK_LED, "led" }, + + { TOK_LEGEND, "legend" }, + { TOK_COLOURLIST, "colourlist" }, + { TOK_ACTIONLIST, "actionlist" }, + + { TOK_BAD_RES_WORD, "<bad reserved word>" }, + { TOK_UPDATE, "update" }, + { TOK_METRIC, "metric" }, + { TOK_HORIZONTAL, "horizontal" }, + { TOK_VERTICAL, "vertical" }, + { TOK_METRICS, "metrics" }, + { TOK_MIN, "min" }, + { TOK_MAX, "max" }, + { TOK_DEFAULT, "default" }, + { TOK_FIXED, "fixed" }, + { TOK_COLOUR, "colour" }, + { TOK_HISTORY, "history" }, + { TOK_NOBORDER, "noborder" }, + + { TOK_IDENTIFIER, "<identifier>" }, + { TOK_INTEGER, "<integer>" }, + { TOK_REAL, "<real>" }, + { TOK_STRING, "<string>" }, + { TOK_LPAREN, "(" }, + { TOK_RPAREN, ")" }, + { TOK_LBRACKET, "[" }, + { TOK_RBRACKET, "]" }, + { TOK_COLON, ":" }, + + { TOK_EOF, "EOF" }, + { 0, "raw EOF" }, +}; +const unsigned nTokenStrings = sizeof(tokenStrings) / sizeof(tokenStrings[0]); + +static const char* +TokenToString(int tok) +{ + for (unsigned i = 0; i < nTokenStrings; i++) + if (tokenStrings[i].id == tok) + return tokenStrings[i].string; + return "???"; +} + +static jmp_buf scannerEofEnv; +static int stashedToken = -1; + +static int +NextToken() +{ + static int atEOF = 0; + + if (atEOF) + return token; + + if (stashedToken < 0) { + if ((token = yylex()) == 0) { + atEOF = 1; + token = TOK_EOF; + if (!eofOK) { + nErrors++; + pmprintf("Error, line %d: unexpected EOF, giving up\n", nLines); + longjmp(scannerEofEnv, 0); + } + } + } + else { + token = stashedToken; + stashedToken = -1; + } + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "input: %s (%d)\n", TokenToString(token), token); + return token; +} + +static void +FindNewStatement() +{ + eofOK = 1; + for(;;) + switch (token) { + case TOK_EOF: + case TOK_LINE: + case TOK_LABEL: + case TOK_BAR: + case TOK_MULTIBAR: + case TOK_BARGRAPH: + case TOK_LED: + case TOK_LEGEND: + case TOK_COLOURLIST: + return; + + default: + NextToken(); + break; + } +} + +static int +ParseUpdate(double& update, int eofMayFollow) +{ + eofOK = 0; + NextToken(); + if (token == TOK_INTEGER) + update = tokenIntVal; + else if (token == TOK_REAL) + update = tokenRealVal; + else { + nErrors++; + pmprintf("Error, line %d: update interval must be a number\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParseHistory(int& history, int eofMayFollow) +{ + int err = 0; + + eofOK = 0; + NextToken(); + if (token == TOK_INTEGER) { + if ((history = tokenIntVal) < 0) + err = 1; + } + else + err = 1; + + if (err) { + nErrors++; + pmprintf("Error, line %d: history must be a positive integer\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParsePosition(int& x, int& y, int eofMayFollow) +{ + eofOK = 0; + if (token == TOK_INTEGER) + x = tokenIntVal * appData.zoom; + else { + nErrors++; + pmprintf("Error, line %d: X coordinate must be an integer\n", nLines); + return -1; + } + NextToken(); + if (token == TOK_INTEGER) + y = tokenIntVal * appData.zoom; + else { + nErrors++; + pmprintf("Error, line %d: Y coordinate must be an integer\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParseSize(unsigned& w, unsigned& h, int eofMayFollow) +{ + eofOK = 0; + if (token == TOK_INTEGER && tokenIntVal > 0) + w = tokenIntVal * appData.zoom; + else { + nErrors++; + pmprintf("Error, line %d: width must be a positive integer\n", nLines); + return -1; + } + NextToken(); + if (token == TOK_INTEGER && tokenIntVal > 0) + h = tokenIntVal * appData.zoom; + else { + nErrors++; + pmprintf("Error, line %d: height must be a positive integer\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParseGeometry(int& x, int& y, unsigned& w, unsigned& h, int eofMayFollow) +{ + int sts; + + eofOK = 0; + if ((sts = ParsePosition(x, y, 0)) < 0) + return sts; + sts = ParseSize(w, h, eofMayFollow); + return sts; +} + +static int +ParseActionListActions(QedActionList *alp, int eofMayFollow) +{ + unsigned int n = 0; + + eofOK = 0; + NextToken(); + while (token == TOK_IDENTIFIER || token == TOK_STRING) { + if (token == TOK_IDENTIFIER || token == TOK_STRING) { + alp->addName(tokenStringVal); + NextToken(); + } + else { + nErrors++; + pmprintf("Error, line %d: action name expected\n", nLines); + return -1; + } + if (token == TOK_IDENTIFIER || token == TOK_STRING) { + alp->addAction(tokenStringVal); + NextToken(); + } + else { + nErrors++; + pmprintf("Error, line %d: action expected\n", nLines); + return -1; + } + if (token == TOK_DEFAULT) { + if (alp->defaultPos() == -1) + alp->setDefaultPos(n); + else { + nErrors++; + pmprintf("Error, line %d: only one default action permitted in list\n", nLines); + } + NextToken(); + } + n++; + } + eofOK = eofMayFollow; + if (token == TOK_RPAREN) + NextToken(); + else { + nErrors++; + pmprintf("Error, line %d: `)' expected at end of action list\n", nLines); + return -1; + } + return 0; +} + +typedef enum { + AL_NEED_NAME = 1, + AL_MAY_BE_REFERENCE = 2 +} ActionListFlags; + +// This parses 3 types of action list: +// +// _actions name (...) definition of action list +// _actions name reference to defined action list +// _actions (...) anonymous action list +// +static int +ParseActionList( + int flags, // AL_* flags above + int eofMayFollow) +{ + char *name; + char anon[64]; + int ai, sts; + int existingActions = -1; + QedActionList *alp; + static int count; + + eofOK = 0; + NextToken(); + if (token == TOK_IDENTIFIER) { + name = tokenStringVal; + for (ai = 0; ai < actionLists.size(); ai++) { + alp = actionLists.at(ai); + if (strcmp(alp->identity(), name) == 0) + break; + } + if (ai != actionLists.size()) + existingActions = ai; + else + alp = new QedActionList(name); + eofOK = eofMayFollow; + NextToken(); + } + else { + if (flags & AL_NEED_NAME) { + nErrors++; + pmprintf("Error, line %d: name expected for actions list\n", nLines); + return -1; + } + snprintf(anon, sizeof(anon), "anonymous#%d", count++); + alp = new QedActionList(anon); + name = anon; + } + + sts = 0; + if (token == TOK_LPAREN) { + // This is an action list definition (the name may be omitted) + // + eofOK = 0; + if (existingActions != -1) { + nErrors++; + pmprintf("Error, line %d: an actions list named \"%s\" already exists\n", + nLines, name); + return -1; + } + sts = ParseActionListActions(alp, eofMayFollow); + } + else { + // This should be a reference to an already defined action list + // + if (name == NULL) { + nErrors++; + pmprintf("Error, line %d: name or ( expected for actions list\n", nLines); + return -1; + } + if (!(flags & AL_MAY_BE_REFERENCE)) { + nErrors++; + pmprintf("Error, line %d: `(' expected for actions list definition\n", nLines); + return -1; + } + if (existingActions == -1) { + nErrors++; + pmprintf("Error, line %d: no action list named %s defined\n", + nLines, name); + return -1; + } + } + + if (sts >= 0 && existingActions == -1) + actionLists.append(alp); + + return sts; +} + +static int +ParseLine(int eofMayFollow) +{ + int x, y, sts; + unsigned w, h; + + eofOK = 0; + NextToken(); + + sts = ParseGeometry(x, y, w, h, eofMayFollow); + if (sts < 0) + return sts; + + if (token == TOK_UPDATE) { + nErrors++; + pmprintf("Error, line %d: lines may not have an update interval\n", + nLines); + return -1; + } + + // Parse optional actions list + // + if (token == TOK_ACTIONLIST) { + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + if (sts < 0) + return sts; + } + + QedLine *l = new QedLine(parent, x, y, w, h); + if (l) { + gadgets.append(l); + } else { + perror("no memory for line gadget"); + exit(1); + } + + if (pmDebug & DBG_TRACE_APPL0) + l->dump(stderr); + return 0; +} + +static int +ParseLabel(int eofMayFollow) +{ + int x, y, sts; + char* text; + char* font = appData.defaultFont; + + eofOK = 0; + NextToken(); + + sts = ParsePosition(x, y, 0); + if (sts < 0) + return sts; + + if (token == TOK_UPDATE) { + nErrors++; + pmprintf("Error, line %d: labels may not have an update interval\n", + nLines); + return -1; + } + + if (token != TOK_STRING && token != TOK_IDENTIFIER) { + nErrors++; + pmprintf("Error, line %d: label string expected\n", nLines); + return -1; + } + text = tokenStringVal; + eofOK = eofMayFollow; + NextToken(); + if (token == TOK_STRING || token == TOK_IDENTIFIER) { + font = tokenStringVal; + NextToken(); + } + + if (token == TOK_ACTIONLIST) + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + + if (sts >= 0) { + sts = 0; + // note: constructor specifies baseline pos + QedLabel* l = new QedLabel(parent, x, y, text, font); + if (l) { + gadgets.append(l); + } else { + perror("no memory for label gadget"); + exit(1); + } + + if (pmDebug & DBG_TRACE_APPL0) + l->dump(stderr); + } + + if (text) + free(text); + if (font && font != appData.defaultFont) + free(font); + return sts; +} + +// A metric spec is one of +// +// <metric> +// <metric>[<inst>] +// <host>:<metric> +// <host>:<metric>[<inst>] +// +static int +ParseMetric(MetricData** resultPtr, int eofMayFollow) +{ + char* host; + char* metric; + char* inst; + char* hostOrMetric = NULL; + + eofOK = 0; + if (token == TOK_IDENTIFIER) + hostOrMetric = tokenStringVal; + else { + nErrors++; + pmprintf("Error, line %d: performance metric expected\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + if (token == TOK_COLON) { // Have "<host>:<metric>" + host = hostOrMetric; + eofOK = 0; + NextToken(); + if (token == TOK_IDENTIFIER) + metric = tokenStringVal; + else { + nErrors++; + pmprintf("Error, line %d: performance metric expected\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + } + else { // Optional "<host>:" omitted + host = NULL; + metric = hostOrMetric; + } + if (token == TOK_LBRACKET) { + eofOK = 0; + NextToken(); + if (token == TOK_IDENTIFIER || token == TOK_STRING) + inst = tokenStringVal; + else { + nErrors++; + pmprintf("Error, line %d: performance metric instance expected\n", + nLines); + return -1; + } + NextToken(); + if (token != TOK_RBRACKET) { + nErrors++; + pmprintf("Error, line %d: `]' for performance metric instance missing\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + } + else + inst = NULL; + + *resultPtr = new MetricData(host, metric, inst); + if (*resultPtr == NULL) { + perror("No memory for metric spec"); + exit(1); + } + return 0; +} + +static int +ParseColor(char** colourName, int eofMayFollow) +{ + eofOK = 0; + NextToken(); + if (token == TOK_IDENTIFIER || token == TOK_STRING) { + *colourName = strdup(tokenStringVal); + if (*colourName == NULL) { + perror("No memory for color name"); + exit(1); + } + } + else { + nErrors++; + pmprintf("Error, line %d: color name missing\n", nLines); + return -1; + } + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParseBar(int eofMayFollow) +{ + int x, y, sts; + unsigned w, h; + MetricData* metricData; + double min = qQNaN(), max = qQNaN(); + int isVertical = 0; + double update = appData.delta; + char* colour = NULL; + int fixMax = 0; + + eofOK = 0; + NextToken(); + + sts = ParseGeometry(x, y, w, h, eofMayFollow); + if (sts < 0) + return sts; + + if (token == TOK_UPDATE) { + sts = ParseUpdate(update, 0); + if (sts < 0) + return sts; + } + + if (token != TOK_METRIC) { + nErrors++; + pmprintf("Error, line %d: _metric expected\n", nLines); + return -1; + } + NextToken(); + + sts = ParseMetric(&metricData, eofMayFollow); + if (sts < 0) + return sts; + + while (token == TOK_HORIZONTAL || + token == TOK_VERTICAL || + token == TOK_FIXED || + token == TOK_MIN || + token == TOK_MAX) { + + if (token == TOK_FIXED) { + eofOK = 0; + NextToken(); + if (token != TOK_MAX) { + nErrors++; + pmprintf("Error, line %d: _fixed _max expected\n", nLines); + return -1; + } + fixMax = 1; + } + + if (token == TOK_HORIZONTAL) { + eofOK = eofMayFollow; + NextToken(); + } + else if (token == TOK_VERTICAL) { + eofOK = eofMayFollow; + NextToken(); + isVertical = 1; + } + else if (token == TOK_MIN) { + eofOK = 0; + NextToken(); + if (token != TOK_INTEGER && token != TOK_REAL) { + nErrors++; + pmprintf("Error, line %d: minimum must be a number\n", nLines); + return -1; + } + min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal; + + eofOK = eofMayFollow; + NextToken(); + } + else if (token == TOK_MAX) { + eofOK = 0; + NextToken(); + if (token != TOK_INTEGER && token != TOK_REAL) { + nErrors++; + pmprintf("Error, line %d: maximum must be a number\n", nLines); + return -1; + } + max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal; + + eofOK = eofMayFollow; + NextToken(); + } + } + + if (token == TOK_COLOUR) { + sts = ParseColor(&colour, eofMayFollow); + if (sts < 0) + return sts; + } + + // Parse optional actions list + // + if (token == TOK_ACTIONLIST) { + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + if (sts < 0) + return sts; + } + + QedBar *b = new QedBar(parent, x, y, w, h); + QList<MetricData*> mlp; + if (b) { + if (min != qQNaN()) + b->setMinimum(min); + if (max != qQNaN()) + b->setMaximum(max); + if (isVertical) + b->setOrientation(Qt::Vertical); + if (fixMax) + b->setScaleRange(0); + gadgets.append(b); + mlp.append(metricData); + } else { + perror("no memory for bar gadget"); + exit(1); + } + if (colour) + b->setColor(colour); + + if (AddMetrics(update, mlp, b)) + nWarnings++; + + if (pmDebug & DBG_TRACE_APPL0) + b->dump(stderr); + return 0; +} + +static int +ParseMultibar(int eofMayFollow) +{ + int x, y, sts, ci; + unsigned w, h; + MetricData* metricData; + int isVertical = 0; + int noborder = 0; + int history = 0; + double update = appData.delta; + + eofOK = 0; + NextToken(); + + sts = ParseGeometry(x, y, w, h, eofMayFollow); + + if (token == TOK_UPDATE) { + sts = ParseUpdate(update, 0); + if (sts < 0) + return sts; + } + + if (token == TOK_NOBORDER) { + noborder = 1; + NextToken(); + } + + if (token == TOK_HISTORY) { + sts = ParseHistory(history, 0); + if (sts < 0) + return sts; + } + + if (token != TOK_METRICS) { + nErrors++; + pmprintf("Error, line %d: _metrics (list) expected\n", nLines); + return -1; + } + NextToken(); + + if (token != TOK_LPAREN) { + nErrors++; + pmprintf("Error, line %d: `(' expected\n", nLines); + return -1; + } + NextToken(); + + QList<MetricData*> mlp; + do { + sts = ParseMetric(&metricData, 0); + if (sts < 0) + return sts; + mlp.append(metricData); + } while (token == TOK_IDENTIFIER); + + if (token != TOK_RPAREN) { + nErrors++; + pmprintf("Error, line %d: `)' expected\n", nLines); + return -1; + } + NextToken(); + + // By default a multibar has a floating maximum initially set to a small + // value. If a max of zero is specified, the multibar is a utilisation. + // The optional _fixed keyword before the maximum can be used to specify + // clipping. + // + int clip = 0; + double max = DBL_MAX; + if (token == TOK_FIXED) { + clip = 1; + NextToken(); + if (token != TOK_MAX) { + nErrors++; + pmprintf("Error, line %d: _maximum expected\n", nLines); + return -1; + } + } + if (token == TOK_MAX) { + NextToken(); + if (token != TOK_INTEGER && token != TOK_REAL) { + nErrors++; + pmprintf("Error, line %d: a maximum must be a number\n", nLines); + return -1; + } + max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal; + if (max < 0.0 || (clip && max == 0.0)) { + nErrors++; + pmprintf("Error, line %d: a fixed maximum must be positive\n", + nLines); + return -1; + } + NextToken(); + } + + if (token != TOK_COLOURLIST) { + nErrors++; + pmprintf("Error, line %d: _colourlist expected\n", nLines); + return -1; + } + NextToken(); + + if (token != TOK_IDENTIFIER) { + nErrors++; + pmprintf("Error, line %d: colourlist name expected\n", nLines); + return -1; + } + + for (ci = 0; ci < colorLists.size(); ci++) { + if (strcmp(colorLists.at(ci)->identity(), tokenStringVal) == 0) + break; + } + if (ci == colorLists.size()) { + nErrors++; + pmprintf("Error, line %d: no colourlist named %s exists\n", + nLines, tokenStringVal); + return -1; + } + + QedColorList *colorList = colorLists.at(ci); + unsigned int nColours = colorList->length(); + unsigned int nMetrics = mlp.length(); + + if (nColours != nMetrics) { + nErrors++; + pmprintf("Error, line %d: %d colours don't match %d metrics\n", + nLines, nColours, nMetrics); + return -1; + } + + eofOK = eofMayFollow; + NextToken(); + + while (token == TOK_HORIZONTAL || token == TOK_VERTICAL) { + isVertical = (token == TOK_VERTICAL); + NextToken(); + } + + // Parse optional actions list + // + if (token == TOK_ACTIONLIST) { + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + if (sts < 0) + return sts; + } + + QedMultiBar* m = new QedMultiBar(parent, x, y, w, h, colorList, history); + if (m) { + if (isVertical) + m->setOrientation(Qt::Vertical); + if (max > 0.0) + m->setMaximum(max, clip); + m->setOutline(!noborder); + gadgets.append(m); + } else { + perror("no memory to allocate multibar gadget"); + exit(1); + } + + if (AddMetrics(update, mlp, m)) + nWarnings++; + + if (pmDebug & DBG_TRACE_APPL0) + m->dump(stderr); + return 0; +} + +static int +ParseBarGraph(int eofMayFollow) +{ + int x, y, sts; + unsigned w, h; + MetricData* metricData; + double min = qQNaN(), max = qQNaN(); + double update = appData.delta; + int fixMax = 0; + + eofOK = 0; + NextToken(); + + sts = ParseGeometry(x, y, w, h, eofMayFollow); + if (sts < 0) + return sts; + + if (token == TOK_UPDATE) { + sts = ParseUpdate(update, 0); + if (sts < 0) + return sts; + } + + if (token != TOK_METRIC) { + nErrors++; + pmprintf("Error, line %d: _metric expected\n", nLines); + return -1; + } + NextToken(); + + // There's only ever one metric for a bargraph, use chunkSize = 1 + QList<MetricData*> mlp; + sts = ParseMetric(&metricData, eofMayFollow); + if (sts < 0) + return sts; + mlp.append(metricData); + + while (token == TOK_MIN || token == TOK_MAX || token == TOK_FIXED) { + + eofOK = 0; + if (token == TOK_FIXED) { + NextToken(); + if (token != TOK_MAX) { + nErrors++; + pmprintf("Error, line %d: _fixed _max expected\n", nLines); + return -1; + } + fixMax = 1; + } + + if (token == TOK_MIN) { + NextToken(); + if (token != TOK_INTEGER && token != TOK_REAL) { + nErrors++; + pmprintf("Error, line %d: minimum must be a number\n", nLines); + return -1; + } + min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal; + } + else { + NextToken(); + if (token != TOK_INTEGER && token != TOK_REAL) { + nErrors++; + pmprintf("Error, line %d: maximum must be a number\n", nLines); + return -1; + } + max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal; + } + eofOK = eofMayFollow; + NextToken(); + } + + // Parse optional actions list + // + if (token == TOK_ACTIONLIST) { + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + if (sts < 0) + return sts; + } + + QedBarGraph* b = new QedBarGraph(parent, x, y, w, h, w - 1); + if (b) { + if (min != qQNaN()) + b->setMinimum(min); + if (max != qQNaN()) + b->setMaximum(max); + if (fixMax) + b->clipRange(); + gadgets.append(b); + } else { + perror("no memory for bargraph gadget"); + exit(1); + } + + if (AddMetrics(update, mlp, b)) + nWarnings++; + + if (pmDebug & DBG_TRACE_APPL0) + b->dump(stderr); + return 0; +} + +static int +ParseLed(int eofMayFollow) +{ + int x, y, sts, li; + unsigned w, h; + MetricData* metricData; + double update = appData.delta; + + eofOK = 0; + NextToken(); + + sts = ParseGeometry(x, y, w, h, eofMayFollow); + if (sts < 0) + return sts; + + if (token == TOK_UPDATE) { + sts = ParseUpdate(update, 0); + if (sts < 0) + return sts; + } + + if (token != TOK_METRIC) { + nErrors++; + pmprintf("Error, line %d: _metric expected\n", nLines); + return -1; + } + NextToken(); + + QList<MetricData*> mlp; + sts = ParseMetric(&metricData, eofMayFollow); + if (sts < 0) + return sts; + mlp.append(metricData); + + if (token != TOK_LEGEND) { + nErrors++; + pmprintf("Error, line %d: _legend expected for _led\n", nLines); + return -1; + } + NextToken(); + + if (token != TOK_IDENTIFIER) { + nErrors++; + pmprintf("Error, line %d: legend name expected\n", nLines); + return -1; + } + + for (li = 0; li < legends.size(); li++) { + if (strcmp(legends.at(li)->identity(), tokenStringVal) == 0) + break; + } + if (li == colorLists.size()) { + nErrors++; + pmprintf("Error, line %d: no legend named %s exists\n", + nLines, tokenStringVal); + return -1; + } + QedLegend *legend = legends.at(li); + + eofOK = eofMayFollow; + NextToken(); + + // Parse optional actions list + // + if (token == TOK_ACTIONLIST) { + sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow); + if (sts < 0) + return sts; + } + + QedRoundLED* l = new QedRoundLED(parent, x, y, w, h, legend); + if (l) { + gadgets.append(l); + } else { + perror("no memory for led gadget"); + exit(1); + } + + if (AddMetrics(update, mlp, l)) + nWarnings++; + + if (pmDebug & DBG_TRACE_APPL0) + l->dump(stderr); + return 0; +} + +static int +ParseLegend(int eofMayFollow) +{ + eofOK = 0; + + NextToken(); + if (token != TOK_IDENTIFIER) { + nErrors++; + pmprintf("Error, line %d: name expected for legend\n", nLines); + return -1; + } + QedLegend* legend = new QedLegend(tokenStringVal); + if (legend == NULL) { + nErrors++; + fprintf(stderr, + "Error, line %d: out of memory allocating legend\n", + nLines); + return -1; + } + legends.append(legend); + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "legend:\n name \"%s\"\n", tokenStringVal); + + NextToken(); + if (token != TOK_LPAREN) { + nErrors++; + pmprintf("Error, line %d: `(' expected for legend\n", nLines); + delete legend; + return -1; + } + NextToken(); + + while (token == TOK_INTEGER || + token == TOK_REAL || + token == TOK_DEFAULT) { + double val = 0.0; + int prevToken; + if (token != TOK_DEFAULT) + val = (token == TOK_REAL) ? tokenRealVal : tokenIntVal; + prevToken = token; + NextToken(); + if (token != TOK_IDENTIFIER && token != TOK_STRING) { + nErrors++; + pmprintf("Error, line %d: colour name expected in legend\n", nLines); + delete legend; + return -1; + } + if (prevToken != TOK_DEFAULT) + legend->addThreshold(val, tokenStringVal); + else + legend->setDefaultColor(tokenStringVal); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, " %.2f = %s\n", val, tokenStringVal); + NextToken(); + } + + if (pmDebug & DBG_TRACE_APPL0) + putc('\n', stderr); + + if (token != TOK_RPAREN) { + nErrors++; + pmprintf("Error, line %d: `)' or threshold expected for legend\n", + nLines); + delete legend; + return -1; + } + + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +static int +ParseColorList(int eofMayFollow) +{ + eofOK = 0; + NextToken(); + if (token != TOK_IDENTIFIER) { + nErrors++; + pmprintf("Error, line %d: name expected for colourlist\n", nLines); + return -1; + } + char *name = tokenStringVal; + NextToken(); + + if (token != TOK_LPAREN) { + nErrors++; + pmprintf("Error, line %d: `(' expected for colourlist\n", nLines); + return -1; + } + NextToken(); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "colourlist: name \"%s\"\n", name); + + QedColorList *colorList = new QedColorList(name); + if (colorList == NULL) { + nErrors++; + fprintf(stderr, + "Error, line %d: out of memory allocating colorList\n", + nLines); + return -1; + } + while (token == TOK_IDENTIFIER || token == TOK_STRING) { + colorList->addColor(tokenStringVal); + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, " %s\n", tokenStringVal); + NextToken(); + } + + if (pmDebug & DBG_TRACE_APPL0) + putc('\n', stderr); + + if (token != TOK_RPAREN) { + nErrors++; + pmprintf("Error, line %d: `)' expected for colourlist\n", nLines); + return -1; + } + colorLists.append(colorList); + eofOK = eofMayFollow; + NextToken(); + return 0; +} + +// Config files are typically created by programs rather than specified by the +// user. In the config file header, after the version number, such programs +// should put their argv so that the desktop can restart pmgadgets +// (indirectly) by re-running the program that created the config file. +// +QStringList configArgv; + +void ParseVersion() +{ + if ((token == TOK_IDENTIFIER || token == TOK_STRING) && + strcmp(tokenStringVal, "pmgadgets") == 0) { + eofOK = 0; + NextToken(); + if (token != TOK_INTEGER) { + pmprintf("Error, line %d: bad pmgadgets config file version\n", + nLines); + pmflush(); + exit(1); + } + if (tokenIntVal > 1) { + pmprintf("Error, line %d: you need a newer version pmgadgets to run this\n", + nLines); + pmflush(); + exit(1); + } + else if (tokenIntVal < 1) { + pmprintf("Error, line %d: ludicrous pmgadgets config file version\n", + nLines); + pmflush(); + exit(1); + } + eofOK = 1; + NextToken(); + while (token == TOK_IDENTIFIER || token == TOK_STRING) { + configArgv.append(strdup(tokenStringVal)); + NextToken(); + } + } +} + +int +Parse(void) +{ + int sts; + NextToken(); + setjmp(scannerEofEnv); // come back here on unexpected EOF + + ParseVersion(); + while (token != TOK_EOF) { + eofOK = 0; + switch (token) { + case TOK_LINE: + sts = ParseLine(1); + break; + + case TOK_LABEL: + sts = ParseLabel(1); + break; + + case TOK_BAR: + sts = ParseBar(1); + break; + + case TOK_MULTIBAR: + sts = ParseMultibar(1); + break; + + case TOK_BARGRAPH: + sts = ParseBarGraph(1); + break; + + case TOK_LED: + sts = ParseLed(1); + break; + + case TOK_LEGEND: + sts = ParseLegend(1); + break; + + case TOK_COLOURLIST: + sts = ParseColorList(1); + break; + + case TOK_ACTIONLIST: + sts = ParseActionList(AL_NEED_NAME, 1); + break; + + default: + sts = -1; + nErrors++; + pmprintf("Error, line %u: Bad gadget type\n", nLines); + break; + } + if (sts == -1) { + eofOK = 1; + FindNewStatement(); + } + const unsigned maxErrors = 10; + if (nErrors > maxErrors) { + pmprintf("Too many errors, giving up!\n"); + break; + } + } + if (nErrors) { + pmprintf("%s: configuration file has errors " + "(%d lines parsed, %d errors)\n", pmProgname, nLines, nErrors); + pmflush(); + return -1; + } + if (nWarnings) + pmflush(); + return 0; +} diff --git a/src/pmgadgets/pmgadgets-args.sh b/src/pmgadgets/pmgadgets-args.sh new file mode 100644 index 0000000..78c6fbc --- /dev/null +++ b/src/pmgadgets/pmgadgets-args.sh @@ -0,0 +1,381 @@ +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1996-2002 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +# +# Note on error handling +# +# We assume this is sourced from a shell script that uses $status to pass +# the exit status back to the parent. +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +prog=`basename $0` + +# +# Standard usage and command-line argument parsing for pmgadgets front ends. +# This file should be included by pmgadgets front end scripts to present a +# consistent interface. See pmgadgets(1) for more information. +# + +# +# The front end scripts should call _pmgadgets_usage after their own usage +# information in a subroutine called _usage. The _usage subroutine may be +# called by either _pmgadgets_note or _pmgadgets_args. +# + +_pmgadgets_usage() +{ + echo ' + -C check configuration file and exit + -h host metrics source is PMCD on host + -n pmnsfile use an alternative PMNS + -t interval sample interval [default 2.0 seconds] + -z set reporting timezone to local time of metrics source + -Z timezone set reporting timezone + + -zoom factor make the gadgets bigger by a factor of 1, 2, 3 or 4 + -infofont fontname use fontname for text in info dialogs + -defaultfont fontname use fontname for label gadgets + + -display display-string + -geometry geometry-string + -name name-string + -title title-string' +} + +# +# One of the first actions of a front end script should be to call +# _pmgadgets_args. It sets the following variables: +# +# host the host specified with -h. +# interval the update interval specified by the user, 0 indicates it +# was not specified. The caller must pass this on to pmgadgets, +# unlike $host which is included in $args. +# args The list of args that pmgadgets will comprehend and use. +# otherArgs The arguments pmgadgets will not understand and should be +# handled by the front end script. +# titleArg The title the user prefers. If empty, the title should be +# provided by the front end script. +# prog The name of the program. +# namespace The namespace (including the flag) if specified, else empty +# eg "-n foo" +# msource The metrics source, whether live or an archive, include the +# flag. eg "-h blah" +# + +_pmgadgets_args() +{ + +host="" +args="" +otherArgs="" +titleArg="" +namespace="" +interval=0 +msource="" + +if [ $# -eq 1 -a '$1' = '-\?' ] +then + _usage + status=0 + exit +fi + +while [ $# -gt 0 ] +do + case $1 + in + -g*|-d*|-fg|-bg|-name) + # assume an X11 argument + if [ $# -lt 2 ] + then + _pmgadgets_note Usage-Error error "$prog: X-11 option $1 requires one argument" + #NOTREACHED + fi + args="$args $1 '$2'" + shift + ;; + + -title) + # assume an X11 argument + if [ $# -lt 2 ] + then + echo "$prog: $1 requires one argument" + _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + titleArg="$2" + shift + ;; + + -D|-Z|-delta|-zoom|-infofont|-defaultfont) + if [ $# -lt 2 ] + then + _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + args="$args $1 '$2'" + shift + ;; + + -D*|-Z*|-z|-C) + args="$args $1" + ;; + + -h) + if [ "X$host" != X ] + then + _pmgadgets_note Usage-Error error "$prog: Only one -h option may be specified" + #NOTREACHED + fi + if [ $# -lt 2 ] + then + _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + host=$2 + args="$args -h $2" + msource="-h $host" + shift + ;; + + -n) + if [ $# -lt 2 ] + then + _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + namespace="-n $2" + args="$args -n $2" + shift + ;; + + -t) + if [ $# -lt 2 ] + then + _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + interval=$2 + shift + ;; + + *) + otherArgs="$otherArgs $1" + ;; + + esac + shift +done + +if [ -z "$host" ] +then + host=`pmhostname` + msource="-h $host" +fi +} + +# standard fatal error reporting +# Usage: _pmgadgets_error message goes in here +# _pmgadgets_error -f file +# +_pmgadgets_error() +{ + _pmgadgets_note Error error $* +} + +# standard warning +# Usage: _pmgadgets_warning message goes in here +# _pmgadgets_warning -f file +# +_pmgadgets_warning() +{ + _pmgadgets_note Warning warning $* +} + +# standard info +# Usage: _pmgadgets_info message goes in here +# _pmgadgets_info -f file +# +_pmgadgets_info() +{ + _pmgadgets_note Info info $* +} + +# generic notifier +# Usage: _pmgadgets_note tag icon args ... +# +_pmgadgets_note() +{ + tag=$1; shift + icon=$1; shift + button="" + [ $tag = Error ] && button="-B Quit" + [ $tag = Usage-Error ] && button="-B Quit -b Usage" + + [ X"$PCP_STDERR" = XDISPLAY -a -z "$DISPLAY" ] && unset PCP_STDERR + + if [ $# -eq 2 -a "X$1" = X-f ] + then + case "$PCP_STDERR" + in + DISPLAY) + ans=`$PCP_XCONFIRM_PROG -icon $icon -file $2 -useslider -header "$tag $prog" $button 2>&1` + ;; + '') + echo "$tag:" >&2 + cat $2 >&2 + ans=Quit + ;; + *) + echo "$tag:" >>$PCP_STDERR + cat $2 >>$PCP_STDERR + ans=Quit + ;; + esac + else + case "$PCP_STDERR" + in + DISPLAY) + ans=`$PCP_XCONFIRM_PROG -icon $icon -t "$*" -noframe -header "$tag $prog" $button 2>&1` + ;; + '') + echo "$tag: $*" >&2 + ans=Quit + ;; + *) + echo "$tag: $*" >>$PCP_STDERR + ans=Quit + ;; + esac + fi + + if [ $tag = Usage-Error ] + then + [ $ans = Usage ] && _usage + tag=Error + fi + + if [ $tag = Error ] + then + status=1 + exit + fi +} + +# Fetch metrics +# +# $1 - pmprobe flag +# $2 - metric name +# number - number of values +# $tmp/pmgadgets_result - values +# +_pmgadgets_fetch() +{ + if pmprobe $namespace $msource $1 $2 > $tmp/pmgadgets_fetch 2>&1 + then + tr < $tmp/pmgadgets_fetch ' ' '\012' \ + | tee $tmp/pmgadgets_output \ + | sed \ + -e 's/"//g' \ + -e '1,2d' \ + > $tmp/pmgadgets_result + + number=`sed -n -e 2p < $tmp/pmgadgets_output` + [ $number -le 0 ] && return 1 + else + number=0 + rm -f $tmp/pmgadgets_output $tmp/pmgadgets_result + return 1 + fi + + return 0 +} + +# Fetch the metric values +# +# $1 - metric name +# number - number of values +# $tmp/pmgadgets_result - values +# +_pmgadgets_fetch_values() +{ + _pmgadgets_fetch -v $1 + return $? +} + +# Fetch the metric instance list +# +# $1 - metric name +# number - number of instances +# $tmp/pmgadgets_result - instances +# +_pmgadgets_fetch_indom() +{ + _pmgadgets_fetch -I $1 + return $? +} + +# Convert pmprobe/pminfo error message into something useful and +# consistent +# +_pmgadgets_fetch_mesg() +{ + $PCP_AWK_PROG ' +$1 == "pmprobe:" { $1 = "'$prog':"; print; exit } +$1 == "pminfo:" { $1 = "'$prog':"; print; exit } +$1 == "Error:" { $1 = ":"; + printf("%s: %s%s\n", "'$prog'", metric, $0); exit } +$1 == "inst" { exit } +NF == 1 { metric = $1; next } +NF == 0 { next } +NF == 2 && $2 == "0" { printf("%s: %s: No values available\n", "'$prog'", $1); exit} + { $2 = ":"; print "'$prog': " $0; exit}' \ + | sed "s/ : /: /" \ + | fmt +} + +# Generate error metric for failed fetch +# +_pmgadgets_fetch_fail() +{ + cat $tmp/pmgadgets_fetch | _pmgadgets_fetch_mesg >> $tmp/msg + echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp/msg + _pmgadgets_error -f $tmp/msg + # NOTREACHED +} + +# Generate warning message for failed fetch +# +_pmgadgets_fetch_warn() +{ + cat $tmp/pmgadgets_fetch | _pmgadgets_fetch_mesg >> $tmp/msg + echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp/msg + _pmgadgets_warning -f $tmp/msg +} + +# Check that $OPTARG for option $1 is a positive integer +# ...note the creative use of unary - to prevent leading signs +# +_pmgadgets_unsigned() +{ + if [ "X-$OPTARG" != "X`expr 0 + -$OPTARG 2>/dev/null`" ] + then + _pmgadgets_error "$prog: -$1 option must have a positive integral argument" + # NOTREACHED + fi +} diff --git a/src/pmgadgets/pmgadgets.cpp b/src/pmgadgets/pmgadgets.cpp new file mode 100644 index 0000000..f0a1977 --- /dev/null +++ b/src/pmgadgets/pmgadgets.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013, Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <QtGui> +#include "pmgadgets.h" +#include "qed_led.h" +#include "qed_line.h" + +PmGadgets::PmGadgets(QWidget *parent) : QDialog(parent) +{ + QedRoundLED *redLED = new QedRoundLED(this, Qt::red); + QedSquareLED *blueLED = new QedSquareLED(this, Qt::blue); + QedRoundLED *greenLED = new QedRoundLED(this, Qt::green); + QedSquareLED *grayLED = new QedSquareLED(this, Qt::darkGray); + QedLine *horizontal = new QedLine(this, 2, Qt::Horizontal); + + my.mainLayout = new QGridLayout; + my.mainLayout->addWidget(redLED, 0, 0); + my.mainLayout->addWidget(blueLED, 0, 1); + my.mainLayout->addWidget(greenLED, 0, 2); + my.mainLayout->addWidget(grayLED, 1, 0); + my.mainLayout->addWidget(horizontal, 1, 1); + + setLayout(my.mainLayout); + setWindowsFlags(); +} + +void PmGadgets::setWindowsFlags(void) +{ + Qt::WindowFlags flags = windowFlags(); + // removal + flags &= ~Qt::WindowMinimizeButtonHint; + flags &= ~Qt::WindowCloseButtonHint; + // addition + flags |= Qt::WindowStaysOnTopHint; + //flags |= Qt::FramelessWindowHint; + setWindowFlags(flags); +} + +void PmGadgets::help() +{ + QMessageBox::information(this, tr("PmGadgets Help"), + tr("Performance Co-Pilot graphical gadgets and great gizmos.")); +} diff --git a/src/pmgadgets/pmgadgets.h b/src/pmgadgets/pmgadgets.h new file mode 100644 index 0000000..8ecc3f9 --- /dev/null +++ b/src/pmgadgets/pmgadgets.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013, Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef PMGADGETS_H +#define PMGADGETS_H + +#include <QGridLayout> +#include <QDialog> +#include <QList> + +class PmGadgets : public QDialog +{ + Q_OBJECT + +public: + PmGadgets(QWidget *parent = 0); + +private slots: + void help(); + +private: + void setWindowsFlags(); + + struct { + QList<QWidget *> widgets; + QGridLayout *mainLayout; + } my; +}; + +#endif // PMGADGETS_H diff --git a/src/pmgadgets/pmgadgets.info.in b/src/pmgadgets/pmgadgets.info.in new file mode 100644 index 0000000..5ace93b --- /dev/null +++ b/src/pmgadgets/pmgadgets.info.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleIconFile</key> + <string>pmgadgets.icns</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>@pkg_version@</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleExecutable</key> + <string>pmgadgets</string> + <key>CFBundleIdentifier</key> + <string>com.redhat.pmgadgets</string> +</dict> +</plist> diff --git a/src/pmgadgets/pmgadgets.pro b/src/pmgadgets/pmgadgets.pro new file mode 100644 index 0000000..72c53b1 --- /dev/null +++ b/src/pmgadgets/pmgadgets.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +LANGUAGE = C++ +HEADERS = pmgadgets.h tokens.h +SOURCES = pmgadgets.cpp main.cpp parse.cpp +FLEXSOURCES = lex.l +ICON = pmgadgets.icns +RESOURCES = pmgadgets.qrc +CONFIG += qt warn_on +INCLUDEPATH += ../include ../libpcp_qed/src +LIBS += -L../libpcp_qed/src -L../libpcp_qmc/src/$$DESTDIR +LIBS += -lpcp -lpcp_qed +win32:LIBS += -lwsock32 +QT += network +QMAKE_INFO_PLIST = pmgadgets.info +QMAKE_EXTRA_COMPILERS += flex +QMAKE_CXXFLAGS += $$(PCP_CFLAGS) + +flex.commands = flex ${QMAKE_FILE_IN} +flex.input = FLEXSOURCES +flex.output = lex.yy.c +flex.variable_out = SOURCES +flex.depends = tokens.h +flex.name = flex diff --git a/src/pmgadgets/pmgadgets.qrc b/src/pmgadgets/pmgadgets.qrc new file mode 100644 index 0000000..3e34d21 --- /dev/null +++ b/src/pmgadgets/pmgadgets.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/aboutpcp.png</file> + <file>images/pmtime.png</file> +</qresource> +</RCC> diff --git a/src/pmgadgets/pmgadgets.sh.IN b/src/pmgadgets/pmgadgets.sh.IN new file mode 100644 index 0000000..1e02c63 --- /dev/null +++ b/src/pmgadgets/pmgadgets.sh.IN @@ -0,0 +1,2 @@ +#!/bin/sh +exec PKG_MAC_DIR/MacOS/pmgadgets "$@" diff --git a/src/pmgadgets/pmgcisco.sh b/src/pmgadgets/pmgcisco.sh new file mode 100755 index 0000000..8d57d80 --- /dev/null +++ b/src/pmgadgets/pmgcisco.sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +status=1 +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -fr $tmp; exit \$status" 0 1 2 3 15 + +# usage - print out the usage of program +# +_usage() +{ + echo >$tmp/msg "Usage: $prog [options] [pmgadgets options]" + echo >>$tmp/msg ' +options: + -t interval sample interval [default 120.0 seconds] + -V verbose/diagnostic output + -x pixels width of each bar graph [default 31] + -y pixels height of each bar graph [default 31] + +pmgadgets(1) options:' + _pmgadgets_usage >>$tmp/msg + echo >>$tmp/msg + _pmgadgets_info -f $tmp/msg +} + +# default variables +# +verbose=false +plot_y=2 +bar_y=31 +bar_x=31 + +# keep the original command line for restart after logout/login +# +echo -n "pmgadgets 1 \"pmgcisco\"" >$tmp/conf +for arg +do + echo -n " \"$arg\"" >>$tmp/conf +done +echo "" >> $tmp/conf + +# get arguments +# +. $PCP_SHARE_DIR/lib/pmgadgets-args + +_pmgadgets_args "$@" +[ "$interval" = 0 ] && interval=120sec +args="$args -t $interval" + +if [ -n "$otherArgs" ] +then + while getopts "Vx:y:?" c $otherArgs + do + case $c + in + V) + verbose=true + ;; + x) # -x pixels (width of bar graph) + bar_x=$OPTARG + ;; + y) # -y pixels (height of bar graph) + bar_y=$OPTARG + ;; + ?) + _usage + status=1 + exit + ;; + esac + done + + set -- $otherArgs + shift `expr $OPTIND - 1` + + if [ $# -gt 0 ] + then + _usage + status=1 + exit + fi +fi + +# check on metric availability +# +if _pmgadgets_fetch_values cisco.bandwidth +then + if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ] + then + _pmgadgets_fetch_fail "get Cisco metrics" + #NOTREACHED + fi +else + _pmgadgets_fetch_fail "get Cisco metrics" + #NOTREACHED +fi + +# output the config file +# +pminfo $msource -f cisco.bandwidth \ +| sed -n \ + -e 's/:/ /' \ + -e '/ value /{ +s/"] value / / +s/.*"// +p +}' \ +| sort >$tmp/data + +# $tmp/data has lines of the form <hostname> <interface> <bandwidth> +# try to truncate the <hostname> by dropping components from the +# right hand end, but retain uniqueness ... the truncated name is used +# for the label gadget +# +sed -e 's/ .*//' <$tmp/data | sort -u >$tmp/hostnames +trim=1 +rm -f $tmp/done +while true +do + $PCP_AWK_PROG -F . <$tmp/hostnames >$tmp/tmp ' +NR == 1 { last_part=NF-'$trim'+1 + if (last_part < 2) { fail=1; exit } + want=$last_part + } +NF >= last_part { if ($last_part != want) { fail=2; exit } + next + } + { fail=3; exit } +END { # print "fail=" fail + if (fail) print "" >"'$tmp/done'" + }' + [ -f $tmp/done ] && break + trim=`expr $trim + 1` +done + +$PCP_AWK_PROG -F . <$tmp/hostnames >$tmp/map ' + { printf "%s",$0 + for (i=1; i<=NF-'$trim'+1; i++) { + if (i == 1) printf "\t%s",$i + else printf ".%s",$i + } + print "" + }' + +# this produces <hostname> <shortname> <interface> <bandwidth> +# +join $tmp/map $tmp/data \ +| $PCP_AWK_PROG >>$tmp/conf ' + +BEGIN { y = 15; step_y = '$bar_y'+31; step_x = '$bar_x'+16 } + { if (!seen[$4]) { + print "_legend link" $4 " (" + print " _default \"#608080\"" + print " " int(0.10*$4) " green" + print " " int(0.70*$4) " yellow" + print " " int(0.80*$4) " orange" + print " " int(0.90*$4) " red" + print ")" + print "" + seen[$4] = 1 + } + + print "_label",5,y,"\"" $2 ":" $3 "\" \"7x13bold\"" + + print "_label",5,y+11,"\"in\" \"7x13\"" + print "_led",5+'$bar_x'-6,y+4,8,8 + print "_metric cisco.bytes_in[\"" $1 ":" $3 "\"]" + print "_legend link" $4 + + print "_bargraph",5,y+14,'$bar_x,$bar_y' + print "_metric cisco.bytes_in[\"" $1 ":" $3 "\"]" + print "_max " $4 + + print "_label",step_x+5,y+11,"\"out\" \"7x13\"" + print "_led",step_x+5+'$bar_x'-6,y+4,8,8 + print "_metric cisco.bytes_out[\"" $1 ":" $3 "\"]" + print "_legend link" $4 + + print "_bargraph",step_x+5,y+14,'$bar_x,$bar_y' + print "_metric cisco.bytes_out[\"" $1 ":" $3 "\"]" + print "_max " $4 + + print "" + y += step_y + } +END { value='"`echo $interval | sed -e 's/[^0-9.].*/; scale=\"&\"/' | tr A-Z a-z`"' + # tt is total time for plot in seconds, each bar is 1 pixel wide + tt = value * ('$bar_x' - 1) + if (scale ~ /^min/ || scale == "m") tt *= 60 + else if (scale ~ /^hour/ || scale == "h") tt *= 3600 + else if (scale ~ /^day/ || scale == "d") tt *= 24*3600 + printf "_label 5 %d \"History: ",y + if (tt > 24*3600) { + xxx = int(tt/24*3600) + printf "%dday",xxx + if (xxx > 1) printf "s" + rem = int(0.5+(tt%(24*3600)/3600)) + if (rem > 1) + printf " %dhours",rem + else if (rem == 1) + printf " 1hour" + } + else if (tt > 3600) { + xxx = int(tt/3600) + printf "%dhour",xxx + if (xxx > 1) printf "s" + rem = int(0.5+(tt%(3600)/60)) + if (rem > 1) + printf " %dmin",rem + else if (rem == 1) + printf " 1min" + } + else if (tt > 60) { + xxx = int(tt/60) + printf "%dmin",xxx + if (xxx > 1) printf "s" + rem = int(0.5+tt%60) + if (rem > 1) + printf " %dsecs",rem + else if (rem == 1) + printf " 1sec" + } + else { + if (tt > 1) + printf "%dsecs",tt + else if (tt == 1) + printf "1sec" + } + print "\"" + }' + +$verbose && cat $tmp/conf +eval pmgadgets <$tmp/conf $args + +status=$? +exit diff --git a/src/pmgadgets/pmgcluster.sh b/src/pmgadgets/pmgcluster.sh new file mode 100755 index 0000000..ddd2229 --- /dev/null +++ b/src/pmgadgets/pmgcluster.sh @@ -0,0 +1,281 @@ +#!/bin/sh +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +# This script uses pmgsys to layout gadgets for a series of hosts and then +# combines the resulting config files into one big one and invokes pmgadgets +# on the result. + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +status=1 +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -fr $tmp; exit \$status" 0 1 2 3 15 +verbose="false" +prog=`basename $0` +nhosts=0 +hosts="" + +# usage - print out the usage of program +# +_usage() +{ + echo >$tmp/msg "Usage: $prog [options] [pmgadgets options] [host ...]" + echo >>$tmp/msg ' + +Default hosts are specified in /etc/nodes (or /etc/ace/nodes). + +options: + -H nodes file specifying nodes in cluster + [default $PCP_CLUSTER_CONFIG or /etc/nodes] + -r rows number of rows for layout + -l suppress host name labels + -L label title label for pmgagdets layout + -V verbose (print pmgadgets configuration) + +pmgadgets(1) options:' + _pmgadgets_usage | sed -e '/.*-h host.*/d' >>$tmp/msg + _pmgadgets_info -f $tmp/msg +} + +# keep the original command line for restart after logout/login +# +echo -n "pmgadgets 1 \"pmgcluster\"" >$tmp/conf +for arg +do + echo -n " \"$arg\"" >>$tmp/conf +done +echo >>$tmp/conf + +# get arguments +# +. $PCP_SHARE_DIR/lib/pmgadgets-args + + +# Have to pre-parse the options for -L coz getopts can't handle multiword +# strings when using $otherArgs +# +if [ -z "$PCP_CLUSTER_CONFIG" ] +then + nodesfile=/etc/nodes + [ ! -f "$nodesfile" ] && nodesfile=/etc/ace/nodes +else + nodesfile="$PCP_CLUSTER_CONFIG" + if [ ! -f "$nodesfile" ] + then + echo "Error: \"$nodesfile\" specified in \$PCP_CLUSTER_CONFIG: file not found" + _usage + status=1 + exit + fi +fi + +while [ $# -gt 0 ] +do + case $1 + in + -h) hosts="$2" + nhosts=1 + shift + ;; + -L) titlelabel="$2" + shift + ;; + *) pmgargs="$pmgargs $1" + ;; + esac + shift +done +set -- $pmgargs + +_pmgadgets_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "H:r:lV?" c $otherArgs + do + case $c + in + H) nodesfile=$OPTARG + if [ ! -f "$nodesfile" ] + then + echo "$prog Error: \"$nodesfile\" for -H file not found" + _usage + status=1 + exit + fi + ;; + r) rows=$OPTARG + ;; + l) pmgsysargs="$pmgsysargs -l" + ;; + V) verbose="true" + ;; + ?) _usage + status=1 + exit + ;; + esac + done + + set -- $otherArgs + shift `expr $OPTIND - 1` +else + set -- +fi + +if [ "$interval" != "0" ] +then + args="$args -t $interval" +fi + +if [ ! -z "$titleArg" ] +then + args="$args -title $titleArg" +fi + +ytitle=0 +if [ ! -z "$titlelabel" ] +then + ytitle=16 + echo "_label 6 13 \"$titlelabel\" \"7x13bold\"" >> $tmp/conf +fi + +if [ "$nhosts" -eq 0 ] +then + nhosts=$# + if [ $nhosts -eq 0 ] + then + if [ -f "$nodesfile" ] + then + hosts=`sed -e 's/[# ].*$//' $nodesfile` + nhosts=`echo $hosts | wc -w` + fi + else + hosts=$* + fi +fi + +if [ $nhosts -eq 0 ] +then + _usage + status=1 + exit +fi + + +ox=0 +oy=$ytitle +row=0 +col=0 +ygap=20 +xgap=50 +xmax=0 +ymax=0 +hostnum=0 + +if [ -z "$rows" ] +then + rows=`echo "sqrt($nhosts)" | bc` +fi + +cols=`expr $nhosts / $rows` + +for host in $hosts +do + if ! pmgsys $pmgsysargs -C -V -h $host > $tmp/pmgsys 2> $tmp/pmgsys.err + then + sed -e "s/pmgsys/$prog/g" $tmp/pmgsys.err >&2 + continue + fi + + sed -e 's/^pmgadgets/# pmgadgets/' \ + -e "/.*_update.*/d" \ + -e "s/-C//g" \ + -e "s/cpuActions/"$hostnum"_cpuActions/g" \ + -e "s/loadActions/"$hostnum"_loadActions/g" \ + -e "s/netActions/"$hostnum"_netActions/g" \ + -e "s/diskActions/"$hostnum"_diskActions/g" \ + -e "s/diskLegend/"$hostnum"_diskLegend/g" \ + -e "s/cpuColours/"$hostnum"_cpuColours/g" \ + -e "s/netColours/"$hostnum"_netColours/g" \ + -e "s/kernel\./"$host":kernel./g" \ + -e "s/disk\./"$host":disk./g" \ + -e "s/mem\./"$host":mem./g" \ + -e "s/swap\./"$host":swap./g" \ + -e "s/network\./"$host":network./g" \ + $tmp/pmgsys \ + | $PCP_AWK_PROG ' + /^_/ && $2 ~ /^[0-9]+$/ && $3 ~ /^[0-9]+$/ { + printf "%s %d %d ", $1, $2 + '$ox', $3 + '$oy' + for (i=4; i <= NF; i++) + printf "%s ", $i + printf "\n" + next + } + { + print + }' | tee $tmp/$host >> $tmp/conf + + # + # set xmax and ymax for this host + # + eval `$PCP_AWK_PROG ' + /^_/ && $2 ~ /^[0-9]+$/ && $3 ~ /^[0-9]+$/ { + if (xmax < $2) + xmax = $2 + if (ymax < $3) + ymax = $3 + } + END { + printf "host_xmax=%d; ymax=%d", xmax, ymax + }' $tmp/$host` + + [ $host_xmax -gt $xmax ] && xmax=$host_xmax + + row=`expr $row + 1` + if [ $row -eq $rows ] + then + row=0 + oy=$ytitle + ox=`expr $xmax + $xgap` + col=`expr $col + 1` + else + oy=`expr $ymax + $ygap` + fi + + rm -f $tmp/$host + hostnum=`expr $hostnum + 1` +done + +rm -f $tmp/pmgsys $tmp/pmgsys.err + +if $verbose +then + cat $tmp/conf +fi + +if [ $hostnum -le 0 ] +then + echo "$prog: unable to monitor any hosts" >&2 + status=1 + exit +fi + +eval pmgadgets $args <$tmp/conf + +status=$? +exit diff --git a/src/pmgadgets/pmgshping.sh b/src/pmgadgets/pmgshping.sh new file mode 100755 index 0000000..455b979 --- /dev/null +++ b/src/pmgadgets/pmgshping.sh @@ -0,0 +1,242 @@ +#!/bin/sh +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +status=1 +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -fr $tmp; exit \$status" 0 1 2 3 15 + +# usage - print out the usage of program +# +_usage() +{ + echo >$tmp/msg "Usage: $prog [options] [pmgadgets options]" + echo >>$tmp/msg ' +options: + -e use espping metrics, instead of shping + -G gap set distance between columns (pixels) + -V verbose/diagnostic output + +pmgadgets(1) options:' + _pmgadgets_usage >>$tmp/msg + echo >>$tmp/msg + _pmgadgets_info -f $tmp/msg +} + +# keep the original command line for restart after logout/login +# +echo -n "pmgadgets 1 \"pmgshping\"" >$tmp/conf +for arg +do + echo -n " \"$arg\"" >>$tmp/conf +done +echo "" >>$tmp/conf + +# get arguments +# +. $PCP_SHARE_DIR/lib/pmgadgets-args + +# default variables +# +gap="" +ping=shping +verbose=false + +_pmgadgets_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "eG:V?" c $otherArgs + do + case $c + in + e) + ping=espping + ;; + G) + gap=$OPTARG + ;; + V) + verbose=true + ;; + ?) + _usage + status=1 + exit + ;; + esac + done + + set -- $otherArgs + shift `expr $OPTIND - 1` + + if [ $# -gt 0 ] + then + _usage + status=1 + exit + fi +fi + +# check on metric availability +# +if _pmgadgets_fetch_indom ${ping}.time.real +then + if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ] + then + _pmgadgets_fetch_fail "get $ping metrics" + #NOTREACHED + fi + # save number of instances of ${ping}.time.real to be used below + # + mynumber=$number +else + _pmgadgets_fetch_fail "get $ping metrics" + #NOTREACHED +fi + +cp $tmp/pmgadgets_result $tmp/info + +# default is update at the same frequency as the $ping PMDA +# runs the commands +# +if [ $interval = 0 ] +then + result='' + if _pmgadgets_fetch_values ${ping}.control.cycletime + then + if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ] + then + _pmgadgets_fetch_fail "get $ping cycle time" + #NOTREACHED + fi + else + _pmgadgets_fetch_fail "get $ping cycle time" + #NOTREACHED + fi + + interval=`cat $tmp/pmgadgets_result` + if [ -z "$interval" ] + then + # if you can't get this, probably doomed, but use 10sec as a fallback + # + interval=10sec + else + interval="${interval}sec" + fi +fi +args="$args -t $interval" + +# $ping PMDA may not be installed here ... fake out $ping.RealTime +# config file for pmchart and use stacked bar instead of line plot +# because the time interval may be quite long +pmchart_config=$tmp/config +cat >$tmp/config <<End-of-File +#pmchart +Version 2.0 host dynamic +Chart Title "Elapsed Time for $ping Commands" Style stacking + Plot Color #-cycle Host * Metric $ping.time.real Matching .* +End-of-File + +# output the config file +# +start_x=8 # starting x co-ord +start_y=8 # starting y co-ord +diam=10 # LED diameter +if [ -z "$gap" ] +then + gap=50 # column width + [ "$ping" = "espping" ] && gap=150 # pmdaespping instance names are long +fi + +pmchart_opts="-t $interval -c $pmchart_config" +[ ! -z "$host" ] && pmchart_opts="$pmchart_opts -h $host" +[ ! -z "$namespace" ] && pmchart_opts="$pmchart_opts $namespace" + +cat <<End-of-File >>$tmp/conf +_actions Actions ( +"pmchart" "/usr/bin/X11/xconfirm -icon info -t 'Values plotted by pmchart will not appear' -t 'until after the first time interval ($interval)' -B Dismiss >/dev/null & pmchart $pmchart_opts" +) + +_legend Status ( + _default "#608080" + 1 yellow + 2 orange + 3 red + 4 purple +) + +_legend Response ( + _default "#608080" + 0 green3 + 1000 yellow + 5000 orange + 10000 red +) +End-of-File + +# get instance information from metrics +# +for tag in `cat $tmp/info` +do + echo "$tag" +done \ +| $PCP_AWK_PROG -v ping=$ping >>$tmp/conf ' +BEGIN { x = '$start_x'; y = '$start_y'; diam = '$diam'; gap = '$gap'; left = 1 + printf "_label %d %d \"%s\"\n",x,y+8,"'$host'" + y += 12 + printf "_label %d %d \"%s\"\n",x,y+8,"S" + x = x + diam + 2 + printf "_label %d %d \"%s\"\n",x,y+8,"Response" + if ('$mynumber' > 1) { + x = x + diam + 2 + x = x + gap + printf "_label %d %d \"%s\"\n",x,y+8,"S" + x = x + diam + 2 + printf "_label %d %d \"%s\"\n",x,y+8,"Response" + } + y += 12 + } + { + if (left) { + print "" + x = '$start_x' + } + else + x = x + gap + print "_led " x " " y " " diam " " diam + print " _metric " ping".status[\"" $1 "\"]" + print " _legend Status" + x = x + diam + 2 + print "_led " x " " y " " diam " " diam + print " _metric " ping ".time.real[\"" $1 "\"]" + print " _legend Response" + print " _actions Actions" + x = x + diam + 2 + printf "_label %d %d \"%s\"\n",x,y+diam-2,$1 + if (!left) { + y = y + diam + 2 + } + left = 1-left + }' + +$verbose && cat $tmp/conf +eval pmgadgets <$tmp/conf $args + +status=$? +exit diff --git a/src/pmgadgets/pmgsys.py b/src/pmgadgets/pmgsys.py new file mode 100755 index 0000000..5f8a1df --- /dev/null +++ b/src/pmgadgets/pmgsys.py @@ -0,0 +1,380 @@ +#!/usr/bin/python +# +# Copyright (C) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +""" Rewrite of pmgsys (originally a C++ application) in python + + Needs to handle the following options at some point: + -h (host), -cpudelta (interval), -l (label), -v (verbose), + -c (config), -zoom (factor), ... rest through to pmgadgets. +""" + +from sys import argv +from pcp import pmapi +from math import sqrt +from cpmapi import PM_TYPE_U32 + +PCPARGS = "" # for pmgadgets-launched child processes to use + +# magic numbers, from original pmgsys algorithms +CPUDELTA = 0.5 +LOADDELTA = 5 +FONTASCENT = 7 +DOLABEL = 1 +HSPACE = 5 +VSPACE = 2 +CPUWIDTH = 30 +CPUHEIGHT = 4 +LOADWIDTH = CPUWIDTH +LOADHEIGHT = 4 * CPUHEIGHT + 3 * VSPACE +MEMWIDTH = CPUHEIGHT +MEMHEIGHT = LOADHEIGHT +DISKSIZE = 6 +NETWIDTH = CPUWIDTH +NETHEIGHT = CPUHEIGHT + +class Machine(object): + def __init__(self, context): + # pcp context + self.context = context + # hardware bits + self.ncpu = 0 + self.ndisk = 0 + self.niface = 0 + self.ndiskmaps = 0 + self.memory = 0 + # external names + self.cpus = [] + self.disks = [] + self.parts = [] + self.ifaces = [] + self.diskmaps = [] + + def get_hinv(self): + """ Extract counts of CPUs, disks, interfaces and memory size + """ + hinv = ('hinv.ncpu', 'hinv.ndisk', 'hinv.ninterface', 'hinv.physmem') + pmids = self.context.pmLookupName(hinv) + descs = self.context.pmLookupDescs(pmids) + result = self.context.pmFetch(pmids) + hardware = [0, 0, 0, 0] + for x in xrange(4): + atom = self.context.pmExtractValue( + result.contents.get_valfmt(x), + result.contents.get_vlist(x, 0), + descs[x].contents.type, PM_TYPE_U32) + hardware[x] = atom.ul + context.pmFreeResult(result) + self.ncpu = hardware[0] + self.ndisk = hardware[1] + self.niface = hardware[2] + self.memory = hardware[3] + + def get_names(self): + """ Extract names of CPUs, disks and network interfaces. + """ + inst = ('kernel.percpu.cpu.user', # expand CPU names + 'disk.dev.total', # expand disk names + 'disk.partitions.total', # expand disk partition names + 'network.interface.total.bytes') # expand network interface names + pmids = self.context.pmLookupName(inst) + descs = self.context.pmLookupDescs(pmids) + + (inst, self.cpus) = self.context.pmGetInDom(descs[0]) + (inst, self.disks) = self.context.pmGetInDom(descs[1]) + (inst, self.parts) = self.context.pmGetInDom(descs[2]) + (inst, self.ifaces) = self.context.pmGetInDom(descs[3]) + + def details(self): + print "CPUs: ", self.ncpu + print "CPU names: ", self.cpus + print "Disks: ", self.ndisk + print "Disk names: ", self.disks + print "Partition names: ", self.parts + print "Interfaces: ", self.niface + print "Interface names: ", self.ifaces + print "Memory: ", self.memory + + def get_diskmaps(self): + """ Produce disk -> partition mappings + This means grouping "sda1 sda2 sda3 sdb1" into two mappings + - "sda" -> (sda1, sda2, sda3) and "sdb" -> (sdb1); done via + the disk.dev.total and disk.partitions.total metrics. + (original: controller -> disk mappings, but ENODATA) + """ + return 0 + + def inventory(self): + """ Wrap calls to getting counts and subsystem names + """ + self.get_hinv() + self.get_names() + self.get_diskmaps() + + def gadgetize(self): + """ Generate a pmgadgets configuration for this host + """ + print "pmgadgets 1", # follow with command line + for arg in argv: + print "\"%s\"" % (arg), + print + + rows = 1 + ctiles = int((self.ncpu - 1) / 4 + 1) # always at least one cpu + ntiles = int((self.niface - 1) / 4 + 1) + if ctiles > 3: + cr = int(math.sqrt(ctiles)) + if cr > rows: + rows = cr + if ntiles > 3: + nr = int(math.sqrt(ntiles)) + if nr > rows: + rows = nr + + baseY = VSPACE + if DOLABEL == 1: + y = FONTASCENT + VSPACE + hostname = self.context.pmGetContextHostName() + print "_label %d %d \"%s\"" % (HSPACE, y, hostname) + baseY += y + baseX = maxX = HSPACE + maxY = baseY + + print "_actions cpuActions (" + print " \"pmchart\"\t\t\"pmchart -c CPU%s\"" % (PCPARGS) + print " \"mpvis *\"\t\t\"mpvis%s\" _default" % (PCPARGS) + # original had IRIX gr_top and gr_osview tools next; + # perhaps some fine day we could implement these as + # pmgadgets front-end tools (certainly the latter) + print ")" + + y = baseY + FONTASCENT + x = baseX + print "_label %d %d \"CPU\"" % (x, y) + print " _actions cpuActions\n" + # original: "these should match the colours in mpvis" + print "_colourlist cpuColours (blue3 red3 yellow3 cyan3 green3)" + + # place the CPU bars + y += VSPACE + ccols = int((ctiles + rows - 1) / rows) + + cpu = 0 + for rc in range(0, self.ncpu): + for ct in range(0, ccols): + tc = 0 + while (cpu < self.ncpu and tc < 4): + print "_multibar %d %d %d %d" % (x, y, CPUWIDTH, CPUHEIGHT) + print(" _update %f" % (CPUDELTA)).rstrip('0').rstrip('.') + print " _metrics (" + print "\tkernel.percpu.cpu.user[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.sys[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.intr[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.wait.total[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.idle[\"%s\"]" % (self.cpus[cpu]) + print " )" + print " _maximum 0.0\n" + print " _colourlist cpuColours" + print " _actions cpuActions\n" + cpu += 1 + tc += 1 + y += VSPACE + CPUHEIGHT + + if maxY < y: + maxY = y + y -= (VSPACE + CPUHEIGHT) * tc + x += HSPACE + CPUWIDTH + + y += (CPUHEIGHT + VSPACE) * 4 + VSPACE + if (maxX < x): + maxX = x + x = baseX + + baseX += (HSPACE + CPUWIDTH) * ccols + + # The load gadget and its label + print "_actions loadActions (" + print " \"pmchart *\"", + print "\t\"pmchart -c LoadAvg%s\" _default" % (PCPARGS) + # original had IRIX gr_top here + print ")" + print "_label %d %d \"Load\"" % (baseX, baseY + FONTASCENT) + print " _actions loadActions" + print + + y = VSPACE + baseY + FONTASCENT + + i = y + LOADHEIGHT + if (i > maxY): + maxY = i + + print "_bargraph %d %d %d %d" % (baseX, y, LOADWIDTH, LOADHEIGHT) + print(" _update %f" % (LOADDELTA)).rstrip('0').rstrip('.') + print " _metric kernel.all.load[\"1 minute\"]" + print " _max 1.0" + print " _actions loadActions" + + # For more than one row, stack LoadAvg on top of Memory. + # + # Move baseX just after the right hand side of the memory, so + # we don't have to do anything special for the netifs. For the + # sake of argument, consider total width occupied by memory + # gauges equal to total width of loadavg graph + if (rows > 1): + y += LOADHEIGHT + VSPACE + baseX += LOADWIDTH + HSPACE + else: + y += baseY + baseX += LOADWIDTH * 2 + HSPACE * 2 + + # The memory gadgets and their label (platform-specific!) + x = baseX - LOADWIDTH - HSPACE + y += FONTASCENT + print "_label %d %d \"Mem\"\n" % (x, y) + print "_colourlist memColours (cyan1 red yellow green)\n" + y += VSPACE + print "_multibar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT) + print " _update 0.5" + print " _metrics (" + print "\tmem.util.cached" + print "\tmem.util.bufmem" + print "\tmem.util.other" + print "\tmem.util.free" + print " )" + print " _colourlist memColours" + x += HSPACE + MEMWIDTH + print "_bar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT) + print " _metric swap.pagesout" + print " _vertical" + + # Check for the max horizontal offset + i = y + MEMHEIGHT + if (i > maxY): + maxY = i + + # The network bars and their label + print "_colourlist netColours (aquamarine orange)" + print "_actions netActions (" + print " \"pmchart-packets *\"", + print "\t\"pmchart -c NetPackets%s\" _default" % (PCPARGS) + print " \"pmchart-bytes\"", + print "\t\t\"pmchart -c NetBytes%s\"" % (PCPARGS) + # original had netstat within an xterm here, next + print ")" + + y = baseY + FONTASCENT + x = baseX + + print "_label %d %d \"Net\"" % (x, y) + print " _actions netActions\n" + + y += VSPACE + + ncols = int((ntiles + rows - 1) / rows) + + ni = 0 + while ni < self.niface: + for nt in range(0, ncols): + tc = 0 + while ni < self.niface and tc < 4: + print "_multibar %d %d %d %d" % (x, y, NETWIDTH, NETHEIGHT) + print " _metrics (" + print "\tnetwork.interface.in.bytes[\"%s\"]" % (self.ifaces[ni]) + print "\tnetwork.interface.out.bytes[\"%s\"]" % (self.ifaces[ni]) + print " )" + print "_colourlist netColours" + print " _actions netActions" + y += NETHEIGHT + VSPACE + + tc += 1 + ni += 1 + + if maxY < y: + maxY = y + y -= (NETHEIGHT + VSPACE) * tc + x += NETWIDTH + HSPACE + if maxX < x: + maxX = x + y += (NETHEIGHT + VSPACE) * 4 + VSPACE + x = baseX + print + + # Disks + dir = 1 + + print "_actions diskActions (" + print " \"pmchart\"", + print "\t\t\"pmchart -c Disk%s\"" % (PCPARGS) + print " \"dkvis *\"", + print "\t\t\"dkvis%s\" _default" % (PCPARGS) + print ")" + + x = HSPACE + y = maxY + FONTASCENT + 2 * VSPACE + print "_label %d %d \"Disk\"" % (x, y) + print " _actions diskActions\n" + print "_legend diskLegend (" + print " _default green3" + print " 15 yellow" + print " 40 orange" + print " 75 red" + print ")" + + x += CPUWIDTH + HSPACE + # this only works if FONTASCENT >= ledSize + y -= DISKSIZE + thickness = 4 + halfDiskSize = int((DISKSIZE - thickness) / 2) + + for i in range(0, self.ndiskmaps): + mapping = self.diskmaps[i] + for j in range(0, len(mappings)): + if j > 0: + if oldX < x: # moved to right + lx = x - VSPACE - 1 + ly = y + halfDiskSize + lw = VSPACE + 2 + lh = thickness + elif oldX > x: # moved to left + lx = oldX - VSPACE - 1 + ly = y + halfDiskSize + lw = VSPACE + 2 + lh = thickness + else: # moved down + lx = x + halfDiskSize + ly = oldY + DISKSIZE - 1 + lw = thickness + lh = VSPACE + 2 + print "_line %d %d %d %d" % (lx, ly, lw, lh) + print "_led %d %d %d %d" % (x, y, DISKSIZE, DISKSIZE) + print " _metric disk.dev.total[\"%s\"]" % (mapping.name()) + print " _legend diskLegend" + print " _actions diskActions" + oldX = x + oldY = y + xStep = dir * (DISKSIZE + VSPACE) # use VSPACE (tighter packing) + x += xStep + if x > maxX - DISKSIZE or x <= HSPACE: + x -= xStep + y += DISKSIZE + VSPACE + dir = -dir + + +if __name__ == '__main__': + context = pmapi.pmContext() + machine = Machine(context) + machine.inventory() + # machine.details() + machine.gadgetize() + diff --git a/src/pmgadgets/tokens.h b/src/pmgadgets/tokens.h new file mode 100644 index 0000000..b299bf8 --- /dev/null +++ b/src/pmgadgets/tokens.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* gadget tokens */ +#define TOK_LINE 1 +#define TOK_LABEL 2 +#define TOK_BAR 3 +#define TOK_MULTIBAR 4 +#define TOK_BARGRAPH 5 +#define TOK_LED 6 + +/* gadget building block tokens */ +#define TOK_LEGEND 100 +#define TOK_COLOURLIST 101 +#define TOK_ACTIONLIST 102 + +/* other reserved words' tokens */ +#define TOK_BAD_RES_WORD 200 +#define TOK_UPDATE 201 +#define TOK_METRIC 202 +#define TOK_HORIZONTAL 203 +#define TOK_VERTICAL 204 +#define TOK_METRICS 205 +#define TOK_MIN 206 +#define TOK_MAX 207 +#define TOK_DEFAULT 208 +#define TOK_FIXED 209 +#define TOK_COLOUR 210 +#define TOK_HISTORY 211 +#define TOK_NOBORDER 212 + +/* other lexical symbols' tokens */ +#define TOK_IDENTIFIER 300 +#define TOK_INTEGER 301 +#define TOK_REAL 302 +#define TOK_STRING 303 +#define TOK_LPAREN 304 +#define TOK_RPAREN 305 +#define TOK_LBRACKET 306 +#define TOK_RBRACKET 307 +#define TOK_COLON 308 + +/* end of file */ +#define TOK_EOF 666 + +extern unsigned nLines; +extern int tokenIntVal; +extern double tokenRealVal; +extern char* tokenStringVal; |