Thomas Vachuska
Committed by Gerrit Code Review

Fixing issues on GUI server side. Adding command to balance mastership. Messing …

…with color scheme per feedback.

Change-Id: I89fb52105f7e724167a417e033048e9c88f31eae
1 +/*
2 + * Copyright 2014 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 +package org.onlab.onos.cli;
17 +
18 +import com.google.common.collect.HashMultimap;
19 +import com.google.common.collect.Multimap;
20 +import org.apache.karaf.shell.commands.Command;
21 +import org.onlab.onos.cluster.ClusterService;
22 +import org.onlab.onos.cluster.ControllerNode;
23 +import org.onlab.onos.mastership.MastershipAdminService;
24 +import org.onlab.onos.mastership.MastershipService;
25 +import org.onlab.onos.net.DeviceId;
26 +import org.onlab.onos.net.MastershipRole;
27 +
28 +import java.util.Collection;
29 +import java.util.Iterator;
30 +import java.util.List;
31 +
32 +import static com.google.common.collect.Lists.newArrayList;
33 +
34 +/**
35 + * Forces device mastership rebalancing.
36 + */
37 +@Command(scope = "onos", name = "balance-masters",
38 + description = "Forces device mastership rebalancing")
39 +public class BalanceMastersCommand extends AbstractShellCommand {
40 +
41 + @Override
42 + protected void execute() {
43 + ClusterService service = get(ClusterService.class);
44 + MastershipService mastershipService = get(MastershipService.class);
45 + MastershipAdminService adminService = get(MastershipAdminService.class);
46 +
47 + List<ControllerNode> nodes = newArrayList(service.getNodes());
48 +
49 + Multimap<ControllerNode, DeviceId> controllerDevices = HashMultimap.create();
50 +
51 + // Create buckets reflecting current ownership.
52 + for (ControllerNode node : nodes) {
53 + controllerDevices.putAll(node, mastershipService.getDevicesOf(node.id()));
54 + }
55 +
56 + int bucketCount = nodes.size();
57 + for (int i = 0; i < bucketCount / 2; i++) {
58 + // Iterate over the buckets and find the smallest and the largest.
59 + ControllerNode smallest = findSmallestBucket(controllerDevices);
60 + ControllerNode largest = findLargestBucket(controllerDevices);
61 + balanceBuckets(smallest, largest, controllerDevices,
62 + mastershipService, adminService);
63 + }
64 + }
65 +
66 + private ControllerNode findSmallestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
67 + int minSize = Integer.MAX_VALUE;
68 + ControllerNode minNode = null;
69 + for (ControllerNode node : controllerDevices.keySet()) {
70 + int size = controllerDevices.get(node).size();
71 + if (size < minSize) {
72 + minSize = size;
73 + minNode = node;
74 + }
75 + }
76 + return minNode;
77 + }
78 +
79 + private ControllerNode findLargestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
80 + int maxSize = -1;
81 + ControllerNode maxNode = null;
82 + for (ControllerNode node : controllerDevices.keySet()) {
83 + int size = controllerDevices.get(node).size();
84 + if (size >= maxSize) {
85 + maxSize = size;
86 + maxNode = node;
87 + }
88 + }
89 + return maxNode;
90 + }
91 +
92 + // FIXME: enhance to better handle cases where smallest cannot take any of the devices from largest
93 +
94 + private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
95 + Multimap<ControllerNode, DeviceId> controllerDevices,
96 + MastershipService mastershipService,
97 + MastershipAdminService adminService) {
98 + Collection<DeviceId> minBucket = controllerDevices.get(smallest);
99 + Collection<DeviceId> maxBucket = controllerDevices.get(largest);
100 +
101 + int delta = (maxBucket.size() - minBucket.size()) / 2;
102 +
103 + print("Attempting to move %d nodes from %s to %s...",
104 + delta, largest.id(), smallest.id());
105 +
106 + int i = 0;
107 + Iterator<DeviceId> it = maxBucket.iterator();
108 + while (it.hasNext() && i < delta) {
109 + DeviceId deviceId = it.next();
110 +
111 + // Check that the transfer can happen for the current element.
112 + if (mastershipService.getNodesFor(deviceId).backups().contains(smallest)) {
113 + print("Setting %s as the new master for %s", smallest.id(), deviceId);
114 + adminService.setRole(smallest.id(), deviceId, MastershipRole.MASTER);
115 + i++;
116 + }
117 + }
118 +
119 + controllerDevices.removeAll(smallest);
120 + }
121 +
122 +}
...@@ -61,6 +61,9 @@ ...@@ -61,6 +61,9 @@
61 <action class="org.onlab.onos.cli.MastersListCommand"/> 61 <action class="org.onlab.onos.cli.MastersListCommand"/>
62 </command> 62 </command>
63 <command> 63 <command>
64 + <action class="org.onlab.onos.cli.BalanceMastersCommand"/>
65 + </command>
66 + <command>
64 <action class="org.onlab.onos.cli.ApplicationIdListCommand"/> 67 <action class="org.onlab.onos.cli.ApplicationIdListCommand"/>
65 </command> 68 </command>
66 69
......
...@@ -289,7 +289,7 @@ public abstract class TopologyViewMessages { ...@@ -289,7 +289,7 @@ public abstract class TopologyViewMessages {
289 } 289 }
290 290
291 // Produces a cluster instance message to the client. 291 // Produces a cluster instance message to the client.
292 - protected ObjectNode instanceMessage(ClusterEvent event) { 292 + protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
293 ControllerNode node = event.subject(); 293 ControllerNode node = event.subject();
294 int switchCount = mastershipService.getDevicesOf(node.id()).size(); 294 int switchCount = mastershipService.getDevicesOf(node.id()).size();
295 ObjectNode payload = mapper.createObjectNode() 295 ObjectNode payload = mapper.createObjectNode()
...@@ -307,8 +307,10 @@ public abstract class TopologyViewMessages { ...@@ -307,8 +307,10 @@ public abstract class TopologyViewMessages {
307 payload.set("labels", labels); 307 payload.set("labels", labels);
308 addMetaUi(node.id().toString(), payload); 308 addMetaUi(node.id().toString(), payload);
309 309
310 - String type = (event.type() == INSTANCE_ADDED) ? "addInstance" : 310 + String type = messageType != null ? messageType :
311 - ((event.type() == INSTANCE_REMOVED) ? "removeInstance" : "updateInstance"); 311 + ((event.type() == INSTANCE_ADDED) ? "addInstance" :
312 + ((event.type() == INSTANCE_REMOVED ? "removeInstance" :
313 + "updateInstance")));
312 return envelope(type, 0, payload); 314 return envelope(type, 0, payload);
313 } 315 }
314 316
......
...@@ -24,6 +24,8 @@ import org.onlab.onos.cluster.ClusterEventListener; ...@@ -24,6 +24,8 @@ import org.onlab.onos.cluster.ClusterEventListener;
24 import org.onlab.onos.cluster.ControllerNode; 24 import org.onlab.onos.cluster.ControllerNode;
25 import org.onlab.onos.core.ApplicationId; 25 import org.onlab.onos.core.ApplicationId;
26 import org.onlab.onos.core.CoreService; 26 import org.onlab.onos.core.CoreService;
27 +import org.onlab.onos.mastership.MastershipEvent;
28 +import org.onlab.onos.mastership.MastershipListener;
27 import org.onlab.onos.net.ConnectPoint; 29 import org.onlab.onos.net.ConnectPoint;
28 import org.onlab.onos.net.Device; 30 import org.onlab.onos.net.Device;
29 import org.onlab.onos.net.Host; 31 import org.onlab.onos.net.Host;
...@@ -46,6 +48,7 @@ import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; ...@@ -46,6 +48,7 @@ import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
46 import org.onlab.onos.net.link.LinkEvent; 48 import org.onlab.onos.net.link.LinkEvent;
47 import org.onlab.onos.net.link.LinkListener; 49 import org.onlab.onos.net.link.LinkListener;
48 import org.onlab.osgi.ServiceDirectory; 50 import org.onlab.osgi.ServiceDirectory;
51 +import org.onlab.packet.Ethernet;
49 52
50 import java.io.IOException; 53 import java.io.IOException;
51 import java.util.ArrayList; 54 import java.util.ArrayList;
...@@ -62,6 +65,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED; ...@@ -62,6 +65,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
62 import static org.onlab.onos.net.DeviceId.deviceId; 65 import static org.onlab.onos.net.DeviceId.deviceId;
63 import static org.onlab.onos.net.HostId.hostId; 66 import static org.onlab.onos.net.HostId.hostId;
64 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; 67 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
68 +import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_UPDATED;
65 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED; 69 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
66 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED; 70 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
67 71
...@@ -80,8 +84,8 @@ public class TopologyViewWebSocket ...@@ -80,8 +84,8 @@ public class TopologyViewWebSocket
80 84
81 private static final String APP_ID = "org.onlab.onos.gui"; 85 private static final String APP_ID = "org.onlab.onos.gui";
82 86
83 - private static final long SUMMARY_FREQUENCY_SEC = 2000; 87 + private static final long SUMMARY_FREQUENCY_SEC = 3000;
84 - private static final long TRAFFIC_FREQUENCY_SEC = 1000; 88 + private static final long TRAFFIC_FREQUENCY_SEC = 1500;
85 89
86 private static final Comparator<? super ControllerNode> NODE_COMPARATOR = 90 private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
87 new Comparator<ControllerNode>() { 91 new Comparator<ControllerNode>() {
...@@ -97,6 +101,7 @@ public class TopologyViewWebSocket ...@@ -97,6 +101,7 @@ public class TopologyViewWebSocket
97 private FrameConnection control; 101 private FrameConnection control;
98 102
99 private final ClusterEventListener clusterListener = new InternalClusterListener(); 103 private final ClusterEventListener clusterListener = new InternalClusterListener();
104 + private final MastershipListener mastershipListener = new InternalMastershipListener();
100 private final DeviceListener deviceListener = new InternalDeviceListener(); 105 private final DeviceListener deviceListener = new InternalDeviceListener();
101 private final LinkListener linkListener = new InternalLinkListener(); 106 private final LinkListener linkListener = new InternalLinkListener();
102 private final HostListener hostListener = new InternalHostListener(); 107 private final HostListener hostListener = new InternalHostListener();
...@@ -164,7 +169,7 @@ public class TopologyViewWebSocket ...@@ -164,7 +169,7 @@ public class TopologyViewWebSocket
164 this.control = (FrameConnection) connection; 169 this.control = (FrameConnection) connection;
165 addListeners(); 170 addListeners();
166 171
167 - sendAllInstances(); 172 + sendAllInstances(null);
168 sendAllDevices(); 173 sendAllDevices();
169 sendAllLinks(); 174 sendAllLinks();
170 sendAllHosts(); 175 sendAllHosts();
...@@ -235,11 +240,12 @@ public class TopologyViewWebSocket ...@@ -235,11 +240,12 @@ public class TopologyViewWebSocket
235 } 240 }
236 241
237 // Sends all controller nodes to the client as node-added messages. 242 // Sends all controller nodes to the client as node-added messages.
238 - private void sendAllInstances() { 243 + private void sendAllInstances(String messageType) {
239 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes()); 244 List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
240 Collections.sort(nodes, NODE_COMPARATOR); 245 Collections.sort(nodes, NODE_COMPARATOR);
241 for (ControllerNode node : nodes) { 246 for (ControllerNode node : nodes) {
242 - sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node))); 247 + sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
248 + messageType));
243 } 249 }
244 } 250 }
245 251
...@@ -307,13 +313,14 @@ public class TopologyViewWebSocket ...@@ -307,13 +313,14 @@ public class TopologyViewWebSocket
307 313
308 // FIXME: clearly, this is not enough 314 // FIXME: clearly, this is not enough
309 TrafficSelector selector = DefaultTrafficSelector.builder() 315 TrafficSelector selector = DefaultTrafficSelector.builder()
316 + .matchEthType(Ethernet.TYPE_IPV4)
310 .matchEthDst(dstHost.mac()).build(); 317 .matchEthDst(dstHost.mac()).build();
311 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); 318 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
312 319
313 MultiPointToSinglePointIntent intent = 320 MultiPointToSinglePointIntent intent =
314 new MultiPointToSinglePointIntent(appId, selector, treatment, 321 new MultiPointToSinglePointIntent(appId, selector, treatment,
315 ingressPoints, dstHost.location()); 322 ingressPoints, dstHost.location());
316 - trafficEvent = event; 323 + startMonitoring(event);
317 intentService.submit(intent); 324 intentService.submit(intent);
318 } 325 }
319 326
...@@ -359,7 +366,6 @@ public class TopologyViewWebSocket ...@@ -359,7 +366,6 @@ public class TopologyViewWebSocket
359 366
360 // Subscribes for host traffic messages. 367 // Subscribes for host traffic messages.
361 private synchronized void requestAllTraffic(ObjectNode event) { 368 private synchronized void requestAllTraffic(ObjectNode event) {
362 - ObjectNode payload = payload(event);
363 long sid = startMonitoring(event); 369 long sid = startMonitoring(event);
364 sendMessage(trafficSummaryMessage(sid)); 370 sendMessage(trafficSummaryMessage(sid));
365 } 371 }
...@@ -375,7 +381,6 @@ public class TopologyViewWebSocket ...@@ -375,7 +381,6 @@ public class TopologyViewWebSocket
375 381
376 // If there is a hover node, include it in the hosts and find intents. 382 // If there is a hover node, include it in the hosts and find intents.
377 String hover = string(payload, "hover"); 383 String hover = string(payload, "hover");
378 - Set<Intent> hoverIntents;
379 if (!isNullOrEmpty(hover)) { 384 if (!isNullOrEmpty(hover)) {
380 addHover(hosts, devices, hover); 385 addHover(hosts, devices, hover);
381 } 386 }
...@@ -447,6 +452,7 @@ public class TopologyViewWebSocket ...@@ -447,6 +452,7 @@ public class TopologyViewWebSocket
447 // Adds all internal listeners. 452 // Adds all internal listeners.
448 private void addListeners() { 453 private void addListeners() {
449 clusterService.addListener(clusterListener); 454 clusterService.addListener(clusterListener);
455 + mastershipService.addListener(mastershipListener);
450 deviceService.addListener(deviceListener); 456 deviceService.addListener(deviceListener);
451 linkService.addListener(linkListener); 457 linkService.addListener(linkListener);
452 hostService.addListener(hostListener); 458 hostService.addListener(hostListener);
...@@ -458,6 +464,7 @@ public class TopologyViewWebSocket ...@@ -458,6 +464,7 @@ public class TopologyViewWebSocket
458 if (!listenersRemoved) { 464 if (!listenersRemoved) {
459 listenersRemoved = true; 465 listenersRemoved = true;
460 clusterService.removeListener(clusterListener); 466 clusterService.removeListener(clusterListener);
467 + mastershipService.removeListener(mastershipListener);
461 deviceService.removeListener(deviceListener); 468 deviceService.removeListener(deviceListener);
462 linkService.removeListener(linkListener); 469 linkService.removeListener(linkListener);
463 hostService.removeListener(hostListener); 470 hostService.removeListener(hostListener);
...@@ -469,7 +476,17 @@ public class TopologyViewWebSocket ...@@ -469,7 +476,17 @@ public class TopologyViewWebSocket
469 private class InternalClusterListener implements ClusterEventListener { 476 private class InternalClusterListener implements ClusterEventListener {
470 @Override 477 @Override
471 public void event(ClusterEvent event) { 478 public void event(ClusterEvent event) {
472 - sendMessage(instanceMessage(event)); 479 + sendMessage(instanceMessage(event, null));
480 + }
481 + }
482 +
483 + // Mastership change listener
484 + private class InternalMastershipListener implements MastershipListener {
485 + @Override
486 + public void event(MastershipEvent event) {
487 + sendAllInstances("updateInstance");
488 + Device device = deviceService.getDevice(event.subject());
489 + sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
473 } 490 }
474 } 491 }
475 492
......
...@@ -123,10 +123,15 @@ ...@@ -123,10 +123,15 @@
123 // TODO: tune colors for light and dark themes 123 // TODO: tune colors for light and dark themes
124 124
125 // blue purple pink mustard cyan green red 125 // blue purple pink mustard cyan green red
126 - var lightNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'], 126 + //var lightNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
127 - lightMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'], 127 + // lightMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'],
128 - darkNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'], 128 + // darkNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
129 - darkMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896']; 129 + // darkMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'];
130 +
131 + var lightNorm = ['#3F587F', '#77533D', '#C94E30', '#892D78', '#138C62', '#006D72', '#59AD00'],
132 + lightMute = ['#56657C', '#665F57', '#C68C7F', '#876E82', '#68897E', '#4E6F70', '#93AA7B'],
133 + darkNorm = ['#3F587F', '#77533D', '#C94E30', '#892D78', '#138C62', '#006D72', '#59AD00'],
134 + darkMute = ['#56657C', '#665F57', '#C68C7F', '#876E82', '#68897E', '#4E6F70', '#93AA7B'];
130 135
131 function cat7() { 136 function cat7() {
132 var colors = { 137 var colors = {
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
51 color: #369; 51 color: #369;
52 } 52 }
53 .dark #mast span.title { 53 .dark #mast span.title {
54 - color: #acf; 54 + color: #eee;
55 } 55 }
56 56
57 #mast span.right { 57 #mast span.right {
......
...@@ -356,7 +356,7 @@ svg .node.host circle { ...@@ -356,7 +356,7 @@ svg .node.host circle {
356 fill: #888; 356 fill: #888;
357 } 357 }
358 #topo-oibox .online svg text { 358 #topo-oibox .online svg text {
359 - fill: #000; 359 + fill: #eee;
360 } 360 }
361 361
362 #topo-oibox svg text.instTitle { 362 #topo-oibox svg text.instTitle {
......
...@@ -1088,8 +1088,8 @@ ...@@ -1088,8 +1088,8 @@
1088 1088
1089 svg.append('rect').attr(rectAttr); 1089 svg.append('rect').attr(rectAttr);
1090 1090
1091 - appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node'); 1091 + //appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node');
1092 - appendBadge(svg, c.birdOx, c.birdOy, c.birdDim, '#bird'); 1092 + appendBadge(svg, 14, 14, 28, '#bird');
1093 1093
1094 if (d.uiAttached) { 1094 if (d.uiAttached) {
1095 attachUiBadge(svg); 1095 attachUiBadge(svg);
......