blob: abedb454ddfd206d0f4e0bb2899b846154cdac8e [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;
Ray Milkey4694e062018-10-31 13:17:18 -0700110import static org.onosproject.newoptical.OsgiPropertyConstants.MAX_PATHS;
111import static org.onosproject.newoptical.OsgiPropertyConstants.MAX_PATHS_DEFAULT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700112
113/**
114 * Main component to configure optical connectivity.
115 */
116@Beta
Ray Milkey4694e062018-10-31 13:17:18 -0700117@Component(
118 immediate = true,
119 service = OpticalPathService.class,
120 property = {
121 MAX_PATHS + ":Integer=" + MAX_PATHS_DEFAULT
122 }
123)
Naoki Shiota5a056062016-05-05 18:43:59 -0700124public class OpticalPathProvisioner
125 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
126 implements OpticalPathService {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800127
Ray Milkey9c9cde42018-01-12 14:22:06 -0800128 private static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700129
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800130 /**
131 * Bandwidth representing no bandwidth requirement specified.
132 */
133 private static final Bandwidth NO_BW_REQUIREMENT = Bandwidth.bps(0);
134
Naoki Shiota5a056062016-05-05 18:43:59 -0700135 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700136 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
137 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
138 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700141 protected IntentService intentService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700144 protected TopologyService topologyService;
Naoki Shiota5a056062016-05-05 18:43:59 -0700145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700147 protected CoreService coreService;
148
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700150 protected LinkService linkService;
151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700153 protected MastershipService mastershipService;
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700156 protected ClusterService clusterService;
157
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700159 protected DeviceService deviceService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700162 protected StorageService storageService;
163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700165 protected NetworkConfigService networkConfigService;
166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Naoki Shiota5a056062016-05-05 18:43:59 -0700168 protected ResourceService resourceService;
169
Ray Milkey4694e062018-10-31 13:17:18 -0700170 /** Maximum number of paths to consider for path provisioning. */
171 private int maxPaths = MAX_PATHS_DEFAULT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700172
173 private ApplicationId appId;
174
175 private AtomicCounter idCounter;
176
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700177 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700178
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700179 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700180
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800181 /**
182 * Map from packet-layer link expected to be realized by some optical Intent to
183 * OpticalConnectivity (~=top level intent over multi-layer topology).
184 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700185 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700186
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700187 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
188
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800189 // FIXME in the long run. This is effectively app's own resource subsystem
190 /**
191 * Set of cross connect link currently used.
192 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700193 private DistributedSet<Link> usedCrossConnectLinkSet;
194
195 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
196 .register(KryoNamespaces.API)
197 .register(PacketLinkRealizedByOptical.class)
198 .register(OpticalConnectivityId.class)
199 .register(OpticalConnectivity.class);
200
201 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
202 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700203 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700204 .register(OpticalConnectivityId.class)
205 .register(OpticalConnectivity.class);
206
207 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
208 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700209
210 @Activate
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700211 protected void activate(ComponentContext context) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700212 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700213 appId = coreService.registerApplication("org.onosproject.newoptical");
214
Naoki Shiota03c29e12016-05-16 16:58:07 -0700215 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700216
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700217 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
218 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
219 .withName(LINKPATH_MAP_NAME)
220 .withApplicationId(appId)
221 .build();
222
223 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
224 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
225 .withName(CONNECTIVITY_MAP_NAME)
226 .withApplicationId(appId)
227 .build();
228
229 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
230 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
231 .withName(CROSSCONNECTLINK_SET_NAME)
232 .withApplicationId(appId)
233 .build()
234 .asDistributedSet();
235
Naoki Shiota5a056062016-05-05 18:43:59 -0700236 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700237
238 listeners = new ListenerTracker();
239 listeners.addListener(linkService, new InternalLinkListener())
240 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700241
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700242 linkPathMap.addListener(storeListener);
243
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700244 readComponentConfiguration(context);
245
Naoki Shiota5a056062016-05-05 18:43:59 -0700246 log.info("Started");
247 }
248
249 @Deactivate
250 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700251 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700252 listeners.removeListeners();
253 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700254
255 log.info("Stopped");
256 }
257
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700258 @Modified
259 public void modified(ComponentContext context) {
260 readComponentConfiguration(context);
261 }
262
263 /**
264 * Extracts properties from the component configuration context.
265 *
266 * @param context the component context
267 */
268 private void readComponentConfiguration(ComponentContext context) {
269 Dictionary<?, ?> properties = context.getProperties();
Ray Milkey4694e062018-10-31 13:17:18 -0700270 maxPaths = Tools.getIntegerProperty(properties, MAX_PATHS, MAX_PATHS_DEFAULT);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700271 log.info("Configured. Maximum paths to consider is configured to {}", maxPaths);
272 }
273
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800274 @Override
275 public Collection<OpticalConnectivity> listConnectivity() {
276 return connectivityMap.values().stream()
277 .map(Versioned::value)
Yuta HIGUCHI498fa1d2017-05-17 16:08:40 -0700278 .collect(ImmutableList.toImmutableList());
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800279 }
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700280
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700281 @Override
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700282 public Set<Key> listIntents(OpticalConnectivityId id) {
283 return linkPathMap.entrySet().stream()
284 .filter(ent -> id.equals(ent.getValue().value().id()))
285 .map(Entry::getKey)
286 .map(PacketLinkRealizedByOptical::realizingIntentKey)
287 .collect(Collectors.toSet());
288 }
289
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800290 /*
291 * Request packet-layer connectivity between specified ports,
292 * over packet-optical multi-layer infrastructure.
293 *
294 * Functionality-wise this is effectively submitting Packet-Optical
295 * multi-layer P2P Intent.
296 *
297 * It computes multi-layer path meeting specified constraint,
298 * and calls setupPath.
299 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700300 @Override
301 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
302 Bandwidth bandwidth, Duration latency) {
303 checkNotNull(ingress);
304 checkNotNull(egress);
305 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
306
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700307 Bandwidth bw = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
Naoki Shiota5a056062016-05-05 18:43:59 -0700308
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700309 Stream<Path> paths = topologyService.getKShortestPaths(
310 topologyService.currentTopology(),
311 ingress.deviceId(), egress.deviceId(),
Naoki Shiota5a056062016-05-05 18:43:59 -0700312 new BandwidthLinkWeight(bandwidth));
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700313
314 // Path service calculates from node to node, we're only interested in port to port
315 Optional<OpticalConnectivityId> id =
316 paths.filter(p -> p.src().equals(ingress) && p.dst().equals(egress))
317 .limit(maxPaths)
318 .map(p -> setupPath(p, bw, latency))
319 .filter(Objects::nonNull)
320 .findFirst();
321
322 if (id.isPresent()) {
323 log.info("Assigned OpticalConnectivityId: {}", id);
324 } else {
325 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700326 }
327
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700328 return id.orElse(null);
Naoki Shiota5a056062016-05-05 18:43:59 -0700329 }
330
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800331 /*
332 * Given a multi-layer path,
333 * compute a set of segments which requires
334 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
335 * to provide packet-layer connectivity.
336 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700337 @Override
338 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
339 checkNotNull(path);
Yuta HIGUCHI8fc32f82017-03-14 22:12:42 -0700340 log.debug("setupPath({}, {}, {})", path, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700341
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700342 // map of cross connect points (optical port -> packet port)
343 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
344
345 // list of (src, dst) pair of optical ports between which optical path should be installed
346 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
347
348 // Scan path to find pairs of connect points between which optical intent is installed
349 // opticalSrcPort works as a flag parameter to show scanning status
350 ConnectPoint opticalSrcPort = null;
351 for (Link link : path.links()) {
352 if (!isCrossConnectLink(link)) {
353 continue;
354 }
355
356 if (opticalSrcPort != null) {
357 // opticalSrcPort!=null means src port was already found
358 // in this case link.src() is optical layer, and link.dst() is packet layer
359
360 // Check if types of src port and dst port matches
361 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
362 "Unknown device ID");
363 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
364 "Unknown device ID");
365 if (srcDevice.type() != dstDevice.type()) {
366 log.error("Unsupported mix of cross connect points : {}, {}",
367 srcDevice.type(), dstDevice.type());
368 return null;
369 }
370
371 // Update cross connect points map
372 crossConnectPointMap.put(link.src(), link.dst());
373
374 // Add optical ports pair to list
375 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
376
377 // Reset flag parameter
378 opticalSrcPort = null;
379 } else {
380 // opticalSrcPort==null means src port was not found yet
381 // in this case link.src() is packet layer, and link.dst() is optical layer
382
383 // Update cross connect points map
384 crossConnectPointMap.put(link.dst(), link.src());
385 // Set opticalSrcPort to src of link (optical port)
386 opticalSrcPort = link.dst();
387 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700388 }
389
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700390 // create intents from cross connect points
391 List<Intent> intents = createIntents(crossConnectPoints);
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800392 if (intents.isEmpty()) {
393 log.error("No intents produced from {}", crossConnectPoints);
394 return null;
395 }
396
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700397 // create set of PacketLinkRealizedByOptical
398 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
399 intents, crossConnectPointMap);
400
401 // create OpticalConnectivity object and store information to distributed store
402 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700403
404 // store cross connect port usage
405 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700406 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700407
408 // Submit the intents
409 for (Intent i : intents) {
410 intentService.submit(i);
411 log.debug("Submitted an intent: {}", i);
412 }
413
414 return connectivity.id();
415 }
416
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700417 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
418 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700419 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700420 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
421 links, Collections.emptySet());
422
423 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700424
Naoki Shiota5a056062016-05-05 18:43:59 -0700425 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700426 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700427
428 return connectivity;
429 }
430
431 @Override
432 public boolean removeConnectivity(OpticalConnectivityId id) {
433 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700434 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700435
436 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700437 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700438 return false;
439 }
440
441 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700442 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700443 Intent intent = intentService.getIntent(l.realizingIntentKey());
444 intentService.withdraw(intent);
445 });
446
447 return true;
448 }
449
450 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700451 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700452 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700453 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700454 log.info("OpticalConnectivity with id {} not found.", id);
455 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700456 }
457
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700458 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700459 }
460
461 /**
462 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700463 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700464 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700465 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700466 * @return list of optical connectivity intents
467 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700468 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700469 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700470 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700471
472 while (itr.hasNext()) {
473 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700474 Pair<ConnectPoint, ConnectPoint> next = itr.next();
475 ConnectPoint src = next.getLeft();
476 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700477
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700478 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
479 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700480
481 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700482 OduCltPort srcOCPort = (OduCltPort) srcPort;
483 OduCltPort dstOCPort = (OduCltPort) dstPort;
484 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
485 continue;
486 }
487
Naoki Shiota5a056062016-05-05 18:43:59 -0700488 // Create OTN circuit
489 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
490 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700491 .src(src)
492 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700493 .signalType(srcOCPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700494 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700495 .build();
496 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700497 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700498 OchPort srcOchPort = (OchPort) srcPort;
499 OchPort dstOchPort = (OchPort) dstPort;
500 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
501 continue;
502 }
503
Naoki Shiota5a056062016-05-05 18:43:59 -0700504 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700505 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
506 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700507 .src(src)
508 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700509 .signalType(srcOchPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700510 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700511 .build();
512 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700513 } else {
514 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
515 return Collections.emptyList();
516 }
517 }
518
519 return intents;
520 }
521
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700522 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
523 List<Intent> intents,
524 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
525 checkArgument(connectPoints.size() == intents.size());
526
527 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
528
529 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
530 Iterator<Intent> intentItr = intents.iterator();
531 while (xcPointsItr.hasNext()) {
532 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
533 Intent intent = intentItr.next();
534
535 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
536 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
537
538 if (intent instanceof OpticalConnectivityIntent) {
539 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
540 (OpticalConnectivityIntent) intent));
541 } else if (intent instanceof OpticalCircuitIntent) {
542 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
543 (OpticalCircuitIntent) intent));
544 } else {
545 log.warn("Unexpected intent type: {}", intent.getClass());
546 }
547 }
548
549 return pLinks;
550 }
551
Naoki Shiota5a056062016-05-05 18:43:59 -0700552 /**
553 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
554 *
555 * @param type device type
556 * @return true if in packet layer, false otherwise
557 */
558 private boolean isPacketLayer(Device.Type type) {
559 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
560 }
561
562 /**
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800563 * Verifies if given device type is NOT in packet layer, i.e., switch or router device.
Naoki Shiota5a056062016-05-05 18:43:59 -0700564 *
565 * @param type device type
Andrea Campanella1c24fb92018-12-20 16:43:59 +0100566 * @return true if in optical layer, false otherwise
Naoki Shiota5a056062016-05-05 18:43:59 -0700567 */
568 private boolean isTransportLayer(Device.Type type) {
Andrea Campanellafa0f6cc2018-12-18 15:17:53 +0100569 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN ||
Andrea Campanella1c24fb92018-12-20 16:43:59 +0100570 type == Device.Type.OLS || type == Device.Type.TERMINAL_DEVICE;
Naoki Shiota5a056062016-05-05 18:43:59 -0700571 }
572
573 /**
574 * Verifies if given link forms a cross-connection between packet and optical layer.
575 *
576 * @param link the link
577 * @return true if the link is a cross-connect link, false otherwise
578 */
579 private boolean isCrossConnectLink(Link link) {
580 if (link.type() != Link.Type.OPTICAL) {
581 return false;
582 }
583
584 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
585 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
586
587 return src != dst &&
588 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
589 }
590
591 /**
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800592 * Updates bandwidth resource of given connect point to specified value.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700593 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700594 * @param cp Connect point
595 * @param bandwidth New bandwidth
596 */
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800597 private void setPortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700598 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
599 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
600 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700601 }
602
603 /**
604 * Updates usage information of bandwidth based on connectivity which is established.
605 * @param connectivity Optical connectivity
606 */
607 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800608 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
609 // no bandwidth requirement, nothing to allocate.
610 return;
611 }
612
Naoki Shiotacb744db2016-05-13 19:32:35 -0700613 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700614
615 List<Link> links = connectivity.links();
616
617 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
618 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
619 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
620 Bandwidth.class).resource(connectivity.bandwidth().bps()))
621 .collect(Collectors.toList());
622
Naoki Shiotacb744db2016-05-13 19:32:35 -0700623 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
624 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700625 if (allocations.isEmpty()) {
626 log.warn("Failed to allocate bandwidth {} to {}",
627 connectivity.bandwidth().bps(), resources);
628 // TODO any recovery?
629 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700630 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700631 }
632
633 /**
634 * Release bandwidth allocated by given connectivity.
635 * @param connectivity Optical connectivity
636 */
637 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700638 if (connectivity.links().isEmpty()) {
639 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700640 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800641 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
642 // no bandwidth requirement, nothing to release.
643 return;
644 }
645
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700646
647 // release resource only if this node is the master for link head device
648 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
649 OpticalConnectivityId connectivityId = connectivity.id();
650
651 log.debug("releasing bandwidth allocated to {}", connectivityId);
652 if (!resourceService.release(connectivityId)) {
653 log.warn("Failed to release bandwidth allocated to {}",
654 connectivityId);
655 // TODO any recovery?
656 }
657 log.debug("DONE releasing bandwidth for {}", connectivityId);
658 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700659 }
660
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800661 private boolean linkDiscoveryEnabled(ConnectPoint cp) {
662 // FIXME should check Device feature and configuration state.
663
664 // short-term hack for ONS'17 time-frame,
665 // only expect OF device to have link discovery.
Jon Halla3fcf672017-03-28 16:53:22 -0700666 return "of".equals(cp.deviceId().uri().getScheme());
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800667 }
668
669 /**
670 * Returns true if both connect point support for link discovery & enabled.
671 *
672 * @param cp1 port 1
673 * @param cp2 port 2
674 * @return true if both connect point support for link discovery & enabled.
675 */
676 private boolean linkDiscoveryEnabled(ConnectPoint cp1, ConnectPoint cp2) {
677 return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
678 }
679
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700680 private class BandwidthLinkWeight extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge> implements LinkWeigher {
Naoki Shiota5a056062016-05-05 18:43:59 -0700681 private Bandwidth bandwidth = null;
682
683 public BandwidthLinkWeight(Bandwidth bandwidth) {
684 this.bandwidth = bandwidth;
685 }
686
687 @Override
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700688 public Weight weight(TopologyEdge edge) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700689 Link l = edge.link();
690
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700691 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700692 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800693 log.trace("{} is not active", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700694 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700695 }
696
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700697 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700698 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800699 log.trace("Cross connect {} in use", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700700 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700701 }
702
703 // Check availability of bandwidth
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800704 if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700705 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700706 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700707 } else {
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700708 log.trace("Not enough bandwidth on {}", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700709 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700710 }
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700711 // Allow everything else
Naoki Shiota5a056062016-05-05 18:43:59 -0700712 } else {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700713 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700714 }
715 }
716
717 private boolean hasEnoughBandwidth(ConnectPoint cp) {
718 if (cp.elementId() instanceof DeviceId) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700719 Device device = deviceService.getDevice(cp.deviceId());
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700720 Device.Type type = device.type();
721
Naoki Shiota5a056062016-05-05 18:43:59 -0700722 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700723 // Check if the port has enough capacity
724 Port port = deviceService.getPort(cp.deviceId(), cp.port());
725 if (port instanceof OduCltPort || port instanceof OchPort) {
726 // Port with capacity
727 return bandwidth.bps() < port.portSpeed() * 1000000.0;
728 } else {
729 // Port without valid capacity (OMS port, etc.)
730 return true;
731 }
732 } else {
733 // Check if enough amount of bandwidth resource remains
734 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
735 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800736 try {
737 return resourceService.isAvailable(resource);
738 } catch (Exception e) {
739 log.error("Resource service failed checking availability of {}",
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700740 resource, e);
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800741 throw e;
742 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700743 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700744 }
745 return false;
746 }
747 }
748
Naoki Shiota5a056062016-05-05 18:43:59 -0700749 public class InternalIntentListener implements IntentListener {
750 @Override
751 public void event(IntentEvent event) {
752 switch (event.type()) {
753 case INSTALLED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700754 log.debug("Intent {} installed.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700755 updateCrossConnectLink(event.subject());
756 break;
757 case WITHDRAWN:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700758 log.debug("Intent {} withdrawn.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700759 removeCrossConnectLinks(event.subject());
760 break;
761 case FAILED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700762 log.debug("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800763 // TODO If it was one of it's own optical Intent,
764 // update link state
765 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700766 break;
767 default:
768 break;
769 }
770 }
771
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800772 // TODO rename "CrossConnectLink"?
773 /**
774 * Update packet-layer link/port state once Intent is installed.
775 *
776 * @param intent which reached installed state
777 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700778 private void updateCrossConnectLink(Intent intent) {
779 linkPathMap.entrySet().stream()
780 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
781 .forEach(e -> {
782 ConnectPoint packetSrc = e.getKey().src();
783 ConnectPoint packetDst = e.getKey().dst();
784 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700785
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700786 // reflect modification only if packetSrc is local_
787 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
788 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800789 setPortBandwidth(packetSrc, bw);
790 setPortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700791
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700792 // Updates link status in distributed map
793 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
794 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800795
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800796
797 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
798 injectLink(packetSrc, packetDst);
799 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700800 }
801 });
802 }
803
804 private void removeCrossConnectLinks(Intent intent) {
805 ConnectPoint src, dst;
806
807 if (intent instanceof OpticalCircuitIntent) {
808 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
809 src = circuit.getSrc();
810 dst = circuit.getDst();
811 } else if (intent instanceof OpticalConnectivityIntent) {
812 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
813 src = conn.getSrc();
814 dst = conn.getDst();
815 } else {
816 return;
817 }
818
819 removeXcLinkUsage(src);
820 removeXcLinkUsage(dst);
821
822 // Set bandwidth of 0 to cross connect ports
823 Bandwidth bw = Bandwidth.bps(0);
824 linkPathMap.entrySet().stream()
825 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
826 .forEach(e -> {
827 ConnectPoint packetSrc = e.getKey().src();
828 ConnectPoint packetDst = e.getKey().dst();
Naoki Shiota5a056062016-05-05 18:43:59 -0700829
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700830 // reflect modification only if packetSrc is local_
831 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
832 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800833 setPortBandwidth(packetSrc, bw);
834 setPortBandwidth(packetDst, bw);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700835
836 // Updates link status in distributed map
837 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
838 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800839
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800840
841 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
842 removeInjectedLink(packetSrc, packetDst);
843 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700844 }
845 });
846 }
847
848 private void removeXcLinkUsage(ConnectPoint cp) {
849 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700850 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700851 .findAny();
852
853 if (!link.isPresent()) {
Yuta HIGUCHIe2689ee2017-05-04 16:53:18 -0700854 log.warn("Cross connect point {} has no cross connect link to release.", cp);
Naoki Shiota5a056062016-05-05 18:43:59 -0700855 return;
856 }
857
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700858 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700859 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800860
861 /**
862 * Injects link between specified packet port.
863 *
864 * @param packetSrc port 1
865 * @param packetDst port 2
866 */
867 private void injectLink(ConnectPoint packetSrc,
868 ConnectPoint packetDst) {
869 // inject expected link or durable link
870 // if packet device cannot advertise packet link
871 try {
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700872 // cannot call addConfig.
873 // it will create default BasicLinkConfig,
874 // which will end up advertising DIRECT links and
875 // DIRECT Link type cannot transition from DIRECT to INDIRECT
876 LinkKey lnkKey = linkKey(packetSrc, packetDst);
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800877 BasicLinkConfig lnkCfg = networkConfigService
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700878 .getConfig(lnkKey, BasicLinkConfig.class);
879 if (lnkCfg == null) {
880 lnkCfg = new BasicLinkConfig(lnkKey);
881 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800882 lnkCfg.isAllowed(true);
883 lnkCfg.isDurable(true);
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700884 lnkCfg.type(Link.Type.INDIRECT);
885 lnkCfg.isBidirectional(false);
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700886 // cannot call apply against manually created instance
887 //lnkCfg.apply();
888 networkConfigService.applyConfig(lnkKey,
889 BasicLinkConfig.class,
890 lnkCfg.node());
891
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800892 } catch (Exception ex) {
893 log.error("Applying BasicLinkConfig failed", ex);
894 }
895 }
896
897 /**
898 * Removes link injected between specified packet port.
899 *
900 * @param packetSrc port 1
901 * @param packetDst port 2
902 */
903 private void removeInjectedLink(ConnectPoint packetSrc,
904 ConnectPoint packetDst) {
905 // remove expected link or durable link
906 // if packet device cannot monitor packet link
907
908 try {
909 // hack to mark link off-line
910 BasicLinkConfig lnkCfg = networkConfigService
911 .getConfig(linkKey(packetSrc, packetDst),
912 BasicLinkConfig.class);
913 lnkCfg.isAllowed(false);
914 lnkCfg.apply();
915 } catch (Exception ex) {
916 log.error("Applying BasicLinkConfig failed", ex);
917 }
918
919 networkConfigService
920 .removeConfig(linkKey(packetSrc, packetDst),
921 BasicLinkConfig.class);
922 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700923 }
924
925
926 private class InternalLinkListener implements LinkListener {
927
928 @Override
929 public void event(LinkEvent event) {
930 switch (event.type()) {
931 case LINK_REMOVED:
932 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700933 // updates linkPathMap only if src device of link is local
934 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
935 return;
936 }
937
938 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700939 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
940 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
941 .collect(Collectors.toSet());
942
943 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700944 // remove found packet links from distributed store
945 linkPathMap.computeIfPresent(l, (plink, conn) -> {
946 // Notifies listeners if all packet links are gone
947 if (conn.isAllRealizingLinkNotEstablished()) {
948 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
949 }
950 return null;
951 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700952 });
953 default:
954 break;
955 }
956 }
957 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700958
959 private class InternalStoreListener
960 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
961
962 @Override
963 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
964 switch (event.type()) {
965 case UPDATE:
966 OpticalConnectivity oldConnectivity = event.oldValue().value();
967 OpticalConnectivity newConnectivity = event.newValue().value();
968
969 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
970 newConnectivity.isAllRealizingLinkEstablished()) {
971 // Notifies listeners if all links are established
972 updateBandwidthUsage(newConnectivity);
973 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
974 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
975 newConnectivity.isAllRealizingLinkNotEstablished()) {
976 // Notifies listeners if all links are gone
977 releaseBandwidthUsage(newConnectivity);
978 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
979 }
980
981 break;
982 default:
983 break;
984 }
985 }
986
987 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700988}
989