blob: faabdfe7afcc07b0cfb9415928ff9540b88320da [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
19import com.google.common.collect.Iterables;
20import com.google.common.collect.Lists;
21import org.apache.commons.lang3.RandomUtils;
22import org.onlab.packet.Ethernet;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.MplsLabel;
25import org.onlab.packet.VlanId;
26import org.onlab.util.KryoNamespace;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.DefaultLink;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link;
31import org.onosproject.net.Path;
32import org.onosproject.net.PortNumber;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.criteria.Criteria;
38import org.onosproject.net.flowobjective.DefaultFilteringObjective;
39import org.onosproject.net.flowobjective.DefaultForwardingObjective;
40import org.onosproject.net.flowobjective.DefaultNextObjective;
41import org.onosproject.net.flowobjective.DefaultObjectiveContext;
42import org.onosproject.net.flowobjective.FilteringObjective;
43import org.onosproject.net.flowobjective.ForwardingObjective;
44import org.onosproject.net.flowobjective.NextObjective;
45import org.onosproject.net.flowobjective.Objective;
46import org.onosproject.net.flowobjective.ObjectiveContext;
47import org.onosproject.net.flowobjective.ObjectiveError;
48import org.onosproject.segmentrouting.SegmentRoutingManager;
49import org.onosproject.segmentrouting.SegmentRoutingService;
50import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080051import org.onosproject.store.serializers.KryoNamespaces;
52import org.onosproject.store.service.ConsistentMap;
53import org.onosproject.store.service.DistributedSet;
54import org.onosproject.store.service.Serializer;
55import org.onosproject.store.service.Versioned;
56import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
59import java.util.ArrayList;
60import java.util.List;
61import java.util.Set;
62import java.util.concurrent.CompletableFuture;
63import java.util.stream.Collectors;
64
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080065import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
66import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
67import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
68import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
69import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
70import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
71
72/**
73 * Handles pwaas related events.
74 */
75public class DefaultL2TunnelHandler implements L2TunnelHandler {
76
77 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
78
79 private final SegmentRoutingManager srManager;
80 /**
81 * To store the next objectives related to the initiation.
82 */
83 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
84 /**
85 * To store the next objectives related to the termination.
86 */
87 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
88
89 /**
90 * To store policies.
91 */
92 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
93
94 /**
95 * To store tunnels.
96 */
97 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
98
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -070099 /**
100 * To store pending tunnels that need to be installed.
101 */
102 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
103
104 /**
105 * To store pending policies that need to be installed.
106 */
107 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
108
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800109 private final KryoNamespace.Builder l2TunnelKryo;
110
111 /**
112 * Contains transport vlans used for spine-leaf pseudowires.
113 */
114 private final DistributedSet<VlanId> vlanStore;
115
116 /**
117 * Used for determining transport vlans for leaf-spine.
118 */
119 private short transportVlanUpper = 4093, transportVlanLower = 3500;
120
121 private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
122
123 /**
124 * Create a l2 tunnel handler for the deploy and
125 * for the tear down of pseudo wires.
126 *
127 * @param segmentRoutingManager the segment routing manager
128 */
129 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
130 srManager = segmentRoutingManager;
131 l2TunnelKryo = new KryoNamespace.Builder()
132 .register(KryoNamespaces.API)
133 .register(L2Tunnel.class,
134 L2TunnelPolicy.class,
135 DefaultL2Tunnel.class,
136 DefaultL2TunnelPolicy.class,
137 L2Mode.class,
138 MplsLabel.class,
139 VlanId.class,
140 ConnectPoint.class);
141
142 l2InitiationNextObjStore = srManager.
143 storageService.
144 <String, NextObjective>consistentMapBuilder().
145 withName("onos-l2initiation-nextobj-store").
146 withSerializer(Serializer.using(l2TunnelKryo.build())).
147 build();
148
149 l2TerminationNextObjStore = srManager.storageService.
150 <String, NextObjective>consistentMapBuilder()
151 .withName("onos-l2termination-nextobj-store")
152 .withSerializer(Serializer.using(l2TunnelKryo.build()))
153 .build();
154
155 l2PolicyStore = srManager.storageService
156 .<String, L2TunnelPolicy>consistentMapBuilder()
157 .withName("onos-l2-policy-store")
158 .withSerializer(Serializer.using(l2TunnelKryo.build()))
159 .build();
160
161 l2TunnelStore = srManager.storageService
162 .<String, L2Tunnel>consistentMapBuilder()
163 .withName("onos-l2-tunnel-store")
164 .withSerializer(Serializer.using(l2TunnelKryo.build()))
165 .build();
166
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700167 pendingL2PolicyStore = srManager.storageService
168 .<String, L2TunnelPolicy>consistentMapBuilder()
169 .withName("onos-l2-pending-policy-store")
170 .withSerializer(Serializer.using(l2TunnelKryo.build()))
171 .build();
172
173 pendingL2TunnelStore = srManager.storageService
174 .<String, L2Tunnel>consistentMapBuilder()
175 .withName("onos-l2-pending-tunnel-store")
176 .withSerializer(Serializer.using(l2TunnelKryo.build()))
177 .build();
178
179
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800180 vlanStore = srManager.storageService.<VlanId>setBuilder()
181 .withName("onos-transport-vlan-store")
182 .withSerializer(Serializer.using(
183 new KryoNamespace.Builder()
184 .register(KryoNamespaces.API)
185 .build()))
186 .build()
187 .asDistributedSet();
188 }
189
190 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800191 * Used by manager only in initialization.
192 */
193 @Override
194 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800195 // Since we have no pseudowires in netcfg there
196 // is nothing to do in initialization.
197 // I leave it here because potentially we might need to
198 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800199 }
200
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700201 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700202 public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
203 if (!pending) {
204 List<L2Tunnel> tunnels = getL2Tunnels();
205 List<L2TunnelPolicy> policies = getL2Policies();
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700206
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700207 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700208 .map(l2Tunnel -> {
209 L2TunnelPolicy policy = null;
210 for (L2TunnelPolicy l2Policy : policies) {
211 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
212 policy = l2Policy;
213 break;
214 }
215 }
216
217 return new DefaultL2TunnelDescription(l2Tunnel, policy);
218 })
219 .collect(Collectors.toSet());
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700220 } else {
221 List<L2Tunnel> tunnels = getL2PendingTunnels();
222 List<L2TunnelPolicy> policies = getL2PendingPolicies();
223
224 return tunnels.stream()
225 .map(l2Tunnel -> {
226 L2TunnelPolicy policy = null;
227 for (L2TunnelPolicy l2Policy : policies) {
228 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
229 policy = l2Policy;
230 break;
231 }
232 }
233
234 return new DefaultL2TunnelDescription(l2Tunnel, policy);
235 })
236 .collect(Collectors.toSet());
237 }
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700238 }
239
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800240 @Override
241 public List<L2TunnelPolicy> getL2Policies() {
242
243 return new ArrayList<>(l2PolicyStore
244 .values()
245 .stream()
246 .map(Versioned::value)
247 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800248 }
249
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800250 @Override
251 public List<L2Tunnel> getL2Tunnels() {
252
253 return new ArrayList<>(l2TunnelStore
254 .values()
255 .stream()
256 .map(Versioned::value)
257 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800258 }
259
260 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700261 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800262
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700263 return new ArrayList<>(pendingL2PolicyStore
264 .values()
265 .stream()
266 .map(Versioned::value)
267 .collect(Collectors.toList()));
268 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800269
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700270 @Override
271 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800272
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700273 return new ArrayList<>(pendingL2TunnelStore
274 .values()
275 .stream()
276 .map(Versioned::value)
277 .collect(Collectors.toList()));
278 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800279
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700280 /**
281 * Manages intermediate filtering rules.
282 *
283 * For leaf-spine-spine pseudowires we need to install a special filtering
284 * rule in the intermediate spine for the appropriate transport vlan.
285 *
286 * @param pw The pseudowire, it will have the path and the transport vlan.
287 */
288 private Result manageIntermediateFiltering(L2TunnelDescription pw, boolean leafSpinePw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800289
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700290 // only leaf-spine-spine should need intermediate rules for now
291 if (!leafSpinePw) {
292 return Result.SUCCESS;
293 }
294 if (pw.l2Tunnel().pathUsed().size() != 2) {
295 return Result.SUCCESS;
296 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800297
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700298 List<Link> path = pw.l2Tunnel().pathUsed();
299 DeviceId intermediateSpineId = pw.l2Tunnel().pathUsed().get(0).dst().deviceId();
300 L2Tunnel l2Tunnel = pw.l2Tunnel();
301
302 log.info("Installing intermediate filtering rules for spine {} , for pseudowire {}",
303 intermediateSpineId, pw.l2Tunnel().tunnelId());
304
305 MacAddress dstMac;
306 try {
307 dstMac = srManager.deviceConfiguration().getDeviceMac(intermediateSpineId);
308 } catch (Exception e) {
309 log.info("Device not found in configuration, no programming of MAC address");
310 dstMac = null;
311 }
312
313 PortNumber inPort;
314
315 inPort = path.get(0).dst().port();
316
317 log.debug("Populating filtering objective for pseudowire transport" +
318 " with vlan = {}, port = {}, mac = {} for device {}",
319 l2Tunnel.transportVlan(),
320 inPort,
321 dstMac,
322 intermediateSpineId);
323
324 FilteringObjective.Builder filteringObjectiveBuilder =
325 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
326 DefaultObjectiveContext context = new DefaultObjectiveContext((objective) ->
327 log.debug("Special filtObj for " +
328 "for {} populated",
329 l2Tunnel.tunnelId()),
330 (objective, error) ->
331 log.warn("Failed to populate " +
332 "special filtObj " +
333 "rule for {}: {}",
334 l2Tunnel.tunnelId(), error));
335 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
336 filteringObjectiveBuilder.withMeta(treatment.build());
337 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
338
339 inPort = path.get(1).src().port();
340
341 log.debug("Populating filtering objective for pseudowire transport" +
342 " with vlan = {}, port = {}, mac = {} for device {}",
343 l2Tunnel.transportVlan(),
344 inPort,
345 dstMac,
346 intermediateSpineId);
347
348 filteringObjectiveBuilder =
349 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
350 context = new DefaultObjectiveContext((objective) ->
351 log.debug("Special filtObj for " + "for {} populated",
352 l2Tunnel.tunnelId()),
353 (objective, error) ->
354 log.warn("Failed to populate " +
355 "special filtObj " +
356 "rule for {}: {}",
357 l2Tunnel.tunnelId(), error));
358 treatment = DefaultTrafficTreatment.builder();
359 filteringObjectiveBuilder.withMeta(treatment.build());
360 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
361
362 return Result.SUCCESS;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800363 }
364
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800365 /**
366 * Returns the new vlan id for an ingress point of a
367 * pseudowire. For double tagged, it is the outer,
368 * For single tagged it is the single tag, and for
369 * inner it is None.
370 *
371 * @param ingressOuter vlanid of ingress outer
372 * @param ingressInner vlanid of ingress inner
373 * @param egressOuter vlanid of egress outer
374 * @param egressInner vlanid of egress inner
375 * @return returns the vlan id which will be installed at vlan table 1.
376 */
377 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
378 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800379 // validity of vlan combinations was checked at verifyPseudowire
380 if (!(ingressOuter.equals(VlanId.NONE))) {
381 return egressOuter;
382 } else if (!(ingressInner.equals(VlanId.NONE))) {
383 return egressInner;
384 } else {
385 return VlanId.vlanId("None");
386 }
387 }
388
389 /**
390 * Determines vlan used for transporting the pw traffic.
391 *
392 * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
393 * and also make sure to add the popVlan instruction.
394 * For spine-leaf pws we choose the highest vlan value available from a certain range.
395 *
396 * @param spinePw if the pw is leaf-spine.
397 * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
398 * then the pw is transported untagged.
399 */
400 private VlanId determineTransportVlan(boolean spinePw) {
401
402 if (!spinePw) {
403
404 log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
405 return UNTAGGED_TRANSPORT_VLAN;
406 } else {
407 for (short i = transportVlanUpper; i > transportVlanLower; i--) {
408
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700409 VlanId vlanToUse = VlanId.vlanId(i);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800410 if (!vlanStore.contains(vlanToUse)) {
411
412 vlanStore.add(vlanToUse);
413 log.info("Transport vlan {} for pseudowire!", vlanToUse);
414 return vlanToUse;
415 }
416 }
417
418 log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
419 "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
420 return UNTAGGED_TRANSPORT_VLAN;
421 }
422 }
423
424 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700425 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700426 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700427 * @param path The path to traverse.
428 * @return The devices on the path with the order they
429 * are traversed.
430 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700431 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700432
433 // iterate over links and get all devices in the order
434 // we find them
435 List<DeviceId> deviceList = new ArrayList<DeviceId>();
436 for (Link link : path) {
437 if (!deviceList.contains(link.src().deviceId())) {
438 deviceList.add(link.src().deviceId());
439 }
440 if (!deviceList.contains(link.dst().deviceId())) {
441 deviceList.add(link.dst().deviceId());
442 }
443 }
444
445 return deviceList;
446 }
447
448 /**
449 * Returns true if path is valid according to the current logic.
450 * For example : leaf to spine pseudowires can be either leaf-spine or
451 * leaf-spine-spine. However, in the configuration we might specify spine device
452 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
453 * two cases and need to provision for them.
454 *
455 * If we have a leaf to leaf pseudowire then all the intermediate devices must
456 * be spines. However, in case of paired switches we can have two leafs interconnected
457 * with each other directly.
458 *
459 * @param path the chosen path
460 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700461 * @return True if path size is valid, false otherwise.
462 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700463 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
464
465 List<DeviceId> devices = getDevicesOnPath(path);
466 if (devices.size() < 2) {
467 log.error("Path size for pseudowire should be greater than 1!");
468 return false;
469 }
470
471 try {
472 if (leafSpinePw) {
473 // can either be leaf-spine-spine or leaf-spine
474 // first device is leaf, all other must be spines
475 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
476 // if first device is a leaf then all other must be spines
477 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
478 devices.remove(0);
479 for (DeviceId devId : devices) {
480 log.debug("Device {} should be a spine!", devId);
481 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
482 return false;
483 }
484 }
485 } else {
486 // if first device is spine, last device must be a leaf
487 // all other devices must be spines
488 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
489 return false;
490 }
491 devices.remove(devices.size() - 1);
492 for (DeviceId devId : devices) {
493 log.debug("Device {} should be a spine!", devId);
494 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
495 return false;
496 }
497 }
498 }
499 } else {
500 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
501 // or leaf-spine-spine-leaf
502 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
503 // check first device, needs to be a leaf
504 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
505 return false;
506 }
507 // check last device, needs to be a leaf
508 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
509 return false;
510 }
511 // remove these devices, rest must all be spines
512 devices.remove(0);
513 devices.remove(devices.size() - 1);
514 for (DeviceId devId : devices) {
515 log.debug("Device {} should be a spine!", devId);
516 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
517 return false;
518 }
519 }
520
521 }
522 } catch (DeviceConfigNotFoundException e) {
523 log.error("Device not found in configuration : {}", e);
524 return false;
525 }
526
527 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700528 }
529
530 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800531 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800532 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700533 * @param pw The pseudowire to deploy
534 * @param removeFromPending if to remove the pseudowire from the pending store
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800535 * @return result of pseudowire deployment
536 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700537 public Result deployPseudowire(L2TunnelDescription pw, boolean removeFromPending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800538
539 Result result;
540 long l2TunnelId;
541
542 l2TunnelId = pw.l2Tunnel().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800543 // The tunnel id cannot be 0.
544 if (l2TunnelId == 0) {
545 log.warn("Tunnel id id must be > 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700546 return Result.WRONG_PARAMETERS
547 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800548 }
549
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700550 // leafSpinePw determines if this is a leaf-leaf
551 // or leaf-spine pseudowire
552 boolean leafSpinePw;
553 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
554 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
555 try {
556 // differentiate between leaf-leaf pseudowires and leaf-spine
557 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
558 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
559 log.error("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700560 return Result.WRONG_PARAMETERS
561 .appendError("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700562 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
563 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
564 leafSpinePw = false;
565 } else {
566 leafSpinePw = true;
567 }
568 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700569 log.error("Device for pseudowire connection points does not exist in the configuration");
570 return Result.INTERNAL_ERROR
571 .appendError("Device for pseudowire connection points does not exist in the configuration");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700572 }
573
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800574 // get path here, need to use the same for fwd and rev direction
575 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
576 pw.l2TunnelPolicy().cP2());
577 if (path == null) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700578 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
579 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800580 }
581
582 Link fwdNextHop;
583 Link revNextHop;
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700584 if (!isValidPath(path, leafSpinePw)) {
585 log.error("Deploying process : Path for pseudowire {} is not valid",
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700586 l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700587 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800588 }
589
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700590 // oneHope flag is used to determine if we need to
591 // install transit mpls rules
592 boolean oneHop = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700593 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700594 oneHop = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700595 }
596
597 fwdNextHop = path.get(0);
598 revNextHop = reverseLink(path.get(path.size() - 1));
599
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800600 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700601 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800602
603 // next hops for next objectives
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800604 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
605
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800606 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
607 pw.l2TunnelPolicy().cP1InnerTag(),
608 pw.l2TunnelPolicy().cP2OuterTag(),
609 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800610 // We establish the tunnel.
611 // result.nextId will be used in fwd
612 result = deployPseudoWireInit(pw.l2Tunnel(),
613 pw.l2TunnelPolicy().cP1(),
614 pw.l2TunnelPolicy().cP2(),
615 FWD,
616 fwdNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700617 leafSpinePw,
618 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800619 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800620 if (result != SUCCESS) {
621 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700622 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800623 }
624
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800625 // We create the policy.
626 result = deployPolicy(l2TunnelId,
627 pw.l2TunnelPolicy().cP1(),
628 pw.l2TunnelPolicy().cP1InnerTag(),
629 pw.l2TunnelPolicy().cP1OuterTag(),
630 egressVlan,
631 result.nextId);
632 if (result != SUCCESS) {
633 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700634 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800635 }
636
637 // We terminate the tunnel
638 result = deployPseudoWireTerm(pw.l2Tunnel(),
639 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800640 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800641 FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700642 leafSpinePw,
643 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800644
645 if (result != SUCCESS) {
646 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700647 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800648 }
649
650 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
651
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800652 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
653 pw.l2TunnelPolicy().cP2InnerTag(),
654 pw.l2TunnelPolicy().cP1OuterTag(),
655 pw.l2TunnelPolicy().cP1InnerTag());
656
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800657 // We establish the reverse tunnel.
658 result = deployPseudoWireInit(pw.l2Tunnel(),
659 pw.l2TunnelPolicy().cP2(),
660 pw.l2TunnelPolicy().cP1(),
661 REV,
662 revNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700663 leafSpinePw,
664 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800665 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800666 if (result != SUCCESS) {
667 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700668 return Result.INTERNAL_ERROR
669 .appendError("Error in deploying pseudowire initiation for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800670 }
671
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800672
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800673 result = deployPolicy(l2TunnelId,
674 pw.l2TunnelPolicy().cP2(),
675 pw.l2TunnelPolicy().cP2InnerTag(),
676 pw.l2TunnelPolicy().cP2OuterTag(),
677 egressVlan,
678 result.nextId);
679 if (result != SUCCESS) {
680 log.info("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700681 return Result.INTERNAL_ERROR
682 .appendError("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800683 }
684
685 result = deployPseudoWireTerm(pw.l2Tunnel(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700686 pw.l2TunnelPolicy().cP1(),
687 egressVlan,
688 REV,
689 leafSpinePw,
690 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800691
692 if (result != SUCCESS) {
693 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700694 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800695 }
696
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700697 result = manageIntermediateFiltering(pw, leafSpinePw);
698 if (result != SUCCESS) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700699 log.info("Deploying process : Error in installing intermediate rules for tagged transport");
700 return Result.INTERNAL_ERROR.appendError("Error in installing intermediate rules for tagged transport");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700701 }
702
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800703 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
704
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700705 // Populate stores as the final step of the process
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800706 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
707 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700708 // if removeFromPending then remove the information from the pending stores.
709 if (removeFromPending) {
710 // check existence of tunnels/policy in the pending store, if one is missing abort!
711 Versioned<L2Tunnel> l2TunnelVersioned = pendingL2TunnelStore.get(Long.toString(l2TunnelId));
712 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pendingL2PolicyStore.get(Long.toString(l2TunnelId));
713 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
714 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {} in pending store",
715 l2TunnelId);
716 } else {
717 pendingL2TunnelStore.remove(Long.toString(l2TunnelId));
718 pendingL2PolicyStore.remove(Long.toString(l2TunnelId));
719 }
720 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800721
722 return Result.SUCCESS;
723 }
724
725 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700726 * Tears down connection points of pseudowires. We can either tear down both connection points,
727 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800728 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700729 * @param l2TunnelId The tunnel id for this pseudowire.
730 * @param tearDownFirst Boolean, true if we want to tear down cp1
731 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700732 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
733 * WRONG_PARAMETERS or INTERNAL_ERROR otherwise
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800734 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700735 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst, boolean tearDownSecond) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800736
737 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
738 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
739
740 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
741 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
742
743 if (l2TunnelId == 0) {
744 log.warn("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700745 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800746 }
747
748 // check existence of tunnels/policy in the store, if one is missing abort!
749 Versioned<L2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
750 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
751 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
752 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700753 return Result.INTERNAL_ERROR
754 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800755 }
756
757 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700758 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800759
760 // remove the tunnels and the policies from the store
761 l2PolicyStore.remove(Long.toString(l2TunnelId));
762 l2TunnelStore.remove(Long.toString(l2TunnelId));
763
764 // remove the reserved transport vlan
765 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
766 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
767 }
768
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700769 if (tearDownFirst) {
770 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800771
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700772 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
773 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
774 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
775 pwToRemove.l2TunnelPolicy().cP2InnerTag());
776 deletePolicy(l2TunnelId,
777 pwToRemove.l2TunnelPolicy().cP1(),
778 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
779 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
780 egressVlan,
781 fwdInitNextFuture,
782 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800783
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700784 fwdInitNextFuture.thenAcceptAsync(status -> {
785 if (status == null) {
786 // Finally we will tear down the pseudo wire.
787 tearDownPseudoWireInit(l2TunnelId,
788 pwToRemove.l2TunnelPolicy().cP1(),
789 fwdTermNextFuture,
790 FWD);
791 }
792 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800793
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700794 fwdTermNextFuture.thenAcceptAsync(status -> {
795 if (status == null) {
796 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
797 pwToRemove.l2TunnelPolicy().cP2(),
798 null,
799 FWD);
800 }
801 });
802 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800803
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700804 if (tearDownSecond) {
805 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800806
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700807 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
808 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
809 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
810 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800811
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700812 // We do the same operations on the reverse side.
813 deletePolicy(l2TunnelId,
814 pwToRemove.l2TunnelPolicy().cP2(),
815 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
816 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
817 egressVlan,
818 revInitNextFuture,
819 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800820
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700821 revInitNextFuture.thenAcceptAsync(status -> {
822 if (status == null) {
823 tearDownPseudoWireInit(l2TunnelId,
824 pwToRemove.l2TunnelPolicy().cP2(),
825 revTermNextFuture,
826 REV);
827 }
828 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800829
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700830 revTermNextFuture.thenAcceptAsync(status -> {
831 if (status == null) {
832 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
833 pwToRemove.l2TunnelPolicy().cP1(),
834 null,
835 REV);
836 }
837 });
838 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800839
840 return Result.SUCCESS;
841 }
842
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700843 /**
844 * Helper function for removing a single pseudowire.
845 * <p>
846 * No mastership of CP1 is checked, because it can be called from
847 * the CLI for removal of pseudowires.
848 *
849 * @param l2TunnelId the id of the pseudowire to tear down
850 * @return Returns SUCCESS if no error is obeserved or an appropriate
851 * error on a failure
852 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700853 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700854 return tearDownConnectionPoints(l2TunnelId, true, true);
855 }
856
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700857 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800858 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
859
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800860 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800861
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700862 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800863 log.info("Removing pseudowire {}", tunnelId);
864
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700865 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700866 if (result != Result.SUCCESS) {
867 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800868 }
869 }
870 }
871
872 /**
873 * Handles the policy establishment which consists in
874 * create the filtering and forwarding objectives related
875 * to the initiation and termination.
876 *
877 * @param tunnelId the tunnel id
878 * @param ingress the ingress point
879 * @param ingressInner the ingress inner tag
880 * @param ingressOuter the ingress outer tag
881 * @param nextId the next objective id
882 * @param egressVlan Vlan-id to set, depends on ingress vlan
883 * combinations. For example, if pw is double tagged
884 * then this is the value of the outer vlan, if single
885 * tagged then it is the new value of the single tag.
886 * Should be None for untagged traffic.
887 * @return the result of the operation
888 */
889 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
890 VlanId ingressOuter, VlanId egressVlan, int nextId) {
891
892 List<Objective> objectives = Lists.newArrayList();
893 // We create the forwarding objective for supporting
894 // the l2 tunnel.
895 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
896 // We create and add objective context.
897 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
898 log.debug("FwdObj for tunnel {} populated", tunnelId),
899 (objective, error) ->
900 log.warn("Failed to populate fwdrObj " +
901 "for tunnel {}", tunnelId, error));
902 objectives.add(fwdBuilder.add(context));
903
904 // We create the filtering objective to define the
905 // permit traffic in the switch
906 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
907
908 // We add the metadata.
909 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
910 .builder()
911 .setTunnelId(tunnelId)
912 .setVlanId(egressVlan);
913 filtBuilder.withMeta(treatment.build());
914
915 // We create and add objective context.
916 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
917 (objective, error) -> log.warn("Failed to populate filterObj for " +
918 "tunnel {}", tunnelId, error));
919 objectives.add(filtBuilder.add(context));
920
921 for (Objective objective : objectives) {
922 if (objective instanceof ForwardingObjective) {
923 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
924 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
925 } else {
926 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
927 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
928 }
929 }
930 return SUCCESS;
931 }
932
933 /**
934 * Handles the tunnel establishment which consists in
935 * create the next objectives related to the initiation.
936 *
937 * @param l2Tunnel the tunnel to deploy
938 * @param ingress the ingress connect point
939 * @param egress the egress connect point
940 * @param direction the direction of the pw
941 * @param spinePw if the pseudowire involves a spine switch
942 * @return the result of the operation
943 */
944 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800945 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700946 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800947
948 if (nextHop == null) {
949 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
950 return WRONG_PARAMETERS;
951 }
952
953 // We create the next objective without the metadata
954 // context and id. We check if it already exists in the
955 // store. If not we store as it is in the store.
956 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
957 nextHop.src(),
958 nextHop.dst(),
959 l2Tunnel,
960 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800961 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700962 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800963 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800964
965 if (nextObjectiveBuilder == null) {
966 return INTERNAL_ERROR;
967 }
968 // We set the metadata. We will use this metadata
969 // to inform the driver we are doing a l2 tunnel.
970 TrafficSelector metadata = DefaultTrafficSelector
971 .builder()
972 .matchTunnelId(l2Tunnel.tunnelId())
973 .build();
974 nextObjectiveBuilder.withMeta(metadata);
975 int nextId = srManager.flowObjectiveService.allocateNextId();
976 if (nextId < 0) {
977 log.warn("Not able to allocate a next id for initiation");
978 return INTERNAL_ERROR;
979 }
980 nextObjectiveBuilder.withId(nextId);
981 String key = generateKey(l2Tunnel.tunnelId(), direction);
982 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
983 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
984 log.debug("Initiation l2 tunnel rule " +
985 "for {} populated",
986 l2Tunnel.tunnelId()),
987 (objective, error) ->
988 log.warn("Failed to populate Initiation " +
989 "l2 tunnel rule for {}: {}",
990 l2Tunnel.tunnelId(), error));
991 NextObjective nextObjective = nextObjectiveBuilder.add(context);
992 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
993 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
994 l2Tunnel.tunnelId(), nextObjective.id());
995 Result result = SUCCESS;
996 result.nextId = nextObjective.id();
997 return result;
998 }
999
1000 /**
1001 * Handles the tunnel termination, which consists in the creation
1002 * of a forwarding objective and a next objective.
1003 *
1004 * @param l2Tunnel the tunnel to terminate
1005 * @param egress the egress point
1006 * @param egressVlan the expected vlan at egress
1007 * @param direction the direction
1008 * @param spinePw if the pseudowire involves a spine switch
1009 * @return the result of the operation
1010 */
1011 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001012 VlanId egressVlan, Direction direction, boolean spinePw, boolean oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001013
1014 // We create the group relative to the termination.
1015 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1016 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001017 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001018 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001019 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001020 if (nextObjectiveBuilder == null) {
1021 return INTERNAL_ERROR;
1022 }
1023 TrafficSelector metadata = DefaultTrafficSelector
1024 .builder()
1025 .matchVlanId(egressVlan)
1026 .build();
1027 nextObjectiveBuilder.withMeta(metadata);
1028 int nextId = srManager.flowObjectiveService.allocateNextId();
1029 if (nextId < 0) {
1030 log.warn("Not able to allocate a next id for initiation");
1031 return INTERNAL_ERROR;
1032 }
1033 nextObjectiveBuilder.withId(nextId);
1034 String key = generateKey(l2Tunnel.tunnelId(), direction);
1035 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1036 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1037 "for {} populated",
1038 l2Tunnel.tunnelId()),
1039 (objective, error) -> log.warn("Failed to populate " +
1040 "termination l2 tunnel " +
1041 "rule for {}: {}",
1042 l2Tunnel.tunnelId(),
1043 error));
1044 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1045 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1046 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1047 l2Tunnel.tunnelId(), nextObjective.id());
1048
1049 // We create the flow relative to the termination.
1050 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1051 egress.port(), nextObjective.id());
1052 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1053 l2Tunnel.tunnelId()),
1054 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1055 " for tunnel termination {}",
1056 l2Tunnel.tunnelId(), error));
1057 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1058 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1059 nextId, l2Tunnel.tunnelId());
1060
1061 if (spinePw) {
1062
1063 // determine the input port at the
1064 PortNumber inPort;
1065
1066 if (egress.deviceId().
1067 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1068 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1069 } else {
1070 inPort = l2Tunnel.pathUsed().get(0).src().port();
1071 }
1072
1073 MacAddress dstMac;
1074 try {
1075 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1076 } catch (Exception e) {
1077 log.info("Device not found in configuration, no programming of MAC address");
1078 dstMac = null;
1079 }
1080
1081 log.info("Populating filtering objective for pseudowire transport" +
1082 " with vlan = {}, port = {}, mac = {}",
1083 l2Tunnel.transportVlan(),
1084 inPort,
1085 dstMac);
1086 FilteringObjective.Builder filteringObjectiveBuilder =
1087 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1088 context = new DefaultObjectiveContext(( objective ) ->
1089 log.debug("Special filtObj for " + "for {} populated",
1090 l2Tunnel.tunnelId()),
1091 ( objective, error ) ->
1092 log.warn("Failed to populate " +
1093 "special filtObj " +
1094 "rule for {}: {}",
1095 l2Tunnel.tunnelId(), error));
1096 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1097 filteringObjectiveBuilder.withMeta(treatment.build());
1098 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1099 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1100 l2Tunnel.tunnelId(),
1101 inPort);
1102 }
1103
1104 return SUCCESS;
1105 }
1106
1107
1108 /**
1109 * Creates the filtering objective according to a given port and vlanid.
1110 *
1111 * @param inPort the in port
1112 * @param vlanId the inner vlan tag
1113 * @return the filtering objective
1114 */
1115 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1116 VlanId vlanId,
1117 MacAddress dstMac) {
1118
1119 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1120 vlanId,
1121 inPort,
1122 dstMac);
1123 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1124 .builder()
1125 .withKey(Criteria.matchInPort(inPort))
1126 .addCondition(Criteria.matchVlanId(vlanId))
1127 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1128 .permit()
1129 .fromApp(srManager.appId());
1130
1131 if (dstMac != null) {
1132 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1133 }
1134
1135 return fwdBuilder;
1136 }
1137
1138 /**
1139 * Creates the filtering objective according to a given policy.
1140 *
1141 * @param inPort the in port
1142 * @param innerTag the inner vlan tag
1143 * @param outerTag the outer vlan tag
1144 * @return the filtering objective
1145 */
1146 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1147
1148 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1149 return DefaultFilteringObjective
1150 .builder()
1151 .withKey(Criteria.matchInPort(inPort))
1152 .addCondition(Criteria.matchInnerVlanId(innerTag))
1153 .addCondition(Criteria.matchVlanId(outerTag))
1154 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1155 .permit()
1156 .fromApp(srManager.appId());
1157 }
1158
1159 /**
1160 * Creates the forwarding objective for the termination.
1161 *
1162 * @param pwLabel the pseudo wire label
1163 * @param tunnelId the tunnel id
1164 * @param egressPort the egress port
1165 * @param nextId the next step
1166 * @return the forwarding objective to support the termination
1167 */
1168 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1169 PortNumber egressPort, int nextId) {
1170
1171 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1172 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1173 // The flow has to match on the pw label and bos
1174 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1175 trafficSelector.matchMplsLabel(pwLabel);
1176 trafficSelector.matchMplsBos(true);
1177 // The flow has to decrement ttl, restore ttl in
1178 // pop mpls, set tunnel id and port.
1179 trafficTreatment.decMplsTtl();
1180 trafficTreatment.copyTtlIn();
1181 trafficTreatment.popMpls();
1182 trafficTreatment.setTunnelId(tunnelId);
1183 trafficTreatment.setOutput(egressPort);
1184
1185 return DefaultForwardingObjective
1186 .builder()
1187 .fromApp(srManager.appId())
1188 .makePermanent()
1189 .nextStep(nextId)
1190 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1191 .withSelector(trafficSelector.build())
1192 .withTreatment(trafficTreatment.build())
1193 .withFlag(VERSATILE);
1194 }
1195
1196 /**
1197 * Creates the forwarding objective for the initiation.
1198 *
1199 * @param tunnelId the tunnel id
1200 * @param inPort the input port
1201 * @param nextId the next step
1202 * @return the forwarding objective to support the initiation.
1203 */
1204 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1205
1206 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1207
1208 // The flow has to match on the mpls logical
1209 // port and the tunnel id.
1210 trafficSelector.matchTunnelId(tunnelId);
1211 trafficSelector.matchInPort(inPort);
1212
1213 return DefaultForwardingObjective
1214 .builder()
1215 .fromApp(srManager.appId())
1216 .makePermanent()
1217 .nextStep(nextId)
1218 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1219 .withSelector(trafficSelector.build())
1220 .withFlag(VERSATILE);
1221
1222 }
1223
1224 /**
1225 * Creates the next objective according to a given
1226 * pipeline. We don't set the next id and we don't
1227 * create the final meta to check if we are re-using
1228 * the same next objective for different tunnels.
1229 *
1230 * @param pipeline the pipeline to support
1231 * @param srcCp the source port
1232 * @param dstCp the destination port
1233 * @param l2Tunnel the tunnel to support
1234 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001235 * @param oneHop if the pw only has one hop, push only pw label
1236 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1237 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001238 * @return the next objective to support the pipeline
1239 */
1240 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1241 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001242 DeviceId egressId, boolean leafSpinePw,
1243 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001244 NextObjective.Builder nextObjBuilder;
1245 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1246 if (pipeline == INITIATION) {
1247 nextObjBuilder = DefaultNextObjective
1248 .builder()
1249 .withType(NextObjective.Type.SIMPLE)
1250 .fromApp(srManager.appId());
1251 // The pw label is the bottom of stack. It has to
1252 // be different -1.
1253 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1254 log.warn("Pw label not configured");
1255 return null;
1256 }
1257 treatmentBuilder.pushMpls();
1258 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1259 treatmentBuilder.setMplsBos(true);
1260 treatmentBuilder.copyTtlOut();
1261
1262 // If the inter-co label is present we have to set the label.
1263 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1264 treatmentBuilder.pushMpls();
1265 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1266 treatmentBuilder.setMplsBos(false);
1267 treatmentBuilder.copyTtlOut();
1268 }
1269
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001270 // if not oneHop install transit mpls labels also
1271 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001272 // We retrieve the sr label from the config
1273 // specific for pseudowire traffic
1274 // using the egress leaf device id.
1275 MplsLabel srLabel;
1276 try {
1277 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1278
1279 } catch (DeviceConfigNotFoundException e) {
1280 log.warn("Sr label for pw traffic not configured");
1281 return null;
1282 }
1283
1284 treatmentBuilder.pushMpls();
1285 treatmentBuilder.setMpls(srLabel);
1286 treatmentBuilder.setMplsBos(false);
1287 treatmentBuilder.copyTtlOut();
1288 }
1289
1290 // We have to rewrite the src and dst mac address.
1291 MacAddress ingressMac;
1292 try {
1293 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1294 } catch (DeviceConfigNotFoundException e) {
1295 log.warn("Was not able to find the ingress mac");
1296 return null;
1297 }
1298 treatmentBuilder.setEthSrc(ingressMac);
1299 MacAddress neighborMac;
1300 try {
1301 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1302 } catch (DeviceConfigNotFoundException e) {
1303 log.warn("Was not able to find the neighbor mac");
1304 return null;
1305 }
1306 treatmentBuilder.setEthDst(neighborMac);
1307
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001308 // if true we need to pop the vlan because
1309 // we instantiate a leaf to leaf pseudowire
1310 if (!leafSpinePw) {
1311 log.info("We should carry this traffic UNTAGGED!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001312 treatmentBuilder.popVlan();
1313 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001314
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001315 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001316 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001317 } else {
1318 // We create the next objective which
1319 // will be a simple l2 group.
1320 nextObjBuilder = DefaultNextObjective
1321 .builder()
1322 .withType(NextObjective.Type.SIMPLE)
1323 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001324
1325 // for termination point we use the outer vlan of the
1326 // encapsulated packet
1327 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001328 }
1329
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001330 treatmentBuilder.setOutput(srcCp.port());
1331 nextObjBuilder.addTreatment(treatmentBuilder.build());
1332 return nextObjBuilder;
1333 }
1334
1335 /**
1336 * Reverses a link.
1337 *
1338 * @param link link to be reversed
1339 * @return the reversed link
1340 */
1341 private Link reverseLink(Link link) {
1342
1343 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1344
1345 linkBuilder.src(link.dst());
1346 linkBuilder.dst(link.src());
1347 linkBuilder.type(link.type());
1348 linkBuilder.providerId(link.providerId());
1349
1350 return linkBuilder.build();
1351 }
1352
1353 /**
1354 * Returns the path betwwen two connect points.
1355 *
1356 * @param srcCp source connect point
1357 * @param dstCp destination connect point
1358 * @return the path
1359 */
1360 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001361 Set<Path> paths = srManager.topologyService.getPaths(
1362 srManager.topologyService.currentTopology(),
1363 srcCp.deviceId(), dstCp.deviceId());
1364
1365 log.debug("Paths obtained from topology service {}", paths);
1366
1367 // We randomly pick a path.
1368 if (paths.isEmpty()) {
1369 return null;
1370 }
1371 int size = paths.size();
1372 int index = RandomUtils.nextInt(0, size);
1373
1374 List<Link> result = Iterables.get(paths, index).links();
1375 log.debug("Randomly picked a path {}", result);
1376
1377 return result;
1378 }
1379
1380 /**
1381 * Deletes a given policy using the parameter supplied.
1382 *
1383 * @param tunnelId the tunnel id
1384 * @param ingress the ingress point
1385 * @param ingressInner the ingress inner vlan id
1386 * @param ingressOuter the ingress outer vlan id
1387 * @param future to perform the async operation
1388 * @param direction the direction: forward or reverse
1389 */
1390 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1391 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1392
1393 String key = generateKey(tunnelId, direction);
1394 if (!l2InitiationNextObjStore.containsKey(key)) {
1395 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1396 if (future != null) {
1397 future.complete(null);
1398 }
1399 return;
1400 }
1401 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1402 int nextId = nextObjective.id();
1403 List<Objective> objectives = Lists.newArrayList();
1404 // We create the forwarding objective.
1405 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1406 ObjectiveContext context = new ObjectiveContext() {
1407 @Override
1408 public void onSuccess(Objective objective) {
1409 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1410 if (future != null) {
1411 future.complete(null);
1412 }
1413 }
1414
1415 @Override
1416 public void onError(Objective objective, ObjectiveError error) {
1417 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1418 if (future != null) {
1419 future.complete(error);
1420 }
1421 }
1422 };
1423 objectives.add(fwdBuilder.remove(context));
1424 // We create the filtering objective to define the
1425 // permit traffic in the switch
1426 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1427 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1428 .builder()
1429 .setTunnelId(tunnelId)
1430 .setVlanId(egressVlan);
1431 filtBuilder.withMeta(treatment.build());
1432 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1433 (objective, error) ->
1434 log.warn("Failed to revoke filterObj for policy {}",
1435 tunnelId, error));
1436 objectives.add(filtBuilder.remove(context));
1437
1438 for (Objective objective : objectives) {
1439 if (objective instanceof ForwardingObjective) {
1440 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1441 } else {
1442 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1443 }
1444 }
1445 }
1446
1447 /**
1448 * Deletes the pseudo wire initiation.
1449 *
1450 * @param l2TunnelId the tunnel id
1451 * @param ingress the ingress connect point
1452 * @param future to perform an async operation
1453 * @param direction the direction: reverse of forward
1454 */
1455 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1456 CompletableFuture<ObjectiveError> future, Direction direction) {
1457
1458 String key = generateKey(l2TunnelId, direction);
1459 if (!l2InitiationNextObjStore.containsKey(key)) {
1460 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1461 if (future != null) {
1462 future.complete(null);
1463 }
1464 return;
1465 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001466
1467 // un-comment in case you want to delete groups used by the pw
1468 // however, this will break the update of pseudowires cause the L2 interface group can
1469 // not be deleted (it is referenced by other groups)
1470 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001471 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001472 ObjectiveContext context = new ObjectiveContext() {
1473 @Override
1474 public void onSuccess(Objective objective) {
1475 log.debug("Previous {} next for {} removed", INITIATION, key);
1476 if (future != null) {
1477 future.complete(null);
1478 }
1479 }
1480
1481 @Override
1482 public void onError(Objective objective, ObjectiveError error) {
1483 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1484 if (future != null) {
1485 future.complete(error);
1486 }
1487 }
1488 };
1489 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1490 */
1491
1492 future.complete(null);
1493 l2InitiationNextObjStore.remove(key);
1494 }
1495
1496 /**
1497 * Deletes the pseudo wire termination.
1498 *
1499 * @param l2Tunnel the tunnel
1500 * @param egress the egress connect point
1501 * @param future the async task
1502 * @param direction the direction of the tunnel
1503 */
1504 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1505 ConnectPoint egress,
1506 CompletableFuture<ObjectiveError> future,
1507 Direction direction) {
1508
1509 String key = generateKey(l2Tunnel.tunnelId(), direction);
1510 if (!l2TerminationNextObjStore.containsKey(key)) {
1511 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1512 if (future != null) {
1513 future.complete(null);
1514 }
1515 return;
1516 }
1517 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1518 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1519 l2Tunnel.tunnelId(),
1520 egress.port(),
1521 nextObjective.id());
1522 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1523 log.debug("FwdObj for {} {}, " +
1524 "direction {} removed",
1525 TERMINATION,
1526 l2Tunnel.tunnelId(),
1527 direction),
1528 (objective, error) ->
1529 log.warn("Failed to remove fwdObj " +
1530 "for {} {}" +
1531 ", direction {}",
1532 TERMINATION,
1533 l2Tunnel.tunnelId(),
1534 error,
1535 direction));
1536 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1537
1538 // un-comment in case you want to delete groups used by the pw
1539 // however, this will break the update of pseudowires cause the L2 interface group can
1540 // not be deleted (it is referenced by other groups)
1541 /*
1542 context = new ObjectiveContext() {
1543 @Override
1544 public void onSuccess(Objective objective) {
1545 log.debug("Previous {} next for {} removed", TERMINATION, key);
1546 if (future != null) {
1547 future.complete(null);
1548 }
1549 }
1550
1551 @Override
1552 public void onError(Objective objective, ObjectiveError error) {
1553 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1554 if (future != null) {
1555 future.complete(error);
1556 }
1557 }
1558 };
1559 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1560 */
1561
1562 // delete the extra filtering objective for terminating
1563 // spine-spine pws
1564 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1565
1566 // determine the input port at the
1567 PortNumber inPort;
1568
1569 if (egress.deviceId().
1570 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1571 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1572 } else {
1573 inPort = l2Tunnel.pathUsed().get(0).src().port();
1574 }
1575
1576 MacAddress dstMac;
1577 try {
1578 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1579 } catch (Exception e) {
1580 log.info("Device not found in configuration, no programming of MAC address");
1581 dstMac = null;
1582 }
1583
1584 log.info("Removing filtering objective for pseudowire transport" +
1585 " with vlan = {}, port = {}, mac = {}",
1586 l2Tunnel.transportVlan(),
1587 inPort,
1588 dstMac);
1589 FilteringObjective.Builder filteringObjectiveBuilder =
1590 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1591 context = new DefaultObjectiveContext(( objective ) ->
1592 log.debug("Special filtObj for " + "for {} removed",
1593 l2Tunnel.tunnelId()), ( objective, error ) ->
1594 log.warn("Failed to populate " + "special filtObj " +
1595 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1596 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1597 filteringObjectiveBuilder.withMeta(treatment.build());
1598 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1599 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1600 l2Tunnel.tunnelId(),
1601 inPort);
1602 }
1603
1604 l2TerminationNextObjStore.remove(key);
1605 future.complete(null);
1606 }
1607
1608 /**
1609 * Utilities to generate pw key.
1610 *
1611 * @param tunnelId the tunnel id
1612 * @param direction the direction of the pw
1613 * @return the key of the store
1614 */
1615 private String generateKey(long tunnelId, Direction direction) {
1616 return String.format("%s-%s", tunnelId, direction);
1617 }
1618
1619}