blob: 02917f1b8eb98a8b65cc80395020ae19393c48a8 [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;
30import java.util.Set;
31import java.util.stream.Collectors;
32
33/**
34 * Arranges access network according to roles assigned to devices and hosts.
35 */
36public class AccessNetworkLayout extends LayoutAlgorithm {
37
38 private static final double COMPUTE_Y = -400.0;
39 private static final double SERVICE_Y = -200.0;
40 private static final double SPINE_Y = 0.0;
41 private static final double AGGREGATION_Y = +200.0;
42 private static final double ACCESS_Y = +400.0;
43 private static final double HOSTS_Y = +700.0;
44 private static final double GATEWAY_X = 900.0;
45
Thomas Vachuska0d933862018-04-06 00:29:30 -070046 private static final double ROW_GAP = 70;
Thomas Vachuska1aa52572018-04-25 19:07:06 -040047 private static final double COMPUTE_ROW_GAP = -120;
48 private static final double COL_GAP = 54;
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -040049 private static final double COMPUTE_OFFSET = 800.0;
Thomas Vachuska0d933862018-04-06 00:29:30 -070050 private static final double GATEWAY_GAP = 200.0;
51 private static final double GATEWAY_OFFSET = -200.0;
52
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -040053 private static final double SERVICE_GAP = 800;
54 private static final int COMPUTE_PER_ROW = 25;
55
56 private static final double SPINES_GAP = 800;
57 private static final double AGGREGATION_GAP = 400;
58 private static final double ACCESS_GAP = 400;
59 private static final int HOSTS_PER_ROW = 6;
60
Thomas Vachuska1aa52572018-04-25 19:07:06 -040061 private int spine, aggregation, accessLeaf, serviceLeaf, gateway;
Thomas Vachuska0d933862018-04-06 00:29:30 -070062
63 @Override
64 protected boolean classify(Device device) {
65 if (!super.classify(device)) {
66 String role;
67
68 // Does the device have any hosts attached? If not, it's a spine
69 if (hostService.getConnectedHosts(device.id()).isEmpty()) {
70 // Does the device have any aggregate links to other devices?
71 Multiset<DeviceId> destinations = HashMultiset.create();
72 linkService.getDeviceEgressLinks(device.id()).stream()
73 .map(l -> l.dst().deviceId()).forEach(destinations::add);
74
75 // If yes, it's the main spine; otherwise it's an aggregate spine
76 role = destinations.entrySet().stream().anyMatch(e -> e.getCount() > 1) ?
77 SPINE : AGGREGATION;
78 } else {
79 // Does the device have any multi-home hosts attached?
80 // If yes, it's a service leaf; otherwise it's an access leaf
81 role = hostService.getConnectedHosts(device.id()).stream()
82 .map(Host::locations).anyMatch(s -> s.size() > 1) ?
83 LEAF : ACCESS;
84 }
85 deviceCategories.put(role, device.id());
86 }
87 return true;
88 }
89
90 @Override
91 protected boolean classify(Host host) {
92 if (!super.classify(host)) {
93 // Is the host attached to an access leaf?
94 // If so, it's an access host; otherwise it's a service host or gateway
95 String role = host.locations().stream().map(ConnectPoint::deviceId)
96 .anyMatch(d -> deviceCategories.get(ACCESS)
97 .contains(deviceService.getDevice(d).id())) ?
98 ACCESS : COMPUTE;
99 hostCategories.put(role, host.id());
100 }
101 return true;
102 }
103
104 @Override
105 public void apply() {
106 placeSpines();
107 placeServiceLeavesAndHosts();
108 placeAccessLeavesAndHosts();
109 }
110
111 private void placeSpines() {
112 spine = 1;
113 List<DeviceId> spines = deviceCategories.get(SPINE);
114 spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR)
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -0400115 .forEach(d -> place(d, c(spine++, spines.size(), SPINES_GAP), SPINE_Y));
Thomas Vachuska0d933862018-04-06 00:29:30 -0700116 }
117
118 private void placeServiceLeavesAndHosts() {
119 List<DeviceId> leaves = deviceCategories.get(LEAF);
120 List<HostId> computes = hostCategories.get(COMPUTE);
121 List<HostId> gateways = hostCategories.get(GATEWAY);
122 Set<HostId> placed = Sets.newHashSet();
123
124 serviceLeaf = 1;
125 leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
126 gateway = 1;
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -0400127 place(id, c(serviceLeaf++, leaves.size(), SERVICE_GAP), SERVICE_Y);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700128
129 List<HostId> gwHosts = hostService.getConnectedHosts(id).stream()
130 .map(Host::id)
131 .filter(gateways::contains)
132 .filter(hid -> !placed.contains(hid))
133 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
134 .collect(Collectors.toList());
135
136 gwHosts.forEach(hid -> {
137 place(hid, serviceLeaf <= 2 ? -GATEWAY_X : GATEWAY_X,
138 c(gateway++, gwHosts.size(), GATEWAY_GAP, GATEWAY_OFFSET));
139 placed.add(hid);
140 });
141
Thomas Vachuska0d933862018-04-06 00:29:30 -0700142 List<HostId> hosts = hostService.getConnectedHosts(id).stream()
143 .map(Host::id)
144 .filter(computes::contains)
145 .filter(hid -> !placed.contains(hid))
146 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
147 .collect(Collectors.toList());
148
Thomas Vachuska1aa52572018-04-25 19:07:06 -0400149 placeHostBlock(hosts, serviceLeaf <= 2 ? -COMPUTE_OFFSET : COMPUTE_OFFSET,
150 COMPUTE_Y, COMPUTE_PER_ROW, COMPUTE_ROW_GAP,
151 serviceLeaf <= 2 ? -COL_GAP : COL_GAP);
152 placed.addAll(hosts);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700153 });
154 }
155
156 private void placeAccessLeavesAndHosts() {
157 List<DeviceId> spines = deviceCategories.get(AGGREGATION);
158 List<DeviceId> leaves = deviceCategories.get(ACCESS);
159 Set<DeviceId> placed = Sets.newHashSet();
160
161 aggregation = 1;
162 accessLeaf = 1;
Thomas Vachuska0c4696a2018-04-24 22:39:22 -0400163 if (spines.isEmpty()) {
Thomas Vachuska1aa52572018-04-25 19:07:06 -0400164 leaves.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR)
165 .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed));
Thomas Vachuska0c4696a2018-04-24 22:39:22 -0400166 } else {
167 spines.stream().sorted(Comparators.ELEMENT_ID_COMPARATOR).forEach(id -> {
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -0400168 place(id, c(aggregation++, spines.size(), AGGREGATION_GAP), AGGREGATION_Y);
Thomas Vachuska0c4696a2018-04-24 22:39:22 -0400169 linkService.getDeviceEgressLinks(id).stream()
170 .map(l -> l.dst().deviceId())
171 .filter(leaves::contains)
172 .filter(lid -> !placed.contains(lid))
173 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
174 .forEach(lid -> placeAccessLeafAndHosts(lid, leaves.size(), placed));
175 });
176 }
177 }
178
179 private void placeAccessLeafAndHosts(DeviceId leafId, int leafCount, Set<DeviceId> placed) {
Thomas Vachuskab6fc05d2018-04-26 16:25:33 -0400180 double x = c(accessLeaf++, leafCount, ACCESS_GAP);
Thomas Vachuska0c4696a2018-04-24 22:39:22 -0400181 place(leafId, x, ACCESS_Y);
182 placed.add(leafId);
183 placeHostBlock(hostService.getConnectedHosts(leafId).stream()
184 .map(Host::id)
185 .sorted(Comparators.ELEMENT_ID_COMPARATOR)
186 .collect(Collectors.toList()), x, HOSTS_Y,
187 HOSTS_PER_ROW, ROW_GAP, COL_GAP);
Thomas Vachuska0d933862018-04-06 00:29:30 -0700188 }
189
190}