summaryrefslogtreecommitdiff
path: root/src/pmwebapi/jsdemos/blinkenlights
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmwebapi/jsdemos/blinkenlights
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmwebapi/jsdemos/blinkenlights')
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/GNUmakefile33
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/blinken_error.pngbin0 -> 7879 bytes
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/blinken_off.pngbin0 -> 6324 bytes
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/blinken_on.pngbin0 -> 6360 bytes
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/blinkenlights.css19
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/blinkenlights.js170
-rw-r--r--src/pmwebapi/jsdemos/blinkenlights/index.html129
7 files changed, 351 insertions, 0 deletions
diff --git a/src/pmwebapi/jsdemos/blinkenlights/GNUmakefile b/src/pmwebapi/jsdemos/blinkenlights/GNUmakefile
new file mode 100644
index 0000000..a2ba409
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/GNUmakefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2013 Red Hat. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+
+TOPDIR = ../../../..
+include $(TOPDIR)/src/include/builddefs
+
+IMAGES = blinken_error.png blinken_off.png blinken_on.png
+JSFILES = blinkenlights.js
+CSSFILES = blinkenlights.css
+HTMLFILES = index.html
+
+LSRCFILES = $(IMAGES) $(JSFILES) $(CSSFILES) $(HTMLFILES)
+
+default:
+
+include $(BUILDRULES)
+
+install:
+
+default_pcp: default
+
+install_pcp: install
diff --git a/src/pmwebapi/jsdemos/blinkenlights/blinken_error.png b/src/pmwebapi/jsdemos/blinkenlights/blinken_error.png
new file mode 100644
index 0000000..556bd3a
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/blinken_error.png
Binary files differ
diff --git a/src/pmwebapi/jsdemos/blinkenlights/blinken_off.png b/src/pmwebapi/jsdemos/blinkenlights/blinken_off.png
new file mode 100644
index 0000000..abb5c1d
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/blinken_off.png
Binary files differ
diff --git a/src/pmwebapi/jsdemos/blinkenlights/blinken_on.png b/src/pmwebapi/jsdemos/blinkenlights/blinken_on.png
new file mode 100644
index 0000000..8d85f61
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/blinken_on.png
Binary files differ
diff --git a/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.css b/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.css
new file mode 100644
index 0000000..c99ceff
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.css
@@ -0,0 +1,19 @@
+#interval-slider-label { margin: 0px; }
+#interval-slider { width: 300px; margin: 0px; display: inline-block; }
+
+#blinkenlights { font-family: monospace; font-size: 130%; }
+#blinkenlights li {
+ list-style: none; line-height: 25px;
+ background-repeat: no-repeat;
+ background-size: 25px 25px;
+ padding-left: 32px; margin-top: 5px;
+}
+
+/* public domain images courtesy of
+ - http://www.clker.com/clipart-led-off.html
+ - http://www.clker.com/clipart-6514.html
+ XXX and related images from the same site
+ */
+#blinkenlights li.on { background-image: url(blinken_on.png); }
+#blinkenlights li.off { background-image: url(blinken_off.png); }
+#blinkenlights li.error { background-image: url(blinken_error.png); }
diff --git a/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.js b/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.js
new file mode 100644
index 0000000..03bb315
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/blinkenlights.js
@@ -0,0 +1,170 @@
+var pm_root = "http://" + location.hostname + ":" + location.port + "/pmapi";
+var pm_context = -1;
+
+// ----------------------------------------------------------------------
+
+function Predicate(name,index,operator,threshold) {
+ this.name = name;
+ this.index = index;
+ this.operator = operator;
+ this.threshold = threshold;
+ this.inames = {};
+}
+
+Predicate.prototype.to_string = function() {
+ return this.name + "[" + this.index + "] "
+ + this.operator + " " + this.threshold;
+};
+
+Predicate.prototype.get_iname = function(iid) {
+ if (!(iid in this.inames)) {
+ var pm_url = pm_root + "/" + pm_context + "/_indom?name=" + this.name + "&instance=" + iid;
+ var predicate = this;
+ $.getJSON(pm_url, function(data, status) {
+ // TODOXXX error check: should return 1 instance
+ predicate.inames[iid] = data.instances[0].name;
+ });
+
+ return "..."; // will be reloaded next cycle
+ }
+
+ return this.inames[iid];
+}
+
+Predicate.prototype.test = function(elt,data_dict,index) {
+ if (this.index == "*" && typeof(index) == "undefined") {
+ var predicate = this;
+ $.each(data_dict[this.name].instances, function(i,_instance) {
+ predicate.test(elt,data_dict,i);
+ });
+ return;
+ }
+
+ if (typeof(index) == "undefined") index = this.index;
+
+ var metric = data_dict[this.name].instances[index].value;
+ var iid = data_dict[this.name].instances[index].instance;
+ var result = 0, error = "";
+ if (this.operator == "<") result = metric < this.threshold;
+ else if (this.operator == ">") result = metric > this.threshold;
+ else if (this.operator == "<=") result = metric <= this.threshold;
+ else if (this.operator == ">=") result = metric >= this.threshold;
+ else if (this.operator == "==") result = metric == this.threshold;
+ else { error = "unknown operator '" + this.operator + "'"; result = -1; }
+
+ // TODOXXX avoid $("#blinkenlights").empty() by using existing li's??
+ var bclass = result < 0 ? "error" : result ? "on" : "off";
+
+ var source = "<strong>" + metric + "</strong> -- "
+ + this.name + " ( " + this.get_iname(iid) + " : " + iid + " ) "
+ + this.operator + " " + this.threshold;
+ var content = "<span>" + source + "</span>"
+ + (result < 0 ? " -- error: " + error : "");
+
+ elt.append("<li class=\"" + bclass + "\">" + content + "</li>");
+};
+
+var predicates = [];
+
+function parsePredicate(src) {
+ var matches = /^([^[ ]+)\s*(\[\d+\]|\[\*\]|\[\]|)\s*(<=|>=|==|<|>)\s*(\S*)$/.exec(src);
+ if (matches == null) return null;
+ var name = matches[1];
+ var index = matches[2]; index = index == "" ? "*" : index.substring(1,index.length-1);
+ var operator = matches[3];
+ var threshold = parseFloat(matches[4]); // TODOXXX what about other types?; accepts 40foobar
+ if (isNaN(threshold)) return null;
+
+ console.log ("create predicate " + name + " : " + index + " : " + operator + " : " + threshold)
+
+ return new Predicate(name,index,operator,threshold);
+}
+
+// ----------------------------------------------------------------------
+
+var updateInterval = 10000; // milliseconds
+var updateIntervalID = 1;
+
+function setUpdateInterval(i) {
+ if (updateIntervalID >= 0) { clearInterval(updateIntervalID); }
+ if (i > updateInterval) { pm_context = -1; } // will likely need a new context
+ updateInterval = i;
+ updateIntervalID = setInterval(updateBlinkenlights, updateInterval);
+}
+
+// default mode
+var pm_context_type = "hostspec";
+var pm_context_spec = "localhost";
+
+function setPMContextType(k) {
+ pm_context_type = k;
+ pm_context = -1;
+ updateBlinkenlights();
+}
+function setPMContextSpec(i) {
+ pm_context_spec = i;
+ pm_context = -1;
+ updateBlinkenlights();
+}
+
+
+function updateBlinkenlights() {
+ var pm_url;
+
+ if (pm_context < 0) {
+ pm_url = pm_root
+ + "/context?"
+ + pm_context_type + "=" + encodeURIComponent(pm_context_spec)
+ + "&polltimeout=" + Math.floor(5*updateInterval/1000);
+ $.getJSON(pm_url, function(data, status) {
+ pm_context = data.context;
+ setTimeout(updateBlinkenlights, 100); // retry soon
+ }).error(function() { pm_context = -1; });
+ return; // will retry one cycle later
+ }
+
+ if(predicates.length == 0) {
+ $("#blinkenlights").html("<b>No predicates requested...</b>");
+ return;
+ }
+
+ // ajax request for JSON data
+ pm_url = pm_root + "/" + pm_context + "/_fetch?names=";
+ $.each(predicates, function(i, predicate) {
+ if (i > 0) pm_url += ",";
+ pm_url += predicate.name;
+ });
+ $.getJSON(pm_url, function(data, status) {
+ // update data_dict
+ var data_dict = {};
+ $.each(data.values, function(i, value) {
+ data_dict[value.name] = value;
+ });
+ // update status field
+ theDate = new Date(0);
+ theDate.setUTCSeconds(data.timestamp.s);
+ theDate.setUTCMilliseconds(data.timestamp.us/1000);
+ $("#status").html("Timestamp: " + theDate.toString());
+ // update the view
+ $("#blinkenlights").empty();
+ $.each(predicates, function(i, predicate) {
+ predicate.test($("#blinkenlights"), data_dict);
+ });
+ }).error(function() {
+ $("#blinkenlights").html("<b>error accessing server, retrying...</b>");
+ pm_context = -1; });
+}
+
+function loadBlinkenlights() {
+ $("#header").html("pcp blinkenlights demo");
+ $("#content").html("<ul id=\"blinkenlights\"><li>loading...</li></ul>");
+
+ // start timer for updateBlinkenlights
+ updateBlinkenlights();
+ setUpdateInterval(updateInterval);
+}
+
+$(document).ready(function() {
+ loadBlinkenlights();
+ // TODOXXX add support for editing mode
+});
diff --git a/src/pmwebapi/jsdemos/blinkenlights/index.html b/src/pmwebapi/jsdemos/blinkenlights/index.html
new file mode 100644
index 0000000..cf2e4fd
--- /dev/null
+++ b/src/pmwebapi/jsdemos/blinkenlights/index.html
@@ -0,0 +1,129 @@
+<html>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <head>
+ <link rel="stylesheet" href="../jquery-ui-themes-1.10.2/themes/humanity/jquery-ui.css">
+ <script type="text/javascript" src="../jquery-1.7.2.js"></script>
+ <script type="text/javascript" src="../jquery-ui-1.10.2.js"></script>
+ <script type="text/javascript" src="blinkenlights.js"></script>
+ <script type="text/javascript">
+ /* Query string handling */
+ var qs = (function(a) {
+ if (a == "") return {};
+ var b = {};
+ for (var i = 0; i < a.length; ++i)
+ {
+ var p=a[i].split('=');
+ if (p.length != 2) continue;
+ b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
+ }
+ return b;
+ })(window.location.search.substr(1).split('&'));
+
+ if ("predicates" in qs) {
+ raw_predicates = qs["predicates"].split('@');
+ for (var i = 0; i < raw_predicates.length; i++) {
+ if (raw_predicates[i] == "") continue;
+ p = parsePredicate(raw_predicates[i]);
+ if (p == null) { alert("cannot parse " + predicates[i]); }
+ else { predicates.push(p); }
+ }
+ } else {
+ p = parsePredicate("kernel.all.load[*] > 0.2");
+ if (p == null) { alert("cannot parse " + predicates[i]); }
+ else { predicates.push(p); }
+ }
+
+ if ("contexttype" in qs) {
+ pm_context_type=qs["contexttype"];
+ }
+
+ if ("contextspec" in qs) {
+ pm_context_spec=qs["contextspec"];
+ }
+
+ if ("updateInterval" in qs) {
+ updateInterval = qs["updateInterval"];
+ }
+
+ function saveQueryString() {
+ raw_predicates = [];
+ for (var i = 0; i < predicates.length; i++)
+ raw_predicates.push(predicates[i].to_string());
+ str = $.param({
+ predicates: raw_predicates.join('@'),
+ updateInterval: updateInterval,
+ contexttype: pm_context_type,
+ contextspec: pm_context_spec
+ });
+ str = location.protocol + "//" + location.hostname + ":" + location.port + location.pathname + "?" + str; // TODOXXX
+ $("#permalink_uri").html(str);
+ $("#permalink_uri").attr("href", str);
+ }
+
+ /* Set up document */
+ $(document).ready(function() {
+ $("#predicate_form").submit(function() {
+ pstr = $("#predicate_input").val();
+ p = parsePredicate(pstr);
+ if (p == null) { alert("cannot parse " + pstr); } else { predicates.push(p); }
+ saveQueryString();
+ return false; // stay on this page
+ });
+ $("#predicate_clear_form").submit(function() {
+ predicates = [];
+ saveQueryString();
+ return false; // stay on this page
+ });
+ $("#interval").val(updateInterval);
+ $("#interval-slider").slider({
+ value: updateInterval, min: 1000, max: 10000,
+ slide: function (event, ui) {
+ setUpdateInterval(ui.value);
+ saveQueryString();
+ $("#interval").val(updateInterval);
+ }
+ });
+ $("#contexttype").val(pm_context_type);
+ $("#contextspec").val(pm_context_spec);
+ $("#contextspec_form").submit(function() {
+ setPMContextType($("#contexttype").val());
+ setPMContextSpec($("#contextspec").val());
+ saveQueryString();
+ return false; // stay on this page
+ });
+ $("#predicate_input").val("kernel.all.load[*] > 0.2");
+ saveQueryString(); // set up initial permalink
+ });
+ </script>
+ <link rel="stylesheet" type="text/css" href="blinkenlights.css" />
+ <title>pcp blinkenlights demo</title>
+ </head>
+ <body>
+ <h1 id="header"></h1>
+ <form id="contextspec_form">
+ <select id="contexttype">
+ <option>hostspec</option>
+ <option>archivefile</option>
+ <option>local</option>
+ </select>
+ <input type="text" size="50" id="contextspec" />
+ <input value="Connect" type="submit" />
+ </form>
+ <form id="predicate_form">
+ <input id="predicate_input" size="50" type="text" />
+ <input value="Add Predicate" type="submit" />
+ </form>
+ <form id="predicate_clear_form">
+ <input value="Clear Predicates" type="submit" />
+ </form>
+ <span id="interval-slider-label"><label for="interval">Refresh interval (ms):</label><input type="text" size="5" id="interval" readonly /></span>
+ <div id="interval-slider"></div>
+ <p id="status"></p>
+ <div id="content"></div>
+ <p id="permalink">permalink: <a href="#" id="permalink_uri"></a></p>
+ <!--<footer>
+ <p id="permalink">permalink: <span></span> (<a>copy</a>)</p>
+ <p id="editlink"></p>
+ </footer>-->
+ </body>
+</html>