Added webservices forwarding script for UI
diff --git a/web/js/models/flowmodel.js b/web/js/models/flowmodel.js
new file mode 100644
index 0000000..80777c3
--- /dev/null
+++ b/web/js/models/flowmodel.js
@@ -0,0 +1,37 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Flow = Backbone.Model.extend({
+
+ defaults: {
+ receiveBytes: 0,
+ receivePackets: 0,
+ transmitBytes: 0,
+ transmitPackets: 0,
+ },
+
+ // initialize:function () {}
+
+});
+
+window.FlowCollection = Backbone.Collection.extend({
+
+ model:Flow,
+
+ // instead of the collection loading its children, the switch will load them
+ // initialize:function () {}
+
+});
\ No newline at end of file
diff --git a/web/js/models/hostmodel.js b/web/js/models/hostmodel.js
new file mode 100644
index 0000000..8de3dd6
--- /dev/null
+++ b/web/js/models/hostmodel.js
@@ -0,0 +1,77 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Host = Backbone.Model.extend({
+
+ defaults: {
+ // vlan: -1,
+ lastSeen: 'never',
+ ip: ' ',
+ swport: ' ',
+ },
+
+ // initialize:function () {}
+
+});
+
+window.HostCollection = Backbone.Collection.extend({
+
+ model:Host,
+
+ fetch:function () {
+ var self = this;
+ //console.log("fetching host list")
+ $.ajax({
+ url:hackBase + "/wm/device/",
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched host list: " + data.length);
+ // console.log(data);
+ // data is a list of device hashes
+ var old_ids = self.pluck('id');
+ //console.log("old_ids" + old_ids);
+ _.each(data, function(h) {
+ h.id = h.mac[0];
+ old_ids = _.without(old_ids, h.id);
+ if (h['attachmentPoint'].length > 0) {
+ h.swport = _.reduce(h['attachmentPoint'], function(memo, ap) {
+ return memo + ap.switchDPID + "-" + ap.port + " "}, "");
+ //console.log(h.swport);
+ h.lastSeen = new Date(h.lastSeen).toLocaleString();
+ self.add(h, {silent: true});
+ }
+ });
+ // old_ids now holds hosts that no longer exist; remove them
+ //console.log("old_ids" + old_ids);
+ _.each(old_ids, function(h) {
+ console.log("---removing host " + h);
+ self.remove({id:h});
+ });
+ self.trigger('add'); // batch redraws
+ }
+ });
+
+ },
+
+ /*
+ * findByName:function (key) { // TODO: Modify service to include firstName
+ * in search var url = (key == '') ? '/host/' : "/host/search/" + key;
+ * console.log('findByName: ' + key); var self = this; $.ajax({ url:url,
+ * dataType:"json", success:function (data) { console.log("search success: " +
+ * data.length); self.reset(data); } }); }
+ */
+
+});
diff --git a/web/js/models/portmodel.js b/web/js/models/portmodel.js
new file mode 100644
index 0000000..563f334
--- /dev/null
+++ b/web/js/models/portmodel.js
@@ -0,0 +1,42 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Port = Backbone.Model.extend({
+
+ defaults: {
+ name: '',
+ receiveBytes: 0,
+ receivePackets: 0,
+ transmitBytes: 0,
+ transmitPackets: 0,
+ dropped: 0,
+ errors: 0,
+ },
+
+ initialize:function () {
+ // TODO hook up associated hosts
+ }
+
+});
+
+window.PortCollection = Backbone.Collection.extend({
+
+ model:Port,
+
+ // instead of the collection loading its children, the switch will load them
+ initialize:function () {}
+
+});
\ No newline at end of file
diff --git a/web/js/models/statusmodel.js b/web/js/models/statusmodel.js
new file mode 100644
index 0000000..b7cdebd
--- /dev/null
+++ b/web/js/models/statusmodel.js
@@ -0,0 +1,73 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Status = Backbone.Model.extend({
+ defaults: {
+ host: 'localhost',
+ ofport: 6633,
+ uptime: 'unknown',
+ free: 0,
+ total: 0,
+ healthy: 'unknown',
+ modules: [],
+ moduleText: ''
+ },
+
+ initialize:function () {
+ var self = this;
+ console.log("fetching controller status");
+ $.ajax({
+ url:hackBase + "/wm/core/health/json",
+ dataType:"json",
+ success:function (data) {
+ console.log("fetched controller status: health");
+ self.set(data);
+ // console.log(self.toJSON());
+ }
+ });
+ $.ajax({
+ url:hackBase + "/wm/core/system/uptime/json",
+ dataType:"json",
+ success:function (data) {
+ console.log("fetched controller status: uptime");
+ self.set({uptime:(Math.round(data.systemUptimeMsec / 1000) + ' s')});
+ // console.log(self.toJSON());
+ }
+ });
+ $.ajax({
+ url:hackBase + "/wm/core/memory/json",
+ dataType:"json",
+ success:function (data) {
+ console.log("fetched controller status: memory");
+ self.set(data);
+ // console.log(self.toJSON());
+ }
+ });
+ $.ajax({
+ url:hackBase + "/wm/core/module/loaded/json",
+ dataType:"json",
+ success:function (data) {
+ console.log("fetched controller status: modules loaded");
+ // console.log(data);
+ self.set({modules:_.keys(data)});
+ self.set({moduleText:_.reduce(_.keys(data), function(s, m)
+ {return s+m.replace("net.floodlightcontroller", "n.f")+", "}, '')});
+ }
+ });
+
+ }
+
+});
\ No newline at end of file
diff --git a/web/js/models/switchmodel.js b/web/js/models/switchmodel.js
new file mode 100644
index 0000000..4104dd0
--- /dev/null
+++ b/web/js/models/switchmodel.js
@@ -0,0 +1,295 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Switch = Backbone.Model.extend({
+
+ urlRoot:"/wm/core/switch/",
+
+ defaults: {
+ datapathDescription: '',
+ hardwareDescription: '',
+ manufacturerDescription: '',
+ serialNumber: '',
+ softwareDescription: '',
+ flowCount: ' ',
+ packetCount: ' ',
+ byteCount: ' ',
+ },
+
+ initialize:function () {
+ var self = this;
+
+ //console.log("fetching switch " + this.id + " desc")
+ $.ajax({
+ url:hackBase + "/wm/core/switch/" + self.id + '/desc/json',
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch " + self.id + " desc");
+ //console.log(data[self.id][0]);
+ self.set(data[self.id][0]);
+ }
+ });
+
+ //console.log("fetching switch " + this.id + " aggregate")
+ $.ajax({
+ url:hackBase + "/wm/core/switch/" + self.id + '/aggregate/json',
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch " + self.id + " aggregate");
+ //console.log(data[self.id][0]);
+ self.set(data[self.id][0]);
+ }
+ });
+ self.trigger('add');
+ this.ports = new PortCollection();
+ this.flows = new FlowCollection();
+ //this.loadPorts();
+ //this.loadFlows();
+ },
+
+ fetch:function () {
+ this.initialize()
+ },
+
+ loadPorts:function () {
+ var self = this;
+ //console.log("fetching switch " + this.id + " ports")
+ //console.log("fetching switch " + this.id + " features")
+ $.when($.ajax({
+ url:hackBase + "/wm/core/switch/" + self.id + '/port/json',
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch " + self.id + " ports");
+ //console.log(data[self.id]);
+ var old_ids = self.ports.pluck('id');
+ //console.log("old_ids" + old_ids);
+
+ // create port models
+ _.each(data[self.id], function(p) {
+ // workaround for REST serialization signed/unsigned bug
+ if(p.portNumber < 0) {p.portNumber = 65536 + p.portNumber};
+
+ p.id = self.id+'-'+p.portNumber;
+ old_ids = _.without(old_ids, p.id);
+ p.dropped = p.receiveDropped + p.transmitDropped;
+ p.errors = p.receiveCRCErrors + p.receiveErrors + p.receiveOverrunErrors +
+ p.receiveFrameErrors + p.transmitErrors;
+ // this is a knda kludgy way to merge models
+ var m = self.ports.get(p.id);
+ if(m) {
+ m.set(p, {silent: true});
+ } else {
+ self.ports.add(p, {silent: true});
+ }
+ //console.log(p);
+ });
+
+ // old_ids now holds ports that no longer exist; remove them
+ //console.log("old_ids" + old_ids);
+ _.each(old_ids, function(p) {
+ console.log("removing port " + p);
+ self.remove({id:p});
+ });
+ }
+ }),
+ $.ajax({
+ url:hackBase + "/wm/core/switch/" + self.id + '/features/json',
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch " + self.id + " features");
+ //console.log(data[self.id]);
+ // update port models
+ _.each(data[self.id].ports, function(p) {
+ p.id = self.id+'-'+p.portNumber;
+ if(p.name != p.portNumber) {
+ p.name = p.portNumber + ' (' + p.name + ')';
+ }
+ p.status = '';
+ p.status += (p.state & 1) ? 'DOWN' : 'UP';
+ switch(p.currentFeatures & 0x7f) {
+ case 1:
+ p.status += ' 10 Mbps';
+ break;
+ case 2:
+ p.status += ' 10 Mbps FDX';
+ break;
+ case 4:
+ p.status += ' 100 Mbps';
+ break;
+ case 8:
+ p.status += ' 100 Mbps FDX';
+ break;
+ case 16:
+ p.status += ' 1 Gbps'; // RLY?
+ break;
+ case 32:
+ p.status += ' 1 Gbps FDX';
+ break;
+ case 64:
+ p.status += ' 10 Gbps FDX';
+ break;
+ }
+ // TODO parse copper/fiber, autoneg, pause
+
+ // this is a knda kludgy way to merge models
+ var m = self.ports.get(p.id);
+ if(m) {
+ m.set(p, {silent: true});
+ } else {
+ self.ports.add(p, {silent: true});
+ }
+ //console.log(p);
+ });
+ }
+ })).done(function() {
+ self.ports.trigger('add'); // batch redraws
+ });
+ },
+
+ loadFlows:function () {
+ var self = this;
+ //console.log("fetching switch " + this.id + " flows")
+ $.ajax({
+ url:hackBase + "/wm/core/switch/" + self.id + '/flow/json',
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch " + self.id + " flows");
+ var flows = data[self.id];
+ //console.log(flows);
+
+ // create flow models
+ var i = 0;
+ _.each(flows, function(f) {
+ f.id = self.id + '-' + i++;
+
+ // build human-readable match
+ f.matchHTML = '';
+ if(!(f.match.wildcards & (1<<0))) { // input port
+ f.matchHTML += "port=" + f.match.inputPort + ", ";
+ }
+ if(!(f.match.wildcards & (1<<1))) { // VLAN ID
+ f.matchHTML += "VLAN=" + f.match.dataLayerVirtualLan + ", ";
+ }
+ if(!(f.match.wildcards & (1<<20))) { // VLAN prio
+ f.matchHTML += "prio=" + f.match.dataLayerVirtualLanPriorityCodePoint + ", ";
+ }
+ if(!(f.match.wildcards & (1<<2))) { // src MAC
+ f.matchHTML += "src=<a href='/host/" + f.match.dataLayerSource + "'>" +
+ f.match.dataLayerSource + "</a>, ";
+ }
+ if(!(f.match.wildcards & (1<<3))) { // dest MAC
+ f.matchHTML += "dest=<a href='/host/" + f.match.dataLayerDestination + "'>" +
+ f.match.dataLayerDestination + "</a>, ";
+ }
+ if(!(f.match.wildcards & (1<<4))) { // Ethertype
+ // TODO print a human-readable name instead of hex
+ f.matchHTML += "ethertype=" + f.match.dataLayerType + ", ";
+ }
+ if(!(f.match.wildcards & (1<<5))) { // IP protocol
+ // TODO print a human-readable name
+ f.matchHTML += "proto=" + f.match.networkProtocol + ", ";
+ }
+ if(!(f.match.wildcards & (1<<6))) { // TCP/UDP source port
+ f.matchHTML += "IP src port=" + f.match.transportSource + ", ";
+ }
+ if(!(f.match.wildcards & (1<<7))) { // TCP/UDP dest port
+ f.matchHTML += "IP dest port=" + f.match.transportDestination + ", ";
+ }
+ if(!(f.match.wildcards & (32<<8))) { // src IP
+ f.matchHTML += "src=" + f.match.networkSource + ", ";
+ }
+ if(!(f.match.wildcards & (32<<14))) { // dest IP
+ f.matchHTML += "dest=" + f.match.networkDestination + ", ";
+ }
+ if(!(f.match.wildcards & (1<<21))) { // IP TOS
+ f.matchHTML += "TOS=" + f.match.networkTypeOfService + ", ";
+ }
+ // remove trailing ", "
+ f.matchHTML = f.matchHTML.substr(0, f.matchHTML.length - 2);
+
+ // build human-readable action list
+ f.actionText = _.reduce(f.actions, function (memo, a) {
+ switch (a.type) {
+ case "OUTPUT":
+ return memo + "output " + a.port + ', ';
+ case "OPAQUE_ENQUEUE":
+ return memo + "enqueue " + a.port + ':' + a.queueId + ', ';
+ case "STRIP_VLAN":
+ return memo + "strip VLAN, ";
+ case "SET_VLAN_ID":
+ return memo + "VLAN=" + a.virtualLanIdentifier + ', ';
+ case "SET_VLAN_PCP":
+ return memo + "prio=" + a.virtualLanPriorityCodePoint + ', ';
+ case "SET_DL_SRC":
+ return memo + "src=" + a.dataLayerAddress + ', ';
+ case "SET_DL_DST":
+ return memo + "dest=" + a.dataLayerAddress + ', ';
+ case "SET_NW_TOS":
+ return memo + "TOS=" + a.networkTypeOfService + ', ';
+ case "SET_NW_SRC":
+ return memo + "src=" + a.networkAddress + ', ';
+ case "SET_NW_DST":
+ return memo + "dest=" + a.networkAddress + ', ';
+ case "SET_TP_SRC":
+ return memo + "src port=" + a.transportPort + ', ';
+ case "SET_TP_DST":
+ return memo + "dest port=" + a.transportPort + ', ';
+ }
+ }, "");
+ // remove trailing ", "
+ f.actionText = f.actionText.substr(0, f.actionText.length - 2);
+
+ //console.log(f);
+ self.flows.add(f, {silent: true});
+ });
+ self.flows.trigger('add');
+ }
+ });
+ },
+});
+
+window.SwitchCollection = Backbone.Collection.extend({
+
+ model:Switch,
+
+ fetch:function () {
+ var self = this;
+ //console.log("fetching switch list")
+ $.ajax({
+ url:hackBase + "/wm/core/controller/switches/json",
+ dataType:"json",
+ success:function (data) {
+ //console.log("fetched switch list: " + data.length);
+ //console.log(data);
+ var old_ids = self.pluck('id');
+ //console.log("old_ids" + old_ids);
+
+ _.each(data, function(sw) {
+ old_ids = _.without(old_ids, sw['dpid']);
+ self.add({id: sw['dpid'], inetAddress: sw.inetAddress,
+ connectedSince: new Date(sw.connectedSince).toLocaleString()})});
+
+ // old_ids now holds switches that no longer exist; remove them
+ //console.log("old_ids" + old_ids);
+ _.each(old_ids, function(sw) {
+ console.log("removing switch " + sw);
+ self.remove({id:sw});
+ });
+ },
+ });
+ },
+
+});
diff --git a/web/js/models/topologymodel.js b/web/js/models/topologymodel.js
new file mode 100644
index 0000000..c5d8f9b
--- /dev/null
+++ b/web/js/models/topologymodel.js
@@ -0,0 +1,65 @@
+/*
+ Copyright 2012 IBM
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+window.Topology = Backbone.Model.extend({
+
+ url:"/wm/topology/links/json",
+
+ defaults:{
+ nodes: [],
+ links: [],
+ },
+
+ initialize:function () {
+ var self = this;
+ console.log("fetching topology")
+ $.ajax({
+ url:hackBase + self.url,
+ dataType:"json",
+ success:function (data) {
+ console.log("fetched topology: " + data.length);
+ // console.log(data);
+ self.nodes = {};
+ self.links = [];
+
+ // step 1: build unique array of switch IDs
+ /* this doesn't work if there's only one switch,
+ because there are no switch-switch links
+ _.each(data, function (l) {
+ self.nodes[l['src-switch']] = true;
+ self.nodes[l['dst-switch']] = true;
+ });
+ // console.log(self.nodes);
+ var nl = _.keys(self.nodes);
+ */
+ var nl = swl.pluck('id');
+ self.nodes = _.map(nl, function (n) {return {name:n}});
+
+ // step 2: build array of links in format D3 expects
+ _.each(data, function (l) {
+ self.links.push({source:nl.indexOf(l['src-switch']),
+ target:nl.indexOf(l['dst-switch']),
+ value:10});
+ });
+ // console.log(self.nodes);
+ // console.log(self.links);
+ self.trigger('change');
+ //self.set(data);
+ }
+ });
+ }
+
+});
\ No newline at end of file