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