blob: c73139f0969b8f94395f0313c9097836f4141241 [file] [log] [blame]
Andrea Campanellabc112a92017-06-26 19:06:43 +02001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
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;
46import org.onosproject.net.pi.runtime.PiPipeconfConfig;
47import org.onosproject.net.pi.runtime.PiPipeconfService;
48import org.slf4j.Logger;
49
50import java.util.HashMap;
51import java.util.Map;
52import java.util.Optional;
53import java.util.Set;
54import java.util.concurrent.CompletableFuture;
55import java.util.concurrent.ConcurrentHashMap;
56import java.util.concurrent.ExecutorService;
57import java.util.concurrent.Executors;
58
Carmelo Cascone44daf562017-07-16 23:55:08 -040059import static java.lang.String.format;
Andrea Campanellabc112a92017-06-26 19:06:43 +020060import static org.onlab.util.Tools.groupedThreads;
61import static org.slf4j.LoggerFactory.getLogger;
62
63
64/**
65 * Implementation of the PiPipeconfService.
66 */
67@Component(immediate = true)
68@Service
Andrea Campanella48f99fa2017-07-13 19:06:21 +020069@Beta
70public class PiPipeconfManager implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020071
72 private final Logger log = getLogger(getClass());
73
74 private static final String DRIVER = "driver";
75 private static final String CFG_SCHEME = "piPipeconf";
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected NetworkConfigRegistry cfgService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected DriverService driverService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DriverAdminService driverAdminService;
85
86 //TODO move to replicated map
87 protected ConcurrentHashMap<PiPipeconfId, PiPipeconf> piPipeconfs = new ConcurrentHashMap<>();
88 //TODO move to replicated map
89 protected ConcurrentHashMap<DeviceId, PiPipeconfId> devicesToPipeconf = new ConcurrentHashMap<>();
90
91 protected ExecutorService executor =
92 Executors.newFixedThreadPool(5, groupedThreads("onos/pipipeconfservice",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +020093 "pipeline-to-device-%d", log));
Andrea Campanellabc112a92017-06-26 19:06:43 +020094
95 protected final ConfigFactory factory =
96 new ConfigFactory<DeviceId, PiPipeconfConfig>(
97 SubjectFactories.DEVICE_SUBJECT_FACTORY,
98 PiPipeconfConfig.class, CFG_SCHEME) {
99 @Override
100 public PiPipeconfConfig createConfig() {
101 return new PiPipeconfConfig();
102 }
103 };
104
105 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
106
107 @Activate
108 public void activate() {
109 cfgService.registerConfigFactory(factory);
110 cfgService.addListener(cfgListener);
111 cfgService.getSubjects(DeviceId.class, PiPipeconfConfig.class)
112 .forEach(this::addPipeconfFromCfg);
113 log.info("Started");
114 }
115
116
117 @Deactivate
118 public void deactivate() {
119 executor.shutdown();
120 cfgService.removeListener(cfgListener);
121 cfgService.unregisterConfigFactory(factory);
122 piPipeconfs.clear();
123 devicesToPipeconf.clear();
124 cfgService = null;
125 driverAdminService = null;
126 driverService = null;
127 log.info("Stopped");
128 }
129
130 @Override
131 public void register(PiPipeconf pipeconf) throws IllegalStateException {
132 log.warn("Currently using local maps, needs to be moved to a distributed store");
Carmelo Cascone44daf562017-07-16 23:55:08 -0400133 if (piPipeconfs.containsKey(pipeconf.id())) {
134 throw new IllegalStateException(format("Pipeconf %s is already registered", pipeconf.id()));
135 }
Andrea Campanellabc112a92017-06-26 19:06:43 +0200136 piPipeconfs.put(pipeconf.id(), pipeconf);
Carmelo Cascone44daf562017-07-16 23:55:08 -0400137 log.info("New pipeconf registered: {}", pipeconf.id());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200138 }
139
140 @Override
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200141 public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
142 //TODO move to the distributed mechanism
143 //TODO add mechanism to remove from device.
144 if (!piPipeconfs.containsKey(pipeconfId)) {
145 throw new IllegalStateException(format("Pipeconf %s is not registered", pipeconfId));
146 }
147 piPipeconfs.remove(pipeconfId);
148 }
149
150 @Override
Andrea Campanellabc112a92017-06-26 19:06:43 +0200151 public Iterable<PiPipeconf> getPipeconfs() {
Andrea Campanella48f99fa2017-07-13 19:06:21 +0200152 return piPipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200153 }
154
155 @Override
156 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
157 return Optional.ofNullable(piPipeconfs.get(id));
158 }
159
160 @Override
161 public CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
162 CompletableFuture<Boolean> operationResult = new CompletableFuture<>();
163
164 executor.execute(() -> {
165 BasicDeviceConfig basicDeviceConfig =
166 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
167 Driver baseDriver = driverService.getDriver(basicDeviceConfig.driver());
168
169 String completeDriverName = baseDriver.name() + ":" + pipeconfId;
170 PiPipeconf piPipeconf = piPipeconfs.get(pipeconfId);
171 if (piPipeconf == null) {
172 log.warn("Pipeconf {} is not present", pipeconfId);
173 operationResult.complete(false);
174 } else {
175 //if driver exists already we don't create a new one.
176 //needs to be done via exception catching due to DriverRegistry throwing it on a null return from
177 //the driver map.
178 try {
179 driverService.getDriver(completeDriverName);
180 } catch (ItemNotFoundException e) {
181
182 log.debug("First time pipeconf {} is used with base driver {}, merging the two",
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200183 pipeconfId, baseDriver);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200184 //extract the behaviours from the pipipeconf.
185 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
186 piPipeconf.behaviours().forEach(b -> {
187 behaviours.put(b, piPipeconf.implementation(b).get());
188 });
189
190 Driver piPipeconfDriver = new DefaultDriver(completeDriverName, baseDriver.parents(),
Andrea Campanellaa9b3c9b2017-07-21 14:03:15 +0200191 baseDriver.manufacturer(), baseDriver.hwVersion(),
192 baseDriver.swVersion(), behaviours, new HashMap<>());
Andrea Campanellabc112a92017-06-26 19:06:43 +0200193 //we take the base driver created with the behaviours of the PiPeconf and
194 // merge it with the base driver that was assigned to the device
195 Driver completeDriver = piPipeconfDriver.merge(baseDriver);
196
197 //This might lead to explosion of number of providers in the core,
198 // due to 1:1:1 pipeconf:driver:provider maybe find better way
199 DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
200
201 //we register to the dirver susbystem the driver provider containing the merged driver
202 driverAdminService.registerProvider(provider);
203 }
204
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200205 // Changing the configuration for the device to enforce the full driver with pipipeconf
Andrea Campanellabc112a92017-06-26 19:06:43 +0200206 // and base behaviours
207 ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
208 newCfg = newCfg.put(DRIVER, completeDriverName);
209 ObjectMapper mapper = new ObjectMapper();
210 JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
211 cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200212 // Completable future is needed for when this method will also apply the pipeline to the device.
213 // FIXME (maybe): the pipeline is currently applied by the general device provider. But we store here
214 // the association between device and pipeconf.
215 devicesToPipeconf.put(deviceId, pipeconfId);
Andrea Campanellabc112a92017-06-26 19:06:43 +0200216 operationResult.complete(true);
217 }
218 });
219 return operationResult;
220 }
221
222 @Override
223 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
224 return Optional.ofNullable(devicesToPipeconf.get(deviceId));
225 }
226
227 private class PiPipeconfDriverProviderInternal implements DriverProvider {
228
229 Driver driver;
230
231 PiPipeconfDriverProviderInternal(Driver driver) {
232 this.driver = driver;
233 }
234
235 @Override
236 public Set<Driver> getDrivers() {
237 return ImmutableSet.of(driver);
238 }
239 }
240
241 private void addPipeconfFromCfg(DeviceId deviceId) {
242 PiPipeconfConfig pipeconfConfig =
243 cfgService.getConfig(deviceId, PiPipeconfConfig.class);
244 PiPipeconfId id = pipeconfConfig.piPipeconfId();
245 if (id.id().equals("")) {
246 log.warn("Not adding empty pipeconfId for device {}", deviceId);
247 } else {
248 devicesToPipeconf.put(deviceId, pipeconfConfig.piPipeconfId());
249 }
250 }
251
252 /**
253 * Listener for configuration events.
254 */
255 private class InternalNetworkConfigListener implements NetworkConfigListener {
256
257
258 @Override
259 public void event(NetworkConfigEvent event) {
260 DeviceId deviceId = (DeviceId) event.subject();
261 addPipeconfFromCfg(deviceId);
262 }
263
264 @Override
265 public boolean isRelevant(NetworkConfigEvent event) {
266 return event.configClass().equals(PiPipeconfConfig.class) &&
267 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
268 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
269 }
270 }
271}