| /* |
| * 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.collect.HashMultiset; |
| import com.google.common.collect.Multiset; |
| import com.google.common.collect.Sets; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.Host; |
| 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; |
| |
| /** |
| * Arranges access network according to roles assigned to devices and hosts. |
| */ |
| public class AccessNetworkLayout extends LayoutAlgorithm { |
| |
| 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 double gatewayX = 1500.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)) { |
| String role; |
| |
| // Does the device have any hosts attached? If not, it's a spine |
| if (hostService.getConnectedHosts(device.id()).isEmpty()) { |
| // Does the device have any aggregate links to other devices? |
| Multiset<DeviceId> destinations = HashMultiset.create(); |
| linkService.getDeviceEgressLinks(device.id()).stream() |
| .map(l -> l.dst().deviceId()).forEach(destinations::add); |
| |
| // If yes, it's the main spine; otherwise it's an aggregate spine |
| role = destinations.entrySet().stream().anyMatch(e -> e.getCount() > 1) ? |
| SPINE : AGGREGATION; |
| } else { |
| // Does the device have any multi-home hosts attached? |
| // If yes, it's a service leaf; otherwise it's an access leaf |
| role = hostService.getConnectedHosts(device.id()).stream() |
| .map(Host::locations).anyMatch(s -> s.size() > 1) ? |
| LEAF : ACCESS; |
| } |
| deviceCategories.put(role, device.id()); |
| } |
| return true; |
| } |
| |
| @Override |
| protected boolean classify(Host host) { |
| if (!super.classify(host)) { |
| // Is the host attached to an access leaf? |
| // If so, it's an access host; otherwise it's a service host or gateway |
| String role = host.locations().stream().map(ConnectPoint::deviceId) |
| .anyMatch(d -> deviceCategories.get(ACCESS) |
| .contains(deviceService.getDevice(d).id())) ? |
| ACCESS : COMPUTE; |
| hostCategories.put(role, host.id()); |
| } |
| return true; |
| } |
| |
| @Override |
| public void apply() { |
| placeSpines(); |
| placeServiceLeavesAndHosts(); |
| placeAccessLeavesAndHosts(); |
| } |
| |
| private void placeSpines() { |
| spine = 1; |
| List<DeviceId> spines = deviceCategories.get(SPINE); |
| spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR) |
| .forEach(d -> place(d, c(spine++, spines.size(), spinesGap), spineY)); |
| } |
| |
| private void placeServiceLeavesAndHosts() { |
| List<DeviceId> leaves = deviceCategories.get(LEAF); |
| List<HostId> computes = hostCategories.get(COMPUTE); |
| List<HostId> gateways = hostCategories.get(GATEWAY); |
| Set<HostId> placed = Sets.newHashSet(); |
| |
| serviceLeaf = 1; |
| leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> { |
| gateway = 1; |
| place(id, c(serviceLeaf++, leaves.size(), serviceGap), serviceY); |
| |
| List<HostId> gwHosts = hostService.getConnectedHosts(id).stream() |
| .map(Host::id) |
| .filter(gateways::contains) |
| .filter(hid -> !placed.contains(hid)) |
| .sorted(Comparators.ELEMENT_ID_COMPARATOR) |
| .collect(Collectors.toList()); |
| |
| gwHosts.forEach(hid -> { |
| place(hid, serviceLeaf <= 2 ? -gatewayX : gatewayX, |
| c(gateway++, gwHosts.size(), gatewayGap, gatewayOffset)); |
| placed.add(hid); |
| }); |
| |
| List<HostId> hosts = hostService.getConnectedHosts(id).stream() |
| .map(Host::id) |
| .filter(computes::contains) |
| .filter(hid -> !placed.contains(hid)) |
| .sorted(Comparators.ELEMENT_ID_COMPARATOR) |
| .collect(Collectors.toList()); |
| |
| placeHostBlock(hosts, serviceLeaf <= 2 ? -computeOffset : computeOffset, |
| computeY, computePerRow, computeRowGap, |
| serviceLeaf <= 2 ? -colGap : colGap); |
| placed.addAll(hosts); |
| }); |
| } |
| |
| private void placeAccessLeavesAndHosts() { |
| List<DeviceId> spines = deviceCategories.get(AGGREGATION); |
| List<DeviceId> leaves = deviceCategories.get(ACCESS); |
| Set<DeviceId> placed = Sets.newHashSet(); |
| |
| aggregation = 1; |
| accessLeaf = 1; |
| if (spines.isEmpty()) { |
| leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR) |
| .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed)); |
| } else { |
| spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> { |
| place(id, c(aggregation++, spines.size(), aggregationGap), aggregationY); |
| linkService.getDeviceEgressLinks(id).stream() |
| .map(l -> l.dst().deviceId()) |
| .filter(leaves::contains) |
| .filter(lid -> !placed.contains(lid)) |
| .sorted(Comparators.ELEMENT_ID_COMPARATOR) |
| .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed)); |
| }); |
| } |
| } |
| |
| private void placeAccessLeafAndHosts(DeviceId leafId, int leafCount, Set<DeviceId> placed) { |
| 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, hostsY, |
| hostsPerRow, rowGap, colGap); |
| } |
| |
| } |