blob: 212cf1ced78174080ea90a1431282d4338a50e0c [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 {
142 log.warn("Currently using local maps, needs to be moved to a distributed store");
Carmelo Cascone44daf562017-07-16 23:55:08 -0400143 if (piPipeconfs.containsKey(pipeconf.id())) {
144 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
145 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200146 piPipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone44daf562017-07-16 23:55:08 -0400147 log.info("New pipeconf registered: {}", pipeconf.id());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200148 }
149
150 @Override
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200151 public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200152 //TODO add mechanism to remove from device.
153 if (!piPipeconfs.containsKey(pipeconfId)) {
154 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
155 }
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200156 // TODO remove the binding from the distributed Store when the lifecycle of a pipeconf is defined.
157 // pipeconfMappingStore.removeBindings(pipeconfId);
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200158 piPipeconfs.remove(pipeconfId);
159 }
160
161 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200162 public Iterable<PiPipeconf> getPipeconfs() {
Andrea Campanella48f99fa2017-07-13 19:06:21 +0200163 return piPipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200164 }
165
166 @Override
167 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
168 return Optional.ofNullable(piPipeconfs.get(id));
169 }
170
171 @Override
172 public CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
173 CompletableFuture<Boolean> operationResult = new CompletableFuture<>();
174
175 executor.execute(() -> {
176 BasicDeviceConfig basicDeviceConfig =
177 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
178 Driver baseDriver = driverService.getDriver(basicDeviceConfig.driver());
179
180 String completeDriverName = baseDriver.name() + ":" + pipeconfId;
181 PiPipeconf piPipeconf = piPipeconfs.get(pipeconfId);
182 if (piPipeconf == null) {
183 log.warn("Pipeconf {} is not present", pipeconfId);
184 operationResult.complete(false);
185 } else {
186 //if driver exists already we don't create a new one.
187 //needs to be done via exception catching due to DriverRegistry throwing it on a null return from
188 //the driver map.
189 try {
190 driverService.getDriver(completeDriverName);
191 } catch (ItemNotFoundException e) {
192
193 log.debug("First time pipeconf {} is used with base driver {}, merging the two",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200194 pipeconfId, baseDriver);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200195 //extract the behaviours from the pipipeconf.
196 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
197 piPipeconf.behaviours().forEach(b -> {
198 behaviours.put(b, piPipeconf.implementation(b).get());
199 });
200
201 Driver piPipeconfDriver = new DefaultDriver(completeDriverName, baseDriver.parents(),
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200202 baseDriver.manufacturer(), baseDriver.hwVersion(),
203 baseDriver.swVersion(), behaviours, new HashMap<>());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200204 //we take the base driver created with the behaviours of the PiPeconf and
205 // merge it with the base driver that was assigned to the device
206 Driver completeDriver = piPipeconfDriver.merge(baseDriver);
207
208 //This might lead to explosion of number of providers in the core,
209 // due to 1:1:1 pipeconf:driver:provider maybe find better way
210 DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
211
Andrea Campanella14e196d2017-07-24 18:11:36 +0200212 //we register to the driver susbystem the driver provider containing the merged driver
Andrea Campanellabc112a92017-06-26 19:06:43 +0200213 driverAdminService.registerProvider(provider);
214 }
215
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200216 // Changing the configuration for the device to enforce the full driver with pipipeconf
Andrea Campanella14e196d2017-07-24 18:11:36 +0200217 // and base behaviours, updating binding only first time something changes
218 NodeId leaderNodeId = leadershipService.getLeader("deploy-" +
219 deviceId.toString() + "-pipeconf");
220 NodeId localNodeId = clusterService.getLocalNode().id();
221
222 if (!basicDeviceConfig.driver().equals(completeDriverName) && localNodeId.equals(leaderNodeId)) {
223 ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
224 newCfg = newCfg.put(DRIVER, completeDriverName);
225 ObjectMapper mapper = new ObjectMapper();
226 JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
227 log.debug("New driver {} for device {}", completeDriverName, deviceId);
228 cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
229 // Completable future is needed for when this method will also apply the pipeline to the device.
230 // FIXME (maybe): the pipeline is currently applied by the general device provider.
231 // But we store here the association between device and pipeconf.
232 pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
233 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200234 operationResult.complete(true);
235 }
236 });
237 return operationResult;
238 }
239
240 @Override
241 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200242 return Optional.ofNullable(pipeconfMappingStore.getPipeconfId(deviceId));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200243 }
244
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200245
Andrea Campanellabc112a92017-06-26 19:06:43 +0200246 private class PiPipeconfDriverProviderInternal implements DriverProvider {
247
248 Driver driver;
249
250 PiPipeconfDriverProviderInternal(Driver driver) {
251 this.driver = driver;
252 }
253
254 @Override
255 public Set<Driver> getDrivers() {
256 return ImmutableSet.of(driver);
257 }
258 }
259
260 private void addPipeconfFromCfg(DeviceId deviceId) {
261 PiPipeconfConfig pipeconfConfig =
262 cfgService.getConfig(deviceId, PiPipeconfConfig.class);
263 PiPipeconfId id = pipeconfConfig.piPipeconfId();
264 if (id.id().equals("")) {
265 log.warn("Not adding empty pipeconfId for device {}", deviceId);
266 } else {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200267 pipeconfMappingStore.createOrUpdateBinding(deviceId, id);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200268 }
269 }
270
271 /**
272 * Listener for configuration events.
273 */
274 private class InternalNetworkConfigListener implements NetworkConfigListener {
275
276
277 @Override
278 public void event(NetworkConfigEvent event) {
279 DeviceId deviceId = (DeviceId) event.subject();
280 addPipeconfFromCfg(deviceId);
281 }
282
283 @Override
284 public boolean isRelevant(NetworkConfigEvent event) {
285 return event.configClass().equals(PiPipeconfConfig.class) &&
286 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
287 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
288 }
289 }
290}