Created
May 20, 2015 01:20
-
-
Save srl295/fecf56cb26cbfb2a6c9b to your computer and use it in GitHub Desktop.
DIff of changes in the cldr-stwatcher. To be merged.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/.cfignore b/.cfignore | |
index 1273728..066ae58 100644 | |
--- a/.cfignore | |
+++ b/.cfignore | |
@@ -1,12 +1,7 @@ | |
-launchConfigurations/ | |
-.git/ | |
-node_modules/ | |
-.cfignore | |
-.gitignore | |
+config/runtime.json | |
+node_modules | |
.svn | |
-*~ | |
-*.txt | |
-*.log | |
-local-creds.sh | |
-public/bower_components/ | |
-npm-debug.log | |
\ No newline at end of file | |
+.git | |
+config/akka.json | |
+config/milu.json | |
+config/gdx.json | |
diff --git a/.gitignore b/.gitignore | |
index abeeff6..e39faf9 100644 | |
--- a/.gitignore | |
+++ b/.gitignore | |
@@ -4,8 +4,10 @@ | |
node_modules | |
cldr-stwatcher.esproj/user* | |
config/akka.json | |
-npmdebug.log | |
+npm-debug.log | |
config/milu.json | |
config/runtime.json | |
+config/runtime.json.tmp* | |
config/gdx.json | |
+config/local.json | |
**/*~ | |
diff --git a/License.txt b/License.txt | |
new file mode 100644 | |
index 0000000..ead1daf | |
--- /dev/null | |
+++ b/License.txt | |
@@ -0,0 +1 @@ | |
+<Replace this text with the license you've chosen for your project.> | |
diff --git a/Readme.md b/Readme.md | |
index 090aeb6..6e94707 100644 | |
--- a/Readme.md | |
+++ b/Readme.md | |
@@ -2,11 +2,8 @@ A watcher for CLDR SurveyTool. | |
Quite under documented. | |
-copy config/SOMEHOST.json to config/localhost.json where localhost is your hostname. | |
+copy config/SOMEHOST.json to config/localhost.json where localhost is your hos | |
If node is installed, 'make' should fire up the server. | |
http://127.0.0.1:3000 should show you the watcher. | |
- | |
- | |
- | |
diff --git a/app.js b/app.js | |
index 4605e54..9534865 100644 | |
--- a/app.js | |
+++ b/app.js | |
@@ -1,29 +1,32 @@ | |
+ | |
/** | |
* Module dependencies. | |
*/ | |
- | |
-var appEnv = require('cfenv').getAppEnv(); | |
- | |
var express = require('express') | |
, routes = require('./routes') | |
, stwatcher = require('./routes/stwatcher') | |
, http = require('http') | |
, path = require('path') | |
-favicon = require('serve-favicon'), | |
-serveStatic = require('serve-static'), | |
-morgan = require('morgan'), | |
-bodyParser = require('body-parser'); | |
+, CONFIG = require('config').SurveyWatcher, | |
+ favicon = require('serve-favicon'), | |
+ serveStatic = require('serve-static'), | |
+ morgan = require('morgan'), | |
+ bodyParser = require('body-parser'); | |
+ | |
+var host = (process.env.VCAP_APP_HOST || undefined); | |
+var port = process.env.VCAP_APP_PORT || CONFIG.port || process.env.PORT || 3000; | |
+console.log("hostport: " + host+":"+port); | |
var app = express(); | |
// all environments | |
-app.set('port', appEnv.port); | |
+app.set('port', port); | |
app.set('views', __dirname + '/views'); | |
app.set('view engine', 'jade'); | |
app.enable('trust proxy'); | |
app.use(favicon(__dirname + '/public/favicon.ico')); | |
-app.use(morgan({ format: 'dev', immediate: true })); | |
+app.use(morgan({format: 'dev', immediate: true})); | |
app.use(bodyParser()); | |
app.use(require('method-override')()); | |
@@ -36,9 +39,9 @@ app.use(serveStatic(path.join(__dirname, 'public'))); | |
// development only | |
if ('development' == app.get('env')) { | |
- app.use(require('errorhandler')()); | |
+ app.use(require('errorhandler')()); | |
} | |
var port = app.get('port'); | |
console.log('Express server listening on port ' + port); | |
-app.listen(appEnv.port, appEnv.bind); | |
+app.listen(app.get('port')); | |
diff --git a/cldr-stwatcher.esproj/Project.espressostorage b/cldr-stwatcher.esproj/Project.espressostorage | |
new file mode 100644 | |
index 0000000..892ee38 | |
Binary files /dev/null and b/cldr-stwatcher.esproj/Project.espressostorage differ | |
diff --git a/config/default.json b/config/default.json | |
index 84ad2e7..929bc9e 100644 | |
--- a/config/default.json | |
+++ b/config/default.json | |
@@ -7,8 +7,10 @@ | |
"title": "SurveyWatcher" | |
}, | |
"watcher": { | |
- "polltime": 10 | |
+ "polltime": 500 | |
}, | |
- "port": 3000 | |
+ "port": 3000, | |
+ "notify": { | |
+ } | |
} | |
} | |
diff --git a/loopy.sh b/loopy.sh | |
new file mode 100644 | |
index 0000000..f2a4be2 | |
--- /dev/null | |
+++ b/loopy.sh | |
@@ -0,0 +1,7 @@ | |
+#!/bin/sh | |
+while [[ 1 == 1 ]]; | |
+do | |
+ make | |
+ sleep 30 | |
+done | |
+ | |
diff --git a/manifest.yml b/manifest.yml | |
index dda6c7c..0c7e4ac 100644 | |
--- a/manifest.yml | |
+++ b/manifest.yml | |
@@ -1,16 +1,12 @@ | |
---- | |
applications: | |
-- name: stwatcher | |
- services: | |
- - Cloudant NoSQL DB-gx | |
- - IBM Globalization-zl | |
- - Monitoring and Analytics-u0 | |
- - SurveyTool-prod | |
- - SurveyTool-smoke | |
- disk_quota: 1024M | |
- host: stwatcher | |
+- services: | |
+ - mysql | |
+ - twilio | |
+ - SendGrid | |
+ - sso-ibm | |
+ disk: 1024M | |
name: stwatcher | |
+ command: node app.js | |
path: . | |
- domain: mybluemix.net | |
+ mem: 256M | |
instances: 1 | |
- memory: 256M | |
diff --git a/package.json b/package.json | |
index 5582c70..2a68cb1 100644 | |
--- a/package.json | |
+++ b/package.json | |
@@ -6,20 +6,20 @@ | |
"start": "node app.js" | |
}, | |
"dependencies": { | |
- "body-parser": "^1.12.3", | |
- "cfenv": "^1.0.0", | |
- "cloudant": "^1.0.0-beta3", | |
- "errorhandler": "^1.3.5", | |
- "express": "^4.2.0", | |
- "gaas": "^1.0.1", | |
- "jade": "^1.9.2", | |
- "method-override": "^2.3.2", | |
- "morgan": "^1.5.2", | |
- "nodemailer": "^1.3.4", | |
- "ping": "^0.1.9", | |
- "request": "^2.34.0", | |
- "serve-favicon": "^2.2.0", | |
- "serve-static": "^1.9.2", | |
- "twilio": "^2.2.0" | |
+ "body-parser":"*", | |
+ "config": "0.4.x", | |
+ "express": "4.2.x", | |
+ "jade": "*", | |
+ "method-override":"*", | |
+ "morgan":"*", | |
+ "mysql": "2.2.xx", | |
+ "orm": "2.1.x", | |
+ "ping": "*", | |
+ "request": "2.34.x", | |
+ "sendgrid":"1.0.3", | |
+ "serve-favicon": "*", | |
+ "serve-static":"*", | |
+ "twilio": ">=1.6.0", | |
+ "errorhandler":"*" | |
} | |
} | |
diff --git a/public/javascripts/stwatcher-client.js b/public/javascripts/stwatcher-client.js | |
index aec3870..4ffe195 100644 | |
--- a/public/javascripts/stwatcher-client.js | |
+++ b/public/javascripts/stwatcher-client.js | |
@@ -23,361 +23,493 @@ function stwatcher_interval(_config) { | |
console.log("Interval = " + n + " sec"); | |
// interval for next time | |
-require(["dojo/query", "dojo/request", "dojo/dom", "dojo/dom-construct", "dojo/main", "dojox/charting/Chart", "dojox/charting/axis2d/Default", "dojox/charting/plot2d/Lines", "dojox/charting/action2d/MouseIndicator", "dojox/charting/action2d/MouseZoomAndPan", "dojox/charting/action2d/TouchZoomAndPan", "dojox/charting/plot2d/Indicator", "dojo/domReady!"], | |
- function stwatcherclient(query,request,dom, dcons, main, Chart, Default, Lines, MouseIndicator, MouseZoomAndPan, TouchZoomAndPan, Indicator) { | |
- | |
- | |
- var powered = dom.byId('powered'); | |
- powered.appendChild(document.createTextNode(" and dōjō " + main.version)); | |
+ require(["dojo/query", "dojo/request", "dojo/dom", "dojo/dom-construct", "dojo/main", "dojox/charting/Chart", "dojox/charting/axis2d/Default", "dojox/charting/plot2d/Lines", "dojox/charting/action2d/MouseIndicator", "dojox/charting/action2d/MouseZoomAndPan", "dojox/charting/action2d/TouchZoomAndPan", "dojox/charting/plot2d/Indicator", | |
+ "dojox/charting/action2d/Tooltip", "dojo/domReady!"], | |
+ function stwatcherclient(query,request,dom, dcons, main, Chart, Default, Lines, MouseIndicator, MouseZoomAndPan, TouchZoomAndPan, Indicator, | |
+ Tooltip) { | |
+ | |
- function fmtNs(val) { | |
- if(val > 1e9) { | |
- val = val / 1e9; | |
- return val.toFixed(3) + "s"; | |
- } else if(val > 1e6) { | |
- val = val / 1e6; | |
- return val.toFixed(3) + "ms"; | |
- } else if(val > 1e3) { | |
- val = val / 1e3; | |
- return val.toFixed(3) + "µs"; | |
- } else { | |
- return val + "ns"; | |
- } | |
- } | |
- function setText(node, text) { | |
- var node2 = query(node); | |
- if(node2==null) return node; | |
- node2 = node2[0]; // it's a list | |
- | |
- var textNode = document.createTextNode(text); | |
- while(node2.firstChild != null) { | |
- node2.removeChild(node2.firstChild); | |
- } | |
- node2.appendChild(textNode); | |
- } | |
+ var powered = dom.byId('powered'); | |
+ powered.appendChild(document.createTextNode(" and dōjō " + main.version)); | |
- setText("#status", "Loading.."); | |
- | |
- var div = query('#SurveyWatcher'); | |
- | |
- div.empty(); | |
- var chart1=null; | |
- | |
- | |
- function show_history(server, obj, hostInfo) { | |
- setText('#chartTitle', "History: " + server + "@" + obj.host); | |
- var hdiv = query("#SurveyChart"); | |
- if(chart1===null) { | |
- hdiv.empty(); | |
- } | |
- setText('#chartstatus', 'Please Wait: Fetching chart info'); | |
- request | |
- .get('history.json?server='+server, {handleAs: 'json'}) | |
- .then(function(json) { | |
- window._HISTORY = json; // DEBUGGING | |
- if(json.err) { | |
- setText('#chartstatus', 'Err: ' + json.err); | |
- } else { | |
- setText('#chartstatus', 'Chart loaded: ' + json.data.length + ' rows'); | |
- var isNew = (chart1 === null); | |
- | |
- if(!isNew) { | |
- chart1.destroy(); | |
+ function fmtNs(val) { | |
+ if(val > 1e9) { | |
+ val = val / 1e9; | |
+ return val.toFixed(3) + "s"; | |
+ } else if(val > 1e6) { | |
+ val = val / 1e6; | |
+ return val.toFixed(3) + "ms"; | |
+ } else if(val > 1e3) { | |
+ val = val / 1e3; | |
+ return val.toFixed(3) + "µs"; | |
+ } else { | |
+ return val + "ns"; | |
} | |
+ } | |
+ | |
+ function setText(node, text) { | |
+ var node2 = query(node); | |
+ if(node2==null) return node; | |
+ node2 = node2[0]; // it's a list | |
- var data = []; | |
- var data2 = []; | |
- var outages = []; | |
- | |
- for(var k in json.data ) { | |
- var row = json.data[k]; | |
- var when = new Date(row.when).getTime(); | |
- var usersn = -3.0; | |
- if(row.users) { | |
- usersn = parseInt(row.users); | |
- data.push({x:when, y:usersn}); | |
- } else { | |
- usersn = 0; | |
- data.push({x:when, y:-3.0}); | |
- } | |
- var loadn = -1.0; | |
- | |
- if(row.isBusted || row.busted) { | |
- loadn = -3.0; | |
- outages.push(when); | |
- data2.push({x:when, y:loadn}); | |
- } else if(row.load === null) { | |
- loadn = -3.0; | |
- outages.push(when); | |
- data2.push({x:when, y:loadn}); | |
- } else { | |
- loadn = parseFloat(row.load.split(' ')[0]); | |
- data2.push({x:when, y:loadn}); | |
- } | |
- | |
+ var textNode = document.createTextNode(text); | |
+ while(node2.firstChild != null) { | |
+ node2.removeChild(node2.firstChild); | |
} | |
- | |
- chart1 = new Chart("SurveyChart"); | |
- chart1.addPlot("default", {type: Lines}); | |
- chart1.addAxis("x", {minorLabels: false, labelFunc: function(xx){ | |
- return new Date(parseInt(xx)).toLocaleString(); | |
- }}); | |
- chart1.addAxis("y", {vertical: true, min: -0}); | |
- chart1.addSeries("Series 1", data); | |
- chart1.addSeries("Series V", data2, {plot: "default", stroke: {color: "blue"}}); | |
- chart1.addPlot("threshold", { type: Indicator, | |
- lineStroke: { color: "red", style: "ShortDash"}, | |
- labels: "none", | |
- labelFunc: function(xx) {return "Ⓧ" /*new Date(outages[xx]).format("HH:MM");*/}, | |
- values: outages}); | |
+ node2.appendChild(textNode); | |
+ } | |
- | |
-// new MouseIndicator(chart1, "default", { series: "Series 1", | |
-// font: "normal normal bold 12pt Tahoma", | |
-// fillFunc: function(v){ | |
-// return v.y>55?"green":"red"; | |
-// }, | |
-// autoScroll: true, | |
-// labelFunc: function(v){ | |
-// return v; | |
-// }}); | |
- new MouseZoomAndPan(chart1, "default", { axis: "x" }); | |
- //new TouchZoomAndPan(chart1, "default", { axis: "x" }); | |
- chart1.render(); | |
- | |
- | |
- /*{ | |
- "busted": null, | |
- "dbused": 1659, | |
- "guests": 0, | |
- "id": 6141, | |
- "info": "Data Submission 24 LOCAL", | |
- "isBusted": false, | |
- "isSetup": true, | |
- "load": "0.71 cpu=8", | |
- "mem": "560.23725/1079.296", | |
- "ns": 333093000, | |
- "pages": null, | |
- "probation": false, | |
- "server": "naf", | |
- "stamp": "2013-05-25T04:46:48.000Z", | |
- "statusCode": 200, | |
- "uptime": "uptime: 1:22:59", | |
- "users": 0, | |
- "when": "2013-05-25T06:09:48.000Z" | |
- }, | |
- */ | |
- | |
- } | |
- }) | |
- .otherwise(function(err) { | |
- setText("#chartstatus", "Error Loading! " + err); | |
- }); | |
- | |
- } | |
- | |
- function consumeType(obj, newlist, now, json) { | |
- var keys = Object.keys(newlist); | |
- for(var k in keys) { | |
- var e = keys[k]; | |
- if(stw_config.reloadOnly && e != stw_config.reloadOnly) continue; | |
- var newEntry = newlist[e]; | |
- var oldEntry = null; | |
- var odiv = obj.div; | |
- if(!obj.list[e]) { | |
- // new | |
- oldEntry = { | |
- subDiv: dcons.create("li",{innerHTML: "<h4>"+e+"</h4>"}), | |
- }; | |
- odiv.appendChild(oldEntry.subDiv); | |
- obj.list[e] = oldEntry; | |
- } else { | |
- oldEntry = obj.list[e]; | |
- } | |
- | |
- obj.update(obj, e, newEntry, now, json); | |
- } | |
- }; | |
- | |
- var servers = { | |
- list: {}, | |
- div: dcons.create("div",{id: "servers", class: "group", innerHTML: "<h3>Servers</h3>" }), | |
- update: function(obj, e, newEntry, now, json) { | |
- var oldEntry = obj.list[e]; | |
- var txtstatus = "unknown"; | |
- | |
- if(newEntry.lastKnownStatus) { | |
- txtstatus = (newEntry.lastKnownStatus.up ? "up" : "down"); | |
- } | |
- oldEntry.subDiv.className = txtstatus; | |
- if(stw_config.reloadOnly) { | |
- oldEntry.subDiv.getElementsByTagName('h4')[0].innerHTML = txtstatus; | |
- } | |
+ var updateCpu = function updateCpu(txt, load, ncpus) { | |
+ var cpus; | |
+ if(txt.cpus) { | |
+ cpus = txt.cpus; | |
+ } else { | |
+ cpus = []; | |
+ var cpuDiv = document.createElement("div"); | |
+ cpuDiv.className="cpus"; | |
+ txt.appendChild(cpuDiv); | |
+ txt.cpuDiv = cpuDiv; | |
+ txt.cpus = cpus; | |
+ txt.cpuMsg = document.createElement("span"); | |
+ txt.cpuMsg.className = "cpuMsg"; | |
+ for(var i=0;i<2;i++) | |
+ { | |
+ var cpu = document.createElement("div"); | |
+ cpuDiv.appendChild(cpu); | |
+ cpus.push(cpu); | |
+ } | |
+ cpus[0].className = "cpu0"; | |
+ cpus[1].className = "cpu1"; | |
+ cpuDiv.appendChild(txt.cpuMsg); | |
+ } | |
+ var totalLoadPct = (load*100.0)/ncpus; | |
+ var loadPct1 = Number(totalLoadPct).toFixed(1); | |
+ var loadPct0 = Number(totalLoadPct).toFixed(0); | |
+ cpus[0].style.width = (loadPct0+"px"); | |
+ cpus[1].style.width = ((100-loadPct0)+"px"); | |
+ setText(txt.cpuMsg, " Total Load:" + loadPct1+"%" + ", " + ncpus + " cpus"); | |
+ }; | |
- var links = oldEntry.links; | |
- if(!links) { | |
- links = dcons.create("ul",{class: "links"}); | |
- oldEntry.links = links; | |
+ setText("#status", "Loading.."); | |
- obj.host = newEntry.host; | |
- obj.hostEnt = json.hosts[obj.host]; | |
- if(stw_config.reloadOnly) { | |
- var urlLink = dcons.create("button",{innerHTML: "Try Now"}); | |
- urlLink.onclick = function() { | |
- window.location.reload(true); | |
- }; | |
- links.appendChild(urlLink); | |
- } else { | |
- | |
- { | |
- var urlLink = dcons.create("a",{href: obj.hostEnt.servers[e].url, innerHTML: "go"}); | |
- var urlLi = dcons.create("li"); | |
- urlLi.appendChild(urlLink); | |
- links.appendChild(urlLi); | |
+ var div = query('#SurveyWatcher'); | |
+ | |
+ div.empty(); | |
+ var chart1=null; | |
+ | |
+ | |
+ function show_history(server, obj, hostInfo) { | |
+ setText('#chartTitle', "History: " + server + "@" + obj.host); | |
+ var hdiv = query("#SurveyChart"); | |
+ if(chart1===null) { | |
+ hdiv.empty(); | |
} | |
- { | |
- var urlLink = dcons.create("button",{innerHTML: "history"}); | |
- var urlLi = dcons.create("li"); | |
- urlLi.appendChild(urlLink); | |
- links.appendChild(urlLi); | |
- | |
- urlLink.onclick = function() { | |
- show_history(e, obj, json.hosts[obj.host]); | |
- return false; | |
- }; | |
+ setText('#chartstatus', 'Please Wait: Fetching chart info'); | |
+ request | |
+ .get('history.json?server='+server, {handleAs: 'json'}) | |
+ .then(function(json) { | |
+ window._HISTORY = json; // DEBUGGING | |
+ if(json.err) { | |
+ setText('#chartstatus', 'Err: ' + json.err); | |
+ } else { | |
+ setText('#chartstatus', 'Chart loaded: ' + json.data.length + ' data points'); | |
+ var isNew = (chart1 === null); | |
+ | |
+ if(!isNew) { | |
+ chart1.destroy(); | |
+ } | |
+ | |
+ var data = []; | |
+ var data2 = []; | |
+ var outages = []; | |
+ var innages = []; | |
+ var revages = []; | |
+ var revs = []; | |
+ var wasup = null; | |
+ var lastUpWhen = null; // the previous 'up' row | |
+ var lastrev = null; | |
+ | |
+ | |
+ function reportRunning(when, isup, info) { | |
+ if( | |
+ (wasup == null) // start of time | |
+ || (wasup != isup) // change | |
+ ) { | |
+ (isup?innages:outages).push(when); // record change | |
+ wasup = isup; | |
+ | |
+ if(isup && (info != null) && info.length>0) { | |
+ var spinfo = info.split(';'); | |
+ if(spinfo && spinfo.length>1) { | |
+ if(lastrev!=null) { | |
+ if(lastUpWhen != null && lastrev != spinfo[0]) { | |
+ // rev change - at lastupWhen | |
+ innages.pop(); // superceded | |
+ revages.push(lastUpWhen); | |
+ revs.push(lastrev); | |
+ console.log("Rev change " + lastUpWhen + "/" + lastrev); | |
+ } | |
+ } | |
+ | |
+ // have to push this here, or it could get out of sync. | |
+ lastrev = spinfo[0]; | |
+ lastUpWhen = when; | |
+ // console.log(".." + lastUpWhen + "/ " + lastrev); | |
+ } else { | |
+ // no data after this, but at least mark the version change | |
+ if(lastUpWhen != null && lastrev != null) { | |
+ innages.pop(); // superceded | |
+ revages.push(lastUpWhen); | |
+ revs.push(lastrev); | |
+ console.log("Rev change (past=NULL) " + lastUpWhen + "/" + lastrev); | |
+ } | |
+ } | |
+ } | |
+ } else { | |
+ // no change | |
+ } | |
+ } | |
+ | |
+ chart1 = new Chart("SurveyChart"); | |
+ chart1.addPlot("default", {type: Lines}); | |
+ chart1.addAxis("x", {minorLabels: false, labelFunc: function(xx){ | |
+ return new Date(parseInt(xx)).toLocaleString(); | |
+ }}); | |
+ chart1.addAxis("y", {vertical: true, min: -0}); | |
+ | |
+ var dataCount = 0; | |
+ var data2Count = 0; | |
+ var dataOpts = {}; | |
+ var data2Opts = {plot: "default", stroke: {color: "blue"}}; | |
+ /** | |
+ * write 'data', start a new chart. | |
+ */ | |
+ function flushData() { | |
+ if(data.length>0) { | |
+ chart1.addSeries("Series 1."+(dataCount++), data, dataOpts); | |
+ } | |
+ data = []; | |
+ } | |
+ function flushData2() { | |
+ if(data2.length>0) { | |
+ chart1.addSeries("Series 2."+(data2Count++), data2, data2Opts); | |
+ } | |
+ data2 = []; | |
+ } | |
+ | |
+ var lastWhen = null; // the previous row | |
+ // this data is in REVERSE ORDER | |
+ for(var k in json.data ) { | |
+ var row = json.data[k]; | |
+ var when = new Date(row.when).getTime(); | |
+ | |
+ if(lastWhen != null) { | |
+ // console.log("Gap: " + (lastWhen-when) + " at " + row.when); | |
+ if((lastWhen-when) > (1000*60*15)) { // over 15 minute gap? | |
+ // break the chart. | |
+ flushData(); | |
+ flushData2(); | |
+ } | |
+ } | |
+ | |
+ var usersn = -3.0; | |
+ if(row.users) { | |
+ usersn = parseInt(row.users); | |
+ data.push({x:when, y:usersn}); | |
+ } else { | |
+ // break the users chart. | |
+ flushData(); | |
+ // usersn = -1.0; | |
+ // data.push({x:when, y:-3.0}); | |
+ } | |
+ var loadn = -1.0; | |
+ if( (row.isBusted || row.busted) || (row.load === null)) { | |
+ reportRunning(when, false, row.info); | |
+ // break the load chart. | |
+ flushData2(); | |
+ // loadn = -3.0; // fake value to get it under the line | |
+ // data2.push({x:when, y:loadn}); | |
+ } else { | |
+ reportRunning(when, true, row.info); | |
+ loadn = parseFloat(row.load.split(' ')[0]); | |
+ data2.push({x:when, y:loadn}); | |
+ } | |
+ lastWhen = when; | |
+ } | |
+ | |
+ // get any trailing data | |
+ flushData(); | |
+ flushData2(); | |
+ | |
+ chart1.addPlot("threshold1", { type: Indicator, | |
+ lineStroke: { color: "red", style: "ShortDash"}, | |
+ labels: "none", | |
+ labelFunc: function(xx) {return "⬇" /*new Date(outages[xx]).format("HH:MM");*/}, | |
+ values: outages}); | |
+ chart1.addPlot("threshold2", { type: Indicator, | |
+ lineStroke: { color: "green", style: "ShortDash"}, | |
+ labels: "none", | |
+ labelFunc: function(xx) {return "⬆" /*new Date(outages[xx]).format("HH:MM");*/}, | |
+ values: innages}); | |
+ chart1.addPlot("threshold3", { type: Indicator, | |
+ lineStroke: { color: "green", style: "ShortDash"}, | |
+ labels: "none", | |
+ labelFunc: function(xx) {return "⬆r"+revs[xx] /*new Date(outages[xx]).format("HH:MM");*/}, | |
+ values: revages}); | |
+ | |
+ | |
+ // new MouseIndicator(chart1, "default", { series: "Series 1", | |
+ // font: "normal normal bold 12pt Tahoma", | |
+ // fillFunc: function(v){ | |
+ // return v.y>55?"green":"red"; | |
+ // }, | |
+ // autoScroll: true, | |
+ // labelFunc: function(v){ | |
+ // return v; | |
+ // }}); | |
+ new MouseZoomAndPan(chart1, "default", { axis: "x" }); | |
+ new Tooltip(chart1, "default"); | |
+ //new TouchZoomAndPan(chart1, "default", { axis: "x" }); | |
+ chart1.render(); | |
+ | |
+ | |
+ /*{ | |
+ "busted": null, | |
+ "dbused": 1659, | |
+ "guests": 0, | |
+ "id": 6141, | |
+ "info": "Data Submission 24 LOCAL", | |
+ "isBusted": false, | |
+ "isSetup": true, | |
+ "load": "0.71 cpu=8", | |
+ "mem": "560.23725/1079.296", | |
+ "ns": 333093000, | |
+ "pages": null, | |
+ "probation": false, | |
+ "server": "naf", | |
+ "stamp": "2013-05-25T04:46:48.000Z", | |
+ "statusCode": 200, | |
+ "uptime": "uptime: 1:22:59", | |
+ "users": 0, | |
+ "when": "2013-05-25T06:09:48.000Z" | |
+ }, | |
+ */ | |
+ | |
+ } | |
+ }) | |
+ .otherwise(function(err) { | |
+ setText("#chartstatus", "Error Loading! " + err); | |
+ }); | |
+ | |
+ } | |
+ | |
+ function consumeType(obj, newlist, now, json) { | |
+ var keys = Object.keys(newlist); | |
+ for(var k in keys) { | |
+ var e = keys[k]; | |
+ if(stw_config.reloadOnly && e != stw_config.reloadOnly) continue; | |
+ (function(e){ | |
+ var newEntry = newlist[e]; | |
+ var oldEntry = null; | |
+ var odiv = obj.div; | |
+ if(!obj.list[e]) { | |
+ // new | |
+ oldEntry = { | |
+ subDiv: dcons.create("li",{innerHTML: "<h4>"+e+"</h4>"}), | |
+ }; | |
+ odiv.appendChild(oldEntry.subDiv); | |
+ obj.list[e] = oldEntry; | |
+ } else { | |
+ oldEntry = obj.list[e]; | |
+ } | |
+ obj.update(obj, e, newEntry, now, json); | |
+ })(e); | |
} | |
- } | |
+ }; | |
- oldEntry.subDiv.appendChild(links); | |
- } | |
- | |
- var txt = oldEntry.txt; | |
- if(!txt) { | |
- txt = dcons.create("i",{class: "info"}); | |
- oldEntry.txt = txt; | |
- oldEntry.subDiv.appendChild(txt); | |
- } | |
- | |
- if(newEntry.latestStatus && newEntry.latestStatus.update) { | |
- var u = newEntry.latestStatus.update; | |
- var j = newEntry.latestStatus.json; | |
- var statusTxt = ""; | |
- if(u.users != null) { | |
- statusTxt = statusTxt + u.users+"u/"; | |
- } | |
- if(u.guests != null) { | |
- statusTxt = statusTxt + u.guests+"g/"; | |
- } | |
- if(u.load != null) { | |
- statusTxt = statusTxt + "l="+u.load +"/"; | |
- } | |
- if(u.isSetup == false) { | |
- statusTxt = statusTxt + "(not running)/"; | |
- } | |
- if(j) { | |
- if(j.status) { | |
- if(j.status.currev !== null) { | |
- statusTxt = statusTxt + "r"+j.status.currev+"/"; | |
+ | |
+ var hosts = { | |
+ list: {}, | |
+ div: dcons.create("div",{id: "hosts", class: "group", innerHTML: "<h3>Servers</h3>" }), | |
+ update: function(obj, e, newEntry, now, json) { | |
+ var oldEntry = obj.list[e]; | |
+ if(newEntry.stealth) { | |
+ oldEntry.subDiv.className = "stealth"; | |
+ } else if(newEntry.latestPing) { | |
+ oldEntry.subDiv.className = (newEntry.latestPing.alive ? "up" : "down"); | |
+ } else { | |
+ oldEntry.subDiv.className = "unknown"; | |
+ } | |
+ | |
+ var txt = oldEntry.txt; | |
+ if(!txt) { | |
+ txt = dcons.create("i",{class: "info"}); | |
+ oldEntry.txt = txt; | |
+ oldEntry.subDiv.appendChild(txt); | |
+ } | |
+ if(newEntry.latestPing) { | |
+ if(newEntry.latestPing.alive) { | |
+ setText(txt, "Ping: " + fmtNs(newEntry.latestPing.ns)); | |
+ } else { | |
+ //setText(txt, "Ping: " + fmtNs(newEntry.latestPing.ns)); | |
+ setText(txt, "Ping Timeout: " + fmtNs(newEntry.latestPing.ns)); | |
+ } | |
+ } else { | |
+ setText(txt, "(no ping)"); | |
} | |
} | |
- } | |
- setText(txt, statusTxt); | |
- } else { | |
- setText(txt, ""); | |
- } | |
- } | |
- }; | |
- | |
- if(stw_config.reloadOnly) { // 'only' mode- no header. | |
- servers.div.innerHTML="<h3>Status:</h3>"; | |
- } | |
- div.adopt(servers.div); | |
- | |
- var hosts = { | |
- list: {}, | |
- div: dcons.create("div",{id: "hosts", class: "group", innerHTML: "<h3>Hosts</h3>" }), | |
- update: function(obj, e, newEntry, now, json) { | |
- var oldEntry = obj.list[e]; | |
- if(newEntry.stealth) { | |
- oldEntry.subDiv.className = "stealth"; | |
- } else if(newEntry.latestPing) { | |
- oldEntry.subDiv.className = (newEntry.latestPing.alive ? "up" : "down"); | |
- } else { | |
- oldEntry.subDiv.className = "unknown"; | |
- } | |
- | |
- var txt = oldEntry.txt; | |
- if(!txt) { | |
- txt = dcons.create("i",{class: "info"}); | |
- oldEntry.txt = txt; | |
- oldEntry.subDiv.appendChild(txt); | |
- } | |
- if(newEntry.latestPing) { | |
- if(newEntry.latestPing.alive) { | |
- setText(txt, "Ping: " + fmtNs(newEntry.latestPing.ns)); | |
- } else { | |
- //setText(txt, "Ping: " + fmtNs(newEntry.latestPing.ns)); | |
- setText(txt, "Ping Timeout: " + fmtNs(newEntry.latestPing.ns)); | |
- } | |
- } else { | |
- setText(txt, "(no ping)"); | |
- } | |
- } | |
- }; | |
+ }; | |
+ var servers = { | |
+ list: {}, | |
+ div: dcons.create("div",{id: "servers", class: "group", innerHTML: "<h3>SurveyTool Instances</h3>" }), | |
+ update: function(obj, e, newEntry, now, json) { | |
+ var oldEntry = obj.list[e]; | |
+ var txtstatus = "unknown"; | |
+ | |
+ if(newEntry.lastKnownStatus) { | |
+ txtstatus = (newEntry.lastKnownStatus.up ? "up" : "down"); | |
+ } | |
+ oldEntry.subDiv.className = txtstatus; | |
- if(!stw_config.reloadOnly) { // 'only' mode- no hosts. | |
- div.adopt(hosts.div); | |
- } | |
- | |
- var lastTime=-1; | |
- var drift =0; | |
- | |
- stw_update = function() { | |
- request | |
- .get(stw_config.jsonurl, {handleAs: 'json'}) | |
- .then(function(json) { | |
- var now = new Date().getTime(); | |
- drift = now - json.now; // loading 'delay' (or clock skew) | |
- | |
- lastTime = now; | |
- setText("#age", "Updated just now"); | |
+ if(stw_config.reloadOnly) { | |
+ oldEntry.subDiv.getElementsByTagName('h4')[0].innerHTML = txtstatus; | |
+ } | |
- if(theInterval2 === -1) { | |
- theInterval2 = setInterval(function(){ | |
- var now2 = new Date().getTime(); | |
- var age = (now2-lastTime); | |
- var till = refreshTime - age; | |
- var tillMsg = ", will refresh soon"; | |
- if(till>0) { | |
- tillMsg = ", will refresh in "+ fmtNs(till*1e6); | |
+ var links = oldEntry.links; | |
+ if(!links) { | |
+ links = dcons.create("ul",{class: "links"}); | |
+ oldEntry.links = links; | |
+ | |
+ obj.host = newEntry.host; | |
+ obj.hostEnt = json.hosts[obj.host]; | |
+ if(stw_config.reloadOnly) { | |
+ var urlLink = dcons.create("button",{innerHTML: "Try Now"}); | |
+ urlLink.onclick = function() { | |
+ window.location.reload(true); | |
+ }; | |
+ links.appendChild(urlLink); | |
+ } else { | |
+ | |
+ { | |
+ var urlLink = dcons.create("a",{href: obj.hostEnt.servers[e].url, innerHTML: "visit"}); | |
+ var urlLi = dcons.create("li"); | |
+ urlLi.appendChild(urlLink); | |
+ links.appendChild(urlLi); | |
+ } | |
+ { | |
+ var urlLink = dcons.create("button",{innerHTML: "history"}); | |
+ var urlLi = dcons.create("li"); | |
+ urlLi.appendChild(urlLink); | |
+ links.appendChild(urlLi); | |
+ | |
+ urlLink.onclick = function() { | |
+ show_history(e, obj, json.hosts[obj.host]); | |
+ return false; | |
+ }; | |
+ } | |
+ } | |
+ | |
+ oldEntry.subDiv.appendChild(links); | |
+ } | |
+ | |
+ var txt = oldEntry.txt; | |
+ if(!txt) { | |
+ txt = dcons.create("i",{class: "info"}); | |
+ oldEntry.txt = txt; | |
+ oldEntry.subDiv.appendChild(txt); | |
} | |
- var ageMsg = "just now"; | |
- if(age > 250) { | |
- ageMsg = fmtNs(age*1e6) + " ago"; | |
+ | |
+ if(newEntry.latestStatus && newEntry.latestStatus.update) { | |
+ var u = newEntry.latestStatus.update; | |
+ var j = newEntry.latestStatus.json; | |
+ var statusTxt = ""; | |
+ if(u.users != null && u.users != 0) { | |
+ statusTxt = statusTxt + u.users+" user(s) "; | |
+ } | |
+ if(u.guests != null&& u.guests!=0) { | |
+ statusTxt = statusTxt + u.guests+" viewer(s) "; | |
+ } | |
+ if(u.load != null) { | |
+ var parts = u.load.split(" "); | |
+ var load = Number(parts[0]); | |
+ var ncpus = Number(parts[1].split('=')[1]); | |
+ updateCpu(hosts.list[obj.host].subDiv, load, ncpus); | |
+ } | |
+ if(u.isSetup == false) { | |
+ statusTxt = statusTxt + "(not running) "; | |
+ } | |
+ if(j) { | |
+ if(j.status) { | |
+ if(j.status.currev !== null) { | |
+ statusTxt = statusTxt + "r"+j.status.currev; | |
+ } | |
+ } | |
+ } | |
+ setText(txt, statusTxt); | |
+ } else { | |
+ setText(txt, ""); | |
} | |
- setText("#age", "Updated "+ageMsg +tillMsg); | |
- }, 5*1000); | |
+ } | |
+ }; | |
+ | |
+ if(stw_config.reloadOnly) { // 'only' mode- no header. | |
+ servers.div.innerHTML="<h3>Status:</h3>"; | |
} | |
+ | |
+ if(!stw_config.reloadOnly) { // 'only' mode- no hosts. | |
+ div.adopt(hosts.div); | |
+ } | |
+ div.adopt(servers.div); | |
- setText("#status", "skew=" + drift+"ms"); | |
- window._JSON = json; // DEBUGGING | |
+ var lastTime=-1; | |
+ var drift =0; | |
- consumeType(servers, json.servers, now, json); | |
- consumeType(hosts, json.hosts, now, json); | |
- }) | |
- .otherwise(function(err) { | |
- setText("#status", "Error Loading! (will retry) " + err); | |
- //clearInterval(theInterval); // do not keep thrashing | |
- }); | |
- }; | |
- | |
- setText("#status", "Loading..."); | |
+ stw_update = function() { | |
+ request | |
+ .get(stw_config.jsonurl, {handleAs: 'json'}) | |
+ .then(function(json) { | |
+ var now = new Date().getTime(); | |
+ drift = now - json.now; // loading 'delay' (or clock skew) | |
+ | |
+ lastTime = now; | |
+ setText("#age", "Updated just now"); | |
- stw_update(); | |
- theInterval = setInterval(function(){console.log('Update!'); stw_update();}, refreshTime); | |
-}); | |
+ if(theInterval2 === -1) { | |
+ theInterval2 = setInterval(function(){ | |
+ var now2 = new Date().getTime(); | |
+ var age = (now2-lastTime); | |
+ var till = refreshTime - age; | |
+ var tillMsg = ", will refresh soon"; | |
+ if(till>0) { | |
+ tillMsg = ", will refresh in "+ fmtNs(till*1e6); | |
+ } | |
+ var ageMsg = "just now"; | |
+ if(age > 250) { | |
+ ageMsg = fmtNs(age*1e6) + " ago"; | |
+ } | |
+ setText("#age", "Updated "+ageMsg +tillMsg); | |
+ }, 5*1000); | |
+ } | |
+ | |
+ setText("#status", "clock skew=" + drift+"ms"); | |
+ window._JSON = json; // DEBUGGING | |
+ | |
+ consumeType(hosts, json.hosts, now, json); | |
+ consumeType(servers, json.servers, now, json); | |
+ }) | |
+ .otherwise(function(err) { | |
+ setText("#status", "Error Loading! (will retry) " + err); | |
+ //clearInterval(theInterval); // do not keep thrashing | |
+ }); | |
+ }; | |
+ | |
+ setText("#status", "Loading..."); | |
+ | |
+ stw_update(); | |
+ theInterval = setInterval(function(){console.log('Update!'); stw_update();}, refreshTime); | |
+ }); | |
} | |
stwatcher_interval(); | |
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css | |
index 836d45f..3f43e5f 100644 | |
--- a/public/stylesheets/style.css | |
+++ b/public/stylesheets/style.css | |
@@ -1,11 +1,96 @@ | |
/* @override http://127.0.0.1:3000/stylesheets/style.css */ | |
body { | |
- margin: 1em; | |
+ margin: 0; | |
+ padding: 0 !important; | |
+} | |
+ | |
+div#heading { | |
+ background-color: #aab1c1; | |
+ padding: .5em; | |
+ margin: 0; | |
+} | |
+ | |
+div#heading ul { | |
+ display: compact; | |
+ margin: 0; | |
+ padding: 0; | |
+} | |
+ | |
+div#heading ul li { | |
+ display: inline; | |
+ padding-right: 1em; | |
+} | |
+ | |
+div#heading .heading-left { | |
+ margin-right: auto; | |
+ text-align: left; | |
+} | |
+ | |
+div#heading .heading-right { | |
+ text-align: right; | |
+ margin-left: auto; | |
+ margin-right: 0; | |
+} | |
+ | |
+div#heading a { | |
+} | |
+ | |
+div.cpus { | |
+ margin: 0.25em; | |
+ display: block; | |
+ padding: 10px; | |
+ margin: 10px; | |
+} | |
+ | |
+div.cpus div { | |
+ height: 20px; | |
+ display: table-cell; | |
+ padding: 0; | |
+ margin: 0; | |
+ border-top: 2px solid black; | |
+ border-bottom: 2px solid black; | |
+} | |
+ | |
+div.cpus .cpu0 { | |
+ background-color: yellow; | |
+ color: white; | |
+ border-left: 2px solid black; | |
+ | |
+} | |
+ | |
+div.cpus .cpu1 { | |
+ background-color: black; | |
+ color: green; | |
+ border-right: 2px solid black; | |
+} | |
+ | |
+.cpuMsg { | |
+ font-size: smaller; | |
+ white-space: pre; | |
} | |
h1 { | |
font-family: Georgia, "Times New Roman", Times, serif; | |
+ color: white; | |
+ text-shadow: 2px 2px 3px black; | |
+ margin-top: 0; | |
+ margin-left: 0; | |
+ margin-bottom: 0; | |
+ padding-left: 0.25em; | |
+ padding-right: 0.25em; | |
+} | |
+ | |
+ | |
+div#main { | |
+ margin: 1em; | |
+} | |
+ | |
+i#status { | |
+ position: fixed; | |
+ right: 0; | |
+ bottom: 0; | |
+ font-size: small; | |
} | |
div.group { | |
@@ -35,7 +120,7 @@ div.group { | |
background-color: #dfd; | |
} | |
.group li.up::before { | |
- content: '✓'; | |
+ content: '⬆'; | |
color: #0F0; | |
} | |
@@ -44,7 +129,7 @@ div.group { | |
color: white; | |
} | |
.group li.down::before { | |
- content: 'Ⓧ'; | |
+ content: '⬇'; | |
color: #F00; | |
} | |
@@ -75,6 +160,7 @@ div.group { | |
.group > li i.info { | |
margin-left: 0.5em; | |
+ font-size: smaller; | |
} | |
#hosts > li { | |
diff --git a/routes/index.js b/routes/index.js | |
index 87a0419..0681b9d 100644 | |
--- a/routes/index.js | |
+++ b/routes/index.js | |
@@ -3,11 +3,7 @@ | |
* GET home page. | |
*/ | |
-//var CONFIG = require("config").SurveyWatcher; | |
-var CONFIG = { | |
- ui: "(ui)", | |
- watcher: "(watcher)" | |
-}; | |
+var CONFIG = require("config").SurveyWatcher; | |
exports.index = function(req, res){ | |
res.render('index', { ui: CONFIG.ui, watcher: CONFIG.watcher } ); | |
diff --git a/routes/stwatcher.js b/routes/stwatcher.js | |
index 38e0181..78aae0c 100644 | |
--- a/routes/stwatcher.js | |
+++ b/routes/stwatcher.js | |
@@ -7,48 +7,78 @@ var os = require('os'); | |
var ping = require("ping"); | |
var url = require("url"); | |
var request = require("request"); | |
-var Cloudant = require('cloudant'); | |
-var gaas = require('gaas'); | |
-var VERBOSE = process.env.STWATCHER_VERBOSE || false; | |
+var orm = require("orm"); | |
+var CONFIG = require("config").SurveyWatcher; | |
+var VERBOSE = CONFIG.watcher.verbose || false; | |
// local data | |
var hosts = {}; | |
var uhosts = {}; | |
var servers = {}; | |
var hostname = os.hostname(); | |
-var appEnv = require('cfenv').getAppEnv(); | |
-var cloudantCred = appEnv.getServiceCreds(/.*[Cc]loudant.*/); | |
-if(cloudantCred == {}) throw "mising cloudant creds"; | |
-var appsToWatch = appEnv.getServices(); | |
-console.log("Setup stwatcher.. verbose="+VERBOSE); | |
+var host = (process.env.VCAP_APP_HOST || hostname); | |
+var port = process.env.VCAP_APP_PORT || CONFIG.port || process.env.PORT || 3000; | |
+console.log("hostport: " + host+"/"+hostname+":"+port); | |
-var matchInstances = /SurveyTool.*/; | |
+var vcap_services = undefined; | |
+var vcap_application = undefined; | |
-for ( var k in appsToWatch ) { | |
- if( ! matchInstances.test(k)) { | |
+if (process.env.VCAP_SERVICES) { | |
+ vcap_services = JSON.parse(process.env.VCAP_SERVICES); | |
+} | |
+if (process.env.VCAP_APPLICATION) { | |
+ // console.log("VCAP_APPLICATION: " + process.env.VCAP_APPLICATION); | |
+ /* | |
+2014-05-28T08:54:53.23-0700 [App/0] OUT VCAP_APPLICATION: {"limits":{"mem":768,"disk":1024,"fds":16384},"application_version":"76ffd510-4229-410c-ab6d-86a76ec66ec8","application_name":"stwatcher","application_uris":["stwatcher.ng.bluemix.net"],"version":"76ffd510-4229-410c-ab6d-86a76ec66ec8","name":"stwatcher","space_name":"dev","space_id":"0ac42d76-22c4-46e6-a9a6-a4323b9819f3","uris":["stwatcher.ng.bluemix.net"],"users":null,"instance_id":"65addb981b7946a39a2107adb61fea12","instance_index":0,"host":"0.0.0.0","port":63556,"started_at":"2014-05-28 15:54:48 +0000","started_at_timestamp":1401292488,"start":"2014-05-28 15:54:48 +0000","state_timestamp":1401292488} | |
+*/ | |
+ vcap_application = JSON.parse(process.env.VCAP_APPLICATION); | |
+} | |
+ | |
+console.log("Setup stwatcher.. verbose="+VERBOSE + " - have vcap services? " + (vcap_services!==undefined)); | |
+ | |
+var dbpath = undefined; | |
+var vcap_config = {}; | |
+ | |
+if(vcap_services !== undefined) { | |
+ console.log("Considering VCAP_SERVICES.."); | |
+ // http://www.ibm.com/developerworks/cloud/library/cl-bluemix-nodejs-app/index.html | |
+ // look for a service starting with 'mysql' | |
+ for(var serviceGroup in vcap_services) { | |
+ for(var k in vcap_services[serviceGroup]) { | |
+ var service = vcap_services[serviceGroup][k]; | |
+ console.log("VCAP service: `" + serviceGroup+"` . `"+service.name+"`"); | |
+ vcap_config[service.name] = service; | |
+ if(service.name == 'mysql') { | |
+ dbpath = service.credentials.uri; | |
+ console.log("DBPath found."); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+for ( var k in CONFIG.servers ) { | |
+ if(CONFIG.servers[k].skip) { | |
continue; | |
} | |
- var serviceInstance = appsToWatch[k]; | |
- if (!serviceInstance.credentials.url) throw "Service " + k + " didn't have credentials.url"; | |
- if (!serviceInstance.credentials.hostname) throw "Service " + k + " didn't have credentials.hostname"; | |
- | |
- var theUrl = serviceInstance.credentials.url; | |
- console.log("Considering " + k + " URL " + theUrl); | |
- if(!hosts[serviceInstance.credentials.hostname]) { | |
- hosts[serviceInstance.credentials.hostname] = { stealth: false, servers: {} }; | |
+ if(CONFIG.servers[k].url.substr(CONFIG.servers[k].url.length-1)!='/') { | |
+ CONFIG.servers[k].url = CONFIG.servers[k].url + '/'; // always end with / | |
} | |
- servers[k] = { host: serviceInstance.credentials.hostname }; | |
- hosts[serviceInstance.credentials.hostname].servers[k] = { url: theUrl }; // only URL. no other leak. | |
- | |
- if(serviceInstance.credentials.stealth) { | |
- hosts[serviceInstance.credentials.hostname].stealth = true; | |
+ var theurl = url.parse(CONFIG.servers[k].url); | |
+ console.log("Considering " + k + " URL " + CONFIG.servers[k].url); | |
+ if(!hosts[theurl.hostname]) { | |
+ hosts[theurl.hostname] = { stealth: false, servers: {} }; | |
+ } | |
+ servers[k] = { host: theurl.hostname }; | |
+ hosts[theurl.hostname].servers[k] = { url: CONFIG.servers[k].url }; // only URL. no other leak. | |
+ if(CONFIG.servers[k].stealth) { | |
+ hosts[theurl.hostname].stealth = true; | |
} | |
} | |
function st_notify(opts) { | |
- if(VERBOSE || true) console.log("Event: " + JSON.stringify(opts)); | |
- return; // ------------------------------------------------------------------------ | |
- | |
+ if(VERBOSE) console.log("Event: " + JSON.stringify(opts)); | |
+ | |
var notifyList = null; | |
if(opts.event == 'boot') { | |
@@ -84,10 +114,11 @@ function st_notify(opts) { | |
} | |
if(notify.kind == "email") { | |
+ if(!vcap_config.SendGrid) { | |
+ console.log('No sendgrid service..'); | |
+ } else { | |
+ var sendgrid = require("sendgrid")(vcap_config.SendGrid.credentials.username, vcap_config.SendGrid.credentials.password); | |
if(VERBOSE) console.log("Sending email notification " + notifyType);; | |
- var nodemailer = require("nodemailer"); | |
- var smtpTransport = nodemailer.createTransport(notify.mail_kind, | |
- notify.mail_opts); | |
var outopts = { | |
from: notify.from, | |
@@ -100,15 +131,14 @@ function st_notify(opts) { | |
('Details: ' + opts.details) + '\n' + | |
(notify.footer) + '\n' | |
}; | |
- | |
- smtpTransport.sendMail(outopts, function(error, response) { | |
+ sendgrid.send(outopts, function(error, response) { | |
if(error) { | |
- console.log(error); | |
+ console.log("Error sending: "+ error ); | |
} else { | |
- console.log("Sent: " + notifyType + " - " + response.message + " " + JSON.stringify(opts)); | |
+ console.log("Sent: " + notifyType + " - " + JSON.stringify(response) + " " + JSON.stringify(opts)); | |
} | |
- smtpTransport.close(); // no more messages | |
}); | |
+ } | |
} else if (false && notify.kind == "xmpp") { | |
var mynotify = notify; | |
if(!notify.xmpp) { | |
@@ -203,6 +233,14 @@ st_notify({ event: "boot", | |
}); | |
+if (dbpath === undefined) { | |
+ if(!CONFIG.watcher.dbpath) { | |
+ throw("No config/SurveyWatcher.watcher.dbpath - needs to be of the form mysql://username:password@host/database"); | |
+ } else { | |
+ dbpath = CONFIG.watcher.dbpath; | |
+ } | |
+} | |
+ | |
exports.latest = function(req,res) { | |
exports._latest(req,res); | |
} | |
@@ -220,35 +258,60 @@ exports._history = function(req,res) { | |
res.send("Not setup yet"); | |
} | |
-// patch this | |
-if ( ! cloudantCred.account ) { | |
- cloudantCred.account = cloudantCred.username; | |
-} | |
- | |
- var db; | |
- | |
-Cloudant(cloudantCred, function(err, cloudant) { | |
- if(err) { | |
- console.log("Error with database: " + err.toString()); | |
- throw err; | |
+orm.connect(dbpath, function (orm_err, db) { | |
+ if(orm_err) { | |
+ console.log("Error with database: " + orm_err.toString()); | |
+ throw orm_err; | |
} | |
- cloudant.db.create('st-watcher', function() { | |
- db = cloudant.db.use('st-watcher', true); | |
+ // setup DB stuff | |
+ var HostStatus = db.define("hoststatus", { | |
+ host : String, | |
+ when : Date, | |
+ alive : Boolean, | |
+ ns : Number | |
+ }, { | |
+ methods: { | |
+ }, | |
+ validations: { | |
+ } | |
+ }); | |
+ | |
+ var FetchStatus = db.define("fetchstatus", { | |
+ server : String, | |
+ when : Date, | |
+ ns : Number, | |
+ probation : Boolean, | |
+ isSetup : Boolean, | |
+ isBusted : Boolean, | |
+ users : Number, | |
+ guests : Number, | |
+ pages : Number, | |
+ dbused : Number, | |
+ mem : String, | |
+ load : String, | |
+ info : String, | |
+ busted : String, | |
+ uptime : String, | |
+ stamp : Date, // boot time, relatively | |
+ statusCode : Number | |
+ }, { | |
+ methods: { | |
+ }, | |
+ validations: { | |
+ } | |
+ }); | |
+ | |
+ db.sync(function (err) { | |
+ !err && console.log("db synced!"); | |
- function postHostStatus(anUpdate, cb) { | |
+ function postHostStatus(anUpdate) { | |
hosts[anUpdate.host].latestPing = anUpdate; | |
- db.insert(anUpdate, function(err, body, header){ | |
- if(!err) { | |
- //cb(null); | |
- //reply({ok: true, _rev: body.rev, _id: body.id}); | |
- } else { | |
- server.log('error', err); | |
- //reply({err: true}); | |
- //cb(err); | |
- } | |
- }); | |
+ HostStatus.create( [anUpdate], | |
+ function (err, items) { | |
+ if(err) throw(err); | |
+ }); | |
} | |
function postFetchStatus(anUpdate, json) { | |
@@ -286,13 +349,13 @@ Cloudant(cloudantCred, function(err, cloudant) { | |
}); | |
} | |
} | |
- db.insert(anUpdate, function(err,body,header) { | |
+ FetchStatus.create( [anUpdate], | |
+ function (err, items) { | |
if(err) throw(err); | |
- //newStatus.id = items[0].id; TODO: get _rev | |
+ newStatus.id = items[0].id; | |
servers[anUpdate.server].lastKnownStatus = newStatus; | |
- }); | |
+ }); | |
} | |
- | |
exports.poll = function() { | |
@@ -330,7 +393,7 @@ exports.poll = function() { | |
for(var j in hosts[k].servers) { | |
var server = hosts[k].servers[j]; | |
- var theurl = url.parse(server.url+'/'); | |
+ var theurl = url.parse(server.url); | |
var statusurl = url.format(url.resolve(theurl, 'SurveyAjax?what=status')); | |
if(VERBOSE) console.log("* " + statusurl); | |
@@ -374,7 +437,7 @@ exports.poll = function() { | |
record.users = body.status.users; | |
record.guests = body.status.guests; | |
record.mem = body.status.memfree + '/' + body.status.memtotal; | |
- record.info = body.status.phase + " " + body.status.newVersion + ' ' + body.status.environment; | |
+ record.info = body.status.currev + ";"+body.status.phase + " " + body.status.newVersion + ' ' + body.status.environment; | |
record.load = body.status.sysload + ' cpu='+body.status.sysprocs; | |
record.dbused = body.status.dbused; | |
record.uptime = body.status.uptime; // string, unfortunately | |
@@ -398,35 +461,37 @@ exports.poll = function() { | |
})(server,j)); | |
} | |
} | |
- | |
+ db.sync(function (err) { | |
+ if(err) throw(err); | |
+ //!err && console.log("db synced on update!"); | |
+ }); | |
return latestUpdate; | |
}; | |
- exports._pollsec = (3600); | |
- exports._probsec = (500); | |
+ exports._pollsec = (CONFIG.watcher.polltime || 3600); | |
+ exports._probsec = (CONFIG.watcher.probation_time || 500); | |
exports._interval = setInterval(exports.poll,exports._pollsec*1000); | |
// get the last time for each server | |
- //TODOTODO | |
-// for(var k in servers) { | |
-// (function(k){return function(){FetchStatus.find({server: k}, 1, ["when","Z"], function(err, stat) { | |
-// if(err) throw (err); | |
-// | |
-// if(stat && stat.length >0) { | |
-// var res = stat[0]; | |
-// var up = (res.isBusted==false && res.statusCode == 200); | |
-// servers[k].lastKnownStatus={ when: res.when, up: up, | |
-// probation: res.probation, | |
-// id: res.id }; | |
-// if(VERBOSE) console.log("SERVER " + k + " : up="+up); | |
-// } else { | |
-// if(VERBOSE) console.log("Not found: " + k); | |
-// } | |
-// });};})(k)(); | |
-// } | |
- | |
- //console.log("Polling every " + CONFIG.watcher.polltime + "s"); | |
+ for(var k in servers) { | |
+ (function(k){return function(){FetchStatus.find({server: k}, 1, ["when","Z"], function(err, stat) { | |
+ if(err) throw (err); | |
+ | |
+ if(stat && stat.length >0) { | |
+ var res = stat[0]; | |
+ var up = (res.isBusted==false && res.statusCode == 200); | |
+ servers[k].lastKnownStatus={ when: res.when, up: up, | |
+ probation: res.probation, | |
+ id: res.id }; | |
+ if(VERBOSE) console.log("SERVER " + k + " : up="+up); | |
+ } else { | |
+ if(VERBOSE) console.log("Not found: " + k); | |
+ } | |
+ });};})(k)(); | |
+ } | |
+ | |
+ console.log("Polling every " + CONFIG.watcher.polltime + "s"); | |
exports.poll(); // first time | |
// DB based now ready | |
@@ -447,21 +512,23 @@ exports.poll = function() { | |
//if(req.query.limit) { | |
//limit = Integer.parse(req.query.limit); | |
//} | |
- | |
- // TODO: IMP | |
- if(false) { | |
-// FetchStatus.find({server: server}, limit, ["when","Z"], function(err, stat) { | |
-// if(err) throw (err); | |
-// | |
-// if(stat && stat.length >0) { | |
-// res.send({ now: new Date().getTime(), server: server, data: stat }); | |
-// } else { | |
-// res.send({ now: new Date().getTime(), server: server, err: 'not found' }); | |
-// } | |
-// }); | |
- } | |
+ | |
+ FetchStatus.find({server: server}, limit, ["when","Z"], function(err, stat) { | |
+ if(err) throw (err); | |
+ | |
+ if(stat && stat.length >0) { | |
+ res.send({ now: new Date().getTime(), server: server, data: stat }); | |
+ } else { | |
+ res.send({ now: new Date().getTime(), server: server, err: 'not found' }); | |
+ } | |
+ }); | |
}; | |
}); | |
-}); | |
+// set up DB versions | |
+ | |
+}); // orm.connect | |
+ | |
+ | |
+ | |
diff --git a/views/index.jade b/views/index.jade | |
index b512edc..2df3264 100644 | |
--- a/views/index.jade | |
+++ b/views/index.jade | |
@@ -2,28 +2,29 @@ extends layout | |
block content | |
- h1= SurveyWatcher | |
- p Note - Charts are currently not working. Pardon the dust as we move to the new host. | |
- p Welcome to the SurveyWatcher | |
- hr | |
- div(id='statusdiv') | |
- i(id='status') Initializing | |
- div(id='SurveyWatcher') | |
- hr | |
- button(onclick='update()') (update) | |
- i(id='age') | |
- hr | |
- h2(id='chartTitle') Charts | |
- i(id='chartstatus') click a history link above to view charts | |
- div(id='SurveyChart') | |
- p (no chart loaded) | |
- p blue is load, black is user count. vertical red is outage. scroll wheel or Ctrl-"+" / Ctrl-"-" to zoom. dbl click to reset. | |
- p chart does not reload unless you click the history button again. | |
- hr | |
- p Note - Charts are currently not working. Pardon the dust as we move to the new host. | |
- hr | |
- i(id='powered') Powered by node.js #{process.version}, Express.js. Running on | |
- a(href='http://bluemix.net') IBM Bluemix | |
- i * | |
- i | |
- a(href='#{ui.mainurl}') Return | |
+ div(id='heading') | |
+ h1= ui.title | |
+ ul(class='heading-right') | |
+ li | |
+ a(href='http://cldr.unicode.org') Return to CLDR | |
+ li | |
+ a(href='http://cldr.unicode.org') Help | |
+ div(id='main') | |
+ div(id='statusdiv') | |
+ i(id='status') Initializing | |
+ div(id='SurveyWatcher') | |
+ hr | |
+ button(onclick='stw_update()') (update) | |
+ i(id='age') | |
+ hr | |
+ h2(id='chartTitle') Charts (broken right now, sorry) | |
+ i(id='chartstatus') click a history link above to view charts | |
+ div(id='SurveyChart') | |
+ p (no chart loaded) | |
+ p blue is load, black is user count. vertical red is outage. scroll wheel or Ctrl-"+" / Ctrl-"-" to zoom. dbl click to reset. | |
+ p chart does not reload unless you click the history button again. | |
+ hr | |
+ i(id='powered') Powered by node.js #{process.version}, Express.js | |
+ i * | |
+ i | |
+ a(href='#{ui.mainurl}') Return | |
diff --git a/views/layout.jade b/views/layout.jade | |
index 09dbc95..460d94f 100644 | |
--- a/views/layout.jade | |
+++ b/views/layout.jade | |
@@ -1,13 +1,15 @@ | |
doctype html | |
-html | |
+html(class='claro') | |
head | |
title= ui.title | |
+ link(rel='stylesheet', href='//ajax.googleapis.com/ajax/libs/dojo/1.9.3/dijit/themes/claro/claro.css') | |
link(rel='stylesheet', href='/stylesheets/style.css') | |
script(src='//ajax.googleapis.com/ajax/libs/dojo/1.9.3/dojo/dojo.js') | |
script window.default_stw_config = { reloadMode: false, jsonurl: '/latest.json', polltime: #{watcher.polltime} }; | |
script(src='/javascripts/stwatcher-client.js') | |
+ | |
- body | |
+ body(class='claro') | |
block content | |
//- block footer | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
re: http://unicode.org/cldr/trac/ticket/8515#comment:4