blob: 2c7928843c6034aeb54dbe68cb7efe8c2f4f5bac [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;
26import org.onosproject.mastership.MastershipService;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Host;
31import org.onosproject.net.HostId;
32import org.onosproject.net.HostLocation;
33import org.onosproject.net.Link;
34import org.onosproject.net.Port;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DefaultDeviceDescription;
37import org.onosproject.net.device.DefaultPortDescription;
38import org.onosproject.net.device.DeviceAdminService;
39import org.onosproject.net.device.DeviceDescription;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.device.DeviceProviderService;
43import org.onosproject.net.device.PortDescription;
44import org.onosproject.net.host.DefaultHostDescription;
45import org.onosproject.net.host.HostDescription;
46import org.onosproject.net.host.HostProviderService;
47import org.onosproject.net.host.HostService;
48import org.onosproject.net.link.DefaultLinkDescription;
49import org.onosproject.net.link.LinkProviderService;
50import org.onosproject.net.link.LinkService;
51import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54import java.util.List;
55import java.util.concurrent.CountDownLatch;
56import java.util.concurrent.TimeUnit;
57
58import static org.onlab.util.Tools.toHex;
59import static org.onosproject.net.HostId.hostId;
60import static org.onosproject.net.Link.Type.DIRECT;
61import static org.onosproject.net.PortNumber.portNumber;
62import static org.onosproject.net.device.DeviceEvent.Type.*;
63import static org.onosproject.provider.nil.NullProviders.SCHEME;
64
65/**
66 * Abstraction of a provider capable to simulate some network topology.
67 */
68public abstract class TopologySimulator {
69
70 protected final Logger log = LoggerFactory.getLogger(getClass());
71
72 protected String[] topoShape;
73 protected int deviceCount;
74 protected int hostCount;
75
76 protected ServiceDirectory directory;
77 protected NodeId localNode;
78
79 protected ClusterService clusterService;
80 protected MastershipService mastershipService;
81
82 protected DeviceAdminService deviceService;
83 protected HostService hostService;
84 protected LinkService linkService;
85
86 protected DeviceProviderService deviceProviderService;
87 protected HostProviderService hostProviderService;
88 protected LinkProviderService linkProviderService;
89
90 protected int maxWaitSeconds = 1;
91 protected int infrastructurePorts = 2;
92 protected CountDownLatch deviceLatch;
93
94 protected final List<DeviceId> deviceIds = Lists.newArrayList();
95
96 private DeviceListener deviceEventCounter = new DeviceEventCounter();
97
98 /**
99 * Initializes a new topology simulator with access to the specified service
100 * directory and various provider services.
101 *
102 * @param topoShape topology shape specifier
103 * @param deviceCount number of devices in the topology
104 * @param hostCount number of hosts per device
105 * @param directory service directory
106 * @param deviceProviderService device provider service
107 * @param hostProviderService host provider service
108 * @param linkProviderService link provider service
109 */
110 protected void init(String topoShape, int deviceCount, int hostCount,
111 ServiceDirectory directory,
112 DeviceProviderService deviceProviderService,
113 HostProviderService hostProviderService,
114 LinkProviderService linkProviderService) {
115 this.deviceCount = deviceCount;
116 this.hostCount = hostCount;
117 this.directory = directory;
118
119 this.clusterService = directory.get(ClusterService.class);
120 this.mastershipService = directory.get(MastershipService.class);
121
122 this.deviceService = directory.get(DeviceAdminService.class);
123 this.hostService = directory.get(HostService.class);
124 this.linkService = directory.get(LinkService.class);
125 this.deviceProviderService = deviceProviderService;
126 this.hostProviderService = hostProviderService;
127 this.linkProviderService = linkProviderService;
128
129 localNode = clusterService.getLocalNode().id();
130
131 processTopoShape(topoShape);
132 }
133
134 /**
135 * Processes the topology shape specifier.
136 *
137 * @param shape topology shape specifier
138 */
139 protected void processTopoShape(String shape) {
140 this.topoShape = shape.split(",");
141 }
142
143 /**
144 * Sets up network topology simulation.
145 */
146 public void setUpTopology() {
147 prepareForDeviceEvents(deviceCount);
148 createDevices();
149 waitForDeviceEvents();
150
151 createLinks();
152 createHosts();
153 }
154
155 /**
156 * Creates simulated devices.
157 */
158 protected void createDevices() {
159 for (int i = 0; i < deviceCount; i++) {
160 createDevice(i + 1);
161 }
162 }
163
164 /**
165 * Creates simulated links.
166 */
167 protected abstract void createLinks();
168
169 /**
170 * Creates simulated hosts.
171 */
172 protected abstract void createHosts();
173
174 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800175 * Creates simulated device and adds its id to the list of devices ids.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700176 *
177 * @param i index of the device id in the list.
178 */
179 protected void createDevice(int i) {
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800180 createDevice(DeviceId.deviceId(SCHEME + ":" + toHex(i)), i);
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800181 }
182
183 /**
184 * Creates simulated device.
185 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200186 * @param id device identifier
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800187 * @param chassisId chassis identifier number
188 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200189 public void createDevice(DeviceId id, int chassisId) {
190 createDevice(id, chassisId, Device.Type.SWITCH, hostCount + infrastructurePorts);
191 }
192
193 /**
194 * Creates simulated device.
195 *
196 * @param id device identifier
197 * @param chassisId chassis identifier number
198 * @param type device type
199 * @param portCount number of device ports
200 */
201 public void createDevice(DeviceId id, int chassisId, Device.Type type, int portCount) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700202 DeviceDescription desc =
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200203 new DefaultDeviceDescription(id.uri(), type,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700204 "ON.Lab", "0.1", "0.1", "1234",
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800205 new ChassisId(chassisId));
Thomas Vachuskab1906d22018-02-14 16:50:16 -0800206 deviceIds.add(id);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700207 deviceProviderService.deviceConnected(id, desc);
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200208 deviceProviderService.updatePorts(id, buildPorts(portCount));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700209 }
210
211 /**
212 * Creates simulated link between two devices.
213 *
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700214 * @param i index of one simulated device
215 * @param j index of another simulated device
216 * @param pi port number of i-th device
217 * @param pj port number of j-th device
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700218 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200219 public void createLink(int i, int j, int pi, int pj) {
Thomas Vachuskaf651cc92015-04-14 16:11:44 -0700220 ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(pi));
221 ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(pj));
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800222 createLink(one, two);
223 }
224
225 /**
226 * Creates simulated link between two connection points.
227 *
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200228 * @param one one connection point
229 * @param two another connection point
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800230 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200231 public void createLink(ConnectPoint one, ConnectPoint two) {
232 createLink(one, two, DIRECT, true);
233 }
234
235 /**
236 * Creates simulated link between two connection points.
237 *
238 * @param one one connection point
239 * @param two another connection point
240 * @param type link type
241 * @param isBidirectional true if link is bidirectional
242 */
243 public void createLink(ConnectPoint one, ConnectPoint two, Link.Type type, boolean isBidirectional) {
244 linkProviderService.linkDetected(new DefaultLinkDescription(one, two, type));
245 if (isBidirectional) {
246 linkProviderService.linkDetected(new DefaultLinkDescription(two, one, type));
247 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700248 }
249
250 /**
251 * Creates simularted hosts for the specified device.
252 *
253 * @param deviceId device identifier
254 * @param portOffset port offset where to start attaching hosts
255 */
Thomas Vachuska5c6ed222016-06-29 11:02:22 +0200256 public void createHosts(DeviceId deviceId, int portOffset) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700257 String s = deviceId.toString();
Thomas Vachuska1cbd65e2016-06-25 16:27:57 +0200258 byte dByte = Byte.parseByte(s.substring(s.length() - 2), 16);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700259 // TODO: this limits the simulation to 256 devices & 256 hosts/device.
260 byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
261 byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
262
263 for (int i = 0; i < hostCount; i++) {
264 int port = portOffset + i + 1;
265 macBytes[5] = (byte) (i + 1);
266 ipBytes[3] = (byte) (i + 1);
267 HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
268 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
Ray Milkeydc083442016-02-22 11:27:57 -0800269 hostProviderService.hostDetected(id, description(id, ip, deviceId, port), false);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700270 }
271 }
272
273 /**
274 * Prepares to count device added/available/removed events.
275 *
276 * @param count number of events to count
277 */
278 protected void prepareForDeviceEvents(int count) {
279 deviceLatch = new CountDownLatch(count);
280 deviceService.addListener(deviceEventCounter);
281 }
282
283 /**
284 * Waits for all expected device added/available/removed events.
285 */
286 protected void waitForDeviceEvents() {
287 try {
288 deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
289 } catch (InterruptedException e) {
290 log.warn("Device events did not arrive in time");
Ray Milkey5c7d4882018-02-05 14:50:39 -0800291 Thread.currentThread().interrupt();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700292 }
293 deviceService.removeListener(deviceEventCounter);
294 }
295
296
297 /**
298 * Tears down network topology simulation.
299 */
300 public void tearDownTopology() {
301 removeHosts();
302 removeLinks();
303 removeDevices();
304 }
305
306 /**
307 * Removes any hosts previously advertised by this provider.
308 */
309 protected void removeHosts() {
310 hostService.getHosts()
311 .forEach(host -> hostProviderService.hostVanished(host.id()));
312 }
313
314 /**
315 * Removes any links previously advertised by this provider.
316 */
317 protected void removeLinks() {
318 linkService.getLinks()
319 .forEach(link -> linkProviderService.linkVanished(description(link)));
320 }
321
322 /**
323 * Removes any devices previously advertised by this provider.
324 */
325 protected void removeDevices() {
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700326 prepareForDeviceEvents(deviceIds.size());
327 deviceIds.forEach(deviceProviderService::deviceDisconnected);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700328 waitForDeviceEvents();
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700329 deviceIds.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700330 }
331
332
333 /**
334 * Produces a device description from the given device.
335 *
336 * @param device device to copy
337 * @return device description
338 */
339 static DeviceDescription description(Device device) {
340 return new DefaultDeviceDescription(device.id().uri(), device.type(),
341 device.manufacturer(),
342 device.hwVersion(), device.swVersion(),
343 device.serialNumber(), device.chassisId());
344 }
345
346 /**
347 * Produces a link description from the given link.
348 *
349 * @param link link to copy
350 * @return link description
351 */
352 static DefaultLinkDescription description(Link link) {
353 return new DefaultLinkDescription(link.src(), link.dst(), link.type());
354 }
355
356 /**
357 * Produces a host description from the given host.
358 *
359 * @param host host to copy
360 * @return host description
361 */
362 static DefaultHostDescription description(Host host) {
363 return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
364 host.ipAddresses());
365 }
366
367 /**
368 * Generates a host description from the given id and location information.
369 *
370 * @param hostId host identifier
371 * @param ip host IP
372 * @param deviceId edge device
373 * @param port edge port
374 * @return host description
375 */
376 static HostDescription description(HostId hostId, IpAddress ip,
377 DeviceId deviceId, int port) {
378 HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
379 return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip);
380 }
381
382 /**
383 * Generates a list of a configured number of ports.
384 *
385 * @param portCount number of ports
386 * @return list of ports
387 */
388 protected List<PortDescription> buildPorts(int portCount) {
389 List<PortDescription> ports = Lists.newArrayList();
Thomas Vachuska6c0582e2016-07-05 12:20:31 -0700390 for (int i = 1; i <= portCount; i++) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700391 ports.add(new DefaultPortDescription(PortNumber.portNumber(i), true,
392 Port.Type.COPPER, 0));
393 }
394 return ports;
395 }
396
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700397 /**
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800398 * Indicates whether or not the simulation deeps the device as available.
Thomas Vachuska5f429d62015-05-28 15:34:36 -0700399 *
400 * @param deviceId device identifier
401 * @return true if device is known
402 */
403 public boolean contains(DeviceId deviceId) {
404 return deviceIds.contains(deviceId);
405 }
406
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700407 // Counts down number of device added/available/removed events.
408 private class DeviceEventCounter implements DeviceListener {
409 @Override
410 public void event(DeviceEvent event) {
411 DeviceEvent.Type type = event.type();
412 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
413 type == DEVICE_AVAILABILITY_CHANGED) {
414 deviceLatch.countDown();
415 }
416 }
417 }
Ray Milkeydc083442016-02-22 11:27:57 -0800418}