blob: 8fdbfc8a809780b65f23b20bef9d927353f1b843 [file] [log] [blame]
Andrea Campanellabc112a92017-06-26 19:06:43 +02001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Andrea Campanellabc112a92017-06-26 19:06:43 +02003 *
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.net.pi.impl;
18
Andrea Campanella48f99fa2017-07-13 19:06:21 +020019import com.google.common.annotations.Beta;
Andrea Campanellabc112a92017-06-26 19:06:43 +020020import com.google.common.collect.ImmutableSet;
Carmelo Casconeda60a612018-08-24 00:01:34 -070021import com.google.common.collect.Sets;
22import com.google.common.util.concurrent.Striped;
Carmelo Cascone3b4baa02019-04-17 16:39:12 -070023import org.onlab.util.HexString;
Andrea Campanellabc112a92017-06-26 19:06:43 +020024import org.onlab.util.ItemNotFoundException;
Carmelo Casconeda0b5592018-09-14 12:54:15 -070025import org.onlab.util.SharedExecutors;
Carmelo Cascone75a9a892019-04-22 12:12:23 -070026import org.onosproject.event.AbstractListenerManager;
Andrea Campanellabc112a92017-06-26 19:06:43 +020027import org.onosproject.net.DeviceId;
Andrea Campanellabc112a92017-06-26 19:06:43 +020028import org.onosproject.net.config.NetworkConfigRegistry;
29import org.onosproject.net.config.basics.BasicDeviceConfig;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080030import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanellabc112a92017-06-26 19:06:43 +020031import org.onosproject.net.driver.Behaviour;
32import org.onosproject.net.driver.DefaultDriver;
33import org.onosproject.net.driver.Driver;
34import org.onosproject.net.driver.DriverAdminService;
Carmelo Casconeda60a612018-08-24 00:01:34 -070035import org.onosproject.net.driver.DriverEvent;
36import org.onosproject.net.driver.DriverListener;
Andrea Campanellabc112a92017-06-26 19:06:43 +020037import org.onosproject.net.driver.DriverProvider;
Andrea Campanellabc112a92017-06-26 19:06:43 +020038import org.onosproject.net.pi.model.PiPipeconf;
39import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone75a9a892019-04-22 12:12:23 -070040import org.onosproject.net.pi.service.PiPipeconfEvent;
41import org.onosproject.net.pi.service.PiPipeconfListener;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080042import org.onosproject.net.pi.service.PiPipeconfMappingStore;
43import org.onosproject.net.pi.service.PiPipeconfService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import org.osgi.service.component.annotations.Activate;
45import org.osgi.service.component.annotations.Component;
46import org.osgi.service.component.annotations.Deactivate;
47import org.osgi.service.component.annotations.Reference;
48import org.osgi.service.component.annotations.ReferenceCardinality;
Andrea Campanellabc112a92017-06-26 19:06:43 +020049import org.slf4j.Logger;
50
51import java.util.HashMap;
52import java.util.Map;
Carmelo Casconeda60a612018-08-24 00:01:34 -070053import java.util.Objects;
Andrea Campanellabc112a92017-06-26 19:06:43 +020054import java.util.Optional;
55import java.util.Set;
Andrea Campanellabc112a92017-06-26 19:06:43 +020056import java.util.concurrent.ConcurrentHashMap;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020057import java.util.concurrent.ConcurrentMap;
Andrea Campanellabc112a92017-06-26 19:06:43 +020058import java.util.concurrent.ExecutorService;
59import java.util.concurrent.Executors;
Carmelo Casconeda0b5592018-09-14 12:54:15 -070060import java.util.concurrent.TimeUnit;
Carmelo Casconeda60a612018-08-24 00:01:34 -070061import java.util.concurrent.locks.Lock;
Andrea Campanellabc112a92017-06-26 19:06:43 +020062
Carmelo Cascone3b4baa02019-04-17 16:39:12 -070063import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone44daf562017-07-16 23:55:08 -040064import static java.lang.String.format;
Andrea Campanellabc112a92017-06-26 19:06:43 +020065import static org.onlab.util.Tools.groupedThreads;
66import static org.slf4j.LoggerFactory.getLogger;
67
68
69/**
70 * Implementation of the PiPipeconfService.
71 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072@Component(immediate = true, service = PiPipeconfService.class)
Andrea Campanella48f99fa2017-07-13 19:06:21 +020073@Beta
Carmelo Cascone75a9a892019-04-22 12:12:23 -070074public class PiPipeconfManager
75 extends AbstractListenerManager<PiPipeconfEvent, PiPipeconfListener>
76 implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020077
78 private final Logger log = getLogger(getClass());
79
Carmelo Cascone158b8c42018-07-04 19:42:37 +020080 private static final String MERGED_DRIVER_SEPARATOR = ":";
Andrea Campanellabc112a92017-06-26 19:06:43 +020081
Carmelo Casconeda0b5592018-09-14 12:54:15 -070082 private static final int MISSING_DRIVER_WATCHDOG_INTERVAL = 5; // Seconds.
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellabc112a92017-06-26 19:06:43 +020085 protected NetworkConfigRegistry cfgService;
86
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellabc112a92017-06-26 19:06:43 +020088 protected DriverAdminService driverAdminService;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020091 private PiPipeconfMappingStore pipeconfMappingStore;
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020092
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020093 // Registered pipeconf are replicated through the app subsystem and
94 // registered on app activated events. Hence, there should be no need of
95 // distributing this map.
96 protected ConcurrentMap<PiPipeconfId, PiPipeconf> pipeconfs = new ConcurrentHashMap<>();
Andrea Campanellabc112a92017-06-26 19:06:43 +020097
Carmelo Casconeda60a612018-08-24 00:01:34 -070098 private final DriverListener driverListener = new InternalDriverListener();
99 private final Set<String> missingMergedDrivers = Sets.newCopyOnWriteArraySet();
100 private final Striped<Lock> locks = Striped.lock(20);
101
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200102 protected ExecutorService executor = Executors.newFixedThreadPool(
103 10, groupedThreads("onos/pipeconf-manager", "%d", log));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200104
Andrea Campanellabc112a92017-06-26 19:06:43 +0200105 @Activate
106 public void activate() {
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700107 driverAdminService.addListener(driverListener);
Carmelo Cascone75a9a892019-04-22 12:12:23 -0700108 eventDispatcher.addSink(PiPipeconfEvent.class, listenerRegistry);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700109 checkMissingMergedDrivers();
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700110 if (!missingMergedDrivers.isEmpty()) {
111 // Missing drivers should be created upon detecting registration
112 // events of a new pipeconf or a base driver. If, for any reason, we
113 // miss such event, here's a watchdog task.
114 SharedExecutors.getPoolThreadExecutor()
115 .execute(this::missingDriversWatchdogTask);
116 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200117 log.info("Started");
118 }
119
120
121 @Deactivate
122 public void deactivate() {
Carmelo Cascone75a9a892019-04-22 12:12:23 -0700123 eventDispatcher.removeSink(PiPipeconfEvent.class);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200124 executor.shutdown();
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700125 driverAdminService.removeListener(driverListener);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200126 pipeconfs.clear();
Carmelo Casconeda60a612018-08-24 00:01:34 -0700127 missingMergedDrivers.clear();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200128 cfgService = null;
129 driverAdminService = null;
Andrea Campanellabc112a92017-06-26 19:06:43 +0200130 log.info("Stopped");
131 }
132
133 @Override
134 public void register(PiPipeconf pipeconf) throws IllegalStateException {
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700135 checkNotNull(pipeconf);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200136 if (pipeconfs.containsKey(pipeconf.id())) {
Carmelo Cascone44daf562017-07-16 23:55:08 -0400137 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
138 }
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200139 pipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700140 log.info("New pipeconf registered: {} (fingerprint={})",
141 pipeconf.id(), HexString.toHexString(pipeconf.fingerprint()));
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700142 executor.execute(() -> attemptMergeAll(pipeconf.id()));
Carmelo Cascone75a9a892019-04-22 12:12:23 -0700143 post(new PiPipeconfEvent(PiPipeconfEvent.Type.REGISTERED, pipeconf));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200144 }
145
146 @Override
Carmelo Cascone75a9a892019-04-22 12:12:23 -0700147 public void unregister(PiPipeconfId pipeconfId) throws IllegalStateException {
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700148 checkNotNull(pipeconfId);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200149 // TODO add mechanism to remove from device.
150 if (!pipeconfs.containsKey(pipeconfId)) {
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200151 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
152 }
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200153 // TODO remove the binding from the distributed Store when the lifecycle of a pipeconf is defined.
154 // pipeconfMappingStore.removeBindings(pipeconfId);
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700155 final PiPipeconf pipeconf = pipeconfs.remove(pipeconfId);
156 log.info("Unregistered pipeconf: {} (fingerprint={})",
157 pipeconfId, HexString.toHexString(pipeconf.fingerprint()));
Carmelo Cascone75a9a892019-04-22 12:12:23 -0700158 post(new PiPipeconfEvent(PiPipeconfEvent.Type.UNREGISTERED, pipeconfId));
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200159 }
160
161 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200162 public Iterable<PiPipeconf> getPipeconfs() {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200163 return pipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200164 }
165
166 @Override
167 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200168 return Optional.ofNullable(pipeconfs.get(id));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200169 }
170
171 @Override
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800172 public Optional<PiPipeconf> getPipeconf(DeviceId deviceId) {
173 if (pipeconfMappingStore.getPipeconfId(deviceId) == null) {
174 return Optional.empty();
175 } else {
176 return Optional.ofNullable(pipeconfs.get(
177 pipeconfMappingStore.getPipeconfId(deviceId)));
178 }
179 }
180
181 @Override
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200182 public void bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700183 PiPipeconfId existingPipeconfId = pipeconfMappingStore.getPipeconfId(deviceId);
184 if (existingPipeconfId != null && !existingPipeconfId.equals(pipeconfId)) {
185 log.error("Cannot set binding for {} to {} as one already exists ({})",
186 deviceId, pipeconfId, existingPipeconfId);
187 return;
188 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200189 pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
190 }
191
192 @Override
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700193 public String getMergedDriver(DeviceId deviceId, PiPipeconfId pipeconfId) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200194 log.debug("Starting device driver merge of {} with {}...", deviceId, pipeconfId);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200195 final BasicDeviceConfig basicDeviceConfig = cfgService.getConfig(
196 deviceId, BasicDeviceConfig.class);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200197 if (basicDeviceConfig == null) {
198 log.warn("Unable to get basic device config for {}, " +
Tian Jian855b1082018-10-23 16:22:47 +0800199 "aborting pipeconf driver merge", deviceId);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200200 return null;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200201 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200202 String baseDriverName = basicDeviceConfig.driver();
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700203 if (baseDriverName == null) {
204 log.warn("Missing driver from basic device config for {}, " +
205 "cannot produce merged driver", deviceId);
206 return null;
207 }
Carmelo Casconeda60a612018-08-24 00:01:34 -0700208 if (isMergedDriverName(baseDriverName)) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200209 // The config already has driver name that is a merged one. We still
210 // need to make sure an instance of that merged driver is present in
211 // this node.
Carmelo Casconeda60a612018-08-24 00:01:34 -0700212 log.debug("Base driver of {} ({}) is a merged one",
213 deviceId, baseDriverName);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200214 baseDriverName = getBaseDriverNameFromMerged(baseDriverName);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200215 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200216
Carmelo Casconeda60a612018-08-24 00:01:34 -0700217 return doMergeDriver(baseDriverName, pipeconfId);
218 }
219
220 @Override
221 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
222 return Optional.ofNullable(pipeconfMappingStore.getPipeconfId(deviceId));
223 }
224
225 private String doMergeDriver(String baseDriverName, PiPipeconfId pipeconfId) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200226 final String newDriverName = mergedDriverName(baseDriverName, pipeconfId);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700227 // Serialize per newDriverName, avoid creating duplicates.
228 locks.get(newDriverName).lock();
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200229 try {
Carmelo Casconeda60a612018-08-24 00:01:34 -0700230 // If merged driver exists already we don't create a new one.
231 if (getDriver(newDriverName) != null) {
232 return newDriverName;
233 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800234 log.debug("Creating merged driver {}...", newDriverName);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700235 final Driver mergedDriver = buildMergedDriver(
236 pipeconfId, baseDriverName, newDriverName);
237 if (mergedDriver == null) {
238 // Error logged by buildMergedDriver
239 return null;
240 }
241 registerMergedDriver(mergedDriver);
242 if (missingMergedDrivers.remove(newDriverName)) {
243 log.info("There are still {} missing merged drivers",
244 missingMergedDrivers.size());
245 }
246 return newDriverName;
247 } finally {
248 locks.get(newDriverName).unlock();
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200249 }
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200250 }
251
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200252 private String mergedDriverSuffix(PiPipeconfId pipeconfId) {
253 return MERGED_DRIVER_SEPARATOR + pipeconfId.id();
254 }
255
256 private String mergedDriverName(String baseDriverName, PiPipeconfId pipeconfId) {
257 return baseDriverName + mergedDriverSuffix(pipeconfId);
258 }
259
260 private String getBaseDriverNameFromMerged(String mergedDriverName) {
261 final String[] pieces = mergedDriverName.split(MERGED_DRIVER_SEPARATOR);
262 if (pieces.length != 2) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200263 return null;
264 }
265 return pieces[0];
266 }
267
Carmelo Casconeda60a612018-08-24 00:01:34 -0700268 private PiPipeconfId getPipeconfIdFromMerged(String mergedDriverName) {
269 final String[] pieces = mergedDriverName.split(MERGED_DRIVER_SEPARATOR);
270 if (pieces.length != 2) {
271 return null;
272 }
273 return new PiPipeconfId(pieces[1]);
274 }
275
276 private boolean isMergedDriverName(String driverName) {
277 final String[] pieces = driverName.split(MERGED_DRIVER_SEPARATOR);
278 return pieces.length == 2;
279 }
280
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200281 private Driver buildMergedDriver(PiPipeconfId pipeconfId, String baseDriverName,
282 String newDriverName) {
Carmelo Casconeda60a612018-08-24 00:01:34 -0700283 final Driver baseDriver = getDriver(baseDriverName);
284 if (baseDriver == null) {
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200285 log.error("Base driver {} not found, cannot build a merged one",
286 baseDriverName);
287 return null;
288 }
289
290 final PiPipeconf pipeconf = pipeconfs.get(pipeconfId);
291 if (pipeconf == null) {
292 log.error("Pipeconf {} is not registered, cannot build a merged driver",
293 pipeconfId);
294 return null;
295 }
296
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200297 // extract the behaviours from the pipipeconf.
298 final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours =
299 new HashMap<>();
300 pipeconf.behaviours().forEach(
301 b -> behaviours.put(b, pipeconf.implementation(b).get()));
Carmelo Casconeab5d41e2019-03-06 18:02:34 -0800302
303 // FIXME: remove this check when stratum_bmv2 will be open source and we
304 // will no longer need to read port counters from the p4 program. Here
305 // we ignore the PortStatisticsDiscovery behaviour from the pipeconf if
306 // the base driver (e.g. stratum with gnmi) already has it. But in
307 // general, we should give higher priority to pipeconf behaviours.
308 if (baseDriver.hasBehaviour(PortStatisticsDiscovery.class)
309 && behaviours.remove(PortStatisticsDiscovery.class) != null) {
310 log.warn("Ignoring {} behaviour from pipeconf {}, but using " +
311 "the one provided by {} driver...",
312 PortStatisticsDiscovery.class.getSimpleName(), pipeconfId,
313 baseDriver.name());
314 }
315
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200316 final Driver piPipeconfDriver = new DefaultDriver(
317 newDriverName, baseDriver.parents(),
318 baseDriver.manufacturer(), baseDriver.hwVersion(),
319 baseDriver.swVersion(), behaviours, new HashMap<>());
320 // take the base driver created with the behaviours of the PiPeconf and
321 // merge it with the base driver that was assigned to the device
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200322 return piPipeconfDriver.merge(baseDriver);
323 }
324
325 private void registerMergedDriver(Driver driver) {
326 final DriverProvider provider = new InternalDriverProvider(driver);
327 if (driverAdminService.getProviders().contains(provider)) {
328 // A provider for this driver already exist.
329 return;
330 }
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200331 driverAdminService.registerProvider(provider);
332 }
333
Carmelo Casconeda60a612018-08-24 00:01:34 -0700334 private Driver getDriver(String name) {
335 try {
Carmelo Cascone0761cd32018-08-29 19:22:50 -0700336 return driverAdminService.getDriver(name);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700337 } catch (ItemNotFoundException e) {
338 return null;
339 }
340 }
341
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700342 private boolean driverExists(String name) {
343 return getDriver(name) != null;
Carmelo Casconeda60a612018-08-24 00:01:34 -0700344 }
345
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700346 private void checkMissingMergedDriver(DeviceId deviceId) {
347 final PiPipeconfId pipeconfId = pipeconfMappingStore.getPipeconfId(deviceId);
348 final BasicDeviceConfig cfg = cfgService.getConfig(deviceId, BasicDeviceConfig.class);
349
350 if (pipeconfId == null) {
351 // No pipeconf associated.
352 return;
353 }
354
355 if (cfg == null || cfg.driver() == null) {
356 log.warn("Missing basic device config or driver key in netcfg for " +
357 "{}, which is odd since it has a " +
358 "pipeconf associated ({})",
359 deviceId, pipeconfId);
360 return;
361 }
362
363 final String baseDriverName = cfg.driver();
364 final String mergedDriverName = mergedDriverName(baseDriverName, pipeconfId);
365
366 if (driverExists(mergedDriverName) ||
367 missingMergedDrivers.contains(mergedDriverName)) {
368 // Not missing, or already aware of it missing.
369 return;
370 }
371
372 log.info("Detected missing merged driver: {}", mergedDriverName);
373 missingMergedDrivers.add(mergedDriverName);
374 // Attempt building the driver now if all pieces are present.
375 // If not, either a driver or pipeconf event will re-trigger
376 // the process.
377 attemptDriverMerge(mergedDriverName);
378 }
379
380 private void attemptDriverMerge(String mergedDriverName) {
381 final String baseDriverName = getBaseDriverNameFromMerged(mergedDriverName);
382 final PiPipeconfId pipeconfId = getPipeconfIdFromMerged(mergedDriverName);
383 if (driverExists(baseDriverName) && pipeconfs.containsKey(pipeconfId)) {
384 doMergeDriver(baseDriverName, pipeconfId);
385 }
386 }
387
388 private void missingDriversWatchdogTask() {
389 while (true) {
390 // Most probably all missing drivers will be created before the
391 // watchdog interval, so wait before starting...
392 try {
393 TimeUnit.SECONDS.sleep(MISSING_DRIVER_WATCHDOG_INTERVAL);
394 } catch (InterruptedException e) {
395 log.warn("Interrupted! There are still {} missing merged drivers",
396 missingMergedDrivers.size());
397 }
398 if (missingMergedDrivers.isEmpty()) {
399 log.info("There are no more missing merged drivers!");
400 return;
401 }
402 log.info("Detected {} missing merged drivers, attempt merge...",
403 missingMergedDrivers.size());
404 missingMergedDrivers.forEach(this::attemptDriverMerge);
405 }
406 }
407
408 private void checkMissingMergedDrivers() {
409 cfgService.getSubjects(DeviceId.class, BasicDeviceConfig.class)
410 .forEach(this::checkMissingMergedDriver);
411 }
412
413 private void attemptMergeAll(String baseDriverName) {
Carmelo Casconeda60a612018-08-24 00:01:34 -0700414 missingMergedDrivers.stream()
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700415 .filter(missingDriver -> {
416 // Filter missing merged drivers using this base driver.
417 final String xx = getBaseDriverNameFromMerged(missingDriver);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700418 return xx != null && xx.equals(baseDriverName);
419 })
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700420 .forEach(this::attemptDriverMerge);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700421 }
422
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700423 private void attemptMergeAll(PiPipeconfId pipeconfId) {
Carmelo Casconeda60a612018-08-24 00:01:34 -0700424 missingMergedDrivers.stream()
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700425 .filter(missingDriver -> {
426 // Filter missing merged drivers using this pipeconf.
427 final PiPipeconfId xx = getPipeconfIdFromMerged(missingDriver);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700428 return xx != null && xx.equals(pipeconfId);
429 })
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700430 .forEach(this::attemptDriverMerge);
Carmelo Casconeda60a612018-08-24 00:01:34 -0700431 }
432
433 private class InternalDriverListener implements DriverListener {
434
435 @Override
436 public void event(DriverEvent event) {
Carmelo Casconeda0b5592018-09-14 12:54:15 -0700437 executor.execute(() -> attemptMergeAll(event.subject().name()));
Carmelo Casconeda60a612018-08-24 00:01:34 -0700438 }
439
440 @Override
441 public boolean isRelevant(DriverEvent event) {
442 return event.type() == DriverEvent.Type.DRIVER_ENHANCED;
443 }
444 }
445
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200446 /**
447 * Internal driver provider used to register merged pipeconf drivers in the
448 * core.
449 */
450 private class InternalDriverProvider implements DriverProvider {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200451
452 Driver driver;
453
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200454 InternalDriverProvider(Driver driver) {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200455 this.driver = driver;
456 }
457
458 @Override
459 public Set<Driver> getDrivers() {
460 return ImmutableSet.of(driver);
461 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200462
463 @Override
464 public boolean equals(Object o) {
465 if (this == o) {
466 return true;
467 }
468 if (o == null || getClass() != o.getClass()) {
469 return false;
470 }
471 InternalDriverProvider that = (InternalDriverProvider) o;
Carmelo Casconeda60a612018-08-24 00:01:34 -0700472 return Objects.equals(driver.name(), that.driver.name());
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200473 }
474
475 @Override
476 public int hashCode() {
477 return Objects.hashCode(driver.name());
478 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200479 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200480}