blob: 55a4a11c009242d9388cee8d830d9980877bf62d [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
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.ObjectMapper;
21import com.fasterxml.jackson.databind.node.ObjectNode;
Andrea Campanella48f99fa2017-07-13 19:06:21 +020022import com.google.common.annotations.Beta;
Andrea Campanellabc112a92017-06-26 19:06:43 +020023import com.google.common.collect.ImmutableSet;
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
30import org.onlab.util.ItemNotFoundException;
Andrea Campanella14e196d2017-07-24 18:11:36 +020031import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.LeadershipService;
33import org.onosproject.cluster.NodeId;
Andrea Campanellabc112a92017-06-26 19:06:43 +020034import org.onosproject.net.DeviceId;
35import org.onosproject.net.config.ConfigFactory;
36import org.onosproject.net.config.NetworkConfigEvent;
37import org.onosproject.net.config.NetworkConfigListener;
38import org.onosproject.net.config.NetworkConfigRegistry;
39import org.onosproject.net.config.basics.BasicDeviceConfig;
40import org.onosproject.net.config.basics.SubjectFactories;
41import org.onosproject.net.driver.Behaviour;
42import org.onosproject.net.driver.DefaultDriver;
43import org.onosproject.net.driver.Driver;
44import org.onosproject.net.driver.DriverAdminService;
45import org.onosproject.net.driver.DriverProvider;
46import org.onosproject.net.driver.DriverService;
47import org.onosproject.net.pi.model.PiPipeconf;
48import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080049import org.onosproject.net.pi.service.PiPipeconfConfig;
50import org.onosproject.net.pi.service.PiPipeconfMappingStore;
51import org.onosproject.net.pi.service.PiPipeconfService;
Andrea Campanellabc112a92017-06-26 19:06:43 +020052import org.slf4j.Logger;
53
54import java.util.HashMap;
55import java.util.Map;
56import java.util.Optional;
57import java.util.Set;
58import java.util.concurrent.CompletableFuture;
59import java.util.concurrent.ConcurrentHashMap;
60import java.util.concurrent.ExecutorService;
61import java.util.concurrent.Executors;
62
Carmelo Cascone44daf562017-07-16 23:55:08 -040063import static java.lang.String.format;
Andrea Campanellabc112a92017-06-26 19:06:43 +020064import static org.onlab.util.Tools.groupedThreads;
65import static org.slf4j.LoggerFactory.getLogger;
66
67
68/**
69 * Implementation of the PiPipeconfService.
70 */
71@Component(immediate = true)
72@Service
Andrea Campanella48f99fa2017-07-13 19:06:21 +020073@Beta
74public class PiPipeconfManager implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020075
76 private final Logger log = getLogger(getClass());
77
78 private static final String DRIVER = "driver";
79 private static final String CFG_SCHEME = "piPipeconf";
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected NetworkConfigRegistry cfgService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella14e196d2017-07-24 18:11:36 +020085 protected LeadershipService leadershipService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanellabc112a92017-06-26 19:06:43 +020088 protected DriverService driverService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DriverAdminService driverAdminService;
92
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020093 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected PiPipeconfMappingStore pipeconfMappingStore;
95
Andrea Campanella14e196d2017-07-24 18:11:36 +020096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected ClusterService clusterService;
98
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020099 // Registered pipeconf are replicated through the app subsystem and registered on app activated events.
Andrea Campanellabc112a92017-06-26 19:06:43 +0200100 protected ConcurrentHashMap<PiPipeconfId, PiPipeconf> piPipeconfs = new ConcurrentHashMap<>();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200101
102 protected ExecutorService executor =
103 Executors.newFixedThreadPool(5, groupedThreads("onos/pipipeconfservice",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200104 "pipeline-to-device-%d", log));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200105
106 protected final ConfigFactory factory =
107 new ConfigFactory<DeviceId, PiPipeconfConfig>(
108 SubjectFactories.DEVICE_SUBJECT_FACTORY,
109 PiPipeconfConfig.class, CFG_SCHEME) {
110 @Override
111 public PiPipeconfConfig createConfig() {
112 return new PiPipeconfConfig();
113 }
114 };
115
116 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
117
118 @Activate
119 public void activate() {
120 cfgService.registerConfigFactory(factory);
121 cfgService.addListener(cfgListener);
122 cfgService.getSubjects(DeviceId.class, PiPipeconfConfig.class)
123 .forEach(this::addPipeconfFromCfg);
124 log.info("Started");
125 }
126
127
128 @Deactivate
129 public void deactivate() {
130 executor.shutdown();
131 cfgService.removeListener(cfgListener);
132 cfgService.unregisterConfigFactory(factory);
133 piPipeconfs.clear();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200134 cfgService = null;
135 driverAdminService = null;
136 driverService = null;
137 log.info("Stopped");
138 }
139
140 @Override
141 public void register(PiPipeconf pipeconf) throws IllegalStateException {
Carmelo Cascone44daf562017-07-16 23:55:08 -0400142 if (piPipeconfs.containsKey(pipeconf.id())) {
143 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
144 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200145 piPipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone44daf562017-07-16 23:55:08 -0400146 log.info("New pipeconf registered: {}", pipeconf.id());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200147 }
148
149 @Override
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200150 public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200151 //TODO add mechanism to remove from device.
152 if (!piPipeconfs.containsKey(pipeconfId)) {
153 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
154 }
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200155 // TODO remove the binding from the distributed Store when the lifecycle of a pipeconf is defined.
156 // pipeconfMappingStore.removeBindings(pipeconfId);
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200157 piPipeconfs.remove(pipeconfId);
158 }
159
160 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200161 public Iterable<PiPipeconf> getPipeconfs() {
Andrea Campanella48f99fa2017-07-13 19:06:21 +0200162 return piPipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200163 }
164
165 @Override
166 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
167 return Optional.ofNullable(piPipeconfs.get(id));
168 }
169
170 @Override
171 public CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
172 CompletableFuture<Boolean> operationResult = new CompletableFuture<>();
173
174 executor.execute(() -> {
175 BasicDeviceConfig basicDeviceConfig =
176 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
177 Driver baseDriver = driverService.getDriver(basicDeviceConfig.driver());
178
179 String completeDriverName = baseDriver.name() + ":" + pipeconfId;
180 PiPipeconf piPipeconf = piPipeconfs.get(pipeconfId);
181 if (piPipeconf == null) {
182 log.warn("Pipeconf {} is not present", pipeconfId);
183 operationResult.complete(false);
184 } else {
185 //if driver exists already we don't create a new one.
186 //needs to be done via exception catching due to DriverRegistry throwing it on a null return from
187 //the driver map.
188 try {
189 driverService.getDriver(completeDriverName);
190 } catch (ItemNotFoundException e) {
191
192 log.debug("First time pipeconf {} is used with base driver {}, merging the two",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200193 pipeconfId, baseDriver);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200194 //extract the behaviours from the pipipeconf.
195 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
196 piPipeconf.behaviours().forEach(b -> {
197 behaviours.put(b, piPipeconf.implementation(b).get());
198 });
199
200 Driver piPipeconfDriver = new DefaultDriver(completeDriverName, baseDriver.parents(),
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200201 baseDriver.manufacturer(), baseDriver.hwVersion(),
202 baseDriver.swVersion(), behaviours, new HashMap<>());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200203 //we take the base driver created with the behaviours of the PiPeconf and
204 // merge it with the base driver that was assigned to the device
205 Driver completeDriver = piPipeconfDriver.merge(baseDriver);
206
207 //This might lead to explosion of number of providers in the core,
208 // due to 1:1:1 pipeconf:driver:provider maybe find better way
209 DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
210
Andrea Campanella14e196d2017-07-24 18:11:36 +0200211 //we register to the driver susbystem the driver provider containing the merged driver
Andrea Campanellabc112a92017-06-26 19:06:43 +0200212 driverAdminService.registerProvider(provider);
213 }
214
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200215 // Changing the configuration for the device to enforce the full driver with pipipeconf
Andrea Campanella14e196d2017-07-24 18:11:36 +0200216 // and base behaviours, updating binding only first time something changes
217 NodeId leaderNodeId = leadershipService.getLeader("deploy-" +
218 deviceId.toString() + "-pipeconf");
219 NodeId localNodeId = clusterService.getLocalNode().id();
220
221 if (!basicDeviceConfig.driver().equals(completeDriverName) && localNodeId.equals(leaderNodeId)) {
222 ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
223 newCfg = newCfg.put(DRIVER, completeDriverName);
224 ObjectMapper mapper = new ObjectMapper();
225 JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
226 log.debug("New driver {} for device {}", completeDriverName, deviceId);
227 cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
228 // Completable future is needed for when this method will also apply the pipeline to the device.
229 // FIXME (maybe): the pipeline is currently applied by the general device provider.
230 // But we store here the association between device and pipeconf.
231 pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
232 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200233 operationResult.complete(true);
234 }
235 });
236 return operationResult;
237 }
238
239 @Override
240 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200241 return Optional.ofNullable(pipeconfMappingStore.getPipeconfId(deviceId));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200242 }
243
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200244
Andrea Campanellabc112a92017-06-26 19:06:43 +0200245 private class PiPipeconfDriverProviderInternal implements DriverProvider {
246
247 Driver driver;
248
249 PiPipeconfDriverProviderInternal(Driver driver) {
250 this.driver = driver;
251 }
252
253 @Override
254 public Set<Driver> getDrivers() {
255 return ImmutableSet.of(driver);
256 }
257 }
258
259 private void addPipeconfFromCfg(DeviceId deviceId) {
260 PiPipeconfConfig pipeconfConfig =
261 cfgService.getConfig(deviceId, PiPipeconfConfig.class);
262 PiPipeconfId id = pipeconfConfig.piPipeconfId();
263 if (id.id().equals("")) {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200264 log.debug("Ignoring empty pipeconf ID for device {}", deviceId);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200265 } else {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200266 pipeconfMappingStore.createOrUpdateBinding(deviceId, id);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200267 }
268 }
269
270 /**
271 * Listener for configuration events.
272 */
273 private class InternalNetworkConfigListener implements NetworkConfigListener {
274
275
276 @Override
277 public void event(NetworkConfigEvent event) {
278 DeviceId deviceId = (DeviceId) event.subject();
279 addPipeconfFromCfg(deviceId);
280 }
281
282 @Override
283 public boolean isRelevant(NetworkConfigEvent event) {
284 return event.configClass().equals(PiPipeconfConfig.class) &&
285 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
286 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
287 }
288 }
289}