blob: ad48e1eccb6f8b531ad8eda78a265786192a9bc4 [file] [log] [blame]
Andrea Campanella241896c2017-05-10 13:11:04 -07001/*
2 * Copyright 2017-present Open Networking Laboratory
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.provider.general.device.impl;
18
19import com.google.common.annotations.Beta;
Andrea Campanellabc112a92017-06-26 19:06:43 +020020import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Maps;
Andrea Campanella241896c2017-05-10 13:11:04 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.ChassisId;
28import org.onlab.util.ItemNotFoundException;
29import org.onosproject.core.CoreService;
30import org.onosproject.net.AnnotationKeys;
31import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.MastershipRole;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.SparseAnnotations;
37import org.onosproject.net.behaviour.PortAdmin;
38import org.onosproject.net.config.ConfigFactory;
39import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
41import org.onosproject.net.config.NetworkConfigRegistry;
42import org.onosproject.net.config.basics.BasicDeviceConfig;
43import org.onosproject.net.config.basics.SubjectFactories;
44import org.onosproject.net.device.DefaultDeviceDescription;
45import org.onosproject.net.device.DeviceDescription;
46import org.onosproject.net.device.DeviceDescriptionDiscovery;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceHandshaker;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceProvider;
51import org.onosproject.net.device.DeviceProviderRegistry;
52import org.onosproject.net.device.DeviceProviderService;
53import org.onosproject.net.device.DeviceService;
54import org.onosproject.net.device.PortDescription;
55import org.onosproject.net.device.PortStatistics;
56import org.onosproject.net.device.PortStatisticsDiscovery;
57import org.onosproject.net.driver.Behaviour;
58import org.onosproject.net.driver.DefaultDriverData;
59import org.onosproject.net.driver.DefaultDriverHandler;
60import org.onosproject.net.driver.Driver;
61import org.onosproject.net.driver.DriverData;
62import org.onosproject.net.driver.DriverService;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040063import org.onosproject.net.pi.model.PiPipeconf;
Andrea Campanellabc112a92017-06-26 19:06:43 +020064import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040065import org.onosproject.net.pi.model.PiPipelineProgrammable;
Andrea Campanellabc112a92017-06-26 19:06:43 +020066import org.onosproject.net.pi.runtime.PiPipeconfConfig;
67import org.onosproject.net.pi.runtime.PiPipeconfService;
Andrea Campanella241896c2017-05-10 13:11:04 -070068import org.onosproject.net.provider.AbstractProvider;
69import org.onosproject.net.provider.ProviderId;
70import org.onosproject.provider.general.device.api.GeneralProviderDeviceConfig;
71import org.slf4j.Logger;
72
73import java.util.ArrayList;
74import java.util.Collection;
Andrea Campanellabc112a92017-06-26 19:06:43 +020075import java.util.Collections;
Andrea Campanella241896c2017-05-10 13:11:04 -070076import java.util.List;
Andrea Campanellabc112a92017-06-26 19:06:43 +020077import java.util.Set;
Andrea Campanella241896c2017-05-10 13:11:04 -070078import java.util.concurrent.CompletableFuture;
Andrea Campanellabc112a92017-06-26 19:06:43 +020079import java.util.concurrent.ConcurrentMap;
80import java.util.concurrent.CopyOnWriteArraySet;
Andrea Campanella241896c2017-05-10 13:11:04 -070081import java.util.concurrent.ExecutionException;
82import java.util.concurrent.ScheduledExecutorService;
83import java.util.concurrent.TimeUnit;
84import java.util.concurrent.TimeoutException;
Andrea Campanellabc112a92017-06-26 19:06:43 +020085import java.util.concurrent.locks.Lock;
86import java.util.concurrent.locks.ReentrantLock;
Andrea Campanella241896c2017-05-10 13:11:04 -070087
88import static java.util.concurrent.Executors.newScheduledThreadPool;
89import static org.onlab.util.Tools.groupedThreads;
90import static org.onosproject.net.device.DeviceEvent.Type;
91import static org.slf4j.LoggerFactory.getLogger;
92
93/**
94 * Provider which uses drivers to detect device and do initial handshake
95 * and channel establishment with devices. Any other provider specific operation
96 * is also delegated to the DeviceHandshaker driver.
97 */
98@Beta
99@Component(immediate = true)
100public class GeneralDeviceProvider extends AbstractProvider
101 implements DeviceProvider {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200102 public static final String DRIVER = "driver";
Andrea Campanella241896c2017-05-10 13:11:04 -0700103 private final Logger log = getLogger(getClass());
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DeviceProviderRegistry providerRegistry;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected NetworkConfigRegistry cfgService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected DeviceService deviceService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected DriverService driverService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanellabc112a92017-06-26 19:06:43 +0200121 protected PiPipeconfService piPipeconfService;
Andrea Campanella241896c2017-05-10 13:11:04 -0700122
123 protected static final String APP_NAME = "org.onosproject.generaldeviceprovider";
124 protected static final String URI_SCHEME = "device";
125 protected static final String CFG_SCHEME = "generalprovider";
126 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.general.provider.device";
127 private static final int CORE_POOL_SIZE = 10;
128 private static final String UNKNOWN = "unknown";
129 private static final int PORT_STATS_PERIOD_SECONDS = 10;
Andrea Campanellabc112a92017-06-26 19:06:43 +0200130 //FIXME this will be removed when the configuration is synced at the source.
131 private static final Set<String> PIPELINE_CONFIGURABLE_PROTOCOLS = ImmutableSet.of("p4runtime");
132 private static final ConcurrentMap<DeviceId, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
133
134 //FIXME to be removed when netcfg will issue device events in a bundle or
135 //ensures all configuration needed is present
136 private Set<DeviceId> deviceConfigured = new CopyOnWriteArraySet<>();
137 private Set<DeviceId> driverConfigured = new CopyOnWriteArraySet<>();
138 private Set<DeviceId> pipelineConfigured = new CopyOnWriteArraySet<>();
Andrea Campanella241896c2017-05-10 13:11:04 -0700139
140
141 protected ScheduledExecutorService connectionExecutor
142 = newScheduledThreadPool(CORE_POOL_SIZE,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400143 groupedThreads("onos/generaldeviceprovider-device",
144 "connection-executor-%d", log));
Andrea Campanella241896c2017-05-10 13:11:04 -0700145 protected ScheduledExecutorService portStatsExecutor
146 = newScheduledThreadPool(CORE_POOL_SIZE,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400147 groupedThreads("onos/generaldeviceprovider-port-stats",
148 "port-stats-executor-%d", log));
Andrea Campanella241896c2017-05-10 13:11:04 -0700149
150 protected DeviceProviderService providerService;
151 private InternalDeviceListener deviceListener = new InternalDeviceListener();
152
153 protected final ConfigFactory factory =
154 new ConfigFactory<DeviceId, GeneralProviderDeviceConfig>(
155 SubjectFactories.DEVICE_SUBJECT_FACTORY,
156 GeneralProviderDeviceConfig.class, CFG_SCHEME) {
157 @Override
158 public GeneralProviderDeviceConfig createConfig() {
159 return new GeneralProviderDeviceConfig();
160 }
161 };
162
163 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
164
165
166 @Activate
167 public void activate() {
168 providerService = providerRegistry.register(this);
169 coreService.registerApplication(APP_NAME);
170 cfgService.registerConfigFactory(factory);
171 cfgService.addListener(cfgListener);
172 deviceService.addListener(deviceListener);
173 //This will fail if ONOS has CFG and drivers which depend on this provider
174 // are activated, failing due to not finding the driver.
175 cfgService.getSubjects(DeviceId.class, GeneralProviderDeviceConfig.class)
176 .forEach(did -> connectionExecutor.execute(() -> connectDevice(did)));
177 log.info("Started");
178 }
179
180
181 @Deactivate
182 public void deactivate() {
183 portStatsExecutor.shutdown();
184 cfgService.removeListener(cfgListener);
185 //Not Removing the device so they can still be used from other driver providers
186 //cfgService.getSubjects(DeviceId.class, GeneralProviderDeviceConfig.class)
187 // .forEach(did -> connectionExecutor.execute(() -> disconnectDevice(did)));
188 connectionExecutor.shutdown();
189 deviceService.removeListener(deviceListener);
190 providerRegistry.unregister(this);
191 providerService = null;
192 cfgService.unregisterConfigFactory(factory);
193 log.info("Stopped");
194 }
195
196 public GeneralDeviceProvider() {
197 super(new ProviderId(URI_SCHEME, DEVICE_PROVIDER_PACKAGE));
198 }
199
200
201 @Override
202 public void triggerProbe(DeviceId deviceId) {
203 //TODO Really don't see the point of this in non OF Context,
204 // for now testing reachability, can be moved to no-op
205 log.debug("Triggering probe equals testing reachability on device {}", deviceId);
206 isReachable(deviceId);
207 }
208
209 @Override
210 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
211 log.debug("Received role {} for device {}", newRole, deviceId);
212 CompletableFuture<MastershipRole> roleReply = getHandshaker(deviceId).roleChanged(newRole);
213 roleReply.thenAcceptAsync(mastership -> providerService.receivedRoleReply(deviceId, newRole, mastership));
214 }
215
216 @Override
217 public boolean isReachable(DeviceId deviceId) {
218 log.debug("Testing rechability for device {}", deviceId);
219 CompletableFuture<Boolean> reachable = getHandshaker(deviceId).isReachable();
220 try {
221 return reachable.get(10, TimeUnit.SECONDS);
222 } catch (InterruptedException | ExecutionException | TimeoutException e) {
223 log.error("Device {} is not reachable", deviceId, e);
224 return false;
225 }
226 }
227
228 @Override
229 public void changePortState(DeviceId deviceId, PortNumber portNumber,
230 boolean enable) {
231 if (deviceService.getDevice(deviceId).is(PortAdmin.class)) {
232
233 PortAdmin portAdmin = getPortAdmin(deviceId);
234 CompletableFuture<Boolean> modified;
235 if (enable) {
236 modified = portAdmin.enable(portNumber);
237 } else {
238 modified = portAdmin.disable(portNumber);
239 }
240 modified.thenAcceptAsync(result -> {
241 if (!result) {
242 log.warn("Your device {} port {} status can't be changed to {}",
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400243 deviceId, portNumber, enable);
Andrea Campanella241896c2017-05-10 13:11:04 -0700244 }
245 });
246
247 } else {
248 log.warn("Device {} does not support PortAdmin behaviour", deviceId);
249 }
250 }
251
252 private DeviceHandshaker getHandshaker(DeviceId deviceId) {
253 Driver driver = getDriver(deviceId);
254 return getBehaviour(driver, DeviceHandshaker.class,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400255 new DefaultDriverData(driver, deviceId));
Andrea Campanella241896c2017-05-10 13:11:04 -0700256 }
257
258 private PortAdmin getPortAdmin(DeviceId deviceId) {
259 Driver driver = getDriver(deviceId);
260 return getBehaviour(driver, PortAdmin.class,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400261 new DefaultDriverData(driver, deviceId));
Andrea Campanella241896c2017-05-10 13:11:04 -0700262
263 }
264
265 private Driver getDriver(DeviceId deviceId) {
266 Driver driver;
267 try {
268 driver = driverService.getDriver(deviceId);
269 } catch (ItemNotFoundException e) {
270 log.debug("Falling back to configuration to fetch driver " +
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400271 "for device {}", deviceId);
Andrea Campanella241896c2017-05-10 13:11:04 -0700272 driver = driverService.getDriver(
273 cfgService.getConfig(deviceId, BasicDeviceConfig.class).driver());
274 }
275 return driver;
276 }
277
278 //needed since the device manager will not return the driver through implementation()
279 // method since the device is not pushed to the core so for the connectDevice
280 // we need to work around that in order to test before calling
281 // store.createOrUpdateDevice
282 private <T extends Behaviour> T getBehaviour(Driver driver, Class<T> type,
283 DriverData data) {
284 if (driver.hasBehaviour(type)) {
285 DefaultDriverHandler handler = new DefaultDriverHandler(data);
286 return driver.createBehaviour(handler, type);
287 } else {
288 return null;
289 }
290 }
291
292 //Connects a general device
293 private void connectDevice(DeviceId deviceId) {
294 //retrieve the configuration
295 GeneralProviderDeviceConfig providerConfig =
296 cfgService.getConfig(deviceId, GeneralProviderDeviceConfig.class);
297 BasicDeviceConfig basicDeviceConfig =
298 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
299
300 if (providerConfig == null || basicDeviceConfig == null) {
301 log.error("Configuration is NULL: basic config {}, general provider " +
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400302 "config {}", basicDeviceConfig, providerConfig);
Andrea Campanella241896c2017-05-10 13:11:04 -0700303 } else {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200304 log.info("Connecting to device {} with driver {}", deviceId, basicDeviceConfig.driver());
Andrea Campanella241896c2017-05-10 13:11:04 -0700305
306 Driver driver = driverService.getDriver(basicDeviceConfig.driver());
307 DriverData driverData = new DefaultDriverData(driver, deviceId);
308
309 DeviceHandshaker handshaker =
310 getBehaviour(driver, DeviceHandshaker.class, driverData);
311
Andrea Campanellabc112a92017-06-26 19:06:43 +0200312 if (handshaker == null) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700313 log.error("Device {}, with driver {} does not support DeviceHandshaker " +
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400314 "behaviour, {}", deviceId, driver.name(), driver.behaviours());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200315 return;
Andrea Campanella241896c2017-05-10 13:11:04 -0700316 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200317 //Storing deviceKeyId and all other config values
318 // as data in the driver with protocol_<info>
319 // name as the key. e.g protocol_ip
320 providerConfig.protocolsInfo()
321 .forEach((protocol, deviceInfoConfig) -> {
322 deviceInfoConfig.configValues()
323 .forEach((k, v) -> driverData.set(protocol + "_" + k, v));
324 driverData.set(protocol + "_key", deviceInfoConfig.deviceKeyId());
325 });
326
327 //Connecting to the device
328 CompletableFuture<Boolean> connected = handshaker.connect();
329
330 connected.thenAcceptAsync(result -> {
331 if (result) {
332
333 //Populated with the default values obtained by the driver
334 ChassisId cid = new ChassisId();
335 SparseAnnotations annotations = DefaultAnnotations.builder()
336 .set(AnnotationKeys.PROTOCOL,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400337 providerConfig.protocolsInfo().keySet().toString())
Andrea Campanellabc112a92017-06-26 19:06:43 +0200338 .build();
339 DeviceDescription description =
340 new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400341 driver.manufacturer(), driver.hwVersion(),
342 driver.swVersion(), UNKNOWN,
343 cid, false, annotations);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200344 //Empty list of ports
345 List<PortDescription> ports = new ArrayList<>();
346
347 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
348 DeviceDescriptionDiscovery deviceDiscovery = driver
349 .createBehaviour(driverData, DeviceDescriptionDiscovery.class);
350
351 DeviceDescription newdescription = deviceDiscovery.discoverDeviceDetails();
352 if (newdescription != null) {
353 description = newdescription;
354 }
355 ports = deviceDiscovery.discoverPortDetails();
356 }
357
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400358 if (!handlePipeconf(deviceId, driver, driverData)) {
359 // Something went wrong during handling of pipeconf.
360 // We already logged the error.
361 handshaker.disconnect();
362 return;
Andrea Campanellabc112a92017-06-26 19:06:43 +0200363 }
364
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400365 advertiseDevice(deviceId, description, ports);
366
Andrea Campanellabc112a92017-06-26 19:06:43 +0200367 } else {
368 log.warn("Can't connect to device {}", deviceId);
369 }
370 });
Andrea Campanella241896c2017-05-10 13:11:04 -0700371 }
372 }
373
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400374 /**
375 * Handles the case of a device that is pipeline programmable. Returns true if the operation wa successful and the
376 * device can be registered to the core, false otherwise.
377 */
378 private boolean handlePipeconf(DeviceId deviceId, Driver driver, DriverData driverData) {
379
380 PiPipelineProgrammable pipelineProg = getBehaviour(driver, PiPipelineProgrammable.class,
381 driverData);
382
383 if (pipelineProg == null) {
384 // Device is not pipeline programmable.
385 return true;
386 }
387
388 PiPipeconfId pipeconfId = piPipeconfService.ofDevice(deviceId).orElseGet(() -> {
389 // No pipeconf has been associated with this device.
390 // Check if device driver provides a default one.
391 if (pipelineProg.getDefaultPipeconf().isPresent()) {
392 PiPipeconf defaultPipeconf = pipelineProg.getDefaultPipeconf().get();
393 log.info("Using default pipeconf {} for {}", defaultPipeconf.id(), deviceId);
394 // Register default one if it is not.
395 // TODO: this should be performed at driver loading.
396 if (!piPipeconfService.getPipeconf(defaultPipeconf.id()).isPresent()) {
397 piPipeconfService.register(defaultPipeconf);
398 }
399 return defaultPipeconf.id();
400 } else {
401 return null;
402 }
403 });
404
405 if (pipeconfId == null) {
406 log.warn("Device {} is pipeline programmable but no pipeconf can be associated to it.", deviceId);
407 return true;
408 }
409
410
411 PiPipeconf pipeconf = piPipeconfService.getPipeconf(pipeconfId).orElseThrow(
412 () -> new IllegalStateException("Pipeconf is not registered: " + pipeconfId)
413 );
414
415
416 try {
417 if (!pipelineProg.deployPipeconf(pipeconf).get()) {
418 log.error("Unable to deploy pipeconf {} to {}, aborting device discovery", pipeconfId, deviceId);
419 return false;
420 }
421
422 if (!piPipeconfService.bindToDevice(pipeconfId, deviceId).get()) {
423 log.error("Unable to merge driver {} for device {} with pipeconf {}, aborting device discovery",
424 driver.name(), deviceId, pipeconfId);
425 return false;
426 }
427 } catch (InterruptedException | ExecutionException e) {
428 throw new IllegalStateException(e);
429 }
430
431 return true;
432 }
433
Andrea Campanellabc112a92017-06-26 19:06:43 +0200434 private void advertiseDevice(DeviceId deviceId, DeviceDescription description, List<PortDescription> ports) {
435 providerService.deviceConnected(deviceId, description);
436 providerService.updatePorts(deviceId, ports);
437 }
438
Andrea Campanella241896c2017-05-10 13:11:04 -0700439 private void disconnectDevice(DeviceId deviceId) {
440 log.info("Disconnecting for device {}", deviceId);
441 DeviceHandshaker handshaker = getHandshaker(deviceId);
442 if (handshaker != null) {
443 CompletableFuture<Boolean> disconnect = handshaker.disconnect();
444
445 disconnect.thenAcceptAsync(result -> {
446 if (result) {
447 log.info("Disconnected device {}", deviceId);
448 providerService.deviceDisconnected(deviceId);
449 } else {
450 log.warn("Device {} was unable to disconnect", deviceId);
451 }
452 });
453 } else {
454 //gracefully ignoring.
455 log.info("No DeviceHandshaker for device {}", deviceId);
456 }
457 }
458
459 //Needed to catch the exception in the executors since are not rethrown otherwise.
460 private Runnable exceptionSafe(Runnable runnable) {
461 return () -> {
462 try {
463 runnable.run();
464 } catch (Exception e) {
465 log.error("Unhandled Exception", e);
466 }
467 };
468 }
469
470 private void updatePortStatistics(DeviceId deviceId) {
471 Collection<PortStatistics> statistics = deviceService.getDevice(deviceId)
472 .as(PortStatisticsDiscovery.class)
473 .discoverPortStatistics();
474 providerService.updatePortStatistics(deviceId, statistics);
475 }
476
477 /**
478 * Listener for configuration events.
479 */
480 private class InternalNetworkConfigListener implements NetworkConfigListener {
481
482
483 @Override
484 public void event(NetworkConfigEvent event) {
485 DeviceId deviceId = (DeviceId) event.subject();
486 //Assuming that the deviceId comes with uri 'device:'
487 if (!deviceId.uri().getScheme().equals(URI_SCHEME)) {
488 // not under my scheme, skipping
489 log.debug("{} is not my scheme, skipping", deviceId);
490 return;
491 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200492 if (deviceService.getDevice(deviceId) != null || deviceService.isAvailable(deviceId)) {
Andrea Campanella241896c2017-05-10 13:11:04 -0700493 log.info("Device {} is already connected to ONOS and is available", deviceId);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200494 return;
495 }
496 //FIXME to be removed when netcfg will issue device events in a bundle or
497 // ensure all configuration needed is present
498 Lock lock = ENTRY_LOCKS.computeIfAbsent(deviceId, key -> new ReentrantLock());
499 lock.lock();
500 try {
501 if (event.configClass().equals(GeneralProviderDeviceConfig.class)) {
502 //FIXME we currently assume that p4runtime devices are pipeline configurable.
503 //If we want to connect a p4runtime device with no pipeline
504 if (event.config().isPresent() &&
505 Collections.disjoint(ImmutableSet.copyOf(event.config().get().node().fieldNames()),
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400506 PIPELINE_CONFIGURABLE_PROTOCOLS)) {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200507 pipelineConfigured.add(deviceId);
508 }
509 deviceConfigured.add(deviceId);
510 } else if (event.configClass().equals(BasicDeviceConfig.class)) {
511 if (event.config().isPresent() && event.config().get().node().has(DRIVER)) {
512 //TODO add check for pipeline and add it to the pipeline list if no
513 // p4runtime is present.
514 driverConfigured.add(deviceId);
515 }
516 } else if (event.configClass().equals(PiPipeconfConfig.class)) {
517 if (event.config().isPresent()
518 && event.config().get().node().has(PiPipeconfConfig.PIPIPECONFID)) {
519 pipelineConfigured.add(deviceId);
520 }
521 }
522 //if the device has no "pipeline configurable protocol it will be present
523 // in the pipelineConfigured
524 if (deviceConfigured.contains(deviceId) && driverConfigured.contains(deviceId)
525 && pipelineConfigured.contains(deviceId)) {
526 checkAndSubmitDeviceTask(deviceId);
527 } else {
528 if (deviceConfigured.contains(deviceId) && driverConfigured.contains(deviceId)) {
529 log.debug("Waiting for pipeline configuration for device {}", deviceId);
530 } else if (pipelineConfigured.contains(deviceId) && driverConfigured.contains(deviceId)) {
531 log.debug("Waiting for device configuration for device {}", deviceId);
532 } else if (pipelineConfigured.contains(deviceId) && deviceConfigured.contains(deviceId)) {
533 log.debug("Waiting for driver configuration for device {}", deviceId);
534 } else if (driverConfigured.contains(deviceId)) {
535 log.debug("Only driver configuration for device {}", deviceId);
536 } else if (deviceConfigured.contains(deviceId)) {
537 log.debug("Only device configuration for device {}", deviceId);
538 }
539 }
540 } finally {
541 lock.unlock();
Andrea Campanella241896c2017-05-10 13:11:04 -0700542 }
543 }
544
545 @Override
546 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200547 return (event.configClass().equals(GeneralProviderDeviceConfig.class) ||
548 event.configClass().equals(BasicDeviceConfig.class) ||
549 event.configClass().equals(PiPipeconfConfig.class)) &&
Andrea Campanella241896c2017-05-10 13:11:04 -0700550 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
551 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
552 }
553 }
554
Andrea Campanellabc112a92017-06-26 19:06:43 +0200555 private void checkAndSubmitDeviceTask(DeviceId deviceId) {
556 connectionExecutor.submit(exceptionSafe(() -> connectDevice(deviceId)));
557 //FIXME this will be removed when configuration is synced.
558 deviceConfigured.remove(deviceId);
559 driverConfigured.remove(deviceId);
560 pipelineConfigured.remove(deviceId);
561
562 }
563
Andrea Campanella241896c2017-05-10 13:11:04 -0700564 /**
565 * Listener for core device events.
566 */
567 private class InternalDeviceListener implements DeviceListener {
568 @Override
569 public void event(DeviceEvent event) {
570 Type type = event.type();
571
572 if (type.equals((Type.DEVICE_ADDED))) {
573
574 //For now this is scheduled periodically, when streaming API will
575 // be available we check and base it on the streaming API (e.g. gNMI)
576 if (deviceService.getDevice(event.subject().id()).
577 is(PortStatisticsDiscovery.class)) {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400578 portStatsExecutor.scheduleAtFixedRate(
579 exceptionSafe(() -> updatePortStatistics(event.subject().id())),
Andrea Campanella241896c2017-05-10 13:11:04 -0700580 0, PORT_STATS_PERIOD_SECONDS, TimeUnit.SECONDS);
581 updatePortStatistics(event.subject().id());
582 }
583
584 } else if (type.equals(Type.DEVICE_REMOVED)) {
585 connectionExecutor.submit(exceptionSafe(() ->
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400586 disconnectDevice(event.subject().id())));
Andrea Campanella241896c2017-05-10 13:11:04 -0700587 }
588 }
589
590 @Override
591 public boolean isRelevant(DeviceEvent event) {
592 return URI_SCHEME.toUpperCase()
593 .equals(event.subject().id().uri().toString());
594 }
595 }
596}