blob: 3a3597140a5c5abb8c63cbd4c6418f75f742633f [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;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Host;
32import org.onosproject.net.HostId;
33import org.onosproject.net.HostLocation;
34import org.onosproject.net.Link;
35import org.onosproject.net.Port;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.device.DefaultDeviceDescription;
38import org.onosproject.net.device.DefaultPortDescription;
39import org.onosproject.net.device.DeviceAdminService;
40import org.onosproject.net.device.DeviceDescription;
41import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
43import org.onosproject.net.device.DeviceProviderService;
44import org.onosproject.net.device.PortDescription;
45import org.onosproject.net.host.DefaultHostDescription;
46import org.onosproject.net.host.HostDescription;
47import org.onosproject.net.host.HostProviderService;
48import org.onosproject.net.host.HostService;
49import org.onosproject.net.link.DefaultLinkDescription;
50import org.onosproject.net.link.LinkProviderService;
51import org.onosproject.net.link.LinkService;
52import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
55import java.util.List;
56import java.util.concurrent.CountDownLatch;
57import java.util.concurrent.TimeUnit;
58
59import static org.onlab.util.Tools.toHex;
60import static org.onosproject.net.HostId.hostId;
61import static org.onosproject.net.Link.Type.DIRECT;
Thomas Vachuskacab29d22018-02-21 15:47:29 -080062import static org.onosproject.net.MastershipRole.MASTER;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070063import static org.onosproject.net.PortNumber.portNumber;
64import static org.onosproject.net.device.DeviceEvent.Type.*;
65import static org.onosproject.provider.nil.NullProviders.SCHEME;
66
67/**
68 * Abstraction of a provider capable to simulate some network topology.
69 */
70public abstract class TopologySimulator {
71
72 protected final Logger log = LoggerFactory.getLogger(getClass());
73
74 protected String[] topoShape;
75 protected int deviceCount;
76 protected int hostCount;
77
78 protected ServiceDirectory directory;
79 protected NodeId localNode;
80
81 protected ClusterService clusterService;
82 protected MastershipService mastershipService;
Thomas Vachuskacab29d22018-02-21 15:47:29 -080083 protected MastershipAdminService mastershipAdminService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070084
85 protected DeviceAdminService deviceService;
86 protected HostService hostService;
87 protected LinkService linkService;
88
89 protected DeviceProviderService deviceProviderService;
90 protected HostProviderService hostProviderService;
91 protected LinkProviderService linkProviderService;
92
93 protected int maxWaitSeconds = 1;
94 protected int infrastructurePorts = 2;
95 protected CountDownLatch deviceLatch;
96
97 protected final List<DeviceId> deviceIds = Lists.newArrayList();
98
Thomas Vachuskacab29d22018-02-21 15:47:29 -080099 private final DeviceListener deviceEventCounter = new DeviceEventCounter();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700100
101 /**
102 * Initializes a new topology simulator with access to the specified service
103 * directory and various provider services.
104 *
105 * @param topoShape topology shape specifier
106 * @param deviceCount number of devices in the topology
107 * @param hostCount number of hosts per device
108 * @param directory service directory
109 * @param deviceProviderService device provider service
110 * @param hostProviderService host provider service
111 * @param linkProviderService link provider service
112 */
113 protected void init(String topoShape, int deviceCount, int hostCount,
114 ServiceDirectory directory,
115 DeviceProviderService deviceProviderService,
116 HostProviderService hostProviderService,
117 LinkProviderService linkProviderService) {
118 this.deviceCount = deviceCount;
119 this.hostCount = hostCount;
120 this.directory = directory;
121
122 this.clusterService = directory.get(ClusterService.class);
123 this.mastershipService = directory.get(MastershipService.class);
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800124 this.mastershipAdminService = directory.get(MastershipAdminService.class);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700125
126 this.deviceService = directory.get(DeviceAdminService.class);
127 this.hostService = directory.get(HostService.class);
128 this.linkService = directory.get(LinkService.class);
129 this.deviceProviderService = deviceProviderService;
130 this.hostProviderService = hostProviderService;
131 this.linkProviderService = linkProviderService;
132
133 localNode = clusterService.getLocalNode().id();
134
135 processTopoShape(topoShape);
136 }
137
138 /**
139 * Processes the topology shape specifier.
140 *
141 * @param shape topology shape specifier
142 */
143 protected void processTopoShape(String shape) {
144 this.topoShape = shape.split(",");
145 }
146
147 /**
148 * Sets up network topology simulation.
149 */
150 public void setUpTopology() {
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800151 deviceIds.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700152 prepareForDeviceEvents(deviceCount);
153 createDevices();
154 waitForDeviceEvents();
155
156 createLinks();
157 createHosts();
158 }
159
160 /**
161 * Creates simulated devices.
162 */
163 protected void createDevices() {
164 for (int i = 0; i < deviceCount; i++) {
165 createDevice(i + 1);
166 }
167 }
168
169 /**
170 * Creates simulated links.
171 */
172 protected abstract void createLinks();
173
174 /**
175 * Creates simulated hosts.
176 */
177 protected abstract void createHosts();
178
179 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800180 * Creates simulated device and adds its id to the list of devices ids.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700181 *
182 * @param i index of the device id in the list.
183 */
184 protected void createDevice(int i) {
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800185 createDevice(DeviceId.deviceId(SCHEME + ":" + toHex(i)), i);
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800186 }
187
188 /**
189 * Creates simulated device.
190 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200191 * @param id device identifier
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800192 * @param chassisId chassis identifier number
193 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200194 public void createDevice(DeviceId id, int chassisId) {
195 createDevice(id, chassisId, Device.Type.SWITCH, hostCount + infrastructurePorts);
196 }
197
198 /**
199 * Creates simulated device.
200 *
201 * @param id device identifier
202 * @param chassisId chassis identifier number
203 * @param type device type
204 * @param portCount number of device ports
205 */
206 public void createDevice(DeviceId id, int chassisId, Device.Type type, int portCount) {
Thomas Vachuska7b1fadc2018-09-27 11:20:41 -0700207 createDevice(id, chassisId, type, "0.1", "0.1.2", portCount);
208 }
209
210 /**
211 * Creates simulated device.
212 *
213 * @param id device identifier
214 * @param chassisId chassis identifier number
215 * @param type device type
216 * @param hw hardware revision
217 * @param sw software revision
218 * @param portCount number of device ports
219 */
220 public void createDevice(DeviceId id, int chassisId, Device.Type type,
221 String hw, String sw, int portCount) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700222 DeviceDescription desc =
Thomas Vachuska7b1fadc2018-09-27 11:20:41 -0700223 new DefaultDeviceDescription(id.uri(), type, "ONF", hw, sw, "1234",
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800224 new ChassisId(chassisId));
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800225 deviceIds.add(id);
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800226 mastershipAdminService.setRoleSync(localNode, id, MASTER);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700227 deviceProviderService.deviceConnected(id, desc);
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200228 deviceProviderService.updatePorts(id, buildPorts(portCount));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700229 }
230
231 /**
232 * Creates simulated link between two devices.
233 *
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700234 * @param i index of one simulated device
235 * @param j index of another simulated device
236 * @param pi port number of i-th device
237 * @param pj port number of j-th device
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700238 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200239 public void createLink(int i, int j, int pi, int pj) {
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700240 ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(pi));
241 ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(pj));
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800242 createLink(one, two);
243 }
244
245 /**
246 * Creates simulated link between two connection points.
247 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200248 * @param one one connection point
249 * @param two another connection point
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800250 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200251 public void createLink(ConnectPoint one, ConnectPoint two) {
252 createLink(one, two, DIRECT, true);
253 }
254
255 /**
256 * Creates simulated link between two connection points.
257 *
258 * @param one one connection point
259 * @param two another connection point
260 * @param type link type
261 * @param isBidirectional true if link is bidirectional
262 */
263 public void createLink(ConnectPoint one, ConnectPoint two, Link.Type type, boolean isBidirectional) {
264 linkProviderService.linkDetected(new DefaultLinkDescription(one, two, type));
265 if (isBidirectional) {
266 linkProviderService.linkDetected(new DefaultLinkDescription(two, one, type));
267 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700268 }
269
270 /**
271 * Creates simularted hosts for the specified device.
272 *
273 * @param deviceId device identifier
274 * @param portOffset port offset where to start attaching hosts
275 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200276 public void createHosts(DeviceId deviceId, int portOffset) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700277 String s = deviceId.toString();
Thomas Vachuska1cbd65e2016-06-25 16:27:57 +0200278 byte dByte = Byte.parseByte(s.substring(s.length() - 2), 16);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700279 // TODO: this limits the simulation to 256 devices & 256 hosts/device.
280 byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
281 byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
282
283 for (int i = 0; i < hostCount; i++) {
284 int port = portOffset + i + 1;
285 macBytes[5] = (byte) (i + 1);
286 ipBytes[3] = (byte) (i + 1);
287 HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
288 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
Ray Milkeydc083442016-02-22 11:27:57 -0800289 hostProviderService.hostDetected(id, description(id, ip, deviceId, port), false);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700290 }
291 }
292
293 /**
294 * Prepares to count device added/available/removed events.
295 *
296 * @param count number of events to count
297 */
298 protected void prepareForDeviceEvents(int count) {
299 deviceLatch = new CountDownLatch(count);
300 deviceService.addListener(deviceEventCounter);
301 }
302
303 /**
304 * Waits for all expected device added/available/removed events.
305 */
306 protected void waitForDeviceEvents() {
307 try {
308 deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
309 } catch (InterruptedException e) {
310 log.warn("Device events did not arrive in time");
Ray Milkey5c7d4882018-02-05 14:50:39 -0800311 Thread.currentThread().interrupt();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700312 }
313 deviceService.removeListener(deviceEventCounter);
314 }
315
316
317 /**
318 * Tears down network topology simulation.
319 */
320 public void tearDownTopology() {
321 removeHosts();
322 removeLinks();
323 removeDevices();
324 }
325
326 /**
327 * Removes any hosts previously advertised by this provider.
328 */
329 protected void removeHosts() {
330 hostService.getHosts()
331 .forEach(host -> hostProviderService.hostVanished(host.id()));
332 }
333
334 /**
335 * Removes any links previously advertised by this provider.
336 */
337 protected void removeLinks() {
338 linkService.getLinks()
339 .forEach(link -> linkProviderService.linkVanished(description(link)));
340 }
341
342 /**
343 * Removes any devices previously advertised by this provider.
344 */
345 protected void removeDevices() {
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700346 prepareForDeviceEvents(deviceIds.size());
347 deviceIds.forEach(deviceProviderService::deviceDisconnected);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700348 waitForDeviceEvents();
Thomas Vachuskacab29d22018-02-21 15:47:29 -0800349 mastershipService.getDevicesOf(localNode).forEach(mastershipService::relinquishMastership);
350 deviceIds.forEach(mastershipService::relinquishMastership);
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700351 deviceIds.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700352 }
353
354
355 /**
356 * Produces a device description from the given device.
357 *
358 * @param device device to copy
359 * @return device description
360 */
361 static DeviceDescription description(Device device) {
362 return new DefaultDeviceDescription(device.id().uri(), device.type(),
363 device.manufacturer(),
364 device.hwVersion(), device.swVersion(),
365 device.serialNumber(), device.chassisId());
366 }
367
368 /**
369 * Produces a link description from the given link.
370 *
371 * @param link link to copy
372 * @return link description
373 */
374 static DefaultLinkDescription description(Link link) {
375 return new DefaultLinkDescription(link.src(), link.dst(), link.type());
376 }
377
378 /**
379 * Produces a host description from the given host.
380 *
381 * @param host host to copy
382 * @return host description
383 */
384 static DefaultHostDescription description(Host host) {
385 return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
386 host.ipAddresses());
387 }
388
389 /**
390 * Generates a host description from the given id and location information.
391 *
392 * @param hostId host identifier
393 * @param ip host IP
394 * @param deviceId edge device
395 * @param port edge port
396 * @return host description
397 */
398 static HostDescription description(HostId hostId, IpAddress ip,
399 DeviceId deviceId, int port) {
400 HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
401 return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip);
402 }
403
404 /**
405 * Generates a list of a configured number of ports.
406 *
407 * @param portCount number of ports
408 * @return list of ports
409 */
410 protected List<PortDescription> buildPorts(int portCount) {
411 List<PortDescription> ports = Lists.newArrayList();
Thomas Vachuska6c0582e2016-07-05 12:20:31 -0700412 for (int i = 1; i <= portCount; i++) {
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800413 ports.add(DefaultPortDescription.builder()
414 .withPortNumber(PortNumber.portNumber(i))
415 .isEnabled(true)
416 .type(Port.Type.COPPER)
417 .portSpeed(0)
418 .build());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700419 }
420 return ports;
421 }
422
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700423 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800424 * Indicates whether or not the simulation deeps the device as available.
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700425 *
426 * @param deviceId device identifier
427 * @return true if device is known
428 */
429 public boolean contains(DeviceId deviceId) {
430 return deviceIds.contains(deviceId);
431 }
432
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700433 // Counts down number of device added/available/removed events.
434 private class DeviceEventCounter implements DeviceListener {
435 @Override
436 public void event(DeviceEvent event) {
437 DeviceEvent.Type type = event.type();
438 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
439 type == DEVICE_AVAILABILITY_CHANGED) {
440 deviceLatch.countDown();
441 }
442 }
443 }
Ray Milkeydc083442016-02-22 11:27:57 -0800444}