blob: d60e9b35e99917f7030e2512e322373441d51541 [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;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.config.ConfigFactory;
33import org.onosproject.net.config.NetworkConfigEvent;
34import org.onosproject.net.config.NetworkConfigListener;
35import org.onosproject.net.config.NetworkConfigRegistry;
36import org.onosproject.net.config.basics.BasicDeviceConfig;
37import org.onosproject.net.config.basics.SubjectFactories;
38import org.onosproject.net.driver.Behaviour;
39import org.onosproject.net.driver.DefaultDriver;
40import org.onosproject.net.driver.Driver;
41import org.onosproject.net.driver.DriverAdminService;
42import org.onosproject.net.driver.DriverProvider;
43import org.onosproject.net.driver.DriverService;
44import org.onosproject.net.pi.model.PiPipeconf;
45import org.onosproject.net.pi.model.PiPipeconfId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080046import org.onosproject.net.pi.service.PiPipeconfConfig;
47import org.onosproject.net.pi.service.PiPipeconfMappingStore;
48import org.onosproject.net.pi.service.PiPipeconfService;
Andrea Campanellabc112a92017-06-26 19:06:43 +020049import org.slf4j.Logger;
50
51import java.util.HashMap;
52import java.util.Map;
53import java.util.Optional;
54import java.util.Set;
55import java.util.concurrent.CompletableFuture;
56import java.util.concurrent.ConcurrentHashMap;
57import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
59
Carmelo Cascone44daf562017-07-16 23:55:08 -040060import static java.lang.String.format;
Andrea Campanellabc112a92017-06-26 19:06:43 +020061import static org.onlab.util.Tools.groupedThreads;
62import static org.slf4j.LoggerFactory.getLogger;
63
64
65/**
66 * Implementation of the PiPipeconfService.
67 */
68@Component(immediate = true)
69@Service
Andrea Campanella48f99fa2017-07-13 19:06:21 +020070@Beta
71public class PiPipeconfManager implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020072
73 private final Logger log = getLogger(getClass());
74
75 private static final String DRIVER = "driver";
76 private static final String CFG_SCHEME = "piPipeconf";
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected NetworkConfigRegistry cfgService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected DriverService driverService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected DriverAdminService driverAdminService;
86
Andrea Campanellaf9c409a2017-07-13 14:14:41 +020087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected PiPipeconfMappingStore pipeconfMappingStore;
89
90 // Registered pipeconf are replicated through the app subsystem and registered on app activated events.
Andrea Campanellabc112a92017-06-26 19:06:43 +020091 protected ConcurrentHashMap<PiPipeconfId, PiPipeconf> piPipeconfs = new ConcurrentHashMap<>();
Andrea Campanellabc112a92017-06-26 19:06:43 +020092
93 protected ExecutorService executor =
94 Executors.newFixedThreadPool(5, groupedThreads("onos/pipipeconfservice",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +020095 "pipeline-to-device-%d", log));
Andrea Campanellabc112a92017-06-26 19:06:43 +020096
97 protected final ConfigFactory factory =
98 new ConfigFactory<DeviceId, PiPipeconfConfig>(
99 SubjectFactories.DEVICE_SUBJECT_FACTORY,
100 PiPipeconfConfig.class, CFG_SCHEME) {
101 @Override
102 public PiPipeconfConfig createConfig() {
103 return new PiPipeconfConfig();
104 }
105 };
106
107 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
108
109 @Activate
110 public void activate() {
111 cfgService.registerConfigFactory(factory);
112 cfgService.addListener(cfgListener);
113 cfgService.getSubjects(DeviceId.class, PiPipeconfConfig.class)
114 .forEach(this::addPipeconfFromCfg);
115 log.info("Started");
116 }
117
118
119 @Deactivate
120 public void deactivate() {
121 executor.shutdown();
122 cfgService.removeListener(cfgListener);
123 cfgService.unregisterConfigFactory(factory);
124 piPipeconfs.clear();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200125 cfgService = null;
126 driverAdminService = null;
127 driverService = null;
128 log.info("Stopped");
129 }
130
131 @Override
132 public void register(PiPipeconf pipeconf) throws IllegalStateException {
133 log.warn("Currently using local maps, needs to be moved to a distributed store");
Carmelo Cascone44daf562017-07-16 23:55:08 -0400134 if (piPipeconfs.containsKey(pipeconf.id())) {
135 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
136 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200137 piPipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone44daf562017-07-16 23:55:08 -0400138 log.info("New pipeconf registered: {}", pipeconf.id());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200139 }
140
141 @Override
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200142 public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200143 //TODO add mechanism to remove from device.
144 if (!piPipeconfs.containsKey(pipeconfId)) {
145 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
146 }
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200147 // TODO remove the binding from the distributed Store when the lifecycle of a pipeconf is defined.
148 // pipeconfMappingStore.removeBindings(pipeconfId);
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200149 piPipeconfs.remove(pipeconfId);
150 }
151
152 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200153 public Iterable<PiPipeconf> getPipeconfs() {
Andrea Campanella48f99fa2017-07-13 19:06:21 +0200154 return piPipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200155 }
156
157 @Override
158 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
159 return Optional.ofNullable(piPipeconfs.get(id));
160 }
161
162 @Override
163 public CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
164 CompletableFuture<Boolean> operationResult = new CompletableFuture<>();
165
166 executor.execute(() -> {
167 BasicDeviceConfig basicDeviceConfig =
168 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
169 Driver baseDriver = driverService.getDriver(basicDeviceConfig.driver());
170
171 String completeDriverName = baseDriver.name() + ":" + pipeconfId;
172 PiPipeconf piPipeconf = piPipeconfs.get(pipeconfId);
173 if (piPipeconf == null) {
174 log.warn("Pipeconf {} is not present", pipeconfId);
175 operationResult.complete(false);
176 } else {
177 //if driver exists already we don't create a new one.
178 //needs to be done via exception catching due to DriverRegistry throwing it on a null return from
179 //the driver map.
180 try {
181 driverService.getDriver(completeDriverName);
182 } catch (ItemNotFoundException e) {
183
184 log.debug("First time pipeconf {} is used with base driver {}, merging the two",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200185 pipeconfId, baseDriver);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200186 //extract the behaviours from the pipipeconf.
187 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
188 piPipeconf.behaviours().forEach(b -> {
189 behaviours.put(b, piPipeconf.implementation(b).get());
190 });
191
192 Driver piPipeconfDriver = new DefaultDriver(completeDriverName, baseDriver.parents(),
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200193 baseDriver.manufacturer(), baseDriver.hwVersion(),
194 baseDriver.swVersion(), behaviours, new HashMap<>());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200195 //we take the base driver created with the behaviours of the PiPeconf and
196 // merge it with the base driver that was assigned to the device
197 Driver completeDriver = piPipeconfDriver.merge(baseDriver);
198
199 //This might lead to explosion of number of providers in the core,
200 // due to 1:1:1 pipeconf:driver:provider maybe find better way
201 DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
202
203 //we register to the dirver susbystem the driver provider containing the merged driver
204 driverAdminService.registerProvider(provider);
205 }
206
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200207 // Changing the configuration for the device to enforce the full driver with pipipeconf
Andrea Campanellabc112a92017-06-26 19:06:43 +0200208 // and base behaviours
209 ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
210 newCfg = newCfg.put(DRIVER, completeDriverName);
211 ObjectMapper mapper = new ObjectMapper();
212 JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
213 cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200214 // Completable future is needed for when this method will also apply the pipeline to the device.
215 // FIXME (maybe): the pipeline is currently applied by the general device provider. But we store here
216 // the association between device and pipeconf.
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200217 pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200218 operationResult.complete(true);
219 }
220 });
221 return operationResult;
222 }
223
224 @Override
225 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200226 return Optional.ofNullable(pipeconfMappingStore.getPipeconfId(deviceId));
Andrea Campanellabc112a92017-06-26 19:06:43 +0200227 }
228
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200229
Andrea Campanellabc112a92017-06-26 19:06:43 +0200230 private class PiPipeconfDriverProviderInternal implements DriverProvider {
231
232 Driver driver;
233
234 PiPipeconfDriverProviderInternal(Driver driver) {
235 this.driver = driver;
236 }
237
238 @Override
239 public Set<Driver> getDrivers() {
240 return ImmutableSet.of(driver);
241 }
242 }
243
244 private void addPipeconfFromCfg(DeviceId deviceId) {
245 PiPipeconfConfig pipeconfConfig =
246 cfgService.getConfig(deviceId, PiPipeconfConfig.class);
247 PiPipeconfId id = pipeconfConfig.piPipeconfId();
248 if (id.id().equals("")) {
249 log.warn("Not adding empty pipeconfId for device {}", deviceId);
250 } else {
Andrea Campanellaf9c409a2017-07-13 14:14:41 +0200251 pipeconfMappingStore.createOrUpdateBinding(deviceId, id);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200252 }
253 }
254
255 /**
256 * Listener for configuration events.
257 */
258 private class InternalNetworkConfigListener implements NetworkConfigListener {
259
260
261 @Override
262 public void event(NetworkConfigEvent event) {
263 DeviceId deviceId = (DeviceId) event.subject();
264 addPipeconfFromCfg(deviceId);
265 }
266
267 @Override
268 public boolean isRelevant(NetworkConfigEvent event) {
269 return event.configClass().equals(PiPipeconfConfig.class) &&
270 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
271 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
272 }
273 }
274}