blob: 245a90f570b9ec6bd1ac6bf00699007b4d1a8feb [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 -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;
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -080033import org.onlab.util.GuavaCollectors;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070034import org.onlab.util.KryoNamespace;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070035import org.onlab.util.Tools;
Naoki Shiota5a056062016-05-05 18:43:59 -070036import org.onosproject.cluster.ClusterService;
Naoki Shiota5a056062016-05-05 18:43:59 -070037import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
Naoki Shiota5a056062016-05-05 18:43:59 -070039import org.onosproject.event.AbstractListenerManager;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070040import org.onosproject.event.ListenerTracker;
Naoki Shiota5a056062016-05-05 18:43:59 -070041import 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;
Yuta HIGUCHI21033042017-05-04 17:58:24 -070046import org.onosproject.net.LinkKey;
Naoki Shiota5a056062016-05-05 18:43:59 -070047import org.onosproject.net.Path;
48import org.onosproject.net.Port;
49import org.onosproject.net.config.NetworkConfigService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070050import org.onosproject.net.config.basics.BandwidthCapacity;
51import org.onosproject.net.config.basics.BasicLinkConfig;
Naoki Shiota5a056062016-05-05 18:43:59 -070052import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.intent.Intent;
54import org.onosproject.net.intent.IntentEvent;
Naoki Shiota5a056062016-05-05 18:43:59 -070055import org.onosproject.net.intent.IntentListener;
56import org.onosproject.net.intent.IntentService;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -070057import org.onosproject.net.intent.Key;
Naoki Shiota5a056062016-05-05 18:43:59 -070058import org.onosproject.net.intent.OpticalCircuitIntent;
59import org.onosproject.net.intent.OpticalConnectivityIntent;
Naoki Shiota5a056062016-05-05 18:43:59 -070060import org.onosproject.net.link.LinkEvent;
61import org.onosproject.net.link.LinkListener;
62import org.onosproject.net.link.LinkService;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070063import org.onosproject.net.optical.OchPort;
64import org.onosproject.net.optical.OduCltPort;
Naoki Shiota5a056062016-05-05 18:43:59 -070065import org.onosproject.net.resource.ContinuousResource;
66import org.onosproject.net.resource.Resource;
67import org.onosproject.net.resource.ResourceAllocation;
68import org.onosproject.net.resource.ResourceService;
69import org.onosproject.net.resource.Resources;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070070import org.onosproject.net.topology.LinkWeigher;
Naoki Shiota5a056062016-05-05 18:43:59 -070071import org.onosproject.net.topology.TopologyEdge;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070072import org.onosproject.net.topology.TopologyService;
73import org.onosproject.net.topology.TopologyVertex;
74import org.onosproject.newoptical.api.OpticalConnectivityId;
75import org.onosproject.newoptical.api.OpticalPathEvent;
76import org.onosproject.newoptical.api.OpticalPathListener;
77import org.onosproject.newoptical.api.OpticalPathService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070078import org.onosproject.store.serializers.KryoNamespaces;
Naoki Shiota5a056062016-05-05 18:43:59 -070079import org.onosproject.store.service.AtomicCounter;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070080import org.onosproject.store.service.ConsistentMap;
81import org.onosproject.store.service.DistributedSet;
82import org.onosproject.store.service.MapEvent;
83import org.onosproject.store.service.MapEventListener;
84import org.onosproject.store.service.Serializer;
Naoki Shiota5a056062016-05-05 18:43:59 -070085import org.onosproject.store.service.StorageService;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070086import org.onosproject.store.service.Versioned;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070087import org.osgi.service.component.ComponentContext;
Naoki Shiota5a056062016-05-05 18:43:59 -070088import org.slf4j.Logger;
89import org.slf4j.LoggerFactory;
90
91import java.time.Duration;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070092import java.util.ArrayList;
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -080093import java.util.Collection;
Naoki Shiota5a056062016-05-05 18:43:59 -070094import java.util.Collections;
Marc De Leenheer552eeb62017-05-15 17:43:27 -070095import java.util.Dictionary;
Naoki Shiota7c3111b2016-06-09 16:12:11 -070096import java.util.HashMap;
97import java.util.HashSet;
Naoki Shiota5a056062016-05-05 18:43:59 -070098import java.util.Iterator;
99import java.util.LinkedList;
100import java.util.List;
101import java.util.Map;
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700102import java.util.Map.Entry;
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700103import java.util.Objects;
Naoki Shiota5a056062016-05-05 18:43:59 -0700104import java.util.Optional;
105import java.util.Set;
Naoki Shiota5a056062016-05-05 18:43:59 -0700106import java.util.stream.Collectors;
107import java.util.stream.Stream;
108
109import static com.google.common.base.Preconditions.checkArgument;
110import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800111import static org.onosproject.net.LinkKey.linkKey;
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700112import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Naoki Shiota5a056062016-05-05 18:43:59 -0700113
114/**
115 * Main component to configure optical connectivity.
116 */
117@Beta
118@Service
119@Component(immediate = true)
120public class OpticalPathProvisioner
121 extends AbstractListenerManager<OpticalPathEvent, OpticalPathListener>
122 implements OpticalPathService {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800123
Naoki Shiota5a056062016-05-05 18:43:59 -0700124 protected static final Logger log = LoggerFactory.getLogger(OpticalPathProvisioner.class);
125
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800126 /**
127 * Bandwidth representing no bandwidth requirement specified.
128 */
129 private static final Bandwidth NO_BW_REQUIREMENT = Bandwidth.bps(0);
130
Naoki Shiota5a056062016-05-05 18:43:59 -0700131 private static final String OPTICAL_CONNECTIVITY_ID_COUNTER = "optical-connectivity-id";
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700132 private static final String LINKPATH_MAP_NAME = "newoptical-linkpath";
133 private static final String CONNECTIVITY_MAP_NAME = "newoptical-connectivity";
134 private static final String CROSSCONNECTLINK_SET_NAME = "newoptical-crossconnectlink";
Naoki Shiota5a056062016-05-05 18:43:59 -0700135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected IntentService intentService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700140 protected TopologyService topologyService;
Naoki Shiota5a056062016-05-05 18:43:59 -0700141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected CoreService coreService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected LinkService linkService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected MastershipService mastershipService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected ClusterService clusterService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected DeviceService deviceService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected StorageService storageService;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected NetworkConfigService networkConfigService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected ResourceService resourceService;
165
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700166 private static final String MAX_PATHS = "maxPaths";
167 private static final int DEFAULT_MAX_PATHS = 10;
168 @Property(name = MAX_PATHS, intValue = DEFAULT_MAX_PATHS,
169 label = "Maximum number of paths to consider for path provisioning")
170 private int maxPaths = DEFAULT_MAX_PATHS;
Naoki Shiota5a056062016-05-05 18:43:59 -0700171
172 private ApplicationId appId;
173
174 private AtomicCounter idCounter;
175
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700176 private ListenerTracker listeners;
Naoki Shiota5a056062016-05-05 18:43:59 -0700177
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700178 private InternalStoreListener storeListener = new InternalStoreListener();
Naoki Shiota5a056062016-05-05 18:43:59 -0700179
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800180 /**
181 * Map from packet-layer link expected to be realized by some optical Intent to
182 * OpticalConnectivity (~=top level intent over multi-layer topology).
183 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700184 private ConsistentMap<PacketLinkRealizedByOptical, OpticalConnectivity> linkPathMap;
Naoki Shiota5a056062016-05-05 18:43:59 -0700185
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700186 private ConsistentMap<OpticalConnectivityId, OpticalConnectivity> connectivityMap;
187
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800188 // FIXME in the long run. This is effectively app's own resource subsystem
189 /**
190 * Set of cross connect link currently used.
191 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700192 private DistributedSet<Link> usedCrossConnectLinkSet;
193
194 private static final KryoNamespace.Builder LINKPATH_SERIALIZER = KryoNamespace.newBuilder()
195 .register(KryoNamespaces.API)
196 .register(PacketLinkRealizedByOptical.class)
197 .register(OpticalConnectivityId.class)
198 .register(OpticalConnectivity.class);
199
200 private static final KryoNamespace.Builder CONNECTIVITY_SERIALIZER = KryoNamespace.newBuilder()
201 .register(KryoNamespaces.API)
Yuta HIGUCHI0086cf82016-07-18 22:49:45 -0700202 .register(PacketLinkRealizedByOptical.class)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700203 .register(OpticalConnectivityId.class)
204 .register(OpticalConnectivity.class);
205
206 private static final KryoNamespace.Builder CROSSCONNECTLINKS_SERIALIZER = KryoNamespace.newBuilder()
207 .register(KryoNamespaces.API);
Naoki Shiota5a056062016-05-05 18:43:59 -0700208
209 @Activate
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700210 protected void activate(ComponentContext context) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700211 deviceService = opticalView(deviceService);
Naoki Shiota5a056062016-05-05 18:43:59 -0700212 appId = coreService.registerApplication("org.onosproject.newoptical");
213
Naoki Shiota03c29e12016-05-16 16:58:07 -0700214 idCounter = storageService.getAtomicCounter(OPTICAL_CONNECTIVITY_ID_COUNTER);
Naoki Shiota5a056062016-05-05 18:43:59 -0700215
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700216 linkPathMap = storageService.<PacketLinkRealizedByOptical, OpticalConnectivity>consistentMapBuilder()
217 .withSerializer(Serializer.using(LINKPATH_SERIALIZER.build()))
218 .withName(LINKPATH_MAP_NAME)
219 .withApplicationId(appId)
220 .build();
221
222 connectivityMap = storageService.<OpticalConnectivityId, OpticalConnectivity>consistentMapBuilder()
223 .withSerializer(Serializer.using(CONNECTIVITY_SERIALIZER.build()))
224 .withName(CONNECTIVITY_MAP_NAME)
225 .withApplicationId(appId)
226 .build();
227
228 usedCrossConnectLinkSet = storageService.<Link>setBuilder()
229 .withSerializer(Serializer.using(CROSSCONNECTLINKS_SERIALIZER.build()))
230 .withName(CROSSCONNECTLINK_SET_NAME)
231 .withApplicationId(appId)
232 .build()
233 .asDistributedSet();
234
Naoki Shiota5a056062016-05-05 18:43:59 -0700235 eventDispatcher.addSink(OpticalPathEvent.class, listenerRegistry);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700236
237 listeners = new ListenerTracker();
238 listeners.addListener(linkService, new InternalLinkListener())
239 .addListener(intentService, new InternalIntentListener());
Naoki Shiota5a056062016-05-05 18:43:59 -0700240
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700241 linkPathMap.addListener(storeListener);
242
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700243 readComponentConfiguration(context);
244
Naoki Shiota5a056062016-05-05 18:43:59 -0700245 log.info("Started");
246 }
247
248 @Deactivate
249 protected void deactivate() {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700250 linkPathMap.removeListener(storeListener);
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700251 listeners.removeListeners();
252 eventDispatcher.removeSink(OpticalPathEvent.class);
Naoki Shiota5a056062016-05-05 18:43:59 -0700253
254 log.info("Stopped");
255 }
256
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700257 @Modified
258 public void modified(ComponentContext context) {
259 readComponentConfiguration(context);
260 }
261
262 /**
263 * Extracts properties from the component configuration context.
264 *
265 * @param context the component context
266 */
267 private void readComponentConfiguration(ComponentContext context) {
268 Dictionary<?, ?> properties = context.getProperties();
269 maxPaths = Tools.getIntegerProperty(properties, MAX_PATHS, DEFAULT_MAX_PATHS);
270 log.info("Configured. Maximum paths to consider is configured to {}", maxPaths);
271 }
272
Yuta HIGUCHIf167bf92017-03-08 13:19:04 -0800273 @Override
274 public Collection<OpticalConnectivity> listConnectivity() {
275 return connectivityMap.values().stream()
276 .map(Versioned::value)
277 .collect(GuavaCollectors.toImmutableList());
278 }
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700279
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700280 @Override
Yuta HIGUCHI8ac05092017-03-15 00:41:22 -0700281 public Set<Key> listIntents(OpticalConnectivityId id) {
282 return linkPathMap.entrySet().stream()
283 .filter(ent -> id.equals(ent.getValue().value().id()))
284 .map(Entry::getKey)
285 .map(PacketLinkRealizedByOptical::realizingIntentKey)
286 .collect(Collectors.toSet());
287 }
288
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800289 /*
290 * Request packet-layer connectivity between specified ports,
291 * over packet-optical multi-layer infrastructure.
292 *
293 * Functionality-wise this is effectively submitting Packet-Optical
294 * multi-layer P2P Intent.
295 *
296 * It computes multi-layer path meeting specified constraint,
297 * and calls setupPath.
298 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700299 @Override
300 public OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
301 Bandwidth bandwidth, Duration latency) {
302 checkNotNull(ingress);
303 checkNotNull(egress);
304 log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
305
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700306 Bandwidth bw = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
Naoki Shiota5a056062016-05-05 18:43:59 -0700307
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700308 Stream<Path> paths = topologyService.getKShortestPaths(
309 topologyService.currentTopology(),
310 ingress.deviceId(), egress.deviceId(),
Naoki Shiota5a056062016-05-05 18:43:59 -0700311 new BandwidthLinkWeight(bandwidth));
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700312
313 // Path service calculates from node to node, we're only interested in port to port
314 Optional<OpticalConnectivityId> id =
315 paths.filter(p -> p.src().equals(ingress) && p.dst().equals(egress))
316 .limit(maxPaths)
317 .map(p -> setupPath(p, bw, latency))
318 .filter(Objects::nonNull)
319 .findFirst();
320
321 if (id.isPresent()) {
322 log.info("Assigned OpticalConnectivityId: {}", id);
323 } else {
324 log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700325 }
326
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700327 return id.orElse(null);
Naoki Shiota5a056062016-05-05 18:43:59 -0700328 }
329
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800330 /*
331 * Given a multi-layer path,
332 * compute a set of segments which requires
333 * OpticalConnectivity(~=OpticalConnectivityIntent or OpticalCircuitPath)
334 * to provide packet-layer connectivity.
335 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700336 @Override
337 public OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
338 checkNotNull(path);
Yuta HIGUCHI8fc32f82017-03-14 22:12:42 -0700339 log.debug("setupPath({}, {}, {})", path, bandwidth, latency);
Naoki Shiota5a056062016-05-05 18:43:59 -0700340
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700341 // map of cross connect points (optical port -> packet port)
342 Map<ConnectPoint, ConnectPoint> crossConnectPointMap = new HashMap<>();
343
344 // list of (src, dst) pair of optical ports between which optical path should be installed
345 List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints = new ArrayList<>();
346
347 // Scan path to find pairs of connect points between which optical intent is installed
348 // opticalSrcPort works as a flag parameter to show scanning status
349 ConnectPoint opticalSrcPort = null;
350 for (Link link : path.links()) {
351 if (!isCrossConnectLink(link)) {
352 continue;
353 }
354
355 if (opticalSrcPort != null) {
356 // opticalSrcPort!=null means src port was already found
357 // in this case link.src() is optical layer, and link.dst() is packet layer
358
359 // Check if types of src port and dst port matches
360 Device srcDevice = checkNotNull(deviceService.getDevice(opticalSrcPort.deviceId()),
361 "Unknown device ID");
362 Device dstDevice = checkNotNull(deviceService.getDevice(link.src().deviceId()),
363 "Unknown device ID");
364 if (srcDevice.type() != dstDevice.type()) {
365 log.error("Unsupported mix of cross connect points : {}, {}",
366 srcDevice.type(), dstDevice.type());
367 return null;
368 }
369
370 // Update cross connect points map
371 crossConnectPointMap.put(link.src(), link.dst());
372
373 // Add optical ports pair to list
374 crossConnectPoints.add(Pair.of(opticalSrcPort, link.src()));
375
376 // Reset flag parameter
377 opticalSrcPort = null;
378 } else {
379 // opticalSrcPort==null means src port was not found yet
380 // in this case link.src() is packet layer, and link.dst() is optical layer
381
382 // Update cross connect points map
383 crossConnectPointMap.put(link.dst(), link.src());
384 // Set opticalSrcPort to src of link (optical port)
385 opticalSrcPort = link.dst();
386 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700387 }
388
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700389 // create intents from cross connect points
390 List<Intent> intents = createIntents(crossConnectPoints);
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800391 if (intents.isEmpty()) {
392 log.error("No intents produced from {}", crossConnectPoints);
393 return null;
394 }
395
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700396 // create set of PacketLinkRealizedByOptical
397 Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
398 intents, crossConnectPointMap);
399
400 // create OpticalConnectivity object and store information to distributed store
401 OpticalConnectivity connectivity = createConnectivity(path, bandwidth, latency, packetLinks);
Naoki Shiota5a056062016-05-05 18:43:59 -0700402
403 // store cross connect port usage
404 path.links().stream().filter(this::isCrossConnectLink)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700405 .forEach(usedCrossConnectLinkSet::add);
Naoki Shiota5a056062016-05-05 18:43:59 -0700406
407 // Submit the intents
408 for (Intent i : intents) {
409 intentService.submit(i);
410 log.debug("Submitted an intent: {}", i);
411 }
412
413 return connectivity.id();
414 }
415
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700416 private OpticalConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency,
417 Set<PacketLinkRealizedByOptical> links) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700418 OpticalConnectivityId id = OpticalConnectivityId.of(idCounter.getAndIncrement());
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700419 OpticalConnectivity connectivity = new OpticalConnectivity(id, path.links(), bandwidth, latency,
420 links, Collections.emptySet());
421
422 links.forEach(l -> linkPathMap.put(l, connectivity));
Naoki Shiota5a056062016-05-05 18:43:59 -0700423
Naoki Shiota5a056062016-05-05 18:43:59 -0700424 // store connectivity information
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700425 connectivityMap.put(connectivity.id(), connectivity);
Naoki Shiota5a056062016-05-05 18:43:59 -0700426
427 return connectivity;
428 }
429
430 @Override
431 public boolean removeConnectivity(OpticalConnectivityId id) {
432 log.info("removeConnectivity({})", id);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700433 Versioned<OpticalConnectivity> connectivity = connectivityMap.remove(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700434
435 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700436 log.info("OpticalConnectivity with id {} not found.", id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700437 return false;
438 }
439
440 // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700441 connectivity.value().getRealizingLinks().forEach(l -> {
Naoki Shiota5a056062016-05-05 18:43:59 -0700442 Intent intent = intentService.getIntent(l.realizingIntentKey());
443 intentService.withdraw(intent);
444 });
445
446 return true;
447 }
448
449 @Override
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700450 public Optional<List<Link>> getPath(OpticalConnectivityId id) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700451 Versioned<OpticalConnectivity> connectivity = connectivityMap.get(id);
Naoki Shiota5a056062016-05-05 18:43:59 -0700452 if (connectivity == null) {
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700453 log.info("OpticalConnectivity with id {} not found.", id);
454 return Optional.empty();
Naoki Shiota5a056062016-05-05 18:43:59 -0700455 }
456
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700457 return Optional.of(ImmutableList.copyOf(connectivity.value().links()));
Naoki Shiota5a056062016-05-05 18:43:59 -0700458 }
459
460 /**
461 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700462 * During the process, save information about packet links to given set.
Naoki Shiota5a056062016-05-05 18:43:59 -0700463 *
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700464 * @param crossConnectPoints list of (src, dst) pair between which optical path will be set up
Naoki Shiota5a056062016-05-05 18:43:59 -0700465 * @return list of optical connectivity intents
466 */
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700467 private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> crossConnectPoints) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700468 List<Intent> intents = new LinkedList<>();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700469 Iterator<Pair<ConnectPoint, ConnectPoint>> itr = crossConnectPoints.iterator();
Naoki Shiota5a056062016-05-05 18:43:59 -0700470
471 while (itr.hasNext()) {
472 // checkArgument at start ensures we'll always have pairs of connect points
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700473 Pair<ConnectPoint, ConnectPoint> next = itr.next();
474 ConnectPoint src = next.getLeft();
475 ConnectPoint dst = next.getRight();
Naoki Shiota5a056062016-05-05 18:43:59 -0700476
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700477 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
478 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Naoki Shiota5a056062016-05-05 18:43:59 -0700479
480 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700481 OduCltPort srcOCPort = (OduCltPort) srcPort;
482 OduCltPort dstOCPort = (OduCltPort) dstPort;
483 if (!srcOCPort.signalType().equals(dstOCPort.signalType())) {
484 continue;
485 }
486
Naoki Shiota5a056062016-05-05 18:43:59 -0700487 // Create OTN circuit
488 OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
489 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700490 .src(src)
491 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700492 .signalType(srcOCPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700493 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700494 .build();
495 intents.add(circuitIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700496 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700497 OchPort srcOchPort = (OchPort) srcPort;
498 OchPort dstOchPort = (OchPort) dstPort;
499 if (!srcOchPort.signalType().equals(dstOchPort.signalType())) {
500 continue;
501 }
502
Naoki Shiota5a056062016-05-05 18:43:59 -0700503 // Create lightpath
Naoki Shiota5a056062016-05-05 18:43:59 -0700504 OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
505 .appId(appId)
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700506 .src(src)
507 .dst(dst)
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700508 .signalType(srcOchPort.signalType())
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700509 .bidirectional(false)
Naoki Shiota5a056062016-05-05 18:43:59 -0700510 .build();
511 intents.add(opticalIntent);
Naoki Shiota5a056062016-05-05 18:43:59 -0700512 } else {
513 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
514 return Collections.emptyList();
515 }
516 }
517
518 return intents;
519 }
520
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700521 private Set<PacketLinkRealizedByOptical> createPacketLinkSet(List<Pair<ConnectPoint, ConnectPoint>> connectPoints,
522 List<Intent> intents,
523 Map<ConnectPoint, ConnectPoint> crossConnectPoints) {
524 checkArgument(connectPoints.size() == intents.size());
525
526 Set<PacketLinkRealizedByOptical> pLinks = new HashSet<>();
527
528 Iterator<Pair<ConnectPoint, ConnectPoint>> xcPointsItr = connectPoints.iterator();
529 Iterator<Intent> intentItr = intents.iterator();
530 while (xcPointsItr.hasNext()) {
531 Pair<ConnectPoint, ConnectPoint> xcPoints = xcPointsItr.next();
532 Intent intent = intentItr.next();
533
534 ConnectPoint packetSrc = checkNotNull(crossConnectPoints.get(xcPoints.getLeft()));
535 ConnectPoint packetDst = checkNotNull(crossConnectPoints.get(xcPoints.getRight()));
536
537 if (intent instanceof OpticalConnectivityIntent) {
538 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
539 (OpticalConnectivityIntent) intent));
540 } else if (intent instanceof OpticalCircuitIntent) {
541 pLinks.add(PacketLinkRealizedByOptical.create(packetSrc, packetDst,
542 (OpticalCircuitIntent) intent));
543 } else {
544 log.warn("Unexpected intent type: {}", intent.getClass());
545 }
546 }
547
548 return pLinks;
549 }
550
Naoki Shiota5a056062016-05-05 18:43:59 -0700551 /**
552 * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
553 *
554 * @param type device type
555 * @return true if in packet layer, false otherwise
556 */
557 private boolean isPacketLayer(Device.Type type) {
558 return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
559 }
560
561 /**
Yuta HIGUCHId2f041e2017-03-10 12:45:15 -0800562 * Verifies if given device type is NOT in packet layer, i.e., switch or router device.
Naoki Shiota5a056062016-05-05 18:43:59 -0700563 *
564 * @param type device type
565 * @return true if in packet layer, false otherwise
566 */
567 private boolean isTransportLayer(Device.Type type) {
568 return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
569 }
570
571 /**
572 * Verifies if given link forms a cross-connection between packet and optical layer.
573 *
574 * @param link the link
575 * @return true if the link is a cross-connect link, false otherwise
576 */
577 private boolean isCrossConnectLink(Link link) {
578 if (link.type() != Link.Type.OPTICAL) {
579 return false;
580 }
581
582 Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
583 Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
584
585 return src != dst &&
586 ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
587 }
588
589 /**
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800590 * Updates bandwidth resource of given connect point to specified value.
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700591 *
Naoki Shiota5a056062016-05-05 18:43:59 -0700592 * @param cp Connect point
593 * @param bandwidth New bandwidth
594 */
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800595 private void setPortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700596 log.debug("update Port {} Bandwidth {}", cp, bandwidth);
597 BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
598 bwCapacity.capacity(bandwidth).apply();
Naoki Shiota5a056062016-05-05 18:43:59 -0700599 }
600
601 /**
602 * Updates usage information of bandwidth based on connectivity which is established.
603 * @param connectivity Optical connectivity
604 */
605 private void updateBandwidthUsage(OpticalConnectivity connectivity) {
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800606 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
607 // no bandwidth requirement, nothing to allocate.
608 return;
609 }
610
Naoki Shiotacb744db2016-05-13 19:32:35 -0700611 OpticalConnectivityId connectivityId = connectivity.id();
Naoki Shiota5a056062016-05-05 18:43:59 -0700612
613 List<Link> links = connectivity.links();
614
615 List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
616 .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
617 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
618 Bandwidth.class).resource(connectivity.bandwidth().bps()))
619 .collect(Collectors.toList());
620
Naoki Shiotacb744db2016-05-13 19:32:35 -0700621 log.debug("allocating bandwidth for {} : {}", connectivityId, resources);
622 List<ResourceAllocation> allocations = resourceService.allocate(connectivityId, resources);
Naoki Shiota5a056062016-05-05 18:43:59 -0700623 if (allocations.isEmpty()) {
624 log.warn("Failed to allocate bandwidth {} to {}",
625 connectivity.bandwidth().bps(), resources);
626 // TODO any recovery?
627 }
Naoki Shiotacb744db2016-05-13 19:32:35 -0700628 log.debug("Done allocating bandwidth for {}", connectivityId);
Naoki Shiota5a056062016-05-05 18:43:59 -0700629 }
630
631 /**
632 * Release bandwidth allocated by given connectivity.
633 * @param connectivity Optical connectivity
634 */
635 private void releaseBandwidthUsage(OpticalConnectivity connectivity) {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700636 if (connectivity.links().isEmpty()) {
637 return;
Naoki Shiota5a056062016-05-05 18:43:59 -0700638 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800639 if (NO_BW_REQUIREMENT.equals(connectivity.bandwidth())) {
640 // no bandwidth requirement, nothing to release.
641 return;
642 }
643
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700644
645 // release resource only if this node is the master for link head device
646 if (mastershipService.isLocalMaster(connectivity.links().get(0).src().deviceId())) {
647 OpticalConnectivityId connectivityId = connectivity.id();
648
649 log.debug("releasing bandwidth allocated to {}", connectivityId);
650 if (!resourceService.release(connectivityId)) {
651 log.warn("Failed to release bandwidth allocated to {}",
652 connectivityId);
653 // TODO any recovery?
654 }
655 log.debug("DONE releasing bandwidth for {}", connectivityId);
656 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700657 }
658
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800659 private boolean linkDiscoveryEnabled(ConnectPoint cp) {
660 // FIXME should check Device feature and configuration state.
661
662 // short-term hack for ONS'17 time-frame,
663 // only expect OF device to have link discovery.
Jon Halla3fcf672017-03-28 16:53:22 -0700664 return "of".equals(cp.deviceId().uri().getScheme());
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800665 }
666
667 /**
668 * Returns true if both connect point support for link discovery & enabled.
669 *
670 * @param cp1 port 1
671 * @param cp2 port 2
672 * @return true if both connect point support for link discovery & enabled.
673 */
674 private boolean linkDiscoveryEnabled(ConnectPoint cp1, ConnectPoint cp2) {
675 return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
676 }
677
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700678 private class BandwidthLinkWeight extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge> implements LinkWeigher {
Naoki Shiota5a056062016-05-05 18:43:59 -0700679 private Bandwidth bandwidth = null;
680
681 public BandwidthLinkWeight(Bandwidth bandwidth) {
682 this.bandwidth = bandwidth;
683 }
684
685 @Override
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700686 public Weight weight(TopologyEdge edge) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700687 Link l = edge.link();
688
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700689 // Avoid inactive links
Naoki Shiota5a056062016-05-05 18:43:59 -0700690 if (l.state() == Link.State.INACTIVE) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800691 log.trace("{} is not active", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700692 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700693 }
694
Naoki Shiota0d2943e2016-05-13 18:53:21 -0700695 // Avoid cross connect links with used ports
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700696 if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800697 log.trace("Cross connect {} in use", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700698 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700699 }
700
701 // Check availability of bandwidth
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800702 if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
Naoki Shiota5a056062016-05-05 18:43:59 -0700703 if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700704 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700705 } else {
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700706 log.trace("Not enough bandwidth on {}", l);
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700707 return ScalarWeight.NON_VIABLE_WEIGHT;
Naoki Shiota5a056062016-05-05 18:43:59 -0700708 }
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700709 // Allow everything else
Naoki Shiota5a056062016-05-05 18:43:59 -0700710 } else {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700711 return new ScalarWeight(1.0);
Naoki Shiota5a056062016-05-05 18:43:59 -0700712 }
713 }
714
715 private boolean hasEnoughBandwidth(ConnectPoint cp) {
716 if (cp.elementId() instanceof DeviceId) {
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700717 Device device = deviceService.getDevice(cp.deviceId());
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700718 Device.Type type = device.type();
719
Naoki Shiota5a056062016-05-05 18:43:59 -0700720 if (isTransportLayer(type)) {
Naoki Shiotae1366ad2016-05-11 19:24:11 -0700721 // Check if the port has enough capacity
722 Port port = deviceService.getPort(cp.deviceId(), cp.port());
723 if (port instanceof OduCltPort || port instanceof OchPort) {
724 // Port with capacity
725 return bandwidth.bps() < port.portSpeed() * 1000000.0;
726 } else {
727 // Port without valid capacity (OMS port, etc.)
728 return true;
729 }
730 } else {
731 // Check if enough amount of bandwidth resource remains
732 ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
733 .resource(bandwidth.bps());
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800734 try {
735 return resourceService.isAvailable(resource);
736 } catch (Exception e) {
737 log.error("Resource service failed checking availability of {}",
Marc De Leenheer552eeb62017-05-15 17:43:27 -0700738 resource, e);
Yuta HIGUCHI806113f2017-03-07 22:46:20 -0800739 throw e;
740 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700741 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700742 }
743 return false;
744 }
745 }
746
Naoki Shiota5a056062016-05-05 18:43:59 -0700747 public class InternalIntentListener implements IntentListener {
748 @Override
749 public void event(IntentEvent event) {
750 switch (event.type()) {
751 case INSTALLED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700752 log.debug("Intent {} installed.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700753 updateCrossConnectLink(event.subject());
754 break;
755 case WITHDRAWN:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700756 log.debug("Intent {} withdrawn.", event.subject());
Naoki Shiota5a056062016-05-05 18:43:59 -0700757 removeCrossConnectLinks(event.subject());
758 break;
759 case FAILED:
Yuta HIGUCHI2a772732017-05-01 20:14:33 -0700760 log.debug("Intent {} failed.", event.subject());
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800761 // TODO If it was one of it's own optical Intent,
762 // update link state
763 // TODO If it was packet P2P Intent, call setupConnectivity
Naoki Shiota5a056062016-05-05 18:43:59 -0700764 break;
765 default:
766 break;
767 }
768 }
769
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800770 // TODO rename "CrossConnectLink"?
771 /**
772 * Update packet-layer link/port state once Intent is installed.
773 *
774 * @param intent which reached installed state
775 */
Naoki Shiota5a056062016-05-05 18:43:59 -0700776 private void updateCrossConnectLink(Intent intent) {
777 linkPathMap.entrySet().stream()
778 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
779 .forEach(e -> {
780 ConnectPoint packetSrc = e.getKey().src();
781 ConnectPoint packetDst = e.getKey().dst();
782 Bandwidth bw = e.getKey().bandwidth();
Naoki Shiota5a056062016-05-05 18:43:59 -0700783
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700784 // reflect modification only if packetSrc is local_
785 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
786 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800787 setPortBandwidth(packetSrc, bw);
788 setPortBandwidth(packetDst, bw);
Naoki Shiota5a056062016-05-05 18:43:59 -0700789
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700790 // Updates link status in distributed map
791 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
792 e.getValue().value().setLinkEstablished(packetSrc, packetDst, true));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800793
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800794
795 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
796 injectLink(packetSrc, packetDst);
797 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700798 }
799 });
800 }
801
802 private void removeCrossConnectLinks(Intent intent) {
803 ConnectPoint src, dst;
804
805 if (intent instanceof OpticalCircuitIntent) {
806 OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
807 src = circuit.getSrc();
808 dst = circuit.getDst();
809 } else if (intent instanceof OpticalConnectivityIntent) {
810 OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
811 src = conn.getSrc();
812 dst = conn.getDst();
813 } else {
814 return;
815 }
816
817 removeXcLinkUsage(src);
818 removeXcLinkUsage(dst);
819
820 // Set bandwidth of 0 to cross connect ports
821 Bandwidth bw = Bandwidth.bps(0);
822 linkPathMap.entrySet().stream()
823 .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
824 .forEach(e -> {
825 ConnectPoint packetSrc = e.getKey().src();
826 ConnectPoint packetDst = e.getKey().dst();
Naoki Shiota5a056062016-05-05 18:43:59 -0700827
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700828 // reflect modification only if packetSrc is local_
829 if (mastershipService.isLocalMaster(packetSrc.deviceId())) {
830 // Updates bandwidth of packet ports
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800831 setPortBandwidth(packetSrc, bw);
832 setPortBandwidth(packetDst, bw);
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700833
834 // Updates link status in distributed map
835 linkPathMap.computeIfPresent(e.getKey(), (link, connectivity) ->
836 e.getValue().value().setLinkEstablished(packetSrc, packetDst, false));
Yuta HIGUCHIe80ec2c2017-03-06 14:16:13 -0800837
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800838
839 if (!linkDiscoveryEnabled(packetSrc, packetDst)) {
840 removeInjectedLink(packetSrc, packetDst);
841 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700842 }
843 });
844 }
845
846 private void removeXcLinkUsage(ConnectPoint cp) {
847 Optional<Link> link = linkService.getLinks(cp).stream()
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700848 .filter(usedCrossConnectLinkSet::contains)
Naoki Shiota5a056062016-05-05 18:43:59 -0700849 .findAny();
850
851 if (!link.isPresent()) {
Yuta HIGUCHIe2689ee2017-05-04 16:53:18 -0700852 log.warn("Cross connect point {} has no cross connect link to release.", cp);
Naoki Shiota5a056062016-05-05 18:43:59 -0700853 return;
854 }
855
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700856 usedCrossConnectLinkSet.remove(link.get());
Naoki Shiota5a056062016-05-05 18:43:59 -0700857 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800858
859 /**
860 * Injects link between specified packet port.
861 *
862 * @param packetSrc port 1
863 * @param packetDst port 2
864 */
865 private void injectLink(ConnectPoint packetSrc,
866 ConnectPoint packetDst) {
867 // inject expected link or durable link
868 // if packet device cannot advertise packet link
869 try {
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700870 // cannot call addConfig.
871 // it will create default BasicLinkConfig,
872 // which will end up advertising DIRECT links and
873 // DIRECT Link type cannot transition from DIRECT to INDIRECT
874 LinkKey lnkKey = linkKey(packetSrc, packetDst);
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800875 BasicLinkConfig lnkCfg = networkConfigService
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700876 .getConfig(lnkKey, BasicLinkConfig.class);
877 if (lnkCfg == null) {
878 lnkCfg = new BasicLinkConfig(lnkKey);
879 }
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800880 lnkCfg.isAllowed(true);
881 lnkCfg.isDurable(true);
Marc De Leenheer43b91ad2017-03-17 14:45:28 -0700882 lnkCfg.type(Link.Type.INDIRECT);
883 lnkCfg.isBidirectional(false);
Yuta HIGUCHI21033042017-05-04 17:58:24 -0700884 // cannot call apply against manually created instance
885 //lnkCfg.apply();
886 networkConfigService.applyConfig(lnkKey,
887 BasicLinkConfig.class,
888 lnkCfg.node());
889
Yuta HIGUCHI9b9c7292017-03-07 22:50:04 -0800890 } catch (Exception ex) {
891 log.error("Applying BasicLinkConfig failed", ex);
892 }
893 }
894
895 /**
896 * Removes link injected between specified packet port.
897 *
898 * @param packetSrc port 1
899 * @param packetDst port 2
900 */
901 private void removeInjectedLink(ConnectPoint packetSrc,
902 ConnectPoint packetDst) {
903 // remove expected link or durable link
904 // if packet device cannot monitor packet link
905
906 try {
907 // hack to mark link off-line
908 BasicLinkConfig lnkCfg = networkConfigService
909 .getConfig(linkKey(packetSrc, packetDst),
910 BasicLinkConfig.class);
911 lnkCfg.isAllowed(false);
912 lnkCfg.apply();
913 } catch (Exception ex) {
914 log.error("Applying BasicLinkConfig failed", ex);
915 }
916
917 networkConfigService
918 .removeConfig(linkKey(packetSrc, packetDst),
919 BasicLinkConfig.class);
920 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700921 }
922
923
924 private class InternalLinkListener implements LinkListener {
925
926 @Override
927 public void event(LinkEvent event) {
928 switch (event.type()) {
929 case LINK_REMOVED:
930 Link link = event.subject();
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700931 // updates linkPathMap only if src device of link is local
932 if (!mastershipService.isLocalMaster(link.src().deviceId())) {
933 return;
934 }
935
936 // find all packet links that correspond to removed link
Naoki Shiota5a056062016-05-05 18:43:59 -0700937 Set<PacketLinkRealizedByOptical> pLinks = linkPathMap.keySet().stream()
938 .filter(l -> l.isBetween(link.src(), link.dst()) || l.isBetween(link.dst(), link.src()))
939 .collect(Collectors.toSet());
940
941 pLinks.forEach(l -> {
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700942 // remove found packet links from distributed store
943 linkPathMap.computeIfPresent(l, (plink, conn) -> {
944 // Notifies listeners if all packet links are gone
945 if (conn.isAllRealizingLinkNotEstablished()) {
946 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, conn.id()));
947 }
948 return null;
949 });
Naoki Shiota5a056062016-05-05 18:43:59 -0700950 });
951 default:
952 break;
953 }
954 }
955 }
Naoki Shiota7c3111b2016-06-09 16:12:11 -0700956
957 private class InternalStoreListener
958 implements MapEventListener<PacketLinkRealizedByOptical, OpticalConnectivity> {
959
960 @Override
961 public void event(MapEvent<PacketLinkRealizedByOptical, OpticalConnectivity> event) {
962 switch (event.type()) {
963 case UPDATE:
964 OpticalConnectivity oldConnectivity = event.oldValue().value();
965 OpticalConnectivity newConnectivity = event.newValue().value();
966
967 if (!oldConnectivity.isAllRealizingLinkEstablished() &&
968 newConnectivity.isAllRealizingLinkEstablished()) {
969 // Notifies listeners if all links are established
970 updateBandwidthUsage(newConnectivity);
971 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_INSTALLED, newConnectivity.id()));
972 } else if (!oldConnectivity.isAllRealizingLinkNotEstablished() &&
973 newConnectivity.isAllRealizingLinkNotEstablished()) {
974 // Notifies listeners if all links are gone
975 releaseBandwidthUsage(newConnectivity);
976 post(new OpticalPathEvent(OpticalPathEvent.Type.PATH_REMOVED, newConnectivity.id()));
977 }
978
979 break;
980 default:
981 break;
982 }
983 }
984
985 }
Naoki Shiota5a056062016-05-05 18:43:59 -0700986}
987