blob: 5c4fc853673f5f75e9c1977401e075ab4741d94e [file] [log] [blame]
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskac40d4632015-04-09 16:55:03 -07003 *
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 */
16package org.onosproject.provider.nil;
17
18import com.google.common.collect.Lists;
19import org.onlab.osgi.ServiceDirectory;
20import org.onlab.packet.ChassisId;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.NodeId;
Thomas Vachuskacab29d22018-02-21 15:47:29 -080026import org.onosproject.mastership.MastershipAdminService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070027import org.onosproject.mastership.MastershipService;
Thomas Vachuskafd168fc2020-07-31 14:22:05 -070028import org.onosproject.net.AnnotationKeys;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070029import org.onosproject.net.ConnectPoint;
Thomas Vachuskafd168fc2020-07-31 14:22:05 -070030import org.onosproject.net.DefaultAnnotations;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070031import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.Host;
34import org.onosproject.net.HostId;
35import org.onosproject.net.HostLocation;
36import org.onosproject.net.Link;
37import org.onosproject.net.Port;
38import org.onosproject.net.PortNumber;
39import org.onosproject.net.device.DefaultDeviceDescription;
40import org.onosproject.net.device.DefaultPortDescription;
41import org.onosproject.net.device.DeviceAdminService;
42import org.onosproject.net.device.DeviceDescription;
43import org.onosproject.net.device.DeviceEvent;
44import org.onosproject.net.device.DeviceListener;
45import org.onosproject.net.device.DeviceProviderService;
46import org.onosproject.net.device.PortDescription;
47import org.onosproject.net.host.DefaultHostDescription;
48import org.onosproject.net.host.HostDescription;
49import org.onosproject.net.host.HostProviderService;
50import org.onosproject.net.host.HostService;
51import org.onosproject.net.link.DefaultLinkDescription;
52import org.onosproject.net.link.LinkProviderService;
53import org.onosproject.net.link.LinkService;
54import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
57import java.util.List;
58import java.util.concurrent.CountDownLatch;
59import java.util.concurrent.TimeUnit;
60
61import static org.onlab.util.Tools.toHex;
62import static org.onosproject.net.HostId.hostId;
63import static org.onosproject.net.Link.Type.DIRECT;
Thomas Vachuskacab29d22018-02-21 15:47:29 -080064import static org.onosproject.net.MastershipRole.MASTER;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070065import static org.onosproject.net.PortNumber.portNumber;
66import static org.onosproject.net.device.DeviceEvent.Type.*;
67import static org.onosproject.provider.nil.NullProviders.SCHEME;
68
69/**
70 * Abstraction of a provider capable to simulate some network topology.
71 */
72public abstract class TopologySimulator {
73
74 protected final Logger log = LoggerFactory.getLogger(getClass());
75
76 protected String[] topoShape;
77 protected int deviceCount;
78 protected int hostCount;
79
80 protected ServiceDirectory directory;
81 protected NodeId localNode;
82
83 protected ClusterService clusterService;
84 protected MastershipService mastershipService;
Thomas Vachuskacab29d22018-02-21 15:47:29 -080085 protected MastershipAdminService mastershipAdminService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070086
87 protected DeviceAdminService deviceService;
88 protected HostService hostService;
89 protected LinkService linkService;
90
91 protected DeviceProviderService deviceProviderService;
92 protected HostProviderService hostProviderService;
93 protected LinkProviderService linkProviderService;
94
95 protected int maxWaitSeconds = 1;
96 protected int infrastructurePorts = 2;
97 protected CountDownLatch deviceLatch;
98
99 protected final List<DeviceId> deviceIds = Lists.newArrayList();
100
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800101 private final DeviceListener deviceEventCounter = new DeviceEventCounter();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700102
103 /**
104 * Initializes a new topology simulator with access to the specified service
105 * directory and various provider services.
106 *
107 * @param topoShape topology shape specifier
108 * @param deviceCount number of devices in the topology
109 * @param hostCount number of hosts per device
110 * @param directory service directory
111 * @param deviceProviderService device provider service
112 * @param hostProviderService host provider service
113 * @param linkProviderService link provider service
114 */
115 protected void init(String topoShape, int deviceCount, int hostCount,
116 ServiceDirectory directory,
117 DeviceProviderService deviceProviderService,
118 HostProviderService hostProviderService,
119 LinkProviderService linkProviderService) {
120 this.deviceCount = deviceCount;
121 this.hostCount = hostCount;
122 this.directory = directory;
123
124 this.clusterService = directory.get(ClusterService.class);
125 this.mastershipService = directory.get(MastershipService.class);
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800126 this.mastershipAdminService = directory.get(MastershipAdminService.class);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700127
128 this.deviceService = directory.get(DeviceAdminService.class);
129 this.hostService = directory.get(HostService.class);
130 this.linkService = directory.get(LinkService.class);
131 this.deviceProviderService = deviceProviderService;
132 this.hostProviderService = hostProviderService;
133 this.linkProviderService = linkProviderService;
134
135 localNode = clusterService.getLocalNode().id();
136
137 processTopoShape(topoShape);
138 }
139
140 /**
141 * Processes the topology shape specifier.
142 *
143 * @param shape topology shape specifier
144 */
145 protected void processTopoShape(String shape) {
146 this.topoShape = shape.split(",");
147 }
148
149 /**
150 * Sets up network topology simulation.
151 */
152 public void setUpTopology() {
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800153 deviceIds.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700154 prepareForDeviceEvents(deviceCount);
155 createDevices();
156 waitForDeviceEvents();
157
158 createLinks();
159 createHosts();
160 }
161
162 /**
163 * Creates simulated devices.
164 */
165 protected void createDevices() {
166 for (int i = 0; i < deviceCount; i++) {
167 createDevice(i + 1);
168 }
169 }
170
171 /**
172 * Creates simulated links.
173 */
174 protected abstract void createLinks();
175
176 /**
177 * Creates simulated hosts.
178 */
179 protected abstract void createHosts();
180
181 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800182 * Creates simulated device and adds its id to the list of devices ids.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700183 *
184 * @param i index of the device id in the list.
185 */
186 protected void createDevice(int i) {
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800187 createDevice(DeviceId.deviceId(SCHEME + ":" + toHex(i)), i);
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800188 }
189
190 /**
191 * Creates simulated device.
192 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200193 * @param id device identifier
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800194 * @param chassisId chassis identifier number
195 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200196 public void createDevice(DeviceId id, int chassisId) {
197 createDevice(id, chassisId, Device.Type.SWITCH, hostCount + infrastructurePorts);
198 }
199
200 /**
201 * Creates simulated device.
202 *
203 * @param id device identifier
204 * @param chassisId chassis identifier number
205 * @param type device type
206 * @param portCount number of device ports
207 */
208 public void createDevice(DeviceId id, int chassisId, Device.Type type, int portCount) {
Thomas Vachuska7b1fadc2018-09-27 11:20:41 -0700209 createDevice(id, chassisId, type, "0.1", "0.1.2", portCount);
210 }
211
212 /**
213 * Creates simulated device.
214 *
215 * @param id device identifier
216 * @param chassisId chassis identifier number
217 * @param type device type
218 * @param hw hardware revision
219 * @param sw software revision
220 * @param portCount number of device ports
221 */
222 public void createDevice(DeviceId id, int chassisId, Device.Type type,
223 String hw, String sw, int portCount) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700224 DeviceDescription desc =
Thomas Vachuska7b1fadc2018-09-27 11:20:41 -0700225 new DefaultDeviceDescription(id.uri(), type, "ONF", hw, sw, "1234",
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700226 new ChassisId(chassisId),
227 DefaultAnnotations.builder()
228 .set(AnnotationKeys.NAME, "Switch " + chassisId)
229 .build());
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800230 deviceIds.add(id);
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800231 mastershipAdminService.setRoleSync(localNode, id, MASTER);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700232 deviceProviderService.deviceConnected(id, desc);
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200233 deviceProviderService.updatePorts(id, buildPorts(portCount));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700234 }
235
236 /**
237 * Creates simulated link between two devices.
238 *
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700239 * @param i index of one simulated device
240 * @param j index of another simulated device
241 * @param pi port number of i-th device
242 * @param pj port number of j-th device
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700243 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200244 public void createLink(int i, int j, int pi, int pj) {
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700245 ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(pi));
246 ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(pj));
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800247 createLink(one, two);
248 }
249
250 /**
251 * Creates simulated link between two connection points.
252 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200253 * @param one one connection point
254 * @param two another connection point
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800255 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200256 public void createLink(ConnectPoint one, ConnectPoint two) {
257 createLink(one, two, DIRECT, true);
258 }
259
260 /**
261 * Creates simulated link between two connection points.
262 *
263 * @param one one connection point
264 * @param two another connection point
265 * @param type link type
266 * @param isBidirectional true if link is bidirectional
267 */
268 public void createLink(ConnectPoint one, ConnectPoint two, Link.Type type, boolean isBidirectional) {
269 linkProviderService.linkDetected(new DefaultLinkDescription(one, two, type));
270 if (isBidirectional) {
271 linkProviderService.linkDetected(new DefaultLinkDescription(two, one, type));
272 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700273 }
274
275 /**
276 * Creates simularted hosts for the specified device.
277 *
278 * @param deviceId device identifier
279 * @param portOffset port offset where to start attaching hosts
280 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200281 public void createHosts(DeviceId deviceId, int portOffset) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700282 String s = deviceId.toString();
Thomas Vachuska1cbd65e2016-06-25 16:27:57 +0200283 byte dByte = Byte.parseByte(s.substring(s.length() - 2), 16);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700284 // TODO: this limits the simulation to 256 devices & 256 hosts/device.
285 byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
286 byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
287
288 for (int i = 0; i < hostCount; i++) {
289 int port = portOffset + i + 1;
290 macBytes[5] = (byte) (i + 1);
291 ipBytes[3] = (byte) (i + 1);
292 HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
293 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700294 hostProviderService.hostDetected(id, description(i, id, ip, deviceId, port), false);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700295 }
296 }
297
298 /**
299 * Prepares to count device added/available/removed events.
300 *
301 * @param count number of events to count
302 */
303 protected void prepareForDeviceEvents(int count) {
304 deviceLatch = new CountDownLatch(count);
305 deviceService.addListener(deviceEventCounter);
306 }
307
308 /**
309 * Waits for all expected device added/available/removed events.
310 */
311 protected void waitForDeviceEvents() {
312 try {
313 deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
314 } catch (InterruptedException e) {
315 log.warn("Device events did not arrive in time");
Ray Milkey5c7d4882018-02-05 14:50:39 -0800316 Thread.currentThread().interrupt();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700317 }
318 deviceService.removeListener(deviceEventCounter);
319 }
320
321
322 /**
323 * Tears down network topology simulation.
324 */
325 public void tearDownTopology() {
326 removeHosts();
327 removeLinks();
328 removeDevices();
329 }
330
331 /**
332 * Removes any hosts previously advertised by this provider.
333 */
334 protected void removeHosts() {
335 hostService.getHosts()
336 .forEach(host -> hostProviderService.hostVanished(host.id()));
337 }
338
339 /**
340 * Removes any links previously advertised by this provider.
341 */
342 protected void removeLinks() {
343 linkService.getLinks()
344 .forEach(link -> linkProviderService.linkVanished(description(link)));
345 }
346
347 /**
348 * Removes any devices previously advertised by this provider.
349 */
350 protected void removeDevices() {
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700351 prepareForDeviceEvents(deviceIds.size());
352 deviceIds.forEach(deviceProviderService::deviceDisconnected);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700353 waitForDeviceEvents();
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800354 mastershipService.getDevicesOf(localNode).forEach(mastershipService::relinquishMastership);
355 deviceIds.forEach(mastershipService::relinquishMastership);
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700356 deviceIds.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700357 }
358
359
360 /**
361 * Produces a device description from the given device.
362 *
363 * @param device device to copy
364 * @return device description
365 */
366 static DeviceDescription description(Device device) {
367 return new DefaultDeviceDescription(device.id().uri(), device.type(),
368 device.manufacturer(),
369 device.hwVersion(), device.swVersion(),
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700370 device.serialNumber(), device.chassisId(),
371 DefaultAnnotations.builder()
372 .putAll(device.annotations())
373 .build());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700374 }
375
376 /**
377 * Produces a link description from the given link.
378 *
379 * @param link link to copy
380 * @return link description
381 */
382 static DefaultLinkDescription description(Link link) {
383 return new DefaultLinkDescription(link.src(), link.dst(), link.type());
384 }
385
386 /**
387 * Produces a host description from the given host.
388 *
389 * @param host host to copy
390 * @return host description
391 */
392 static DefaultHostDescription description(Host host) {
393 return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700394 host.ipAddresses(),
395 DefaultAnnotations.builder()
396 .putAll(host.annotations())
397 .build());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700398 }
399
400 /**
401 * Generates a host description from the given id and location information.
402 *
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700403 * @param index host index for friendly name
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700404 * @param hostId host identifier
405 * @param ip host IP
406 * @param deviceId edge device
407 * @param port edge port
408 * @return host description
409 */
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700410 static HostDescription description(int index, HostId hostId, IpAddress ip,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700411 DeviceId deviceId, int port) {
412 HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
Thomas Vachuskafd168fc2020-07-31 14:22:05 -0700413 return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip,
414 DefaultAnnotations.builder()
415 .set(AnnotationKeys.NAME, "Host " + index)
416 .build());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700417 }
418
419 /**
420 * Generates a list of a configured number of ports.
421 *
422 * @param portCount number of ports
423 * @return list of ports
424 */
425 protected List<PortDescription> buildPorts(int portCount) {
426 List<PortDescription> ports = Lists.newArrayList();
Thomas Vachuska6c0582e2016-07-05 12:20:31 -0700427 for (int i = 1; i <= portCount; i++) {
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800428 ports.add(DefaultPortDescription.builder()
429 .withPortNumber(PortNumber.portNumber(i))
430 .isEnabled(true)
431 .type(Port.Type.COPPER)
432 .portSpeed(0)
433 .build());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700434 }
435 return ports;
436 }
437
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700438 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800439 * Indicates whether or not the simulation deeps the device as available.
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700440 *
441 * @param deviceId device identifier
442 * @return true if device is known
443 */
444 public boolean contains(DeviceId deviceId) {
445 return deviceIds.contains(deviceId);
446 }
447
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700448 // Counts down number of device added/available/removed events.
449 private class DeviceEventCounter implements DeviceListener {
450 @Override
451 public void event(DeviceEvent event) {
452 DeviceEvent.Type type = event.type();
453 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
454 type == DEVICE_AVAILABILITY_CHANGED) {
455 deviceLatch.countDown();
456 }
457 }
458 }
Ray Milkeydc083442016-02-22 11:27:57 -0800459}