| /* |
| * 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.Set; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Arranges access network according to roles assigned to devices and hosts. |
| */ |
| 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 static final int HOSTS_PER_ROW = 6; |
| private static final int COMPUTE_PER_ROW = 12; |
| |
| 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 = 400.0; |
| private static final double GATEWAY_GAP = 200.0; |
| private static final double GATEWAY_OFFSET = -200.0; |
| |
| private int spine, aggregation, accessLeaf, serviceLeaf, gateway; |
| |
| @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()), SPINE_Y)); |
| } |
| |
| 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()), SERVICE_Y); |
| |
| 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 ? -GATEWAY_X : GATEWAY_X, |
| c(gateway++, gwHosts.size(), GATEWAY_GAP, GATEWAY_OFFSET)); |
| 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 ? -COMPUTE_OFFSET : COMPUTE_OFFSET, |
| COMPUTE_Y, COMPUTE_PER_ROW, COMPUTE_ROW_GAP, |
| serviceLeaf <= 2 ? -COL_GAP : COL_GAP); |
| 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()), AGGREGATION_Y); |
| 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); |
| place(leafId, x, ACCESS_Y); |
| 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); |
| } |
| |
| } |