blob: 85d3fc3b04cd008f754f1b7bfb8e6fdc79322666 [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
59import static org.onlab.util.Tools.groupedThreads;
60import static org.slf4j.LoggerFactory.getLogger;
61
62
63/**
64 * Implementation of the PiPipeconfService.
65 */
66@Component(immediate = true)
67@Service
Andrea Campanella48f99fa2017-07-13 19:06:21 +020068@Beta
69public class PiPipeconfManager implements PiPipeconfService {
Andrea Campanellabc112a92017-06-26 19:06:43 +020070
71 private final Logger log = getLogger(getClass());
72
73 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
85 //TODO move to replicated map
86 protected ConcurrentHashMap<PiPipeconfId, PiPipeconf> piPipeconfs = new ConcurrentHashMap<>();
87 //TODO move to replicated map
88 protected ConcurrentHashMap<DeviceId, PiPipeconfId> devicesToPipeconf = new ConcurrentHashMap<>();
89
90 protected ExecutorService executor =
91 Executors.newFixedThreadPool(5, groupedThreads("onos/pipipeconfservice",
92 "pipeline-to-device-%d", log));
93
94 protected final ConfigFactory factory =
95 new ConfigFactory<DeviceId, PiPipeconfConfig>(
96 SubjectFactories.DEVICE_SUBJECT_FACTORY,
97 PiPipeconfConfig.class, CFG_SCHEME) {
98 @Override
99 public PiPipeconfConfig createConfig() {
100 return new PiPipeconfConfig();
101 }
102 };
103
104 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
105
106 @Activate
107 public void activate() {
108 cfgService.registerConfigFactory(factory);
109 cfgService.addListener(cfgListener);
110 cfgService.getSubjects(DeviceId.class, PiPipeconfConfig.class)
111 .forEach(this::addPipeconfFromCfg);
112 log.info("Started");
113 }
114
115
116 @Deactivate
117 public void deactivate() {
118 executor.shutdown();
119 cfgService.removeListener(cfgListener);
120 cfgService.unregisterConfigFactory(factory);
121 piPipeconfs.clear();
122 devicesToPipeconf.clear();
123 cfgService = null;
124 driverAdminService = null;
125 driverService = null;
126 log.info("Stopped");
127 }
128
129 @Override
130 public void register(PiPipeconf pipeconf) throws IllegalStateException {
131 log.warn("Currently using local maps, needs to be moved to a distributed store");
132 piPipeconfs.put(pipeconf.id(), pipeconf);
133 }
134
135 @Override
136 public Iterable<PiPipeconf> getPipeconfs() {
Andrea Campanella48f99fa2017-07-13 19:06:21 +0200137 return piPipeconfs.values();
Andrea Campanellabc112a92017-06-26 19:06:43 +0200138 }
139
140 @Override
141 public Optional<PiPipeconf> getPipeconf(PiPipeconfId id) {
142 return Optional.ofNullable(piPipeconfs.get(id));
143 }
144
145 @Override
146 public CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId) {
147 CompletableFuture<Boolean> operationResult = new CompletableFuture<>();
148
149 executor.execute(() -> {
150 BasicDeviceConfig basicDeviceConfig =
151 cfgService.getConfig(deviceId, BasicDeviceConfig.class);
152 Driver baseDriver = driverService.getDriver(basicDeviceConfig.driver());
153
154 String completeDriverName = baseDriver.name() + ":" + pipeconfId;
155 PiPipeconf piPipeconf = piPipeconfs.get(pipeconfId);
156 if (piPipeconf == null) {
157 log.warn("Pipeconf {} is not present", pipeconfId);
158 operationResult.complete(false);
159 } else {
160 //if driver exists already we don't create a new one.
161 //needs to be done via exception catching due to DriverRegistry throwing it on a null return from
162 //the driver map.
163 try {
164 driverService.getDriver(completeDriverName);
165 } catch (ItemNotFoundException e) {
166
167 log.debug("First time pipeconf {} is used with base driver {}, merging the two",
168 pipeconfId, baseDriver);
169 //extract the behaviours from the pipipeconf.
170 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
171 piPipeconf.behaviours().forEach(b -> {
172 behaviours.put(b, piPipeconf.implementation(b).get());
173 });
174
175 Driver piPipeconfDriver = new DefaultDriver(completeDriverName, baseDriver.parents(),
176 baseDriver.manufacturer(), baseDriver.hwVersion(), baseDriver.swVersion(),
177 behaviours, new HashMap<>());
178 //we take the base driver created with the behaviours of the PiPeconf and
179 // merge it with the base driver that was assigned to the device
180 Driver completeDriver = piPipeconfDriver.merge(baseDriver);
181
182 //This might lead to explosion of number of providers in the core,
183 // due to 1:1:1 pipeconf:driver:provider maybe find better way
184 DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
185
186 //we register to the dirver susbystem the driver provider containing the merged driver
187 driverAdminService.registerProvider(provider);
188 }
189
190 //Changing the configuration for the device to enforce the full driver with pipipeconf
191 // and base behaviours
192 ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
193 newCfg = newCfg.put(DRIVER, completeDriverName);
194 ObjectMapper mapper = new ObjectMapper();
195 JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
196 cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
197 //Completable future is needed for when this method will also apply the pipeline to the device.
198 operationResult.complete(true);
199 }
200 });
201 return operationResult;
202 }
203
204 @Override
205 public Optional<PiPipeconfId> ofDevice(DeviceId deviceId) {
206 return Optional.ofNullable(devicesToPipeconf.get(deviceId));
207 }
208
209 private class PiPipeconfDriverProviderInternal implements DriverProvider {
210
211 Driver driver;
212
213 PiPipeconfDriverProviderInternal(Driver driver) {
214 this.driver = driver;
215 }
216
217 @Override
218 public Set<Driver> getDrivers() {
219 return ImmutableSet.of(driver);
220 }
221 }
222
223 private void addPipeconfFromCfg(DeviceId deviceId) {
224 PiPipeconfConfig pipeconfConfig =
225 cfgService.getConfig(deviceId, PiPipeconfConfig.class);
226 PiPipeconfId id = pipeconfConfig.piPipeconfId();
227 if (id.id().equals("")) {
228 log.warn("Not adding empty pipeconfId for device {}", deviceId);
229 } else {
230 devicesToPipeconf.put(deviceId, pipeconfConfig.piPipeconfId());
231 }
232 }
233
234 /**
235 * Listener for configuration events.
236 */
237 private class InternalNetworkConfigListener implements NetworkConfigListener {
238
239
240 @Override
241 public void event(NetworkConfigEvent event) {
242 DeviceId deviceId = (DeviceId) event.subject();
243 addPipeconfFromCfg(deviceId);
244 }
245
246 @Override
247 public boolean isRelevant(NetworkConfigEvent event) {
248 return event.configClass().equals(PiPipeconfConfig.class) &&
249 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
250 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
251 }
252 }
253}