blob: 5e073337465e29683d6dfb292930285f0f7522a1 [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;
Carmelo Cascone158b8c42018-07-04 19:42:37 +020020import com.google.common.base.Objects;
Andrea Campanellabc112a92017-06-26 19:06:43 +020021import com.google.common.collect.ImmutableSet;
22import 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.apache.felix.scr.annotations.Service;
28import org.onlab.util.ItemNotFoundException;
Andrea Campanella14e196d2017-07-24 18:11:36 +020029import org.onosproject.cluster.ClusterService;
Andrea Campanellabc112a92017-06-26 19:06:43 +020030import org.onosproject.net.DeviceId;
31import org.onosproject.net.config.ConfigFactory;
Andrea Campanellabc112a92017-06-26 19:06:43 +020032import org.onosproject.net.config.NetworkConfigRegistry;
33import org.onosproject.net.config.basics.BasicDeviceConfig;
34import org.onosproject.net.config.basics.SubjectFactories;
35import org.onosproject.net.driver.Behaviour;
36import org.onosproject.net.driver.DefaultDriver;
37import org.onosproject.net.driver.Driver;
38import org.onosproject.net.driver.DriverAdminService;
39import org.onosproject.net.driver.DriverProvider;
40import org.onosproject.net.driver.DriverService;
41import org.onosproject.net.pi.model.PiPipeconf;
42import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080043import org.onosproject.net.pi.service.PiPipeconfConfig;
44import org.onosproject.net.pi.service.PiPipeconfMappingStore;
45import org.onosproject.net.pi.service.PiPipeconfService;
Andrea Campanellabc112a92017-06-26 19:06:43 +020046import org.slf4j.Logger;
47
48import java.util.HashMap;
49import java.util.Map;
50import java.util.Optional;
51import java.util.Set;
Andrea Campanellabc112a92017-06-26 19:06:43 +020052import java.util.concurrent.ConcurrentHashMap;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020053import java.util.concurrent.ConcurrentMap;
Andrea Campanellabc112a92017-06-26 19:06:43 +020054import java.util.concurrent.ExecutorService;
55import java.util.concurrent.Executors;
56
Carmelo Cascone44daf562017-07-16 23:55:08 -040057import static java.lang.String.format;
Andrea Campanellabc112a92017-06-26 19:06:43 +020058import static org.onlab.util.Tools.groupedThreads;
59import static org.slf4j.LoggerFactory.getLogger;
60
61
62/**
63 * Implementation of the PiPipeconfService.
64 */
65@Component(immediate = true)
66@Service
Andrea Campanella48f99fa2017-07-13 19:06:21 +020067@Beta
68public class PiPipeconfManager implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020069
70 private final Logger log = getLogger(getClass());
71
Carmelo Cascone158b8c42018-07-04 19:42:37 +020072 private static final String MERGED_DRIVER_SEPARATOR = ":";
Andrea Campanellabc112a92017-06-26 19:06:43 +020073 private static final String DRIVER = "driver";
74 private static final String CFG_SCHEME = "piPipeconf";
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected NetworkConfigRegistry cfgService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected DriverService driverService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DriverAdminService driverAdminService;
84
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020085 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020086 private PiPipeconfMappingStore pipeconfMappingStore;
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020087
Andrea Campanella14e196d2017-07-24 18:11:36 +020088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected ClusterService clusterService;
90
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020091 // Registered pipeconf are replicated through the app subsystem and
92 // registered on app activated events. Hence, there should be no need of
93 // distributing this map.
94 protected ConcurrentMap<PiPipeconfId, PiPipeconf> pipeconfs = new ConcurrentHashMap<>();
Andrea Campanellabc112a92017-06-26 19:06:43 +020095
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020096 protected ExecutorService executor = Executors.newFixedThreadPool(
97 10, groupedThreads("onos/pipeconf-manager", "%d", log));
Andrea Campanellabc112a92017-06-26 19:06:43 +020098
Carmelo Cascone96beb6f2018-06-27 18:07:12 +020099 protected final ConfigFactory configFactory =
Andrea Campanellabc112a92017-06-26 19:06:43 +0200100 new ConfigFactory<DeviceId, PiPipeconfConfig>(
101 SubjectFactories.DEVICE_SUBJECT_FACTORY,
102 PiPipeconfConfig.class, CFG_SCHEME) {
103 @Override
104 public PiPipeconfConfig createConfig() {
105 return new PiPipeconfConfig();
106 }
107 };
108
Andrea Campanellabc112a92017-06-26 19:06:43 +0200109 @Activate
110 public void activate() {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200111 cfgService.registerConfigFactory(configFactory);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200112 log.info("Started");
113 }
114
115
116 @Deactivate
117 public void deactivate() {
118 executor.shutdown();
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200119 cfgService.unregisterConfigFactory(configFactory);
120 pipeconfs.clear();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200121 cfgService = null;
122 driverAdminService = null;
123 driverService = null;
124 log.info("Stopped");
125 }
126
127 @Override
128 public void register(PiPipeconf pipeconf) throws IllegalStateException {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200129 if (pipeconfs.containsKey(pipeconf.id())) {
Carmelo Cascone44daf562017-07-16 23:55:08 -0400130 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
131 }
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200132 pipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone44daf562017-07-16 23:55:08 -0400133 log.info("New pipeconf registered: {}", pipeconf.id());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200134 }
135
136 @Override
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200137 public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200138 // TODO add mechanism to remove from device.
139 if (!pipeconfs.containsKey(pipeconfId)) {
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200140 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
141 }
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200142 // TODO remove the binding from the distributed Store when the lifecycle of a pipeconf is defined.
143 // pipeconfMappingStore.removeBindings(pipeconfId);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200144 log.info("Removing pipeconf {}", pipeconfId);
145 pipeconfs.remove(pipeconfId);
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200146 }
147
148 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200149 public Iterable<PiPipeconf> getPipeconfs() {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200150 return pipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200151 }
152
153 @Override
154 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200155 return Optional.ofNullable(pipeconfs.get(id));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200156 }
157
158 @Override
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200159 public void bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
160 pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
161 }
162
163 @Override
164 public String mergeDriver(DeviceId deviceId, PiPipeconfId pipeconfId) {
165 return doMergeDriver(deviceId, pipeconfId);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200166 }
167
168 @Override
169 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200170 return Optional.ofNullable(pipeconfMappingStore.getPipeconfId(deviceId));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200171 }
172
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200173 private String doMergeDriver(DeviceId deviceId, PiPipeconfId pipeconfId) {
174 log.debug("Starting device driver merge of {} with {}...", deviceId, pipeconfId);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200175 final BasicDeviceConfig basicDeviceConfig = cfgService.getConfig(
176 deviceId, BasicDeviceConfig.class);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200177 if (basicDeviceConfig == null) {
178 log.warn("Unable to get basic device config for {}, " +
179 "aborting pipeconf driver merge");
180 return null;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200181 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200182 String baseDriverName = basicDeviceConfig.driver();
183 if (baseDriverName.endsWith(mergedDriverSuffix(pipeconfId))) {
184 // The config already has driver name that is a merged one. We still
185 // need to make sure an instance of that merged driver is present in
186 // this node.
187 log.debug("Base driver of {} ({}) has been already merged with {}",
188 deviceId, baseDriverName, pipeconfId);
189 baseDriverName = getBaseDriverNameFromMerged(baseDriverName);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200190 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200191
192 final String newDriverName = mergedDriverName(baseDriverName, pipeconfId);
193 // If merged driver exists already we don't create a new one.
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200194 try {
195 driverService.getDriver(newDriverName);
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200196 return newDriverName;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200197 } catch (ItemNotFoundException e) {
198 log.info("Creating merged driver {}...", newDriverName);
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200199 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200200 final Driver mergedDriver = buildMergedDriver(
201 pipeconfId, baseDriverName, newDriverName);
202 if (mergedDriver == null) {
203 // Error logged by buildMergedDriver
204 return null;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200205 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200206 registerMergedDriver(mergedDriver);
207 return newDriverName;
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200208 }
209
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200210 private String mergedDriverSuffix(PiPipeconfId pipeconfId) {
211 return MERGED_DRIVER_SEPARATOR + pipeconfId.id();
212 }
213
214 private String mergedDriverName(String baseDriverName, PiPipeconfId pipeconfId) {
215 return baseDriverName + mergedDriverSuffix(pipeconfId);
216 }
217
218 private String getBaseDriverNameFromMerged(String mergedDriverName) {
219 final String[] pieces = mergedDriverName.split(MERGED_DRIVER_SEPARATOR);
220 if (pieces.length != 2) {
221 log.error("Unrecognized merged driver name format '{}', cannot " +
222 "extract base driver name", mergedDriverName);
223 return null;
224 }
225 return pieces[0];
226 }
227
228 private Driver buildMergedDriver(PiPipeconfId pipeconfId, String baseDriverName,
229 String newDriverName) {
230 final Driver baseDriver;
231 try {
232 baseDriver = driverService.getDriver(baseDriverName);
233 } catch (ItemNotFoundException e) {
234 log.error("Base driver {} not found, cannot build a merged one",
235 baseDriverName);
236 return null;
237 }
238
239 final PiPipeconf pipeconf = pipeconfs.get(pipeconfId);
240 if (pipeconf == null) {
241 log.error("Pipeconf {} is not registered, cannot build a merged driver",
242 pipeconfId);
243 return null;
244 }
245
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200246 // extract the behaviours from the pipipeconf.
247 final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours =
248 new HashMap<>();
249 pipeconf.behaviours().forEach(
250 b -> behaviours.put(b, pipeconf.implementation(b).get()));
251 final Driver piPipeconfDriver = new DefaultDriver(
252 newDriverName, baseDriver.parents(),
253 baseDriver.manufacturer(), baseDriver.hwVersion(),
254 baseDriver.swVersion(), behaviours, new HashMap<>());
255 // take the base driver created with the behaviours of the PiPeconf and
256 // merge it with the base driver that was assigned to the device
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200257 return piPipeconfDriver.merge(baseDriver);
258 }
259
260 private void registerMergedDriver(Driver driver) {
261 final DriverProvider provider = new InternalDriverProvider(driver);
262 if (driverAdminService.getProviders().contains(provider)) {
263 // A provider for this driver already exist.
264 return;
265 }
Carmelo Cascone96beb6f2018-06-27 18:07:12 +0200266 driverAdminService.registerProvider(provider);
267 }
268
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200269 /**
270 * Internal driver provider used to register merged pipeconf drivers in the
271 * core.
272 */
273 private class InternalDriverProvider implements DriverProvider {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200274
275 Driver driver;
276
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200277 InternalDriverProvider(Driver driver) {
Andrea Campanellabc112a92017-06-26 19:06:43 +0200278 this.driver = driver;
279 }
280
281 @Override
282 public Set<Driver> getDrivers() {
283 return ImmutableSet.of(driver);
284 }
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200285
286 @Override
287 public boolean equals(Object o) {
288 if (this == o) {
289 return true;
290 }
291 if (o == null || getClass() != o.getClass()) {
292 return false;
293 }
294 InternalDriverProvider that = (InternalDriverProvider) o;
295 return Objects.equal(driver.name(), that.driver.name());
296 }
297
298 @Override
299 public int hashCode() {
300 return Objects.hashCode(driver.name());
301 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200302 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200303}