blob: 7ef2c00c04ceff31e5fe2c2b69f82325b627dcee [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;
Naoki Shiota5a056062016-05-05 18:43:59 -070060import org.onosproject.net.resource.ContinuousResource;
61import org.onosproject.net.resource.Resource;
62import org.onosproject.net.resource.ResourceAllocation;
63import org.onosproject.net.resource.ResourceService;
64import org.onosproject.net.resource.Resources;
65import org.onosproject.net.topology.LinkWeight;
66import org.onosproject.net.topology.PathService;
67import org.onosproject.net.topology.TopologyEdge;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070068import org.onosproject.store.serializers.KryoNamespaces;
Naoki Shiota5a056062016-05-05 18:43:59 -070069import org.onosproject.store.service.AtomicCounter;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070070import org.onosproject.store.service.ConsistentMap;
71import org.onosproject.store.service.DistributedSet;
72import org.onosproject.store.service.MapEvent;
73import org.onosproject.store.service.MapEventListener;
74import org.onosproject.store.service.Serializer;
Naoki Shiota5a056062016-05-05 18:43:59 -070075import org.onosproject.store.service.StorageService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070076import org.onosproject.store.service.Versioned;
Naoki Shiota5a056062016-05-05 18:43:59 -070077import org.slf4j.Logger;
78import org.slf4j.LoggerFactory;
79
80import java.time.Duration;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070081import java.util.ArrayList;
Naoki Shiota5a056062016-05-05 18:43:59 -070082import java.util.Collections;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070083import java.util.HashMap;
84import java.util.HashSet;
Naoki Shiota5a056062016-05-05 18:43:59 -070085import java.util.Iterator;
86import java.util.LinkedList;
87import java.util.List;
88import java.util.Map;
89import java.util.Optional;
90import java.util.Set;
Naoki Shiota5a056062016-05-05 18:43:59 -070091import java.util.stream.Collectors;
92import java.util.stream.Stream;
93
94import static com.google.common.base.Preconditions.checkArgument;
95import static com.google.common.base.Preconditions.checkNotNull;
Naoki Shiotae1366ad2016-05-11 19:24:11 -070096import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Naoki Shiota5a056062016-05-05 18:43:59 -070097
98/**
99 * Main component to configure optical connectivity.
100 */
101@Beta
102@Service
103@Component(immediate = true)
104public class OpticalPathProvisioner
105 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
106 implements OpticalPathService {
107 protected static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
108
109 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700110 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
111 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
112 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected IntentService intentService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected PathService pathService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected LinkService linkService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected MastershipService mastershipService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected ClusterService clusterService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected DeviceService deviceService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected StorageService storageService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected NetworkConfigService networkConfigService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected ResourceService resourceService;
143
144
145 private ApplicationId appId;
146
147 private AtomicCounter idCounter;
148
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700149 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700150
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700151 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700152
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800153 /**
154 * Map from packet-layer link expected to be realized by some optical Intent to
155 * OpticalConnectivity (~=top level intent over multi-layer topology).
156 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700157 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700158
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700159 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
160
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800161 // FIXME in the long run. This is effectively app's own resource subsystem
162 /**
163 * Set of cross connect link currently used.
164 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700165 private DistributedSet<Link> usedCrossConnectLinkSet;
166
167 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
168 .register(KryoNamespaces.API)
169 .register(PacketLinkRealizedByOptical.class)
170 .register(OpticalConnectivityId.class)
171 .register(OpticalConnectivity.class);
172
173 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
174 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700175 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700176 .register(OpticalConnectivityId.class)
177 .register(OpticalConnectivity.class);
178
179 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
180 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700181
182 @Activate
183 protected void activate() {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700184 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700185 appId = coreService.registerApplication("org.onosproject.newoptical");
186
Naoki Shiota03c29e12016-05-16 16:58:07 -0700187 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700188
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700189 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
190 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
191 .withName(LINKPATH_MAP_NAME)
192 .withApplicationId(appId)
193 .build();
194
195 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
196 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
197 .withName(CONNECTIVITY_MAP_NAME)
198 .withApplicationId(appId)
199 .build();
200
201 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
202 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
203 .withName(CROSSCONNECTLINK_SET_NAME)
204 .withApplicationId(appId)
205 .build()
206 .asDistributedSet();
207
Naoki Shiota5a056062016-05-05 18:43:59 -0700208 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700209
210 listeners = new ListenerTracker();
211 listeners.addListener(linkService, new InternalLinkListener())
212 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700213
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700214 linkPathMap.addListener(storeListener);
215
Naoki Shiota5a056062016-05-05 18:43:59 -0700216 log.info("Started");
217 }
218
219 @Deactivate
220 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700221 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700222 listeners.removeListeners();
223 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700224
225 log.info("Stopped");
226 }
227
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800228 /*
229 * Request packet-layer connectivity between specified ports,
230 * over packet-optical multi-layer infrastructure.
231 *
232 * Functionality-wise this is effectively submitting Packet-Optical
233 * multi-layer P2P Intent.
234 *
235 * It computes multi-layer path meeting specified constraint,
236 * and calls setupPath.
237 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700238 @Override
239 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
240 Bandwidth bandwidth, Duration latency) {
241 checkNotNull(ingress);
242 checkNotNull(egress);
243 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
244
245 bandwidth = (bandwidth == null) ? Bandwidth.bps(0) : bandwidth;
246
247 Set<Path> paths = pathService.getPaths(ingress.deviceId(), egress.deviceId(),
248 new BandwidthLinkWeight(bandwidth));
249 if (paths.isEmpty()) {
250 log.warn("Unable to find multi-layer path.");
251 return null;
252 }
253
254 // Search path with available cross connect points
255 for (Path path : paths) {
256 OpticalConnectivityId id = setupPath(path, bandwidth, latency);
257 if (id != null) {
258 log.info("Assigned OpticalConnectivityId: {}", id);
259 return id;
260 }
261 }
262
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800263 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700264
Naoki Shiota5a056062016-05-05 18:43:59 -0700265 return null;
266 }
267
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800268 /*
269 * Given a multi-layer path,
270 * compute a set of segments which requires
271 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
272 * to provide packet-layer connectivity.
273 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700274 @Override
275 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
276 checkNotNull(path);
277 log.info("setupPath({}, {}, {})", path, bandwidth, latency);
278
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700279 // map of cross connect points (optical port -> packet port)
280 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
281
282 // list of (src, dst) pair of optical ports between which optical path should be installed
283 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
284
285 // Scan path to find pairs of connect points between which optical intent is installed
286 // opticalSrcPort works as a flag parameter to show scanning status
287 ConnectPoint opticalSrcPort = null;
288 for (Link link : path.links()) {
289 if (!isCrossConnectLink(link)) {
290 continue;
291 }
292
293 if (opticalSrcPort != null) {
294 // opticalSrcPort!=null means src port was already found
295 // in this case link.src() is optical layer, and link.dst() is packet layer
296
297 // Check if types of src port and dst port matches
298 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
299 "Unknown device ID");
300 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
301 "Unknown device ID");
302 if (srcDevice.type() != dstDevice.type()) {
303 log.error("Unsupported mix of cross connect points : {}, {}",
304 srcDevice.type(), dstDevice.type());
305 return null;
306 }
307
308 // Update cross connect points map
309 crossConnectPointMap.put(link.src(), link.dst());
310
311 // Add optical ports pair to list
312 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
313
314 // Reset flag parameter
315 opticalSrcPort = null;
316 } else {
317 // opticalSrcPort==null means src port was not found yet
318 // in this case link.src() is packet layer, and link.dst() is optical layer
319
320 // Update cross connect points map
321 crossConnectPointMap.put(link.dst(), link.src());
322 // Set opticalSrcPort to src of link (optical port)
323 opticalSrcPort = link.dst();
324 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700325 }
326
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700327 // create intents from cross connect points
328 List<Intent> intents = createIntents(crossConnectPoints);
Naoki Shiota5a056062016-05-05 18:43:59 -0700329
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700330 // create set of PacketLinkRealizedByOptical
331 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
332 intents, crossConnectPointMap);
333
334 // create OpticalConnectivity object and store information to distributed store
335 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700336
337 // store cross connect port usage
338 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700339 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700340
341 // Submit the intents
342 for (Intent i : intents) {
343 intentService.submit(i);
344 log.debug("Submitted an intent: {}", i);
345 }
346
347 return connectivity.id();
348 }
349
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700350 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
351 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700352 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700353 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
354 links, Collections.emptySet());
355
356 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700357
Naoki Shiota5a056062016-05-05 18:43:59 -0700358 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700359 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700360
361 return connectivity;
362 }
363
364 @Override
365 public boolean removeConnectivity(OpticalConnectivityId id) {
366 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700367 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700368
369 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700370 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700371 return false;
372 }
373
374 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700375 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700376 Intent intent = intentService.getIntent(l.realizingIntentKey());
377 intentService.withdraw(intent);
378 });
379
380 return true;
381 }
382
383 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700384 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700385 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700386 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700387 log.info("OpticalConnectivity with id {} not found.", id);
388 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700389 }
390
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700391 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700392 }
393
394 /**
395 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700396 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700397 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700398 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700399 * @return list of optical connectivity intents
400 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700401 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700402 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700403 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700404
405 while (itr.hasNext()) {
406 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700407 Pair<ConnectPoint, ConnectPoint> next = itr.next();
408 ConnectPoint src = next.getLeft();
409 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700410
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700411 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
412 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700413
414 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700415 OduCltPort srcOCPort = (OduCltPort) srcPort;
416 OduCltPort dstOCPort = (OduCltPort) dstPort;
417 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
418 continue;
419 }
420
Naoki Shiota5a056062016-05-05 18:43:59 -0700421 // Create OTN circuit
422 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
423 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700424 .src(src)
425 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700426 .signalType(srcOCPort.signalType())
Naoki Shiota5a056062016-05-05 18:43:59 -0700427 .bidirectional(true)
428 .build();
429 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700430 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700431 OchPort srcOchPort = (OchPort) srcPort;
432 OchPort dstOchPort = (OchPort) dstPort;
433 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
434 continue;
435 }
436
Naoki Shiota5a056062016-05-05 18:43:59 -0700437 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700438 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
439 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700440 .src(src)
441 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700442 .signalType(srcOchPort.signalType())
Naoki Shiota5a056062016-05-05 18:43:59 -0700443 .bidirectional(true)
444 .build();
445 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700446 } else {
447 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
448 return Collections.emptyList();
449 }
450 }
451
452 return intents;
453 }
454
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700455 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
456 List<Intent> intents,
457 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
458 checkArgument(connectPoints.size() == intents.size());
459
460 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
461
462 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
463 Iterator<Intent> intentItr = intents.iterator();
464 while (xcPointsItr.hasNext()) {
465 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
466 Intent intent = intentItr.next();
467
468 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
469 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
470
471 if (intent instanceof OpticalConnectivityIntent) {
472 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
473 (OpticalConnectivityIntent) intent));
474 } else if (intent instanceof OpticalCircuitIntent) {
475 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
476 (OpticalCircuitIntent) intent));
477 } else {
478 log.warn("Unexpected intent type: {}", intent.getClass());
479 }
480 }
481
482 return pLinks;
483 }
484
Naoki Shiota5a056062016-05-05 18:43:59 -0700485 /**
486 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
487 *
488 * @param type device type
489 * @return true if in packet layer, false otherwise
490 */
491 private boolean isPacketLayer(Device.Type type) {
492 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
493 }
494
495 /**
496 * Verifies if given device type is in packet layer, i.e., switch or router device.
497 *
498 * @param type device type
499 * @return true if in packet layer, false otherwise
500 */
501 private boolean isTransportLayer(Device.Type type) {
502 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
503 }
504
505 /**
506 * Verifies if given link forms a cross-connection between packet and optical layer.
507 *
508 * @param link the link
509 * @return true if the link is a cross-connect link, false otherwise
510 */
511 private boolean isCrossConnectLink(Link link) {
512 if (link.type() != Link.Type.OPTICAL) {
513 return false;
514 }
515
516 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
517 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
518
519 return src != dst &&
520 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
521 }
522
523 /**
524 * Updates bandwidth resource of given connect point.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700525 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700526 * @param cp Connect point
527 * @param bandwidth New bandwidth
528 */
529 private void updatePortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700530 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
531 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
532 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700533 }
534
535 /**
536 * Updates usage information of bandwidth based on connectivity which is established.
537 * @param connectivity Optical connectivity
538 */
539 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiotacb744db2016-05-13 19:32:35 -0700540 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700541
542 List<Link> links = connectivity.links();
543
544 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
545 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
546 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
547 Bandwidth.class).resource(connectivity.bandwidth().bps()))
548 .collect(Collectors.toList());
549
Naoki Shiotacb744db2016-05-13 19:32:35 -0700550 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
551 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700552 if (allocations.isEmpty()) {
553 log.warn("Failed to allocate bandwidth {} to {}",
554 connectivity.bandwidth().bps(), resources);
555 // TODO any recovery?
556 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700557 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700558 }
559
560 /**
561 * Release bandwidth allocated by given connectivity.
562 * @param connectivity Optical connectivity
563 */
564 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700565 if (connectivity.links().isEmpty()) {
566 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700567 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700568
569 // release resource only if this node is the master for link head device
570 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
571 OpticalConnectivityId connectivityId = connectivity.id();
572
573 log.debug("releasing bandwidth allocated to {}", connectivityId);
574 if (!resourceService.release(connectivityId)) {
575 log.warn("Failed to release bandwidth allocated to {}",
576 connectivityId);
577 // TODO any recovery?
578 }
579 log.debug("DONE releasing bandwidth for {}", connectivityId);
580 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700581 }
582
583 private class BandwidthLinkWeight implements LinkWeight {
584 private Bandwidth bandwidth = null;
585
586 public BandwidthLinkWeight(Bandwidth bandwidth) {
587 this.bandwidth = bandwidth;
588 }
589
590 @Override
591 public double weight(TopologyEdge edge) {
592 Link l = edge.link();
593
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700594 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700595 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800596 log.trace("{} is not active", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700597 return -1.0;
598 }
599
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700600 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700601 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800602 log.trace("Cross connect {} in use", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700603 return -1.0;
604 }
605
606 // Check availability of bandwidth
607 if (bandwidth != null) {
608 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
609 return 1.0;
610 } else {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800611 log.trace("Not enought bandwidth on {}", l);
Naoki Shiota5a056062016-05-05 18:43:59 -0700612 return -1.0;
613 }
614 } else {
615 // TODO needs to differentiate optical and packet?
616 if (l.type() == Link.Type.OPTICAL) {
617 // Transport links
618 return 1.0;
619 } else {
620 // Packet links
621 return 1.0;
622 }
623 }
624 }
625
626 private boolean hasEnoughBandwidth(ConnectPoint cp) {
627 if (cp.elementId() instanceof DeviceId) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700628 Device device = deviceService.getDevice(cp.deviceId());
629 Device.Type type = device.type();
630
Naoki Shiota5a056062016-05-05 18:43:59 -0700631 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700632 // Check if the port has enough capacity
633 Port port = deviceService.getPort(cp.deviceId(), cp.port());
634 if (port instanceof OduCltPort || port instanceof OchPort) {
635 // Port with capacity
636 return bandwidth.bps() < port.portSpeed() * 1000000.0;
637 } else {
638 // Port without valid capacity (OMS port, etc.)
639 return true;
640 }
641 } else {
642 // Check if enough amount of bandwidth resource remains
643 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
644 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800645 try {
646 return resourceService.isAvailable(resource);
647 } catch (Exception e) {
648 log.error("Resource service failed checking availability of {}",
649 resource, e);
650 throw e;
651 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700652 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700653 }
654 return false;
655 }
656 }
657
658
659 public class InternalIntentListener implements IntentListener {
660 @Override
661 public void event(IntentEvent event) {
662 switch (event.type()) {
663 case INSTALLED:
664 log.info("Intent {} installed.", event.subject());
665 updateCrossConnectLink(event.subject());
666 break;
667 case WITHDRAWN:
668 log.info("Intent {} withdrawn.", event.subject());
669 removeCrossConnectLinks(event.subject());
670 break;
671 case FAILED:
672 log.info("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800673 // TODO If it was one of it's own optical Intent,
674 // update link state
675 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700676 break;
677 default:
678 break;
679 }
680 }
681
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800682 // TODO rename "CrossConnectLink"?
683 /**
684 * Update packet-layer link/port state once Intent is installed.
685 *
686 * @param intent which reached installed state
687 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700688 private void updateCrossConnectLink(Intent intent) {
689 linkPathMap.entrySet().stream()
690 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
691 .forEach(e -> {
692 ConnectPoint packetSrc = e.getKey().src();
693 ConnectPoint packetDst = e.getKey().dst();
694 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700695
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700696 // reflect modification only if packetSrc is local_
697 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
698 // Updates bandwidth of packet ports
699 updatePortBandwidth(packetSrc, bw);
700 updatePortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700701
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700702 // Updates link status in distributed map
703 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
704 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800705
706 // TODO inject expected link or durable link
707 // if packet device cannot advertise packet link
Naoki Shiota5a056062016-05-05 18:43:59 -0700708 }
709 });
710 }
711
712 private void removeCrossConnectLinks(Intent intent) {
713 ConnectPoint src, dst;
714
715 if (intent instanceof OpticalCircuitIntent) {
716 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
717 src = circuit.getSrc();
718 dst = circuit.getDst();
719 } else if (intent instanceof OpticalConnectivityIntent) {
720 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
721 src = conn.getSrc();
722 dst = conn.getDst();
723 } else {
724 return;
725 }
726
727 removeXcLinkUsage(src);
728 removeXcLinkUsage(dst);
729
730 // Set bandwidth of 0 to cross connect ports
731 Bandwidth bw = Bandwidth.bps(0);
732 linkPathMap.entrySet().stream()
733 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
734 .forEach(e -> {
735 ConnectPoint packetSrc = e.getKey().src();
736 ConnectPoint packetDst = e.getKey().dst();
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
741 updatePortBandwidth(packetSrc, bw);
742 updatePortBandwidth(packetDst, bw);
743
744 // Updates link status in distributed map
745 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
746 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800747
748 // TODO remove expected link or durable link
749 // if packet device cannot monitor packet link
Naoki Shiota5a056062016-05-05 18:43:59 -0700750 }
751 });
752 }
753
754 private void removeXcLinkUsage(ConnectPoint cp) {
755 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700756 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700757 .findAny();
758
759 if (!link.isPresent()) {
760 log.warn("Cross connect point {} has no cross connect link.", cp);
761 return;
762 }
763
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700764 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700765 }
766 }
767
768
769 private class InternalLinkListener implements LinkListener {
770
771 @Override
772 public void event(LinkEvent event) {
773 switch (event.type()) {
774 case LINK_REMOVED:
775 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700776 // updates linkPathMap only if src device of link is local
777 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
778 return;
779 }
780
781 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700782 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
783 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
784 .collect(Collectors.toSet());
785
786 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700787 // remove found packet links from distributed store
788 linkPathMap.computeIfPresent(l, (plink, conn) -> {
789 // Notifies listeners if all packet links are gone
790 if (conn.isAllRealizingLinkNotEstablished()) {
791 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
792 }
793 return null;
794 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700795 });
796 default:
797 break;
798 }
799 }
800 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700801
802 private class InternalStoreListener
803 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
804
805 @Override
806 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
807 switch (event.type()) {
808 case UPDATE:
809 OpticalConnectivity oldConnectivity = event.oldValue().value();
810 OpticalConnectivity newConnectivity = event.newValue().value();
811
812 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
813 newConnectivity.isAllRealizingLinkEstablished()) {
814 // Notifies listeners if all links are established
815 updateBandwidthUsage(newConnectivity);
816 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
817 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
818 newConnectivity.isAllRealizingLinkNotEstablished()) {
819 // Notifies listeners if all links are gone
820 releaseBandwidthUsage(newConnectivity);
821 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
822 }
823
824 break;
825 default:
826 break;
827 }
828 }
829
830 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700831}
832