"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "projects/MatrixTesting/src/matrixSlaveHealth.C" between
rose-0.11.53.0.tar.gz and rose-0.11.54.0.tar.gz

About: ROSE is a compiler infrastructure to build source-to-source program transformation and analysis tools for large-scale C, C++, UPC, Fortran, OpenMP, Java, Python and PHP applications.

matrixSlaveHealth.C  (rose-0.11.53.0):matrixSlaveHealth.C  (rose-0.11.54.0)
static const char *purpose = "report slave health"; static const char *gPurpose = "query or adjust slave health";
static const char *description = static const char *gDescription =
"Reports testing slave health to the database server."; "Queries the slave health reports for users, or sends slave health reports f
or slaves.";
#include <rose.h> #include <rose.h>
#include "matrixTools.h"
#include <Rose/CommandLine.h>
#include <SqlDatabase.h> // rose
#include <rose_getline.h> #include <rose_getline.h>
#include <Sawyer/CommandLine.h> #include <Rose/CommandLine.h>
#include <Rose/FormattedTable.h>
#include <Rose/StringUtility/Escape.h>
#include <Sawyer/Message.h> #include <Sawyer/Message.h>
#include <cstdio>
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp>
#ifdef __linux__ #ifdef __linux__
#include <sys/statvfs.h> #include <sys/statvfs.h>
#endif #endif
using namespace Rose; using namespace Rose;
using namespace Sawyer::Message::Common; using namespace Sawyer::Message::Common;
namespace DB = Sawyer::Database;
static const unsigned maxAgeHours = 72; // limit which tests are
shown by default
struct Settings { struct Settings {
bool dryRun; // if true, don't change uint64_t maxAge = 7*86400; // maximum age of report
database in seconds
bool showingAll; // do not limit to last Sawyer::Optional<size_t> testId; // test ID for "test" ev
24 hours ents
Sawyer::Optional<unsigned> testId; // test ID for "test" ev
ents
std::string databaseUri; // e.g., postgresql://us er:password@host/database std::string databaseUri; // e.g., postgresql://us er:password@host/database
Format outputFormat = Format::PLAIN; // how to show results
Settings()
: dryRun(false), showingAll(false)
#ifdef DEFAULT_DATABASE
, databaseUri(DEFAULT_DATABASE)
#endif
{}
}; };
static Sawyer::Message::Facility mlog; static Sawyer::Message::Facility mlog;
// Parse command-line and return event type, "start", "stop", or "test". // Parse command-line and return event type, "start", "stop", or "test".
static std::string static std::string
parseCommandLine(int argc, char *argv[], Settings &settings) { parseCommandLine(int argc, char *argv[], Settings &settings) {
using namespace Sawyer::CommandLine; using namespace Sawyer::CommandLine;
Parser parser = Rose::CommandLine::createEmptyParser(purpose, description); Parser parser = Rose::CommandLine::createEmptyParser(gPurpose, gDescription) ;
parser.errorStream(mlog[FATAL]); parser.errorStream(mlog[FATAL]);
parser.doc("Synopsis", parser.doc("Synopsis",
"@prop{programName} [@v{switches}] [show]\n\n" "@prop{programName} [@v{switches}] [show]\n\n"
"@prop{programName} [@v{switches}] start|stop\n\n" "@prop{programName} [@v{switches}] start|stop\n\n"
"@prop{programName} [@v{switches}] test TEST_ID"); "@prop{programName} [@v{switches}] test TEST_ID");
SwitchGroup tool("Tool-specific switches"); SwitchGroup tool("Tool-specific switches");
tool.insert(Switch("database", 'd') insertDatabaseSwitch(tool, settings.databaseUri);
.argument("uri", anyParser(settings.databaseUri)) insertOutputFormatSwitch(tool, settings.outputFormat,
.doc("Uniform resource locator for the database. This switch ove FormatFlags()
rrides the ROSE_MATRIX_DATABASE environment " .set(Format::PLAIN)
"variable. " + SqlDatabase::uriDocumentation())); .set(Format::YAML)
.set(Format::HTML)
tool.insert(Switch("all") .set(Format::CSV)
.intrinsicValue(true, settings.showingAll) .set(Format::SHELL));
.doc("Show all records, not just those that have arrived in the
last " + tool.insert(Switch("since")
StringUtility::plural(maxAgeHours, "hours") + ".")); .argument("duration", Rose::CommandLine::durationParser(settings
.maxAge))
tool.insert(Switch("dry-run", 'n') .doc("Show reports that happened recently, where the @v{duration
.intrinsicValue(true, settings.dryRun) } is how far back to go in time "
.doc("Avoid changing the database. All normal actions are perfor "from now. " + Rose::CommandLine::DurationParser::docString
med, but the final database transaction " () + " The default is " +
"is not committed. This can be used for testing.")); Rose::CommandLine::DurationParser::toString(settings.maxAge
) + "."));
parser.with(Rose::CommandLine::genericSwitches());
parser.with(tool); std::vector<std::string> args = parser
std::vector<std::string> args = parser.parse(argc, argv).apply().unreachedAr .with(Rose::CommandLine::genericSwitches())
gs(); .with(tool)
.parse(argc, argv)
.apply()
.unreachedArgs();
if (args.empty()) { if (args.empty()) {
return "show"; return "show";
} else if (args[0] == "test") { } else if (args[0] == "test") {
if (args.size() == 2) { if (args.size() == 2) {
settings.testId = boost::lexical_cast<unsigned>(args[1]); settings.testId = boost::lexical_cast<unsigned>(args[1]);
} else { } else {
mlog[FATAL] <<"incorrect usage; see --help\n"; mlog[FATAL] <<"incorrect usage; see --help\n";
exit(1); exit(1);
} }
return "test"; return "test";
} else if (args.size() != 1) { } else if (args.size() != 1) {
mlog[FATAL] <<"incorrect usage; see --help\n"; mlog[FATAL] <<"incorrect usage; see --help\n";
exit(1); exit(1);
} else if (args[0] == "show" || args[0] == "start" || args[0] == "stop") { } else if (args[0] == "show" || args[0] == "start" || args[0] == "stop") {
return args[0]; return args[0];
} else { } else {
mlog[FATAL] <<"unrecognized even type \"" <<StringUtility::cEscape(args[ 0]) <<"\"\n"; mlog[FATAL] <<"unrecognized event type \"" <<StringUtility::cEscape(args [0]) <<"\"\n";
exit(1); exit(1);
} }
} }
static std::string static std::string
slaveName() { slaveName() {
#ifdef __linux__ #ifdef __linux__
char buf[64]; char buf[64];
std::string userName; std::string userName;
if (getlogin_r(buf, sizeof buf) == 0) { if (const char *s = getenv("USER")) {
userName = buf;
} else if (const char *s = getenv("USER")) {
userName = s; userName = s;
} else if (getlogin_r(buf, sizeof buf) == 0) {
userName = buf;
} else { } else {
userName = "unknown"; userName = "unknown";
} }
std::string hostName; std::string hostName;
if (-1 == gethostname(buf, sizeof buf)) { if (-1 == gethostname(buf, sizeof buf)) {
hostName = "unknown"; hostName = "unknown";
} else { } else {
hostName = buf; hostName = buf;
if (-1 != getdomainname(buf, sizeof buf)) { if (-1 != getdomainname(buf, sizeof buf)) {
skipping to change at line 176 skipping to change at line 170
return 0; return 0;
return round((double)sb.f_bavail * sb.f_bsize / (1024.0*1024.0)); return round((double)sb.f_bavail * sb.f_bsize / (1024.0*1024.0));
#else #else
return 0; return 0;
#endif #endif
} }
// SQL "where" clause for limiting output // SQL "where" clause for limiting output
std::string std::string
showingWhereClause(const Settings &settings) { showingWhereClause(const Settings &settings) {
if (settings.showingAll) { return " where timestamp >= " + boost::lexical_cast<std::string>(now() - set
return ""; tings.maxAge);
} else {
return " where timestamp >= " + boost::lexical_cast<std::string>(now() -
maxAgeHours * 3600);
}
} }
class AgeRenderer: public SqlDatabase::Renderer<unsigned> {
public:
std::string operator()(const unsigned &value, size_t width) const ROSE_OVERR
IDE {
time_t secondsAgo = time(NULL) - value;
if (unsigned nYears = secondsAgo / (365*86400)) {
return StringUtility::plural(nYears, "years") + " ago";
} else if (unsigned nMonths = secondsAgo / (30*86400)) {
return StringUtility::plural(nMonths, "months") + " ago";
} else if (unsigned nWeeks = secondsAgo / (7*86400)) {
return StringUtility::plural(nWeeks, "weeks") + " ago";
} else if (unsigned nDays = secondsAgo / 86400) {
return StringUtility::plural(nDays, "days") + " ago";
} else if (unsigned nHours = secondsAgo / 3600) {
return StringUtility::plural(nHours, "hours") + " ago";
} else if (unsigned nMinutes = secondsAgo / 60) {
return StringUtility::plural(nMinutes, "minutes") + " ago";
} else {
return "just now";
}
}
};
class PercentRenderer: public SqlDatabase::Renderer<double> {
public:
std::string operator()(const double &value, size_t width) const ROSE_OVERRID
E {
unsigned pct = round(100.0 * value);
return boost::lexical_cast<std::string>(pct) + "%";
}
};
class SizeRenderer: public SqlDatabase::Renderer<unsigned> {
public:
std::string operator()(const unsigned &value, size_t width) const ROSE_OVERR
IDE {
if (unsigned gib = value / 1024) {
return boost::lexical_cast<std::string>(gib) + " GiB";
} else {
return boost::lexical_cast<std::string>(value) + " MiB";
}
}
};
class DurationRenderer: public SqlDatabase::Renderer<Sawyer::Optional<unsigned>
> {
public:
std::string operator()(const Sawyer::Optional<unsigned> &value, size_t width
) const ROSE_OVERRIDE {
if (value) {
if (unsigned nDays = *value / 86400) {
unsigned nHours = (*value - nDays * 86400) / 3600;
return StringUtility::plural(nDays, "days") + " " + StringUtilit
y::plural(nHours, "hours");
} else if (unsigned nHours = *value / 3600) {
unsigned nMinutes = (*value - nHours * 3600) / 60;
return StringUtility::plural(nHours, "hours ") + " " + StringUti
lity::plural(nMinutes, "minutes");
} else if (unsigned nMinutes = *value / 60) {
unsigned nSeconds = *value % 60;
return StringUtility::plural(nMinutes, "minutes") + " " + String
Utility::plural(nSeconds, "seconds");
} else {
return StringUtility::plural(*value, "seconds");
}
} else {
return "";
}
}
};
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
ROSE_INITIALIZE; ROSE_INITIALIZE;
Diagnostics::initAndRegister(&mlog, "tool"); Diagnostics::initAndRegister(&mlog, "tool");
Settings settings; Settings settings;
if (const char *dbUri = getenv("ROSE_MATRIX_DATABASE"))
settings.databaseUri = dbUri;
std::string event = parseCommandLine(argc, argv, settings); std::string event = parseCommandLine(argc, argv, settings);
DB::Connection db = connectToDatabase(settings.databaseUri, mlog);
SqlDatabase::TransactionPtr tx;
try {
tx = SqlDatabase::Connection::create(settings.databaseUri)->transaction(
);
} catch (const SqlDatabase::Exception &e) {
mlog[FATAL] <<"cannot open database: " <<e.what() <<"\n";
exit(1);
}
if ("show" == event) { if ("show" == event) {
typedef SqlDatabase::Table<std::string, unsigned, double, unsigned, std: const time_t earliest = settings.maxAge < now() ? now() - settings.maxAg
:string, e : 0;
std::string, std::string, Sawyer::Optional<un auto stmt = db.stmt("select name, timestamp, load_ave, free_space, event
signed> > Table; , test_id"
Table table; " from slave_health"
table.headers("Slave name", "Report time", "CPU load", "Disk space", "Ev " where timestamp >= ?ts"
ent", " order by timestamp desc").bind("ts", earliest);
"Operating system", "Phase", "Duration");
table.renderers().r1 = new AgeRenderer; FormattedTable table;
table.renderers().r2 = new PercentRenderer; table.columnHeader(0, 0, "Slave Name");
table.renderers().r3 = new SizeRenderer; table.columnHeader(0, 1, "Latest Report from Slave");
table.renderers().r7 = new DurationRenderer; table.columnHeader(0, 2, "Load\nAverage");
table.columnHeader(0, 3, "Free\nSpace");
SqlDatabase::StatementPtr stmt = table.columnHeader(0, 4, "Latest\nEvent");
tx->statement("select name, timestamp, load_ave, free_space, event, table.columnHeader(0, 5, "Latest\nTest ID");
test_id" table.columnHeader(0, 6, "Operating\nSystem");
" from slave_health" + table.columnHeader(0, 7, "Duration");
showingWhereClause(settings) + for (auto row: stmt) {
" order by name"); using namespace Rose::StringUtility;
for (SqlDatabase::Statement::iterator row = stmt->begin(); row != stmt-> const std::string name = row.get<std::string>(0).orElse("none");
end(); ++row) { const time_t whenReported = row.get<time_t>(1).orElse(0);
std::string name = row.get_str(0); const std::string loadAverage = (boost::format("%6.2f") % (100.0*row
unsigned timestamp = row.get_u32(1); .get<double>(2).orElse(0))).str();
double loadAverage = row.get_dbl(2); const std::string freeSpace = (boost::format("%.0f") % (row.get<size
unsigned freeSpace = row.get_u32(3); _t>(3).orElse(0) / 1024.0)).str();
std::string event = row.get_str(4); const std::string latestEvent = row.get<std::string>(4).orElse("none
unsigned testId = row.get_u32(5); ");
std::string os; const std::string latestId = row.get<std::string>(5).orElse("none");
std::string status; if (Format::YAML == settings.outputFormat) {
Sawyer::Optional<unsigned> duration; std::cout <<"- name: " <<yamlEscape(name) <<"\n";
std::cout <<" when: " <<whenReported <<" # " <<timeToLocal(when
if (testId) { Reported)
SqlDatabase::StatementPtr testQuery = <<", " <<approximateAge(whenReported) <<"\n";
tx->statement("select os, status, duration from test_results std::cout <<" loadavg: " <<loadAverage <<" # percent\n";
where id = ?") std::cout <<" free_space: " <<freeSpace <<" # GiB\n";
->bind(0, testId); std::cout <<" latest_event: " <<latestEvent <<"\n";
SqlDatabase::Statement::iterator testRow = testQuery->begin(); std::cout <<" latest_id: " <<latestId <<"\n";
if (testRow != testQuery->end()) { } else {
os = testRow.get_str(0); const size_t i = table.nRows();
status = testRow.get_str(1); table.insert(i, 0, name);
duration = testRow.get_u32(2); table.insert(i, 1, timeToLocal(whenReported) + ", " + approximat
} eAge(whenReported));
table.insert(i, 2, loadAverage + "%");
table.insert(i, 3, freeSpace + " GiB");
table.insert(i, 4, latestEvent);
table.insert(i, 5, latestId);
} }
table.insert(Table::Tuple(name, timestamp, loadAverage, freeSpace, e if (auto testId = row.get<size_t>(5)) {
vent, os, status, duration)); auto test = db.stmt("select os, duration"
" from test_results"
" where id = ?id")
.bind("id", *testId);
for (auto row: test) {
const std::string os = row.get<std::string>(0).orElse("");
const auto duration = row.get<uint64_t>(1);
const std::string durationStr = duration ? Rose::CommandLine
::DurationParser::toString(duration) : "";
if (Format::YAML == settings.outputFormat) {
std::cout <<" os: " <<os <<"\n";
if (duration)
std::cout <<" duration: " <<*duration <<" # " <<dur
ationStr <<"\n";
} else {
table.insert(table.nRows()-1, 6, os);
table.insert(table.nRows()-1, 7, durationStr);
}
break;
}
}
}
if (Format::YAML != settings.outputFormat) {
table.format(tableFormat(settings.outputFormat));
std::cout <<table;
} }
table.print(std::cout);
} else { } else {
// We want only one record per slave, so first delete any old records, t // We want only one record per slave, but slaves can't delete things. Th
hen insert the new one. erefore either update or insert
tx->statement("delete from slave_health where name = ?") DB::Statement stmt;
->bind(0, slaveName())
->execute(); if (db.stmt("select count(*) from slave_health where name = ?name").bind
("name", slaveName()).get<size_t>().get()) {
tx->statement("insert into slave_health" stmt = db.stmt("update slave_health set"
" (name, timestamp, load_ave, free_space, event, te " name = ?name,"
st_id)" " timestamp = ?timestamp,"
" values (?, ?, ?, ?, ?, ?) " load_ave = ?load_ave,"
") " free_space = ?free_space,"
->bind(0, slaveName()) " event = ?event,"
->bind(1, now()) " test_id = ?test_id"
->bind(2, cpuLoad()) " where name = ?name");
->bind(3, diskFreeSpace()) } else {
->bind(4, event) stmt = db.stmt("insert into slave_health"
->bind(5, settings.testId.orElse(0)) " (name, timestamp, load_ave, free_space,
->execute(); event, test_id)"
" values (?name, ?timestamp, ?load_ave, ?free_space,
?event, ?test_id)");
}
if (!settings.dryRun) stmt
tx->commit(); .bind("name", slaveName())
.bind("timestamp", now())
.bind("load_ave", cpuLoad())
.bind("free_space", diskFreeSpace())
.bind("event", event)
.bind("test_id", settings.testId.orElse(0))
.run();
} }
} }
 End of changes. 23 change blocks. 
204 lines changed or deleted 148 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)