blob: 8f3d02feeb45b883743944a59daf72813e95ca86 [file] [log] [blame]
Thomas Vachuska0d933862018-04-06 00:29:30 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.layout;
18
19import com.google.common.collect.HashMultiset;
20import com.google.common.collect.Multiset;
21import com.google.common.collect.Sets;
22import org.onosproject.net.ConnectPoint;
23import org.onosproject.net.Device;
24import org.onosproject.net.DeviceId;
25import org.onosproject.net.Host;
26import org.onosproject.net.HostId;
27import org.onosproject.utils.Comparators;
28
29import java.util.List;
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -070030import java.util.Map;
Thomas Vachuska0d933862018-04-06 00:29:30 -070031import java.util.Set;
32import java.util.stream.Collectors;
33
34/**
35 * Arranges access network according to roles assigned to devices and hosts.
36 */
37public class AccessNetworkLayout extends LayoutAlgorithm {
38
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -070039 private double computeY = -350.0;
40 private double serviceY = -200.0;
41 private double spineY = 0.0;
42 private double aggregationY = +200.0;
43 private double accessY = +400.0;
44 private double hostsY = +550.0;
Thomas Vachuska0d933862018-04-06 00:29:30 -070045
Thomas Vachuska6f6b662f2018-05-07 15:01:06 -070046 private double gatewayX = 1500.0;
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -070047 private double rowGap = 70;
48 private double computeRowGap = -120;
49 private double colGap = 54;
50 private double computeOffset = 800.0;
51 private double gatewayGap = 200.0;
52 private double gatewayOffset = -200.0;
53 private double serviceGap = 800;
54 private int computePerRow = 25;
55 private double spinesGap = 800;
56 private double aggregationGap = 400;
57 private double accessGap = 400;
58 private int hostsPerRow = 6;
Thomas Vachuska9f0e15b2018-04-26 16:25:33 -040059
Thomas Vachuska3fd1cee2018-04-25 19:07:06 -040060 private int spine, aggregation, accessLeaf, serviceLeaf, gateway;
Thomas Vachuska0d933862018-04-06 00:29:30 -070061
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -070062 /**
63 * Creates the network layout using default layout options.
64 */
65 public AccessNetworkLayout() {
66 }
67
68 /**
69 * Creates the network layout using the specified layout property overrides.
70 *
71 * @param custom overrides of the default layout properties
72 */
73 public AccessNetworkLayout(Map<String, Object> custom) {
74 computeY = (double) custom.getOrDefault("computeY", computeY);
75 serviceY = (double) custom.getOrDefault("serviceY", serviceY);
76 spineY = (double) custom.getOrDefault("spineY", spineY);
77 aggregationY = (double) custom.getOrDefault("aggregationY", aggregationY);
78 accessY = (double) custom.getOrDefault("accessY", accessY);
79 hostsY = (double) custom.getOrDefault("hostsY", hostsY);
80 gatewayX = (double) custom.getOrDefault("gatewayX", gatewayX);
81 rowGap = (double) custom.getOrDefault("rowGap", rowGap);
82 computeRowGap = (double) custom.getOrDefault("computeRowGap", computeRowGap);
83 colGap = (double) custom.getOrDefault("colGap", colGap);
84 computeOffset = (double) custom.getOrDefault("computeOffset", computeOffset);
85 gatewayGap = (double) custom.getOrDefault("gatewayGap", gatewayGap);
86 gatewayOffset = (double) custom.getOrDefault("gatewayOffset", gatewayOffset);
87 serviceGap = (double) custom.getOrDefault("serviceGap", serviceGap);
88 computePerRow = (int) custom.getOrDefault("computePerRow", computePerRow);
89 spinesGap = (double) custom.getOrDefault("spinesGap", spinesGap);
90 aggregationGap = (double) custom.getOrDefault("aggregationGap", aggregationGap);
91 accessGap = (double) custom.getOrDefault("accessGap", accessGap);
92 hostsPerRow = (int) custom.getOrDefault("hostsPerRow", hostsPerRow);
93 }
94
Thomas Vachuska0d933862018-04-06 00:29:30 -070095 @Override
96 protected boolean classify(Device device) {
97 if (!super.classify(device)) {
98 String role;
99
100 // Does the device have any hosts attached? If not, it's a spine
101 if (hostService.getConnectedHosts(device.id()).isEmpty()) {
102 // Does the device have any aggregate links to other devices?
103 Multiset<DeviceId> destinations = HashMultiset.create();
104 linkService.getDeviceEgressLinks(device.id()).stream()
105 .map(l -> l.dst().deviceId()).forEach(destinations::add);
106
107 // If yes, it's the main spine; otherwise it's an aggregate spine
108 role = destinations.entrySet().stream().anyMatch(e -> e.getCount() > 1) ?
109 SPINE : AGGREGATION;
110 } else {
111 // Does the device have any multi-home hosts attached?
112 // If yes, it's a service leaf; otherwise it's an access leaf
113 role = hostService.getConnectedHosts(device.id()).stream()
114 .map(Host::locations).anyMatch(s -> s.size() > 1) ?
115 LEAF : ACCESS;
116 }
117 deviceCategories.put(role, device.id());
118 }
119 return true;
120 }
121
122 @Override
123 protected boolean classify(Host host) {
124 if (!super.classify(host)) {
125 // Is the host attached to an access leaf?
126 // If so, it's an access host; otherwise it's a service host or gateway
127 String role = host.locations().stream().map(ConnectPoint::deviceId)
128 .anyMatch(d -> deviceCategories.get(ACCESS)
129 .contains(deviceService.getDevice(d).id())) ?
130 ACCESS : COMPUTE;
131 hostCategories.put(role, host.id());
132 }
133 return true;
134 }
135
136 @Override
137 public void apply() {
138 placeSpines();
139 placeServiceLeavesAndHosts();
140 placeAccessLeavesAndHosts();
141 }
142
143 private void placeSpines() {
144 spine = 1;
145 List<DeviceId> spines = deviceCategories.get(SPINE);
146 spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR)
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700147 .forEach(d -> place(d, c(spine++, spines.size(), spinesGap), spineY));
Thomas Vachuska0d933862018-04-06 00:29:30 -0700148 }
149
150 private void placeServiceLeavesAndHosts() {
151 List<DeviceId> leaves = deviceCategories.get(LEAF);
152 List<HostId> computes = hostCategories.get(COMPUTE);
153 List<HostId> gateways = hostCategories.get(GATEWAY);
154 Set<HostId> placed = Sets.newHashSet();
155
156 serviceLeaf = 1;
157 leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
158 gateway = 1;
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700159 place(id, c(serviceLeaf++, leaves.size(), serviceGap), serviceY);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700160
161 List<HostId> gwHosts = hostService.getConnectedHosts(id).stream()
162 .map(Host::id)
163 .filter(gateways::contains)
164 .filter(hid -> !placed.contains(hid))
165 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
166 .collect(Collectors.toList());
167
168 gwHosts.forEach(hid -> {
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700169 place(hid, serviceLeaf <= 2 ? -gatewayX : gatewayX,
170 c(gateway++, gwHosts.size(), gatewayGap, gatewayOffset));
Thomas Vachuska0d933862018-04-06 00:29:30 -0700171 placed.add(hid);
172 });
173
Thomas Vachuska0d933862018-04-06 00:29:30 -0700174 List<HostId> hosts = hostService.getConnectedHosts(id).stream()
175 .map(Host::id)
176 .filter(computes::contains)
177 .filter(hid -> !placed.contains(hid))
178 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
179 .collect(Collectors.toList());
180
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700181 placeHostBlock(hosts, serviceLeaf <= 2 ? -computeOffset : computeOffset,
182 computeY, computePerRow, computeRowGap,
183 serviceLeaf <= 2 ? -colGap : colGap);
Thomas Vachuska3fd1cee2018-04-25 19:07:06 -0400184 placed.addAll(hosts);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700185 });
186 }
187
188 private void placeAccessLeavesAndHosts() {
189 List<DeviceId> spines = deviceCategories.get(AGGREGATION);
190 List<DeviceId> leaves = deviceCategories.get(ACCESS);
191 Set<DeviceId> placed = Sets.newHashSet();
192
193 aggregation = 1;
194 accessLeaf = 1;
Thomas Vachuska834cc082018-04-24 22:39:22 -0400195 if (spines.isEmpty()) {
Thomas Vachuska3fd1cee2018-04-25 19:07:06 -0400196 leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR)
197 .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed));
Thomas Vachuska834cc082018-04-24 22:39:22 -0400198 } else {
199 spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700200 place(id, c(aggregation++, spines.size(), aggregationGap), aggregationY);
Thomas Vachuska834cc082018-04-24 22:39:22 -0400201 linkService.getDeviceEgressLinks(id).stream()
202 .map(l -> l.dst().deviceId())
203 .filter(leaves::contains)
204 .filter(lid -> !placed.contains(lid))
205 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
206 .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed));
207 });
208 }
209 }
210
211 private void placeAccessLeafAndHosts(DeviceId leafId, int leafCount, Set<DeviceId> placed) {
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700212 double x = c(accessLeaf++, leafCount, accessGap);
213 place(leafId, x, accessY);
Thomas Vachuska834cc082018-04-24 22:39:22 -0400214 placed.add(leafId);
215 placeHostBlock(hostService.getConnectedHosts(leafId).stream()
216 .map(Host::id)
217 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
Thomas Vachuska5b48d6c2018-04-27 18:24:27 -0700218 .collect(Collectors.toList()), x, hostsY,
219 hostsPerRow, rowGap, colGap);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700220 }
221
222}