Bri Prebilic Cole

ONOS-1281, ONOS-1747 - GUI -- Flows table created; version updated.

Change-Id: I06477793d6a1943ed90825f5103c8f6f4e962b70
...@@ -92,7 +92,8 @@ ...@@ -92,7 +92,8 @@
92 org.onlab.packet.*, 92 org.onlab.packet.*,
93 org.onlab.rest.*, 93 org.onlab.rest.*,
94 org.onosproject.*, 94 org.onosproject.*,
95 - org.joda.time.* 95 + org.joda.time.*,
96 + org.apache.commons.*
96 </Import-Package> 97 </Import-Package>
97 <Web-ContextPath>${web.context}</Web-ContextPath> 98 <Web-ContextPath>${web.context}</Web-ContextPath>
98 </instructions> 99 </instructions>
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +
17 +package org.onosproject.ui.impl;
18 +
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import com.google.common.collect.ImmutableSet;
22 +import org.apache.commons.lang.WordUtils;
23 +import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.flow.FlowEntry;
25 +import org.onosproject.net.flow.FlowRuleService;
26 +import org.onosproject.net.flow.TrafficSelector;
27 +import org.onosproject.net.flow.TrafficTreatment;
28 +import org.onosproject.net.flow.criteria.Criterion;
29 +import org.onosproject.net.flow.instructions.Instruction;
30 +
31 +import java.util.ArrayList;
32 +import java.util.Arrays;
33 +import java.util.List;
34 +import java.util.Set;
35 +
36 +
37 +/**
38 + * Message handler for flow view related messages.
39 + */
40 +public class FlowViewMessageHandler extends AbstractTabularViewMessageHandler {
41 +
42 + private static final String NO_DEV = "none";
43 +
44 + /**
45 + * Creates a new message handler for the flow messages.
46 + */
47 + protected FlowViewMessageHandler() {
48 + super(ImmutableSet.of("flowDataRequest"));
49 + }
50 +
51 + @Override
52 + public void process(ObjectNode message) {
53 + ObjectNode payload = payload(message);
54 + String uri = string(payload, "devId", NO_DEV);
55 + String sortCol = string(payload, "sortCol", "id");
56 + String sortDir = string(payload, "sortDir", "asc");
57 +
58 + ObjectNode rootNode;
59 + if (uri.equals(NO_DEV)) {
60 + rootNode = mapper.createObjectNode();
61 + rootNode.set("flows", mapper.createArrayNode());
62 + } else {
63 + DeviceId deviceId = DeviceId.deviceId(uri);
64 +
65 + FlowRuleService service = get(FlowRuleService.class);
66 + TableRow[] rows = generateTableRows(service, deviceId);
67 + RowComparator rc =
68 + new RowComparator(sortCol, RowComparator.direction(sortDir));
69 + Arrays.sort(rows, rc);
70 + ArrayNode flows = generateArrayNode(rows);
71 +
72 + rootNode = mapper.createObjectNode();
73 + rootNode.set("flows", flows);
74 + }
75 +
76 + connection().sendMessage("flowDataResponse", 0, rootNode);
77 + }
78 +
79 + private TableRow[] generateTableRows(FlowRuleService service,
80 + DeviceId deviceId) {
81 + List<TableRow> list = new ArrayList<>();
82 + for (FlowEntry flow : service.getFlowEntries(deviceId)) {
83 + list.add(new FlowTableRow(flow));
84 + }
85 + return list.toArray(new TableRow[list.size()]);
86 + }
87 +
88 + /**
89 + * TableRow implementation for {@link org.onosproject.net.flow.FlowRule flows}.
90 + */
91 + private static class FlowTableRow extends AbstractTableRow {
92 +
93 + private static final String ID = "id";
94 + private static final String APP_ID = "appId";
95 + private static final String GROUP_ID = "groupId";
96 + private static final String TABLE_ID = "tableId";
97 + private static final String PRIORITY = "priority";
98 + private static final String SELECTOR = "selector";
99 + private static final String TREATMENT = "treatment";
100 + private static final String TIMEOUT = "timeout";
101 + private static final String PERMANENT = "permanent";
102 + private static final String STATE = "state";
103 +
104 + private static final String COMMA = ", ";
105 +
106 + private static final String[] COL_IDS = {
107 + ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, SELECTOR,
108 + TREATMENT, TIMEOUT, PERMANENT, STATE
109 + };
110 +
111 + public FlowTableRow(FlowEntry f) {
112 + add(ID, Long.toString(f.id().value()));
113 + add(APP_ID, Short.toString(f.appId()));
114 + add(GROUP_ID, Integer.toString(f.groupId().id()));
115 + add(TABLE_ID, Integer.toString(f.tableId()));
116 + add(PRIORITY, Integer.toString(f.priority()));
117 + add(SELECTOR, getSelectorString(f));
118 + add(TREATMENT, getTreatmentString(f));
119 + add(TIMEOUT, Integer.toString(f.timeout()));
120 + add(PERMANENT, Boolean.toString(f.isPermanent()));
121 + add(STATE, WordUtils.capitalizeFully(f.state().toString()));
122 + }
123 +
124 + private String getSelectorString(FlowEntry f) {
125 + String result;
126 + TrafficSelector selector = f.selector();
127 + Set<Criterion> criteria = selector.criteria();
128 +
129 + if (criteria.isEmpty()) {
130 + result = "(No traffic selectors for this flow)";
131 + } else {
132 + StringBuilder sb = new StringBuilder("Criteria = ");
133 + for (Criterion c : criteria) {
134 + sb.append(WordUtils.capitalizeFully(c.type().toString()))
135 + .append(COMMA);
136 + }
137 + result = removeTrailingComma(sb).toString();
138 + }
139 + return result;
140 + }
141 +
142 + private String getTreatmentString(FlowEntry f) {
143 + TrafficTreatment treatment = f.treatment();
144 + List<Instruction> deferred = treatment.deferred();
145 + List<Instruction> immediate = treatment.immediate();
146 + boolean haveDef = !deferred.isEmpty();
147 + boolean haveImm = !immediate.isEmpty();
148 + boolean both = haveDef && haveImm;
149 + boolean neither = !haveDef && !haveImm;
150 + String result;
151 +
152 + if (neither) {
153 + result = "(No traffic treatment instructions for this flow)";
154 + } else {
155 + StringBuilder sb = new StringBuilder();
156 + addDeferred(sb, deferred);
157 + if (both) {
158 + sb.append(COMMA);
159 + }
160 + addImmediate(sb, immediate);
161 + result = sb.toString();
162 + }
163 + return result;
164 + }
165 +
166 + private void addDeferred(StringBuilder sb, List<Instruction> deferred) {
167 + if (!deferred.isEmpty()) {
168 + sb.append("Deferred instructions = ");
169 + for (Instruction i : deferred) {
170 + sb.append(WordUtils.capitalizeFully(i.type().toString()))
171 + .append(COMMA);
172 + }
173 + removeTrailingComma(sb);
174 + }
175 + }
176 +
177 + private void addImmediate(StringBuilder sb, List<Instruction> immediate) {
178 + if (!immediate.isEmpty()) {
179 + sb.append("Immediate instructions = ");
180 + for (Instruction i : immediate) {
181 + sb.append(WordUtils.capitalizeFully(i.type().toString()))
182 + .append(COMMA);
183 + }
184 + removeTrailingComma(sb);
185 + }
186 + }
187 +
188 + private StringBuilder removeTrailingComma(StringBuilder sb) {
189 + int pos = sb.lastIndexOf(COMMA);
190 + sb.delete(pos, sb.length());
191 + return sb;
192 + }
193 +
194 +
195 + @Override
196 + protected String[] columnIds() {
197 + return COL_IDS;
198 + }
199 + }
200 +
201 +}
...@@ -79,6 +79,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -79,6 +79,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
79 new DeviceViewMessageHandler(), 79 new DeviceViewMessageHandler(),
80 new LinkViewMessageHandler(), 80 new LinkViewMessageHandler(),
81 new HostViewMessageHandler(), 81 new HostViewMessageHandler(),
82 + new FlowViewMessageHandler(),
82 new IntentViewMessageHandler(), 83 new IntentViewMessageHandler(),
83 new ApplicationViewMessageHandler(), 84 new ApplicationViewMessageHandler(),
84 new ClusterViewMessageHandler() 85 new ClusterViewMessageHandler()
......
...@@ -114,6 +114,24 @@ ...@@ -114,6 +114,24 @@
114 "-2,4.1-2.9,7-2.9c2.9,0,5.1,0.9,6.9,2.9c5,5.4,5.6,17.1,5.4,22.6" + 114 "-2,4.1-2.9,7-2.9c2.9,0,5.1,0.9,6.9,2.9c5,5.4,5.6,17.1,5.4,22.6" +
115 "h-25C42.3,43.1,43.1,31.5,48.1,26.1z", 115 "h-25C42.3,43.1,43.1,31.5,48.1,26.1z",
116 116
117 + flowTable: 'M15.9,19.1h-8v-13h8V19.1z M90.5,6.1H75.6v13h14.9V6.1z' +
118 + ' M71.9,6.1H56.9v13h14.9V6.1z M53.2,6.1H38.3v13h14.9V6.1z M34.5,' +
119 + '6.1H19.6v13h14.9V6.1z M102.2,6.1h-8v13h8V6.1z M102.2,23.6H7.9v' +
120 + '78.5h94.4V23.6z M86,63.2c0,3.3-2.7,6-6,6c-2.8,0-5.1-1.9-5.8-' +
121 + '4.5H63.3v5.1c0,0.9-0.7,1.5-1.5,1.5h-5.2v10.6c2.6,0.7,4.5,3,4.5,' +
122 + '5.8c0,3.3-2.7,6-6,6c-3.3,0-6-2.7-6-6c0-2.8,1.9-5.1,4.4-5.8V71.3' +
123 + 'H48c-0.9,0-1.5-0.7-1.5-1.5v-5.1H36c-0.7,2.6-3,4.4-5.8,4.4c-3.3,' +
124 + '0-6-2.7-6-6s2.7-6,6-6c2.8,0,5.2,1.9,5.8,4.5h10.5V56c0-0.9,0.7-' +
125 + '1.5,1.5-1.5h5.5V43.8c-2.6-0.7-4.5-3-4.5-5.8c0-3.3,2.7-6,6-6s6,' +
126 + '2.7,6,6c0,2.8-1.9,5.1-4.5,5.8v10.6h5.2c0.9,0,1.5,0.7,1.5,1.5v' +
127 + '5.6h10.8c0.7-2.6,3-4.5,5.8-4.5C83.3,57.1,86,59.8,86,63.2z M55.1,' +
128 + '42.3c2.3,0,4.3-1.9,4.3-4.3c0-2.3-1.9-4.3-4.3-4.3s-4.3,1.9-4.3,' +
129 + '4.3C50.8,40.4,52.7,42.3,55.1,42.3z M34.4,63.1c0-2.3-1.9-4.3-4.3' +
130 + '-4.3s-4.3,1.9-4.3,4.3s1.9,4.3,4.3,4.3S34.4,65.5,34.4,63.1z ' +
131 + 'M55.1,83.5c-2.3,0-4.3,1.9-4.3,4.3s1.9,4.3,4.3,4.3s4.3-1.9,4.3-' +
132 + '4.3S57.5,83.5,55.1,83.5zM84.2,63.2c0-2.3-1.9-4.3-4.3-4.3s-4.3,' +
133 + '1.9-4.3,4.3s1.9,4.3,4.3,4.3S84.2,65.5,84.2,63.2z',
134 +
117 // --- Topology toolbar specific glyphs ---------------------- 135 // --- Topology toolbar specific glyphs ----------------------
118 136
119 summary: "M95.8,9.2H14.2c-2.8,0-5,2.2-5,5v81.5c0,2.8,2.2,5,5," + 137 summary: "M95.8,9.2H14.2c-2.8,0-5,2.2-5,5v81.5c0,2.8,2.2,5,5," +
......
...@@ -29,9 +29,12 @@ ...@@ -29,9 +29,12 @@
29 // scope: $scope, <- controller scope 29 // scope: $scope, <- controller scope
30 // tag: 'device', <- table identifier 30 // tag: 'device', <- table identifier
31 // selCb: selCb <- row selection callback (optional) 31 // selCb: selCb <- row selection callback (optional)
32 + // query: params <- query parameters in URL (optional)
32 // } 33 // }
33 // Note: selCb() is passed the row data model of the selected row, 34 // Note: selCb() is passed the row data model of the selected row,
34 // or null when no row is selected. 35 // or null when no row is selected.
36 + // Note: query is always an object (empty or containing properties)
37 + // it comes from $location.search()
35 38
36 function buildTable(o) { 39 function buildTable(o) {
37 var handlers = {}, 40 var handlers = {},
...@@ -48,7 +51,8 @@ ...@@ -48,7 +51,8 @@
48 } 51 }
49 52
50 function sortCb(params) { 53 function sortCb(params) {
51 - wss.sendEvent(req, params); 54 + var p = angular.extend({}, params, o.query);
55 + wss.sendEvent(req, p);
52 } 56 }
53 o.scope.sortCallback = sortCb; 57 o.scope.sortCallback = sortCb;
54 58
......
...@@ -119,11 +119,11 @@ ...@@ -119,11 +119,11 @@
119 119
120 bns.button(btnsDiv, 120 bns.button(btnsDiv,
121 'dev-dets-p-flows', 121 'dev-dets-p-flows',
122 - 'flowsTable', 122 + 'flowTable',
123 function () { 123 function () {
124 ns.navTo(flowPath, { devId: details.id }); 124 ns.navTo(flowPath, { devId: details.id });
125 }, 125 },
126 - 'Show flows for this device'); 126 + 'Show flows table for this device');
127 } 127 }
128 128
129 function addPortRow(tbody, port) { 129 function addPortRow(tbody, port) {
......
...@@ -18,5 +18,28 @@ ...@@ -18,5 +18,28 @@
18 ONOS GUI -- Flow View -- CSS file 18 ONOS GUI -- Flow View -- CSS file
19 */ 19 */
20 20
21 -#ov-flow td { 21 +.light #ov-flow tr:nth-child(6n + 2),
22 +.light #ov-flow tr:nth-child(6n + 3),
23 +.light #ov-flow tr:nth-child(6n + 4) {
24 + background-color: #eee;
25 +}
26 +.light #ov-flow tr:nth-child(6n + 5),
27 +.light #ov-flow tr:nth-child(6n + 6),
28 +.light #ov-flow tr:nth-child(6n + 1) {
29 + background-color: #ddd;
30 +}
31 +.dark #ov-flow tr:nth-child(6n + 2),
32 +.dark #ov-flow tr:nth-child(6n + 3),
33 +.dark #ov-flow tr:nth-child(6n + 4) {
34 + background-color: #444;
35 +}
36 +.dark #ov-flow tr:nth-child(6n + 5),
37 +.dark #ov-flow tr:nth-child(6n + 6),
38 +.dark #ov-flow tr:nth-child(6n + 1) {
39 + background-color: #333;
40 +}
41 +
42 +#ov-flow td.selector,
43 +#ov-flow td.treatment {
44 + padding-left: 36px;
22 } 45 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -<!-- Host partial HTML --> 1 +<!-- Flow partial HTML -->
2 <div id="ov-flow"> 2 <div id="ov-flow">
3 - <h1> Flows are here </h1> 3 + <div class="tabular-header">
4 + <h2>
5 + Flows for Device {{ctrl.devId || "none"}}
6 + ({{ctrl.tableData.length}} total)
7 + </h2>
8 + </div>
9 +
10 + <table class="summary-list"
11 + onos-fixed-header
12 + onos-sortable-header
13 + sort-callback="sortCallback(requestParams)">
14 + <thead>
15 + <tr>
16 + <th colId="id" sortable>Flow ID </th>
17 + <th colId="appId" sortable>App ID </th>
18 + <th colId="groupId" sortable>Group ID </th>
19 + <th colId="tableId" sortable>Table ID </th>
20 + <th colId="priority" sortable>Priority </th>
21 + <th colId="timeout" sortable>Timeout </th>
22 + <th colId="permanent" sortable>Permanent </th>
23 + <th colId="state" sortable>State </th>
24 + </tr>
25 + </thead>
26 +
27 + <tbody>
28 + <tr ng-hide="ctrl.tableData.length">
29 + <td class="nodata" colspan="8">
30 + No Flows found
31 + </td>
32 + </tr>
33 +
34 + <tr ng-repeat-start="flow in ctrl.tableData">
35 + <td>{{flow.id}}</td>
36 + <td>{{flow.appId}}</td>
37 + <td>{{flow.groupId}}</td>
38 + <td>{{flow.tableId}}</td>
39 + <td>{{flow.priority}}</td>
40 + <td>{{flow.timeout}}</td>
41 + <td>{{flow.permanent}}</td>
42 + <td>{{flow.state}}</td>
43 + </tr>
44 + <tr>
45 + <td class="selector" colspan="8">{{flow.selector}}</td>
46 + </tr>
47 + <tr ng-repeat-end ng-repeat-done>
48 + <td class="treatment" colspan="8">{{flow.treatment}}</td>
49 + </tr>
50 + </tbody>
51 + </table>
4 </div> 52 </div>
......
...@@ -21,16 +21,33 @@ ...@@ -21,16 +21,33 @@
21 (function () { 21 (function () {
22 'use strict'; 22 'use strict';
23 23
24 + // injected references
25 + var $log, $scope, $location, fs, tbs;
26 +
24 angular.module('ovFlow', []) 27 angular.module('ovFlow', [])
25 .controller('OvFlowCtrl', 28 .controller('OvFlowCtrl',
26 - ['$log', '$scope', 'TableBuilderService', 29 + ['$log', '$scope', '$location', 'FnService', 'TableBuilderService',
27 - 30 +
28 - function ($log, $scope, tbs) { 31 + function (_$log_, _$scope_, _$location_, _fs_, _tbs_) {
29 - //tbs.buildTable({ 32 + var self = this,
30 - // self: this, 33 + params;
31 - // scope: $scope, 34 + $log = _$log_;
32 - // tag: 'flow' 35 + $scope = _$scope_;
33 - //}); 36 + $location = _$location_;
37 + fs = _fs_;
38 + tbs = _tbs_;
39 +
40 + params = $location.search();
41 + if (params.hasOwnProperty('devId')) {
42 + self.devId = params['devId'];
43 + }
44 +
45 + tbs.buildTable({
46 + self: self,
47 + scope: $scope,
48 + tag: 'flow',
49 + query: params
50 + });
34 51
35 $log.log('OvFlowCtrl has been created'); 52 $log.log('OvFlowCtrl has been created');
36 }]); 53 }]);
......
...@@ -248,9 +248,9 @@ ...@@ -248,9 +248,9 @@
248 if ((data.props).hasOwnProperty('URI')) { 248 if ((data.props).hasOwnProperty('URI')) {
249 tps.addAction({ 249 tps.addAction({
250 id: 'flows-table-btn', 250 id: 'flows-table-btn',
251 - gid: 'flowsTable', 251 + gid: 'flowTable',
252 cb: function () { 252 cb: function () {
253 - ns.navTo(flowPath, { devId: data.id }); 253 + ns.navTo(flowPath, { devId: data.props['URI'] });
254 }, 254 },
255 tt: 'Show flows for this device' 255 tt: 'Show flows for this device'
256 }); 256 });
......
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
78 self.$route = $route; 78 self.$route = $route;
79 self.$routeParams = $routeParams; 79 self.$routeParams = $routeParams;
80 self.$location = $location; 80 self.$location = $location;
81 - self.version = '1.1.0'; 81 + self.version = '1.3.0';
82 82
83 // initialize services... 83 // initialize services...
84 ts.init(); 84 ts.init();
......
...@@ -29,7 +29,7 @@ describe('Controller: OnosCtrl', function () { ...@@ -29,7 +29,7 @@ describe('Controller: OnosCtrl', function () {
29 ctrl = $controller('OnosCtrl'); 29 ctrl = $controller('OnosCtrl');
30 })); 30 }));
31 31
32 - it('should report version 1.1.0', function () { 32 + it('should report version 1.3.0', function () {
33 - expect(ctrl.version).toEqual('1.1.0'); 33 + expect(ctrl.version).toEqual('1.3.0');
34 }); 34 });
35 }); 35 });
...\ No newline at end of file ...\ No newline at end of file
......