Adding topology overlay support for the server-side topo layout app.

Also:
- parametrized access network layout in preparation for multiple variants
- removed WS authentication code temporarily until proper forced-logout is implemented
- updated STC warden environment (test only)

Change-Id: I0adbe60737828db79350e7eb2fc72cf313b78a28
This commit is contained in:
Thomas Vachuska 2018-04-27 18:24:27 -07:00 committed by Charles Chan
parent 4a9c094d1d
commit 5b48d6ca84
11 changed files with 303 additions and 44 deletions

View File

@ -1,5 +1,6 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:JACKSON',
'//lib:org.apache.karaf.shell.console',
'//core/common:onos-core-common',
'//cli:onos-cli',

View File

@ -27,6 +27,7 @@ import org.onosproject.net.HostId;
import org.onosproject.utils.Comparators;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@ -35,31 +36,62 @@ import java.util.stream.Collectors;
*/
public class AccessNetworkLayout extends LayoutAlgorithm {
private static final double COMPUTE_Y = -400.0;
private static final double SERVICE_Y = -200.0;
private static final double SPINE_Y = 0.0;
private static final double AGGREGATION_Y = +200.0;
private static final double ACCESS_Y = +400.0;
private static final double HOSTS_Y = +700.0;
private static final double GATEWAY_X = 900.0;
private double computeY = -350.0;
private double serviceY = -200.0;
private double spineY = 0.0;
private double aggregationY = +200.0;
private double accessY = +400.0;
private double hostsY = +550.0;
private static final double ROW_GAP = 70;
private static final double COMPUTE_ROW_GAP = -120;
private static final double COL_GAP = 54;
private static final double COMPUTE_OFFSET = 800.0;
private static final double GATEWAY_GAP = 200.0;
private static final double GATEWAY_OFFSET = -200.0;
private static final double SERVICE_GAP = 800;
private static final int COMPUTE_PER_ROW = 25;
private static final double SPINES_GAP = 800;
private static final double AGGREGATION_GAP = 400;
private static final double ACCESS_GAP = 400;
private static final int HOSTS_PER_ROW = 6;
private double gatewayX = 900.0;
private double rowGap = 70;
private double computeRowGap = -120;
private double colGap = 54;
private double computeOffset = 800.0;
private double gatewayGap = 200.0;
private double gatewayOffset = -200.0;
private double serviceGap = 800;
private int computePerRow = 25;
private double spinesGap = 800;
private double aggregationGap = 400;
private double accessGap = 400;
private int hostsPerRow = 6;
private int spine, aggregation, accessLeaf, serviceLeaf, gateway;
/**
* Creates the network layout using default layout options.
*/
public AccessNetworkLayout() {
}
/**
* Creates the network layout using the specified layout property overrides.
*
* @param custom overrides of the default layout properties
*/
public AccessNetworkLayout(Map<String, Object> custom) {
computeY = (double) custom.getOrDefault("computeY", computeY);
serviceY = (double) custom.getOrDefault("serviceY", serviceY);
spineY = (double) custom.getOrDefault("spineY", spineY);
aggregationY = (double) custom.getOrDefault("aggregationY", aggregationY);
accessY = (double) custom.getOrDefault("accessY", accessY);
hostsY = (double) custom.getOrDefault("hostsY", hostsY);
gatewayX = (double) custom.getOrDefault("gatewayX", gatewayX);
rowGap = (double) custom.getOrDefault("rowGap", rowGap);
computeRowGap = (double) custom.getOrDefault("computeRowGap", computeRowGap);
colGap = (double) custom.getOrDefault("colGap", colGap);
computeOffset = (double) custom.getOrDefault("computeOffset", computeOffset);
gatewayGap = (double) custom.getOrDefault("gatewayGap", gatewayGap);
gatewayOffset = (double) custom.getOrDefault("gatewayOffset", gatewayOffset);
serviceGap = (double) custom.getOrDefault("serviceGap", serviceGap);
computePerRow = (int) custom.getOrDefault("computePerRow", computePerRow);
spinesGap = (double) custom.getOrDefault("spinesGap", spinesGap);
aggregationGap = (double) custom.getOrDefault("aggregationGap", aggregationGap);
accessGap = (double) custom.getOrDefault("accessGap", accessGap);
hostsPerRow = (int) custom.getOrDefault("hostsPerRow", hostsPerRow);
}
@Override
protected boolean classify(Device device) {
if (!super.classify(device)) {
@ -112,7 +144,7 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
spine = 1;
List<DeviceId> spines = deviceCategories.get(SPINE);
spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR)
.forEach(d -> place(d, c(spine++, spines.size(), SPINES_GAP), SPINE_Y));
.forEach(d -> place(d, c(spine++, spines.size(), spinesGap), spineY));
}
private void placeServiceLeavesAndHosts() {
@ -124,7 +156,7 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
serviceLeaf = 1;
leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
gateway = 1;
place(id, c(serviceLeaf++, leaves.size(), SERVICE_GAP), SERVICE_Y);
place(id, c(serviceLeaf++, leaves.size(), serviceGap), serviceY);
List<HostId> gwHosts = hostService.getConnectedHosts(id).stream()
.map(Host::id)
@ -134,8 +166,8 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
.collect(Collectors.toList());
gwHosts.forEach(hid -> {
place(hid, serviceLeaf <= 2 ? -GATEWAY_X : GATEWAY_X,
c(gateway++, gwHosts.size(), GATEWAY_GAP, GATEWAY_OFFSET));
place(hid, serviceLeaf <= 2 ? -gatewayX : gatewayX,
c(gateway++, gwHosts.size(), gatewayGap, gatewayOffset));
placed.add(hid);
});
@ -146,9 +178,9 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
.sorted(Comparators.ELEMENT_ID_COMPARATOR)
.collect(Collectors.toList());
placeHostBlock(hosts, serviceLeaf <= 2 ? -COMPUTE_OFFSET : COMPUTE_OFFSET,
COMPUTE_Y, COMPUTE_PER_ROW, COMPUTE_ROW_GAP,
serviceLeaf <= 2 ? -COL_GAP : COL_GAP);
placeHostBlock(hosts, serviceLeaf <= 2 ? -computeOffset : computeOffset,
computeY, computePerRow, computeRowGap,
serviceLeaf <= 2 ? -colGap : colGap);
placed.addAll(hosts);
});
}
@ -165,7 +197,7 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
.forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed));
} else {
spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
place(id, c(aggregation++, spines.size(), AGGREGATION_GAP), AGGREGATION_Y);
place(id, c(aggregation++, spines.size(), aggregationGap), aggregationY);
linkService.getDeviceEgressLinks(id).stream()
.map(l -> l.dst().deviceId())
.filter(leaves::contains)
@ -177,14 +209,14 @@ public class AccessNetworkLayout extends LayoutAlgorithm {
}
private void placeAccessLeafAndHosts(DeviceId leafId, int leafCount, Set<DeviceId> placed) {
double x = c(accessLeaf++, leafCount, ACCESS_GAP);
place(leafId, x, ACCESS_Y);
double x = c(accessLeaf++, leafCount, accessGap);
place(leafId, x, accessY);
placed.add(leafId);
placeHostBlock(hostService.getConnectedHosts(leafId).stream()
.map(Host::id)
.sorted(Comparators.ELEMENT_ID_COMPARATOR)
.collect(Collectors.toList()), x, HOSTS_Y,
HOSTS_PER_ROW, ROW_GAP, COL_GAP);
.collect(Collectors.toList()), x, hostsY,
hostsPerRow, rowGap, colGap);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2018-present Open Networking Foundation
*
* 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.onosproject.layout;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
/**
* ONOS UI Layout Topology-Overlay message handler.
*/
public class LayoutOverlayMessageHandler extends UiMessageHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String DO_LAYOUT = "doLayout";
private static final String TYPE = "type";
RoleBasedLayoutManager layoutManager;
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
layoutManager = directory.get(RoleBasedLayoutManager.class);
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new LayoutHandler()
);
}
private final class LayoutHandler extends RequestHandler {
public LayoutHandler() {
super(DO_LAYOUT);
}
@Override
public void process(ObjectNode payload) {
String algorithm = string(payload, TYPE);
switch (algorithm) {
case "access":
layoutManager.layout(new AccessNetworkLayout());
break;
default:
layoutManager.layout(new DefaultForceLayout());
break;
}
}
}
}

View File

@ -16,6 +16,7 @@
package org.onosproject.layout;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@ -26,9 +27,18 @@ import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.link.LinkService;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiTopoOverlay;
import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Manages automatic layout of the current network elements into one of several
* supported layout variants using roles assigned to network elements using
@ -40,6 +50,30 @@ public class RoleBasedLayoutManager {
private Logger log = LoggerFactory.getLogger(getClass());
private static final String VIEW_ID = "tlTopov";
private static final String OVERLAY_ID = "tl-overlay";
// List of application views
private final List<UiView> uiViews = ImmutableList.of(
new UiViewHidden(VIEW_ID)
);
// Factory for UI message handlers
private final UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(new LayoutOverlayMessageHandler());
// Factory for UI topology overlays
private final UiTopoOverlayFactory topoOverlayFactory =
() -> ImmutableList.of(new UiTopoOverlay(OVERLAY_ID));
// Application UI extension
protected UiExtension extension =
new UiExtension.Builder(getClass().getClassLoader(), uiViews)
.resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory)
.topoOverlayFactory(topoOverlayFactory)
.build();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService networkConfigService;
@ -52,17 +86,21 @@ public class RoleBasedLayoutManager {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
@Activate
protected void activate() {
uiExtensionService.register(extension);
log.info("Started");
}
@Deactivate
protected void deactivate() {
uiExtensionService.unregister(extension);
log.info("Stopped");
}
/**
* Executes the specified layout algorithm.
*

View File

@ -0,0 +1,2 @@
/* css for layout topology overlay */

View File

@ -0,0 +1,4 @@
<!-- partial HTML -->
<div id="ov-tl-topov">
<p>This is a hidden view .. just a placeholder to house the javascript</p>
</div>

View File

@ -0,0 +1,47 @@
/*
* Copyright 2015-present Open Networking Foundation
*
* 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.
*/
/*
Module containing the "business logic" for the layout topology overlay.
*/
(function () {
'use strict';
// injected refs
var $log, flash, wss;
function doLayout(type, description) {
flash.flash(description);
wss.sendEvent('doLayout', {
type: type
});
}
angular.module('ovTlTopov', [])
.factory('LayoutTopovService',
['$log', 'FlashService', 'WebSocketService',
function (_$log_, _flash_, _wss_) {
$log = _$log_;
flash = _flash_;
wss = _wss_;
return {
doLayout: doLayout
};
}]);
}());

View File

@ -0,0 +1,58 @@
// path painter topology overlay - client side
//
// This is the glue that binds our business logic (in ppTopov.js)
// to the overlay framework.
(function () {
'use strict';
// injected refs
var $log, tov, lts;
// our overlay definition
var overlay = {
overlayId: 'tl-overlay',
glyphId: 'm_disjointPaths',
tooltip: 'Algorithmic Layout Overlay',
activate: function () {
$log.debug("Layout topology overlay ACTIVATED");
},
deactivate: function () {
lts.clear();
$log.debug("Layout topology overlay DEACTIVATED");
},
keyBindings: {
0: {
cb: function () {
lts.doLayout('default', 'Default (force-based) Layout');
},
tt: 'Default (force-based) layout',
gid: 'm_fiberSwitch'
},
1: {
cb: function () {
lts.doLayout('access', 'Access Network Layout - separate service leafs');
},
tt: 'Access layout - separate service leafs',
gid: 'm_disjointPaths'
},
_keyOrder: [
'0', '1'
]
}
};
// invoke code to register with the overlay service
angular.module('ovTlTopov')
.run(['$log', 'TopoOverlayService', 'LayoutTopovService',
function (_$log_, _tov_, _lts_) {
$log = _$log_;
tov = _tov_;
lts = _lts_;
tov.register(overlay);
}]);
}());

View File

@ -0,0 +1 @@
<link rel="stylesheet" href="app/view/tlTopov/tlTopov.css">

View File

@ -0,0 +1,2 @@
<script src="app/view/tlTopov/tlTopov.js"></script>
<script src="app/view/tlTopov/tlTopovOverlay.js"></script>

View File

@ -251,18 +251,18 @@ public class UiWebSocket
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
String type = message.path(EVENT).asText(UNKNOWN);
if (sessionToken == null) {
authenticate(type, message);
// if (sessionToken == null) {
// authenticate(type, message);
//
// } else {
UiMessageHandler handler = handlers.get(type);
if (handler != null) {
log.debug("RX message: {}", message);
handler.process(message);
} else {
UiMessageHandler handler = handlers.get(type);
if (handler != null) {
log.debug("RX message: {}", message);
handler.process(message);
} else {
log.warn("No GUI message handler for type {}", type);
}
log.warn("No GUI message handler for type {}", type);
}
// }
} catch (Exception e) {
log.warn("Unable to parse GUI message {} due to {}", data, e);