blob: 7201364d3109c719afe275d4b02b0f6873d569a0 [file] [log] [blame]
Yuta HIGUCHI1d0d9cc2016-11-14 16:26:24 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Yuta HIGUCHI1d0d9cc2016-11-14 16:26:24 -08003 *
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 */
16package org.onosproject.net.intent.impl;
17
18import static java.util.concurrent.Executors.newSingleThreadExecutor;
19import static org.onlab.util.Tools.groupedThreads;
20import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
21import static org.slf4j.LoggerFactory.getLogger;
22
23import java.util.EnumSet;
24import java.util.List;
25import java.util.Map;
26import java.util.Map.Entry;
27import java.util.Optional;
28import java.util.Set;
29import java.util.concurrent.CompletableFuture;
30import java.util.concurrent.ExecutionException;
31import java.util.concurrent.ExecutorService;
32import java.util.concurrent.TimeUnit;
33
34import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
37import org.apache.felix.scr.annotations.Reference;
38import org.apache.felix.scr.annotations.ReferenceCardinality;
39import org.apache.felix.scr.annotations.Service;
40import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.MastershipRole;
43import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
44import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState;
45import org.onosproject.net.behaviour.protection.ProtectionConfig;
46import org.onosproject.net.behaviour.protection.ProtectionConfigBehaviour;
47import org.onosproject.net.config.ConfigFactory;
48import org.onosproject.net.config.NetworkConfigEvent;
49import org.onosproject.net.config.NetworkConfigListener;
50import org.onosproject.net.config.NetworkConfigRegistry;
51import org.onosproject.net.config.NetworkConfigService;
52import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.driver.DriverHandler;
54import org.onosproject.net.driver.DriverService;
55import org.slf4j.Logger;
56
57import com.google.common.collect.ImmutableList;
58import com.google.common.collect.ImmutableSet;
59
60// TODO In theory just @Component should be sufficient,
61// but won't work without @Service. Need investigation.
62/**
63 * Component to monitor {@link ProtectionConfig} changes.
64 */
65@Service(value = ProtectionConfigMonitor.class)
66@Component(immediate = true)
67public class ProtectionConfigMonitor {
68
69 private final Logger log = getLogger(getClass());
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected NetworkConfigService networkConfigService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected DeviceService deviceService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected DriverService driverService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected NetworkConfigRegistry cfgRegistry;
82
83 private final List<ConfigFactory<?, ?>> factories = ImmutableList.of(
84 new ConfigFactory<DeviceId, ProtectionConfig>(DEVICE_SUBJECT_FACTORY,
85 ProtectionConfig.class, ProtectionConfig.CONFIG_KEY) {
86 @Override
87 public ProtectionConfig createConfig() {
88 return new ProtectionConfig();
89 }
90 });
91
92
93 private final ProtectionConfigListener listener = new ProtectionConfigListener();
94
95 private ExecutorService worker;
96
97
98 @Activate
99 public void activate() {
100 worker = newSingleThreadExecutor(groupedThreads("onos/protection",
101 "installer",
102 log));
103 networkConfigService.addListener(listener);
104
105 factories.forEach(cfgRegistry::registerConfigFactory);
106
107 log.info("Started");
108 }
109
110 @Deactivate
111 public void deactivate() {
112 networkConfigService.removeListener(listener);
113
114 worker.shutdown();
115 try {
116 worker.awaitTermination(5, TimeUnit.SECONDS);
117 } catch (InterruptedException e) {
118 log.warn("Interrupted.", e);
119 Thread.currentThread().interrupt();
120 }
121 factories.forEach(cfgRegistry::unregisterConfigFactory);
122
123 log.info("Stopped");
124 }
125
126 /**
127 * Retrieves {@link ProtectionConfigBehaviour} for the Device.
128 *
129 * @param did {@link DeviceId} of the Device to fetch
130 * @return {@link ProtectionConfigBehaviour}
131 * or throws {@link UnsupportedOperationException} on error.
132 */
133 private ProtectionConfigBehaviour getBehaviour(DeviceId did) {
134 DriverHandler handler = driverService.createHandler(did);
135 if (!handler.hasBehaviour(ProtectionConfigBehaviour.class)) {
136 log.error("{} does not support protection", did);
137 throw new UnsupportedOperationException(did + " does not support protection");
138 }
139
140 return handler.behaviour(ProtectionConfigBehaviour.class);
141 }
142
143 /**
144 * Retrieves first virtual Port with specified fingerprint.
145 *
146 * @param behaviour to use to query the Device
147 * @param fingerprint to look for
148 * @return virtual Port {@link ConnectPoint} if found.
149 */
150 private Optional<ConnectPoint> findFirstVirtualPort(ProtectionConfigBehaviour behaviour,
151 String fingerprint) {
152
153 CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointState>>
154 states = behaviour.getProtectionEndpointStates();
155
156 Map<ConnectPoint, ProtectedTransportEndpointState> map;
157 try {
158 map = states.get();
159 } catch (InterruptedException e1) {
160 log.error("Interrupted.", e1);
161 Thread.currentThread().interrupt();
162 return Optional.empty();
163 } catch (ExecutionException e1) {
164 log.error("Exception caught.", e1);
165 return Optional.empty();
166 }
167
168 // TODO this is not clean, should add utility method to API?
169 return map.entrySet().stream()
170 .filter(e -> fingerprint.equals(e.getValue().description().fingerprint()))
171 .map(Entry::getKey)
172 .findFirst();
173 }
174
175 private void addProtection(DeviceId did, ProtectionConfig added) {
176 ProtectedTransportEndpointDescription description = added.asDescription();
177 log.info("adding protection {}-{}", did, description);
178
179 ProtectionConfigBehaviour behaviour = getBehaviour(did);
180
181
182 CompletableFuture<ConnectPoint> result;
183 result = behaviour.createProtectionEndpoint(description);
184 result.handle((vPort, e) -> {
185 if (vPort != null) {
186 log.info("Virtual Port {} created for {}", vPort, description);
187 log.debug("{}", deviceService.getPort(vPort));
188 } else {
189 log.error("Protection {} exceptionally failed.", added, e);
190 }
191 return vPort;
192 });
193 }
194
195 private void updateProtection(DeviceId did, ProtectionConfig before, ProtectionConfig after) {
196 ProtectedTransportEndpointDescription description = after.asDescription();
197 log.info("updating protection {}-{}", did, description);
198
199 ProtectionConfigBehaviour behaviour = getBehaviour(did);
200
201 Optional<ConnectPoint> existing = findFirstVirtualPort(behaviour, after.fingerprint());
202 if (!existing.isPresent()) {
203 log.warn("Update requested, but not found, falling back as add");
204 addProtection(did, after);
205 return;
206 }
207 ConnectPoint vPort = existing.get();
208 log.info("updating protection virtual Port {} : {}", vPort, description);
209 behaviour.updateProtectionEndpoint(vPort, description)
210 .handle((vPortNew, e) -> {
211 if (vPort != null) {
212 log.info("Virtual Port {} updated for {}", vPort, description);
213 log.debug("{}", deviceService.getPort(vPort));
214 } else {
215 log.error("Protection {} -> {} exceptionally failed.",
216 before, after, e);
217 }
218 return vPort;
219 });
220 }
221
222 private void removeProtection(DeviceId did, ProtectionConfig removed) {
223 ProtectedTransportEndpointDescription description = removed.asDescription();
224 log.info("removing protection {}-{}", did, description);
225
226 ProtectionConfigBehaviour behaviour = getBehaviour(did);
227
228 Optional<ConnectPoint> existing = findFirstVirtualPort(behaviour, removed.fingerprint());
229 if (!existing.isPresent()) {
230 log.warn("Remove requested, but not found, ignoring");
231 return;
232 }
233 ConnectPoint vPort = existing.get();
234
235 log.info("removing protection virtual port {} : {}", vPort, description);
236 behaviour.deleteProtectionEndpoint(vPort)
237 .handle((result, ex) -> {
238 if (ex != null) {
239 log.info("removed protection {} : {}", vPort, result);
240 } else {
241 log.warn("removed protection {} failed.", vPort, ex);
242 }
243 return result;
244 });
245 }
246
247 /**
248 * Listens for new {@link ProtectionConfig} to install/remove.
249 */
250 public class ProtectionConfigListener
251 implements NetworkConfigListener {
252
253 /**
254 * Relevant {@link NetworkConfigEvent} type.
255 */
256 private final Set<NetworkConfigEvent.Type> relevant
257 = ImmutableSet.copyOf(EnumSet.of(
258 NetworkConfigEvent.Type.CONFIG_ADDED,
259 NetworkConfigEvent.Type.CONFIG_UPDATED,
260 NetworkConfigEvent.Type.CONFIG_REMOVED));
261
262 @Override
263 public boolean isRelevant(NetworkConfigEvent event) {
264 return event.configClass() == ProtectionConfig.class &&
265 relevant.contains(event.type());
266 }
267
268 @Override
269 public void event(NetworkConfigEvent event) {
270 worker.execute(() -> processEvent(event));
271 }
272
273 /**
274 * Process {@link ProtectionConfig} add/update/remove event.
275 * <p>
276 * Note: will be executed in the worker thread.
277 *
278 * @param event {@link ProtectionConfig} add/update/remove event
279 */
280 protected void processEvent(NetworkConfigEvent event) {
281
282 final DeviceId did = (DeviceId) event.subject();
283 log.debug("{} to {}: {}", event.type(), did, event);
284
285 if (deviceService.getRole(did) != MastershipRole.MASTER) {
286 log.debug("Not the master, ignoring. {}", event);
287 return;
288 }
289
290 switch (event.type()) {
291 case CONFIG_ADDED:
292 addProtection(did,
293 (ProtectionConfig) event.config().get());
294 break;
295 case CONFIG_UPDATED:
296 updateProtection(did,
297 (ProtectionConfig) event.prevConfig().get(),
298 (ProtectionConfig) event.config().get());
299 break;
300 case CONFIG_REMOVED:
301 removeProtection(did,
302 (ProtectionConfig) event.prevConfig().get());
303 break;
304
305 case CONFIG_REGISTERED:
306 case CONFIG_UNREGISTERED:
307 default:
308 log.warn("Ignoring unexpected event: {}", event);
309 break;
310 }
311 }
312 }
313
314}