From 8ececebf042d7a11253dfb32c86f516dbf241360 Mon Sep 17 00:00:00 2001 From: Jens Rehsack Date: Mon, 19 Nov 2012 14:33:53 +0100 Subject: [PATCH] rewrite stats collecting using iterators --- src/watch/watch_mongodb.cpp | 1906 ++++++++++++++++++++++++++----------------- 1 file changed, 1173 insertions(+), 733 deletions(-) rewrite src/watch/watch_mongodb.cpp (63%) diff --git a/src/watch/watch_mongodb.cpp b/src/watch/watch_mongodb.cpp dissimilarity index 63% index 1dde587..7d275ad 100644 --- a/src/watch/watch_mongodb.cpp +++ b/src/watch/watch_mongodb.cpp @@ -1,733 +1,1173 @@ -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include "asn1.h" - -using namespace mongo; -using namespace std; -using namespace boost; -using namespace boost::program_options; - -#if 1 -#define DBNAME "admin" -#else -#define DBNAME "local" -#endif - -extern -void connect(DBClientConnection &c, std::string const &dsn, std::string const &dbname, std::string const &user); - -namespace boost -{ - -template<> -std::string -lexical_cast( bool const &v ) -{ - return v ? "true" : "false"; -} - -} - -string -join( string const &delim, vector const &list ) -{ - string rc; - - for( vector::const_iterator ci = list.begin(); ci != list.end(); ++ci ) - { - if( !rc.empty() ) - rc += delim; - rc += *ci; - } - - return rc; -} - -#define EXCEED_TYPE_BOUNDS(TYPE, VAL) \ - std::numeric_limits::is_integer && \ - ((VAL > std::numeric_limits::max()) || \ - (VAL < std::numeric_limits::min())) - -template -T -extract_number(BSONElement const &e, string const &oid) -{ - T result; - - switch( e.type() ) - { - case NumberDouble: - { - double tmp = e.Double(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("double value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - case Bool: - { - int tmp = e.Bool(); - return (T)tmp; - } - case NumberInt: - { - long long tmp = e.Int(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("int value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - case Date: - { - unsigned long long tmp = e.Date(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("int value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - case Timestamp: - { - if( std::numeric_limits::max() <= std::numeric_limits::max() ) - { - unsigned int tmp = e.timestampInc(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("int value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - else - { - unsigned long long tmp = e.timestampTime(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("int value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - } - case NumberLong: - { - long long tmp = e.Long(); - if( EXCEED_TYPE_BOUNDS(T, tmp) ) - throw out_of_range( - string("long long value for ") + oid + - " (" + lexical_cast(tmp) + ") not between " + - lexical_cast(std::numeric_limits::min()) + - " and " + - lexical_cast(std::numeric_limits::max())); - return (T)tmp; - } - default: - { - StringBuilder ss; - ss << "wrong type for field (" << e.fieldName() << ") " << e.type() << " not in " - << NumberDouble << ", " - << Bool << ", " - << NumberInt << ", " - << Timestamp << " or " - << NumberLong; - msgasserted(13111, ss.str() ); - break; - } - } -} - -struct out_val -{ - string oid; - unsigned type; - string value; -}; - -inline bool -operator < (out_val const &x, out_val const &y) -{ - return x.oid < y.oid; -} - -out_val -extract_string(BSONElement const &e, string const &oid) -{ - out_val val = { oid, ASN_OCTET_STR }; - e.Val(val.value); - return val; -} - -out_val -extract_int(BSONElement const &e, string const &oid) -{ - out_val val = { oid, ASN_INTEGER }; - val.value = lexical_cast( extract_number(e, oid) ); - return val; -} - -out_val -extract_uint(BSONElement const &e, string const &oid) -{ - out_val val = { oid, SMI_UINTEGER }; - val.value = lexical_cast( extract_number(e, oid) ); - return val; -} - -out_val -extract_uint64(BSONElement const &e, string const &oid) -{ - out_val val = { oid, SMI_COUNTER64 }; - val.value = lexical_cast( extract_number(e, oid) ); - return val; -} - -out_val -extract_float(BSONElement const &e, string const &oid) -{ - out_val val = { oid, ASN_OCTET_STR }; - val.value = lexical_cast( extract_number(e, oid) ); - return val; -} - -void -collect(DBClientConnection &c, set &out_vals) -{ - BSONObj serv_status, dbases, repl_info, cmd; - - cmd = BSONObjBuilder().append( "serverStatus", 1 ).obj(); - c.runCommand(DBNAME, cmd, serv_status); - if( serv_status.hasField("host") ) - out_vals.insert( extract_string( serv_status["host"], ".1" ) ); - if( serv_status.hasField("version") ) - out_vals.insert( extract_string( serv_status["version"], ".2" ) ); - if( serv_status.hasField("process") ) - out_vals.insert( extract_string( serv_status["process"], ".3" ) ); - if( serv_status.hasField("pid") ) - out_vals.insert( extract_int( serv_status["pid"], ".4" ) ); - if( serv_status.hasField("uptimeMillis") ) - out_vals.insert( extract_uint64( serv_status["uptimeMillis"], ".5" ) ); - - if( serv_status.hasField("globalLock") ) - { - BSONObj o1 = serv_status["globalLock"].Obj(); - - if( o1.hasField("totalTime") ) - out_vals.insert( extract_uint64( o1["totalTime"], ".10.1" ) ); - if( o1.hasField("lockTime") ) - out_vals.insert( extract_uint64( o1["lockTime"], ".10.2" ) ); - - if( o1.hasField("currentQueue") ) - { - BSONObj o2 = o1["currentQueue"].Obj(); - - if( o2.hasField("total") ) - out_vals.insert( extract_uint64( o2["total"], ".10.3.1" ) ); - if( o2.hasField("readers") ) - out_vals.insert( extract_uint64( o2["readers"], ".10.3.2" ) ); - if( o2.hasField("writers") ) - out_vals.insert( extract_uint64( o2["writers"], ".10.3.2" ) ); - } - - if( o1.hasField("activeClients") ) - { - BSONObj o2 = o1["activeClients"].Obj(); - - if( o2.hasField("total") ) - out_vals.insert( extract_uint64( o2["total"], ".10.4.1" ) ); - if( o2.hasField("readers") ) - out_vals.insert( extract_uint64( o2["readers"], ".10.4.2" ) ); - if( o2.hasField("writers") ) - out_vals.insert( extract_uint64( o2["writers"], ".10.4.2" ) ); - } - } - - if( serv_status.hasField("mem") ); - { - BSONObj o1 = serv_status["mem"].Obj(); - - if( o1.hasField("bits") ) - out_vals.insert( extract_uint( o1["bits"], ".11.1" ) ); - if( o1.hasField("resident") ) - out_vals.insert( extract_uint64( o1["resident"], ".11.2" ) ); - if( o1.hasField("virtual") ) - out_vals.insert( extract_uint64( o1["virtual"], ".11.3" ) ); - if( o1.hasField("supported") ) - out_vals.insert( extract_uint64( o1["supported"], ".11.4" ) ); - if( o1.hasField("mapped") ) - out_vals.insert( extract_uint64( o1["mapped"], ".11.5" ) ); - } - - if( serv_status.hasField("connections") ); - { - BSONObj o1 = serv_status["connections"].Obj(); - - if( o1.hasField("current") ) - out_vals.insert( extract_uint( o1["current"], ".12.1" ) ); - if( o1.hasField("available") ) - out_vals.insert( extract_uint64( o1["available"], ".12.2" ) ); - } - - if( serv_status.hasField("backgroundFlushing") && serv_status["backgroundFlushing"].ok() ); - { - BSONObj o1 = serv_status["backgroundFlushing"].Obj(); - - if( o1.hasField("flushes") ) - out_vals.insert( extract_uint64( o1["flushes"], ".13.1" ) ); - if( o1.hasField("total_ms") ) - out_vals.insert( extract_uint64( o1["total_ms"], ".13.2" ) ); - if( o1.hasField("average_ms") ) - out_vals.insert( extract_uint64( o1["average_ms"], ".13.3" ) ); - if( o1.hasField("last_ms") ) - out_vals.insert( extract_uint64( o1["last_ms"], ".13.4" ) ); - if( o1.hasField("last_finished") ) - out_vals.insert( extract_uint64( o1["last_finished"], ".13.5" ) ); - } - - if( serv_status.hasField("cursors") ); - { - BSONObj o1 = serv_status["cursors"].Obj(); - - if( o1.hasField("totalOpen") ) - out_vals.insert( extract_uint64( o1["totalOpen"], ".14.1" ) ); - if( o1.hasField("clientCursors_size") ) - out_vals.insert( extract_uint64( o1["clientCursors_size"], ".14.2" ) ); - if( o1.hasField("timedOut") ) - out_vals.insert( extract_uint64( o1["timedOut"], ".14.3" ) ); - } - - if( serv_status.hasField("network") ); - { - BSONObj o1 = serv_status["network"].Obj(); - - if( o1.hasField("bytesIn") ) - out_vals.insert( extract_uint64( o1["bytesIn"], ".15.1" ) ); - if( o1.hasField("bytesOut") ) - out_vals.insert( extract_uint64( o1["bytesOut"], ".15.2" ) ); - if( o1.hasField("numRequests") ) - out_vals.insert( extract_uint64( o1["numRequests"], ".15.3" ) ); - } - - if( serv_status.hasField("opcounters") ); - { - BSONObj o1 = serv_status["opcounters"].Obj(); - - if( o1.hasField("insert") ) - out_vals.insert( extract_uint64( o1["insert"], ".16.1" ) ); - if( o1.hasField("query") ) - out_vals.insert( extract_uint64( o1["query"], ".16.2" ) ); - if( o1.hasField("update") ) - out_vals.insert( extract_uint64( o1["update"], ".16.3" ) ); - if( o1.hasField("delete") ) - out_vals.insert( extract_uint64( o1["delete"], ".16.4" ) ); - if( o1.hasField("getmore") ) - out_vals.insert( extract_uint64( o1["getmore"], ".16.5" ) ); - if( o1.hasField("command") ) - out_vals.insert( extract_uint64( o1["command"], ".16.6" ) ); - } - - if( serv_status.hasField("asserts") ); - { - BSONObj o1 = serv_status["asserts"].Obj(); - - if( o1.hasField("regular") ) - out_vals.insert( extract_uint64( o1["regular"], ".17.1" ) ); - if( o1.hasField("warning") ) - out_vals.insert( extract_uint64( o1["warning"], ".17.2" ) ); - if( o1.hasField("msg") ) - out_vals.insert( extract_uint64( o1["msg"], ".17.3" ) ); - if( o1.hasField("user") ) - out_vals.insert( extract_uint64( o1["user"], ".17.4" ) ); - if( o1.hasField("rollovers") ) - out_vals.insert( extract_uint64( o1["rollovers"], ".17.5" ) ); - } - - if( serv_status.hasField("recordStats") ); - { - BSONObj o1 = serv_status["recordStats"].Obj(); - - if( o1.hasField("accessesNotInMemory") ) - out_vals.insert( extract_uint64( o1["accessesNotInMemory"], ".18.1" ) ); - if( o1.hasField("pageFaultExceptionsThrown") ) - out_vals.insert( extract_uint64( o1["pageFaultExceptionsThrown"], ".18.2" ) ); - - // extract more on-table serv_status - } - - if( serv_status.hasField("locks") ); - { - BSONObj o1 = serv_status["locks"].Obj(); - - if( o1.hasField(".") ) - { - BSONObj o2 = o1["."].Obj(); - - if( o2.hasField("timeLockedMicros") ) - { - BSONObj o3 = o2["timeLockedMicros"].Obj(); - - if( o3.hasField("R") ) - out_vals.insert( extract_uint64( o3["R"], ".19.1.1" ) ); - if( o3.hasField("W") ) - out_vals.insert( extract_uint64( o3["W"], ".19.1.2" ) ); - } - - if( o2.hasField("timeAcquiringMicros") ) - { - BSONObj o3 = o2["timeAcquiringMicros"].Obj(); - - if( o3.hasField("R") ) - out_vals.insert( extract_uint64( o3["R"], ".19.2.1" ) ); - if( o3.hasField("W") ) - out_vals.insert( extract_uint64( o3["W"], ".19.2.2" ) ); - } - } - - // extract more on-table serv_status - } - - if( serv_status.hasField("repl") ); - { - BSONObj o1 = serv_status["repl"].Obj(); - - if( o1.hasField("setName") ) - out_vals.insert( extract_string( o1["setName"], ".20.1" ) ); - if( o1.hasField("ismaster") ) - out_vals.insert( extract_int( o1["ismaster"], ".20.2" ) ); - if( o1.hasField("secondary") ) - out_vals.insert( extract_int( o1["secondary"], ".20.3" ) ); - if( o1.hasField("me") ) - out_vals.insert( extract_string( o1["me"], ".20.4" ) ); - if( o1.hasField("primary") ) - out_vals.insert( extract_string( o1["primary"], ".20.5" ) ); - - unsigned row = 1; - string repl_host_name_col = ".20.7.1.2."; - string repl_host_type_col = ".20.7.1.5."; - if( o1.hasField("hosts") ) - { - vector hosts = o1["hosts"].Array(); - - for( vector::iterator iter = hosts.begin(); - iter != hosts.end(); - ++iter, ++row ) - { - if( iter->ok() ) - { - stringstream ss; - ss << repl_host_name_col; - ss << row; - out_vals.insert( extract_string( *iter, ss.str() ) ); - // set state_str, ... - } - } - } - - if( o1.hasField("arbiters") ) - { - vector hosts = o1["arbiters"].Array(); - - for( vector::iterator iter = hosts.begin(); - iter != hosts.end(); - ++iter, ++row ) - { - if( iter->ok() ) - { - stringstream ss; - ss << repl_host_name_col; - ss << row; - out_vals.insert( extract_string( *iter, ss.str() ) ); - // set state_str, ... - } - } - } - - } - - if( serv_status.hasField("replNetworkQueue") ); - { - BSONObj o1 = serv_status["replNetworkQueue"].Obj(); - - if( o1.hasField("waitTimeMs") ) - out_vals.insert( extract_uint64( o1["waitTimeMs"], ".20.6.1" ) ); - if( o1.hasField("numElems") ) - out_vals.insert( extract_uint64( o1["numElems"], ".20.6.2" ) ); - if( o1.hasField("numBytes") ) - out_vals.insert( extract_uint64( o1["numBytes"], ".20.6.3" ) ); - } - - if( serv_status.hasField("indexCounters") ); - { - } - - cmd = BSONObjBuilder().append("replSetGetStatus", 1).obj(); - c.runCommand(DBNAME, cmd, repl_info); - - if( repl_info.hasField("members") ); - { - vector repl_membrs = repl_info["members"].Array(); - - unsigned row = 1; - const string repl_host_health_col = "20.7.1.3"; - for( vector::iterator iter = repl_membrs.begin(); - iter != repl_membrs.end(); - ++iter ) - { - if( !iter->ok() ) - continue; - - BSONObj rm = iter->Obj(); - string row_str; - if( rm.hasField("name") ) - { - out_val cur_val = extract_string( rm["name"], string(".20.7.1.2") ); - set::iterator names_i = out_vals.lower_bound(cur_val); - while( ( names_i != out_vals.end() ) && - ( names_i->oid < repl_host_health_col ) ) - { - if( names_i->value == cur_val.value ) - { - row_str = names_i->oid.substr( names_i->oid.find_last_of("." ) + 1 ); - break; - } - - ++names_i; - } - - if( row_str.empty() ) - { - row_str = lexical_cast(row); - ++row; - } - } - else - { - row_str = lexical_cast(row); - ++row; - } - if( rm.hasField("_id") ) - out_vals.insert( extract_uint( rm["_id"], string(".20.7.1.1.") + row_str ) ); - if( rm.hasField("health") ) - out_vals.insert( extract_float( rm["health"], string(".20.7.1.3.") + row_str ) ); - if( rm.hasField("state") ) - out_vals.insert( extract_uint( rm["state"], string(".20.7.1.4.") + row_str ) ); - if( rm.hasField("stateStr") ) - out_vals.insert( extract_string( rm["stateStr"], string(".20.7.1.5.") + row_str ) ); - if( rm.hasField("uptime") ) - out_vals.insert( extract_uint64( rm["uptime"], string(".20.7.1.6.") + row_str ) ); - if( rm.hasField("optime") ) - { - out_vals.insert( extract_uint64( rm["optime"], string(".20.7.1.7.") + row_str ) ); // XXX should retrieve timestampTime - out_vals.insert( extract_uint( rm["optime"], string(".20.7.1.8.") + row_str ) ); // XXX should retrieve timestampInc - } - if( rm.hasField("pingMs") ) - out_vals.insert( extract_uint64( rm["pingMs"], string(".20.7.1.9.") + row_str ) ); - if( rm.hasField("lastHeartbeat") ) - out_vals.insert( extract_uint64( rm["lastHeartbeat"], string(".20.7.1.10.") + row_str ) ); - } - } - - cmd = BSONObjBuilder().append("listDatabases", 1).obj(); - c.runCommand(DBNAME, cmd, dbases); - - if( dbases.hasField("databases") ) - { - unsigned row = 1; - vector databases = dbases["databases"].Array(); - - cmd = BSONObjBuilder().append("dbstats", 1).obj(); - for( vector::iterator iter = databases.begin(); - iter != databases.end(); - ++iter ) - { - if( iter->ok() ) - { - BSONObj db = iter->Obj(); - out_val dbname; - string row_str = lexical_cast(row); - ++row; - - if( db.hasField("name") ) - { - dbname = extract_string( db["name"], string(".21.1.1.") + row_str ); - out_vals.insert( dbname ); - } - - if( db.hasField("sizeOnDisk") ) - out_vals.insert( extract_uint64( db["sizeOnDisk"], string(".21.1.2.") + row_str ) ); - if( db.hasField("empty") ) - out_vals.insert( extract_int( db["empty"], string(".21.1.3.") + row_str ) ); - - BSONObj dbinfo; - c.runCommand(dbname.value, cmd, dbinfo); - if( dbinfo.hasField("collections") ) - out_vals.insert( extract_uint64( dbinfo["collections"], string(".21.1.4.") + row_str ) ); - if( dbinfo.hasField("objects") ) - out_vals.insert( extract_int( dbinfo["objects"], string(".21.1.5.") + row_str ) ); - if( dbinfo.hasField("avgObjSize") ) - out_vals.insert( extract_float( dbinfo["avgObjSize"], string(".21.1.6.") + row_str ) ); - if( dbinfo.hasField("dataSize") ) - out_vals.insert( extract_uint64( dbinfo["dataSize"], string(".21.1.7.") + row_str ) ); - if( dbinfo.hasField("storageSize") ) - out_vals.insert( extract_uint64( dbinfo["storageSize"], string(".21.1.8.") + row_str ) ); - if( dbinfo.hasField("numExtents") ) - out_vals.insert( extract_uint( dbinfo["numExtents"], string(".21.1.9.") + row_str ) ); - if( dbinfo.hasField("indexes") ) - out_vals.insert( extract_uint( dbinfo["indexes"], string(".21.1.10.") + row_str ) ); - if( dbinfo.hasField("indexSize") ) - out_vals.insert( extract_uint( dbinfo["indexSize"], string(".21.1.11.") + row_str ) ); - if( dbinfo.hasField("fileSize") ) - out_vals.insert( extract_uint64( dbinfo["fileSize"], string(".21.1.12.") + row_str ) ); - if( dbinfo.hasField("nsSizeMB") ) - out_vals.insert( extract_uint( dbinfo["nsSizeMB"], string(".21.1.13.") + row_str ) ); - - if( serv_status.hasField("locks") && serv_status["locks"].Obj().hasField(dbname.value.c_str()) ); - { - BSONObj dbl = serv_status["locks"].Obj().getField(dbname.value).Obj(); - - if( dbl.hasField("timeLockedMicros") ) - { - BSONObj o3 = dbl["timeLockedMicros"].Obj(); - - if( o3.hasField("r") ) - out_vals.insert( extract_uint64( o3["r"], string(".21.1.14.") + row_str ) ); - if( o3.hasField("w") ) - out_vals.insert( extract_uint64( o3["w"], string(".21.1.15.") + row_str ) ); - } - - if( dbl.hasField("timeAcquiringMicros") ) - { - BSONObj o3 = dbl["timeAcquiringMicros"].Obj(); - - if( o3.hasField("r") ) - out_vals.insert( extract_uint64( o3["r"], string(".21.1.16.") + row_str ) ); - if( o3.hasField("w") ) - out_vals.insert( extract_uint64( o3["w"], string(".21.1.17.") + row_str ) ); - } - } - } - } - } -} - -void -dump(set const &out_vals) -{ - vector result; - result.reserve(out_vals.size() + 2); - for( set::iterator iter = out_vals.begin(); - iter != out_vals.end(); - ++iter ) - { - std::string s = " [ \""; - - s += iter->oid; - s += "\", "; - s += lexical_cast(iter->type); - s += ", "; - if( ASN_OCTET_STR == iter->type ) - s += "\""; - if( ASN_NULL == iter->type ) - s += "null"; - else if( ASN_OCTET_STR == iter->type ) - s += boost::locale::conv::utf_to_utf(iter->value); - else - s += iter->value; - if( ASN_OCTET_STR == iter->type ) - s += "\""; - s += " ]"; - - result.push_back(s); - } - - cout << "[" << endl - << join(",\n", result) << endl - << "]" << endl; -} - -int -main(int argc, char *argv[]) -{ - try - { - options_description desc("Allowed options"); - desc.add_options() - ("help", "produce help message") - ("dsn", value(), "set mongodb dsn") - ; - variables_map vm; - store( parse_command_line( argc, argv, desc ), vm ); - notify(vm); - - if( vm.count("help") ) - { - cout << desc << endl; - return 1; - } - - if( vm.count("dsn") == 0 ) - { - cerr << desc << endl; - return 255; - } - - set out_vals; - do { - DBClientConnection c; - - boost::timer::cpu_timer db_dur; - db_dur.start(); - connect(c, vm["dsn"].as(), DBNAME, "admin"); - collect(c, out_vals); - db_dur.stop(); - - out_val val = { ".99.1", SMI_COUNTER64 }; - val.value = lexical_cast( db_dur.elapsed().user ); - out_vals.insert( val ); - - val.oid = ".99.2"; - val.value = lexical_cast( db_dur.elapsed().system ); - out_vals.insert( val ); - - val.oid = ".99.3"; - val.value = lexical_cast( db_dur.elapsed().wall ); - out_vals.insert( val ); - } while(0); - - dump(out_vals); - } - catch( DBException &e ) - { - cout << "caught " << e.what() << endl; - } - - return 0; -} +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "asn1.h" + +using namespace mongo; +using namespace std; +using namespace boost; +using namespace boost::program_options; +using namespace boost::lambda; + +#if 1 +#define DBNAME "admin" +#else +#define DBNAME "local" +#endif + +extern +void connect(DBClientConnection &c, std::string const &dsn, std::string const &dbname, std::string const &user); + +namespace boost +{ + +template<> +std::string +lexical_cast( bool const &v ) +{ + return v ? "true" : "false"; +} + +} + +string +join( string const &delim, vector const &list ) +{ + string rc; + + for( vector::const_iterator ci = list.begin(); ci != list.end(); ++ci ) + { + if( !rc.empty() ) + rc += delim; + rc += *ci; + } + + return rc; +} + +#define EXCEED_TYPE_BOUNDS(TYPE, VAL) \ + std::numeric_limits::is_integer && \ + ((VAL > std::numeric_limits::max()) || \ + (VAL < std::numeric_limits::min())) + +template +T +extract_number(BSONElement const &e, string const &oid) +{ + T result; + + switch( e.type() ) + { + case NumberDouble: + { + double tmp = e.Double(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("double value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + case Bool: + { + int tmp = e.Bool(); + return (T)tmp; + } + case NumberInt: + { + long long tmp = e.Int(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("int value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + case Date: + { + unsigned long long tmp = e.Date(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("int value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + case Timestamp: + { + if( std::numeric_limits::max() <= std::numeric_limits::max() ) + { + unsigned int tmp = e.timestampInc(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("int value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + else + { + unsigned long long tmp = e.timestampTime(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("int value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + } + case NumberLong: + { + long long tmp = e.Long(); + if( EXCEED_TYPE_BOUNDS(T, tmp) ) + throw out_of_range( + string("long long value for ") + oid + + " (" + lexical_cast(tmp) + ") not between " + + lexical_cast(std::numeric_limits::min()) + + " and " + + lexical_cast(std::numeric_limits::max())); + return (T)tmp; + } + default: + { + StringBuilder ss; + ss << "wrong type for field (" << e.fieldName() << ") " << e.type() << " not in " + << NumberDouble << ", " + << Bool << ", " + << NumberInt << ", " + << Timestamp << " or " + << NumberLong; + msgasserted(13111, ss.str() ); + break; + } + } +} + +struct OidValueTuple +{ + string oid; + unsigned type; + string value; + + OidValueTuple(string const &an_oid, unsigned a_type = ASN_NULL, string const &a_value = string()) + : oid(an_oid) + , type(a_type) + , value(a_value) + {} +}; + +inline bool +operator < (OidValueTuple const &x, OidValueTuple const &y) +{ + return x.oid < y.oid; +} + +template +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid ); +} + +template<> +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid, ASN_OCTET_STR, e.String() ); +} + +template<> +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid, ASN_INTEGER, lexical_cast( extract_number(e, oid) ) ); +} + +template<> +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid, SMI_UINTEGER, lexical_cast( extract_number(e, oid) ) ); +} + +template<> +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid, SMI_COUNTER64, lexical_cast( extract_number(e, oid) ) ); +} + +template<> +OidValueTuple +extract(BSONElement const &e, string const &oid) +{ + return OidValueTuple( oid, ASN_OCTET_STR, lexical_cast( extract_number(e, oid) ) ); +} + +struct Extractor +{ +public: + virtual void operator()(BSONElement const &e, set &out_vals) = 0; +}; + +ostream & +operator << (ostream &os, OidValueTuple const val) +{ + os << "(" << val.oid << ", " << val.type << ", " << val.value << ")"; + return os; +} + +template +struct ItemExtractor + : public Extractor +{ +public: + ItemExtractor(string const &oid) + : Extractor() + , m_oid(oid) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + out_vals.insert( extract( e, m_oid ) ); + } + +protected: + string const m_oid; + +private: + ItemExtractor(); +}; + +struct StructExtractor + : public Extractor +{ +public: + StructExtractor(map *item_rules) + : Extractor() + , m_item_rules(item_rules) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + BSONObjIterator i(e.Obj()); + while( i.more() ) + { + BSONElement elem = i.next(); + string fname = elem.fieldName(); + + map::iterator iter = m_item_rules->find( fname ); + if( iter != m_item_rules->end() ) + { + Extractor &ex = *iter->second; + ex(elem, out_vals); + } + } + } + + virtual void operator()(BSONObj const &o, set &out_vals) + { + BSONObjIterator i(o.begin()); + while( i.more() ) + { + BSONElement elem = i.next(); + string fname = elem.fieldName(); + + map::iterator iter = m_item_rules->find( fname ); + if( iter != m_item_rules->end() ) + { + Extractor &ex = *iter->second; + ex(elem, out_vals); + } + } + } + + virtual ~StructExtractor() + { + for( map::iterator i = m_item_rules->begin(); + i != m_item_rules->end(); + ++i ) + { + delete i->second; + i->second = 0; + } + + delete m_item_rules; + } + +protected: + map *m_item_rules; + +private: + StructExtractor(); + StructExtractor(StructExtractor const &); + StructExtractor & operator = (StructExtractor const &); +}; + +struct Anyfix +{ + virtual string operator()() const = 0; +}; + +struct StaticAnyfix + : public Anyfix +{ + StaticAnyfix(string const &anyfix) + : Anyfix() + , m_anyfix(anyfix) + {} + + virtual string operator()() const { return m_anyfix; } + +protected: + string m_anyfix; +}; + +#if 0 +template, class Allocator = allocator > +std::set::iterator +insert_or_update( std::set &vals, Key const &v ) +{ + std::set::iterator i = vals.lower_bound(v); + if( ( i == vals.end() ) || ( vals.key_comp()(v, *i ) ) ) + { + i = vals.insert( i, v ); + } + else + { + Key &ev = const_cast(*i); + ev = v; + } + + return i; +} +#endif + +template +struct BothfixStructExtractor + : public Embed +{ +public: + BothfixStructExtractor(Pre prefix, Post postfix) + : Embed() + , m_prefix(prefix) + , m_postfix(postfix) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + set ofs_vals; + Embed::operator()(e, ofs_vals); + apply_fixes(ofs_vals, out_vals); + } + + void apply_fixes(set &collected_vals, set &out_vals) + { + for( set::iterator iter = collected_vals.begin(); + iter != collected_vals.end(); + ++iter ) + { + OidValueTuple ov = *iter; + ov.oid = m_prefix() + ov.oid + m_postfix(); + insert_or_update( out_vals, ov ); + } + } + +protected: + Pre m_prefix; + Post m_postfix; + + std::set::iterator + insert_or_update( std::set &vals, OidValueTuple const &v ) + { + std::set::iterator i = vals.lower_bound(v); + if( ( i == vals.end() ) || ( vals.key_comp()(v, *i ) ) ) + { + i = vals.insert( i, v ); + } + else + { + OidValueTuple &ev = const_cast(*i); + ev = v; + } + + return i; + } + +private: + BothfixStructExtractor(); + BothfixStructExtractor(StructExtractor const &); + BothfixStructExtractor & operator = (BothfixStructExtractor const &); +}; + +struct RowPostfix + : public Anyfix +{ + friend void swap(RowPostfix &a, RowPostfix &b); + +public: + RowPostfix(unsigned row = 0) + : Anyfix() + , m_row(row) + {} + + virtual string operator()() const { return "." + lexical_cast(m_row); } + + operator unsigned() const { return m_row; } + + unsigned getRow() const { return m_row; } + unsigned nextRow() { return ++m_row; } + unsigned setNextRow( unsigned next_row ) { return m_row = next_row; } + +protected: + unsigned m_row; +}; + +void +swap(RowPostfix &a, RowPostfix &b) +{ + swap(a.m_row, b.m_row); +} + +template +struct ServReplHostsExtractor + : public ItemExtractor +{ +public: + ServReplHostsExtractor(string const &type) + : ItemExtractor(".2") + , m_type(type) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + ItemExtractor::operator()( e, out_vals ); + + out_vals.insert( OidValueTuple( ".5", ASN_OCTET_STR, m_type ) ); + } + +protected: + string m_type; +}; + +struct ServReplWorkersExtractor + : public ServReplHostsExtractor +{ + ServReplWorkersExtractor() + : ServReplHostsExtractor("PRIORSEC") + {} +}; + +struct ServReplArbiterExtractor + : public ServReplHostsExtractor +{ + ServReplArbiterExtractor() + : ServReplHostsExtractor("ARBITER") + {} +}; + +template +struct TableRowExtractor + : public BothfixStructExtractor +{ +public: + TableRowExtractor(string const &tblOid, RowPostfix &rowPostfix, vector const &key_chk = vector() ) + : BothfixStructExtractor(StaticAnyfix(tblOid + ".1"), rowPostfix) + , m_key_chk(key_chk) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + unsigned merge_row; + set embed_vals; + + E::operator()(e, embed_vals); + if( ( merge_row = find_key(embed_vals, out_vals) ) > 0 ) + { + RowPostfix save(merge_row); + + swap(save, this->m_postfix); + this->apply_fixes(embed_vals, out_vals); + swap(save, this->m_postfix); + } + else + { + if( merge_row < 0 ) + this->m_postfix.setNextRow( 0 - ( merge_row - 1 ) ); + else + this->m_postfix.nextRow(); + this->apply_fixes(embed_vals, out_vals); + } + } + + unsigned find_key(set const &embed_vals, set &out_vals) const + { + vector found; + for( vector::const_iterator ci = m_key_chk.begin(); + ci != m_key_chk.end(); + ++ci ) + { + OidValueTuple search_key( *ci, ASN_OCTET_STR ); + set::iterator cmp_iter = embed_vals.lower_bound(search_key); + if( cmp_iter == embed_vals.end() ) + continue; + + search_key.value = cmp_iter->value; + search_key.oid = this->m_prefix() + search_key.oid + "."; + + for( cmp_iter = out_vals.lower_bound(search_key); + ( cmp_iter != out_vals.end() ) && ( 0 == cmp_iter->oid.compare( 0, search_key.oid.length(), search_key.oid ) ); + ++cmp_iter ) + { + if( cmp_iter->value == search_key.value ) + { + string row_str = cmp_iter->oid.substr( cmp_iter->oid.find_last_of("." ) + 1 ); + unsigned row = lexical_cast(row_str); + if( found.size() < (row+1) ) + found.resize(row+1); + + if( ci == m_key_chk.begin() ) + found[row] = true; + else + found[row] = found[row] & true; + } + } + } + + // scan found for first full matching row + unsigned row = 0; + for( vector::iterator vbi = found.begin(); + vbi != found.end(); + ++vbi, ++row ) + { + if( *vbi ) + return row; + } + + return 0; + } + +protected: + vector m_key_chk; + +private: + TableRowExtractor(); +}; + +template +struct ListRowExtractor + : public TableRowExtractor +{ +public: + ListRowExtractor( string const &tblOid, RowPostfix &rowPostfix = RowPostfix(), vector const &key_chk = vector() ) + : TableRowExtractor( tblOid, rowPostfix, key_chk ) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + BSONObjIterator i(e.Obj()); + while( i.more() ) + { + BSONElement elem = i.next(); + TableRowExtractor::operator()(elem, out_vals); + } + } +}; + +Extractor * +global_lock_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "totalTime", new ItemExtractor( ".10.1" ) ) ); + extractor_map->insert( make_pair( "lockTime", new ItemExtractor( ".10.2" ) ) ); + + map *current_queue_map = new map; + current_queue_map->insert( make_pair( "total", new ItemExtractor( ".10.3.1" ) ) ); + current_queue_map->insert( make_pair( "readers", new ItemExtractor( ".10.3.2" ) ) ); + current_queue_map->insert( make_pair( "writers", new ItemExtractor( ".10.3.3" ) ) ); + extractor_map->insert( make_pair( "lockTime", new StructExtractor(current_queue_map) ) ); + + map *active_clients_map = new map; + active_clients_map->insert( make_pair( "total", new ItemExtractor( ".10.4.1" ) ) ); + active_clients_map->insert( make_pair( "readers", new ItemExtractor( ".10.4.2" ) ) ); + active_clients_map->insert( make_pair( "writers", new ItemExtractor( ".10.4.3" ) ) ); + extractor_map->insert( make_pair( "lockTime", new StructExtractor(active_clients_map) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +mem_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "bits", new ItemExtractor( ".11.1" ) ) ); + extractor_map->insert( make_pair( "resident", new ItemExtractor( ".11.2" ) ) ); + extractor_map->insert( make_pair( "virtual", new ItemExtractor( ".11.3" ) ) ); + extractor_map->insert( make_pair( "supported", new ItemExtractor( ".11.4" ) ) ); + extractor_map->insert( make_pair( "mapped", new ItemExtractor( ".11.5" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +connections_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "current", new ItemExtractor( ".12.1" ) ) ); + extractor_map->insert( make_pair( "available", new ItemExtractor( ".12.2" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +bg_flush_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "flushes", new ItemExtractor( ".13.1" ) ) ); + extractor_map->insert( make_pair( "total_ms", new ItemExtractor( ".13.2" ) ) ); + extractor_map->insert( make_pair( "average_ms", new ItemExtractor( ".13.3" ) ) ); + extractor_map->insert( make_pair( "last_ms", new ItemExtractor( ".13.4" ) ) ); + extractor_map->insert( make_pair( "last_finished", new ItemExtractor( ".13.5" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +cursors_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "totalOpen", new ItemExtractor( ".14.1" ) ) ); + extractor_map->insert( make_pair( "clientCursors_size", new ItemExtractor( ".14.2" ) ) ); + extractor_map->insert( make_pair( "timedOut", new ItemExtractor( ".14.3" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +network_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "bytesIn", new ItemExtractor( ".15.1" ) ) ); + extractor_map->insert( make_pair( "bytesOut", new ItemExtractor( ".15.2" ) ) ); + extractor_map->insert( make_pair( "numRequests", new ItemExtractor( ".15.3" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +opcounters_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "insert", new ItemExtractor( ".16.1" ) ) ); + extractor_map->insert( make_pair( "query", new ItemExtractor( ".16.2" ) ) ); + extractor_map->insert( make_pair( "update", new ItemExtractor( ".16.3" ) ) ); + extractor_map->insert( make_pair( "delete", new ItemExtractor( ".16.4" ) ) ); + extractor_map->insert( make_pair( "getmore", new ItemExtractor( ".16.5" ) ) ); + extractor_map->insert( make_pair( "command", new ItemExtractor( ".16.6" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +asserts_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "regular", new ItemExtractor( ".17.1" ) ) ); + extractor_map->insert( make_pair( "warning", new ItemExtractor( ".17.2" ) ) ); + extractor_map->insert( make_pair( "msg", new ItemExtractor( ".17.3" ) ) ); + extractor_map->insert( make_pair( "user", new ItemExtractor( ".17.4" ) ) ); + extractor_map->insert( make_pair( "rollovers", new ItemExtractor( ".17.5" ) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +record_stats_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "accessesNotInMemory", new ItemExtractor( ".18.1" ) ) ); + extractor_map->insert( make_pair( "pageFaultExceptionsThrown", new ItemExtractor( ".18.2" ) ) ); + + return new StructExtractor(extractor_map); +} + +struct LocksExtractor + : public StructExtractor +{ +public: + LocksExtractor(int row) + : StructExtractor(get_extractor_map(row)) + {} + +protected: + static map * + get_extractor_map(int row) + { + map *overall_map = new map; + + if( 0 == row ) + { + map *details_map = new map; + details_map->insert( make_pair( "R", new ItemExtractor( ".19.1.1" ) ) ); + details_map->insert( make_pair( "W", new ItemExtractor( ".19.1.2" ) ) ); + overall_map->insert( make_pair( "timeLockedMicros", new StructExtractor(details_map) ) ); + + details_map = new map; + details_map->insert( make_pair( "R", new ItemExtractor( ".19.2.1" ) ) ); + details_map->insert( make_pair( "W", new ItemExtractor( ".19.2.2" ) ) ); + overall_map->insert( make_pair( "timeAcquiringMicros", new StructExtractor(details_map) ) ); + } + else + { + map *details_map = new map; + details_map->insert( make_pair( "r", new ItemExtractor( ".21.1.14." + lexical_cast(row) ) ) ); + details_map->insert( make_pair( "w", new ItemExtractor( ".21.1.15." + lexical_cast(row) ) ) ); + overall_map->insert( make_pair( "timeLockedMicros", new StructExtractor(details_map) ) ); + + details_map = new map; + details_map->insert( make_pair( "r", new ItemExtractor( ".21.1.16." + lexical_cast(row) ) ) ); + details_map->insert( make_pair( "w", new ItemExtractor( ".21.1.17." + lexical_cast(row) ) ) ); + overall_map->insert( make_pair( "timeAcquiringMicros", new StructExtractor(details_map) ) ); + } + + return overall_map; + } +}; + + +Extractor * +locks_extractors(vector const &dbnames) +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( ".", new LocksExtractor(0) ) ); + unsigned row = 1; + for( vector::const_iterator ci = dbnames.begin(); ci != dbnames.end(); ++ci, ++row ) + extractor_map->insert( make_pair( *ci, new LocksExtractor(row) ) ); + + return new StructExtractor(extractor_map); +} + +StructExtractor * +serv_info_repl_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "setName", new ItemExtractor( ".20.1" ) ) ); + extractor_map->insert( make_pair( "ismaster", new ItemExtractor( ".20.2" ) ) ); + extractor_map->insert( make_pair( "secondary", new ItemExtractor( ".20.3" ) ) ); + extractor_map->insert( make_pair( "me", new ItemExtractor( ".20.4" ) ) ); + extractor_map->insert( make_pair( "primary", new ItemExtractor( ".20.5" ) ) ); + + static RowPostfix repl_rows; + vector repl_tbl; + repl_tbl.push_back(".2"); + extractor_map->insert( make_pair( + "hosts", new ListRowExtractor(".20.7", repl_rows, repl_tbl) ) ); + extractor_map->insert( make_pair( + "arbiters", new ListRowExtractor(".20.7", repl_rows, repl_tbl) ) ); + + return new StructExtractor(extractor_map); +} + +Extractor * +repl_network_queue_extractors() +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "waitTimeMs", new ItemExtractor( ".20.6.1" ) ) ); + extractor_map->insert( make_pair( "numElems", new ItemExtractor( ".20.6.2" ) ) ); + extractor_map->insert( make_pair( "numBytes", new ItemExtractor( ".20.6.3" ) ) ); + + return new StructExtractor(extractor_map); +} + +StructExtractor * +server_status_extractors(vector const &dbnames) +{ + map *extractor_map = new map; + + extractor_map->insert( make_pair( "host", new ItemExtractor( ".1" ) ) ); + extractor_map->insert( make_pair( "version", new ItemExtractor( ".2" ) ) ); + extractor_map->insert( make_pair( "process", new ItemExtractor( ".3" ) ) ); + extractor_map->insert( make_pair( "pid", new ItemExtractor( ".4" ) ) ); + extractor_map->insert( make_pair( "uptimeMillis", new ItemExtractor( ".5" ) ) ); + + extractor_map->insert( make_pair( "globalLock", global_lock_extractors() ) ); + extractor_map->insert( make_pair( "mem", mem_extractors() ) ); + extractor_map->insert( make_pair( "connections", connections_extractors() ) ); + extractor_map->insert( make_pair( "backgroundFlushing", bg_flush_extractors() ) ); + extractor_map->insert( make_pair( "cursors", cursors_extractors() ) ); + extractor_map->insert( make_pair( "network", network_extractors() ) ); + extractor_map->insert( make_pair( "opcounters", opcounters_extractors() ) ); + extractor_map->insert( make_pair( "asserts", asserts_extractors() ) ); + extractor_map->insert( make_pair( "recordStats", record_stats_extractors() ) ); + +/* XXX + if( serv_status.hasField("locks") && serv_status["locks"].Obj().hasField(dbname.value.c_str()) ); + { + BSONObj dbl = serv_status["locks"].Obj().getField(dbname.value).Obj(); + + if( dbl.hasField("timeLockedMicros") ) + { + BSONObj o3 = dbl["timeLockedMicros"].Obj(); + + if( o3.hasField("r") ) + out_vals.insert( extract_uint64( o3["r"], string(".21.1.14.") + row_str ) ); + if( o3.hasField("w") ) + out_vals.insert( extract_uint64( o3["w"], string(".21.1.15.") + row_str ) ); + } + + if( dbl.hasField("timeAcquiringMicros") ) + { + BSONObj o3 = dbl["timeAcquiringMicros"].Obj(); + + if( o3.hasField("r") ) + out_vals.insert( extract_uint64( o3["r"], string(".21.1.16.") + row_str ) ); + if( o3.hasField("w") ) + out_vals.insert( extract_uint64( o3["w"], string(".21.1.17.") + row_str ) ); + } + } +*/ + extractor_map->insert( make_pair( "locks", locks_extractors(dbnames) ) ); + extractor_map->insert( make_pair( "repl", serv_info_repl_extractors() ) ); + extractor_map->insert( make_pair( "replNetworkQueue", repl_network_queue_extractors() ) ); + // extractor_map->insert( make_pair( "indexCounters", index_cOunters_extractors() ) ); + + return new StructExtractor(extractor_map); +} + +template +struct DualItemExtractor + : public Extractor +{ +public: + DualItemExtractor(string const &oid1, string const &oid2) + : Extractor() + , m_oid1(oid1) + , m_oid2(oid2) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + out_vals.insert( extract( e, m_oid1 ) ); + out_vals.insert( extract( e, m_oid2 ) ); + } + +protected: + string const m_oid1; + string const m_oid2; + +private: + DualItemExtractor(); +}; + +struct ReplSetMemberRowExtractor + : public StructExtractor +{ +public: + ReplSetMemberRowExtractor() + : StructExtractor(get_extractor_map()) + {} + +protected: + static map * + get_extractor_map() + { + map *extractor_map = new map; + + extractor_map->insert( make_pair( "_id", new ItemExtractor( ".1" ) ) ); + extractor_map->insert( make_pair( "name", new ItemExtractor( ".2" ) ) ); + extractor_map->insert( make_pair( "health", new ItemExtractor( ".3" ) ) ); + extractor_map->insert( make_pair( "state", new ItemExtractor( ".4" ) ) ); + extractor_map->insert( make_pair( "stateStr", new ItemExtractor( ".5" ) ) ); + extractor_map->insert( make_pair( "uptime", new ItemExtractor( ".6" ) ) ); + extractor_map->insert( make_pair( + "optime", new DualItemExtractor( ".7", ".8" ) ) ); + extractor_map->insert( make_pair( "pingMs", new ItemExtractor( ".9" ) ) ); + extractor_map->insert( make_pair( "lastHeartbeat", new ItemExtractor( ".10" ) ) ); + + return extractor_map; + } +}; + +StructExtractor * +repl_set_status_extractors() +{ + map *extractor_map = new map; + + static RowPostfix repl_rows; + vector repl_tbl; + repl_tbl.push_back(".2"); + extractor_map->insert( make_pair( + "members", new ListRowExtractor(".20.7", repl_rows, repl_tbl) ) ); + + return new StructExtractor(extractor_map); +} + +struct DatabasesMemberRowExtractor + : public StructExtractor +{ +public: + DatabasesMemberRowExtractor() + : StructExtractor(get_extractor_map()) + , m_conn(NULL) + , m_cmd(BSONObjBuilder().append("dbstats", 1).obj()) + , m_dbinfo() + , m_dbextractor(get_dbinfo_map()) + {} + + DatabasesMemberRowExtractor(DBClientConnection *conn) + : StructExtractor(get_extractor_map()) + , m_conn(conn) + , m_cmd(BSONObjBuilder().append("dbstats", 1).obj()) + , m_dbinfo() + , m_dbextractor(get_dbinfo_map()) + {} + + virtual void operator()(BSONElement const &e, set &out_vals) + { + StructExtractor::operator() (e, out_vals); + + OidValueTuple search_key( ".1" ); // ASN.1 type doesn't matter, only OID + set::iterator cmp_iter = out_vals.lower_bound(search_key); + if( m_conn && (cmp_iter != out_vals.end()) ) + { + m_conn->runCommand(cmp_iter->value, m_cmd, m_dbinfo); + m_dbextractor(m_dbinfo, out_vals); + } + } + + virtual void operator()(BSONObj const &o, set &out_vals) + { + StructExtractor::operator() (o, out_vals); + + OidValueTuple search_key( ".1" ); // ASN.1 type doesn't matter, only OID + set::iterator cmp_iter = out_vals.lower_bound(search_key); + if( m_conn && (cmp_iter != out_vals.end()) ) + { + m_conn->runCommand(cmp_iter->value, m_cmd, m_dbinfo); + m_dbextractor(m_dbinfo, out_vals); + } + } + + void set_conn(DBClientConnection *conn) { m_conn = conn; } + +protected: + DBClientConnection *m_conn; + BSONObj m_cmd, m_dbinfo; + StructExtractor m_dbextractor; + + static map * + get_extractor_map() + { + map *extractor_map = new map; + + extractor_map->insert( make_pair( "name", new ItemExtractor( ".1" ) ) ); + extractor_map->insert( make_pair( "sizeOnDisk", new ItemExtractor( ".2" ) ) ); + extractor_map->insert( make_pair( "empty", new ItemExtractor( ".3" ) ) ); + + return extractor_map; + } + + static map * + get_dbinfo_map() + { + map *extractor_map = new map; + + extractor_map->insert( make_pair( "collections", new ItemExtractor( ".4" ) ) ); + extractor_map->insert( make_pair( "objects", new ItemExtractor( ".5" ) ) ); + extractor_map->insert( make_pair( "avgObjSize", new ItemExtractor( ".6" ) ) ); + extractor_map->insert( make_pair( "dataSize", new ItemExtractor( ".7" ) ) ); + extractor_map->insert( make_pair( "storageSize", new ItemExtractor( ".8" ) ) ); + extractor_map->insert( make_pair( "numExtents", new ItemExtractor( ".9" ) ) ); + extractor_map->insert( make_pair( "indexes", new ItemExtractor( ".10" ) ) ); + extractor_map->insert( make_pair( "sizeOnDisk", new ItemExtractor( ".11" ) ) ); + extractor_map->insert( make_pair( "fileSize", new ItemExtractor( ".12" ) ) ); + extractor_map->insert( make_pair( "nsSizeMB", new ItemExtractor( ".13" ) ) ); + + return extractor_map; + } +}; + +StructExtractor * +databases_extractors(DBClientConnection &conn) +{ + map *extractor_map = new map; + + static RowPostfix db_rows; + vector db_tbl; + db_tbl.push_back(".1"); + ListRowExtractor *lre = new ListRowExtractor(".21", db_rows, db_tbl); + lre->set_conn(&conn); + extractor_map->insert( make_pair( "databases", lre ) ); + + return new StructExtractor(extractor_map); +} + +void +collect(DBClientConnection &c, set &out_vals) +{ + BSONObj serv_status, dbases, repl_info, cmd; + StructExtractor *bson_extractor = 0; + + cmd = BSONObjBuilder().append("listDatabases", 1).obj(); + c.runCommand(DBNAME, cmd, dbases); + + bson_extractor = databases_extractors(c); + (*bson_extractor)(dbases, out_vals); + + // XXX extract row + dbname for serv_status.locks[] + vector database_names; + OidValueTuple search_key(".21.1.1."); + for( set::iterator cmp_iter = out_vals.lower_bound(search_key); + ( cmp_iter != out_vals.end() ) && ( 0 == cmp_iter->oid.compare( 0, search_key.oid.length(), search_key.oid ) ); + ++cmp_iter ) + { + database_names.push_back(cmp_iter->value); + } + + cmd = BSONObjBuilder().append( "serverStatus", 1 ).obj(); + c.runCommand(DBNAME, cmd, serv_status); + + bson_extractor = server_status_extractors(database_names); + (*bson_extractor)(serv_status, out_vals); + + cmd = BSONObjBuilder().append("replSetGetStatus", 1).obj(); + c.runCommand(DBNAME, cmd, repl_info); + + bson_extractor = repl_set_status_extractors(); + (*bson_extractor)(repl_info, out_vals); +} + +void +dump(set const &out_vals) +{ + vector result; + result.reserve(out_vals.size() + 2); + for( set::iterator iter = out_vals.begin(); + iter != out_vals.end(); + ++iter ) + { + std::string s = " [ \""; + + s += iter->oid; + s += "\", "; + s += lexical_cast(iter->type); + s += ", "; + if( ASN_OCTET_STR == iter->type ) + s += "\""; + if( ASN_NULL == iter->type ) + s += "null"; + else if( ASN_OCTET_STR == iter->type ) + s += boost::locale::conv::utf_to_utf(iter->value); + else + s += iter->value; + if( ASN_OCTET_STR == iter->type ) + s += "\""; + s += " ]"; + + result.push_back(s); + } + + cout << "[" << endl + << join(",\n", result) << endl + << "]" << endl; +} + +int +main(int argc, char *argv[]) +{ + try + { + options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ("dsn", value(), "set mongodb dsn") + ; + variables_map vm; + store( parse_command_line( argc, argv, desc ), vm ); + notify(vm); + + if( vm.count("help") ) + { + cout << desc << endl; + return 1; + } + + if( vm.count("dsn") == 0 ) + { + cerr << desc << endl; + return 255; + } + + set out_vals; + do { + DBClientConnection c; + + boost::timer::cpu_timer db_dur; + db_dur.start(); + connect(c, vm["dsn"].as(), DBNAME, "admin"); + collect(c, out_vals); + db_dur.stop(); + + OidValueTuple val( ".99.1", SMI_COUNTER64 ); + val.value = lexical_cast( db_dur.elapsed().user ); + out_vals.insert( val ); + + val.oid = ".99.2"; + val.value = lexical_cast( db_dur.elapsed().system ); + out_vals.insert( val ); + + val.oid = ".99.3"; + val.value = lexical_cast( db_dur.elapsed().wall ); + out_vals.insert( val ); + } while(0); + + dump(out_vals); + } + catch( DBException &e ) + { + cout << "caught " << e.what() << endl; + } + + return 0; +} -- 2.11.4.GIT