blob: fe65e6e159602ab3fe05723f4f0d8b8a6bcc4e78 [file] [log] [blame]
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001/*
2 * Copyright 2016-present Open Networking Foundation
3 *
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 */
16
17package org.onosproject.segmentrouting.pwaas;
18
Charles Chand5814aa2018-08-19 19:21:46 -070019import com.google.common.collect.ImmutableMap;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080020import com.google.common.collect.Iterables;
21import com.google.common.collect.Lists;
22import org.apache.commons.lang3.RandomUtils;
23import org.onlab.packet.Ethernet;
24import org.onlab.packet.MacAddress;
25import org.onlab.packet.MplsLabel;
26import org.onlab.packet.VlanId;
27import org.onlab.util.KryoNamespace;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.DefaultLink;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.Path;
33import org.onosproject.net.PortNumber;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080034import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flow.criteria.Criteria;
39import org.onosproject.net.flowobjective.DefaultFilteringObjective;
40import org.onosproject.net.flowobjective.DefaultForwardingObjective;
41import org.onosproject.net.flowobjective.DefaultNextObjective;
42import org.onosproject.net.flowobjective.DefaultObjectiveContext;
43import org.onosproject.net.flowobjective.FilteringObjective;
44import org.onosproject.net.flowobjective.ForwardingObjective;
45import org.onosproject.net.flowobjective.NextObjective;
46import org.onosproject.net.flowobjective.Objective;
47import org.onosproject.net.flowobjective.ObjectiveContext;
48import org.onosproject.net.flowobjective.ObjectiveError;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070049import org.onosproject.net.topology.LinkWeigher;
50import org.onosproject.segmentrouting.SRLinkWeigher;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080051import org.onosproject.segmentrouting.SegmentRoutingManager;
52import org.onosproject.segmentrouting.SegmentRoutingService;
53import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080054import org.onosproject.store.serializers.KryoNamespaces;
55import org.onosproject.store.service.ConsistentMap;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070056import org.onosproject.store.service.DistributedLock;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080057import org.onosproject.store.service.Serializer;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070058import org.onosproject.store.service.StorageException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080059import org.onosproject.store.service.Versioned;
60import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
63import java.util.ArrayList;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070064import java.util.HashSet;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080065import java.util.List;
66import java.util.Set;
67import java.util.concurrent.CompletableFuture;
68import java.util.stream.Collectors;
69
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080070import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
71import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
72import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
73import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
74import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
75import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -070076import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080077
78/**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070079 * Handler for pseudowire management.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080080 */
81public class DefaultL2TunnelHandler implements L2TunnelHandler {
82
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070083 private static final String LOCK_NAME = "l2-tunnel-handler-lock";
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080084 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
85
86 private final SegmentRoutingManager srManager;
87 /**
88 * To store the next objectives related to the initiation.
89 */
90 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
91 /**
92 * To store the next objectives related to the termination.
93 */
94 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
95
96 /**
97 * To store policies.
98 */
99 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
100
101 /**
102 * To store tunnels.
103 */
104 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
105
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700106 /**
107 * To store pending tunnels that need to be installed.
108 */
109 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
110
111 /**
112 * To store pending policies that need to be installed.
113 */
114 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
115
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800116 private final KryoNamespace.Builder l2TunnelKryo;
117
118 /**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700119 * Lock used when creating or removing pseudowires.
120 */
121 private final DistributedLock pwLock;
122
123 private static final long LOCK_TIMEOUT = 2000;
124
125 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800126 * Create a l2 tunnel handler for the deploy and
127 * for the tear down of pseudo wires.
128 *
129 * @param segmentRoutingManager the segment routing manager
130 */
131 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
132 srManager = segmentRoutingManager;
133 l2TunnelKryo = new KryoNamespace.Builder()
134 .register(KryoNamespaces.API)
135 .register(L2Tunnel.class,
136 L2TunnelPolicy.class,
137 DefaultL2Tunnel.class,
138 DefaultL2TunnelPolicy.class,
139 L2Mode.class,
140 MplsLabel.class,
141 VlanId.class,
142 ConnectPoint.class);
143
144 l2InitiationNextObjStore = srManager.
145 storageService.
146 <String, NextObjective>consistentMapBuilder().
147 withName("onos-l2initiation-nextobj-store").
148 withSerializer(Serializer.using(l2TunnelKryo.build())).
149 build();
150
151 l2TerminationNextObjStore = srManager.storageService.
152 <String, NextObjective>consistentMapBuilder()
153 .withName("onos-l2termination-nextobj-store")
154 .withSerializer(Serializer.using(l2TunnelKryo.build()))
155 .build();
156
157 l2PolicyStore = srManager.storageService
158 .<String, L2TunnelPolicy>consistentMapBuilder()
159 .withName("onos-l2-policy-store")
160 .withSerializer(Serializer.using(l2TunnelKryo.build()))
161 .build();
162
163 l2TunnelStore = srManager.storageService
164 .<String, L2Tunnel>consistentMapBuilder()
165 .withName("onos-l2-tunnel-store")
166 .withSerializer(Serializer.using(l2TunnelKryo.build()))
167 .build();
168
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700169 pendingL2PolicyStore = srManager.storageService
170 .<String, L2TunnelPolicy>consistentMapBuilder()
171 .withName("onos-l2-pending-policy-store")
172 .withSerializer(Serializer.using(l2TunnelKryo.build()))
173 .build();
174
175 pendingL2TunnelStore = srManager.storageService
176 .<String, L2Tunnel>consistentMapBuilder()
177 .withName("onos-l2-pending-tunnel-store")
178 .withSerializer(Serializer.using(l2TunnelKryo.build()))
179 .build();
180
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700181 pwLock = srManager.storageService.lockBuilder()
182 .withName(LOCK_NAME)
183 .build()
184 .asLock(LOCK_TIMEOUT);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800185 }
186
187 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800188 * Used by manager only in initialization.
189 */
190 @Override
191 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800192 // Since we have no pseudowires in netcfg there
193 // is nothing to do in initialization.
194 // I leave it here because potentially we might need to
195 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800196 }
197
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700198 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700199 public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700200 // get pending tunnels/policies OR installed tunnels/policies
201 List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
202 List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700203 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700204 .map(l2Tunnel -> {
205 L2TunnelPolicy policy = null;
206 for (L2TunnelPolicy l2Policy : policies) {
207 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
208 policy = l2Policy;
209 break;
210 }
211 }
212
213 return new DefaultL2TunnelDescription(l2Tunnel, policy);
214 })
215 .collect(Collectors.toSet());
216 }
217
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800218 @Override
219 public List<L2TunnelPolicy> getL2Policies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800220 return new ArrayList<>(l2PolicyStore
221 .values()
222 .stream()
223 .map(Versioned::value)
224 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800225 }
226
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800227 @Override
228 public List<L2Tunnel> getL2Tunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800229 return new ArrayList<>(l2TunnelStore
230 .values()
231 .stream()
232 .map(Versioned::value)
233 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800234 }
235
236 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700237 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700238 return new ArrayList<>(pendingL2PolicyStore
239 .values()
240 .stream()
241 .map(Versioned::value)
242 .collect(Collectors.toList()));
243 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800244
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700245 @Override
246 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700247 return new ArrayList<>(pendingL2TunnelStore
248 .values()
249 .stream()
250 .map(Versioned::value)
251 .collect(Collectors.toList()));
252 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800253
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700254 @Override
255 public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
256
257 // get both added and pending pseudowires
258 List<L2TunnelDescription> newPseudowires = new ArrayList<>();
259 newPseudowires.addAll(getL2Descriptions(false));
260 newPseudowires.addAll(getL2Descriptions(true));
261 // add the new one
262 newPseudowires.add(pwToAdd);
263
264 return configurationValidity(newPseudowires);
265 }
266
Charles Chand5814aa2018-08-19 19:21:46 -0700267 @Override
268 public ImmutableMap<String, NextObjective> getInitNext() {
269 if (l2InitiationNextObjStore != null) {
270 return ImmutableMap.copyOf(l2InitiationNextObjStore.asJavaMap());
271 } else {
272 return ImmutableMap.of();
273 }
274 }
275
276 @Override
277 public ImmutableMap<String, NextObjective> getTermNext() {
278 if (l2TerminationNextObjStore != null) {
279 return ImmutableMap.copyOf(l2TerminationNextObjStore.asJavaMap());
280 } else {
281 return ImmutableMap.of();
282 }
283 }
284
285 @Override
286 public void removeNextId(int nextId) {
287 l2InitiationNextObjStore.entrySet().forEach(e -> {
288 if (e.getValue().value().id() == nextId) {
289 l2InitiationNextObjStore.remove(e.getKey());
290 }
291 });
292
293 l2TerminationNextObjStore.entrySet().forEach(e -> {
294 if (e.getValue().value().id() == nextId) {
295 l2TerminationNextObjStore.remove(e.getKey());
296 }
297 });
298 }
299
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700300 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800301 * Returns the new vlan id for an ingress point of a
302 * pseudowire. For double tagged, it is the outer,
303 * For single tagged it is the single tag, and for
304 * inner it is None.
305 *
306 * @param ingressOuter vlanid of ingress outer
307 * @param ingressInner vlanid of ingress inner
308 * @param egressOuter vlanid of egress outer
309 * @param egressInner vlanid of egress inner
310 * @return returns the vlan id which will be installed at vlan table 1.
311 */
312 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
313 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800314 // validity of vlan combinations was checked at verifyPseudowire
315 if (!(ingressOuter.equals(VlanId.NONE))) {
316 return egressOuter;
317 } else if (!(ingressInner.equals(VlanId.NONE))) {
318 return egressInner;
319 } else {
320 return VlanId.vlanId("None");
321 }
322 }
323
324 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700325 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700326 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700327 * @param path The path to traverse.
328 * @return The devices on the path with the order they
329 * are traversed.
330 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700331 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700332
333 // iterate over links and get all devices in the order
334 // we find them
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700335 List<DeviceId> deviceList = new ArrayList<>();
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700336 for (Link link : path) {
337 if (!deviceList.contains(link.src().deviceId())) {
338 deviceList.add(link.src().deviceId());
339 }
340 if (!deviceList.contains(link.dst().deviceId())) {
341 deviceList.add(link.dst().deviceId());
342 }
343 }
344
345 return deviceList;
346 }
347
348 /**
349 * Returns true if path is valid according to the current logic.
350 * For example : leaf to spine pseudowires can be either leaf-spine or
351 * leaf-spine-spine. However, in the configuration we might specify spine device
352 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
353 * two cases and need to provision for them.
354 *
355 * If we have a leaf to leaf pseudowire then all the intermediate devices must
356 * be spines. However, in case of paired switches we can have two leafs interconnected
357 * with each other directly.
358 *
359 * @param path the chosen path
360 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700361 * @return True if path size is valid, false otherwise.
362 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700363 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
364
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700365 log.debug("Checking path validity for pseudowire.");
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700366 List<DeviceId> devices = getDevicesOnPath(path);
367 if (devices.size() < 2) {
368 log.error("Path size for pseudowire should be greater than 1!");
369 return false;
370 }
371
372 try {
373 if (leafSpinePw) {
374 // can either be leaf-spine-spine or leaf-spine
375 // first device is leaf, all other must be spines
376 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
377 // if first device is a leaf then all other must be spines
378 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
379 devices.remove(0);
380 for (DeviceId devId : devices) {
381 log.debug("Device {} should be a spine!", devId);
382 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
383 return false;
384 }
385 }
386 } else {
387 // if first device is spine, last device must be a leaf
388 // all other devices must be spines
389 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
390 return false;
391 }
392 devices.remove(devices.size() - 1);
393 for (DeviceId devId : devices) {
394 log.debug("Device {} should be a spine!", devId);
395 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
396 return false;
397 }
398 }
399 }
400 } else {
401 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
402 // or leaf-spine-spine-leaf
403 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
404 // check first device, needs to be a leaf
405 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
406 return false;
407 }
408 // check last device, needs to be a leaf
409 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
410 return false;
411 }
412 // remove these devices, rest must all be spines
413 devices.remove(0);
414 devices.remove(devices.size() - 1);
415 for (DeviceId devId : devices) {
416 log.debug("Device {} should be a spine!", devId);
417 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
418 return false;
419 }
420 }
421
422 }
423 } catch (DeviceConfigNotFoundException e) {
424 log.error("Device not found in configuration : {}", e);
425 return false;
426 }
427
428 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700429 }
430
431 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800432 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800433 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700434 * @param pw The pseudowire to deploy
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800435 * @return result of pseudowire deployment
436 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700437 public Result deployPseudowire(L2TunnelDescription pw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800438
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700439 try {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700440 // take the lock
441 pwLock.lock();
442 Result result;
443 long l2TunnelId;
444 log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
445 pw.l2Tunnel().tunnelId());
446 l2TunnelId = pw.l2Tunnel().tunnelId();
447 // The tunnel id cannot be 0.
448 if (l2TunnelId == 0) {
449 log.warn("Tunnel id id must be > 0 in {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700450 return Result.WRONG_PARAMETERS
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700451 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700452 }
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700453
454 result = verifyGlobalValidity(pw);
455 if (result != SUCCESS) {
456 log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
457 return result;
458 }
459
460 // leafSpinePw determines if this is a leaf-leaf
461 // or leaf-spine pseudowire
462 boolean leafSpinePw;
463 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
464 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
465 try {
466 // differentiate between leaf-leaf pseudowires and leaf-spine
467 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
468 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
469 log.error("Can not deploy pseudowire {} from spine to spine!", l2TunnelId);
470 return Result.WRONG_PARAMETERS
471 .appendError("Can not deploy pseudowire from spine to spine!");
472 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
473 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
474 leafSpinePw = false;
475 } else {
476 leafSpinePw = true;
477 }
478 } catch (DeviceConfigNotFoundException e) {
479 log.error("Device for pseudowire {} connection points does not exist in the configuration", l2TunnelId);
480 return Result.INTERNAL_ERROR
481 .appendError("Device for pseudowire connection points does not exist in the configuration");
482 }
483
484 // reverse the policy in order for leaf switch to be at CP1
485 // this will help us for re-using SRLinkWeigher for computing valid paths
486 L2TunnelPolicy reversedPolicy = reverseL2TunnelPolicy(pw.l2TunnelPolicy());
487 if (reversedPolicy == null) {
488 log.error("Error in reversing policy, device configuration was not found for pseudowire {}.",
489 l2TunnelId);
490 return INTERNAL_ERROR
491 .appendError("Device configuration not found when reversing the policy.");
492 }
493 pw.setL2TunnelPolicy(reversedPolicy);
494
495 // get path here, need to use the same for fwd and rev direction
496 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
497 pw.l2TunnelPolicy().cP2());
498 if (path == null) {
499 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
500 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
501 }
502
503 Link fwdNextHop;
504 Link revNextHop;
505 if (!isValidPath(path, leafSpinePw)) {
506 log.error("Deploying process : Path for pseudowire {} is not valid", l2TunnelId);
507 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
508 }
509 // oneHope flag is used to determine if we need to
510 // install transit mpls rules
511 boolean oneHop = true;
512 if (path.size() > 1) {
513 oneHop = false;
514 }
515
516 fwdNextHop = path.get(0);
517 revNextHop = reverseLink(path.get(path.size() - 1));
518
519 pw.l2Tunnel().setPath(path);
Saurav Das09c2c4d2018-08-13 15:34:26 -0700520 pw.l2Tunnel().setTransportVlan(srManager.getPwTransportVlan());
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700521
522 // next hops for next objectives
523 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
524
525 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
526 pw.l2TunnelPolicy().cP1InnerTag(),
527 pw.l2TunnelPolicy().cP2OuterTag(),
528 pw.l2TunnelPolicy().cP2InnerTag());
529 result = deployPseudoWireInit(pw.l2Tunnel(),
530 pw.l2TunnelPolicy().cP1(),
531 pw.l2TunnelPolicy().cP2(),
532 FWD,
533 fwdNextHop,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700534 oneHop,
535 egressVlan);
536 if (result != SUCCESS) {
537 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP1", l2TunnelId);
538 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
539 }
540
541 result = deployPolicy(l2TunnelId,
542 pw.l2TunnelPolicy().cP1(),
543 pw.l2TunnelPolicy().cP1InnerTag(),
544 pw.l2TunnelPolicy().cP1OuterTag(),
545 egressVlan,
546 result.getNextId());
547 if (result != SUCCESS) {
548 log.error("Deploying process : Error in deploying pseudowire {} policy for CP1", l2TunnelId);
549 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
550 }
551
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700552 result = deployPseudoWireTerm(pw.l2Tunnel(),
553 pw.l2TunnelPolicy().cP2(),
554 egressVlan,
555 FWD,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700556 oneHop);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700557
558 if (result != SUCCESS) {
559 log.error("Deploying process : Error in deploying pseudowire {} termination for CP1", l2TunnelId);
560 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
561 }
562
563 // We establish the reverse tunnel.
564 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
565 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
566 pw.l2TunnelPolicy().cP2InnerTag(),
567 pw.l2TunnelPolicy().cP1OuterTag(),
568 pw.l2TunnelPolicy().cP1InnerTag());
569
570 result = deployPseudoWireInit(pw.l2Tunnel(),
571 pw.l2TunnelPolicy().cP2(),
572 pw.l2TunnelPolicy().cP1(),
573 REV,
574 revNextHop,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700575 oneHop,
576 egressVlan);
577 if (result != SUCCESS) {
578 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP2", l2TunnelId);
579 return Result.INTERNAL_ERROR
580 .appendError("Error in deploying pseudowire initiation for CP2");
581 }
582
583 result = deployPolicy(l2TunnelId,
584 pw.l2TunnelPolicy().cP2(),
585 pw.l2TunnelPolicy().cP2InnerTag(),
586 pw.l2TunnelPolicy().cP2OuterTag(),
587 egressVlan,
588 result.getNextId());
589 if (result != SUCCESS) {
590 log.error("Deploying process : Error in deploying policy {} for CP2", l2TunnelId);
591 return Result.INTERNAL_ERROR
592 .appendError("Deploying process : Error in deploying policy for CP2");
593 }
594
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700595 result = deployPseudoWireTerm(pw.l2Tunnel(),
596 pw.l2TunnelPolicy().cP1(),
597 egressVlan,
598 REV,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700599 oneHop);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700600
601 if (result != SUCCESS) {
602 log.error("Deploying process : Error in deploying pseudowire {} termination for CP2", l2TunnelId);
603 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
604 }
605
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700606 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
607
608 // Populate stores as the final step of the process
609 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
610 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
611
612 return Result.SUCCESS;
613 } catch (StorageException.Timeout e) {
614 log.error("Can not acquire distributed lock for pseudowire {}!", pw.l2Tunnel().tunnelId());
615 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
616 } finally {
617 // release the lock
618 pwLock.unlock();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700619 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800620 }
621
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700622 @Override
623 public Result checkIfPwExists(long tunnelId, boolean pending) {
624
625 List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
626 .stream()
627 .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
628 .collect(Collectors.toList());
629
630 if (pseudowires.size() == 0) {
631 String store = ((pending) ? "pending" : "installed");
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700632 log.debug("Pseudowire {} does not exist in {} store", tunnelId, store);
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700633 return Result.WRONG_PARAMETERS.
634 appendError("Pseudowire " + tunnelId + " does not exist in " + store);
635 } else {
636 return SUCCESS;
637 }
638 }
639
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800640 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700641 * Tears down connection points of pseudowires. We can either tear down both connection points,
642 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800643 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700644 * @param l2TunnelId The tunnel id for this pseudowire.
645 * @param tearDownFirst Boolean, true if we want to tear down cp1
646 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700647 * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
648 * in the network, else remove flows/groups in the devices also.
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700649 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700650 * a descriptive error otherwise.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800651 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700652 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
653 boolean tearDownSecond, boolean pending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800654
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700655 Result res;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800656 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
657 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800658 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
659 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
660
661 if (l2TunnelId == 0) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700662 log.error("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700663 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800664 }
665
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700666 res = checkIfPwExists(l2TunnelId, pending);
667 if (res != Result.SUCCESS) {
668 return res;
669 }
670
671 // remove and get the tunnel and the policy from the appropriate store
672 // if null, return error.
673 Versioned<L2Tunnel> l2TunnelVersioned = pending ?
674 pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
675 l2TunnelStore.remove(Long.toString(l2TunnelId));
676 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
677 pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
678 l2PolicyStore.remove(Long.toString(l2TunnelId));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800679 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
680 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700681 return Result.INTERNAL_ERROR
682 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800683 }
684
685 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700686 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800687
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700688 if (pending) {
689 // no need to remove flows / groups for a pseudowire
690 // in pending state
691 return Result.SUCCESS;
692 }
693
694 // remove flows/groups involving with this pseudowire
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700695 if (tearDownFirst) {
696 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800697
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700698 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
699 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
700 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
701 pwToRemove.l2TunnelPolicy().cP2InnerTag());
702 deletePolicy(l2TunnelId,
703 pwToRemove.l2TunnelPolicy().cP1(),
704 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
705 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
706 egressVlan,
707 fwdInitNextFuture,
708 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800709
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700710 fwdInitNextFuture.thenAcceptAsync(status -> {
711 if (status == null) {
712 // Finally we will tear down the pseudo wire.
713 tearDownPseudoWireInit(l2TunnelId,
714 pwToRemove.l2TunnelPolicy().cP1(),
715 fwdTermNextFuture,
716 FWD);
717 }
718 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800719
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700720 fwdTermNextFuture.thenAcceptAsync(status -> {
721 if (status == null) {
722 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
723 pwToRemove.l2TunnelPolicy().cP2(),
724 null,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700725 FWD);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700726 }
727 });
728 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800729
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700730 if (tearDownSecond) {
731 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800732
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700733 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
734 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
735 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
736 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800737
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700738 // We do the same operations on the reverse side.
739 deletePolicy(l2TunnelId,
740 pwToRemove.l2TunnelPolicy().cP2(),
741 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
742 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
743 egressVlan,
744 revInitNextFuture,
745 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800746
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700747 revInitNextFuture.thenAcceptAsync(status -> {
748 if (status == null) {
749 tearDownPseudoWireInit(l2TunnelId,
750 pwToRemove.l2TunnelPolicy().cP2(),
751 revTermNextFuture,
752 REV);
753 }
754 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800755
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700756 revTermNextFuture.thenAcceptAsync(status -> {
757 if (status == null) {
758 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
759 pwToRemove.l2TunnelPolicy().cP1(),
760 null,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700761 REV);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700762 }
763 });
764 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800765
766 return Result.SUCCESS;
767 }
768
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700769 /**
770 * Helper function for removing a single pseudowire.
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700771 *
772 * Tries to remove pseudowire from any store it might reside (pending or installed).
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700773 *
774 * @param l2TunnelId the id of the pseudowire to tear down
775 * @return Returns SUCCESS if no error is obeserved or an appropriate
776 * error on a failure
777 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700778 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700779
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700780 try {
781 // take the lock
782 pwLock.lock();
783
784 if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
785 return tearDownConnectionPoints(l2TunnelId, true, true, true);
786 } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
787 return tearDownConnectionPoints(l2TunnelId, true, true, false);
788 } else {
789 return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
790 + l2TunnelId
791 + " did not reside in any store!");
792 }
793 } catch (StorageException.Timeout e) {
794 log.error("Can not acquire distributed lock for pseudowire {}!", l2TunnelId);
795 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
796 } finally {
797 // release the lock
798 pwLock.unlock();
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700799 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700800 }
801
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700802 @Override
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700803 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800804 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
805
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800806 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800807
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700808 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800809 log.info("Removing pseudowire {}", tunnelId);
810
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700811 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700812 if (result != Result.SUCCESS) {
813 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800814 }
815 }
816 }
817
818 /**
819 * Handles the policy establishment which consists in
820 * create the filtering and forwarding objectives related
821 * to the initiation and termination.
822 *
823 * @param tunnelId the tunnel id
824 * @param ingress the ingress point
825 * @param ingressInner the ingress inner tag
826 * @param ingressOuter the ingress outer tag
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800827 * @param egressVlan Vlan-id to set, depends on ingress vlan
828 * combinations. For example, if pw is double tagged
829 * then this is the value of the outer vlan, if single
830 * tagged then it is the new value of the single tag.
831 * Should be None for untagged traffic.
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700832 * @param nextId the next objective id
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800833 * @return the result of the operation
834 */
835 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
836 VlanId ingressOuter, VlanId egressVlan, int nextId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700837 log.debug("Starting deploying policy for pseudowire {}.", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800838
839 List<Objective> objectives = Lists.newArrayList();
840 // We create the forwarding objective for supporting
841 // the l2 tunnel.
842 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
843 // We create and add objective context.
844 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
845 log.debug("FwdObj for tunnel {} populated", tunnelId),
846 (objective, error) ->
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700847 log.warn("Failed to populate fwdObj " +
848 "for tunnel {} : {}",
849 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800850 objectives.add(fwdBuilder.add(context));
851
852 // We create the filtering objective to define the
853 // permit traffic in the switch
854 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
855
856 // We add the metadata.
857 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
858 .builder()
859 .setTunnelId(tunnelId)
860 .setVlanId(egressVlan);
861 filtBuilder.withMeta(treatment.build());
862
863 // We create and add objective context.
864 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
865 (objective, error) -> log.warn("Failed to populate filterObj for " +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700866 "tunnel {} : {}",
867 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800868 objectives.add(filtBuilder.add(context));
869
870 for (Objective objective : objectives) {
871 if (objective instanceof ForwardingObjective) {
872 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
873 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
874 } else {
875 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
876 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
877 }
878 }
879 return SUCCESS;
880 }
881
882 /**
883 * Handles the tunnel establishment which consists in
884 * create the next objectives related to the initiation.
885 *
886 * @param l2Tunnel the tunnel to deploy
887 * @param ingress the ingress connect point
888 * @param egress the egress connect point
889 * @param direction the direction of the pw
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700890 * @param nextHop next hop of the initiation point
891 * @param oneHop if this pseudowire has only one link
892 * @param termVlanId the termination vlan id
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800893 * @return the result of the operation
894 */
895 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800896 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700897 Link nextHop, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700898 log.debug("Started deploying init next objectives for pseudowire {} for tunnel {} -> {}.",
899 l2Tunnel.tunnelId(), ingress, egress);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800900 if (nextHop == null) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700901 log.warn("No path between ingress and egress connection points for tunnel {}", l2Tunnel.tunnelId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800902 return WRONG_PARAMETERS;
903 }
904
905 // We create the next objective without the metadata
906 // context and id. We check if it already exists in the
907 // store. If not we store as it is in the store.
908 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
909 nextHop.src(),
910 nextHop.dst(),
911 l2Tunnel,
912 egress.deviceId(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700913 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800914 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800915
916 if (nextObjectiveBuilder == null) {
917 return INTERNAL_ERROR;
918 }
919 // We set the metadata. We will use this metadata
920 // to inform the driver we are doing a l2 tunnel.
921 TrafficSelector metadata = DefaultTrafficSelector
922 .builder()
923 .matchTunnelId(l2Tunnel.tunnelId())
924 .build();
925 nextObjectiveBuilder.withMeta(metadata);
926 int nextId = srManager.flowObjectiveService.allocateNextId();
927 if (nextId < 0) {
928 log.warn("Not able to allocate a next id for initiation");
929 return INTERNAL_ERROR;
930 }
931 nextObjectiveBuilder.withId(nextId);
932 String key = generateKey(l2Tunnel.tunnelId(), direction);
933 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
Charles Chan55b806f2018-08-23 14:30:33 -0700934 ObjectiveContext context = new DefaultObjectiveContext(
935 (objective) -> log.debug("Initiation l2 tunnel rule for {} populated", l2Tunnel.tunnelId()),
936 (objective, error) -> {
937 log.warn("Failed to populate Initiation l2 tunnel rule for {}: {}", l2Tunnel.tunnelId(), error);
938 srManager.invalidateNextObj(objective.id());
939 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800940 NextObjective nextObjective = nextObjectiveBuilder.add(context);
941 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
942 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
943 l2Tunnel.tunnelId(), nextObjective.id());
944 Result result = SUCCESS;
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -0700945 result.setNextId(nextObjective.id());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800946 return result;
947 }
948
949 /**
950 * Handles the tunnel termination, which consists in the creation
951 * of a forwarding objective and a next objective.
952 *
953 * @param l2Tunnel the tunnel to terminate
954 * @param egress the egress point
955 * @param egressVlan the expected vlan at egress
956 * @param direction the direction
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800957 * @return the result of the operation
958 */
959 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700960 VlanId egressVlan, Direction direction,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700961 boolean oneHop) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700962 log.debug("Started deploying termination objectives for pseudowire {} , direction {}.",
963 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800964
965 // We create the group relative to the termination.
966 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
967 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700968 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800969 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800970 if (nextObjectiveBuilder == null) {
971 return INTERNAL_ERROR;
972 }
973 TrafficSelector metadata = DefaultTrafficSelector
974 .builder()
975 .matchVlanId(egressVlan)
976 .build();
977 nextObjectiveBuilder.withMeta(metadata);
978 int nextId = srManager.flowObjectiveService.allocateNextId();
979 if (nextId < 0) {
980 log.warn("Not able to allocate a next id for initiation");
981 return INTERNAL_ERROR;
982 }
983 nextObjectiveBuilder.withId(nextId);
984 String key = generateKey(l2Tunnel.tunnelId(), direction);
985 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
Charles Chan55b806f2018-08-23 14:30:33 -0700986 ObjectiveContext context = new DefaultObjectiveContext(
987 (objective) -> log.debug("Termination l2 tunnel rule for {} populated", l2Tunnel.tunnelId()),
988 (objective, error) -> {
989 log.warn("Failed to populate termination l2 tunnel rule for {}: {}", l2Tunnel.tunnelId(), error);
990 srManager.invalidateNextObj(objective.id());
991 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800992 NextObjective nextObjective = nextObjectiveBuilder.add(context);
993 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
994 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
995 l2Tunnel.tunnelId(), nextObjective.id());
996
997 // We create the flow relative to the termination.
998 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
999 egress.port(), nextObjective.id());
1000 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1001 l2Tunnel.tunnelId()),
1002 (objective, error) -> log.warn("Failed to populate fwdrObj" +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001003 " for tunnel termination {} : {}",
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001004 l2Tunnel.tunnelId(), error));
1005 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1006 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1007 nextId, l2Tunnel.tunnelId());
1008
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001009 return SUCCESS;
1010 }
1011
1012
1013 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001014 * Creates the filtering objective according to a given policy.
1015 *
1016 * @param inPort the in port
1017 * @param innerTag the inner vlan tag
1018 * @param outerTag the outer vlan tag
1019 * @return the filtering objective
1020 */
1021 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1022
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001023 log.debug("Creating connection point filtering objective for vlans {} / {}", outerTag, innerTag);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001024 return DefaultFilteringObjective
1025 .builder()
1026 .withKey(Criteria.matchInPort(inPort))
1027 .addCondition(Criteria.matchInnerVlanId(innerTag))
1028 .addCondition(Criteria.matchVlanId(outerTag))
1029 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1030 .permit()
1031 .fromApp(srManager.appId());
1032 }
1033
1034 /**
1035 * Creates the forwarding objective for the termination.
1036 *
1037 * @param pwLabel the pseudo wire label
1038 * @param tunnelId the tunnel id
1039 * @param egressPort the egress port
1040 * @param nextId the next step
1041 * @return the forwarding objective to support the termination
1042 */
1043 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1044 PortNumber egressPort, int nextId) {
1045
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001046 log.debug("Creating forwarding objective for termination for tunnel {} : pwLabel {}, egressPort {}, nextId {}",
1047 tunnelId, pwLabel, egressPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001048 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1049 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1050 // The flow has to match on the pw label and bos
1051 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1052 trafficSelector.matchMplsLabel(pwLabel);
1053 trafficSelector.matchMplsBos(true);
1054 // The flow has to decrement ttl, restore ttl in
1055 // pop mpls, set tunnel id and port.
1056 trafficTreatment.decMplsTtl();
1057 trafficTreatment.copyTtlIn();
1058 trafficTreatment.popMpls();
1059 trafficTreatment.setTunnelId(tunnelId);
1060 trafficTreatment.setOutput(egressPort);
1061
1062 return DefaultForwardingObjective
1063 .builder()
1064 .fromApp(srManager.appId())
1065 .makePermanent()
1066 .nextStep(nextId)
1067 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1068 .withSelector(trafficSelector.build())
1069 .withTreatment(trafficTreatment.build())
1070 .withFlag(VERSATILE);
1071 }
1072
1073 /**
1074 * Creates the forwarding objective for the initiation.
1075 *
1076 * @param tunnelId the tunnel id
1077 * @param inPort the input port
1078 * @param nextId the next step
1079 * @return the forwarding objective to support the initiation.
1080 */
1081 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1082
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001083 log.debug("Creating forwarding objective for tunnel {} : Port {} , nextId {}", tunnelId, inPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001084 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1085
1086 // The flow has to match on the mpls logical
1087 // port and the tunnel id.
1088 trafficSelector.matchTunnelId(tunnelId);
1089 trafficSelector.matchInPort(inPort);
1090
1091 return DefaultForwardingObjective
1092 .builder()
1093 .fromApp(srManager.appId())
1094 .makePermanent()
1095 .nextStep(nextId)
1096 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1097 .withSelector(trafficSelector.build())
1098 .withFlag(VERSATILE);
1099
1100 }
1101
1102 /**
1103 * Creates the next objective according to a given
1104 * pipeline. We don't set the next id and we don't
1105 * create the final meta to check if we are re-using
1106 * the same next objective for different tunnels.
1107 *
1108 * @param pipeline the pipeline to support
1109 * @param srcCp the source port
1110 * @param dstCp the destination port
1111 * @param l2Tunnel the tunnel to support
1112 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001113 * @param oneHop if the pw only has one hop, push only pw label
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001114 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001115 * @return the next objective to support the pipeline
1116 */
1117 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1118 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001119 DeviceId egressId, boolean oneHop,
1120 VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001121 log.debug("Creating {} next objective for pseudowire {}.",
1122 pipeline == TERMINATION ? "termination" : "inititation");
1123
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001124 NextObjective.Builder nextObjBuilder;
1125 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1126 if (pipeline == INITIATION) {
1127 nextObjBuilder = DefaultNextObjective
1128 .builder()
1129 .withType(NextObjective.Type.SIMPLE)
1130 .fromApp(srManager.appId());
1131 // The pw label is the bottom of stack. It has to
1132 // be different -1.
1133 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001134 log.error("Pw label not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001135 return null;
1136 }
1137 treatmentBuilder.pushMpls();
1138 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1139 treatmentBuilder.setMplsBos(true);
1140 treatmentBuilder.copyTtlOut();
1141
1142 // If the inter-co label is present we have to set the label.
1143 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1144 treatmentBuilder.pushMpls();
1145 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1146 treatmentBuilder.setMplsBos(false);
1147 treatmentBuilder.copyTtlOut();
1148 }
1149
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001150 // if not oneHop install transit mpls labels also
1151 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001152 // We retrieve the sr label from the config
1153 // specific for pseudowire traffic
1154 // using the egress leaf device id.
1155 MplsLabel srLabel;
1156 try {
1157 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1158
1159 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001160 log.error("Sr label for pw traffic not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001161 return null;
1162 }
1163
1164 treatmentBuilder.pushMpls();
1165 treatmentBuilder.setMpls(srLabel);
1166 treatmentBuilder.setMplsBos(false);
1167 treatmentBuilder.copyTtlOut();
1168 }
1169
1170 // We have to rewrite the src and dst mac address.
1171 MacAddress ingressMac;
1172 try {
1173 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1174 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001175 log.error("Was not able to find the ingress mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001176 return null;
1177 }
1178 treatmentBuilder.setEthSrc(ingressMac);
1179 MacAddress neighborMac;
1180 try {
1181 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1182 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001183 log.error("Was not able to find the neighbor mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001184 return null;
1185 }
1186 treatmentBuilder.setEthDst(neighborMac);
1187
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001188 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001189 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001190 } else {
1191 // We create the next objective which
1192 // will be a simple l2 group.
1193 nextObjBuilder = DefaultNextObjective
1194 .builder()
1195 .withType(NextObjective.Type.SIMPLE)
1196 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001197
1198 // for termination point we use the outer vlan of the
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001199 // encapsulated packet for the vlan
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001200 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001201 }
1202
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001203 treatmentBuilder.setOutput(srcCp.port());
1204 nextObjBuilder.addTreatment(treatmentBuilder.build());
1205 return nextObjBuilder;
1206 }
1207
1208 /**
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001209 * Reverse an l2 tunnel policy in order to have as CP1 the leaf switch,
1210 * in case one of the switches is a spine.
1211 *
1212 * This makes possible the usage of SRLinkWeigher for computing valid paths,
1213 * which cuts leaf-spine links from the path computation with a src different
1214 * than the source leaf.
1215 *
1216 * @param policy The policy to reverse, if needed
1217 * @return a l2TunnelPolicy containing the leaf at CP1, suitable for usage with
1218 * current SRLinkWeigher
1219 */
1220 private L2TunnelPolicy reverseL2TunnelPolicy(L2TunnelPolicy policy) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001221
1222 log.debug("Reversing policy for pseudowire.");
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001223 try {
1224 // if cp1 is a leaf, just return
1225 if (srManager.deviceConfiguration().isEdgeDevice(policy.cP1().deviceId())) {
1226 return policy;
1227 } else {
1228 // return a policy with reversed cp1 and cp2, and also with reversed tags
1229 return new DefaultL2TunnelPolicy(policy.tunnelId(),
1230 policy.cP2(), policy.cP2InnerTag(), policy.cP2OuterTag(),
1231 policy.cP1(), policy.cP1InnerTag(), policy.cP1OuterTag());
1232
1233 }
1234 } catch (DeviceConfigNotFoundException e) {
1235 // should never come here, since it has been checked before
1236 log.error("Configuration for device {}, does not exist!");
1237 return null;
1238 }
1239 }
1240
1241 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001242 * Reverses a link.
1243 *
1244 * @param link link to be reversed
1245 * @return the reversed link
1246 */
1247 private Link reverseLink(Link link) {
1248
1249 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1250
1251 linkBuilder.src(link.dst());
1252 linkBuilder.dst(link.src());
1253 linkBuilder.type(link.type());
1254 linkBuilder.providerId(link.providerId());
1255
1256 return linkBuilder.build();
1257 }
1258
1259 /**
1260 * Returns the path betwwen two connect points.
1261 *
1262 * @param srcCp source connect point
1263 * @param dstCp destination connect point
1264 * @return the path
1265 */
1266 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001267
1268 // use SRLinkWeigher to avoid pair links, and also
1269 // avoid going from the spine to the leaf and to the
1270 // spine again, we need to have the leaf as CP1 here.
1271 LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
1272
1273 Set<Path> paths = srManager.topologyService.
1274 getPaths(srManager.topologyService.currentTopology(),
1275 srcCp.deviceId(), dstCp.deviceId(), srLw);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001276
1277 log.debug("Paths obtained from topology service {}", paths);
1278
1279 // We randomly pick a path.
1280 if (paths.isEmpty()) {
1281 return null;
1282 }
1283 int size = paths.size();
1284 int index = RandomUtils.nextInt(0, size);
1285
1286 List<Link> result = Iterables.get(paths, index).links();
1287 log.debug("Randomly picked a path {}", result);
1288
1289 return result;
1290 }
1291
1292 /**
1293 * Deletes a given policy using the parameter supplied.
1294 *
1295 * @param tunnelId the tunnel id
1296 * @param ingress the ingress point
1297 * @param ingressInner the ingress inner vlan id
1298 * @param ingressOuter the ingress outer vlan id
1299 * @param future to perform the async operation
1300 * @param direction the direction: forward or reverse
1301 */
1302 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1303 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1304
1305 String key = generateKey(tunnelId, direction);
1306 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001307 log.error("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001308 if (future != null) {
1309 future.complete(null);
1310 }
1311 return;
1312 }
1313 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1314 int nextId = nextObjective.id();
1315 List<Objective> objectives = Lists.newArrayList();
1316 // We create the forwarding objective.
1317 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1318 ObjectiveContext context = new ObjectiveContext() {
1319 @Override
1320 public void onSuccess(Objective objective) {
1321 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1322 if (future != null) {
1323 future.complete(null);
1324 }
1325 }
1326
1327 @Override
1328 public void onError(Objective objective, ObjectiveError error) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001329 log.error("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001330 if (future != null) {
1331 future.complete(error);
1332 }
1333 }
1334 };
1335 objectives.add(fwdBuilder.remove(context));
1336 // We create the filtering objective to define the
1337 // permit traffic in the switch
1338 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1339 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1340 .builder()
1341 .setTunnelId(tunnelId)
1342 .setVlanId(egressVlan);
1343 filtBuilder.withMeta(treatment.build());
1344 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1345 (objective, error) ->
1346 log.warn("Failed to revoke filterObj for policy {}",
1347 tunnelId, error));
1348 objectives.add(filtBuilder.remove(context));
1349
1350 for (Objective objective : objectives) {
1351 if (objective instanceof ForwardingObjective) {
1352 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1353 } else {
1354 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1355 }
1356 }
1357 }
1358
1359 /**
1360 * Deletes the pseudo wire initiation.
1361 *
1362 * @param l2TunnelId the tunnel id
1363 * @param ingress the ingress connect point
1364 * @param future to perform an async operation
1365 * @param direction the direction: reverse of forward
1366 */
1367 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1368 CompletableFuture<ObjectiveError> future, Direction direction) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001369 log.debug("Starting tearing dowing initation of pseudowire {} for direction {}.",
1370 l2TunnelId, direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001371 String key = generateKey(l2TunnelId, direction);
1372 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001373 log.error("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001374 if (future != null) {
1375 future.complete(null);
1376 }
1377 return;
1378 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001379
1380 // un-comment in case you want to delete groups used by the pw
1381 // however, this will break the update of pseudowires cause the L2 interface group can
1382 // not be deleted (it is referenced by other groups)
1383 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001384 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001385 ObjectiveContext context = new ObjectiveContext() {
1386 @Override
1387 public void onSuccess(Objective objective) {
1388 log.debug("Previous {} next for {} removed", INITIATION, key);
1389 if (future != null) {
1390 future.complete(null);
1391 }
1392 }
1393
1394 @Override
1395 public void onError(Objective objective, ObjectiveError error) {
1396 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1397 if (future != null) {
1398 future.complete(error);
1399 }
1400 }
1401 };
1402 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1403 */
1404
1405 future.complete(null);
1406 l2InitiationNextObjStore.remove(key);
1407 }
1408
1409 /**
1410 * Deletes the pseudo wire termination.
1411 *
1412 * @param l2Tunnel the tunnel
1413 * @param egress the egress connect point
1414 * @param future the async task
1415 * @param direction the direction of the tunnel
1416 */
1417 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1418 ConnectPoint egress,
1419 CompletableFuture<ObjectiveError> future,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001420 Direction direction) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001421 log.debug("Starting tearing down termination for pseudowire {} direction {}.",
1422 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001423 String key = generateKey(l2Tunnel.tunnelId(), direction);
1424 if (!l2TerminationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001425 log.error("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001426 if (future != null) {
1427 future.complete(null);
1428 }
1429 return;
1430 }
1431 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1432 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1433 l2Tunnel.tunnelId(),
1434 egress.port(),
1435 nextObjective.id());
1436 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1437 log.debug("FwdObj for {} {}, " +
1438 "direction {} removed",
1439 TERMINATION,
1440 l2Tunnel.tunnelId(),
1441 direction),
1442 (objective, error) ->
1443 log.warn("Failed to remove fwdObj " +
1444 "for {} {}" +
1445 ", direction {}",
1446 TERMINATION,
1447 l2Tunnel.tunnelId(),
1448 error,
1449 direction));
1450 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1451
1452 // un-comment in case you want to delete groups used by the pw
1453 // however, this will break the update of pseudowires cause the L2 interface group can
1454 // not be deleted (it is referenced by other groups)
1455 /*
1456 context = new ObjectiveContext() {
1457 @Override
1458 public void onSuccess(Objective objective) {
1459 log.debug("Previous {} next for {} removed", TERMINATION, key);
1460 if (future != null) {
1461 future.complete(null);
1462 }
1463 }
1464
1465 @Override
1466 public void onError(Objective objective, ObjectiveError error) {
1467 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1468 if (future != null) {
1469 future.complete(error);
1470 }
1471 }
1472 };
1473 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1474 */
1475
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001476 l2TerminationNextObjStore.remove(key);
1477 future.complete(null);
1478 }
1479
1480 /**
1481 * Utilities to generate pw key.
1482 *
1483 * @param tunnelId the tunnel id
1484 * @param direction the direction of the pw
1485 * @return the key of the store
1486 */
1487 private String generateKey(long tunnelId, Direction direction) {
1488 return String.format("%s-%s", tunnelId, direction);
1489 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001490}