From 65fbd2ec9349ba03983e7106fe88d99d32858dd4 Mon Sep 17 00:00:00 2001 From: Michael McThrow Date: Wed, 27 Oct 2010 13:31:26 -0700 Subject: [PATCH] Add the Ceph monitoring GUI This adds a graphical monitoring mode to the ceph cluster monitoring tool. Its functionality is similar to ./ceph -w. With ./ceph -g, you can watch over the whole cluster graphically. It uses GTK2. Signed-off-by: Colin McCabe --- configure.ac | 2 + src/Makefile.am | 8 + src/tools/ceph.cc | 402 ++-- src/tools/ceph.h | 60 + src/tools/gui.cc | 1727 +++++++++++++++++ src/tools/gui.h | 394 ++++ src/tools/gui_resources.h | 19 + src/tools/gui_resources/blacklist.svg | 224 +++ src/tools/gui_resources/client.svg | 96 + .../gui_resources/cluster_stats_window.glade | 102 + src/tools/gui_resources/down_osd.svg | 181 ++ src/tools/gui_resources/failed_mds.svg | 118 ++ src/tools/gui_resources/gui_monitor.build | 804 ++++++++ src/tools/gui_resources/gui_monitor.glade | 970 +++++++++ src/tools/gui_resources/gui_monitor_old.glade | 1027 ++++++++++ src/tools/gui_resources/main-window.glade | 591 ++++++ src/tools/gui_resources/mds.svg | 110 ++ src/tools/gui_resources/monitor.svg | 92 + .../gui_resources/node_stats_window.glade | 102 + src/tools/gui_resources/osd.svg | 181 ++ src/tools/gui_resources/out_osd.svg | 185 ++ src/tools/gui_resources/pg.svg | 111 ++ src/tools/gui_resources/stats_window.glade | 159 ++ src/tools/gui_resources/stopped_mds.svg | 120 ++ 24 files changed, 7611 insertions(+), 174 deletions(-) create mode 100644 src/tools/ceph.h create mode 100644 src/tools/gui.cc create mode 100644 src/tools/gui.h create mode 100644 src/tools/gui_resources.h create mode 100644 src/tools/gui_resources/blacklist.svg create mode 100644 src/tools/gui_resources/client.svg create mode 100644 src/tools/gui_resources/cluster_stats_window.glade create mode 100644 src/tools/gui_resources/down_osd.svg create mode 100644 src/tools/gui_resources/failed_mds.svg create mode 100644 src/tools/gui_resources/gui_monitor.build create mode 100644 src/tools/gui_resources/gui_monitor.glade create mode 100644 src/tools/gui_resources/gui_monitor_old.glade create mode 100644 src/tools/gui_resources/main-window.glade create mode 100644 src/tools/gui_resources/mds.svg create mode 100644 src/tools/gui_resources/monitor.svg create mode 100644 src/tools/gui_resources/node_stats_window.glade create mode 100644 src/tools/gui_resources/osd.svg create mode 100644 src/tools/gui_resources/out_osd.svg create mode 100644 src/tools/gui_resources/pg.svg create mode 100644 src/tools/gui_resources/stats_window.glade create mode 100644 src/tools/gui_resources/stopped_mds.svg diff --git a/configure.ac b/configure.ac index 39244910825..5805ad81841 100644 --- a/configure.ac +++ b/configure.ac @@ -220,6 +220,8 @@ AM_CONDITIONAL(WITH_GTK2, [test "x$HAVE_GTK2" = "xyes"]) if test "x$HAVE_GTK2" == "xyes"; then PKG_CHECK_MODULES(GTKMM, [gtkmm-2.4 >= 1.0.0]) + AC_DEFINE([HAVE_GTK2], [1], + [Define if you have GTK2]) fi AC_CONFIG_HEADERS([src/acconfig.h]) diff --git a/src/Makefile.am b/src/Makefile.am index 2c8e78300f9..b57000df8ca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,6 +29,14 @@ cmds_CXXFLAGS = ${AM_CFLAGS} # admin tools ceph_SOURCES = tools/ceph.cc msg/SimpleMessenger.cc ceph_LDADD = libcrush.a libcommon.a -ledit -lpthread -lm -lcrypto +ceph_CXXFLAGS = ${AM_CFLAGS} + +if WITH_GTK2 +ceph_SOURCES += tools/gui.cc +ceph_LDADD += $(GTKMM_LIBS) +ceph_CXXFLAGS += $(GTKMM_CFLAGS) +endif + cconf_SOURCES = cconf.cc cconf_LDADD = libcommon.a -lpthread -lm -lcrypto cauthtool_SOURCES = cauthtool.cc diff --git a/src/tools/ceph.cc b/src/tools/ceph.cc index 8486a8d15a6..d0cc82d0654 100644 --- a/src/tools/ceph.cc +++ b/src/tools/ceph.cc @@ -17,14 +17,16 @@ #include using namespace std; -#include "config.h" - -#include "mon/MonMap.h" -#include "mon/MonClient.h" -#include "msg/SimpleMessenger.h" +#include "acconfig.h" #include "messages/MMonCommand.h" #include "messages/MMonCommandAck.h" +#include "mon/MonClient.h" +#include "mon/MonMap.h" +#include "msg/SimpleMessenger.h" +#include "tools/ceph.h" +#include "common/Cond.h" +#include "common/Mutex.h" #include "common/Timer.h" #include "common/common_init.h" @@ -40,15 +42,22 @@ extern "C" { #include } +enum CephToolMode { + CEPH_TOOL_MODE_CLI_INPUT = 0, + CEPH_TOOL_MODE_OBSERVER = 1, + CEPH_TOOL_MODE_ONE_SHOT_OBSERVER = 2, + CEPH_TOOL_MODE_GUI = 3 +}; +static enum CephToolMode ceph_tool_mode(CEPH_TOOL_MODE_CLI_INPUT); -Mutex lock("ceph.cc lock"); -Cond cond; -SimpleMessenger *messenger = 0; -SafeTimer timer(lock); -MonClient mc; +struct ceph_tool_data g; -const char *outfile = 0; +static Cond cmd_cond; +static SimpleMessenger *messenger = 0; +static SafeTimer timer(g.lock); + +static const char *outfile = 0; @@ -76,27 +85,22 @@ Context *resend_event = 0; #include "messages/MMonObserve.h" #include "messages/MMonObserveNotify.h" -int observe = 0; -bool one_shot = false; -static PGMap pgmap; -static MDSMap mdsmap; -static OSDMap osdmap; static set registered, seen; version_t map_ver[PAXOS_NUM]; -void handle_observe(MMonObserve *observe) +static void handle_observe(MMonObserve *observe) { dout(1) << observe->get_source() << " -> " << get_paxos_name(observe->machine_id) << " registered" << dendl; - lock.Lock(); + g.lock.Lock(); registered.insert(observe->machine_id); - lock.Unlock(); + g.lock.Unlock(); observe->put(); } -void handle_notify(MMonObserveNotify *notify) +static void handle_notify(MMonObserveNotify *notify) { utime_t now = g_clock.now(); @@ -105,8 +109,8 @@ void handle_notify(MMonObserveNotify *notify) << (notify->is_latest ? " (latest)" : "") << dendl; - if (ceph_fsid_compare(¬ify->fsid, &mc.monmap.fsid)) { - dout(0) << notify->get_source_inst() << " notify fsid " << notify->fsid << " != " << mc.monmap.fsid << dendl; + if (ceph_fsid_compare(¬ify->fsid, &g.mc.monmap.fsid)) { + dout(0) << notify->get_source_inst() << " notify fsid " << notify->fsid << " != " << g.mc.monmap.fsid << dendl; notify->put(); return; } @@ -119,31 +123,34 @@ void handle_notify(MMonObserveNotify *notify) { bufferlist::iterator p = notify->bl.begin(); if (notify->is_latest) { - pgmap.decode(p); + g.pgmap.decode(p); } else { PGMap::Incremental inc; inc.decode(p); - pgmap.apply_incremental(inc); + g.pgmap.apply_incremental(inc); } - cout << now << " pg " << pgmap << std::endl; + *g.log << now << " pg " << g.pgmap << std::endl; + g.updates |= PG_MON_UPDATE; break; } case PAXOS_MDSMAP: - mdsmap.decode(notify->bl); - cout << now << " mds " << mdsmap << std::endl; + g.mdsmap.decode(notify->bl); + *g.log << now << " mds " << g.mdsmap << std::endl; + g.updates |= MDS_MON_UPDATE; break; case PAXOS_OSDMAP: { if (notify->is_latest) { - osdmap.decode(notify->bl); + g.osdmap.decode(notify->bl); } else { OSDMap::Incremental inc(notify->bl); - osdmap.apply_incremental(inc); + g.osdmap.apply_incremental(inc); } - cout << now << " osd " << osdmap << std::endl; + *g.log << now << " osd " << g.osdmap << std::endl; } + g.updates |= OSD_MON_UPDATE; break; case PAXOS_LOG: @@ -154,14 +161,14 @@ void handle_notify(MMonObserveNotify *notify) ::decode(summary, p); // show last log message if (!summary.tail.empty()) - cout << now << " log " << summary.tail.back() << std::endl; + *g.log << now << " log " << summary.tail.back() << std::endl; } else { LogEntry le; __u8 v; ::decode(v, p); while (!p.end()) { le.decode(p); - cout << now << " log " << le << std::endl; + *g.log << now << " log " << le << std::endl; } } break; @@ -180,7 +187,7 @@ void handle_notify(MMonObserveNotify *notify) tClassVersionMap::iterator iter = map.begin(); if (iter != map.end()) - cout << now << " class " << iter->second << std::endl; + *g.log << now << " class " << iter->second << std::endl; } } else { __u8 v; @@ -190,7 +197,7 @@ void handle_notify(MMonObserveNotify *notify) ::decode(inc, p); ClassInfo info; inc.decode_info(info); - cout << now << " class " << info << std::endl; + *g.log << now << " class " << info << std::endl; } } break; @@ -203,12 +210,12 @@ void handle_notify(MMonObserveNotify *notify) if (notify->is_latest) { KeyServerData data; ::decode(data, p); - cout << now << " auth " << std::endl; + *g.log << now << " auth " << std::endl; } else { while (!p.end()) { AuthMonitor::Incremental inc; inc.decode(p); - cout << now << " auth " << inc.name.to_str() << std::endl; + *g.log << now << " auth " << inc.name.to_str() << std::endl; } } #endif @@ -218,22 +225,32 @@ void handle_notify(MMonObserveNotify *notify) case PAXOS_MONMAP: { - mc.monmap.decode(notify->bl); - cout << now << " mon " << mc.monmap << std::endl; + g.mc.monmap.decode(notify->bl); + *g.log << now << " mon " << g.mc.monmap << std::endl; } break; default: - cout << now << " ignoring unknown machine id " << notify->machine_id << std::endl; + *g.log << now << " ignoring unknown machine id " << notify->machine_id << std::endl; } map_ver[notify->machine_id] = notify->ver; // have we seen them all? seen.insert(notify->machine_id); - if (one_shot && seen.size() == PAXOS_NUM) { - messenger->shutdown(); - } + switch (ceph_tool_mode) { + case CEPH_TOOL_MODE_ONE_SHOT_OBSERVER: + if (seen.size() == PAXOS_NUM) { + messenger->shutdown(); + } + break; + case CEPH_TOOL_MODE_GUI: + g.gui_cond.Signal(); + break; + default: + // do nothing + break; + } notify->put(); } @@ -255,9 +272,9 @@ static void send_observe_requests() bool sent = false; for (int i=0; iget_source_inst(); reply_rs = ack->rs; reply_rc = ack->r; reply_bl = ack->get_data(); - cond.Signal(); + cmd_cond.Signal(); if (resend_event) { timer.cancel_event(resend_event); resend_event = 0; } - lock.Unlock(); + g.lock.Unlock(); ack->put(); } -void send_command() +static void send_command() { version_t last_seen_version = 0; - MMonCommand *m = new MMonCommand(mc.monmap.fsid, last_seen_version); + MMonCommand *m = new MMonCommand(g.mc.monmap.fsid, last_seen_version); m->cmd = pending_cmd; m->set_data(pending_bl); - cout << g_clock.now() << " mon" << " <- " << pending_cmd << std::endl; - mc.send_mon_message(m); + *g.log << g_clock.now() << " mon" << " <- " << pending_cmd << std::endl; + g.mc.send_mon_message(m); } - class Admin : public Dispatcher { bool ms_dispatch(Message *m) { switch (m->get_type()) { @@ -319,12 +335,13 @@ class Admin : public Dispatcher { void ms_handle_connect(Connection *con) { if (con->get_peer_type() == CEPH_ENTITY_TYPE_MON) { - lock.Lock(); - if (observe) + g.lock.Lock(); + if (ceph_tool_mode != CEPH_TOOL_MODE_CLI_INPUT) { send_observe_requests(); + } if (pending_cmd.size()) send_command(); - lock.Unlock(); + g.lock.Unlock(); } } bool ms_handle_reset(Connection *con) { return false; } @@ -334,7 +351,7 @@ class Admin : public Dispatcher { int do_command(vector& cmd, bufferlist& bl, string& rs, bufferlist& rbl) { - Mutex::Locker l(lock); + Mutex::Locker l(g.lock); pending_cmd = cmd; pending_bl = bl; @@ -343,11 +360,11 @@ int do_command(vector& cmd, bufferlist& bl, string& rs, bufferlist& rbl) send_command(); while (!reply) - cond.Wait(lock); + cmd_cond.Wait(g.lock); rs = rs; rbl = reply_bl; - cout << g_clock.now() << " " + *g.log << g_clock.now() << " " << reply_from.name << " -> '" << reply_rs << "' (" << reply_rc << ")" << std::endl; @@ -355,9 +372,7 @@ int do_command(vector& cmd, bufferlist& bl, string& rs, bufferlist& rbl) return reply_rc; } - - -void usage() +static void usage() { cerr << "usage: ceph [options] [commands]" << std::endl; cerr << "If no commands are specified, enter interactive mode.\n"; @@ -373,11 +388,13 @@ void usage() cerr << " print current system status\n"; cerr << " -w or --watch\n"; cerr << " watch system status changes in real time (push)\n"; + cerr << " -g or --gui\n"; + cerr << " watch system status changes graphically\n"; generic_client_usage(); } - -const char *cli_prompt(EditLine *e) { +static const char *cli_prompt(EditLine *e) +{ return "ceph> "; } @@ -402,95 +419,21 @@ int do_cli() /* This sets up the call back functions for history functionality */ el_set(el, EL_HIST, history, myhistory); - Tokenizer *tok = tok_init(NULL); - - bufferlist in; while (1) { - int count; // # chars read - const char *line = el_gets(el, &count); + int chars_read; + const char *line = el_gets(el, &chars_read); - if (!count) { - cout << "quit" << std::endl; + //*g.log << "typed '" << line << "'" << std::endl; + + if (chars_read == 0) { + *g.log << "quit" << std::endl; break; } - //cout << "typed '" << line << "'" << std::endl; - - if (strcmp(line, "quit\n") == 0) - break; - history(myhistory, &ev, H_ENTER, line); - int argc; - const char **argv; - tok_str(tok, line, &argc, &argv); - tok_reset(tok); - - vector cmd; - const char *infile = 0; - const char *outfile = 0; - for (int i=0; i") == 0 && i < argc-1) { - outfile = argv[++i]; - continue; - } - if (argv[i][0] == '>') { - outfile = argv[i] + 1; - while (*outfile == ' ') outfile++; - continue; - } - if (strcmp(argv[i], "<") == 0 && i < argc-1) { - infile = argv[++i]; - continue; - } - if (argv[i][0] == '<') { - infile = argv[i] + 1; - while (*infile == ' ') infile++; - continue; - } - cmd.push_back(argv[i]); - } - if (cmd.empty()) - continue; - - if (cmd.size() == 1 && cmd[0] == "print") { - cout << "----" << std::endl; - write(1, in.c_str(), in.length()); - cout << "---- (" << in.length() << " bytes)" << std::endl; - continue; - } - - //cout << "cmd is " << cmd << std::endl; - - bufferlist out; - if (infile) { - if (out.read_file(infile) == 0) { - cout << "read " << out.length() << " from " << infile << std::endl; - } else { - char buf[80]; - cerr << "couldn't read from " << infile << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl; - continue; - } - } - - in.clear(); - string rs; - do_command(cmd, out, rs, in); - - if (in.length()) { - if (outfile) { - if (strcmp(outfile, "-") == 0) { - cout << "----" << std::endl; - write(1, in.c_str(), in.length()); - cout << "---- (" << in.length() << " bytes)" << std::endl; - } else { - in.write_file(outfile); - cout << "wrote " << in.length() << " to " << outfile << std::endl; - } - } else { - cout << "got " << in.length() << " byte payload; 'print' to dump to terminal, or add '>-' to command." << std::endl; - } - } + if (run_command(line)) + break; } history_end(myhistory); @@ -499,12 +442,95 @@ int do_cli() return 0; } - - - - -int main(int argc, const char **argv, const char *envp[]) +int run_command(const char *line) { + if (strcmp(line, "quit\n") == 0) + return 1; + + int argc; + const char **argv; + Tokenizer *tok = tok_init(NULL); + tok_str(tok, line, &argc, &argv); + tok_reset(tok); + tok_end(tok); + + vector cmd; + const char *infile = 0; + const char *outfile = 0; + for (int i=0; i") == 0 && i < argc-1) { + outfile = argv[++i]; + continue; + } + if (argv[i][0] == '>') { + outfile = argv[i] + 1; + while (*outfile == ' ') outfile++; + continue; + } + if (strcmp(argv[i], "<") == 0 && i < argc-1) { + infile = argv[++i]; + continue; + } + if (argv[i][0] == '<') { + infile = argv[i] + 1; + while (*infile == ' ') infile++; + continue; + } + cmd.push_back(argv[i]); + } + if (cmd.empty()) + return 0; + + bufferlist in; + if (cmd.size() == 1 && cmd[0] == "print") { + *g.log << "----" << std::endl; + write(1, in.c_str(), in.length()); + *g.log << "---- (" << in.length() << " bytes)" << std::endl; + return 0; + } + + //out << "cmd is " << cmd << std::endl; + + bufferlist out; + if (infile) { + if (out.read_file(infile) == 0) { + *g.log << "read " << out.length() << " from " << infile << std::endl; + } else { + char buf[80]; + *g.log << "couldn't read from " << infile << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl; + return 0; + } + } + + in.clear(); + string rs; + do_command(cmd, out, rs, in); + + if (in.length() == 0) + return 0; + + if (outfile) { + if (strcmp(outfile, "-") == 0) { + *g.log << "----" << std::endl; + write(1, in.c_str(), in.length()); + *g.log << "---- (" << in.length() << " bytes)" << std::endl; + } + else { + in.write_file(outfile); + *g.log << "wrote " << in.length() << " to " + << outfile << std::endl; + } + } + else { + *g.log << "got " << in.length() << " byte payload; 'print' " + << "to dump to terminal, or add '>-' to command." << std::endl; + } + return 0; +} + +int main(int argc, const char **argv) +{ + ostringstream gss; DEFINE_CONF_VARS(usage); vector args; argv_to_vec(argc, argv, args); @@ -542,12 +568,13 @@ int main(int argc, const char **argv, const char *envp[]) cout << "read " << st.st_size << " bytes from " << args[i] << std::endl; } } else if (CONF_ARG_EQ("status", 's')) { - CONF_SAFE_SET_ARG_VAL(&observe, OPT_BOOL); - one_shot = true; + ceph_tool_mode = CEPH_TOOL_MODE_ONE_SHOT_OBSERVER; } else if (CONF_ARG_EQ("watch", 'w')) { - CONF_SAFE_SET_ARG_VAL(&observe, OPT_BOOL); + ceph_tool_mode = CEPH_TOOL_MODE_OBSERVER; } else if (CONF_ARG_EQ("help", 'h')) { usage(); + } else if (CONF_ARG_EQ("gui", 'g')) { + ceph_tool_mode = CEPH_TOOL_MODE_GUI; } else if (args[i][0] == '-' && nargs.empty()) { cerr << "unrecognized option " << args[i] << std::endl; usage(); @@ -565,7 +592,7 @@ int main(int argc, const char **argv, const char *envp[]) } // get monmap - if (mc.build_initial_monmap() < 0) + if (g.mc.build_initial_monmap() < 0) return -1; // start up network @@ -575,31 +602,39 @@ int main(int argc, const char **argv, const char *envp[]) messenger->start(); - mc.set_messenger(messenger); - mc.init(); + g.mc.set_messenger(messenger); + g.mc.init(); - if (mc.authenticate() < 0) { + if (g.mc.authenticate() < 0) { cerr << "unable to authenticate as " << *g_conf.entity_name << std::endl; return -1; } - if (mc.get_monmap() < 0) { + if (g.mc.get_monmap() < 0) { cerr << "unable to get monmap" << std::endl; return -1; } int ret = 0; - if (observe) { - lock.Lock(); - send_observe_requests(); - lock.Unlock(); - } else { - if (vcmd.size()) { - + switch (ceph_tool_mode) + { + case CEPH_TOOL_MODE_OBSERVER: + case CEPH_TOOL_MODE_ONE_SHOT_OBSERVER: + g.lock.Lock(); + send_observe_requests(); + g.lock.Unlock(); + break; + + case CEPH_TOOL_MODE_CLI_INPUT: { + if (vcmd.empty()) { + // interactive mode + do_cli(); + messenger->shutdown(); + break; + } string rs; bufferlist odata; ret = do_command(vcmd, indata, rs, odata); - int len = odata.length(); if (len) { if (outfile) { @@ -613,18 +648,37 @@ int main(int argc, const char **argv, const char *envp[]) cout << g_clock.now() << " got " << len << " byte payload, discarding (specify -o shutdown(); + break; } - - messenger->shutdown(); - } + case CEPH_TOOL_MODE_GUI: { +#ifdef HAVE_GTK2 + g.log = &gss; + g.slog = &gss; + + // TODO: make sure that we capture the log this generates in the GUI + g.lock.Lock(); + send_observe_requests(); + g.lock.Unlock(); + + run_gui(argc, (char **)argv); +#else + cerr << "I'm sorry. This tool was not compiled with support for " + << "GTK2." << std::endl; + ret = EXIT_FAILURE; +#endif + messenger->shutdown(); + break; + } + + default: + assert(0); + break; + } // wait for messenger to finish messenger->wait(); messenger->destroy(); return ret; } - diff --git a/src/tools/ceph.h b/src/tools/ceph.h new file mode 100644 index 00000000000..ca15c2ca1e2 --- /dev/null +++ b/src/tools/ceph.h @@ -0,0 +1,60 @@ +#ifndef CEPH_TOOL_H +#define CEPH_TOOL_H + +#include "common/Cond.h" +#include "common/Mutex.h" +#include "mon/MonClient.h" +#include "mon/PGMap.h" +#include "mds/MDSMap.h" +#include "osd/OSDMap.h" + +#include +#include + +#define OSD_MON_UPDATE (1<<0) +#define MDS_MON_UPDATE (1<<1) +#define PG_MON_UPDATE (1<<2) +#define MON_MON_UPDATE (1<<3) +#define EVERYTHING_UPDATE 0xffffffff + +// tool/ceph.cc +struct ceph_tool_data +{ + PGMap pgmap; + MDSMap mdsmap; + OSDMap osdmap; + MonClient mc; + + // Which aspects of the cluster have been updated recently? + uint32_t updates; + + // The main log for ceph-tool + std::ostream *log; + + // Used by the GUI to read from the log. + // NULL if there is no GUI active. + std::ostringstream *slog; + + // The ceph-tool lock + Mutex lock; + + // A condition variable used to wake up the GUI thread + Cond gui_cond; + + ceph_tool_data() + : updates(EVERYTHING_UPDATE), + log(&std::cout), + slog(NULL), + lock("ceph.cc lock") + { + } +}; + +// tool/ceph.cc +extern struct ceph_tool_data g; +int run_command(const char *line); + +// tool/gyi.cc +int run_gui(int argc, char **argv); + +#endif diff --git a/src/tools/gui.cc b/src/tools/gui.cc new file mode 100644 index 00000000000..79e3758a837 --- /dev/null +++ b/src/tools/gui.cc @@ -0,0 +1,1727 @@ +/* + * gui_monitor_interface.cc -- Handles the GUI of the Ceph monitor + * + * 2009-10 Michael McThrow + */ + +#include "common/Clock.h" +#include "common/Cond.h" +#include "common/Mutex.h" +#include "mon/MonClient.h" +#include "mon/MonMap.h" +#include "tools/ceph.h" +#include "tools/gui.h" +#include "tools/gui_resources.h" + +#include +#include + +using std::set; +using std::string; + +#define MAX_VIEW_ICONS 6 + +// Computes floor(m/n), where m and n are integers. +#define FLOOR(x, y) ((x) % (y)) ? ((x) / (y) + 1) : ((x) / (y)) + +#define MY_GET_WIDGET(x) do { builder->get_widget(#x, x); } while(0); + +///////////////// Functions ///////////////// + +// Given an integer m and an array of length n, divides m into n categories of +// equal or near-equal values. +static void gen_ranges(unsigned int m, unsigned int n, + unsigned int *ranges) +{ + unsigned int multiple = FLOOR(m, n); + unsigned int remainder = m % n; + long int i, j; + + if (m <= n) { + for (i = 0; i < n; i++) + ranges[i] = (i < m) ? 1 : 0; + } + else { + for (i = n - 1, j = (remainder) ? n - remainder: 0; i >= 0; i--) { + if (j) { + ranges[i] = multiple - 1; + --j; + } + else + ranges[i] = multiple; + } + } +} + +// Converts a Ceph entity_addr_t to a C++ string. +static std::string addr_to_str(const entity_addr_t& addr) +{ + ostringstream oss; + oss << addr; + return oss.str(); +} + +///////////////// Classes ///////////////// + +class GuiMonitorThread : public Thread +{ +public: + GuiMonitorThread(GuiMonitor *gui_) + : gui(gui_), + shutting_down(false) + { + } + + void *entry() + { + g.lock.Lock(); + while (true) { + utime_t t(g_clock.now()); + t += 3.0; + g.gui_cond.WaitUntil(g.lock, t); + if (shutting_down) { + g.lock.Unlock(); + return NULL; + } + gui->check_status(); + } + } + + void shutdown() + { + shutting_down = true; + g.gui_cond.Signal(); + } + +private: + GuiMonitor *gui; + bool shutting_down; +}; + +GuiMonitor::GuiMonitor(Glib::RefPtr builder) + : pg_cluster_zoom(0), + osd_cluster_zoom(0), + mds_cluster_zoom(0), + send_command_success(false), + view_node_success(false), + thread(NULL) +{ + MY_GET_WIDGET(guiMonitorWindow); + MY_GET_WIDGET(guiMonitorQuitImageMenuItem); + MY_GET_WIDGET(guiMonitorCopyImageMenuItem); + MY_GET_WIDGET(guiMonitorSendCommandMenuItem); + MY_GET_WIDGET(guiMonitorViewNodeMenuItem); + MY_GET_WIDGET(guiMonitorAboutImageMenuItem); + MY_GET_WIDGET(guiMonitorPGClusterStatsLabel); + MY_GET_WIDGET(guiMonitorPGClusterIconView); + MY_GET_WIDGET(guiMonitorPGClusterBackButton); + MY_GET_WIDGET(guiMonitorPGClusterViewAllButton); + MY_GET_WIDGET(guiMonitorPGClusterStatsButton); + MY_GET_WIDGET(guiMonitorMonitorClusterStatsLabel); + MY_GET_WIDGET(guiMonitorMonitorClusterTreeView); + MY_GET_WIDGET(guiMonitorMonitorClusterStatsButton); + MY_GET_WIDGET(guiMonitorOSDClusterStatsLabel); + MY_GET_WIDGET(guiMonitorOSDClusterIconView); + MY_GET_WIDGET(guiMonitorOSDClusterBackButton); + MY_GET_WIDGET(guiMonitorOSDClusterViewAllButton); + MY_GET_WIDGET(guiMonitorOSDClusterStatsButton); + MY_GET_WIDGET(guiMonitorMDSClusterStatsLabel); + MY_GET_WIDGET(guiMonitorMDSClusterIconView); + MY_GET_WIDGET(guiMonitorMDSClusterBackButton); + MY_GET_WIDGET(guiMonitorMDSClusterViewAllButton); + MY_GET_WIDGET(guiMonitorMDSClusterStatsButton); + MY_GET_WIDGET(guiMonitorLogTextView); + MY_GET_WIDGET(guiMonitorStatusbar); + MY_GET_WIDGET(guiMonitorAboutDialog); + MY_GET_WIDGET(viewNodeDialog); + MY_GET_WIDGET(viewNodeNameEntry); + MY_GET_WIDGET(viewNodeTypeComboBox); + MY_GET_WIDGET(viewNodeNameLabel); + MY_GET_WIDGET(sendCommandDialog); + MY_GET_WIDGET(sendCommandPromptEntry); +} + +GuiMonitor::~GuiMonitor() +{ + delete guiMonitorWindow; + delete viewNodeDialog; + delete sendCommandDialog; + delete guiMonitorAboutDialog; +} + +bool GuiMonitor::open_icon(Glib::RefPtr &icon, std::string path) +{ + try { + icon = Gdk::Pixbuf::create_from_file(path); + } + catch (const Gdk::PixbufError& e) { + cerr << "Problem making graphic from " << path << "; error code: " << + e.code() << std::endl; + return false; + } + catch (const Glib::FileError& e) { + cerr << "Problem open " << path << std::endl; + return false; + } + + return true; +} + +bool GuiMonitor::init() +{ + assert(!thread); + + if (!guiMonitorWindow) + return false; + if (!open_icon(blacklistIcon, BLACKLIST_ICON_PATH)) + return false; + if (!open_icon(clientIcon, CLIENT_ICON_PATH)) + return false; + if (!open_icon(MDSIcon, MDS_ICON_PATH)) + return false; + //if (!open_icon(failedMDSIcon, FAILED_MDS_ICON_PATH)) + // return false; + //if (!open_icon(stoppedMDSIcon, STOPPED_MDS_ICON_PATH)) + // return false; + if (!open_icon(monitorIcon, MONITOR_ICON_PATH)) + return false; + if (!open_icon(upOSDIcon, UP_OSD_ICON_PATH)) + return false; + if (!open_icon(downOSDIcon, DOWN_OSD_ICON_PATH)) + return false; + if (!open_icon(outOSDIcon, OUT_OSD_ICON_PATH)) + return false; + if (!open_icon(PGIcon, PG_ICON_PATH)) + return false; + + // connect callbacks to their corresponding signals. + connect_signals(); + + // Link elements (e.g., text buffers, list stores, etc.) to their + // containers (e.g., text views, list views, etc.) + link_elements(); + + thread = new GuiMonitorThread(this); + thread->create(); + return true; +} + +void GuiMonitor::run_main_loop(Gtk::Main &kit) +{ + kit.run(*guiMonitorWindow); +} + +// Connects signals to the GUI elements such that they respond to events. +void GuiMonitor::connect_signals() +{ + //Gtk::Main::signal_run().connect(sigc::mem_fun(this, + // &GuiMonitor::init_check_status)); + + Gtk::Main::signal_quit().connect(sigc::mem_fun(this, + &GuiMonitor::quit_signal_handler)); + + // guiMonitorWindow + guiMonitorWindow->signal_delete_event().connect( + sigc::mem_fun(this, &GuiMonitor::quit_gui)); + + // guiMonitorQuitImageMenuItem + guiMonitorQuitImageMenuItem->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::gui_monitor_quit)); + + // guiMonitorCopyImageMenuItem + guiMonitorCopyImageMenuItem->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::copy_log)); + + // grey out the copy menu item until the log text box gets data in it. + guiMonitorCopyImageMenuItem->set_sensitive(false); + + // guiMonitorSendCommandMenuItem + guiMonitorSendCommandMenuItem->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::open_send_command)); + + // guiMonitorViewNodeMenuItem + guiMonitorViewNodeMenuItem->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::open_view_mode)); + + // guiMonitorAboutImageMenuItem + guiMonitorAboutImageMenuItem->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::open_about_dialog)); + + // sendCommandDialog + sendCommandDialog->signal_response().connect(sigc::mem_fun(this, + &GuiMonitor::handle_send_command_response)); + + // viewNodeDialog + viewNodeDialog->signal_response().connect(sigc::mem_fun(this, + &GuiMonitor::handle_view_node_response)); + viewNodeTypeComboBox->signal_changed().connect(sigc::mem_fun(this, + &GuiMonitor::handle_view_node_change)); + + /* + * OSD Cluster + */ + // guiMonitorOSDClusterIconView + guiMonitorOSDClusterIconView->signal_item_activated().connect( + sigc::mem_fun(this, &GuiMonitor::osdc_cluster_zoom_in)); + + // guiMonitorOSDClusterBackButton + guiMonitorOSDClusterBackButton->signal_activate().connect(sigc::mem_fun(this, + &GuiMonitor::osdc_cluster_back)); + guiMonitorOSDClusterBackButton->signal_clicked().connect(sigc::mem_fun(this, + &GuiMonitor::osdc_cluster_back)); + + // guiMonitorOSDClusterViewAllButton + guiMonitorOSDClusterViewAllButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::osdc_cluster_view_all)); + guiMonitorOSDClusterViewAllButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::osdc_cluster_view_all)); + + // Grey out the "Back" and "View All" icons of the OSD cluster until the + // OSDCluterZoom > 0. + guiMonitorOSDClusterBackButton->set_sensitive(false); + guiMonitorOSDClusterViewAllButton->set_sensitive(false); + + // guiMonitorOSDClusterStatsButton + guiMonitorOSDClusterStatsButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::osdc_cluster_stats)); + guiMonitorOSDClusterStatsButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::osdc_cluster_stats)); + + /* + * MDS Cluster + */ + // guiMonitorMDSClusterIconView + guiMonitorMDSClusterIconView->signal_item_activated().connect( + sigc::mem_fun(this, &GuiMonitor::mds_cluster_zoom_in)); + + // guiMonitorMDSClusterBackButton + guiMonitorMDSClusterBackButton->signal_activate().connect(sigc::mem_fun(this, + &GuiMonitor::mds_cluster_back)); + guiMonitorMDSClusterBackButton->signal_clicked().connect(sigc::mem_fun(this, + &GuiMonitor::mds_cluster_back)); + + // guiMonitorMDSClusterViewAllButton + guiMonitorMDSClusterViewAllButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::mds_cluster_view_all)); + guiMonitorMDSClusterViewAllButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::mds_cluster_view_all)); + + // Grey out the "Back" and "View All" icons of the MDS cluster until the + // MDSCluterZoom > 0. + guiMonitorMDSClusterBackButton->set_sensitive(false); + guiMonitorMDSClusterViewAllButton->set_sensitive(false); + + // guiMonitorMDSClusterStatsButton + guiMonitorMDSClusterStatsButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::mds_cluster_stats)); + guiMonitorMDSClusterStatsButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::mds_cluster_stats)); + + /* + * PG Cluster + */ + // guiMonitorPGClusterIconView + guiMonitorPGClusterIconView->signal_item_activated().connect( + sigc::mem_fun(this, &GuiMonitor::pg_cluster_zoom_in)); + + // guiMonitorPGClusterBackButton + guiMonitorPGClusterBackButton->signal_activate().connect(sigc::mem_fun(this, + &GuiMonitor::pg_cluster_back)); + guiMonitorPGClusterBackButton->signal_clicked().connect(sigc::mem_fun(this, + &GuiMonitor::pg_cluster_back)); + + // guiMonitorPGClusterViewAllButton + guiMonitorPGClusterViewAllButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::pg_cluster_view_all)); + guiMonitorPGClusterViewAllButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::pg_cluster_view_all)); + + // Grey out the "Back" and "View All" icons of the PG cluster until the + // PGCluterZoom > 0. + guiMonitorPGClusterBackButton->set_sensitive(false); + guiMonitorPGClusterViewAllButton->set_sensitive(false); + + // guiMonitorPGClusterStatsButton + guiMonitorPGClusterStatsButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::pg_cluster_stats)); + guiMonitorPGClusterStatsButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::pg_cluster_stats)); + + /* + * Monitor Cluster + */ + // guiMonitorMonitorClusterStatsButton + guiMonitorMonitorClusterStatsButton->signal_activate().connect( + sigc::mem_fun(this, &GuiMonitor::monitor_cluster_stats)); + guiMonitorMonitorClusterStatsButton->signal_clicked().connect( + sigc::mem_fun(this, &GuiMonitor::monitor_cluster_stats)); +} + +// Connects elements to their GUI containers. +void GuiMonitor::link_elements() +{ + // get the buffer from the log's TextView + guiMonitorLogTextBuffer = guiMonitorLogTextView->get_buffer(); + + // create tree models + guiMonitorMonitorClusterEntries = Gtk::ListStore::create(monitorColumns); + guiMonitorOSDClusterIcons = Gtk::ListStore::create(icon_columns); + guiMonitorMDSClusterIcons = Gtk::ListStore::create(icon_columns); + guiMonitorPGClusterIcons = Gtk::ListStore::create(icon_columns); + + // connect the list stores to the tree views and icon views + guiMonitorMonitorClusterTreeView->set_model( + guiMonitorMonitorClusterEntries); + guiMonitorMonitorClusterTreeView->append_column("Monitor #", + monitorColumns.key); + guiMonitorMonitorClusterTreeView->append_column("Address", + monitorColumns.value); + + guiMonitorOSDClusterIconView->set_model(guiMonitorOSDClusterIcons); + guiMonitorOSDClusterIconView->set_text_column(icon_columns.caption); + guiMonitorOSDClusterIconView->set_pixbuf_column(icon_columns.icon); + + guiMonitorMDSClusterIconView->set_model(guiMonitorMDSClusterIcons); + guiMonitorMDSClusterIconView->set_text_column(icon_columns.caption); + guiMonitorMDSClusterIconView->set_pixbuf_column(icon_columns.icon); + + guiMonitorPGClusterIconView->set_model(guiMonitorPGClusterIcons); + guiMonitorPGClusterIconView->set_text_column(icon_columns.caption); + guiMonitorPGClusterIconView->set_pixbuf_column(icon_columns.icon); + + viewNodeNodeTypeListStore = Gtk::ListStore::create(viewNodeColumns); + viewNodeTypeComboBox->set_model(viewNodeNodeTypeListStore); + Gtk::TreeModel::Row currentRow; + + // Set contents of the list store for use in the the combo box in the "View + // Node..." dialog box + for (int i = 0; i < NUM_NODE_TYPES; i++) { + enum NodeType type = (NodeType)i; + + if (type == MON_NODE) + continue; + + currentRow = *(viewNodeNodeTypeListStore->append()); + + currentRow[viewNodeColumns.type] = type; + + currentRow[viewNodeColumns.name] = (type == OSD_NODE) ? + "Object Storage Device" : + (type == PG_NODE) ? "Placement Group" : + (type == MDS_NODE) ? "Metadata Server" : + "Monitor"; + } +} + +/* + * For the OSD, MDS, and PG clusters, the program should limit the amount + * of cluster icons displayed in their respective cluster view areas in + * order to ensure that users are not overwhelmed by the amount of + * clusters in large Ceph installations. Activating an icon corresponding + * to a node will open a window that displays information about the node. + * The number of icons shown in each view should not take up more than the + * display area of each view. If the amount of icons cannot be displayed + * in the view without a heavy amount of scrolling, then some or all icons + * will correspond to a *range* of nodes (for example, nodes 0 to 9 may be + * represented by one icon in the view). Activating an icon corresponding + * to a range of nodes will update the view to include only information + * about the nodes in that range; this can be conceputalized as "zooming + * into" a group of nodes. Range information corresponding to previous + * clusters will be preserved, but not updated. The stats label + * correspnding to the cluster will state if any updates are available. + * If the user clicks "View All," then this has the effect of updating the + * entire cluster view again. + * + * For the monitor cluster, a simple list containing information about + * each monitor cluster is shown. Activating an entry will open a window + * that displays more information about the monitor cluster. Unlike the + * other clusters, no "zoom in" or "zoom out" functionality is + * implemented. + */ +void GuiMonitor::update_osd_cluster_view() +{ + int num_osds = g.osdmap.get_max_osd(); + + // Do not update the OSD cluster view if the zoom level is not zero. + if (!osd_cluster_zoom) + view_osd_nodes(0, num_osds); + + ostringstream oss; + oss << num_osds << " OSD" << ((num_osds == 1) ? "" : "s") + << " Total: " << g.osdmap.get_num_up_osds() << " Up, " + << g.osdmap.get_num_in_osds() << " In"; + guiMonitorOSDClusterStatsLabel->set_label(oss.str()); +} + +void GuiMonitor::update_mds_cluster_view() +{ + // Do not update the MDS cluster view if the zoom level is not zero. + if (!mds_cluster_zoom) + view_mds_nodes(); + + ostringstream oss; + oss << g.mdsmap.get_num_mds() << " In, " + << g.mdsmap.get_num_failed() << " Failed, " + << g.mdsmap.get_num_mds(MDSMap::STATE_STOPPED) << " Stopped, " + << g.mdsmap.get_max_mds() << " Max"; + guiMonitorMDSClusterStatsLabel->set_label(oss.str()); +} + +void GuiMonitor::update_pg_cluster_view() +{ + // Do not update the PG cluster view if the zoom level is not zero. + if (!pg_cluster_zoom) + view_pg_nodes(0, 0, true); + + ostringstream oss; + oss << g.pgmap.pg_stat.size() << " Placement Groups\n" + << kb_t(g.pgmap.pg_sum.num_kb) << " Data, " + << kb_t(g.pgmap.osd_sum.kb_used) << " Used, " + << kb_t(g.pgmap.osd_sum.kb_avail) << " / " + << kb_t(g.pgmap.osd_sum.kb) << " Available"; + guiMonitorPGClusterStatsLabel->set_label(oss.str()); +} + +void GuiMonitor::update_mon_cluster_view() +{ + stringstream input; + Gtk::TreeModel::Row current_row; + entity_inst_t currentMonitor; + string currentAddress; + unsigned int monitors = g.mc.monmap.size(); + + // Clear current contents of the monitor cluster area of the GUI. + guiMonitorMonitorClusterEntries->clear(); + + string label = str(boost::format("%lu Monitors") % monitors); + + guiMonitorMonitorClusterStatsLabel->set_label(label); + + // For each monitor in the monitor map, output its ID and its address. + for (unsigned int i = 0; i < monitors; i++) { + currentMonitor = g.mc.monmap.get_inst(i); + + input << currentMonitor.addr; + currentAddress = input.str(); + input.str(""); + input.flush(); + currentAddress = addr_to_str(currentMonitor.addr); + + current_row = *(guiMonitorMonitorClusterEntries->append()); + + current_row[monitorColumns.key] = str(boost::format("%d") % i); + current_row[monitorColumns.value] = currentAddress; + } +} + +std::string GuiMonitor::gen_osd_icon_caption(unsigned int begin, unsigned int end) +{ + boost::format rangeFormatter("OSDs %lu-%lu"); + boost::format singleFormatter("OSD %lu"); + + return (end - begin) ? str(rangeFormatter % begin % end) : + str(singleFormatter % begin); +} + +void GuiMonitor::view_osd_nodes(unsigned int begin, unsigned int end, bool + viewAll) +{ + unsigned int size = end - begin; + unsigned int *ranges = new unsigned int[MAX_VIEW_ICONS]; + //unsigned int maxViewIcons = getMaxViewIcons(); + + gen_ranges(size, MAX_VIEW_ICONS, ranges); + + // remove old icons + if (viewAll) { + while (!old_osd_cluster_zoom_states.empty()) + old_osd_cluster_zoom_states.pop(); + + osd_cluster_zoom = 0; + } + + guiMonitorOSDClusterIcons->clear(); + + unsigned int i = 0, j, range_offset = begin, begin_range, end_range; + int icon_status = CEPH_OSD_UP; + Gtk::TreeModel::Row row; + + string caption; + + // create and display icons + for (i = 0; i < MAX_VIEW_ICONS; i++) { + if (ranges[i]) { + icon_status = CEPH_OSD_UP; + row = *(guiMonitorOSDClusterIcons->append()); + + begin_range = range_offset; + end_range = ranges[i] - 1 + range_offset; + + caption = gen_osd_icon_caption(begin_range, end_range); + + for (j = begin_range; j <= end_range; j++) { + if (g.osdmap.is_out(j)) { + icon_status = CEPH_OSD_OUT; + break; + } + else if (g.osdmap.is_down(j)) + icon_status = ~CEPH_OSD_UP; + else + ; + } + + row[icon_columns.status] = icon_status; + + switch (icon_status) { + case CEPH_OSD_OUT: + row[icon_columns.icon] = outOSDIcon; + break; + case ~CEPH_OSD_UP: + row[icon_columns.icon] = downOSDIcon; + break; + default: + row[icon_columns.icon] = upOSDIcon; + break; + } + + row[icon_columns.caption] = caption; + row[icon_columns.begin_range] = begin_range; + row[icon_columns.end_range] = end_range; + + range_offset += ranges[i]; + } + } + + if (viewAll) { + guiMonitorOSDClusterBackButton->set_sensitive(false); + guiMonitorOSDClusterViewAllButton->set_sensitive(false); + } + + delete[] ranges; +} + +std::string GuiMonitor:: +gen_mds_icon_caption(unsigned int begin, unsigned int end) +{ + if (end == begin) { + ostringstream oss; + oss << "MDS " << current_up_mds.at(begin); + return oss.str(); + } + + ostringstream oss; + oss << "MDSes " << current_up_mds.at(begin) + << "-" << current_up_mds.at(end); + return oss.str(); +} + +void GuiMonitor::view_mds_nodes(unsigned int begin, unsigned int end, bool + viewAll) +{ + unsigned int size; + unsigned int *ranges = new unsigned int[MAX_VIEW_ICONS]; + + // If viewAll is set, then the names set will be filled with all of the names + // of the MDSes that are up. + if (viewAll) { + // Gather all of the names of the up MDSes + set up_mds; + g.mdsmap.get_up_mds_set(up_mds); + current_up_mds.clear(); + for (set::const_iterator mds_id = up_mds.begin(); + mds_id != up_mds.end(); + ++mds_id) + { + const MDSMap::mds_info_t& info = g.mdsmap.get_mds_info(*mds_id); + + current_up_mds.push_back(info.name); + } + size = current_up_mds.size(); + } + else + size = end - begin; + + // Remove old icons + if (viewAll) { + while (!old_mds_cluster_zoom_states.empty()) + old_mds_cluster_zoom_states.pop(); + + mds_cluster_zoom = 0; + } + + guiMonitorMDSClusterIcons->clear(); + + gen_ranges(size, MAX_VIEW_ICONS, ranges); + + // Create and display icons + Gtk::TreeModel::Row row; + string caption; + + int i = 0; + + for (unsigned int mdsIndex = viewAll ? 0 : begin; + i < MAX_VIEW_ICONS; + i++, mdsIndex += ranges[i]) + { + if (ranges[i]) { + row = *(guiMonitorMDSClusterIcons->append()); + + row[icon_columns.icon] = MDSIcon; + row[icon_columns.caption] = + gen_mds_icon_caption(mdsIndex, mdsIndex + ranges[i] - 1); + row[icon_columns.begin_range] = mdsIndex; + row[icon_columns.end_range] = mdsIndex + ranges[i] - 1; + } + } + + if (viewAll) { + guiMonitorMDSClusterBackButton->set_sensitive(false); + guiMonitorMDSClusterViewAllButton->set_sensitive(false); + } + + delete[] ranges; +} + +std::string GuiMonitor::gen_pg_icon_caption(unsigned int begin, unsigned int end) +{ + boost::format rangeFormatter("PGs %lu-%lu"); + boost::format singleFormatter("PG %lu"); + + return (end - begin) ? str(rangeFormatter % begin % end) : + str(singleFormatter % begin); +} + +void GuiMonitor:: +view_pg_nodes(unsigned int begin, unsigned int end, bool view_all) +{ + unsigned int size; + unsigned int *ranges = new unsigned int[MAX_VIEW_ICONS]; + + // remove old icons + if (view_all) { + while (!old_pg_cluster_zoom_states.empty()) + old_pg_cluster_zoom_states.pop(); + + size = g.pgmap.pg_stat.size(); + pg_cluster_zoom = 0; + + current_pgs.clear(); + std::copy(g.pgmap.pg_set.begin(), g.pgmap.pg_set.end(), + std::inserter(current_pgs, current_pgs.begin() ) ); + } + else + size = end - begin; + + guiMonitorPGClusterIcons->clear(); + + unsigned int i = 0, range_offset = begin, begin_range, end_range; + Gtk::TreeModel::Row currentRow; + + string caption; + + gen_ranges(size, MAX_VIEW_ICONS, ranges); + + // create and display icons + for (i = 0; i < MAX_VIEW_ICONS; i++) { + if (ranges[i]) { + currentRow = *(guiMonitorPGClusterIcons->append()); + + begin_range = range_offset; + end_range = ranges[i] - 1 + range_offset; + + caption = gen_pg_icon_caption(begin_range, end_range); + + currentRow[icon_columns.icon] = PGIcon; + currentRow[icon_columns.caption] = caption; + currentRow[icon_columns.begin_range] = begin_range; + currentRow[icon_columns.end_range] = end_range; + + range_offset += ranges[i]; + } + } + + if (view_all) { + guiMonitorPGClusterBackButton->set_sensitive(false); + guiMonitorPGClusterViewAllButton->set_sensitive(false); + } + + delete[] ranges; +} + +void GuiMonitor::check_status() +{ + // Check isCephUpdated to see if the placement groups, OSDs, metadata + // servers, and the monitors have been updated. If any of these have been + // updated, then update the GUI accordingly. + + assert(g.lock.is_locked()); + if (g.updates & OSD_MON_UPDATE) + update_osd_cluster_view(); + if (g.updates & MDS_MON_UPDATE) + update_mds_cluster_view(); + if (g.updates & PG_MON_UPDATE) + update_pg_cluster_view(); + if (g.updates & MON_MON_UPDATE) + update_mon_cluster_view(); + g.updates = 0; + + // See if the log has been updated. If it has, then + // update the log text box in the GUI and then clear the log stream. + string log(g.slog->str()); + if (!log.empty()) { + if (!guiMonitorCopyImageMenuItem->is_sensitive()) + guiMonitorCopyImageMenuItem->set_sensitive(true); + Gtk::TextIter end = guiMonitorLogTextBuffer->insert( + guiMonitorLogTextBuffer->end(), log); + end.backward_line(); + guiMonitorLogTextView->scroll_to( + guiMonitorLogTextBuffer->create_mark(end)); + g.slog->str(""); + g.slog->flush(); + } +} + +// Handler for quitting the GUI via the "x" button on the window. +bool GuiMonitor::quit_gui(GdkEventAny *event) +{ + if (thread) + thread->shutdown(); + guiMonitorWindow->hide(); + return true; +} + +// Handler for quitting the GUI via the menu option. +void GuiMonitor::gui_monitor_quit() +{ + if (thread) + thread->shutdown(); + guiMonitorWindow->hide(); +} + +// Called when the main window is closed. Ends the program. +bool GuiMonitor::quit_signal_handler() +{ + Gtk::Main::quit(); + return false; // never executed +} + +// Called when the "Copy" menu option in the Edit menu bar is clicked. Copies +// the contents of the log to the clipboard. +void GuiMonitor::copy_log() +{ + Glib::RefPtr clipboard = + Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); + + clipboard->set_text(guiMonitorLogTextBuffer->get_text()); + clipboard->store(); +} + +// Called when the "Send Command..." menu option in the Ceph menu is clicked. +// Displays the "Send Command..." dialog box. +void GuiMonitor::open_send_command() +{ + sendCommandPromptEntry->set_text(""); + int result = sendCommandDialog->run(); + + if (result == Gtk::RESPONSE_OK) { + if (send_command_success) { + send_command_success = false; + sendCommandDialog->hide(); + } + else + open_send_command(); // needs to continue running + } + else + sendCommandDialog->hide(); +} + +// Called when a button is pressed in the "Send Command..." dialog box. +void GuiMonitor::handle_send_command_response(int response) +{ + send_command_success = false; + if (response != Gtk::RESPONSE_OK) { + // todo: show error? + return; + } + string command_str(sendCommandPromptEntry->get_text()); + boost::trim(command_str); + + if (command_str.empty()) { + dialog_error("Please enter a Ceph command.", Gtk::MESSAGE_INFO); + return; + } + + run_command(command_str.c_str()); + + send_command_success = true; +} + +// Called when the "View Node..." menu option in the Ceph menu is clicked. +// Displays the "View Node..." dialog box. +void GuiMonitor::open_view_mode() +{ + viewNodeTypeComboBox->set_active(-1); + viewNodeNameEntry->set_text(""); + viewNodeNameLabel->set_label("Node ID:"); + + int result = viewNodeDialog->run(); + + if (result == Gtk::RESPONSE_OK) { + if (view_node_success) { + view_node_success = false; + viewNodeDialog->hide(); + } + else + open_view_mode(); // needs to continue running + } + else + viewNodeDialog->hide(); +} + +std::vector GuiMonitor::gen_node_info_from_icons + (Glib::RefPtr iconStore, enum NodeType type) +{ + vector clusterInfo; + Gtk::TreeModel::Children icons = iconStore->children(); + Gtk::TreeModel::iterator iter; + Gtk::TreeModel::Row currentRow; + NodeInfo *currentNodeInfo; + int status; + + for (iter = icons.begin(); iter != icons.end(); iter++) { + currentRow = *iter; + + currentNodeInfo = new NodeInfo(currentRow[icon_columns.begin_range], + currentRow[icon_columns.end_range], type, status); + + clusterInfo.push_back(currentNodeInfo); + } + + return clusterInfo; +} + +void GuiMonitor::gen_icons_from_node_info(vector& nodeInfo) +{ + Glib::RefPtr icons; + Gtk::TreeModel::Row row; + NodeInfo *node; + + switch (nodeInfo[0]->type) { + case OSD_NODE: + icons = guiMonitorOSDClusterIcons; + break; + case MDS_NODE: + icons = guiMonitorMDSClusterIcons; + break; + case PG_NODE: + icons = guiMonitorPGClusterIcons; + break; + default: + break; + } + + icons->clear(); + + for (unsigned int i = 0; i < nodeInfo.size(); i++) { + node = nodeInfo[i]; + row = *(icons->append()); + + row[icon_columns.begin_range] = node->begin_range; + row[icon_columns.end_range] = node->end_range; + + switch (node->type) { + case OSD_NODE: + row[icon_columns.caption] = + gen_osd_icon_caption(node->begin_range, node->end_range); + + switch (node->status) { + case CEPH_OSD_OUT: + row[icon_columns.icon] = outOSDIcon; + break; + case CEPH_OSD_UP: + row[icon_columns.icon] = upOSDIcon; + break; + default: // ~CEPH_OSD_UP + row[icon_columns.icon] = downOSDIcon; + break; + } + + break; + case MDS_NODE: + row[icon_columns.caption] = + gen_mds_icon_caption(node->begin_range, node->end_range); + row[icon_columns.icon] = MDSIcon; + + break; + case PG_NODE: + row[icon_columns.caption] = + gen_pg_icon_caption(node->begin_range, node->end_range); + row[icon_columns.icon] = PGIcon; + + break; + default: + break; + } + + delete node; + } + + nodeInfo.clear(); +} + +// Constructs a StatsWindowInfo object and opens a window containing the stats +// of a node or cluster. +void GuiMonitor::open_stats(enum NodeType type, bool is_cluster, int id) +{ + Glib::RefPtr builder_file = + Gtk::Builder::create_from_file(STATS_WINDOW_BUILDER_FILE); + + /* note that node_stats will be deleted once the stats window closes */ + StatsWindowInfo *node_stats = + new StatsWindowInfo(this, builder_file, is_cluster, type, id); + node_stats->init(); + node_stats->stats_window->show_all(); +} + +// Called when an icon in the OSD icon view is activated. +void GuiMonitor::osdc_cluster_zoom_in(const Gtk::TreeModel::Path& path) +{ + Gtk::TreeModel::iterator iter = guiMonitorOSDClusterIcons->get_iter(path); + + if (!iter) + return; + + Gtk::TreeModel::Row row = *iter; + + int begin_range = row[icon_columns.begin_range]; + int end_range = row[icon_columns.end_range]; + + if (end_range == begin_range) { + if (begin_range >= g.osdmap.get_max_osd()) { + dialog_error("OSD map has changed and the node no longer exists.", + Gtk::MESSAGE_ERROR); + + view_osd_nodes(0, g.osdmap.get_max_osd()); + } + else { + open_stats(OSD_NODE, false, begin_range); + } + } + else { + open_stats(OSD_NODE, false, begin_range); + // Zoom in, since this is a range. Place the old icons on the stack + // and then call view_osd_icons() on the narrower range. + vector oldOSDInfo = + gen_node_info_from_icons(guiMonitorOSDClusterIcons, OSD_NODE); + + //Glib::RefPtr oldOSDIcons(guiMonitorOSDClusterIcons); + + old_osd_cluster_zoom_states.push(oldOSDInfo); + osd_cluster_zoom++; + + guiMonitorOSDClusterBackButton->set_sensitive(true); + guiMonitorOSDClusterViewAllButton->set_sensitive(true); + + view_osd_nodes(begin_range, end_range + 1, false); + } +} + +// Called when the "Back" button is activated in the OSD cluster view area. +// Displays the previous level of OSDs. Note that no updates occur to these +// nodes, however. +void GuiMonitor::osdc_cluster_back() +{ + if (osd_cluster_zoom) { + vector oldOSDInfo = old_osd_cluster_zoom_states.top(); + old_osd_cluster_zoom_states.pop(); + + gen_icons_from_node_info(oldOSDInfo); + osd_cluster_zoom--; + + if (!osd_cluster_zoom) { + guiMonitorOSDClusterBackButton->set_sensitive(false); + guiMonitorOSDClusterViewAllButton->set_sensitive(false); + } + } +} + +// Called when the "View All" button is activated in the OSD cluster view area. +// Displays all OSDs, with recent updates. +void GuiMonitor::osdc_cluster_view_all() +{ + view_osd_nodes(0, g.osdmap.get_max_osd()); +} + +// Allows user to obtain the statistics of the OSD cluster. Called by +// guiMonitorOSDClusterStatsButton by signal_clicked. +void GuiMonitor::osdc_cluster_stats() +{ + open_stats(OSD_NODE, true, 0); +} + +// Called when an icon in the MDS icon view is activated. +void GuiMonitor::mds_cluster_zoom_in(const Gtk::TreeModel::Path& path) +{ + Gtk::TreeModel::iterator iter = + guiMonitorMDSClusterIcons->get_iter(path); + + if (!iter) + return; + Gtk::TreeModel::Row row = *iter; + + unsigned int begin_range = row[icon_columns.begin_range]; + unsigned int end_range = row[icon_columns.end_range]; + + if (end_range - begin_range) { + // Zoom in, since this is a range. Place the old icons on the stack + // and then call viewMDSIcons() on the narrower range. + vector old_mds_info = + gen_node_info_from_icons(guiMonitorMDSClusterIcons, MDS_NODE); + + old_mds_cluster_zoom_states.push(old_mds_info); + mds_cluster_zoom++; + + guiMonitorMDSClusterBackButton->set_sensitive(true); + guiMonitorMDSClusterViewAllButton->set_sensitive(true); + + view_mds_nodes(begin_range, end_range + 1, false); + } + else { + set up_mds; + g.mdsmap.get_up_mds_set(up_mds); + + if (begin_range >= up_mds.size()) { + dialog_error("Metadata server map has changed and the node no " + "longer exists.", Gtk::MESSAGE_ERROR); + + view_mds_nodes(0, 0, true); + } + else { + open_stats(MDS_NODE, false, (int)begin_range); + } + } +} + +// Called when the "Back" button is activated in the MDS cluster view area. +// Displays the previous level of MDSes. Note that no updates occur to these +// nodes, however. +void GuiMonitor::mds_cluster_back() +{ + if (!mds_cluster_zoom) + return; + vector old_mds_info = old_mds_cluster_zoom_states.top(); + old_mds_cluster_zoom_states.pop(); + + gen_icons_from_node_info(old_mds_info); + mds_cluster_zoom--; + + if (!mds_cluster_zoom) { + guiMonitorMDSClusterBackButton->set_sensitive(false); + guiMonitorMDSClusterViewAllButton->set_sensitive(false); + } +} + +// Called when the "View All" button is activated in the MDS cluster view area. +// Displays all MDSes, with recent updates. +void GuiMonitor::mds_cluster_view_all() +{ + view_mds_nodes(); +} + +// Allows user to obtain the statistics of the MDS cluster. Called by +// guiMonitorMDSClusterStatsButton by signal_clicked. +void GuiMonitor::mds_cluster_stats() +{ + open_stats(MDS_NODE, true, 0); +} + +// Called when an icon in the PG icon view is activated. +void GuiMonitor::pg_cluster_zoom_in(const Gtk::TreeModel::Path& path) +{ + Gtk::TreeModel::iterator iter(guiMonitorPGClusterIcons->get_iter(path)); + + if (!iter) + return; + Gtk::TreeModel::Row row = *iter; + + unsigned int begin_range = row[icon_columns.begin_range]; + unsigned int end_range = row[icon_columns.end_range]; + + if (end_range - begin_range) { + // Zoom in, since this is a range. Place the old icons on the stack + // and then call viewPGIcons() on the narrower range. + vector oldPGInfo = + gen_node_info_from_icons(guiMonitorPGClusterIcons, PG_NODE); + + old_pg_cluster_zoom_states.push(oldPGInfo); + pg_cluster_zoom++; + + guiMonitorPGClusterBackButton->set_sensitive(true); + guiMonitorPGClusterViewAllButton->set_sensitive(true); + + view_pg_nodes(begin_range, end_range + 1, false); + } + else { + if (begin_range >= g.pgmap.pg_stat.size()) { + dialog_error("Placement group map has changed and the node no " + "longer exists.", Gtk::MESSAGE_ERROR); + + view_pg_nodes(0, 0, true); + } + else + open_stats(PG_NODE, false, (int)begin_range); + } +} + +// Called when the "Back" button is activated in the PG cluster view area. +// Displays the previous level of PGs. Note that no updates occur to these +// nodes, however. +void GuiMonitor::pg_cluster_back() +{ + if (!pg_cluster_zoom) + return; + vector oldPGInfo = old_pg_cluster_zoom_states.top(); + old_pg_cluster_zoom_states.pop(); + + gen_icons_from_node_info(oldPGInfo); + pg_cluster_zoom--; + + if (!pg_cluster_zoom) { + guiMonitorPGClusterBackButton->set_sensitive(false); + guiMonitorPGClusterViewAllButton->set_sensitive(false); + } +} + +// Called when the "View All" button is activated in the PG cluster view area. +// Displays all PGs, with recent updates. +void GuiMonitor::pg_cluster_view_all() +{ + view_pg_nodes(0, 0, true); +} + +// Allows user to obtain the statistics of the PG cluster. Called by +// guiMonitorPCClusterStatsButton by signal_clicked. +void GuiMonitor::pg_cluster_stats() +{ + open_stats(PG_NODE, true, 0); +} + +// Allows user to obtain the statistics of the monitor cluster. Called by +// guiMonitorMonitorClusterStatsButton by signal_clicked. +void GuiMonitor::monitor_cluster_stats() +{ + open_stats(MON_NODE, true, 0); +} + +void GuiMonitor::dialog_error(const std::string &msg, Gtk::MessageType type) +{ + Gtk::MessageDialog errorMsg(msg, false, type, Gtk::BUTTONS_OK, true); + + errorMsg.run(); + errorMsg.hide(); +} + +// Finds a metadata server in the metadata server map that is up based on its +// name. Returns the ID of the metadata server or returns -1 if not found. +int GuiMonitor::find_mds_id(const std::string &name) +{ + set upMDSes; + g.mdsmap.get_up_mds_set(upMDSes); + + for (set::const_iterator MDSId = upMDSes.begin(); + MDSId != upMDSes.end(); MDSId++) + { + const MDSMap::mds_info_t& current_mds_info = + g.mdsmap.get_mds_info(*MDSId); + if (current_mds_info.name == name) + return *MDSId; + } + + return -1; +} + +// Called when a button is pressed in the "View Node..." dialog box. +void GuiMonitor::handle_view_node_response(int response) +{ + view_node_success = false; + if (response != Gtk::RESPONSE_OK) { + // todo: display error? + return; + } + Gtk::TreeModel::iterator selected_type_iter = + viewNodeTypeComboBox->get_active(); + + if (!selected_type_iter) { + dialog_error("Type of node needs to be selected.", + Gtk::MESSAGE_INFO); + return; + } + Gtk::TreeRow selected_row = *selected_type_iter; + enum NodeType selected_type = selected_row[viewNodeColumns.type]; + string id_entry = viewNodeNameEntry->get_text(); + boost::trim(id_entry); + + if (id_entry.empty()) { + dialog_error("Please enter the node ID.", Gtk::MESSAGE_INFO); + return; + } + + switch (selected_type) { + case OSD_NODE: { + long id; + + try { + id = boost::lexical_cast(id_entry); + } + catch (const boost::bad_lexical_cast &) { + dialog_error("Node ID must be a number.", Gtk::MESSAGE_ERROR); + return; + } + if (id < 0) { + dialog_error("ID must be a positive number.", Gtk::MESSAGE_ERROR); + return; + } + if (id >= g.osdmap.get_max_osd()) { + dialog_error("OSD does not exist.", Gtk::MESSAGE_ERROR); + return; + } + open_stats(selected_type, false, (int)id); + break; + } + + case MDS_NODE: { + int id = find_mds_id(id_entry); + if (id == -1) { + string error("Can't find MDS "); + error += id_entry; + dialog_error(error, Gtk::MESSAGE_ERROR); + return; + } + open_stats(selected_type, false, id); + break; + } + + case PG_NODE: { + unsigned int id; + + try { + id = boost::lexical_cast(id_entry); + } + catch (const boost::bad_lexical_cast &) { + dialog_error("Node ID must be a number.", Gtk::MESSAGE_ERROR); + return; + } + + if (id >= g.pgmap.pg_stat.size()) { + dialog_error("PG does not exist.", Gtk::MESSAGE_ERROR); + return; + } + open_stats(selected_type, false, (int)id); + break; + } + + default: + break; + } + view_node_success = true; +} + +// Called when a node type is selected in the combo box in the "View Node..." +// dialog box. Activated by signal_changed(). +void GuiMonitor::handle_view_node_change() +{ + if (viewNodeTypeComboBox->get_active_row_number() == -1) + return; + + Gtk::TreeModel::iterator selected_type_iter = + viewNodeTypeComboBox->get_active(); + + Gtk::TreeRow selected_row = *selected_type_iter; + enum NodeType selected_type = selected_row[viewNodeColumns.type]; + std::string request_type = + (selected_type == OSD_NODE || selected_type == PG_NODE || + selected_type == MON_NODE) ? "Node ID:" : "MDS Name:"; + + viewNodeNameLabel->set_label(request_type); +} + +// Called when the "About" menu option in the Help menu is clicked. Displays +// the "About" dialog box. +void GuiMonitor::open_about_dialog() +{ + guiMonitorAboutDialog->run(); + guiMonitorAboutDialog->hide(); +} + +/* + * GuiMonitor Subclass Methods + */ +GuiMonitor::StatsWindowInfo::StatsWindowInfo(GuiMonitor *gui_, + Glib::RefPtr builder, + bool is_cluster_, enum NodeType type_, int id_) + : gui(gui_), + is_cluster(is_cluster_), + type(type_), + id(id_) +{ + builder->get_widget("statsWindow", stats_window); + builder->get_widget("statsInfoLabel", stats_info_label); + builder->get_widget("statsInfoTreeView", stats_info_tree_view); + builder->get_widget("statsCopyButton", stats_copy_button); + builder->get_widget("statsCloseButton", stats_close_button); + + stats = Gtk::ListStore::create(columns); + + stats_info_tree_view->set_model(stats); + stats_info_tree_view->append_column("Name", columns.key); + stats_info_tree_view->append_column("Value", columns.value); + + stats_window->signal_delete_event().connect(sigc::mem_fun(this, + &GuiMonitor::StatsWindowInfo::closeWindow)); + + stats_close_button->signal_activate().connect(sigc::mem_fun(this, + &GuiMonitor::StatsWindowInfo::close)); + stats_close_button->signal_clicked().connect(sigc::mem_fun(this, + &GuiMonitor::StatsWindowInfo::close)); + + stats_copy_button->signal_activate().connect(sigc::mem_fun(this, + &GuiMonitor::StatsWindowInfo::copy)); + stats_copy_button->signal_clicked().connect(sigc::mem_fun(this, + &GuiMonitor::StatsWindowInfo::copy)); +} + +GuiMonitor::StatsWindowInfo::~StatsWindowInfo() +{ + delete stats_window; +} + +void GuiMonitor::StatsWindowInfo::init() +{ + if (is_cluster) { + if (type == OSD_NODE) + gen_osd_cluster_columns(); + else if (type == PG_NODE) + gen_pg_cluster_columns(); + else if (type == MDS_NODE) + gen_mds_cluster_columns(); + else + gen_monitor_cluster_columns(); + } + else { + if (type == OSD_NODE) + gen_osd_node_columns(); + else if (type == PG_NODE) + gen_pg_node_columns(); + else // type == MDS_NODE + gen_mds_node_columns(); + } +} + +void GuiMonitor::StatsWindowInfo::gen_osd_cluster_columns() +{ + string label("OSD Cluster Statistics"); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + { + ostringstream oss; + oss << g.osdmap.get_epoch(); + insert_stats("Epoch", oss.str()); + } + { + ostringstream oss; + oss << g.osdmap.get_max_osd(); + insert_stats("Maximum Amount of OSDs", oss.str()); + } + { + ostringstream oss; + oss << g.osdmap.get_num_up_osds(); + insert_stats("Amount of Up OSDs", oss.str()); + } + { + ostringstream oss; + oss << g.osdmap.get_num_in_osds(); + insert_stats("Amount of In OSDs", oss.str()); + } +} + +void GuiMonitor::StatsWindowInfo::gen_mds_cluster_columns() +{ + string label("MDS Cluster Statistics"); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + set up_mds; + set stopped_mds; + + g.mdsmap.get_up_mds_set(up_mds); + g.mdsmap.get_stopped_mds_set(stopped_mds); + + insert_stats("Epoch", str(boost::format("%llu") % g.mdsmap.get_epoch())); + insert_stats("Maximum Amount of MDSes", str(boost::format("%u") % + g.mdsmap.get_max_mds())); + insert_stats("Amount of Up MDSes", str(boost::format("%d") % up_mds.size())); + insert_stats("Amount of In MDSes", str(boost::format("%u") % + g.mdsmap.get_num_mds())); + insert_stats("Amount of Failed MDSes", str(boost::format("%d") % + g.mdsmap.get_num_failed())); + insert_stats("Amount of Stopped MDSes", str(boost::format("%d") % + stopped_mds.size())); +} + +void GuiMonitor::StatsWindowInfo::gen_pg_cluster_columns() +{ + string label("PG Cluster Statistics"); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + { + ostringstream oss; + oss << g.pgmap.version; + insert_stats("Version", oss.str()); + } + + { + ostringstream oss; + oss << g.pgmap.pg_stat.size(); + insert_stats("Amount of PGs", oss.str()); + } + + { + ostringstream oss; + oss << kb_t(g.pgmap.pg_sum.num_kb); + insert_stats("Data ", oss.str()); + } + + { + ostringstream oss; + oss << kb_t(g.pgmap.osd_sum.kb_used); + insert_stats("Amount of Storage Used", oss.str()); + } + + { + ostringstream oss; + oss << kb_t(g.pgmap.osd_sum.kb_avail); + insert_stats("Amount of Storage Available", oss.str()); + } + + { + ostringstream oss; + oss << kb_t(g.pgmap.osd_sum.kb); + insert_stats("Total Storage", oss.str()); + } +} + +void GuiMonitor::StatsWindowInfo::gen_monitor_cluster_columns() +{ + string label("Monitor Cluster Statistics"); + stats_window->set_title(label); + stats_info_label->set_label(label); + { + ostringstream oss; + oss << g.mc.monmap.size(); + insert_stats("Amount of Monitors", oss.str()); + } + { + ostringstream oss; + oss << g.mc.monmap.epoch; + insert_stats("Epoch", oss.str()); + } + { + ostringstream oss; + oss << g.mc.monmap.fsid; + insert_stats("File System ID", oss.str()); + } + { + ostringstream oss; + oss << g.mc.monmap.last_changed; + insert_stats("Last Changed", oss.str()); + } + { + ostringstream oss; + oss << g.mc.monmap.created; + insert_stats("Created", oss.str()); + } +} + +void GuiMonitor::StatsWindowInfo::gen_osd_node_columns() +{ + bool isIn = g.osdmap.is_in(id); + const entity_addr_t& addr = g.osdmap.get_addr(id); + const osd_info_t& osdInfo = g.osdmap.get_info(id); + + string label(str(boost::format("OSD %lu Statistics:") % id)); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + insert_stats("OSD ID", str(boost::format("%lu") % id)); + insert_stats("Address", isIn ? addr_to_str(addr) : "None"); + insert_stats("Up?", g.osdmap.is_up(id) ? "Up" : "Down"); + insert_stats("In?", isIn ? "In" : "Out"); + insert_stats("Weight", isIn ? str(boost::format("%f") % + g.osdmap.get_weightf(id)) : "None"); + insert_stats("Up From", str(boost::format("%lu") % osdInfo.up_from)); + insert_stats("Up Through", str(boost::format("%lu") % osdInfo.up_thru)); + insert_stats("Down At", str(boost::format("%lu") % osdInfo.down_at)); + insert_stats("Last Clean", str(boost::format("First: %lu, Last: %lu") % + osdInfo.last_clean_first % osdInfo.last_clean_last)); +} + +void GuiMonitor::StatsWindowInfo::gen_mds_node_columns() +{ + const MDSMap::mds_info_t &mdsInfo = g.mdsmap.get_mds_info(id); + + string label(str(boost::format("MDS %s Statistics:") % mdsInfo.name)); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + insert_stats("Name", mdsInfo.name); + insert_stats("Address", addr_to_str(mdsInfo.addr)); + insert_stats("Rank", str(boost::format("%lu") % mdsInfo.rank)); + insert_stats("Laggy?", mdsInfo.laggy() ? "Yes" : "No"); + insert_stats("State", ceph_mds_state_name(mdsInfo.state)); +} + +void GuiMonitor::StatsWindowInfo::gen_pg_node_columns() +{ + pg_t pg(gui->current_pgs.at(id)); + + hash_map::const_iterator s = g.pgmap.pg_stat.find(pg); + assert(s != g.pgmap.pg_stat.end()); + + const pg_stat_t &stat(s->second); + std::stringstream converter; + + string label(str(boost::format("PG %lu Statistics:") % id)); + + stats_window->set_title(label); + stats_info_label->set_label(label); + + insert_stats("Number of Objects", + str(boost::format("%llu") % stat.num_objects)); + insert_stats("Number of Objects Missing on Primary", + str(boost::format("%llu") % stat.num_objects_missing_on_primary)); + insert_stats("Number of Objects Degraded", + str(boost::format("%llu") % stat.num_objects_degraded)); + insert_stats("KB", str(boost::format("%llu") % stat.num_kb)); + insert_stats("Bytes", str(boost::format("%llu") % stat.num_bytes)); + insert_stats("Log Size", str(boost::format("%llu") % stat.log_size)); + insert_stats("On-Disk Log Size", str(boost::format("%llu") % + stat.ondisk_log_size)); + insert_stats("State", pg_state_string(stat.state)); + + ostringstream vss; + vss << stat.version; + insert_stats("Version", vss.str()); + + ostringstream rss; + rss << stat.reported; + insert_stats("Reported", rss.str()); + + converter << stat.up; + + insert_stats("Up", converter.str()); + + converter.str(""); + converter.flush(); + + converter << stat.acting; + + insert_stats("Acting", converter.str()); + + converter.str(""); + converter.flush(); + + converter << stat.last_scrub; + + insert_stats("Last Scrub", converter.str()); + + converter.str(""); + converter.flush(); + + converter << stat.last_scrub_stamp; + + insert_stats("Last Scrub Timestamp", converter.str()); + + converter.str(""); + converter.flush(); +} + +// Copies the information inside of the tree view object that contains the +// information about a node or cluster. Called by nodeStatsCopyButton by +// signal_clicked. +void GuiMonitor::StatsWindowInfo::copy() +{ + Glib::RefPtr clipboard = + Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); + + clipboard->set_text(stats_string()); + clipboard->store(); +} + +// Converts the statistics into a string +std::string GuiMonitor::StatsWindowInfo::stats_string() +{ + ostringstream oss; + Gtk::TreeModel::Children rows = stats->children(); + string delim; + + for (Gtk::TreeModel::iterator iter = rows.begin(); + iter != rows.end(); iter++) + { + oss << delim; + Gtk::TreeModel::Row currentRow(*iter); + Glib::ustring key(currentRow[columns.key]); + Glib::ustring value(currentRow[columns.value]); + oss << key << '\t' << value; + delim = "\n"; + } + + return oss.str(); +} + +void GuiMonitor::StatsWindowInfo::insert_stats(const std::string &key, + const std::string &value) +{ + Gtk::TreeModel::Row currentRow = *(stats->append()); + currentRow[columns.key] = key; + currentRow[columns.value] = value; +} + +int run_gui(int argc, char **argv) +{ + int ret = EXIT_SUCCESS; + + // Now that the monitor map is up and running, initialize the GUI and start + // sending observe requests. The observe requests will update the + // guiMonitorWindow (the main window of the Ceph monitor). Observe requests + // will continue to be sent until the application is closed or until the file + // system is no longer running. + Gtk::Main kit(&argc, &argv); + + // read the Gtk::Builder file that contains the layout of the windows. + Glib::RefPtr builder = + Gtk::Builder::create_from_file(GUI_MONITOR_BUILDER_FILE); + + // stores pointers to all GUI elements + GuiMonitor *gui = new GuiMonitor(builder); + + if (!gui->init()) { + cerr << "There was a problem with initializing the GUI." << std::endl; + ret = EXIT_FAILURE; + goto done; + } + + // The GUI will now enter its main run loop and will + // not exit until the GUI closes. + gui->run_main_loop(kit); + +done: + delete gui; + return ret; +} diff --git a/src/tools/gui.h b/src/tools/gui.h new file mode 100644 index 00000000000..8982efd3dfa --- /dev/null +++ b/src/tools/gui.h @@ -0,0 +1,394 @@ +/* -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- + * vim: ts=8 sw=2 smarttab + * + * gui_monitor_interface.h + * + * Michael McThrow + */ +#ifndef CEPH_GUI_H +#define CEPH_GUI_H + +#include "common/common_init.h" +#include "mds/MDSMap.h" +#include "mon/MonMap.h" +#include "mon/PGMap.h" +#include "msg/tcp.h" +#include "osd/OSDMap.h" + +#include +#include +#include +#include +#include + +enum NodeType { + OSD_NODE = 0, + PG_NODE, + MDS_NODE, + MON_NODE, + NUM_NODE_TYPES +}; + +class GuiMonitorThread; + +// Contains information about a range of nodes (or a specific node) +class NodeInfo { +public: + unsigned int begin_range; + unsigned int end_range; + enum NodeType type; + int status; + + NodeInfo(unsigned int begin_range_, unsigned int end_range_, + enum NodeType type_, int status_) + : begin_range(begin_range_), + end_range(end_range_), + type(type_), + status(status_) + { + } +}; + +// Variables, classes, and methods releated to handling the main window and +// dialog boxes are stored in the GuiMonitor class. +class GuiMonitor +{ +private: + // Used for displaying icons of the nodes in a cluster. + class NodeIconColumns : public Gtk::TreeModel::ColumnRecord { + public: + Gtk::TreeModelColumn > icon; + Gtk::TreeModelColumn caption; + Gtk::TreeModelColumn begin_range; + Gtk::TreeModelColumn end_range; + Gtk::TreeModelColumn status; + + NodeIconColumns() { + add(icon); + add(caption); + add(begin_range); + add(end_range); + add(status); + } + }; + + // Used for displaying information about either a cluter or a node. + class KeyValueModelColumns : public Gtk::TreeModel::ColumnRecord { + public: + Gtk::TreeModelColumn key; + Gtk::TreeModelColumn value; + + KeyValueModelColumns() { + add(key); + add(value); + } + }; + + // Used for displaying different types of nodes in the "View Node..." dialog + // box. + class ViewNodeOptionColumns : public Gtk::TreeModel::ColumnRecord { + public: + Gtk::TreeModelColumn type; + Gtk::TreeModelColumn name; + + ViewNodeOptionColumns() { + add(type); + add(name); + } + }; + + // Variables, classes, and methods related to the node statistics and cluster + // statistics windows. + class StatsWindowInfo { + public: + StatsWindowInfo(GuiMonitor *gui_, + Glib::RefPtr builder, + bool is_cluster_, enum NodeType type_, int id_); + + ~StatsWindowInfo(); + void init(); + + public: + /* + * GUI Elements + */ + GuiMonitor *gui; + Gtk::Window *stats_window; + Gtk::Label *stats_info_label; + Gtk::TreeView *stats_info_tree_view; + Gtk::Button *stats_copy_button; + Gtk::Button *stats_save_button; + Gtk::Button *stats_close_button; + + private: + KeyValueModelColumns columns; + Glib::RefPtr stats; + + bool is_cluster; + enum NodeType type; + int id; + + /* + * Private Functions + */ + private: + // Closes the window that contains the statistics of a node or cluster. + // Called by nodeStatsWindow by signal_delete_event. + bool closeWindow(GdkEventAny *event) { + delete this; + return true; + } + + void copy(); + + // Closes the window that contains the statistics of a node or cluster. + // Called by nodeStatsCloseButton by signal_clicked. + void close() { + delete this; + } + + void gen_osd_cluster_columns(); + void gen_mds_cluster_columns(); + void gen_pg_cluster_columns(); + void gen_monitor_cluster_columns(); + + void gen_osd_node_columns(); + void gen_mds_node_columns(); + void gen_pg_node_columns(); + + std::string stats_string(); + + void insert_stats(const std::string &key, const std::string &value); + }; + + friend class StatsWindowInfo; + +public: + GuiMonitor(Glib::RefPtr builder); + ~GuiMonitor(); + + bool init(); + void run_main_loop(Gtk::Main &kit); + + void check_status(); + +private: + /* + * Private Functions + */ + void get_widgets(Glib::RefPtr builder_object); + bool open_icon(Glib::RefPtr &icon, std::string path); + void connect_signals(); + void link_elements(); + void update_osd_cluster_view(); + void update_mds_cluster_view(); + void update_pg_cluster_view(); + void update_mon_cluster_view(); + + std::string gen_osd_icon_caption(unsigned int begin, unsigned int end); + std::string gen_mds_icon_caption(unsigned int begin, unsigned int end); + std::string gen_pg_icon_caption(unsigned int begin, unsigned int end); + + std::vector gen_node_info_from_icons + (Glib::RefPtr iconStore, enum NodeType type); + void gen_icons_from_node_info(vector& node_info); + + void view_osd_nodes(unsigned int begin, unsigned int end, + bool view_all = true); + void view_mds_nodes(unsigned int begin = 0, unsigned int end = 0, + bool view_all = true); + void view_pg_nodes(unsigned int begin, unsigned int end, + bool view_all); + int find_mds_id(const std::string &name); + void open_stats(enum NodeType type, bool is_cluster, int id); + void dialog_error(const std::string &msg, Gtk::MessageType type); + + /* + * Signal handlers + */ + // Exits the program. Called by Gtk::Main by signal_quit + bool quit_signal_handler(); + + // Quits the GUI. Called by guiMonitorWindow by signal_delete_event. + bool quit_gui(GdkEventAny *event); + + // Quits the GUI. Called by guiMonitorQuitImageMenuItem by signal_activate. + void gui_monitor_quit(); + + // Copies the text in guiMonitorLogTextView onto the clipboard. Called by + // guiMonitorCopyImageMenuItem by signal_activate. + void copy_log(); + + // Opens the "Send Command...." window. Called by + // guiMonitorSendCommandMenuItem for signal_activate. + void open_send_command(); + + // Opens the "View Node..." window. Called by guiMonitorViewNodeMenuItem by + // signal_activate. + void open_view_mode(); + + // Opens the "About" dialog box. Called by guiMonitorAboutImageMenuItem by + // signal_activate. + void open_about_dialog(); + + // Performs "zoom in" option for the PG cluster icon view area. Allows the + // user to "zoom in" on a narrower range of nodes until he or she selects a + // specific node of interest. Called by guiMonitorPGClusterIconView by + // signal_item_activated. + void pg_cluster_zoom_in(const Gtk::TreeModel::Path& path); + + // Allows user to "zoom out" one level of the PG cluster view area to obtain + // a broader range of nodes until he or she views the overall amount of + // nodes. Called by guiMonitorPGClusterBackButton by signal_clicked + void pg_cluster_back(); + + // Allows user to view the broadest range of nodes in the PG cluster. Called + // by guiMonitorPGClusterViewAllButton by signal_clicked. + void pg_cluster_view_all(); + + // Allows user to obtain the statistics of the PG cluster. Called by + // guiMonitorPCClusterStatsButton by signal_clicked. + void pg_cluster_stats(); + + // Allows user to obtain the statistics of the monitor cluster. Called by + // guiMonitorMonitorClusterStatsButton by signal_clicked. + void monitor_cluster_stats(); + + // Performs "zoom in" option for the OSD cluster icon view area. Allows the + // user to "zoom in" on a narrower range of nodes until he or she selects a + // specific node of interest. Called by guiMonitorOSDClusterIconView by + // signal_item_activated. + void osdc_cluster_zoom_in(const Gtk::TreeModel::Path& path); + + // Allows user to "zoom out" one level of the OSD cluster view area to obtain + // a broader range of nodes until he or she views the overall amount of + // nodes. Called by guiMonitorOSDClusterBackButton by signal_clicked + void osdc_cluster_back(); + + // Allows user to view the broadest range of nodes in the PG cluster. Called + // by guiMonitorOSDClusterViewAllButton by signal_clicked. + void osdc_cluster_view_all(); + + // Allows user to obtain the statistics of the OSD cluster. Called by + // guiMonitorOSDClusterStatsButton by signal_clicked. + void osdc_cluster_stats(); + + // Performs "zoom in" option for the MDS cluster icon view area. Allows the + // user to "zoom in" on a narrower range of nodes until he or she selects a + // specific node of interest. Called by guiMonitorMDSClusterIconView by + // signal_item_activated. + void mds_cluster_zoom_in(const Gtk::TreeModel::Path& path); + + // Allows user to "zoom out" one level of the MDS cluster view area to obtain + // a broader range of nodes until he or she views the overall amount of + // nodes. Called by guiMonitorMDSClusterBackButton by signal_clicked. + void mds_cluster_back(); + + // Allows user to view the broadest range of nodes in the MDS cluster. + // Called by guiMonitorMDSClusterViewAllButton by signal_clicked. + void mds_cluster_view_all(); + + // Allows user to obtain the statistics of the MDS cluster. Called by + // guiMonitorMDSClusterStatsButton by signal_clicked. + void mds_cluster_stats(); + + // Called when a button is pressed in the "View Node..." dialog box. + void handle_view_node_response(int response); + + // Called when a node type is selected in the "View Node..." dialog box. + void handle_view_node_change(); + + // Called when a button is pressed in the "Send Command..." dialog box. + void handle_send_command_response(int response); + + unsigned int pg_cluster_zoom; // current zoom level of the PG cluster view + unsigned int osd_cluster_zoom; // current zoom level of the OSD cluster view + unsigned int mds_cluster_zoom; // current zoom level of the MDS cluster view + + stack > old_pg_cluster_zoom_states; + stack > old_osd_cluster_zoom_states; + stack > old_mds_cluster_zoom_states; + + std::vector current_up_mds; + std::vector current_pgs; + + bool send_command_success; // did "Send Command..." end without errors? + bool view_node_success; // did "View Node..." end without errors?" + + GuiMonitorThread *thread; + + ///// GUI Elements ///// + // Main Window + Gtk::Window *guiMonitorWindow; + + Gtk::ImageMenuItem *guiMonitorQuitImageMenuItem; + Gtk::ImageMenuItem *guiMonitorCopyImageMenuItem; + Gtk::MenuItem *guiMonitorSendCommandMenuItem; + Gtk::MenuItem *guiMonitorViewNodeMenuItem; + Gtk::ImageMenuItem *guiMonitorAboutImageMenuItem; + Gtk::TextView *guiMonitorLogTextView; + Glib::RefPtr guiMonitorLogTextBuffer; + Gtk::Statusbar *guiMonitorStatusbar; + + // Main Window -- Placement Groups Section + Gtk::Label *guiMonitorPGClusterStatsLabel; + Gtk::IconView *guiMonitorPGClusterIconView; + Glib::RefPtr guiMonitorPGClusterIcons; + Gtk::Button *guiMonitorPGClusterBackButton; + Gtk::Button *guiMonitorPGClusterViewAllButton; + Gtk::Button *guiMonitorPGClusterStatsButton; + + // Main Window -- Monitor Cluster Section + Gtk::Label *guiMonitorMonitorClusterStatsLabel; + Gtk::Button *guiMonitorMonitorClusterStatsButton; + Gtk::TreeView *guiMonitorMonitorClusterTreeView; + Glib::RefPtr guiMonitorMonitorClusterEntries; + KeyValueModelColumns monitorColumns; + + // Main Window -- OSD Cluster Section + Gtk::Label *guiMonitorOSDClusterStatsLabel; + Gtk::IconView *guiMonitorOSDClusterIconView; + Glib::RefPtr guiMonitorOSDClusterIcons; + Gtk::Button *guiMonitorOSDClusterBackButton; + Gtk::Button *guiMonitorOSDClusterViewAllButton; + Gtk::Button *guiMonitorOSDClusterStatsButton; + + // Main Window -- Metadata Server Section + Gtk::Label *guiMonitorMDSClusterStatsLabel; + Gtk::IconView *guiMonitorMDSClusterIconView; + Glib::RefPtr guiMonitorMDSClusterIcons; + Gtk::Button *guiMonitorMDSClusterBackButton; + Gtk::Button *guiMonitorMDSClusterViewAllButton; + Gtk::Button *guiMonitorMDSClusterStatsButton; + + // View Node Dialog + Gtk::Dialog *viewNodeDialog; + Gtk::Entry *viewNodeNameEntry; + Gtk::ComboBox *viewNodeTypeComboBox; + Glib::RefPtr viewNodeNodeTypeListStore; + Gtk::Label *viewNodeNameLabel; + ViewNodeOptionColumns viewNodeColumns; + + // Send Command Window + Gtk::Dialog *sendCommandDialog; + Gtk::Entry *sendCommandPromptEntry; + + // About Dialog + Gtk::AboutDialog *guiMonitorAboutDialog; + + // Icons + Glib::RefPtr blacklistIcon; + Glib::RefPtr clientIcon; + //Glib::RefPtr failedMDSIcon; + Glib::RefPtr MDSIcon; + Glib::RefPtr monitorIcon; + Glib::RefPtr upOSDIcon; + Glib::RefPtr downOSDIcon; + Glib::RefPtr outOSDIcon; + Glib::RefPtr PGIcon; + //Glib::RefPtr stoppedMDSIcon; + + NodeIconColumns icon_columns; +}; + +#endif diff --git a/src/tools/gui_resources.h b/src/tools/gui_resources.h new file mode 100644 index 00000000000..71b4edf0817 --- /dev/null +++ b/src/tools/gui_resources.h @@ -0,0 +1,19 @@ +#ifndef GUI_RESOURCES_H +#define GUI_RESOURCES_H + +/* TODO: Place these in a resource file or some other mechanism */ +#define GUI_MONITOR_BUILDER_FILE "gui_resources/gui_monitor.build" +#define STATS_WINDOW_BUILDER_FILE "gui_resources/stats_window.glade" + +#define BLACKLIST_ICON_PATH "gui_resources/blacklist.svg" +#define CLIENT_ICON_PATH "gui_resources/client.svg" +#define FAILED_MDS_ICON_PATH "gui_resources/failed_mds.svg" +#define MDS_ICON_PATH "gui_resources/mds.svg" +#define MONITOR_ICON_PATH "gui_resources/monitor.svg" +#define UP_OSD_ICON_PATH "gui_resources/osd.svg" +#define DOWN_OSD_ICON_PATH "gui_resources/down_osd.svg" +#define OUT_OSD_ICON_PATH "gui_resources/out_osd.svg" +#define PG_ICON_PATH "gui_resources/pg.svg" +#define STOPPED_MDS_ICON_PATH "gui_resources/stopped_mds.svg" + +#endif diff --git a/src/tools/gui_resources/blacklist.svg b/src/tools/gui_resources/blacklist.svg new file mode 100644 index 00000000000..a4f84ecbecd --- /dev/null +++ b/src/tools/gui_resources/blacklist.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/client.svg b/src/tools/gui_resources/client.svg new file mode 100644 index 00000000000..36a44455bd3 --- /dev/null +++ b/src/tools/gui_resources/client.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/tools/gui_resources/cluster_stats_window.glade b/src/tools/gui_resources/cluster_stats_window.glade new file mode 100644 index 00000000000..88cea3becc1 --- /dev/null +++ b/src/tools/gui_resources/cluster_stats_window.glade @@ -0,0 +1,102 @@ + + + + + + 5 + + + True + vertical + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + diff --git a/src/tools/gui_resources/down_osd.svg b/src/tools/gui_resources/down_osd.svg new file mode 100644 index 00000000000..53184a54f1d --- /dev/null +++ b/src/tools/gui_resources/down_osd.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/failed_mds.svg b/src/tools/gui_resources/failed_mds.svg new file mode 100644 index 00000000000..e0f0a86454f --- /dev/null +++ b/src/tools/gui_resources/failed_mds.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/gui_monitor.build b/src/tools/gui_resources/gui_monitor.build new file mode 100644 index 00000000000..c22664a5e97 --- /dev/null +++ b/src/tools/gui_resources/gui_monitor.build @@ -0,0 +1,804 @@ + + + + + + 5 + Ceph Monitor + + + True + 10 + + + True + + + True + _File + True + + + True + + + gtk-quit + True + True + True + + + + + + + + + True + _Edit + True + + + True + + + gtk-copy + True + True + True + + + + + + + + + True + _Ceph + True + + + True + + + True + _Send Command... + True + + + + + True + _View Node... + True + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + 2 + 2 + + + True + 5 + + + True + 0 + 0 + Placement Groups: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Placement Group Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + Monitors: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + True + + + + + 2 + + + + + Monitor Cluster Stats... + True + True + True + + + False + 3 + + + + + 1 + 2 + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + OSDs: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + OSD Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + + + True + 5 + + + True + 0 + 0 + Metadata Servers: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Metadata Server Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + 1 + + + + + True + 5 + + + True + 0 + 0 + Log: + fill + word-char + + + False + 0 + + + + + 70 + True + True + automatic + automatic + + + True + True + False + word + + + + + False + 1 + + + + + False + 2 + + + + + True + + + False + 3 + + + + + + + 5 + About Ceph Monitor + False + center-on-parent + True + monitor + dialog + False + Ceph Monitor + 0.1 + (c) 2009 Michael McThrow +All rights reserved + Monitoring tool for the Ceph distributed file system + http://ceph.newdream.net + Ceph website + Author: Michael McThrow +Based on code written by Sage Weil and other contributors. + + + True + 2 + + + + + + True + end + + + False + end + 0 + + + + + + + 5 + View Node... + True + center + True + dialog + False + + + True + vertical + 5 + + + True + vertical + 5 + + + True + 0 + 0 + Which node would you like to view? + + + 0 + + + + + True + True + + + True + 0 + 0 + _Type of Node + True + viewNodeTypeComboBox + + + 0 + + + + + True + True + True + + + + 1 + + + + + 1 + + + + + 1 + + + + + True + True + + + True + 0 + 0 + _Node ID + True + viewNodeNameEntry + + + 0 + + + + + True + True + + False + + + 1 + + + + + 2 + + + + + 5 + 1 + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + viewNodeCancelButton + viewNodeOkButton + + + + 5 + Send Command... + True + 400 + True + dialog + False + + + True + vertical + 2 + + + True + vertical + + + True + 0 + 0 + _Please enter a Ceph command: + True + sendCommandPromptEntry + + + 0 + + + + + True + True + True + + + + 1 + + + + + 5 + 1 + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + _Run + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + sendCommandCancelButton + sendCommandRunButton + + + diff --git a/src/tools/gui_resources/gui_monitor.glade b/src/tools/gui_resources/gui_monitor.glade new file mode 100644 index 00000000000..dae0e8d9346 --- /dev/null +++ b/src/tools/gui_resources/gui_monitor.glade @@ -0,0 +1,970 @@ + + + + + + + + + + + + OSD + + + Metadata server + + + Placement group + + + Monitor + + + + + 5 + Ceph Monitor + + + True + 10 + + + True + + + True + _File + True + + + True + + + gtk-quit + True + True + True + + + + + + + + + True + _Edit + True + + + True + + + gtk-copy + True + True + True + + + + + + + + + True + _Ceph + True + + + True + + + True + _Send Command... + True + + + + + True + _View Node... + True + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + 2 + 2 + + + True + 5 + + + True + 0 + 0 + Placement Groups: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Placement Group Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + Monitors: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + Monitor Cluster Stats... + True + True + True + + + False + 3 + + + + + 1 + 2 + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + OSDs: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + OSD Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + + + True + 5 + + + True + 0 + 0 + Metadata Servers: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Metadata Server Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + 1 + + + + + True + 5 + + + True + 0 + 0 + Log: + fill + word-char + + + False + 0 + + + + + 70 + True + True + automatic + automatic + + + True + True + False + + + + + False + 1 + + + + + False + 2 + + + + + True + + + False + 3 + + + + + + + 5 + About Ceph Monitor + False + center-on-parent + True + monitor + dialog + False + Ceph Monitor + 0.1 + (c) 2009 Michael McThrow +All rights reserved + Monitoring tool for the Ceph distributed file system + http://ceph.newdream.net + Ceph website + Main Author: Michael McThrow +Portions of the code for the Ceph Monitor were written by Sage Weil and other contributors of the Ceph distributed file system. + + + True + 2 + + + + + + True + end + + + False + end + 0 + + + + + + + 5 + + + True + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + + + True + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + popup + Save File + True + center-on-parent + True + dialog + False + save + + + True + 2 + + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + gtk-save + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + saveCancelButton + saveSaveButton + + + + 5 + True + normal + False + + + True + vertical + 5 + + + True + vertical + 5 + + + True + 0 + 0 + Which node would you like to view? + + + 0 + + + + + True + True + + + True + 0 + 0 + Type of Node + + + 0 + + + + + True + viewNodeNodeTypeListStore + + + 1 + + + + + 1 + + + + + True + True + + + True + 0 + 0 + Node Name + + + 0 + + + + + True + True + + False + + + 1 + + + + + 2 + + + + + 5 + 1 + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + viewNodeCancelButton + viewNodeOkButton + + + diff --git a/src/tools/gui_resources/gui_monitor_old.glade b/src/tools/gui_resources/gui_monitor_old.glade new file mode 100644 index 00000000000..6e868a092b1 --- /dev/null +++ b/src/tools/gui_resources/gui_monitor_old.glade @@ -0,0 +1,1027 @@ + + + + + + 5 + Ceph Monitor + + + True + 10 + + + True + + + True + _File + True + + + True + + + gtk-quit + True + True + True + + + + + + + + + True + _Edit + True + + + True + + + gtk-copy + True + True + True + + + + + + + + + True + _Ceph + True + + + True + + + True + _Send Command... + True + + + + + True + _View Node... + True + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + 2 + 2 + + + True + 5 + + + True + 0 + 0 + Placement Groups: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Placement Group Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + Monitors: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + Monitor Cluster Stats... + True + True + True + + + False + 3 + + + + + 1 + 2 + 1 + 2 + + + + + True + 5 + + + True + 0 + 0 + OSDs: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + OSD Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + + + True + 5 + + + True + 0 + 0 + Metadata Servers: + + + False + 0 + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + automatic + automatic + + + True + True + + + + + 2 + + + + + True + + + Back + True + True + True + + + False + 0 + + + + + View All + True + True + True + + + False + 1 + + + + + Metadata Server Cluster Stats... + True + True + True + + + False + 2 + + + + + False + 3 + + + + + 1 + 2 + + + + + 1 + + + + + True + 5 + + + True + 0 + 0 + Log: + fill + word-char + + + False + 0 + + + + + 70 + True + True + automatic + automatic + + + True + True + False + + + + + False + 1 + + + + + False + 2 + + + + + True + + + False + 3 + + + + + + + 5 + About Ceph Monitor + False + center-on-parent + True + monitor + dialog + False + Ceph Monitor + 0.1 + (c) 2009 Michael McThrow +All rights reserved + Monitoring tool for the Ceph distributed file system + http://ceph.newdream.net + Ceph website + Main Author: Michael McThrow +Portions of the code for the Ceph Monitor were written by Sage Weil and other contributors of the Ceph distributed file system. + + + True + 2 + + + + + + True + end + + + False + end + 0 + + + + + + + 5 + popup + True + True + + + True + 5 + + + True + 0 + 0 + Which node would you like to view? + + + False + 0 + + + + + True + 2 + 2 + 160 + + + True + Metadata Server +Monitor +OSD +Placement Group + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + 0 + Type of Node + + + GTK_FILL + GTK_FILL + + + + + True + 0 + 0 + Name or Number + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + True + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + False + 1 + + + + + True + + + + + + True + + + gtk-cancel + True + True + True + True + + + False + 0 + + + + + gtk-ok + True + True + True + True + + + False + 1 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + popup + True + True + + + True + + + True + 0 + 0 + Ceph command to run: + + + False + 0 + + + + + True + True + + + False + 1 + + + + + True + + + + + + True + + + gtk-cancel + True + True + True + True + + + False + 0 + + + + + Run + True + True + True + + + False + 1 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + + + True + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + + + True + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + popup + Save File + True + center-on-parent + True + dialog + False + save + + + True + 2 + + + + + + True + end + + + gtk-cancel + -6 + True + True + True + True + + + False + False + 0 + + + + + gtk-save + -5 + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + diff --git a/src/tools/gui_resources/main-window.glade b/src/tools/gui_resources/main-window.glade new file mode 100644 index 00000000000..b64b165f146 --- /dev/null +++ b/src/tools/gui_resources/main-window.glade @@ -0,0 +1,591 @@ + + + + + + Ceph Monitor + + + True + 10 + + + True + + + True + _File + True + + + True + + + True + gtk-quit + True + True + + + + + + + + + True + _Edit + True + + + True + + + True + gtk-copy + True + True + + + + + + + + + True + _Ceph + True + + + True + + + True + _Send Command... + True + + + + + True + _View Node... + True + + + + + + + + + True + _Help + True + + + True + + + True + gtk-about + True + True + + + + + + + + + False + + + + + True + 3 + 2 + 20 + 20 + + + True + + + True + 0 + 0 + Monitors: + + + False + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + + + + + True + + + True + True + True + Back + 0 + + + False + + + + + True + True + True + View All + 0 + + + False + 1 + + + + + 3 + + + + + 2 + 3 + + + + + True + + + True + 0 + 0 + Placement Groups: + + + False + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + + + + + True + + + True + True + True + Back + 0 + + + False + + + + + True + True + True + View All + 0 + + + False + 1 + + + + + 3 + + + + + 1 + 2 + 1 + 2 + + + + + True + + + True + 0 + 0 + Clients: + + + False + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + + + + + True + + + True + True + True + Back + 0 + + + False + + + + + True + True + True + View All + 0 + + + False + 1 + + + + + 3 + + + + + 1 + 2 + + + + + True + + + True + 0 + 0 + Metadata Servers: + + + False + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + + + + + True + + + True + True + True + Back + 0 + + + False + + + + + True + True + True + View All + 0 + + + False + 1 + + + + + 3 + + + + + 1 + 2 + + + + + True + + + True + 0 + 0 + OSDs: + + + False + + + + + True + 0 + 0 + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + + + + + True + + + True + True + True + Back + 0 + + + False + + + + + True + True + True + View All + 0 + + + False + 1 + + + + + 3 + + + + + + + True + + + True + 0 + 0 + Summary: + + + False + + + + + + + + 1 + 2 + 2 + 3 + + + + + 1 + + + + + True + + + True + 0 + 0 + Log: + GTK_JUSTIFY_FILL + PANGO_WRAP_WORD_CHAR + + + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 1 + + + + + 2 + + + + + + + + + + + diff --git a/src/tools/gui_resources/mds.svg b/src/tools/gui_resources/mds.svg new file mode 100644 index 00000000000..e00211ff502 --- /dev/null +++ b/src/tools/gui_resources/mds.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/monitor.svg b/src/tools/gui_resources/monitor.svg new file mode 100644 index 00000000000..55fd2eaf536 --- /dev/null +++ b/src/tools/gui_resources/monitor.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/node_stats_window.glade b/src/tools/gui_resources/node_stats_window.glade new file mode 100644 index 00000000000..9cedae967e1 --- /dev/null +++ b/src/tools/gui_resources/node_stats_window.glade @@ -0,0 +1,102 @@ + + + + + + 5 + + + True + vertical + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-save-as + True + True + True + True + + + False + 1 + + + + + gtk-close + True + True + True + True + + + False + 2 + + + + + False + 1 + + + + + False + 2 + + + + + + diff --git a/src/tools/gui_resources/osd.svg b/src/tools/gui_resources/osd.svg new file mode 100644 index 00000000000..225661abe10 --- /dev/null +++ b/src/tools/gui_resources/osd.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/out_osd.svg b/src/tools/gui_resources/out_osd.svg new file mode 100644 index 00000000000..586b7d4c354 --- /dev/null +++ b/src/tools/gui_resources/out_osd.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/pg.svg b/src/tools/gui_resources/pg.svg new file mode 100644 index 00000000000..3a694923346 --- /dev/null +++ b/src/tools/gui_resources/pg.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/tools/gui_resources/stats_window.glade b/src/tools/gui_resources/stats_window.glade new file mode 100644 index 00000000000..9ce10cfb72c --- /dev/null +++ b/src/tools/gui_resources/stats_window.glade @@ -0,0 +1,159 @@ + + + + + + 5 + 350 + 500 + True + dialog + + + True + vertical + + + True + 0 + 0 + + + False + 0 + + + + + True + True + automatic + automatic + + + True + True + False + True + + + + + 1 + + + + + True + + + + + + True + + + gtk-copy + True + True + True + True + + + False + 0 + + + + + gtk-close + True + True + True + True + + + False + 1 + + + + + False + 1 + + + + + False + 2 + + + + + + + 5 + popup + Save File + True + center-on-parent + True + dialog + False + save + + + True + vertical + 2 + + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + gtk-save + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + saveCancelButton + saveSaveButton + + + diff --git a/src/tools/gui_resources/stopped_mds.svg b/src/tools/gui_resources/stopped_mds.svg new file mode 100644 index 00000000000..67ed576020a --- /dev/null +++ b/src/tools/gui_resources/stopped_mds.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + +