blob: 2776a195094bd8862f22ea610e54480267d5d703 [file] [log] [blame]
Naoki Shiota5a056062016-05-05 18:43:59 -07001/*
Brian O'Connor0a4e6742016-09-15 23:03:10 -07002 * Copyright 2016-present Open Networking Laboratory
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 -070020
21import org.apache.commons.lang3.tuple.Pair;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
28import org.onlab.util.Bandwidth;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070029import org.onlab.util.KryoNamespace;
Naoki Shiota5a056062016-05-05 18:43:59 -070030import org.onosproject.cluster.ClusterService;
Naoki Shiota5a056062016-05-05 18:43:59 -070031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
Naoki Shiota0d2943e2016-05-13 18:53:21 -070033import org.onosproject.event.ListenerTracker;
34import org.onosproject.net.optical.OchPort;
35import org.onosproject.net.optical.OduCltPort;
Naoki Shiota5a056062016-05-05 18:43:59 -070036import org.onosproject.newoptical.api.OpticalConnectivityId;
37import org.onosproject.newoptical.api.OpticalPathEvent;
38import org.onosproject.newoptical.api.OpticalPathListener;
39import org.onosproject.newoptical.api.OpticalPathService;
40import org.onosproject.event.AbstractListenerManager;
41import org.onosproject.mastership.MastershipService;
Naoki Shiota5a056062016-05-05 18:43:59 -070042import org.onosproject.net.ConnectPoint;
43import org.onosproject.net.Device;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Link;
Naoki Shiota5a056062016-05-05 18:43:59 -070046import org.onosproject.net.Path;
47import org.onosproject.net.Port;
48import org.onosproject.net.config.NetworkConfigService;
49import 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;
Naoki Shiota5a056062016-05-05 18:43:59 -070054import org.onosproject.net.intent.OpticalCircuitIntent;
55import org.onosproject.net.intent.OpticalConnectivityIntent;
Naoki Shiota5a056062016-05-05 18:43:59 -070056import org.onosproject.net.link.LinkEvent;
57import org.onosproject.net.link.LinkListener;
58import org.onosproject.net.link.LinkService;
Sho SHIMIZUb6c63a32016-05-26 12:07:19 -070059import org.onosproject.net.config.basics.BandwidthCapacity;
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -080060import org.onosproject.net.config.basics.BasicLinkConfig;
Naoki Shiota5a056062016-05-05 18:43:59 -070061import org.onosproject.net.resource.ContinuousResource;
62import org.onosproject.net.resource.Resource;
63import org.onosproject.net.resource.ResourceAllocation;
64import org.onosproject.net.resource.ResourceService;
65import org.onosproject.net.resource.Resources;
66import org.onosproject.net.topology.LinkWeight;
67import org.onosproject.net.topology.PathService;
68import org.onosproject.net.topology.TopologyEdge;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070069import org.onosproject.store.serializers.KryoNamespaces;
Naoki Shiota5a056062016-05-05 18:43:59 -070070import org.onosproject.store.service.AtomicCounter;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070071import org.onosproject.store.service.ConsistentMap;
72import org.onosproject.store.service.DistributedSet;
73import org.onosproject.store.service.MapEvent;
74import org.onosproject.store.service.MapEventListener;
75import org.onosproject.store.service.Serializer;
Naoki Shiota5a056062016-05-05 18:43:59 -070076import org.onosproject.store.service.StorageService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070077import org.onosproject.store.service.Versioned;
Naoki Shiota5a056062016-05-05 18:43:59 -070078import org.slf4j.Logger;
79import org.slf4j.LoggerFactory;
80
81import java.time.Duration;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070082import java.util.ArrayList;
Naoki Shiota5a056062016-05-05 18:43:59 -070083import java.util.Collections;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070084import java.util.HashMap;
85import java.util.HashSet;
Naoki Shiota5a056062016-05-05 18:43:59 -070086import java.util.Iterator;
87import java.util.LinkedList;
88import java.util.List;
89import java.util.Map;
90import java.util.Optional;
91import java.util.Set;
Naoki Shiota5a056062016-05-05 18:43:59 -070092import java.util.stream.Collectors;
93import java.util.stream.Stream;
94
95import static com.google.common.base.Preconditions.checkArgument;
96import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -080097import static org.onosproject.net.LinkKey.linkKey;
Naoki Shiotae1366ad2016-05-11 19:24:11 -070098import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Naoki Shiota5a056062016-05-05 18:43:59 -070099
100/**
101 * Main component to configure optical connectivity.
102 */
103@Beta
104@Service
105@Component(immediate = true)
106public class OpticalPathProvisioner
107 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
108 implements OpticalPathService {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800109
Naoki Shiota5a056062016-05-05 18:43:59 -0700110 protected static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
111
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800112 /**
113 * Bandwidth representing no bandwidth requirement specified.
114 */
115 private static final Bandwidth NO_BW_REQUIREMENT = Bandwidth.bps(0);
116
Naoki Shiota5a056062016-05-05 18:43:59 -0700117 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700118 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
119 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
120 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected IntentService intentService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected PathService pathService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected CoreService coreService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected LinkService linkService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected MastershipService mastershipService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected ClusterService clusterService;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected DeviceService deviceService;
142
143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected StorageService storageService;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
147 protected NetworkConfigService networkConfigService;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
150 protected ResourceService resourceService;
151
152
153 private ApplicationId appId;
154
155 private AtomicCounter idCounter;
156
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700157 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700158
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700159 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700160
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800161 /**
162 * Map from packet-layer link expected to be realized by some optical Intent to
163 * OpticalConnectivity (~=top level intent over multi-layer topology).
164 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700165 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700166
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700167 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
168
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800169 // FIXME in the long run. This is effectively app's own resource subsystem
170 /**
171 * Set of cross connect link currently used.
172 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700173 private DistributedSet<Link> usedCrossConnectLinkSet;
174
175 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
176 .register(KryoNamespaces.API)
177 .register(PacketLinkRealizedByOptical.class)
178 .register(OpticalConnectivityId.class)
179 .register(OpticalConnectivity.class);
180
181 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
182 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700183 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700184 .register(OpticalConnectivityId.class)
185 .register(OpticalConnectivity.class);
186
187 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
188 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700189
190 @Activate
191 protected void activate() {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700192 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700193 appId = coreService.registerApplication("org.onosproject.newoptical");
194
Naoki Shiota03c29e12016-05-16 16:58:07 -0700195 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700196
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700197 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
198 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
199 .withName(LINKPATH_MAP_NAME)
200 .withApplicationId(appId)
201 .build();
202
203 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
204 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
205 .withName(CONNECTIVITY_MAP_NAME)
206 .withApplicationId(appId)
207 .build();
208
209 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
210 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
211 .withName(CROSSCONNECTLINK_SET_NAME)
212 .withApplicationId(appId)
213 .build()
214 .asDistributedSet();
215
Naoki Shiota5a056062016-05-05 18:43:59 -0700216 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700217
218 listeners = new ListenerTracker();
219 listeners.addListener(linkService, new InternalLinkListener())
220 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700221
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700222 linkPathMap.addListener(storeListener);
223
Naoki Shiota5a056062016-05-05 18:43:59 -0700224 log.info("Started");
225 }
226
227 @Deactivate
228 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700229 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700230 listeners.removeListeners();
231 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700232
233 log.info("Stopped");
234 }
235
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800236 /*
237 * Request packet-layer connectivity between specified ports,
238 * over packet-optical multi-layer infrastructure.
239 *
240 * Functionality-wise this is effectively submitting Packet-Optical
241 * multi-layer P2P Intent.
242 *
243 * It computes multi-layer path meeting specified constraint,
244 * and calls setupPath.
245 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700246 @Override
247 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
248 Bandwidth bandwidth, Duration latency) {
249 checkNotNull(ingress);
250 checkNotNull(egress);
251 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
252
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800253 bandwidth = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
Naoki Shiota5a056062016-05-05 18:43:59 -0700254
255 Set<Path> paths = pathService.getPaths(ingress.deviceId(), egress.deviceId(),
256 new BandwidthLinkWeight(bandwidth));
257 if (paths.isEmpty()) {
258 log.warn("Unable to find multi-layer path.");
259 return null;
260 }
261
262 // Search path with available cross connect points
263 for (Path path : paths) {
264 OpticalConnectivityId id = setupPath(path, bandwidth, latency);
265 if (id != null) {
266 log.info("Assigned OpticalConnectivityId: {}", id);
267 return id;
268 }
269 }
270
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800271 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700272
Naoki Shiota5a056062016-05-05 18:43:59 -0700273 return null;
274 }
275
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800276 /*
277 * Given a multi-layer path,
278 * compute a set of segments which requires
279 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
280 * to provide packet-layer connectivity.
281 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700282 @Override
283 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
284 checkNotNull(path);
285 log.info("setupPath({}, {}, {})", path, bandwidth, latency);
286
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700287 // map of cross connect points (optical port -> packet port)
288 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
289
290 // list of (src, dst) pair of optical ports between which optical path should be installed
291 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
292
293 // Scan path to find pairs of connect points between which optical intent is installed
294 // opticalSrcPort works as a flag parameter to show scanning status
295 ConnectPoint opticalSrcPort = null;
296 for (Link link : path.links()) {
297 if (!isCrossConnectLink(link)) {
298 continue;
299 }
300
301 if (opticalSrcPort != null) {
302 // opticalSrcPort!=null means src port was already found
303 // in this case link.src() is optical layer, and link.dst() is packet layer
304
305 // Check if types of src port and dst port matches
306 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
307 "Unknown device ID");
308 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
309 "Unknown device ID");
310 if (srcDevice.type() != dstDevice.type()) {
311 log.error("Unsupported mix of cross connect points : {}, {}",
312 srcDevice.type(), dstDevice.type());
313 return null;
314 }
315
316 // Update cross connect points map
317 crossConnectPointMap.put(link.src(), link.dst());
318
319 // Add optical ports pair to list
320 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
321
322 // Reset flag parameter
323 opticalSrcPort = null;
324 } else {
325 // opticalSrcPort==null means src port was not found yet
326 // in this case link.src() is packet layer, and link.dst() is optical layer
327
328 // Update cross connect points map
329 crossConnectPointMap.put(link.dst(), link.src());
330 // Set opticalSrcPort to src of link (optical port)
331 opticalSrcPort = link.dst();
332 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700333 }
334
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700335 // create intents from cross connect points
336 List<Intent> intents = createIntents(crossConnectPoints);
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800337 if (intents.isEmpty()) {
338 log.error("No intents produced from {}", crossConnectPoints);
339 return null;
340 }
341
Naoki Shiota5a056062016-05-05 18:43:59 -0700342
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700343 // create set of PacketLinkRealizedByOptical
344 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
345 intents, crossConnectPointMap);
346
347 // create OpticalConnectivity object and store information to distributed store
348 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700349
350 // store cross connect port usage
351 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700352 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700353
354 // Submit the intents
355 for (Intent i : intents) {
356 intentService.submit(i);
357 log.debug("Submitted an intent: {}", i);
358 }
359
360 return connectivity.id();
361 }
362
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700363 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
364 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700365 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700366 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
367 links, Collections.emptySet());
368
369 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700370
Naoki Shiota5a056062016-05-05 18:43:59 -0700371 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700372 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700373
374 return connectivity;
375 }
376
377 @Override
378 public boolean removeConnectivity(OpticalConnectivityId id) {
379 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700380 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700381
382 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700383 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700384 return false;
385 }
386
387 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700388 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700389 Intent intent = intentService.getIntent(l.realizingIntentKey());
390 intentService.withdraw(intent);
391 });
392
393 return true;
394 }
395
396 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700397 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700398 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700399 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700400 log.info("OpticalConnectivity with id {} not found.", id);
401 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700402 }
403
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700404 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700405 }
406
407 /**
408 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700409 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700410 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700411 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700412 * @return list of optical connectivity intents
413 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700414 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700415 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700416 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700417
418 while (itr.hasNext()) {
419 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700420 Pair<ConnectPoint, ConnectPoint> next = itr.next();
421 ConnectPoint src = next.getLeft();
422 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700423
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700424 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
425 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700426
427 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700428 OduCltPort srcOCPort = (OduCltPort) srcPort;
429 OduCltPort dstOCPort = (OduCltPort) dstPort;
430 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
431 continue;
432 }
433
Naoki Shiota5a056062016-05-05 18:43:59 -0700434 // Create OTN circuit
435 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
436 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700437 .src(src)
438 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700439 .signalType(srcOCPort.signalType())
Naoki Shiota5a056062016-05-05 18:43:59 -0700440 .bidirectional(true)
441 .build();
442 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700443 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700444 OchPort srcOchPort = (OchPort) srcPort;
445 OchPort dstOchPort = (OchPort) dstPort;
446 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
447 continue;
448 }
449
Naoki Shiota5a056062016-05-05 18:43:59 -0700450 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700451 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
452 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700453 .src(src)
454 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700455 .signalType(srcOchPort.signalType())
Naoki Shiota5a056062016-05-05 18:43:59 -0700456 .bidirectional(true)
457 .build();
458 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700459 } else {
460 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
461 return Collections.emptyList();
462 }
463 }
464
465 return intents;
466 }
467
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700468 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
469 List<Intent> intents,
470 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
471 checkArgument(connectPoints.size() == intents.size());
472
473 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
474
475 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
476 Iterator<Intent> intentItr = intents.iterator();
477 while (xcPointsItr.hasNext()) {
478 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
479 Intent intent = intentItr.next();
480
481 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
482 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
483
484 if (intent instanceof OpticalConnectivityIntent) {
485 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
486 (OpticalConnectivityIntent) intent));
487 } else if (intent instanceof OpticalCircuitIntent) {
488 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
489 (OpticalCircuitIntent) intent));
490 } else {
491 log.warn("Unexpected intent type: {}", intent.getClass());
492 }
493 }
494
495 return pLinks;
496 }
497
Naoki Shiota5a056062016-05-05 18:43:59 -0700498 /**
499 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
500 *
501 * @param type device type
502 * @return true if in packet layer, false otherwise
503 */
504 private boolean isPacketLayer(Device.Type type) {
505 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
506 }
507
508 /**
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800509 * Verifies if given device type is NOT in packet layer, i.e., switch or router device.
Naoki Shiota5a056062016-05-05 18:43:59 -0700510 *
511 * @param type device type
512 * @return true if in packet layer, false otherwise
513 */
514 private boolean isTransportLayer(Device.Type type) {
515 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
516 }
517
518 /**
519 * Verifies if given link forms a cross-connection between packet and optical layer.
520 *
521 * @param link the link
522 * @return true if the link is a cross-connect link, false otherwise
523 */
524 private boolean isCrossConnectLink(Link link) {
525 if (link.type() != Link.Type.OPTICAL) {
526 return false;
527 }
528
529 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
530 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
531
532 return src != dst &&
533 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
534 }
535
536 /**
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800537 * Updates bandwidth resource of given connect point to specified value.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700538 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700539 * @param cp Connect point
540 * @param bandwidth New bandwidth
541 */
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800542 private void setPortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700543 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
544 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
545 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700546 }
547
548 /**
549 * Updates usage information of bandwidth based on connectivity which is established.
550 * @param connectivity Optical connectivity
551 */
552 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800553 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
554 // no bandwidth requirement, nothing to allocate.
555 return;
556 }
557
Naoki Shiotacb744db2016-05-13 19:32:35 -0700558 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700559
560 List<Link> links = connectivity.links();
561
562 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
563 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
564 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
565 Bandwidth.class).resource(connectivity.bandwidth().bps()))
566 .collect(Collectors.toList());
567
Naoki Shiotacb744db2016-05-13 19:32:35 -0700568 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
569 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700570 if (allocations.isEmpty()) {
571 log.warn("Failed to allocate bandwidth {} to {}",
572 connectivity.bandwidth().bps(), resources);
573 // TODO any recovery?
574 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700575 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700576 }
577
578 /**
579 * Release bandwidth allocated by given connectivity.
580 * @param connectivity Optical connectivity
581 */
582 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700583 if (connectivity.links().isEmpty()) {
584 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700585 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800586 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
587 // no bandwidth requirement, nothing to release.
588 return;
589 }
590
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700591
592 // release resource only if this node is the master for link head device
593 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
594 OpticalConnectivityId connectivityId = connectivity.id();
595
596 log.debug("releasing bandwidth allocated to {}", connectivityId);
597 if (!resourceService.release(connectivityId)) {
598 log.warn("Failed to release bandwidth allocated to {}",
599 connectivityId);
600 // TODO any recovery?
601 }
602 log.debug("DONE releasing bandwidth for {}", connectivityId);
603 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700604 }
605
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800606 private boolean linkDiscoveryEnabled(ConnectPoint cp) {
607 // FIXME should check Device feature and configuration state.
608
609 // short-term hack for ONS'17 time-frame,
610 // only expect OF device to have link discovery.
611 return cp.deviceId().uri().getScheme().equals("of");
612 }
613
614 /**
615 * Returns true if both connect point support for link discovery & enabled.
616 *
617 * @param cp1 port 1
618 * @param cp2 port 2
619 * @return true if both connect point support for link discovery & enabled.
620 */
621 private boolean linkDiscoveryEnabled(ConnectPoint cp1, ConnectPoint cp2) {
622 return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
623 }
624
Naoki Shiota5a056062016-05-05 18:43:59 -0700625 private class BandwidthLinkWeight implements LinkWeight {
626 private Bandwidth bandwidth = null;
627
628 public BandwidthLinkWeight(Bandwidth bandwidth) {
629 this.bandwidth = bandwidth;
630 }
631
632 @Override
633 public double weight(TopologyEdge edge) {
634 Link l = edge.link();
635
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700636 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700637 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800638 log.trace("{} is not active", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700639 return -1.0;
640 }
641
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700642 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700643 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800644 log.trace("Cross connect {} in use", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700645 return -1.0;
646 }
647
648 // Check availability of bandwidth
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800649 if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700650 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
651 return 1.0;
652 } else {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800653 log.trace("Not enought bandwidth on {}", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700654 return -1.0;
655 }
656 } else {
657 // TODO needs to differentiate optical and packet?
658 if (l.type() == Link.Type.OPTICAL) {
659 // Transport links
660 return 1.0;
661 } else {
662 // Packet links
663 return 1.0;
664 }
665 }
666 }
667
668 private boolean hasEnoughBandwidth(ConnectPoint cp) {
669 if (cp.elementId() instanceof DeviceId) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700670 Device device = deviceService.getDevice(cp.deviceId());
671 Device.Type type = device.type();
672
Naoki Shiota5a056062016-05-05 18:43:59 -0700673 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700674 // Check if the port has enough capacity
675 Port port = deviceService.getPort(cp.deviceId(), cp.port());
676 if (port instanceof OduCltPort || port instanceof OchPort) {
677 // Port with capacity
678 return bandwidth.bps() < port.portSpeed() * 1000000.0;
679 } else {
680 // Port without valid capacity (OMS port, etc.)
681 return true;
682 }
683 } else {
684 // Check if enough amount of bandwidth resource remains
685 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
686 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800687 try {
688 return resourceService.isAvailable(resource);
689 } catch (Exception e) {
690 log.error("Resource service failed checking availability of {}",
691 resource, e);
692 throw e;
693 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700694 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700695 }
696 return false;
697 }
698 }
699
700
701 public class InternalIntentListener implements IntentListener {
702 @Override
703 public void event(IntentEvent event) {
704 switch (event.type()) {
705 case INSTALLED:
706 log.info("Intent {} installed.", event.subject());
707 updateCrossConnectLink(event.subject());
708 break;
709 case WITHDRAWN:
710 log.info("Intent {} withdrawn.", event.subject());
711 removeCrossConnectLinks(event.subject());
712 break;
713 case FAILED:
714 log.info("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800715 // TODO If it was one of it's own optical Intent,
716 // update link state
717 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700718 break;
719 default:
720 break;
721 }
722 }
723
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800724 // TODO rename "CrossConnectLink"?
725 /**
726 * Update packet-layer link/port state once Intent is installed.
727 *
728 * @param intent which reached installed state
729 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700730 private void updateCrossConnectLink(Intent intent) {
731 linkPathMap.entrySet().stream()
732 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
733 .forEach(e -> {
734 ConnectPoint packetSrc = e.getKey().src();
735 ConnectPoint packetDst = e.getKey().dst();
736 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700737
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700738 // reflect modification only if packetSrc is local_
739 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
740 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800741 setPortBandwidth(packetSrc, bw);
742 setPortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700743
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700744 // Updates link status in distributed map
745 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
746 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800747
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800748
749 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
750 injectLink(packetSrc, packetDst);
751 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700752 }
753 });
754 }
755
756 private void removeCrossConnectLinks(Intent intent) {
757 ConnectPoint src, dst;
758
759 if (intent instanceof OpticalCircuitIntent) {
760 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
761 src = circuit.getSrc();
762 dst = circuit.getDst();
763 } else if (intent instanceof OpticalConnectivityIntent) {
764 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
765 src = conn.getSrc();
766 dst = conn.getDst();
767 } else {
768 return;
769 }
770
771 removeXcLinkUsage(src);
772 removeXcLinkUsage(dst);
773
774 // Set bandwidth of 0 to cross connect ports
775 Bandwidth bw = Bandwidth.bps(0);
776 linkPathMap.entrySet().stream()
777 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
778 .forEach(e -> {
779 ConnectPoint packetSrc = e.getKey().src();
780 ConnectPoint packetDst = e.getKey().dst();
Naoki Shiota5a056062016-05-05 18:43:59 -0700781
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700782 // reflect modification only if packetSrc is local_
783 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
784 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800785 setPortBandwidth(packetSrc, bw);
786 setPortBandwidth(packetDst, bw);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700787
788 // Updates link status in distributed map
789 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
790 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800791
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800792
793 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
794 removeInjectedLink(packetSrc, packetDst);
795 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700796 }
797 });
798 }
799
800 private void removeXcLinkUsage(ConnectPoint cp) {
801 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700802 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700803 .findAny();
804
805 if (!link.isPresent()) {
806 log.warn("Cross connect point {} has no cross connect link.", cp);
807 return;
808 }
809
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700810 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700811 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800812
813 /**
814 * Injects link between specified packet port.
815 *
816 * @param packetSrc port 1
817 * @param packetDst port 2
818 */
819 private void injectLink(ConnectPoint packetSrc,
820 ConnectPoint packetDst) {
821 // inject expected link or durable link
822 // if packet device cannot advertise packet link
823 try {
824 BasicLinkConfig lnkCfg = networkConfigService
825 .addConfig(linkKey(packetSrc, packetDst),
826 BasicLinkConfig.class);
827 lnkCfg.isAllowed(true);
828 lnkCfg.isDurable(true);
829 lnkCfg.type(Link.Type.DIRECT);
830 lnkCfg.apply();
831 } catch (Exception ex) {
832 log.error("Applying BasicLinkConfig failed", ex);
833 }
834 }
835
836 /**
837 * Removes link injected between specified packet port.
838 *
839 * @param packetSrc port 1
840 * @param packetDst port 2
841 */
842 private void removeInjectedLink(ConnectPoint packetSrc,
843 ConnectPoint packetDst) {
844 // remove expected link or durable link
845 // if packet device cannot monitor packet link
846
847 try {
848 // hack to mark link off-line
849 BasicLinkConfig lnkCfg = networkConfigService
850 .getConfig(linkKey(packetSrc, packetDst),
851 BasicLinkConfig.class);
852 lnkCfg.isAllowed(false);
853 lnkCfg.apply();
854 } catch (Exception ex) {
855 log.error("Applying BasicLinkConfig failed", ex);
856 }
857
858 networkConfigService
859 .removeConfig(linkKey(packetSrc, packetDst),
860 BasicLinkConfig.class);
861 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700862 }
863
864
865 private class InternalLinkListener implements LinkListener {
866
867 @Override
868 public void event(LinkEvent event) {
869 switch (event.type()) {
870 case LINK_REMOVED:
871 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700872 // updates linkPathMap only if src device of link is local
873 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
874 return;
875 }
876
877 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700878 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
879 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
880 .collect(Collectors.toSet());
881
882 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700883 // remove found packet links from distributed store
884 linkPathMap.computeIfPresent(l, (plink, conn) -> {
885 // Notifies listeners if all packet links are gone
886 if (conn.isAllRealizingLinkNotEstablished()) {
887 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
888 }
889 return null;
890 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700891 });
892 default:
893 break;
894 }
895 }
896 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700897
898 private class InternalStoreListener
899 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
900
901 @Override
902 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
903 switch (event.type()) {
904 case UPDATE:
905 OpticalConnectivity oldConnectivity = event.oldValue().value();
906 OpticalConnectivity newConnectivity = event.newValue().value();
907
908 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
909 newConnectivity.isAllRealizingLinkEstablished()) {
910 // Notifies listeners if all links are established
911 updateBandwidthUsage(newConnectivity);
912 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
913 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
914 newConnectivity.isAllRealizingLinkNotEstablished()) {
915 // Notifies listeners if all links are gone
916 releaseBandwidthUsage(newConnectivity);
917 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
918 }
919
920 break;
921 default:
922 break;
923 }
924 }
925
926 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700927}
928