fbnodesync.flush()
node_round = fbnodesync.round
- if node_round < global_round:
+ if node_round < global_round or config.force:
# recreate node stats when refreshed
#print "%s" % nodename
req = threadpool.WorkRequest(collectPingAndSSH, [pcuname, cohash], {},
dbname="findbadpcus",
cachenodes=False,
refresh=False,
+ force=False,
)
parser.add_option("-f", "--nodelist", dest="nodelist", metavar="FILE",
help="Provide the input file for the node list")
help="Refresh the cached values")
parser.add_option("-i", "--increment", action="store_true", dest="increment",
help="Increment round number to force refresh or retry")
+ parser.add_option("", "--force", action="store_true", dest="force",
+ help="Force probe without incrementing global 'round'.")
parser = parsermodule.getParser(['defaults'], parser)
config = parsermodule.parse_args(parser)
try:
plcdb_hn2lb = None
plcdb_lb2hn = None
plcdb_netid2ip = None
+plcdb_id2lb = None
def init():
global l_sites
global plcdb_hn2lb
global plcdb_lb2hn
global plcdb_netid2ip
+ global plcdb_id2lb
api = plc.getCachedAuthAPI()
l_sites = api.GetSites({'peer_id':None},
plcdb_hn2lb = hn2lb
plcdb_lb2hn = lb2hn
plcdb_netid2ip = netid2ip
+ plcdb_id2lb = id2lb
return l_nodes
HTTP = 3
IPAL = 4
- TELNET_TIMEOUT = 60
+ TELNET_TIMEOUT = 120
def __init__(self, type, verbose):
self.type = type
if self.transport != None:
output = self.transport.read_until(expected, self.TELNET_TIMEOUT)
if output.find(expected) == -1:
+ print "OUTPUT: --%s--" % output
raise ErrorClass, "'%s' not found" % expected
else:
self.transport.write(buffer + "\r\n")
import traceback
traceback.print_exc()
return "EOF connection reset" + str(err)
-
+
+class IPMI(PCUControl):
+
+ supported_ports = [80,443,623]
+
+ # TODO: get exit codes to determine success or failure...
+ def run(self, node_port, dryrun):
+
+ if not dryrun:
+ cmd = "ipmitool -I lanplus -H %s -U %s -P '%s' power cycle"
+ p = os.popen(cmd % ( self.host, self.username, self.password) )
+ result = p.read()
+ print "RESULT: ", result
+ else:
+ cmd = "ipmitool -I lanplus -H %s -U %s -P '%s' user list"
+ p = os.popen(cmd % ( self.host, self.username, self.password) )
+ result = p.read()
+ print "RESULT: ", result
+
+ if "Error" in result:
+ return result
+ else:
+ return 0
+
class IPAL(PCUControl):
"""
This now uses a proprietary format for communicating with the PCU. I
self.close()
return 0
+class ManualPCU(PCUControl):
+ supported_ports = [22,23,80,443,9100,16992]
+
+ def run(self, node_port, dryrun):
+ if not dryrun:
+ # TODO: send email message to monitor admin requesting manual
+ # intervention. This should always be an option for ridiculous,
+ # custom jobs.
+ return 0
### rebooting european BlackBox PSE boxes
# Thierry Parmentelat - May 11 2005
# curl --http1.0 --basic --user <username>:<password> --data P<port>=r \
# http://<hostname>:<http_port>/cmd.html && echo OK
+# log in:
+
+## BB PSMaverick
+class BlackBoxPSMaverick(PCUControl):
+ supported_ports = [80]
+
+ def run(self, node_port, dryrun):
+ if not dryrun:
+ # send reboot signal.
+ cmd = "curl -s --data 'P%s=r' --anyauth --user '%s:%s' http://%s/config/home_f.html" % ( node_port, self.username, self.password, self.host)
+ else:
+ # else, just try to log in
+ cmd = "curl -s --anyauth --user '%s:%s' http://%s/config/home_f.html" % ( self.username, self.password, self.host)
+
+ p = os.popen(cmd)
+ result = p.read()
+ print "RESULT: ", result
+
+ if len(result.split()) > 3:
+ return 0
+ else:
+ return result
+
def bbpse_reboot (pcu_ip,username,password,port_in_pcu,http_port, dryrun):
global verbose
return WTIIPS4
elif "ePowerSwitch" in modelname:
return ePowerSwitch
+ elif "ipmi" in modelname:
+ return IPMI
+ elif "bbsemaverick" in modelname:
+ return BlackBoxPSMaverick
else:
return Unknown
apc = APCBrazil(values, verbose, ['22', '23'])
rb_ret = apc.reboot(values[nodename], dryrun)
- elif values['pcu_id'] in [1221,1225,1220]:
+ elif values['pcu_id'] in [1221,1225,1220,1192]:
apc = APCBerlin(values, verbose, ['22', '23'])
rb_ret = apc.reboot(values[nodename], dryrun)
- elif values['pcu_id'] in [1173,1240,47]:
+ elif values['pcu_id'] in [1173,1240,47,1363,1405,1401,1372,1371]:
apc = APCFolsom(values, verbose, ['22', '23'])
rb_ret = apc.reboot(values[nodename], dryrun)
amt = IntelAMT(values, verbose, ['16992'])
rb_ret = amt.reboot(values[nodename], dryrun)
- # BlackBox PSExxx-xx (e.g. PSE505-FR)
+ elif continue_probe and values['model'].find("bbsemaverick") >=0:
+ print "TRYING BlackBoxPSMaverick"
+ bbe = BlackBoxPSMaverick(values, verbose, ['80'])
+ rb_ret = bbe.reboot(values[nodename], dryrun)
+
+ elif continue_probe and values['model'].find("ipmi") >=0:
+
+ print "TRYING IPMI"
+ ipmi = IPMI(values, verbose, ['80', '443', '623'])
+ rb_ret = ipmi.reboot(values[nodename], dryrun)
+
elif continue_probe and values['model'].find("ePowerSwitch") >=0:
# TODO: allow a different port than http 80.
if values['pcu_id'] in [1089, 1071, 1046, 1035, 1118]:
# log = logging.getLogger("monitorweb.controllers")
from monitor.database.info.model import *
from pcucontrol import reboot
+from monitor.wrapper.plccache import plcdb_id2lb as site_id2lb
def format_ports(pcu):
retval = []
if pcu.port_status and len(pcu.port_status.keys()) > 0 :
obj = reboot.model_to_object(pcu.plc_pcu_stats['model'])
for port in obj.supported_ports:
- state = pcu.port_status[str(port)]
+ try:
+ state = pcu.port_status[str(port)]
+ except:
+ state = "unknown"
+
retval.append( (port, state) )
if retval == []:
else:
filtercount['pending'] += 1
+ try:
+ node.loginbase = site_id2lb[node.plc_pcu_stats['site_id']]
+ except:
+ node.loginbase = "unknown"
+
node.ports = format_ports(node)
node.status = format_pcu_shortstatus(node)
--- /dev/null
+/*
+
+On page load, the SortableManager:
+
+- Finds the table by its id (sortable_table).
+- Parses its thead for columns with a "mochi:format" attribute.
+- Parses the data out of the tbody based upon information given in the
+ "mochi:format" attribute, and clones the tr elements for later re-use.
+- Clones the column header th elements for use as a template when drawing
+ sort arrow columns.
+- Stores away a reference to the tbody, as it will be replaced on each sort.
+- Performs the first sort.
+
+
+On sort request:
+
+- Sorts the data based on the given key and direction
+- Creates a new tbody from the rows in the new ordering
+- Replaces the column header th elements with clickable versions, adding an
+ indicator (↑ or ↓) to the most recently sorted column.
+
+*/
+
+SortableManager = function () {
+ this.thead = null;
+ this.tbody = null;
+ this.columns = [];
+ this.rows = [];
+ this.sortState = {};
+ this.sortkey = 0;
+};
+
+mouseOverFunc = function () {
+ addElementClass(this, "over");
+};
+
+mouseOutFunc = function () {
+ removeElementClass(this, "over");
+};
+
+ignoreEvent = function (ev) {
+ if (ev && ev.preventDefault) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ } else if (typeof(event) != 'undefined') {
+ event.cancelBubble = false;
+ event.returnValue = false;
+ }
+};
+
+
+update(SortableManager.prototype, {
+
+ "initWithTable": function (table) {
+ /***
+
+ Initialize the SortableManager with a table object
+
+ ***/
+ // Ensure that it's a DOM element
+ table = getElement(table);
+ // Find the thead
+ this.thead = table.getElementsByTagName('thead')[0];
+ // get the mochi:format key and contents for each column header
+ var cols = this.thead.getElementsByTagName('th');
+ for (var i = 0; i < cols.length; i++) {
+ var node = cols[i];
+ var attr = null;
+ try {
+ attr = node.getAttribute("mochi:format");
+ } catch (err) {
+ // pass
+ }
+ var o = node.childNodes;
+ this.columns.push({
+ "format": attr,
+ "element": node,
+ "proto": node.cloneNode(true)
+ });
+ }
+ // scrape the tbody for data
+ this.tbody = table.getElementsByTagName('tbody')[0];
+ // every row
+ var rows = this.tbody.getElementsByTagName('tr');
+ for (var i = 0; i < rows.length; i++) {
+ // every cell
+ var row = rows[i];
+ var cols = row.getElementsByTagName('td');
+ var rowData = [];
+ for (var j = 0; j < cols.length; j++) {
+ // scrape the text and build the appropriate object out of it
+ var cell = cols[j];
+ var obj = scrapeText(cell);
+ switch (this.columns[j].format) {
+ case 'isodate':
+ obj = isoDate(obj);
+ break;
+ case 'int':
+ obj = parseInt(obj);
+ break;
+ case 'str':
+ break;
+ case 'istr':
+ obj = obj.toLowerCase();
+ break;
+ // cases for numbers, etc. could be here
+ default:
+ break;
+ }
+ rowData.push(obj);
+ }
+ // stow away a reference to the TR and save it
+ rowData.row = row.cloneNode(true);
+ this.rows.push(rowData);
+
+ }
+
+ // do initial sort on first column
+ this.drawSortedRows(this.sortkey, true, false);
+
+ },
+
+ "onSortClick": function (name) {
+ /***
+
+ Return a sort function for click events
+
+ ***/
+ return method(this, function () {
+ log('onSortClick', name);
+ var order = this.sortState[name];
+ if (order == null) {
+ order = true;
+ } else if (name == this.sortkey) {
+ order = !order;
+ }
+ this.drawSortedRows(name, order, true);
+ });
+ },
+
+ "drawSortedRows": function (key, forward, clicked) {
+ /***
+
+ Draw the new sorted table body, and modify the column headers
+ if appropriate
+
+ ***/
+ log('drawSortedRows', key, forward);
+ this.sortkey = key;
+ // sort based on the state given (forward or reverse)
+ var cmp = (forward ? keyComparator : reverseKeyComparator);
+ this.rows.sort(cmp(key));
+ // save it so we can flip next time
+ this.sortState[key] = forward;
+ // get every "row" element from this.rows and make a new tbody
+ var newBody = TBODY(null, map(itemgetter("row"), this.rows));
+ // swap in the new tbody
+ this.tbody = swapDOM(this.tbody, newBody);
+ // replace first column with row count.
+ var rows = this.tbody.getElementsByTagName('tr');
+ for (var i=0; i < rows.length; i++) {
+ var row = rows[i];
+ var cols = row.getElementsByTagName('td');
+ // var cell = cols[0];
+ cols[0].innerHTML = String(i);
+ }
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ var node = col.proto.cloneNode(true);
+ // remove the existing events to minimize IE leaks
+ col.element.onclick = null;
+ col.element.onmousedown = null;
+ col.element.onmouseover = null;
+ col.element.onmouseout = null;
+ // set new events for the new node
+ node.onclick = this.onSortClick(i);
+ node.onmousedown = ignoreEvent;
+ node.onmouseover = mouseOverFunc;
+ node.onmouseout = mouseOutFunc;
+ // if this is the sorted column
+ if (key == i) {
+ // \u2193 is down arrow, \u2191 is up arrow
+ // forward sorts mean the rows get bigger going down
+ var arrow = (forward ? "\u2193" : "\u2191");
+ // add the character to the column header
+ node.appendChild(SPAN(null, arrow));
+ if (clicked) {
+ node.onmouseover();
+ }
+ }
+
+ // swap in the new th
+ col.element = swapDOM(col.element, node);
+ }
+ }
+});
+
+sortableManager = new SortableManager();
+
+addLoadEvent(function () {
+ sortableManager.initWithTable('sortable_table');
+});
+
+// rewrite the view-source links
+addLoadEvent(function () {
+ var elems = getElementsByTagAndClassName("A", "view-source");
+ var page = "sortable_tables/";
+ for (var i = 0; i < elems.length; i++) {
+ var elem = elems[i];
+ var href = elem.href.split(/\//).pop();
+ elem.target = "_blank";
+ elem.href = "../view-source/view-source.html#" + page + href;
+ }
+});
from monitor import config
def plc_site_link(pcu):
- return "https://" + config.MONITOR_HOSTNAME + "/db/sites/index.php?id=" + str(pcu['site_id'])
+ return "https://" + config.PLC_WEB_HOSTNAME + "/db/sites/index.php?id=" + str(pcu['site_id'])
def pcu_link(pcu):
- return "https://" + config.MONITOR_HOSTNAME + "/db/sites/pcu.php?id=" + str(pcu['pcu_id'])
+ return "https://" + config.PLC_WEB_HOSTNAME + "/db/sites/pcu.php?id=" + str(pcu['pcu_id'])
?>
<html py:layout="'sitemenu.kid'"
- xmlns:py="http://purl.org/kid/ns#">
+ xmlns:py="http://purl.org/kid/ns#"
+ xmlns:mochi="http://www.mochi.org">
<div py:match="item.tag == 'content'">
<table id="sub-table" width="100%">
<tbody>
<tr>
<td colspan="5">
- <table border="1" width="100%">
+ <table id="sortable_table" class="datagrid" border="1" width="100%">
<thead>
<tr>
- <th>Site</th>
+ <th mochi:format="int"></th>
+ <th mochi:format="str">Site</th>
<th>PCU Name</th>
<th>Missing Fields</th>
<th>DNS Status</th>
</thead>
<tbody>
<tr py:for="i,node in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
- <td><a href="${plc_site_link(node.plc_pcu_stats)}">sitename</a></td>
+ <td></td>
+ <td><a href="${plc_site_link(node.plc_pcu_stats)}">${node.loginbase}</a></td>
<td nowrap="true" >
<a href="${pcu_link(node.plc_pcu_stats)}">${pcu_name(node.plc_pcu_stats)}</a></td>
<td py:content="node.entry_complete"></td>
<html xmlns:py="http://purl.org/kid/ns#">
<head>
<title>App Name - ${page_title}</title>
- <link href="/static/css/style.css" type="text/css" rel="stylesheet" />
+ <link href="static/css/style.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="${tg.tg_js}/MochiKit.js"></script>
+ <script type="text/javascript" src="static/javascript/sortable_tables.js"></script>
+
</head>
<body>