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
/*
* Copyright 2014 Open Networking Laboratory
*
* 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.
*/
package org.onlab.onos.cli;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.mastership.MastershipAdminService;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.MastershipRole;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
/**
* Forces device mastership rebalancing.
*/
@Command(scope = "onos", name = "balance-masters",
description = "Forces device mastership rebalancing")
public class BalanceMastersCommand extends AbstractShellCommand {
@Override
protected void execute() {
ClusterService service = get(ClusterService.class);
MastershipService mastershipService = get(MastershipService.class);
MastershipAdminService adminService = get(MastershipAdminService.class);
List<ControllerNode> nodes = newArrayList(service.getNodes());
Multimap<ControllerNode, DeviceId> controllerDevices = HashMultimap.create();
// Create buckets reflecting current ownership.
for (ControllerNode node : nodes) {
controllerDevices.putAll(node, mastershipService.getDevicesOf(node.id()));
}
int bucketCount = nodes.size();
for (int i = 0; i < bucketCount / 2; i++) {
// Iterate over the buckets and find the smallest and the largest.
ControllerNode smallest = findSmallestBucket(controllerDevices);
ControllerNode largest = findLargestBucket(controllerDevices);
balanceBuckets(smallest, largest, controllerDevices,
mastershipService, adminService);
}
}
private ControllerNode findSmallestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
int minSize = Integer.MAX_VALUE;
ControllerNode minNode = null;
for (ControllerNode node : controllerDevices.keySet()) {
int size = controllerDevices.get(node).size();
if (size < minSize) {
minSize = size;
minNode = node;
}
}
return minNode;
}
private ControllerNode findLargestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
int maxSize = -1;
ControllerNode maxNode = null;
for (ControllerNode node : controllerDevices.keySet()) {
int size = controllerDevices.get(node).size();
if (size >= maxSize) {
maxSize = size;
maxNode = node;
}
}
return maxNode;
}
// FIXME: enhance to better handle cases where smallest cannot take any of the devices from largest
private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
Multimap<ControllerNode, DeviceId> controllerDevices,
MastershipService mastershipService,
MastershipAdminService adminService) {
Collection<DeviceId> minBucket = controllerDevices.get(smallest);
Collection<DeviceId> maxBucket = controllerDevices.get(largest);
int delta = (maxBucket.size() - minBucket.size()) / 2;
print("Attempting to move %d nodes from %s to %s...",
delta, largest.id(), smallest.id());
int i = 0;
Iterator<DeviceId> it = maxBucket.iterator();
while (it.hasNext() && i < delta) {
DeviceId deviceId = it.next();
// Check that the transfer can happen for the current element.
if (mastershipService.getNodesFor(deviceId).backups().contains(smallest)) {
print("Setting %s as the new master for %s", smallest.id(), deviceId);
adminService.setRole(smallest.id(), deviceId, MastershipRole.MASTER);
i++;
}
}
controllerDevices.removeAll(smallest);
}
}
......@@ -61,6 +61,9 @@
<action class="org.onlab.onos.cli.MastersListCommand"/>
</command>
<command>
<action class="org.onlab.onos.cli.BalanceMastersCommand"/>
</command>
<command>
<action class="org.onlab.onos.cli.ApplicationIdListCommand"/>
</command>
......
......@@ -289,7 +289,7 @@ public abstract class TopologyViewMessages {
}
// Produces a cluster instance message to the client.
protected ObjectNode instanceMessage(ClusterEvent event) {
protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
ControllerNode node = event.subject();
int switchCount = mastershipService.getDevicesOf(node.id()).size();
ObjectNode payload = mapper.createObjectNode()
......@@ -307,8 +307,10 @@ public abstract class TopologyViewMessages {
payload.set("labels", labels);
addMetaUi(node.id().toString(), payload);
String type = (event.type() == INSTANCE_ADDED) ? "addInstance" :
((event.type() == INSTANCE_REMOVED) ? "removeInstance" : "updateInstance");
String type = messageType != null ? messageType :
((event.type() == INSTANCE_ADDED) ? "addInstance" :
((event.type() == INSTANCE_REMOVED ? "removeInstance" :
"updateInstance")));
return envelope(type, 0, payload);
}
......
......@@ -24,6 +24,8 @@ import org.onlab.onos.cluster.ClusterEventListener;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.core.CoreService;
import org.onlab.onos.mastership.MastershipEvent;
import org.onlab.onos.mastership.MastershipListener;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
......@@ -46,6 +48,7 @@ import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkListener;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Ethernet;
import java.io.IOException;
import java.util.ArrayList;
......@@ -62,6 +65,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.HostId.hostId;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_UPDATED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
......@@ -80,8 +84,8 @@ public class TopologyViewWebSocket
private static final String APP_ID = "org.onlab.onos.gui";
private static final long SUMMARY_FREQUENCY_SEC = 2000;
private static final long TRAFFIC_FREQUENCY_SEC = 1000;
private static final long SUMMARY_FREQUENCY_SEC = 3000;
private static final long TRAFFIC_FREQUENCY_SEC = 1500;
private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
new Comparator<ControllerNode>() {
......@@ -97,6 +101,7 @@ public class TopologyViewWebSocket
private FrameConnection control;
private final ClusterEventListener clusterListener = new InternalClusterListener();
private final MastershipListener mastershipListener = new InternalMastershipListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
private final LinkListener linkListener = new InternalLinkListener();
private final HostListener hostListener = new InternalHostListener();
......@@ -164,7 +169,7 @@ public class TopologyViewWebSocket
this.control = (FrameConnection) connection;
addListeners();
sendAllInstances();
sendAllInstances(null);
sendAllDevices();
sendAllLinks();
sendAllHosts();
......@@ -235,11 +240,12 @@ public class TopologyViewWebSocket
}
// Sends all controller nodes to the client as node-added messages.
private void sendAllInstances() {
private void sendAllInstances(String messageType) {
List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
Collections.sort(nodes, NODE_COMPARATOR);
for (ControllerNode node : nodes) {
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node)));
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
messageType));
}
}
......@@ -307,13 +313,14 @@ public class TopologyViewWebSocket
// FIXME: clearly, this is not enough
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchEthDst(dstHost.mac()).build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
MultiPointToSinglePointIntent intent =
new MultiPointToSinglePointIntent(appId, selector, treatment,
ingressPoints, dstHost.location());
trafficEvent = event;
startMonitoring(event);
intentService.submit(intent);
}
......@@ -359,7 +366,6 @@ public class TopologyViewWebSocket
// Subscribes for host traffic messages.
private synchronized void requestAllTraffic(ObjectNode event) {
ObjectNode payload = payload(event);
long sid = startMonitoring(event);
sendMessage(trafficSummaryMessage(sid));
}
......@@ -375,7 +381,6 @@ public class TopologyViewWebSocket
// If there is a hover node, include it in the hosts and find intents.
String hover = string(payload, "hover");
Set<Intent> hoverIntents;
if (!isNullOrEmpty(hover)) {
addHover(hosts, devices, hover);
}
......@@ -447,6 +452,7 @@ public class TopologyViewWebSocket
// Adds all internal listeners.
private void addListeners() {
clusterService.addListener(clusterListener);
mastershipService.addListener(mastershipListener);
deviceService.addListener(deviceListener);
linkService.addListener(linkListener);
hostService.addListener(hostListener);
......@@ -458,6 +464,7 @@ public class TopologyViewWebSocket
if (!listenersRemoved) {
listenersRemoved = true;
clusterService.removeListener(clusterListener);
mastershipService.removeListener(mastershipListener);
deviceService.removeListener(deviceListener);
linkService.removeListener(linkListener);
hostService.removeListener(hostListener);
......@@ -469,7 +476,17 @@ public class TopologyViewWebSocket
private class InternalClusterListener implements ClusterEventListener {
@Override
public void event(ClusterEvent event) {
sendMessage(instanceMessage(event));
sendMessage(instanceMessage(event, null));
}
}
// Mastership change listener
private class InternalMastershipListener implements MastershipListener {
@Override
public void event(MastershipEvent event) {
sendAllInstances("updateInstance");
Device device = deviceService.getDevice(event.subject());
sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
}
}
......
......@@ -123,10 +123,15 @@
// TODO: tune colors for light and dark themes
// blue purple pink mustard cyan green red
var lightNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
lightMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'],
darkNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
darkMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'];
//var lightNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
// lightMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'],
// darkNorm = ['#1f77b4', '#9467bd', '#e377c2', '#bcbd22', '#17becf', '#2ca02c', '#d62728'],
// darkMute = ['#aec7e8', '#c5b0d5', '#f7b6d2', '#dbdb8d', '#9edae5', '#98df8a', '#ff9896'];
var lightNorm = ['#3F587F', '#77533D', '#C94E30', '#892D78', '#138C62', '#006D72', '#59AD00'],
lightMute = ['#56657C', '#665F57', '#C68C7F', '#876E82', '#68897E', '#4E6F70', '#93AA7B'],
darkNorm = ['#3F587F', '#77533D', '#C94E30', '#892D78', '#138C62', '#006D72', '#59AD00'],
darkMute = ['#56657C', '#665F57', '#C68C7F', '#876E82', '#68897E', '#4E6F70', '#93AA7B'];
function cat7() {
var colors = {
......
......@@ -51,7 +51,7 @@
color: #369;
}
.dark #mast span.title {
color: #acf;
color: #eee;
}
#mast span.right {
......
......@@ -356,7 +356,7 @@ svg .node.host circle {
fill: #888;
}
#topo-oibox .online svg text {
fill: #000;
fill: #eee;
}
#topo-oibox svg text.instTitle {
......
......@@ -1088,8 +1088,8 @@
svg.append('rect').attr(rectAttr);
appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node');
appendBadge(svg, c.birdOx, c.birdOy, c.birdDim, '#bird');
//appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node');
appendBadge(svg, 14, 14, 28, '#bird');
if (d.uiAttached) {
attachUiBadge(svg);
......