blob: 19edcbc1fde6c153834b2348c305e62b8586eefb [file] [log] [blame]
Naoki Shiota5a056062016-05-05 18:43:59 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Naoki Shiota5a056062016-05-05 18:43:59 -07003 *
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.newoptical;
17
18import com.google.common.annotations.Beta;
19import com.google.common.collect.ImmutableList;
Naoki Shiota5a056062016-05-05 18:43:59 -070020import org.apache.commons.lang3.tuple.Pair;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.osgi.service.component.annotations.Activate;
22import org.osgi.service.component.annotations.Component;
23import org.osgi.service.component.annotations.Deactivate;
24import org.osgi.service.component.annotations.Modified;
25import org.osgi.service.component.annotations.Reference;
26import org.osgi.service.component.annotations.ReferenceCardinality;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070027import org.onlab.graph.DefaultEdgeWeigher;
28import org.onlab.graph.ScalarWeight;
29import org.onlab.graph.Weight;
Naoki Shiota5a056062016-05-05 18:43:59 -070030import org.onlab.util.Bandwidth;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070031import org.onlab.util.KryoNamespace;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070032import org.onlab.util.Tools;
Naoki Shiota5a056062016-05-05 18:43:59 -070033import org.onosproject.cluster.ClusterService;
Naoki Shiota5a056062016-05-05 18:43:59 -070034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Naoki Shiota5a056062016-05-05 18:43:59 -070036import org.onosproject.event.AbstractListenerManager;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070037import org.onosproject.event.ListenerTracker;
Naoki Shiota5a056062016-05-05 18:43:59 -070038import org.onosproject.mastership.MastershipService;
Naoki Shiota5a056062016-05-05 18:43:59 -070039import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.Link;
Yuta HIGUCHI21033042017-05-04 17:58:24 -070043import org.onosproject.net.LinkKey;
Naoki Shiota5a056062016-05-05 18:43:59 -070044import org.onosproject.net.Path;
45import org.onosproject.net.Port;
46import org.onosproject.net.config.NetworkConfigService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070047import org.onosproject.net.config.basics.BandwidthCapacity;
48import org.onosproject.net.config.basics.BasicLinkConfig;
Naoki Shiota5a056062016-05-05 18:43:59 -070049import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.intent.Intent;
51import org.onosproject.net.intent.IntentEvent;
Naoki Shiota5a056062016-05-05 18:43:59 -070052import org.onosproject.net.intent.IntentListener;
53import org.onosproject.net.intent.IntentService;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -070054import org.onosproject.net.intent.Key;
Naoki Shiota5a056062016-05-05 18:43:59 -070055import org.onosproject.net.intent.OpticalCircuitIntent;
56import org.onosproject.net.intent.OpticalConnectivityIntent;
Naoki Shiota5a056062016-05-05 18:43:59 -070057import org.onosproject.net.link.LinkEvent;
58import org.onosproject.net.link.LinkListener;
59import org.onosproject.net.link.LinkService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070060import org.onosproject.net.optical.OchPort;
61import org.onosproject.net.optical.OduCltPort;
Naoki Shiota5a056062016-05-05 18:43:59 -070062import org.onosproject.net.resource.ContinuousResource;
63import org.onosproject.net.resource.Resource;
64import org.onosproject.net.resource.ResourceAllocation;
65import org.onosproject.net.resource.ResourceService;
66import org.onosproject.net.resource.Resources;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070067import org.onosproject.net.topology.LinkWeigher;
Naoki Shiota5a056062016-05-05 18:43:59 -070068import org.onosproject.net.topology.TopologyEdge;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070069import org.onosproject.net.topology.TopologyService;
70import org.onosproject.net.topology.TopologyVertex;
71import org.onosproject.newoptical.api.OpticalConnectivityId;
72import org.onosproject.newoptical.api.OpticalPathEvent;
73import org.onosproject.newoptical.api.OpticalPathListener;
74import org.onosproject.newoptical.api.OpticalPathService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070075import org.onosproject.store.serializers.KryoNamespaces;
Naoki Shiota5a056062016-05-05 18:43:59 -070076import org.onosproject.store.service.AtomicCounter;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070077import org.onosproject.store.service.ConsistentMap;
78import org.onosproject.store.service.DistributedSet;
79import org.onosproject.store.service.MapEvent;
80import org.onosproject.store.service.MapEventListener;
81import org.onosproject.store.service.Serializer;
Naoki Shiota5a056062016-05-05 18:43:59 -070082import org.onosproject.store.service.StorageService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070083import org.onosproject.store.service.Versioned;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070084import org.osgi.service.component.ComponentContext;
Naoki Shiota5a056062016-05-05 18:43:59 -070085import org.slf4j.Logger;
86import org.slf4j.LoggerFactory;
87
88import java.time.Duration;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070089import java.util.ArrayList;
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -080090import java.util.Collection;
Naoki Shiota5a056062016-05-05 18:43:59 -070091import java.util.Collections;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070092import java.util.Dictionary;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070093import java.util.HashMap;
94import java.util.HashSet;
Naoki Shiota5a056062016-05-05 18:43:59 -070095import java.util.Iterator;
96import java.util.LinkedList;
97import java.util.List;
98import java.util.Map;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -070099import java.util.Map.Entry;
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700100import java.util.Objects;
Naoki Shiota5a056062016-05-05 18:43:59 -0700101import java.util.Optional;
102import java.util.Set;
Naoki Shiota5a056062016-05-05 18:43:59 -0700103import java.util.stream.Collectors;
104import java.util.stream.Stream;
105
106import static com.google.common.base.Preconditions.checkArgument;
107import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800108import static org.onosproject.net.LinkKey.linkKey;
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700109import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Naoki Shiota5a056062016-05-05 18:43:59 -0700110
111/**
112 * Main component to configure optical connectivity.
113 */
114@Beta
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115@Component(immediate = true, service = OpticalPathService.class)
Naoki Shiota5a056062016-05-05 18:43:59 -0700116public class OpticalPathProvisioner
117 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
118 implements OpticalPathService {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800119
Ray Milkey9c9cde42018-01-12 14:22:06 -0800120 private static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700121
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800122 /**
123 * Bandwidth representing no bandwidth requirement specified.
124 */
125 private static final Bandwidth NO_BW_REQUIREMENT = Bandwidth.bps(0);
126
Naoki Shiota5a056062016-05-05 18:43:59 -0700127 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700128 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
129 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
130 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700133 protected IntentService intentService;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700136 protected TopologyService topologyService;
Naoki Shiota5a056062016-05-05 18:43:59 -0700137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700139 protected CoreService coreService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700142 protected LinkService linkService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700145 protected MastershipService mastershipService;
146
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700148 protected ClusterService clusterService;
149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700151 protected DeviceService deviceService;
152
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700154 protected StorageService storageService;
155
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700157 protected NetworkConfigService networkConfigService;
158
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700160 protected ResourceService resourceService;
161
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700162 private static final String MAX_PATHS = "maxPaths";
163 private static final int DEFAULT_MAX_PATHS = 10;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 //@Property(name = MAX_PATHS, intValue = DEFAULT_MAX_PATHS,
165 // label = "Maximum number of paths to consider for path provisioning")
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700166 private int maxPaths = DEFAULT_MAX_PATHS;
Naoki Shiota5a056062016-05-05 18:43:59 -0700167
168 private ApplicationId appId;
169
170 private AtomicCounter idCounter;
171
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700172 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700173
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700174 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700175
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800176 /**
177 * Map from packet-layer link expected to be realized by some optical Intent to
178 * OpticalConnectivity (~=top level intent over multi-layer topology).
179 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700180 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700181
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700182 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
183
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800184 // FIXME in the long run. This is effectively app's own resource subsystem
185 /**
186 * Set of cross connect link currently used.
187 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700188 private DistributedSet<Link> usedCrossConnectLinkSet;
189
190 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
191 .register(KryoNamespaces.API)
192 .register(PacketLinkRealizedByOptical.class)
193 .register(OpticalConnectivityId.class)
194 .register(OpticalConnectivity.class);
195
196 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
197 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700198 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700199 .register(OpticalConnectivityId.class)
200 .register(OpticalConnectivity.class);
201
202 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
203 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700204
205 @Activate
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700206 protected void activate(ComponentContext context) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700207 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700208 appId = coreService.registerApplication("org.onosproject.newoptical");
209
Naoki Shiota03c29e12016-05-16 16:58:07 -0700210 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700211
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700212 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
213 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
214 .withName(LINKPATH_MAP_NAME)
215 .withApplicationId(appId)
216 .build();
217
218 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
219 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
220 .withName(CONNECTIVITY_MAP_NAME)
221 .withApplicationId(appId)
222 .build();
223
224 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
225 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
226 .withName(CROSSCONNECTLINK_SET_NAME)
227 .withApplicationId(appId)
228 .build()
229 .asDistributedSet();
230
Naoki Shiota5a056062016-05-05 18:43:59 -0700231 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700232
233 listeners = new ListenerTracker();
234 listeners.addListener(linkService, new InternalLinkListener())
235 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700236
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700237 linkPathMap.addListener(storeListener);
238
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700239 readComponentConfiguration(context);
240
Naoki Shiota5a056062016-05-05 18:43:59 -0700241 log.info("Started");
242 }
243
244 @Deactivate
245 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700246 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700247 listeners.removeListeners();
248 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700249
250 log.info("Stopped");
251 }
252
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700253 @Modified
254 public void modified(ComponentContext context) {
255 readComponentConfiguration(context);
256 }
257
258 /**
259 * Extracts properties from the component configuration context.
260 *
261 * @param context the component context
262 */
263 private void readComponentConfiguration(ComponentContext context) {
264 Dictionary<?, ?> properties = context.getProperties();
265 maxPaths = Tools.getIntegerProperty(properties, MAX_PATHS, DEFAULT_MAX_PATHS);
266 log.info("Configured. Maximum paths to consider is configured to {}", maxPaths);
267 }
268
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800269 @Override
270 public Collection<OpticalConnectivity> listConnectivity() {
271 return connectivityMap.values().stream()
272 .map(Versioned::value)
Yuta HIGUCHI498fa1d2017-05-17 16:08:40 -0700273 .collect(ImmutableList.toImmutableList());
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800274 }
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700275
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700276 @Override
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700277 public Set<Key> listIntents(OpticalConnectivityId id) {
278 return linkPathMap.entrySet().stream()
279 .filter(ent -> id.equals(ent.getValue().value().id()))
280 .map(Entry::getKey)
281 .map(PacketLinkRealizedByOptical::realizingIntentKey)
282 .collect(Collectors.toSet());
283 }
284
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800285 /*
286 * Request packet-layer connectivity between specified ports,
287 * over packet-optical multi-layer infrastructure.
288 *
289 * Functionality-wise this is effectively submitting Packet-Optical
290 * multi-layer P2P Intent.
291 *
292 * It computes multi-layer path meeting specified constraint,
293 * and calls setupPath.
294 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700295 @Override
296 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
297 Bandwidth bandwidth, Duration latency) {
298 checkNotNull(ingress);
299 checkNotNull(egress);
300 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
301
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700302 Bandwidth bw = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
Naoki Shiota5a056062016-05-05 18:43:59 -0700303
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700304 Stream<Path> paths = topologyService.getKShortestPaths(
305 topologyService.currentTopology(),
306 ingress.deviceId(), egress.deviceId(),
Naoki Shiota5a056062016-05-05 18:43:59 -0700307 new BandwidthLinkWeight(bandwidth));
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700308
309 // Path service calculates from node to node, we're only interested in port to port
310 Optional<OpticalConnectivityId> id =
311 paths.filter(p -> p.src().equals(ingress) && p.dst().equals(egress))
312 .limit(maxPaths)
313 .map(p -> setupPath(p, bw, latency))
314 .filter(Objects::nonNull)
315 .findFirst();
316
317 if (id.isPresent()) {
318 log.info("Assigned OpticalConnectivityId: {}", id);
319 } else {
320 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700321 }
322
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700323 return id.orElse(null);
Naoki Shiota5a056062016-05-05 18:43:59 -0700324 }
325
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800326 /*
327 * Given a multi-layer path,
328 * compute a set of segments which requires
329 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
330 * to provide packet-layer connectivity.
331 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700332 @Override
333 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
334 checkNotNull(path);
Yuta HIGUCHI8fc32f82017-03-14 22:12:42 -0700335 log.debug("setupPath({}, {}, {})", path, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700336
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700337 // map of cross connect points (optical port -> packet port)
338 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
339
340 // list of (src, dst) pair of optical ports between which optical path should be installed
341 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
342
343 // Scan path to find pairs of connect points between which optical intent is installed
344 // opticalSrcPort works as a flag parameter to show scanning status
345 ConnectPoint opticalSrcPort = null;
346 for (Link link : path.links()) {
347 if (!isCrossConnectLink(link)) {
348 continue;
349 }
350
351 if (opticalSrcPort != null) {
352 // opticalSrcPort!=null means src port was already found
353 // in this case link.src() is optical layer, and link.dst() is packet layer
354
355 // Check if types of src port and dst port matches
356 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
357 "Unknown device ID");
358 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
359 "Unknown device ID");
360 if (srcDevice.type() != dstDevice.type()) {
361 log.error("Unsupported mix of cross connect points : {}, {}",
362 srcDevice.type(), dstDevice.type());
363 return null;
364 }
365
366 // Update cross connect points map
367 crossConnectPointMap.put(link.src(), link.dst());
368
369 // Add optical ports pair to list
370 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
371
372 // Reset flag parameter
373 opticalSrcPort = null;
374 } else {
375 // opticalSrcPort==null means src port was not found yet
376 // in this case link.src() is packet layer, and link.dst() is optical layer
377
378 // Update cross connect points map
379 crossConnectPointMap.put(link.dst(), link.src());
380 // Set opticalSrcPort to src of link (optical port)
381 opticalSrcPort = link.dst();
382 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700383 }
384
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700385 // create intents from cross connect points
386 List<Intent> intents = createIntents(crossConnectPoints);
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800387 if (intents.isEmpty()) {
388 log.error("No intents produced from {}", crossConnectPoints);
389 return null;
390 }
391
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700392 // create set of PacketLinkRealizedByOptical
393 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
394 intents, crossConnectPointMap);
395
396 // create OpticalConnectivity object and store information to distributed store
397 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700398
399 // store cross connect port usage
400 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700401 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700402
403 // Submit the intents
404 for (Intent i : intents) {
405 intentService.submit(i);
406 log.debug("Submitted an intent: {}", i);
407 }
408
409 return connectivity.id();
410 }
411
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700412 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
413 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700414 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700415 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
416 links, Collections.emptySet());
417
418 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700419
Naoki Shiota5a056062016-05-05 18:43:59 -0700420 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700421 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700422
423 return connectivity;
424 }
425
426 @Override
427 public boolean removeConnectivity(OpticalConnectivityId id) {
428 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700429 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700430
431 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700432 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700433 return false;
434 }
435
436 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700437 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700438 Intent intent = intentService.getIntent(l.realizingIntentKey());
439 intentService.withdraw(intent);
440 });
441
442 return true;
443 }
444
445 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700446 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700447 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700448 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700449 log.info("OpticalConnectivity with id {} not found.", id);
450 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700451 }
452
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700453 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700454 }
455
456 /**
457 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700458 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700459 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700460 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700461 * @return list of optical connectivity intents
462 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700463 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700464 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700465 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700466
467 while (itr.hasNext()) {
468 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700469 Pair<ConnectPoint, ConnectPoint> next = itr.next();
470 ConnectPoint src = next.getLeft();
471 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700472
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700473 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
474 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700475
476 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700477 OduCltPort srcOCPort = (OduCltPort) srcPort;
478 OduCltPort dstOCPort = (OduCltPort) dstPort;
479 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
480 continue;
481 }
482
Naoki Shiota5a056062016-05-05 18:43:59 -0700483 // Create OTN circuit
484 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
485 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700486 .src(src)
487 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700488 .signalType(srcOCPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700489 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700490 .build();
491 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700492 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700493 OchPort srcOchPort = (OchPort) srcPort;
494 OchPort dstOchPort = (OchPort) dstPort;
495 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
496 continue;
497 }
498
Naoki Shiota5a056062016-05-05 18:43:59 -0700499 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700500 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
501 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700502 .src(src)
503 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700504 .signalType(srcOchPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700505 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700506 .build();
507 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700508 } else {
509 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
510 return Collections.emptyList();
511 }
512 }
513
514 return intents;
515 }
516
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700517 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
518 List<Intent> intents,
519 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
520 checkArgument(connectPoints.size() == intents.size());
521
522 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
523
524 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
525 Iterator<Intent> intentItr = intents.iterator();
526 while (xcPointsItr.hasNext()) {
527 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
528 Intent intent = intentItr.next();
529
530 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
531 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
532
533 if (intent instanceof OpticalConnectivityIntent) {
534 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
535 (OpticalConnectivityIntent) intent));
536 } else if (intent instanceof OpticalCircuitIntent) {
537 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
538 (OpticalCircuitIntent) intent));
539 } else {
540 log.warn("Unexpected intent type: {}", intent.getClass());
541 }
542 }
543
544 return pLinks;
545 }
546
Naoki Shiota5a056062016-05-05 18:43:59 -0700547 /**
548 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
549 *
550 * @param type device type
551 * @return true if in packet layer, false otherwise
552 */
553 private boolean isPacketLayer(Device.Type type) {
554 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
555 }
556
557 /**
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800558 * Verifies if given device type is NOT in packet layer, i.e., switch or router device.
Naoki Shiota5a056062016-05-05 18:43:59 -0700559 *
560 * @param type device type
561 * @return true if in packet layer, false otherwise
562 */
563 private boolean isTransportLayer(Device.Type type) {
564 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
565 }
566
567 /**
568 * Verifies if given link forms a cross-connection between packet and optical layer.
569 *
570 * @param link the link
571 * @return true if the link is a cross-connect link, false otherwise
572 */
573 private boolean isCrossConnectLink(Link link) {
574 if (link.type() != Link.Type.OPTICAL) {
575 return false;
576 }
577
578 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
579 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
580
581 return src != dst &&
582 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
583 }
584
585 /**
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800586 * Updates bandwidth resource of given connect point to specified value.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700587 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700588 * @param cp Connect point
589 * @param bandwidth New bandwidth
590 */
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800591 private void setPortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700592 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
593 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
594 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700595 }
596
597 /**
598 * Updates usage information of bandwidth based on connectivity which is established.
599 * @param connectivity Optical connectivity
600 */
601 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800602 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
603 // no bandwidth requirement, nothing to allocate.
604 return;
605 }
606
Naoki Shiotacb744db2016-05-13 19:32:35 -0700607 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700608
609 List<Link> links = connectivity.links();
610
611 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
612 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
613 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
614 Bandwidth.class).resource(connectivity.bandwidth().bps()))
615 .collect(Collectors.toList());
616
Naoki Shiotacb744db2016-05-13 19:32:35 -0700617 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
618 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700619 if (allocations.isEmpty()) {
620 log.warn("Failed to allocate bandwidth {} to {}",
621 connectivity.bandwidth().bps(), resources);
622 // TODO any recovery?
623 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700624 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700625 }
626
627 /**
628 * Release bandwidth allocated by given connectivity.
629 * @param connectivity Optical connectivity
630 */
631 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700632 if (connectivity.links().isEmpty()) {
633 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700634 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800635 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
636 // no bandwidth requirement, nothing to release.
637 return;
638 }
639
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700640
641 // release resource only if this node is the master for link head device
642 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
643 OpticalConnectivityId connectivityId = connectivity.id();
644
645 log.debug("releasing bandwidth allocated to {}", connectivityId);
646 if (!resourceService.release(connectivityId)) {
647 log.warn("Failed to release bandwidth allocated to {}",
648 connectivityId);
649 // TODO any recovery?
650 }
651 log.debug("DONE releasing bandwidth for {}", connectivityId);
652 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700653 }
654
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800655 private boolean linkDiscoveryEnabled(ConnectPoint cp) {
656 // FIXME should check Device feature and configuration state.
657
658 // short-term hack for ONS'17 time-frame,
659 // only expect OF device to have link discovery.
Jon Halla3fcf672017-03-28 16:53:22 -0700660 return "of".equals(cp.deviceId().uri().getScheme());
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800661 }
662
663 /**
664 * Returns true if both connect point support for link discovery & enabled.
665 *
666 * @param cp1 port 1
667 * @param cp2 port 2
668 * @return true if both connect point support for link discovery & enabled.
669 */
670 private boolean linkDiscoveryEnabled(ConnectPoint cp1, ConnectPoint cp2) {
671 return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
672 }
673
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700674 private class BandwidthLinkWeight extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge> implements LinkWeigher {
Naoki Shiota5a056062016-05-05 18:43:59 -0700675 private Bandwidth bandwidth = null;
676
677 public BandwidthLinkWeight(Bandwidth bandwidth) {
678 this.bandwidth = bandwidth;
679 }
680
681 @Override
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700682 public Weight weight(TopologyEdge edge) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700683 Link l = edge.link();
684
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700685 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700686 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800687 log.trace("{} is not active", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700688 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700689 }
690
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700691 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700692 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800693 log.trace("Cross connect {} in use", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700694 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700695 }
696
697 // Check availability of bandwidth
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800698 if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700699 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700700 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700701 } else {
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700702 log.trace("Not enough bandwidth on {}", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700703 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700704 }
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700705 // Allow everything else
Naoki Shiota5a056062016-05-05 18:43:59 -0700706 } else {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700707 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700708 }
709 }
710
711 private boolean hasEnoughBandwidth(ConnectPoint cp) {
712 if (cp.elementId() instanceof DeviceId) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700713 Device device = deviceService.getDevice(cp.deviceId());
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700714 Device.Type type = device.type();
715
Naoki Shiota5a056062016-05-05 18:43:59 -0700716 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700717 // Check if the port has enough capacity
718 Port port = deviceService.getPort(cp.deviceId(), cp.port());
719 if (port instanceof OduCltPort || port instanceof OchPort) {
720 // Port with capacity
721 return bandwidth.bps() < port.portSpeed() * 1000000.0;
722 } else {
723 // Port without valid capacity (OMS port, etc.)
724 return true;
725 }
726 } else {
727 // Check if enough amount of bandwidth resource remains
728 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
729 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800730 try {
731 return resourceService.isAvailable(resource);
732 } catch (Exception e) {
733 log.error("Resource service failed checking availability of {}",
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700734 resource, e);
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800735 throw e;
736 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700737 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700738 }
739 return false;
740 }
741 }
742
Naoki Shiota5a056062016-05-05 18:43:59 -0700743 public class InternalIntentListener implements IntentListener {
744 @Override
745 public void event(IntentEvent event) {
746 switch (event.type()) {
747 case INSTALLED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700748 log.debug("Intent {} installed.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700749 updateCrossConnectLink(event.subject());
750 break;
751 case WITHDRAWN:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700752 log.debug("Intent {} withdrawn.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700753 removeCrossConnectLinks(event.subject());
754 break;
755 case FAILED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700756 log.debug("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800757 // TODO If it was one of it's own optical Intent,
758 // update link state
759 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700760 break;
761 default:
762 break;
763 }
764 }
765
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800766 // TODO rename "CrossConnectLink"?
767 /**
768 * Update packet-layer link/port state once Intent is installed.
769 *
770 * @param intent which reached installed state
771 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700772 private void updateCrossConnectLink(Intent intent) {
773 linkPathMap.entrySet().stream()
774 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
775 .forEach(e -> {
776 ConnectPoint packetSrc = e.getKey().src();
777 ConnectPoint packetDst = e.getKey().dst();
778 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700779
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700780 // reflect modification only if packetSrc is local_
781 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
782 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800783 setPortBandwidth(packetSrc, bw);
784 setPortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700785
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700786 // Updates link status in distributed map
787 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
788 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800789
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800790
791 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
792 injectLink(packetSrc, packetDst);
793 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700794 }
795 });
796 }
797
798 private void removeCrossConnectLinks(Intent intent) {
799 ConnectPoint src, dst;
800
801 if (intent instanceof OpticalCircuitIntent) {
802 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
803 src = circuit.getSrc();
804 dst = circuit.getDst();
805 } else if (intent instanceof OpticalConnectivityIntent) {
806 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
807 src = conn.getSrc();
808 dst = conn.getDst();
809 } else {
810 return;
811 }
812
813 removeXcLinkUsage(src);
814 removeXcLinkUsage(dst);
815
816 // Set bandwidth of 0 to cross connect ports
817 Bandwidth bw = Bandwidth.bps(0);
818 linkPathMap.entrySet().stream()
819 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
820 .forEach(e -> {
821 ConnectPoint packetSrc = e.getKey().src();
822 ConnectPoint packetDst = e.getKey().dst();
Naoki Shiota5a056062016-05-05 18:43:59 -0700823
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700824 // reflect modification only if packetSrc is local_
825 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
826 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800827 setPortBandwidth(packetSrc, bw);
828 setPortBandwidth(packetDst, bw);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700829
830 // Updates link status in distributed map
831 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
832 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800833
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800834
835 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
836 removeInjectedLink(packetSrc, packetDst);
837 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700838 }
839 });
840 }
841
842 private void removeXcLinkUsage(ConnectPoint cp) {
843 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700844 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700845 .findAny();
846
847 if (!link.isPresent()) {
Yuta HIGUCHIe2689ee2017-05-04 16:53:18 -0700848 log.warn("Cross connect point {} has no cross connect link to release.", cp);
Naoki Shiota5a056062016-05-05 18:43:59 -0700849 return;
850 }
851
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700852 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700853 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800854
855 /**
856 * Injects link between specified packet port.
857 *
858 * @param packetSrc port 1
859 * @param packetDst port 2
860 */
861 private void injectLink(ConnectPoint packetSrc,
862 ConnectPoint packetDst) {
863 // inject expected link or durable link
864 // if packet device cannot advertise packet link
865 try {
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700866 // cannot call addConfig.
867 // it will create default BasicLinkConfig,
868 // which will end up advertising DIRECT links and
869 // DIRECT Link type cannot transition from DIRECT to INDIRECT
870 LinkKey lnkKey = linkKey(packetSrc, packetDst);
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800871 BasicLinkConfig lnkCfg = networkConfigService
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700872 .getConfig(lnkKey, BasicLinkConfig.class);
873 if (lnkCfg == null) {
874 lnkCfg = new BasicLinkConfig(lnkKey);
875 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800876 lnkCfg.isAllowed(true);
877 lnkCfg.isDurable(true);
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700878 lnkCfg.type(Link.Type.INDIRECT);
879 lnkCfg.isBidirectional(false);
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700880 // cannot call apply against manually created instance
881 //lnkCfg.apply();
882 networkConfigService.applyConfig(lnkKey,
883 BasicLinkConfig.class,
884 lnkCfg.node());
885
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800886 } catch (Exception ex) {
887 log.error("Applying BasicLinkConfig failed", ex);
888 }
889 }
890
891 /**
892 * Removes link injected between specified packet port.
893 *
894 * @param packetSrc port 1
895 * @param packetDst port 2
896 */
897 private void removeInjectedLink(ConnectPoint packetSrc,
898 ConnectPoint packetDst) {
899 // remove expected link or durable link
900 // if packet device cannot monitor packet link
901
902 try {
903 // hack to mark link off-line
904 BasicLinkConfig lnkCfg = networkConfigService
905 .getConfig(linkKey(packetSrc, packetDst),
906 BasicLinkConfig.class);
907 lnkCfg.isAllowed(false);
908 lnkCfg.apply();
909 } catch (Exception ex) {
910 log.error("Applying BasicLinkConfig failed", ex);
911 }
912
913 networkConfigService
914 .removeConfig(linkKey(packetSrc, packetDst),
915 BasicLinkConfig.class);
916 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700917 }
918
919
920 private class InternalLinkListener implements LinkListener {
921
922 @Override
923 public void event(LinkEvent event) {
924 switch (event.type()) {
925 case LINK_REMOVED:
926 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700927 // updates linkPathMap only if src device of link is local
928 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
929 return;
930 }
931
932 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700933 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
934 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
935 .collect(Collectors.toSet());
936
937 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700938 // remove found packet links from distributed store
939 linkPathMap.computeIfPresent(l, (plink, conn) -> {
940 // Notifies listeners if all packet links are gone
941 if (conn.isAllRealizingLinkNotEstablished()) {
942 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
943 }
944 return null;
945 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700946 });
947 default:
948 break;
949 }
950 }
951 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700952
953 private class InternalStoreListener
954 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
955
956 @Override
957 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
958 switch (event.type()) {
959 case UPDATE:
960 OpticalConnectivity oldConnectivity = event.oldValue().value();
961 OpticalConnectivity newConnectivity = event.newValue().value();
962
963 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
964 newConnectivity.isAllRealizingLinkEstablished()) {
965 // Notifies listeners if all links are established
966 updateBandwidthUsage(newConnectivity);
967 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
968 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
969 newConnectivity.isAllRealizingLinkNotEstablished()) {
970 // Notifies listeners if all links are gone
971 releaseBandwidthUsage(newConnectivity);
972 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
973 }
974
975 break;
976 default:
977 break;
978 }
979 }
980
981 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700982}
983