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