blob: 4104dd0dfd898d9e462da0cc04f3fb6808342981 [file] [log] [blame]
Pankaj Berdec4ae4b82013-01-12 09:56:47 -08001/*
2 Copyright 2012 IBM
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17window.Switch = Backbone.Model.extend({
18
19 urlRoot:"/wm/core/switch/",
20
21 defaults: {
22 datapathDescription: '',
23 hardwareDescription: '',
24 manufacturerDescription: '',
25 serialNumber: '',
26 softwareDescription: '',
27 flowCount: ' ',
28 packetCount: ' ',
29 byteCount: ' ',
30 },
31
32 initialize:function () {
33 var self = this;
34
35 //console.log("fetching switch " + this.id + " desc")
36 $.ajax({
37 url:hackBase + "/wm/core/switch/" + self.id + '/desc/json',
38 dataType:"json",
39 success:function (data) {
40 //console.log("fetched switch " + self.id + " desc");
41 //console.log(data[self.id][0]);
42 self.set(data[self.id][0]);
43 }
44 });
45
46 //console.log("fetching switch " + this.id + " aggregate")
47 $.ajax({
48 url:hackBase + "/wm/core/switch/" + self.id + '/aggregate/json',
49 dataType:"json",
50 success:function (data) {
51 //console.log("fetched switch " + self.id + " aggregate");
52 //console.log(data[self.id][0]);
53 self.set(data[self.id][0]);
54 }
55 });
56 self.trigger('add');
57 this.ports = new PortCollection();
58 this.flows = new FlowCollection();
59 //this.loadPorts();
60 //this.loadFlows();
61 },
62
63 fetch:function () {
64 this.initialize()
65 },
66
67 loadPorts:function () {
68 var self = this;
69 //console.log("fetching switch " + this.id + " ports")
70 //console.log("fetching switch " + this.id + " features")
71 $.when($.ajax({
72 url:hackBase + "/wm/core/switch/" + self.id + '/port/json',
73 dataType:"json",
74 success:function (data) {
75 //console.log("fetched switch " + self.id + " ports");
76 //console.log(data[self.id]);
77 var old_ids = self.ports.pluck('id');
78 //console.log("old_ids" + old_ids);
79
80 // create port models
81 _.each(data[self.id], function(p) {
82 // workaround for REST serialization signed/unsigned bug
83 if(p.portNumber < 0) {p.portNumber = 65536 + p.portNumber};
84
85 p.id = self.id+'-'+p.portNumber;
86 old_ids = _.without(old_ids, p.id);
87 p.dropped = p.receiveDropped + p.transmitDropped;
88 p.errors = p.receiveCRCErrors + p.receiveErrors + p.receiveOverrunErrors +
89 p.receiveFrameErrors + p.transmitErrors;
90 // this is a knda kludgy way to merge models
91 var m = self.ports.get(p.id);
92 if(m) {
93 m.set(p, {silent: true});
94 } else {
95 self.ports.add(p, {silent: true});
96 }
97 //console.log(p);
98 });
99
100 // old_ids now holds ports that no longer exist; remove them
101 //console.log("old_ids" + old_ids);
102 _.each(old_ids, function(p) {
103 console.log("removing port " + p);
104 self.remove({id:p});
105 });
106 }
107 }),
108 $.ajax({
109 url:hackBase + "/wm/core/switch/" + self.id + '/features/json',
110 dataType:"json",
111 success:function (data) {
112 //console.log("fetched switch " + self.id + " features");
113 //console.log(data[self.id]);
114 // update port models
115 _.each(data[self.id].ports, function(p) {
116 p.id = self.id+'-'+p.portNumber;
117 if(p.name != p.portNumber) {
118 p.name = p.portNumber + ' (' + p.name + ')';
119 }
120 p.status = '';
121 p.status += (p.state & 1) ? 'DOWN' : 'UP';
122 switch(p.currentFeatures & 0x7f) {
123 case 1:
124 p.status += ' 10 Mbps';
125 break;
126 case 2:
127 p.status += ' 10 Mbps FDX';
128 break;
129 case 4:
130 p.status += ' 100 Mbps';
131 break;
132 case 8:
133 p.status += ' 100 Mbps FDX';
134 break;
135 case 16:
136 p.status += ' 1 Gbps'; // RLY?
137 break;
138 case 32:
139 p.status += ' 1 Gbps FDX';
140 break;
141 case 64:
142 p.status += ' 10 Gbps FDX';
143 break;
144 }
145 // TODO parse copper/fiber, autoneg, pause
146
147 // this is a knda kludgy way to merge models
148 var m = self.ports.get(p.id);
149 if(m) {
150 m.set(p, {silent: true});
151 } else {
152 self.ports.add(p, {silent: true});
153 }
154 //console.log(p);
155 });
156 }
157 })).done(function() {
158 self.ports.trigger('add'); // batch redraws
159 });
160 },
161
162 loadFlows:function () {
163 var self = this;
164 //console.log("fetching switch " + this.id + " flows")
165 $.ajax({
166 url:hackBase + "/wm/core/switch/" + self.id + '/flow/json',
167 dataType:"json",
168 success:function (data) {
169 //console.log("fetched switch " + self.id + " flows");
170 var flows = data[self.id];
171 //console.log(flows);
172
173 // create flow models
174 var i = 0;
175 _.each(flows, function(f) {
176 f.id = self.id + '-' + i++;
177
178 // build human-readable match
179 f.matchHTML = '';
180 if(!(f.match.wildcards & (1<<0))) { // input port
181 f.matchHTML += "port=" + f.match.inputPort + ", ";
182 }
183 if(!(f.match.wildcards & (1<<1))) { // VLAN ID
184 f.matchHTML += "VLAN=" + f.match.dataLayerVirtualLan + ", ";
185 }
186 if(!(f.match.wildcards & (1<<20))) { // VLAN prio
187 f.matchHTML += "prio=" + f.match.dataLayerVirtualLanPriorityCodePoint + ", ";
188 }
189 if(!(f.match.wildcards & (1<<2))) { // src MAC
190 f.matchHTML += "src=<a href='/host/" + f.match.dataLayerSource + "'>" +
191 f.match.dataLayerSource + "</a>, ";
192 }
193 if(!(f.match.wildcards & (1<<3))) { // dest MAC
194 f.matchHTML += "dest=<a href='/host/" + f.match.dataLayerDestination + "'>" +
195 f.match.dataLayerDestination + "</a>, ";
196 }
197 if(!(f.match.wildcards & (1<<4))) { // Ethertype
198 // TODO print a human-readable name instead of hex
199 f.matchHTML += "ethertype=" + f.match.dataLayerType + ", ";
200 }
201 if(!(f.match.wildcards & (1<<5))) { // IP protocol
202 // TODO print a human-readable name
203 f.matchHTML += "proto=" + f.match.networkProtocol + ", ";
204 }
205 if(!(f.match.wildcards & (1<<6))) { // TCP/UDP source port
206 f.matchHTML += "IP src port=" + f.match.transportSource + ", ";
207 }
208 if(!(f.match.wildcards & (1<<7))) { // TCP/UDP dest port
209 f.matchHTML += "IP dest port=" + f.match.transportDestination + ", ";
210 }
211 if(!(f.match.wildcards & (32<<8))) { // src IP
212 f.matchHTML += "src=" + f.match.networkSource + ", ";
213 }
214 if(!(f.match.wildcards & (32<<14))) { // dest IP
215 f.matchHTML += "dest=" + f.match.networkDestination + ", ";
216 }
217 if(!(f.match.wildcards & (1<<21))) { // IP TOS
218 f.matchHTML += "TOS=" + f.match.networkTypeOfService + ", ";
219 }
220 // remove trailing ", "
221 f.matchHTML = f.matchHTML.substr(0, f.matchHTML.length - 2);
222
223 // build human-readable action list
224 f.actionText = _.reduce(f.actions, function (memo, a) {
225 switch (a.type) {
226 case "OUTPUT":
227 return memo + "output " + a.port + ', ';
228 case "OPAQUE_ENQUEUE":
229 return memo + "enqueue " + a.port + ':' + a.queueId + ', ';
230 case "STRIP_VLAN":
231 return memo + "strip VLAN, ";
232 case "SET_VLAN_ID":
233 return memo + "VLAN=" + a.virtualLanIdentifier + ', ';
234 case "SET_VLAN_PCP":
235 return memo + "prio=" + a.virtualLanPriorityCodePoint + ', ';
236 case "SET_DL_SRC":
237 return memo + "src=" + a.dataLayerAddress + ', ';
238 case "SET_DL_DST":
239 return memo + "dest=" + a.dataLayerAddress + ', ';
240 case "SET_NW_TOS":
241 return memo + "TOS=" + a.networkTypeOfService + ', ';
242 case "SET_NW_SRC":
243 return memo + "src=" + a.networkAddress + ', ';
244 case "SET_NW_DST":
245 return memo + "dest=" + a.networkAddress + ', ';
246 case "SET_TP_SRC":
247 return memo + "src port=" + a.transportPort + ', ';
248 case "SET_TP_DST":
249 return memo + "dest port=" + a.transportPort + ', ';
250 }
251 }, "");
252 // remove trailing ", "
253 f.actionText = f.actionText.substr(0, f.actionText.length - 2);
254
255 //console.log(f);
256 self.flows.add(f, {silent: true});
257 });
258 self.flows.trigger('add');
259 }
260 });
261 },
262});
263
264window.SwitchCollection = Backbone.Collection.extend({
265
266 model:Switch,
267
268 fetch:function () {
269 var self = this;
270 //console.log("fetching switch list")
271 $.ajax({
272 url:hackBase + "/wm/core/controller/switches/json",
273 dataType:"json",
274 success:function (data) {
275 //console.log("fetched switch list: " + data.length);
276 //console.log(data);
277 var old_ids = self.pluck('id');
278 //console.log("old_ids" + old_ids);
279
280 _.each(data, function(sw) {
281 old_ids = _.without(old_ids, sw['dpid']);
282 self.add({id: sw['dpid'], inetAddress: sw.inetAddress,
283 connectedSince: new Date(sw.connectedSince).toLocaleString()})});
284
285 // old_ids now holds switches that no longer exist; remove them
286 //console.log("old_ids" + old_ids);
287 _.each(old_ids, function(sw) {
288 console.log("removing switch " + sw);
289 self.remove({id:sw});
290 });
291 },
292 });
293 },
294
295});