Adding topology auto-layout.
Change-Id: I2b9e0b5808b8d193e8d08b7fe6ffdb007b083809
diff --git a/apps/layout/src/main/java/org/onosproject/layout/LayoutAlgorithm.java b/apps/layout/src/main/java/org/onosproject/layout/LayoutAlgorithm.java
new file mode 100644
index 0000000..f9e062b
--- /dev/null
+++ b/apps/layout/src/main/java/org/onosproject/layout/LayoutAlgorithm.java
@@ -0,0 +1,225 @@
+/*
+ * 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.google.common.base.MoreObjects;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.config.basics.BasicHostConfig;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+
+import java.util.Collection;
+
+/**
+ * Represents a topology layout algorithm.
+ */
+public abstract class LayoutAlgorithm {
+
+ public static final String SPINE = "spine";
+ public static final String AGGREGATION = "aggregation";
+ public static final String LEAF = "leaf";
+ public static final String ACCESS = "access";
+ public static final String GATEWAY = "gateway";
+ public static final String COMPUTE = "compute";
+
+ protected DeviceService deviceService;
+ protected HostService hostService;
+ protected LinkService linkService;
+ protected NetworkConfigService netConfigService;
+
+ protected ListMultimap<String, DeviceId> deviceCategories = ArrayListMultimap.create();
+ protected ListMultimap<String, HostId> hostCategories = ArrayListMultimap.create();
+
+
+ /**
+ * Initializes layout algorithm for operating on device and host inventory.
+ *
+ * @param deviceService device service
+ * @param hostService host service
+ * @param linkService link service
+ * @param networkConfigService net config service
+ */
+ protected void init(DeviceService deviceService,
+ HostService hostService,
+ LinkService linkService,
+ NetworkConfigService networkConfigService) {
+ this.deviceService = deviceService;
+ this.hostService = hostService;
+ this.linkService = linkService;
+ this.netConfigService = networkConfigService;
+ }
+
+ /**
+ * Places the specified device on the layout grid.
+ *
+ * @param id device identifier
+ * @param x grid X
+ * @param y grid Y
+ */
+ protected void place(DeviceId id, double x, double y) {
+ netConfigService.addConfig(id, BasicDeviceConfig.class)
+ .gridX(x).gridY(y).locType("grid").apply();
+ }
+
+ /**
+ * Places the specified device on the layout grid.
+ *
+ * @param id host identifier
+ * @param x grid X
+ * @param y grid Y
+ */
+ protected void place(HostId id, double x, double y) {
+ netConfigService.addConfig(id, BasicHostConfig.class)
+ .gridX(x).gridY(y).locType("grid").apply();
+ }
+
+ /**
+ * Computes grid coordinate for the i-th element of n-elements in a tier
+ * using a default gap of 400.
+ *
+ * @param i element index
+ * @param n number of elements
+ * @return grid Y
+ */
+ protected double c(int i, int n) {
+ return c(i, n, 400);
+ }
+
+ /**
+ * Computes grid coordinate for the i-th element of n-elements in a tier.
+ *
+ * @param i element index
+ * @param n number of elements
+ * @param gap gap width
+ * @return grid Y
+ */
+ protected double c(int i, int n, double gap) {
+ return c(i, n, gap, 0);
+ }
+
+ /**
+ * Computes grid coordinate for the i-th element of n-elements in a tier.
+ *
+ * @param i element index
+ * @param n number of elements
+ * @param gap gap width
+ * @param offset additional Y offset
+ * @return grid Y
+ */
+ protected double c(int i, int n, double gap, double offset) {
+ return gap * (i - 1) - (gap * (n - 1)) / 2 + offset;
+ }
+
+ /**
+ * Places the specified collection of hosts (all presumably connected to
+ * the same network device) in a block.
+ *
+ * @param hosts hosts to place
+ * @param gridX grid X of the top of the block
+ * @param gridY grid Y of the center of the block
+ * @param hostsPerRow number of hosts in a 'row'
+ * @param rowGap gap width between rows
+ * @param colGap gap width between columns
+ */
+ protected void placeHostBlock(Collection<HostId> hosts,
+ double gridX, double gridY, int hostsPerRow,
+ double rowGap, double colGap) {
+ double yStep = rowGap / hostsPerRow;
+ double y = gridY;
+ double x = gridX - (colGap * (hostsPerRow - 1)) / 2;
+ int i = 1;
+
+ for (HostId id : hosts) {
+ place(id, x, y);
+ if ((i % hostsPerRow) == 0) {
+ x = gridX - (colGap * (hostsPerRow - 1)) / 2;
+ } else {
+ x += colGap;
+ y += yStep;
+ }
+ i++;
+ }
+ }
+
+ /**
+ * Applies device and host classifications.
+ */
+ public void classify() {
+ deviceService.getDevices().forEach(this::classify);
+ hostService.getHosts().forEach(this::classify);
+ }
+
+ /**
+ * Classifies the specified device.
+ *
+ * @param device device to be classified
+ * @return true if classified
+ */
+ protected boolean classify(Device device) {
+ BasicDeviceConfig cfg = netConfigService.getConfig(device.id(), BasicDeviceConfig.class);
+ if (cfg != null && !cfg.roles().isEmpty()) {
+ cfg.roles().forEach(r -> deviceCategories.put(r, device.id()));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Classifies the specified host.
+ *
+ * @param host host to be classified
+ * @return true if classified
+ */
+ protected boolean classify(Host host) {
+ BasicHostConfig cfg = netConfigService.getConfig(host.id(), BasicHostConfig.class);
+ if (cfg != null && !cfg.roles().isEmpty()) {
+ cfg.roles().forEach(r -> hostCategories.put(r, host.id()));
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Applies the specified layout algorithm.
+ */
+ abstract void apply();
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("deviceCategories", count(deviceCategories))
+ .add("hostCategories", count(hostCategories))
+ .toString();
+ }
+
+ private String count(ListMultimap<String, ? extends ElementId> categories) {
+ StringBuilder sb = new StringBuilder("[ ");
+ categories.keySet().forEach(k -> sb.append(k).append("=").append(categories.get(k).size()).append(" "));
+ return sb.append("]").toString();
+ }
+
+}