aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--web/index.html1
-rw-r--r--web/js/nms-types.js174
-rw-r--r--web/js/nms-ui-boxes.js2
-rw-r--r--web/js/nms-ui-switch.js125
4 files changed, 257 insertions, 45 deletions
diff --git a/web/index.html b/web/index.html
index 3e92dc8..acb0fbf 100644
--- a/web/index.html
+++ b/web/index.html
@@ -337,6 +337,7 @@
<script type="text/javascript" src="js/nms-template.js"></script>
<script type="text/javascript" src="js/nms-draw-chart.js"></script>
<script type="text/javascript" src="js/nms-ui-boxes.js"></script>
+ <script type="text/javascript" src="js/nms-types.js"></script>
<script type="text/javascript" src="js/nms-ui-switch.js"></script>
<script src="js/jquery.datetimepicker.full.js" type="text/javascript"></script>
<script type="text/javascript">
diff --git a/web/js/nms-types.js b/web/js/nms-types.js
new file mode 100644
index 0000000..254c00b
--- /dev/null
+++ b/web/js/nms-types.js
@@ -0,0 +1,174 @@
+"use strict";
+/*
+ * Base type. All nmsType* classes represent a logical family of units.
+ * This is used to provide input validation on the class-level and
+ * is used to store the value and description of variables.
+ * This is not bound to editing in theory. It is natural to think
+ * that nms-map-handlers will leverage the same type-system to
+ * verify that things are as they should.
+ *
+ * An example of future features outside the scope of simply editing stuff:
+ * verify that a nmsTypeIP is contained in nmsTypeNetwork
+ * use nmsTypePlace for all placement-logic
+ *
+ * Stuff that should be supported within an editor:
+ * - Validation (duh)
+ * - Hints (auto-complete, parse-error-feedback)
+ * - Creating a new network "on the fly" when adding a switch
+ * - Rich editing modes (e.g.: the placement-editing should
+ * allow you to just drag the switch and see the values change,
+ * and tags should obviously be smater too. And yeah, the snmp-
+ * thing should be an actual secret like it is today in
+ * nms-info-box
+ */
+class nmsType {
+ constructor(description, priority) {
+ this._value = null;
+ this.description = description;
+ this.validationReason = "";
+ if (priority == undefined) {
+ priority = this._defaultPriority;
+ }
+ this.priority = priority
+ }
+ // Override this to get free validation. Just return true or false.
+ validate(input) {
+ return true;
+ }
+ get value() {
+ return this._value;
+ }
+ _valueParse(input) {
+ return input;
+ }
+ set value(input) {
+ if (this.validate(input)) {
+ this._value = this._valueParse(input);
+ } else {
+ throw "Invalid input. Use .validate() before setting stuff please. I am " + this + " and my input is " + input
+ }
+ }
+ get _defaultPriority() {
+ return nmsPriority.optional;
+ }
+}
+
+class nmsTypeInterval extends nmsType {
+ validate(input) {
+ return !!(input.match(/^\d\d:\d\d:\d\d$/))
+ }
+}
+class nmsTypeIP extends nmsType {
+ get _defaultPriority() {
+ return nmsPriority.important;
+ }
+ _validateV4(input) {
+ var x = input.match(/^(\d+)\.(\d+).(\d+).(\d+)$/)
+ if (!x) {
+ this.validationReason = "Doesn't look like IPv4 address or IPv6";
+ return false;
+ }
+ for (var i = 1; i < 5; i ++) {
+ if (x[i] < 0 || x[i] > 255) {
+ this.validationReason = "The " + i + "'th octet("+x[i]+") is outside of expected range (0-255)"
+ return false
+ }
+ }
+ this.validationReason = "OK"
+ return true;
+ }
+ /* It's so easy to check if IPv6 addresses are valid.
+ */
+ _validateV6(input) {
+ if (!!input.match(/^[a-fA-F0-9:]+$/)) {
+ this.validationReason = "OK IPv6 address"
+ return true;
+ } else {
+ this.validationReason = "Doesn't parse as a IPv6 address despite :";
+ return false;
+ }
+ }
+ validate(input) {
+ if (input.match(":")) {
+ return this._validateV6(input);
+ } else {
+ return this._validateV4(input);
+ }
+ }
+}
+class nmsTypeNetwork extends nmsType {
+ get _defaultPriority() {
+ return nmsPriority.important;
+ }
+ validate(input) {
+ var x =testTree(nmsData,["networks","networks",input])
+ if (x) {
+ this.validationReason = "OK"
+ } else {
+ this.validationReason = "No such network: " + input
+ }
+ return x;
+ }
+}
+class nmsTypeJSON extends nmsType {
+ get value() {
+ if (this._value == null || this._value == undefined) {
+ return "";
+ }
+ return JSON.stringify(this._value)
+ }
+ set value(input) {
+ super.value = input;
+ }
+ /* This should probably actually know the basic template
+ * for a placement-object...
+ */
+ validate(input) {
+ try {
+ this._valueParse(input);
+ this.validationReason = "OK"
+ return true;
+ } catch(e) {
+ this.validationReason = e.message;
+ return false;
+ }
+ }
+ _valueParse(input) {
+ if (input instanceof Object) {
+ return input;
+ } else {
+ return JSON.parse(input);
+ }
+ }
+}
+class nmsTypePlace extends nmsTypeJSON { }
+class nmsTypePort extends nmsType { }
+class nmsTypeSysname extends nmsType {
+ get _defaultPriority() {
+ return nmsPriority.newOnly;
+ }
+}
+class nmsTypeSysnameReference extends nmsType {
+ get _defaultPriority() {
+ return nmsPriority.important;
+ }
+ validate(input) {
+ var x = testTree(nmsData,["switches","switches",input])
+ if (x) {
+ this.validationReason = "OK"
+ } else {
+ this.validationReason = "No such switch: " + input
+ }
+ return x;
+ }
+}
+class nmsTypeTags extends nmsTypeJSON { }
+class nmsTypeSecret extends nmsType { }
+
+var nmsPriority = {
+ readOnly: 0,
+ required: 1,
+ important: 2,
+ optional: 3,
+ newOnly: 4
+}
diff --git a/web/js/nms-ui-boxes.js b/web/js/nms-ui-boxes.js
index 3ed65cd..ac4445e 100644
--- a/web/js/nms-ui-boxes.js
+++ b/web/js/nms-ui-boxes.js
@@ -166,7 +166,7 @@ class nmsPanel extends nmsBox{
/* Mainly just to make the constructor more readable. */
makeHeading(title) {
var titleObject = new nmsBox("div",{html:{classList: ["panel-heading"]}});
- this._titleText = new nmsBox("p",{html:{textContent: title}});
+ this._titleText = new nmsBox("h4",{html:{textContent: title}});
var closeButton = new nmsBox("button");
closeButton.html.className = "close";
closeButton.panel = this;
diff --git a/web/js/nms-ui-switch.js b/web/js/nms-ui-switch.js
index fe4bbe9..3e6dd47 100644
--- a/web/js/nms-ui-switch.js
+++ b/web/js/nms-ui-switch.js
@@ -1,6 +1,16 @@
"use strict";
-class nmsUiSwitch extends nmsPanel {
+/* Basic editor for switches, and possibly networks and whatever.
+ * This is the first real use of both the nmsBox and nmsType, so
+ * expect changes as the need(s) arise.
+ *
+ * The general idea is simple, though: Editing and adding is to be treated
+ * as similar as possible, and there should be no hard-coding anywhere. If
+ * we need a "one-off" for whatever, we should find a genric way of solving
+ * it to avoid complicating things.
+ *
+ */
+class nmsModSwitch extends nmsPanel {
constructor(sw) {
var title;
if (sw == undefined) {
@@ -10,23 +20,50 @@ class nmsUiSwitch extends nmsPanel {
}
super(title)
this._sw = sw;
+ this.nav.add(new nmsString("Adding and editing stuff has immediate effects and blah blah blah, insert sensible help-text here."));
+ this.generateBaseTemplate()
this.populate()
}
- /*
- * We really should base this on a backend-API exposing relevant fields...
+ /* Pretty sure that the type-thing is OK, but what I want is to
+ * generate a nmsTemplate or something that can be used to get/set
+ * variables generically, and replaces nmsEditRow. Since right now,
+ * both the template and the row is fiddling with values, luckily
+ * all through the same actual object, but still....
+ * This is because I wrote nmsEditRow before I added a type-system.
+ *
+ * The fundamental problem is that the row-rendering is obviously
+ * affected by the type, and the overall "template"/parent
+ * (nmsModSwitch right now) is also obviously affected by changes to
+ * the individual rows.
+ *
+ * Right now a change in a value means nmsEditRow will get the
+ * event, it will use the nmsType to validate, and ultimately set,
+ * but it ALSO has to store a text-representation of the value if it
+ * changes from other sources (e.g.: auto-complete), and we need to
+ * alert nmsModSwitch that a change has occurred so it can act
+ * approrpiately (e.g.: Enabling/disabling a save button).
+ *
+ * This means that nmsType instances, nmsEditRow instances and
+ * nmsModSwitch instance is tightly coupled in non-obvious ways.
+ *
+ * Which means bugs.
*/
- getTemplate(sw) {
- if (sw == undefined) {
- return {
- mgmt_v4_addr: null,
- mgmt_v6_addr: null,
- community: null,
- placement: null,
- mgmt_vlan: null,
- poll_frequency: null,
- tags: null
- };
- }
+ generateBaseTemplate() {
+ this._template = {
+ sysname: new nmsTypeSysname("Unique systemname/switch name. Only required field." ),
+ mgmt_v4_addr: new nmsTypeIP("Management address IPv4"),
+ mgmt_v6_addr: new nmsTypeIP("Management address IPv6"),
+ mgmt_vlan: new nmsTypeNetwork("Management VLAN"),
+ traffic_vlan: new nmsTypeNetwork("Traffic VLAN"),
+ distro_name: new nmsTypeSysnameReference("Distro switch upstream of this system. Required for provisioning."),
+ distro_phy_port: new nmsTypePort("Name of port we connect to at the distro switch. Used for provisioning, among other things."),
+ poll_frequency: new nmsTypeInterval("Poll frequency for SNMP (will use default from backend)"),
+ community: new nmsTypeSecret("SNMP community (will use default from backend)"),
+ placement: new nmsTypePlace("Map placement (If following a regular naming scheme, the backend will place it poperly, otherwise a random place will be chose)"),
+ tags: new nmsTypeTags("Additional tags in JSON text array format. Can be anything. Used to provide a simple escape hatch mechanism to tag systems.")
+ }
+ }
+ _populateTemplate(sw) {
var swi = [];
var swm = [];
try {
@@ -38,32 +75,25 @@ class nmsUiSwitch extends nmsPanel {
var template = {}
for (var v in swi) {
- template[v] = swi[v];
+ console.assert(this._template[v] instanceof nmsType)
+ this._template[v].value = swi[v];
}
for (var v in swm) {
if (v == "last_updated") {
continue;
}
- template[v] = swm[v];
+ console.assert(this._template[v] instanceof nmsType)
+ this._template[v].value = swm[v];
}
- return template;
}
populate() {
- var template = this.getTemplate(this._sw);
- this.table = new nmsTable();
- var first = new Array("sysname","distro_name","distro_phy_port","traffic_vlan")
- var sorted = new Array();
- for (var v in template) {
- if (!first.includes(v)) {
- sorted.push(v);
- }
+ if (this._sw != undefined) {
+ this._populateTemplate(this._sw);
}
- sorted.sort();
- var finals = first.concat(sorted);
+ this.table = new nmsTable();
this.rows = {}
- for (var i in finals) {
- var v = finals[i];
- this.rows[v] = new nmsEditRow(v, nmsInfoBox._nullBlank(template[v]));
+ for (var v in this._template) {
+ this.rows[v] = new nmsEditRow(v, this._template[v]);
this.rows[v].parent = this;
this.table.add(this.rows[v]);
}
@@ -81,19 +111,19 @@ class nmsEditRow extends nmsBox {
constructor(text,value) {
super("tr")
// This should/could be smarter in the future.
- if (value instanceof Object) {
- value = JSON.stringify(value);
- }
+ console.assert(value instanceof nmsType)
this.name = text;
this._value = value;
- this.original = value;
+ this.original = value.value;
var td1 = new nmsBox("td")
- td1.add(new nmsString(text))
+ var name = new nmsString(text);
+ name.html.title = value.description;
+ td1.add(name)
this.add(td1);
var td2 = new nmsBox("td")
var input = new nmsBox("input")
- input.html.value = value;
+ input.html.value = value.value;
input.html.className = "form-control";
input.html.type = "text";
input.row = this;
@@ -109,17 +139,24 @@ class nmsEditRow extends nmsBox {
this.add(td2)
}
get value() {
- return this._value;
+ return this._value.value;
}
+ /* THIS IS A MESS */
set value(value) {
- this._value = value;
- if (this._input.html.value != value) {
- this._input.html.value = value
+ if (!this._value.validate(value)) {
+ this._td2.html.classList.add("has-error");
+ return;
+ } else {
+ this._td2.html.classList.remove("has-error");
+ this._value.value = value;
+ }
+ if (this._input.html.value != this._value.value) {
+ this._input.html.value = this._value.value
}
- if (this._value != this.original) {
- this._td2.html.classList.add("has-warning");
+ if (this._value.value != this.original) {
+ this._td2.html.classList.add("has-success");
} else {
- this._td2.html.classList.remove("has-warning");
+ this._td2.html.classList.remove("has-success");
}
this.parent.changed(this)
}