blob: ca2d3decba4d2d6b8e0382eba006539254e97865 [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;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
Naoki Shiota5a056062016-05-05 18:43:59 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070029import org.onlab.graph.DefaultEdgeWeigher;
30import org.onlab.graph.ScalarWeight;
31import org.onlab.graph.Weight;
Naoki Shiota5a056062016-05-05 18:43:59 -070032import org.onlab.util.Bandwidth;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070033import org.onlab.util.KryoNamespace;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070034import org.onlab.util.Tools;
Naoki Shiota5a056062016-05-05 18:43:59 -070035import org.onosproject.cluster.ClusterService;
Naoki Shiota5a056062016-05-05 18:43:59 -070036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
Naoki Shiota5a056062016-05-05 18:43:59 -070038import org.onosproject.event.AbstractListenerManager;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070039import org.onosproject.event.ListenerTracker;
Naoki Shiota5a056062016-05-05 18:43:59 -070040import org.onosproject.mastership.MastershipService;
Naoki Shiota5a056062016-05-05 18:43:59 -070041import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.Device;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.Link;
Yuta HIGUCHI21033042017-05-04 17:58:24 -070045import org.onosproject.net.LinkKey;
Naoki Shiota5a056062016-05-05 18:43:59 -070046import org.onosproject.net.Path;
47import org.onosproject.net.Port;
48import org.onosproject.net.config.NetworkConfigService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070049import org.onosproject.net.config.basics.BandwidthCapacity;
50import org.onosproject.net.config.basics.BasicLinkConfig;
Naoki Shiota5a056062016-05-05 18:43:59 -070051import org.onosproject.net.device.DeviceService;
52import org.onosproject.net.intent.Intent;
53import org.onosproject.net.intent.IntentEvent;
Naoki Shiota5a056062016-05-05 18:43:59 -070054import org.onosproject.net.intent.IntentListener;
55import org.onosproject.net.intent.IntentService;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -070056import org.onosproject.net.intent.Key;
Naoki Shiota5a056062016-05-05 18:43:59 -070057import org.onosproject.net.intent.OpticalCircuitIntent;
58import org.onosproject.net.intent.OpticalConnectivityIntent;
Naoki Shiota5a056062016-05-05 18:43:59 -070059import org.onosproject.net.link.LinkEvent;
60import org.onosproject.net.link.LinkListener;
61import org.onosproject.net.link.LinkService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070062import org.onosproject.net.optical.OchPort;
63import org.onosproject.net.optical.OduCltPort;
Naoki Shiota5a056062016-05-05 18:43:59 -070064import org.onosproject.net.resource.ContinuousResource;
65import org.onosproject.net.resource.Resource;
66import org.onosproject.net.resource.ResourceAllocation;
67import org.onosproject.net.resource.ResourceService;
68import org.onosproject.net.resource.Resources;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070069import org.onosproject.net.topology.LinkWeigher;
Naoki Shiota5a056062016-05-05 18:43:59 -070070import org.onosproject.net.topology.TopologyEdge;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070071import org.onosproject.net.topology.TopologyService;
72import org.onosproject.net.topology.TopologyVertex;
73import org.onosproject.newoptical.api.OpticalConnectivityId;
74import org.onosproject.newoptical.api.OpticalPathEvent;
75import org.onosproject.newoptical.api.OpticalPathListener;
76import org.onosproject.newoptical.api.OpticalPathService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070077import org.onosproject.store.serializers.KryoNamespaces;
Naoki Shiota5a056062016-05-05 18:43:59 -070078import org.onosproject.store.service.AtomicCounter;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070079import org.onosproject.store.service.ConsistentMap;
80import org.onosproject.store.service.DistributedSet;
81import org.onosproject.store.service.MapEvent;
82import org.onosproject.store.service.MapEventListener;
83import org.onosproject.store.service.Serializer;
Naoki Shiota5a056062016-05-05 18:43:59 -070084import org.onosproject.store.service.StorageService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070085import org.onosproject.store.service.Versioned;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070086import org.osgi.service.component.ComponentContext;
Naoki Shiota5a056062016-05-05 18:43:59 -070087import org.slf4j.Logger;
88import org.slf4j.LoggerFactory;
89
90import java.time.Duration;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070091import java.util.ArrayList;
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -080092import java.util.Collection;
Naoki Shiota5a056062016-05-05 18:43:59 -070093import java.util.Collections;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070094import java.util.Dictionary;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070095import java.util.HashMap;
96import java.util.HashSet;
Naoki Shiota5a056062016-05-05 18:43:59 -070097import java.util.Iterator;
98import java.util.LinkedList;
99import java.util.List;
100import java.util.Map;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700101import java.util.Map.Entry;
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700102import java.util.Objects;
Naoki Shiota5a056062016-05-05 18:43:59 -0700103import java.util.Optional;
104import java.util.Set;
Naoki Shiota5a056062016-05-05 18:43:59 -0700105import java.util.stream.Collectors;
106import java.util.stream.Stream;
107
108import static com.google.common.base.Preconditions.checkArgument;
109import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800110import static org.onosproject.net.LinkKey.linkKey;
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700111import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Naoki Shiota5a056062016-05-05 18:43:59 -0700112
113/**
114 * Main component to configure optical connectivity.
115 */
116@Beta
117@Service
118@Component(immediate = true)
119public class OpticalPathProvisioner
120 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
121 implements OpticalPathService {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800122
Naoki Shiota5a056062016-05-05 18:43:59 -0700123 protected static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
124
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800125 /**
126 * Bandwidth representing no bandwidth requirement specified.
127 */
128 private static final Bandwidth NO_BW_REQUIREMENT = Bandwidth.bps(0);
129
Naoki Shiota5a056062016-05-05 18:43:59 -0700130 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700131 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
132 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
133 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected IntentService intentService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700139 protected TopologyService topologyService;
Naoki Shiota5a056062016-05-05 18:43:59 -0700140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected CoreService coreService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected LinkService linkService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected MastershipService mastershipService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected ClusterService clusterService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected DeviceService deviceService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected StorageService storageService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected NetworkConfigService networkConfigService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected ResourceService resourceService;
164
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700165 private static final String MAX_PATHS = "maxPaths";
166 private static final int DEFAULT_MAX_PATHS = 10;
167 @Property(name = MAX_PATHS, intValue = DEFAULT_MAX_PATHS,
168 label = "Maximum number of paths to consider for path provisioning")
169 private int maxPaths = DEFAULT_MAX_PATHS;
Naoki Shiota5a056062016-05-05 18:43:59 -0700170
171 private ApplicationId appId;
172
173 private AtomicCounter idCounter;
174
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700175 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700176
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700177 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700178
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800179 /**
180 * Map from packet-layer link expected to be realized by some optical Intent to
181 * OpticalConnectivity (~=top level intent over multi-layer topology).
182 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700183 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700184
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700185 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
186
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800187 // FIXME in the long run. This is effectively app's own resource subsystem
188 /**
189 * Set of cross connect link currently used.
190 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700191 private DistributedSet<Link> usedCrossConnectLinkSet;
192
193 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
194 .register(KryoNamespaces.API)
195 .register(PacketLinkRealizedByOptical.class)
196 .register(OpticalConnectivityId.class)
197 .register(OpticalConnectivity.class);
198
199 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
200 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700201 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700202 .register(OpticalConnectivityId.class)
203 .register(OpticalConnectivity.class);
204
205 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
206 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700207
208 @Activate
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700209 protected void activate(ComponentContext context) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700210 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700211 appId = coreService.registerApplication("org.onosproject.newoptical");
212
Naoki Shiota03c29e12016-05-16 16:58:07 -0700213 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700214
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700215 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
216 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
217 .withName(LINKPATH_MAP_NAME)
218 .withApplicationId(appId)
219 .build();
220
221 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
222 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
223 .withName(CONNECTIVITY_MAP_NAME)
224 .withApplicationId(appId)
225 .build();
226
227 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
228 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
229 .withName(CROSSCONNECTLINK_SET_NAME)
230 .withApplicationId(appId)
231 .build()
232 .asDistributedSet();
233
Naoki Shiota5a056062016-05-05 18:43:59 -0700234 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700235
236 listeners = new ListenerTracker();
237 listeners.addListener(linkService, new InternalLinkListener())
238 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700239
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700240 linkPathMap.addListener(storeListener);
241
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700242 readComponentConfiguration(context);
243
Naoki Shiota5a056062016-05-05 18:43:59 -0700244 log.info("Started");
245 }
246
247 @Deactivate
248 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700249 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700250 listeners.removeListeners();
251 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700252
253 log.info("Stopped");
254 }
255
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700256 @Modified
257 public void modified(ComponentContext context) {
258 readComponentConfiguration(context);
259 }
260
261 /**
262 * Extracts properties from the component configuration context.
263 *
264 * @param context the component context
265 */
266 private void readComponentConfiguration(ComponentContext context) {
267 Dictionary<?, ?> properties = context.getProperties();
268 maxPaths = Tools.getIntegerProperty(properties, MAX_PATHS, DEFAULT_MAX_PATHS);
269 log.info("Configured. Maximum paths to consider is configured to {}", maxPaths);
270 }
271
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800272 @Override
273 public Collection<OpticalConnectivity> listConnectivity() {
274 return connectivityMap.values().stream()
275 .map(Versioned::value)
Yuta HIGUCHI498fa1d2017-05-17 16:08:40 -0700276 .collect(ImmutableList.toImmutableList());
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800277 }
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700278
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700279 @Override
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700280 public Set<Key> listIntents(OpticalConnectivityId id) {
281 return linkPathMap.entrySet().stream()
282 .filter(ent -> id.equals(ent.getValue().value().id()))
283 .map(Entry::getKey)
284 .map(PacketLinkRealizedByOptical::realizingIntentKey)
285 .collect(Collectors.toSet());
286 }
287
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800288 /*
289 * Request packet-layer connectivity between specified ports,
290 * over packet-optical multi-layer infrastructure.
291 *
292 * Functionality-wise this is effectively submitting Packet-Optical
293 * multi-layer P2P Intent.
294 *
295 * It computes multi-layer path meeting specified constraint,
296 * and calls setupPath.
297 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700298 @Override
299 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
300 Bandwidth bandwidth, Duration latency) {
301 checkNotNull(ingress);
302 checkNotNull(egress);
303 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
304
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700305 Bandwidth bw = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
Naoki Shiota5a056062016-05-05 18:43:59 -0700306
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700307 Stream<Path> paths = topologyService.getKShortestPaths(
308 topologyService.currentTopology(),
309 ingress.deviceId(), egress.deviceId(),
Naoki Shiota5a056062016-05-05 18:43:59 -0700310 new BandwidthLinkWeight(bandwidth));
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700311
312 // Path service calculates from node to node, we're only interested in port to port
313 Optional<OpticalConnectivityId> id =
314 paths.filter(p -> p.src().equals(ingress) && p.dst().equals(egress))
315 .limit(maxPaths)
316 .map(p -> setupPath(p, bw, latency))
317 .filter(Objects::nonNull)
318 .findFirst();
319
320 if (id.isPresent()) {
321 log.info("Assigned OpticalConnectivityId: {}", id);
322 } else {
323 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700324 }
325
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700326 return id.orElse(null);
Naoki Shiota5a056062016-05-05 18:43:59 -0700327 }
328
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800329 /*
330 * Given a multi-layer path,
331 * compute a set of segments which requires
332 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
333 * to provide packet-layer connectivity.
334 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700335 @Override
336 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
337 checkNotNull(path);
Yuta HIGUCHI8fc32f82017-03-14 22:12:42 -0700338 log.debug("setupPath({}, {}, {})", path, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700339
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700340 // map of cross connect points (optical port -> packet port)
341 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
342
343 // list of (src, dst) pair of optical ports between which optical path should be installed
344 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
345
346 // Scan path to find pairs of connect points between which optical intent is installed
347 // opticalSrcPort works as a flag parameter to show scanning status
348 ConnectPoint opticalSrcPort = null;
349 for (Link link : path.links()) {
350 if (!isCrossConnectLink(link)) {
351 continue;
352 }
353
354 if (opticalSrcPort != null) {
355 // opticalSrcPort!=null means src port was already found
356 // in this case link.src() is optical layer, and link.dst() is packet layer
357
358 // Check if types of src port and dst port matches
359 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
360 "Unknown device ID");
361 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
362 "Unknown device ID");
363 if (srcDevice.type() != dstDevice.type()) {
364 log.error("Unsupported mix of cross connect points : {}, {}",
365 srcDevice.type(), dstDevice.type());
366 return null;
367 }
368
369 // Update cross connect points map
370 crossConnectPointMap.put(link.src(), link.dst());
371
372 // Add optical ports pair to list
373 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
374
375 // Reset flag parameter
376 opticalSrcPort = null;
377 } else {
378 // opticalSrcPort==null means src port was not found yet
379 // in this case link.src() is packet layer, and link.dst() is optical layer
380
381 // Update cross connect points map
382 crossConnectPointMap.put(link.dst(), link.src());
383 // Set opticalSrcPort to src of link (optical port)
384 opticalSrcPort = link.dst();
385 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700386 }
387
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700388 // create intents from cross connect points
389 List<Intent> intents = createIntents(crossConnectPoints);
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800390 if (intents.isEmpty()) {
391 log.error("No intents produced from {}", crossConnectPoints);
392 return null;
393 }
394
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700395 // create set of PacketLinkRealizedByOptical
396 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
397 intents, crossConnectPointMap);
398
399 // create OpticalConnectivity object and store information to distributed store
400 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700401
402 // store cross connect port usage
403 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700404 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700405
406 // Submit the intents
407 for (Intent i : intents) {
408 intentService.submit(i);
409 log.debug("Submitted an intent: {}", i);
410 }
411
412 return connectivity.id();
413 }
414
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700415 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
416 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700417 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700418 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
419 links, Collections.emptySet());
420
421 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700422
Naoki Shiota5a056062016-05-05 18:43:59 -0700423 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700424 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700425
426 return connectivity;
427 }
428
429 @Override
430 public boolean removeConnectivity(OpticalConnectivityId id) {
431 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700432 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700433
434 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700435 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700436 return false;
437 }
438
439 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700440 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700441 Intent intent = intentService.getIntent(l.realizingIntentKey());
442 intentService.withdraw(intent);
443 });
444
445 return true;
446 }
447
448 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700449 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700450 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700451 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700452 log.info("OpticalConnectivity with id {} not found.", id);
453 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700454 }
455
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700456 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700457 }
458
459 /**
460 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700461 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700462 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700463 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700464 * @return list of optical connectivity intents
465 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700466 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700467 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700468 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700469
470 while (itr.hasNext()) {
471 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700472 Pair<ConnectPoint, ConnectPoint> next = itr.next();
473 ConnectPoint src = next.getLeft();
474 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700475
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700476 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
477 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700478
479 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700480 OduCltPort srcOCPort = (OduCltPort) srcPort;
481 OduCltPort dstOCPort = (OduCltPort) dstPort;
482 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
483 continue;
484 }
485
Naoki Shiota5a056062016-05-05 18:43:59 -0700486 // Create OTN circuit
487 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
488 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700489 .src(src)
490 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700491 .signalType(srcOCPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700492 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700493 .build();
494 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700495 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700496 OchPort srcOchPort = (OchPort) srcPort;
497 OchPort dstOchPort = (OchPort) dstPort;
498 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
499 continue;
500 }
501
Naoki Shiota5a056062016-05-05 18:43:59 -0700502 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700503 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
504 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700505 .src(src)
506 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700507 .signalType(srcOchPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700508 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700509 .build();
510 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700511 } else {
512 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
513 return Collections.emptyList();
514 }
515 }
516
517 return intents;
518 }
519
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700520 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
521 List<Intent> intents,
522 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
523 checkArgument(connectPoints.size() == intents.size());
524
525 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
526
527 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
528 Iterator<Intent> intentItr = intents.iterator();
529 while (xcPointsItr.hasNext()) {
530 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
531 Intent intent = intentItr.next();
532
533 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
534 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
535
536 if (intent instanceof OpticalConnectivityIntent) {
537 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
538 (OpticalConnectivityIntent) intent));
539 } else if (intent instanceof OpticalCircuitIntent) {
540 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
541 (OpticalCircuitIntent) intent));
542 } else {
543 log.warn("Unexpected intent type: {}", intent.getClass());
544 }
545 }
546
547 return pLinks;
548 }
549
Naoki Shiota5a056062016-05-05 18:43:59 -0700550 /**
551 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
552 *
553 * @param type device type
554 * @return true if in packet layer, false otherwise
555 */
556 private boolean isPacketLayer(Device.Type type) {
557 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
558 }
559
560 /**
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800561 * Verifies if given device type is NOT in packet layer, i.e., switch or router device.
Naoki Shiota5a056062016-05-05 18:43:59 -0700562 *
563 * @param type device type
564 * @return true if in packet layer, false otherwise
565 */
566 private boolean isTransportLayer(Device.Type type) {
567 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
568 }
569
570 /**
571 * Verifies if given link forms a cross-connection between packet and optical layer.
572 *
573 * @param link the link
574 * @return true if the link is a cross-connect link, false otherwise
575 */
576 private boolean isCrossConnectLink(Link link) {
577 if (link.type() != Link.Type.OPTICAL) {
578 return false;
579 }
580
581 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
582 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
583
584 return src != dst &&
585 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
586 }
587
588 /**
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800589 * Updates bandwidth resource of given connect point to specified value.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700590 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700591 * @param cp Connect point
592 * @param bandwidth New bandwidth
593 */
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800594 private void setPortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700595 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
596 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
597 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700598 }
599
600 /**
601 * Updates usage information of bandwidth based on connectivity which is established.
602 * @param connectivity Optical connectivity
603 */
604 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800605 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
606 // no bandwidth requirement, nothing to allocate.
607 return;
608 }
609
Naoki Shiotacb744db2016-05-13 19:32:35 -0700610 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700611
612 List<Link> links = connectivity.links();
613
614 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
615 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
616 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
617 Bandwidth.class).resource(connectivity.bandwidth().bps()))
618 .collect(Collectors.toList());
619
Naoki Shiotacb744db2016-05-13 19:32:35 -0700620 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
621 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700622 if (allocations.isEmpty()) {
623 log.warn("Failed to allocate bandwidth {} to {}",
624 connectivity.bandwidth().bps(), resources);
625 // TODO any recovery?
626 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700627 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700628 }
629
630 /**
631 * Release bandwidth allocated by given connectivity.
632 * @param connectivity Optical connectivity
633 */
634 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700635 if (connectivity.links().isEmpty()) {
636 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700637 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800638 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
639 // no bandwidth requirement, nothing to release.
640 return;
641 }
642
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700643
644 // release resource only if this node is the master for link head device
645 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
646 OpticalConnectivityId connectivityId = connectivity.id();
647
648 log.debug("releasing bandwidth allocated to {}", connectivityId);
649 if (!resourceService.release(connectivityId)) {
650 log.warn("Failed to release bandwidth allocated to {}",
651 connectivityId);
652 // TODO any recovery?
653 }
654 log.debug("DONE releasing bandwidth for {}", connectivityId);
655 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700656 }
657
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800658 private boolean linkDiscoveryEnabled(ConnectPoint cp) {
659 // FIXME should check Device feature and configuration state.
660
661 // short-term hack for ONS'17 time-frame,
662 // only expect OF device to have link discovery.
Jon Halla3fcf672017-03-28 16:53:22 -0700663 return "of".equals(cp.deviceId().uri().getScheme());
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800664 }
665
666 /**
667 * Returns true if both connect point support for link discovery & enabled.
668 *
669 * @param cp1 port 1
670 * @param cp2 port 2
671 * @return true if both connect point support for link discovery & enabled.
672 */
673 private boolean linkDiscoveryEnabled(ConnectPoint cp1, ConnectPoint cp2) {
674 return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
675 }
676
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700677 private class BandwidthLinkWeight extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge> implements LinkWeigher {
Naoki Shiota5a056062016-05-05 18:43:59 -0700678 private Bandwidth bandwidth = null;
679
680 public BandwidthLinkWeight(Bandwidth bandwidth) {
681 this.bandwidth = bandwidth;
682 }
683
684 @Override
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700685 public Weight weight(TopologyEdge edge) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700686 Link l = edge.link();
687
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700688 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700689 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800690 log.trace("{} is not active", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700691 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700692 }
693
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700694 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700695 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800696 log.trace("Cross connect {} in use", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700697 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700698 }
699
700 // Check availability of bandwidth
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800701 if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700702 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700703 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700704 } else {
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700705 log.trace("Not enough bandwidth on {}", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700706 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700707 }
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700708 // Allow everything else
Naoki Shiota5a056062016-05-05 18:43:59 -0700709 } else {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700710 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700711 }
712 }
713
714 private boolean hasEnoughBandwidth(ConnectPoint cp) {
715 if (cp.elementId() instanceof DeviceId) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700716 Device device = deviceService.getDevice(cp.deviceId());
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700717 Device.Type type = device.type();
718
Naoki Shiota5a056062016-05-05 18:43:59 -0700719 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700720 // Check if the port has enough capacity
721 Port port = deviceService.getPort(cp.deviceId(), cp.port());
722 if (port instanceof OduCltPort || port instanceof OchPort) {
723 // Port with capacity
724 return bandwidth.bps() < port.portSpeed() * 1000000.0;
725 } else {
726 // Port without valid capacity (OMS port, etc.)
727 return true;
728 }
729 } else {
730 // Check if enough amount of bandwidth resource remains
731 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
732 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800733 try {
734 return resourceService.isAvailable(resource);
735 } catch (Exception e) {
736 log.error("Resource service failed checking availability of {}",
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700737 resource, e);
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800738 throw e;
739 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700740 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700741 }
742 return false;
743 }
744 }
745
Naoki Shiota5a056062016-05-05 18:43:59 -0700746 public class InternalIntentListener implements IntentListener {
747 @Override
748 public void event(IntentEvent event) {
749 switch (event.type()) {
750 case INSTALLED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700751 log.debug("Intent {} installed.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700752 updateCrossConnectLink(event.subject());
753 break;
754 case WITHDRAWN:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700755 log.debug("Intent {} withdrawn.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700756 removeCrossConnectLinks(event.subject());
757 break;
758 case FAILED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700759 log.debug("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800760 // TODO If it was one of it's own optical Intent,
761 // update link state
762 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700763 break;
764 default:
765 break;
766 }
767 }
768
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800769 // TODO rename "CrossConnectLink"?
770 /**
771 * Update packet-layer link/port state once Intent is installed.
772 *
773 * @param intent which reached installed state
774 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700775 private void updateCrossConnectLink(Intent intent) {
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();
781 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700782
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700783 // reflect modification only if packetSrc is local_
784 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
785 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800786 setPortBandwidth(packetSrc, bw);
787 setPortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700788
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700789 // Updates link status in distributed map
790 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
791 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800792
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800793
794 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
795 injectLink(packetSrc, packetDst);
796 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700797 }
798 });
799 }
800
801 private void removeCrossConnectLinks(Intent intent) {
802 ConnectPoint src, dst;
803
804 if (intent instanceof OpticalCircuitIntent) {
805 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
806 src = circuit.getSrc();
807 dst = circuit.getDst();
808 } else if (intent instanceof OpticalConnectivityIntent) {
809 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
810 src = conn.getSrc();
811 dst = conn.getDst();
812 } else {
813 return;
814 }
815
816 removeXcLinkUsage(src);
817 removeXcLinkUsage(dst);
818
819 // Set bandwidth of 0 to cross connect ports
820 Bandwidth bw = Bandwidth.bps(0);
821 linkPathMap.entrySet().stream()
822 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
823 .forEach(e -> {
824 ConnectPoint packetSrc = e.getKey().src();
825 ConnectPoint packetDst = e.getKey().dst();
Naoki Shiota5a056062016-05-05 18:43:59 -0700826
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700827 // reflect modification only if packetSrc is local_
828 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
829 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800830 setPortBandwidth(packetSrc, bw);
831 setPortBandwidth(packetDst, bw);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700832
833 // Updates link status in distributed map
834 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
835 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800836
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800837
838 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
839 removeInjectedLink(packetSrc, packetDst);
840 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700841 }
842 });
843 }
844
845 private void removeXcLinkUsage(ConnectPoint cp) {
846 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700847 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700848 .findAny();
849
850 if (!link.isPresent()) {
Yuta HIGUCHIe2689ee2017-05-04 16:53:18 -0700851 log.warn("Cross connect point {} has no cross connect link to release.", cp);
Naoki Shiota5a056062016-05-05 18:43:59 -0700852 return;
853 }
854
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700855 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700856 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800857
858 /**
859 * Injects link between specified packet port.
860 *
861 * @param packetSrc port 1
862 * @param packetDst port 2
863 */
864 private void injectLink(ConnectPoint packetSrc,
865 ConnectPoint packetDst) {
866 // inject expected link or durable link
867 // if packet device cannot advertise packet link
868 try {
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700869 // cannot call addConfig.
870 // it will create default BasicLinkConfig,
871 // which will end up advertising DIRECT links and
872 // DIRECT Link type cannot transition from DIRECT to INDIRECT
873 LinkKey lnkKey = linkKey(packetSrc, packetDst);
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800874 BasicLinkConfig lnkCfg = networkConfigService
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700875 .getConfig(lnkKey, BasicLinkConfig.class);
876 if (lnkCfg == null) {
877 lnkCfg = new BasicLinkConfig(lnkKey);
878 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800879 lnkCfg.isAllowed(true);
880 lnkCfg.isDurable(true);
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700881 lnkCfg.type(Link.Type.INDIRECT);
882 lnkCfg.isBidirectional(false);
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700883 // cannot call apply against manually created instance
884 //lnkCfg.apply();
885 networkConfigService.applyConfig(lnkKey,
886 BasicLinkConfig.class,
887 lnkCfg.node());
888
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800889 } catch (Exception ex) {
890 log.error("Applying BasicLinkConfig failed", ex);
891 }
892 }
893
894 /**
895 * Removes link injected between specified packet port.
896 *
897 * @param packetSrc port 1
898 * @param packetDst port 2
899 */
900 private void removeInjectedLink(ConnectPoint packetSrc,
901 ConnectPoint packetDst) {
902 // remove expected link or durable link
903 // if packet device cannot monitor packet link
904
905 try {
906 // hack to mark link off-line
907 BasicLinkConfig lnkCfg = networkConfigService
908 .getConfig(linkKey(packetSrc, packetDst),
909 BasicLinkConfig.class);
910 lnkCfg.isAllowed(false);
911 lnkCfg.apply();
912 } catch (Exception ex) {
913 log.error("Applying BasicLinkConfig failed", ex);
914 }
915
916 networkConfigService
917 .removeConfig(linkKey(packetSrc, packetDst),
918 BasicLinkConfig.class);
919 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700920 }
921
922
923 private class InternalLinkListener implements LinkListener {
924
925 @Override
926 public void event(LinkEvent event) {
927 switch (event.type()) {
928 case LINK_REMOVED:
929 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700930 // updates linkPathMap only if src device of link is local
931 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
932 return;
933 }
934
935 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700936 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
937 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
938 .collect(Collectors.toSet());
939
940 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700941 // remove found packet links from distributed store
942 linkPathMap.computeIfPresent(l, (plink, conn) -> {
943 // Notifies listeners if all packet links are gone
944 if (conn.isAllRealizingLinkNotEstablished()) {
945 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
946 }
947 return null;
948 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700949 });
950 default:
951 break;
952 }
953 }
954 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700955
956 private class InternalStoreListener
957 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
958
959 @Override
960 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
961 switch (event.type()) {
962 case UPDATE:
963 OpticalConnectivity oldConnectivity = event.oldValue().value();
964 OpticalConnectivity newConnectivity = event.newValue().value();
965
966 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
967 newConnectivity.isAllRealizingLinkEstablished()) {
968 // Notifies listeners if all links are established
969 updateBandwidthUsage(newConnectivity);
970 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
971 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
972 newConnectivity.isAllRealizingLinkNotEstablished()) {
973 // Notifies listeners if all links are gone
974 releaseBandwidthUsage(newConnectivity);
975 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
976 }
977
978 break;
979 default:
980 break;
981 }
982 }
983
984 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700985}
986