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
diff --git a/apps/layout/src/main/java/org/onosproject/layout/AccessNetworkLayout.java b/apps/layout/src/main/java/org/onosproject/layout/AccessNetworkLayout.java
index 02917f1..9f4f39d 100644
--- a/apps/layout/src/main/java/org/onosproject/layout/AccessNetworkLayout.java
+++ b/apps/layout/src/main/java/org/onosproject/layout/AccessNetworkLayout.java
@@ -27,6 +27,7 @@
 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 @@
  */
 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 @@
         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 @@
         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 @@
                     .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 @@
                     .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 @@
                     .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 @@
     }
 
     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);
     }
 
 }
diff --git a/apps/layout/src/main/java/org/onosproject/layout/LayoutOverlayMessageHandler.java b/apps/layout/src/main/java/org/onosproject/layout/LayoutOverlayMessageHandler.java
new file mode 100644
index 0000000..f66f1bd
--- /dev/null
+++ b/apps/layout/src/main/java/org/onosproject/layout/LayoutOverlayMessageHandler.java
@@ -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;
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/apps/layout/src/main/java/org/onosproject/layout/RoleBasedLayoutManager.java b/apps/layout/src/main/java/org/onosproject/layout/RoleBasedLayoutManager.java
index 0340c7c..932e626 100644
--- a/apps/layout/src/main/java/org/onosproject/layout/RoleBasedLayoutManager.java
+++ b/apps/layout/src/main/java/org/onosproject/layout/RoleBasedLayoutManager.java
@@ -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.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 @@
 
     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 @@
     @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.
      *
diff --git a/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.css b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.css
new file mode 100644
index 0000000..8aa18f6
--- /dev/null
+++ b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.css
@@ -0,0 +1,2 @@
+/* css for layout topology overlay  */
+
diff --git a/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.html b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.html
new file mode 100644
index 0000000..1f76225
--- /dev/null
+++ b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.html
@@ -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>
diff --git a/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.js b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.js
new file mode 100644
index 0000000..729d117
--- /dev/null
+++ b/apps/layout/src/main/resources/app/view/tlTopov/tlTopov.js
@@ -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
+            };
+        }]);
+}());
diff --git a/apps/layout/src/main/resources/app/view/tlTopov/tlTopovOverlay.js b/apps/layout/src/main/resources/app/view/tlTopov/tlTopovOverlay.js
new file mode 100644
index 0000000..aba7d14
--- /dev/null
+++ b/apps/layout/src/main/resources/app/view/tlTopov/tlTopovOverlay.js
@@ -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);
+            }]);
+}());
diff --git a/apps/layout/src/main/resources/tlTopov/css.html b/apps/layout/src/main/resources/tlTopov/css.html
new file mode 100644
index 0000000..12810e7
--- /dev/null
+++ b/apps/layout/src/main/resources/tlTopov/css.html
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/tlTopov/tlTopov.css">
\ No newline at end of file
diff --git a/apps/layout/src/main/resources/tlTopov/js.html b/apps/layout/src/main/resources/tlTopov/js.html
new file mode 100644
index 0000000..85afb32
--- /dev/null
+++ b/apps/layout/src/main/resources/tlTopov/js.html
@@ -0,0 +1,2 @@
+<script src="app/view/tlTopov/tlTopov.js"></script>
+<script src="app/view/tlTopov/tlTopovOverlay.js"></script>
\ No newline at end of file