blob: f7c64f648a92a5e2f8d58a39c0f7af75e56de005 [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
409 VlanId vlanToUse = VlanId.vlanId((short) i);
410 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 */
431 List<DeviceId> getDevicesOnPath(List<Link> path) {
432
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 Pantelopoulos811bbae2018-03-15 16:56:09 -0700537 private 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");
546 return Result.ADDITION_ERROR;
547 }
548
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700549 // leafSpinePw determines if this is a leaf-leaf
550 // or leaf-spine pseudowire
551 boolean leafSpinePw;
552 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
553 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
554 try {
555 // differentiate between leaf-leaf pseudowires and leaf-spine
556 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
557 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
558 log.error("Can not deploy pseudowire from spine to spine!");
559 return Result.INTERNAL_ERROR;
560 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
561 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
562 leafSpinePw = false;
563 } else {
564 leafSpinePw = true;
565 }
566 } catch (DeviceConfigNotFoundException e) {
567 log.error("A device for pseudowire connection points does not exist.");
568 return Result.INTERNAL_ERROR;
569 }
570
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800571 // get path here, need to use the same for fwd and rev direction
572 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
573 pw.l2TunnelPolicy().cP2());
574 if (path == null) {
575 log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
576 return WRONG_PARAMETERS;
577 }
578
579 Link fwdNextHop;
580 Link revNextHop;
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700581 if (!isValidPath(path, leafSpinePw)) {
582 log.error("Deploying process : Path for pseudowire {} is not valid",
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700583 l2TunnelId);
584 return INTERNAL_ERROR;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800585 }
586
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700587 // oneHope flag is used to determine if we need to
588 // install transit mpls rules
589 boolean oneHop = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700590 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700591 oneHop = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700592 }
593
594 fwdNextHop = path.get(0);
595 revNextHop = reverseLink(path.get(path.size() - 1));
596
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800597 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700598 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800599
600 // next hops for next objectives
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800601 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
602
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800603 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
604 pw.l2TunnelPolicy().cP1InnerTag(),
605 pw.l2TunnelPolicy().cP2OuterTag(),
606 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800607 // We establish the tunnel.
608 // result.nextId will be used in fwd
609 result = deployPseudoWireInit(pw.l2Tunnel(),
610 pw.l2TunnelPolicy().cP1(),
611 pw.l2TunnelPolicy().cP2(),
612 FWD,
613 fwdNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700614 leafSpinePw,
615 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800616 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800617 if (result != SUCCESS) {
618 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
619 return Result.ADDITION_ERROR;
620 }
621
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800622 // We create the policy.
623 result = deployPolicy(l2TunnelId,
624 pw.l2TunnelPolicy().cP1(),
625 pw.l2TunnelPolicy().cP1InnerTag(),
626 pw.l2TunnelPolicy().cP1OuterTag(),
627 egressVlan,
628 result.nextId);
629 if (result != SUCCESS) {
630 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
631 return Result.ADDITION_ERROR;
632 }
633
634 // We terminate the tunnel
635 result = deployPseudoWireTerm(pw.l2Tunnel(),
636 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800637 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800638 FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700639 leafSpinePw,
640 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800641
642 if (result != SUCCESS) {
643 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
644 return Result.ADDITION_ERROR;
645
646 }
647
648 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
649
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800650 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
651 pw.l2TunnelPolicy().cP2InnerTag(),
652 pw.l2TunnelPolicy().cP1OuterTag(),
653 pw.l2TunnelPolicy().cP1InnerTag());
654
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800655 // We establish the reverse tunnel.
656 result = deployPseudoWireInit(pw.l2Tunnel(),
657 pw.l2TunnelPolicy().cP2(),
658 pw.l2TunnelPolicy().cP1(),
659 REV,
660 revNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700661 leafSpinePw,
662 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800663 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800664 if (result != SUCCESS) {
665 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
666 return Result.ADDITION_ERROR;
667 }
668
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800669
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800670 result = deployPolicy(l2TunnelId,
671 pw.l2TunnelPolicy().cP2(),
672 pw.l2TunnelPolicy().cP2InnerTag(),
673 pw.l2TunnelPolicy().cP2OuterTag(),
674 egressVlan,
675 result.nextId);
676 if (result != SUCCESS) {
677 log.info("Deploying process : Error in deploying policy for CP2");
678 return Result.ADDITION_ERROR;
679 }
680
681 result = deployPseudoWireTerm(pw.l2Tunnel(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700682 pw.l2TunnelPolicy().cP1(),
683 egressVlan,
684 REV,
685 leafSpinePw,
686 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800687
688 if (result != SUCCESS) {
689 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
690 return Result.ADDITION_ERROR;
691 }
692
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700693 result = manageIntermediateFiltering(pw, leafSpinePw);
694 if (result != SUCCESS) {
695 log.info("Deploying process : Error in installing intermediate rules for tagged transport!");
696 return Result.ADDITION_ERROR;
697 }
698
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800699 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
700
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700701 // Populate stores as the final step of the process
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800702 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
703 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700704 // if removeFromPending then remove the information from the pending stores.
705 if (removeFromPending) {
706 // check existence of tunnels/policy in the pending store, if one is missing abort!
707 Versioned<L2Tunnel> l2TunnelVersioned = pendingL2TunnelStore.get(Long.toString(l2TunnelId));
708 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pendingL2PolicyStore.get(Long.toString(l2TunnelId));
709 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
710 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {} in pending store",
711 l2TunnelId);
712 } else {
713 pendingL2TunnelStore.remove(Long.toString(l2TunnelId));
714 pendingL2PolicyStore.remove(Long.toString(l2TunnelId));
715 }
716 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800717
718 return Result.SUCCESS;
719 }
720
721 /**
722 * To deploy a number of pseudo wires.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800723 *
724 * @param pwToAdd the set of pseudo wires to add
725 */
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800726 public void deploy(Set<L2TunnelDescription> pwToAdd) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800727
728 Result result;
729
730 for (L2TunnelDescription currentL2Tunnel : pwToAdd) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700731 // add pseudowires one by one
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800732 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700733 result = deployPseudowire(currentL2Tunnel, false);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800734 switch (result) {
735 case INTERNAL_ERROR:
736 log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
737 break;
738 case WRONG_PARAMETERS:
739 log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
740 break;
741 case ADDITION_ERROR:
742 log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
743 break;
744 default:
745 log.info("Pseudowire with {} succesfully deployed!", tunnelId);
746 break;
747 }
748 }
749 }
750
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800751 /**
752 * Helper function to update a pw.
753 * <p>
754 * Called upon configuration changes that update existing pseudowires and
755 * when links fail. Checking of mastership for CP1 is mandatory because it is
756 * called in multiple instances for both cases.
757 * <p>
758 * Meant to call asynchronously for various events, thus this call can not block and need
759 * to perform asynchronous operations.
760 * <p>
761 * For this reason error checking is omitted.
762 *
763 * @param oldPw the pseudo wire to remove
764 * @param newPw the pseudo wire to add
765 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700766 /*
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800767 private void updatePw(L2TunnelDescription oldPw,
768 L2TunnelDescription newPw) {
769 ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
770 long tunnelId = oldPw.l2Tunnel().tunnelId();
771
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800772 // only determine if the new pseudowire is leaf-spine, because
773 // removal process is the same for both leaf-leaf and leaf-spine pws
774 boolean newPwSpine;
775 try {
776 newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
777 !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
778 } catch (DeviceConfigNotFoundException e) {
779 // if exception is caught treat the new pw as leaf-leaf
780 newPwSpine = false;
781 }
782
783 // copy the variable here because we need to use it in lambda thus it needs to be final
784 boolean finalNewPwSpine = newPwSpine;
785
786 log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
787
788 // The async tasks to orchestrate the next and forwarding update
789 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
790 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
791 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
792 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
793 CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
794 CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
795
796 // first delete all information from our stores, we can not do it asynchronously
797 l2PolicyStore.remove(Long.toString(tunnelId));
798
799 // grab the old l2 tunnel from the store, since it carries information which is not exposed
800 // to the user configuration and set it to oldPw.
801 oldPw.setL2Tunnel(l2TunnelStore.get(Long.toString(tunnelId)).value());
802 VlanId transportVlan = l2TunnelStore.get(Long.toString(tunnelId)).value().transportVlan();
803 l2TunnelStore.remove(Long.toString(tunnelId));
804
805 // remove the reserved transport vlan, if one is used
806 if (!transportVlan.equals(UNTAGGED_TRANSPORT_VLAN)) {
807 vlanStore.remove(transportVlan);
808 }
809
810 // First we remove both policy.
811 log.debug("Start deleting fwd policy for {}", tunnelId);
812 VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
813 oldPw.l2TunnelPolicy().cP1InnerTag(),
814 oldPw.l2TunnelPolicy().cP2OuterTag(),
815 oldPw.l2TunnelPolicy().cP2InnerTag());
816 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP1(),
817 oldPw.l2TunnelPolicy().cP1InnerTag(),
818 oldPw.l2TunnelPolicy().cP1OuterTag(),
819 egressVlan,
820 fwdInitNextFuture,
821 FWD);
822
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800823 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP2(),
824 oldPw.l2TunnelPolicy().cP2InnerTag(),
825 oldPw.l2TunnelPolicy().cP2OuterTag(),
826 egressVlan, revInitNextFuture,
827 REV);
828
829 // Finally we remove both the tunnels.
830 fwdInitNextFuture.thenAcceptAsync(status -> {
831 if (status == null) {
832 log.debug("Update process : Fwd policy removed. " +
833 "Now remove fwd {} for {}", INITIATION, tunnelId);
834 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP1(), fwdTermNextFuture, FWD);
835 }
836 });
837 revInitNextFuture.thenAcceptAsync(status -> {
838 if (status == null) {
839 log.debug("Update process : Rev policy removed. " +
840 "Now remove rev {} for {}", INITIATION, tunnelId);
841 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP2(), revTermNextFuture, REV);
842 }
843 });
844 fwdTermNextFuture.thenAcceptAsync(status -> {
845 if (status == null) {
846 log.debug("Update process : Fwd {} removed. " +
847 "Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
848 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP2(), fwdPwFuture, FWD);
849 }
850 });
851 revTermNextFuture.thenAcceptAsync(status -> {
852 if (status == null) {
853 log.debug("Update process : Rev {} removed. " +
854 "Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
855 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP1(), revPwFuture, REV);
856 }
857 });
858
859 // get path here, need to use the same for fwd and rev direction
860 List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
861 newPw.l2TunnelPolicy().cP2());
862 if (path == null) {
863 log.error("Update process : " +
864 "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
865 return;
866 }
867
868 Link fwdNextHop, revNextHop;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700869 if (!isValidPathSize(path.size())) {
870 log.error("Deploying process : Path size for pseudowire should be of one of the following sizes" +
871 " = [1, 2, 3, 4], for pseudowire {}",
872 newPw.l2Tunnel().tunnelId());
873 return;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800874 }
875
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700876 // spinePw signifies if we have a leaf-spine pw
877 // thus only one label should be pushed (that of pw)
878 // if size>1 we need to push intermediate labels also.
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700879 boolean oneHope = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700880 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700881 oneHope = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700882 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700883 final boolean oneHopeFinal = oneHope;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700884
885 fwdNextHop = path.get(0);
886 revNextHop = reverseLink(path.get(path.size() - 1));
887
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800888 // set new path and transport vlan.
889 newPw.l2Tunnel().setPath(path);
890 newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
891
892 // At the end we install the updated PW.
893 fwdPwFuture.thenAcceptAsync(status -> {
894 if (status == null) {
895
896 // Upgrade stores and book keeping information, need to move this here
897 // cause this call is asynchronous.
898 l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
899 l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
900
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800901 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
902 newPw.l2TunnelPolicy().cP1InnerTag(),
903 newPw.l2TunnelPolicy().cP2OuterTag(),
904 newPw.l2TunnelPolicy().cP2InnerTag());
905
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800906 log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
907 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP1(),
908 newPw.l2TunnelPolicy().cP2(), FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700909 fwdNextHop, finalNewPwSpine, oneHopeFinal, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800910 if (lamdaResult != SUCCESS) {
911 return;
912 }
913
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800914 lamdaResult = deployPolicy(tunnelId, newPw.l2TunnelPolicy().cP1(),
915 newPw.l2TunnelPolicy().cP1InnerTag(),
916 newPw.l2TunnelPolicy().cP1OuterTag(),
917 egressVlanId, lamdaResult.nextId);
918 if (lamdaResult != SUCCESS) {
919 return;
920 }
921 deployPseudoWireTerm(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700922 egressVlanId, FWD, finalNewPwSpine, oneHopeFinal);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800923
924 }
925 });
926 revPwFuture.thenAcceptAsync(status -> {
927 if (status == null) {
928
929 log.debug("Update process : Deploying new rev pw for {}", tunnelId);
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800930
931 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
932 newPw.l2TunnelPolicy().cP2InnerTag(),
933 newPw.l2TunnelPolicy().cP1OuterTag(),
934 newPw.l2TunnelPolicy().cP1InnerTag());
935
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800936 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
937 newPw.l2TunnelPolicy().cP2(),
938 newPw.l2TunnelPolicy().cP1(),
939 REV,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700940 revNextHop, finalNewPwSpine, oneHopeFinal, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800941 if (lamdaResult != SUCCESS) {
942 return;
943 }
944
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800945 lamdaResult = deployPolicy(tunnelId,
946 newPw.l2TunnelPolicy().cP2(),
947 newPw.l2TunnelPolicy().cP2InnerTag(),
948 newPw.l2TunnelPolicy().cP2OuterTag(),
949 egressVlanId,
950 lamdaResult.nextId);
951 if (lamdaResult != SUCCESS) {
952 return;
953 }
954 deployPseudoWireTerm(newPw.l2Tunnel(),
955 newPw.l2TunnelPolicy().cP1(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800956 egressVlanId,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700957 REV, finalNewPwSpine, oneHopeFinal);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800958 }
959 });
960 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700961 */
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800962
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800963 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700964 * Tears down connection points of pseudowires. We can either tear down both connection points,
965 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800966 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700967 * @param l2TunnelId The tunnel id for this pseudowire.
968 * @param tearDownFirst Boolean, true if we want to tear down cp1
969 * @param tearDownSecond Boolean, true if we want to tear down cp2
970 * @return
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800971 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700972 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst, boolean tearDownSecond) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800973
974 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
975 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
976
977 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
978 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
979
980 if (l2TunnelId == 0) {
981 log.warn("Removal process : Tunnel id cannot be 0");
982 return Result.WRONG_PARAMETERS;
983 }
984
985 // check existence of tunnels/policy in the store, if one is missing abort!
986 Versioned<L2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
987 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
988 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
989 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
990 return Result.REMOVAL_ERROR;
991 }
992
993 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700994 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800995
996 // remove the tunnels and the policies from the store
997 l2PolicyStore.remove(Long.toString(l2TunnelId));
998 l2TunnelStore.remove(Long.toString(l2TunnelId));
999
1000 // remove the reserved transport vlan
1001 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1002 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
1003 }
1004
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001005 if (tearDownFirst) {
1006 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001007
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001008 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
1009 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
1010 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
1011 pwToRemove.l2TunnelPolicy().cP2InnerTag());
1012 deletePolicy(l2TunnelId,
1013 pwToRemove.l2TunnelPolicy().cP1(),
1014 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
1015 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
1016 egressVlan,
1017 fwdInitNextFuture,
1018 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001019
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001020 fwdInitNextFuture.thenAcceptAsync(status -> {
1021 if (status == null) {
1022 // Finally we will tear down the pseudo wire.
1023 tearDownPseudoWireInit(l2TunnelId,
1024 pwToRemove.l2TunnelPolicy().cP1(),
1025 fwdTermNextFuture,
1026 FWD);
1027 }
1028 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001029
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001030 fwdTermNextFuture.thenAcceptAsync(status -> {
1031 if (status == null) {
1032 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
1033 pwToRemove.l2TunnelPolicy().cP2(),
1034 null,
1035 FWD);
1036 }
1037 });
1038 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001039
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001040 if (tearDownSecond) {
1041 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001042
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001043 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
1044 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
1045 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
1046 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001047
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001048 // We do the same operations on the reverse side.
1049 deletePolicy(l2TunnelId,
1050 pwToRemove.l2TunnelPolicy().cP2(),
1051 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
1052 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
1053 egressVlan,
1054 revInitNextFuture,
1055 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001056
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001057 revInitNextFuture.thenAcceptAsync(status -> {
1058 if (status == null) {
1059 tearDownPseudoWireInit(l2TunnelId,
1060 pwToRemove.l2TunnelPolicy().cP2(),
1061 revTermNextFuture,
1062 REV);
1063 }
1064 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001065
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001066 revTermNextFuture.thenAcceptAsync(status -> {
1067 if (status == null) {
1068 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
1069 pwToRemove.l2TunnelPolicy().cP1(),
1070 null,
1071 REV);
1072 }
1073 });
1074 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001075
1076 return Result.SUCCESS;
1077 }
1078
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001079 /**
1080 * Helper function for removing a single pseudowire.
1081 * <p>
1082 * No mastership of CP1 is checked, because it can be called from
1083 * the CLI for removal of pseudowires.
1084 *
1085 * @param l2TunnelId the id of the pseudowire to tear down
1086 * @return Returns SUCCESS if no error is obeserved or an appropriate
1087 * error on a failure
1088 */
1089 private Result tearDownPseudowire(long l2TunnelId) {
1090 return tearDownConnectionPoints(l2TunnelId, true, true);
1091 }
1092
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001093 @Override
1094 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
1095
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001096 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001097
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001098 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001099 log.info("Removing pseudowire {}", tunnelId);
1100
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001101 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001102 switch (result) {
1103 case WRONG_PARAMETERS:
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -07001104 log.error("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001105 tunnelId);
1106 break;
1107 case REMOVAL_ERROR:
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -07001108 log.error("Error in pseudowire removal with tunnel id {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001109 break;
1110 default:
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -07001111 log.info("Pseudowire with tunnel id {} was removed successfully", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001112 }
1113 }
1114 }
1115
1116 /**
1117 * Handles the policy establishment which consists in
1118 * create the filtering and forwarding objectives related
1119 * to the initiation and termination.
1120 *
1121 * @param tunnelId the tunnel id
1122 * @param ingress the ingress point
1123 * @param ingressInner the ingress inner tag
1124 * @param ingressOuter the ingress outer tag
1125 * @param nextId the next objective id
1126 * @param egressVlan Vlan-id to set, depends on ingress vlan
1127 * combinations. For example, if pw is double tagged
1128 * then this is the value of the outer vlan, if single
1129 * tagged then it is the new value of the single tag.
1130 * Should be None for untagged traffic.
1131 * @return the result of the operation
1132 */
1133 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
1134 VlanId ingressOuter, VlanId egressVlan, int nextId) {
1135
1136 List<Objective> objectives = Lists.newArrayList();
1137 // We create the forwarding objective for supporting
1138 // the l2 tunnel.
1139 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1140 // We create and add objective context.
1141 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1142 log.debug("FwdObj for tunnel {} populated", tunnelId),
1143 (objective, error) ->
1144 log.warn("Failed to populate fwdrObj " +
1145 "for tunnel {}", tunnelId, error));
1146 objectives.add(fwdBuilder.add(context));
1147
1148 // We create the filtering objective to define the
1149 // permit traffic in the switch
1150 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1151
1152 // We add the metadata.
1153 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1154 .builder()
1155 .setTunnelId(tunnelId)
1156 .setVlanId(egressVlan);
1157 filtBuilder.withMeta(treatment.build());
1158
1159 // We create and add objective context.
1160 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
1161 (objective, error) -> log.warn("Failed to populate filterObj for " +
1162 "tunnel {}", tunnelId, error));
1163 objectives.add(filtBuilder.add(context));
1164
1165 for (Objective objective : objectives) {
1166 if (objective instanceof ForwardingObjective) {
1167 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1168 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
1169 } else {
1170 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1171 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
1172 }
1173 }
1174 return SUCCESS;
1175 }
1176
1177 /**
1178 * Handles the tunnel establishment which consists in
1179 * create the next objectives related to the initiation.
1180 *
1181 * @param l2Tunnel the tunnel to deploy
1182 * @param ingress the ingress connect point
1183 * @param egress the egress connect point
1184 * @param direction the direction of the pw
1185 * @param spinePw if the pseudowire involves a spine switch
1186 * @return the result of the operation
1187 */
1188 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001189 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001190 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001191
1192 if (nextHop == null) {
1193 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
1194 return WRONG_PARAMETERS;
1195 }
1196
1197 // We create the next objective without the metadata
1198 // context and id. We check if it already exists in the
1199 // store. If not we store as it is in the store.
1200 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
1201 nextHop.src(),
1202 nextHop.dst(),
1203 l2Tunnel,
1204 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001205 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001206 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001207 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001208
1209 if (nextObjectiveBuilder == null) {
1210 return INTERNAL_ERROR;
1211 }
1212 // We set the metadata. We will use this metadata
1213 // to inform the driver we are doing a l2 tunnel.
1214 TrafficSelector metadata = DefaultTrafficSelector
1215 .builder()
1216 .matchTunnelId(l2Tunnel.tunnelId())
1217 .build();
1218 nextObjectiveBuilder.withMeta(metadata);
1219 int nextId = srManager.flowObjectiveService.allocateNextId();
1220 if (nextId < 0) {
1221 log.warn("Not able to allocate a next id for initiation");
1222 return INTERNAL_ERROR;
1223 }
1224 nextObjectiveBuilder.withId(nextId);
1225 String key = generateKey(l2Tunnel.tunnelId(), direction);
1226 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
1227 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1228 log.debug("Initiation l2 tunnel rule " +
1229 "for {} populated",
1230 l2Tunnel.tunnelId()),
1231 (objective, error) ->
1232 log.warn("Failed to populate Initiation " +
1233 "l2 tunnel rule for {}: {}",
1234 l2Tunnel.tunnelId(), error));
1235 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1236 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1237 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
1238 l2Tunnel.tunnelId(), nextObjective.id());
1239 Result result = SUCCESS;
1240 result.nextId = nextObjective.id();
1241 return result;
1242 }
1243
1244 /**
1245 * Handles the tunnel termination, which consists in the creation
1246 * of a forwarding objective and a next objective.
1247 *
1248 * @param l2Tunnel the tunnel to terminate
1249 * @param egress the egress point
1250 * @param egressVlan the expected vlan at egress
1251 * @param direction the direction
1252 * @param spinePw if the pseudowire involves a spine switch
1253 * @return the result of the operation
1254 */
1255 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001256 VlanId egressVlan, Direction direction, boolean spinePw, boolean oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001257
1258 // We create the group relative to the termination.
1259 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1260 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001261 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001262 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001263 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001264 if (nextObjectiveBuilder == null) {
1265 return INTERNAL_ERROR;
1266 }
1267 TrafficSelector metadata = DefaultTrafficSelector
1268 .builder()
1269 .matchVlanId(egressVlan)
1270 .build();
1271 nextObjectiveBuilder.withMeta(metadata);
1272 int nextId = srManager.flowObjectiveService.allocateNextId();
1273 if (nextId < 0) {
1274 log.warn("Not able to allocate a next id for initiation");
1275 return INTERNAL_ERROR;
1276 }
1277 nextObjectiveBuilder.withId(nextId);
1278 String key = generateKey(l2Tunnel.tunnelId(), direction);
1279 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1280 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1281 "for {} populated",
1282 l2Tunnel.tunnelId()),
1283 (objective, error) -> log.warn("Failed to populate " +
1284 "termination l2 tunnel " +
1285 "rule for {}: {}",
1286 l2Tunnel.tunnelId(),
1287 error));
1288 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1289 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1290 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1291 l2Tunnel.tunnelId(), nextObjective.id());
1292
1293 // We create the flow relative to the termination.
1294 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1295 egress.port(), nextObjective.id());
1296 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1297 l2Tunnel.tunnelId()),
1298 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1299 " for tunnel termination {}",
1300 l2Tunnel.tunnelId(), error));
1301 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1302 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1303 nextId, l2Tunnel.tunnelId());
1304
1305 if (spinePw) {
1306
1307 // determine the input port at the
1308 PortNumber inPort;
1309
1310 if (egress.deviceId().
1311 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1312 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1313 } else {
1314 inPort = l2Tunnel.pathUsed().get(0).src().port();
1315 }
1316
1317 MacAddress dstMac;
1318 try {
1319 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1320 } catch (Exception e) {
1321 log.info("Device not found in configuration, no programming of MAC address");
1322 dstMac = null;
1323 }
1324
1325 log.info("Populating filtering objective for pseudowire transport" +
1326 " with vlan = {}, port = {}, mac = {}",
1327 l2Tunnel.transportVlan(),
1328 inPort,
1329 dstMac);
1330 FilteringObjective.Builder filteringObjectiveBuilder =
1331 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1332 context = new DefaultObjectiveContext(( objective ) ->
1333 log.debug("Special filtObj for " + "for {} populated",
1334 l2Tunnel.tunnelId()),
1335 ( objective, error ) ->
1336 log.warn("Failed to populate " +
1337 "special filtObj " +
1338 "rule for {}: {}",
1339 l2Tunnel.tunnelId(), error));
1340 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1341 filteringObjectiveBuilder.withMeta(treatment.build());
1342 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1343 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1344 l2Tunnel.tunnelId(),
1345 inPort);
1346 }
1347
1348 return SUCCESS;
1349 }
1350
1351
1352 /**
1353 * Creates the filtering objective according to a given port and vlanid.
1354 *
1355 * @param inPort the in port
1356 * @param vlanId the inner vlan tag
1357 * @return the filtering objective
1358 */
1359 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1360 VlanId vlanId,
1361 MacAddress dstMac) {
1362
1363 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1364 vlanId,
1365 inPort,
1366 dstMac);
1367 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1368 .builder()
1369 .withKey(Criteria.matchInPort(inPort))
1370 .addCondition(Criteria.matchVlanId(vlanId))
1371 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1372 .permit()
1373 .fromApp(srManager.appId());
1374
1375 if (dstMac != null) {
1376 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1377 }
1378
1379 return fwdBuilder;
1380 }
1381
1382 /**
1383 * Creates the filtering objective according to a given policy.
1384 *
1385 * @param inPort the in port
1386 * @param innerTag the inner vlan tag
1387 * @param outerTag the outer vlan tag
1388 * @return the filtering objective
1389 */
1390 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1391
1392 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1393 return DefaultFilteringObjective
1394 .builder()
1395 .withKey(Criteria.matchInPort(inPort))
1396 .addCondition(Criteria.matchInnerVlanId(innerTag))
1397 .addCondition(Criteria.matchVlanId(outerTag))
1398 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1399 .permit()
1400 .fromApp(srManager.appId());
1401 }
1402
1403 /**
1404 * Creates the forwarding objective for the termination.
1405 *
1406 * @param pwLabel the pseudo wire label
1407 * @param tunnelId the tunnel id
1408 * @param egressPort the egress port
1409 * @param nextId the next step
1410 * @return the forwarding objective to support the termination
1411 */
1412 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1413 PortNumber egressPort, int nextId) {
1414
1415 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1416 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1417 // The flow has to match on the pw label and bos
1418 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1419 trafficSelector.matchMplsLabel(pwLabel);
1420 trafficSelector.matchMplsBos(true);
1421 // The flow has to decrement ttl, restore ttl in
1422 // pop mpls, set tunnel id and port.
1423 trafficTreatment.decMplsTtl();
1424 trafficTreatment.copyTtlIn();
1425 trafficTreatment.popMpls();
1426 trafficTreatment.setTunnelId(tunnelId);
1427 trafficTreatment.setOutput(egressPort);
1428
1429 return DefaultForwardingObjective
1430 .builder()
1431 .fromApp(srManager.appId())
1432 .makePermanent()
1433 .nextStep(nextId)
1434 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1435 .withSelector(trafficSelector.build())
1436 .withTreatment(trafficTreatment.build())
1437 .withFlag(VERSATILE);
1438 }
1439
1440 /**
1441 * Creates the forwarding objective for the initiation.
1442 *
1443 * @param tunnelId the tunnel id
1444 * @param inPort the input port
1445 * @param nextId the next step
1446 * @return the forwarding objective to support the initiation.
1447 */
1448 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1449
1450 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1451
1452 // The flow has to match on the mpls logical
1453 // port and the tunnel id.
1454 trafficSelector.matchTunnelId(tunnelId);
1455 trafficSelector.matchInPort(inPort);
1456
1457 return DefaultForwardingObjective
1458 .builder()
1459 .fromApp(srManager.appId())
1460 .makePermanent()
1461 .nextStep(nextId)
1462 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1463 .withSelector(trafficSelector.build())
1464 .withFlag(VERSATILE);
1465
1466 }
1467
1468 /**
1469 * Creates the next objective according to a given
1470 * pipeline. We don't set the next id and we don't
1471 * create the final meta to check if we are re-using
1472 * the same next objective for different tunnels.
1473 *
1474 * @param pipeline the pipeline to support
1475 * @param srcCp the source port
1476 * @param dstCp the destination port
1477 * @param l2Tunnel the tunnel to support
1478 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001479 * @param oneHop if the pw only has one hop, push only pw label
1480 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1481 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001482 * @return the next objective to support the pipeline
1483 */
1484 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1485 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001486 DeviceId egressId, boolean leafSpinePw,
1487 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001488 NextObjective.Builder nextObjBuilder;
1489 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1490 if (pipeline == INITIATION) {
1491 nextObjBuilder = DefaultNextObjective
1492 .builder()
1493 .withType(NextObjective.Type.SIMPLE)
1494 .fromApp(srManager.appId());
1495 // The pw label is the bottom of stack. It has to
1496 // be different -1.
1497 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1498 log.warn("Pw label not configured");
1499 return null;
1500 }
1501 treatmentBuilder.pushMpls();
1502 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1503 treatmentBuilder.setMplsBos(true);
1504 treatmentBuilder.copyTtlOut();
1505
1506 // If the inter-co label is present we have to set the label.
1507 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1508 treatmentBuilder.pushMpls();
1509 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1510 treatmentBuilder.setMplsBos(false);
1511 treatmentBuilder.copyTtlOut();
1512 }
1513
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001514 // if not oneHop install transit mpls labels also
1515 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001516 // We retrieve the sr label from the config
1517 // specific for pseudowire traffic
1518 // using the egress leaf device id.
1519 MplsLabel srLabel;
1520 try {
1521 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1522
1523 } catch (DeviceConfigNotFoundException e) {
1524 log.warn("Sr label for pw traffic not configured");
1525 return null;
1526 }
1527
1528 treatmentBuilder.pushMpls();
1529 treatmentBuilder.setMpls(srLabel);
1530 treatmentBuilder.setMplsBos(false);
1531 treatmentBuilder.copyTtlOut();
1532 }
1533
1534 // We have to rewrite the src and dst mac address.
1535 MacAddress ingressMac;
1536 try {
1537 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1538 } catch (DeviceConfigNotFoundException e) {
1539 log.warn("Was not able to find the ingress mac");
1540 return null;
1541 }
1542 treatmentBuilder.setEthSrc(ingressMac);
1543 MacAddress neighborMac;
1544 try {
1545 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1546 } catch (DeviceConfigNotFoundException e) {
1547 log.warn("Was not able to find the neighbor mac");
1548 return null;
1549 }
1550 treatmentBuilder.setEthDst(neighborMac);
1551
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001552 // if true we need to pop the vlan because
1553 // we instantiate a leaf to leaf pseudowire
1554 if (!leafSpinePw) {
1555 log.info("We should carry this traffic UNTAGGED!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001556 treatmentBuilder.popVlan();
1557 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001558
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001559 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001560 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001561 } else {
1562 // We create the next objective which
1563 // will be a simple l2 group.
1564 nextObjBuilder = DefaultNextObjective
1565 .builder()
1566 .withType(NextObjective.Type.SIMPLE)
1567 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001568
1569 // for termination point we use the outer vlan of the
1570 // encapsulated packet
1571 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001572 }
1573
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001574 treatmentBuilder.setOutput(srcCp.port());
1575 nextObjBuilder.addTreatment(treatmentBuilder.build());
1576 return nextObjBuilder;
1577 }
1578
1579 /**
1580 * Reverses a link.
1581 *
1582 * @param link link to be reversed
1583 * @return the reversed link
1584 */
1585 private Link reverseLink(Link link) {
1586
1587 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1588
1589 linkBuilder.src(link.dst());
1590 linkBuilder.dst(link.src());
1591 linkBuilder.type(link.type());
1592 linkBuilder.providerId(link.providerId());
1593
1594 return linkBuilder.build();
1595 }
1596
1597 /**
1598 * Returns the path betwwen two connect points.
1599 *
1600 * @param srcCp source connect point
1601 * @param dstCp destination connect point
1602 * @return the path
1603 */
1604 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
1605 /* TODO We retrieve a set of paths in case of a link failure, what happens
1606 * if the TopologyService gets the link notification AFTER us and has not updated the paths?
1607 *
1608 * TODO This has the potential to act on old topology.
1609 * Maybe we should make SRManager be a listener on topology events instead raw link events.
1610 */
1611 Set<Path> paths = srManager.topologyService.getPaths(
1612 srManager.topologyService.currentTopology(),
1613 srcCp.deviceId(), dstCp.deviceId());
1614
1615 log.debug("Paths obtained from topology service {}", paths);
1616
1617 // We randomly pick a path.
1618 if (paths.isEmpty()) {
1619 return null;
1620 }
1621 int size = paths.size();
1622 int index = RandomUtils.nextInt(0, size);
1623
1624 List<Link> result = Iterables.get(paths, index).links();
1625 log.debug("Randomly picked a path {}", result);
1626
1627 return result;
1628 }
1629
1630 /**
1631 * Deletes a given policy using the parameter supplied.
1632 *
1633 * @param tunnelId the tunnel id
1634 * @param ingress the ingress point
1635 * @param ingressInner the ingress inner vlan id
1636 * @param ingressOuter the ingress outer vlan id
1637 * @param future to perform the async operation
1638 * @param direction the direction: forward or reverse
1639 */
1640 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1641 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1642
1643 String key = generateKey(tunnelId, direction);
1644 if (!l2InitiationNextObjStore.containsKey(key)) {
1645 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1646 if (future != null) {
1647 future.complete(null);
1648 }
1649 return;
1650 }
1651 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1652 int nextId = nextObjective.id();
1653 List<Objective> objectives = Lists.newArrayList();
1654 // We create the forwarding objective.
1655 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1656 ObjectiveContext context = new ObjectiveContext() {
1657 @Override
1658 public void onSuccess(Objective objective) {
1659 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1660 if (future != null) {
1661 future.complete(null);
1662 }
1663 }
1664
1665 @Override
1666 public void onError(Objective objective, ObjectiveError error) {
1667 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1668 if (future != null) {
1669 future.complete(error);
1670 }
1671 }
1672 };
1673 objectives.add(fwdBuilder.remove(context));
1674 // We create the filtering objective to define the
1675 // permit traffic in the switch
1676 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1677 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1678 .builder()
1679 .setTunnelId(tunnelId)
1680 .setVlanId(egressVlan);
1681 filtBuilder.withMeta(treatment.build());
1682 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1683 (objective, error) ->
1684 log.warn("Failed to revoke filterObj for policy {}",
1685 tunnelId, error));
1686 objectives.add(filtBuilder.remove(context));
1687
1688 for (Objective objective : objectives) {
1689 if (objective instanceof ForwardingObjective) {
1690 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1691 } else {
1692 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1693 }
1694 }
1695 }
1696
1697 /**
1698 * Deletes the pseudo wire initiation.
1699 *
1700 * @param l2TunnelId the tunnel id
1701 * @param ingress the ingress connect point
1702 * @param future to perform an async operation
1703 * @param direction the direction: reverse of forward
1704 */
1705 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1706 CompletableFuture<ObjectiveError> future, Direction direction) {
1707
1708 String key = generateKey(l2TunnelId, direction);
1709 if (!l2InitiationNextObjStore.containsKey(key)) {
1710 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1711 if (future != null) {
1712 future.complete(null);
1713 }
1714 return;
1715 }
1716 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1717
1718 // un-comment in case you want to delete groups used by the pw
1719 // however, this will break the update of pseudowires cause the L2 interface group can
1720 // not be deleted (it is referenced by other groups)
1721 /*
1722 ObjectiveContext context = new ObjectiveContext() {
1723 @Override
1724 public void onSuccess(Objective objective) {
1725 log.debug("Previous {} next for {} removed", INITIATION, key);
1726 if (future != null) {
1727 future.complete(null);
1728 }
1729 }
1730
1731 @Override
1732 public void onError(Objective objective, ObjectiveError error) {
1733 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1734 if (future != null) {
1735 future.complete(error);
1736 }
1737 }
1738 };
1739 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1740 */
1741
1742 future.complete(null);
1743 l2InitiationNextObjStore.remove(key);
1744 }
1745
1746 /**
1747 * Deletes the pseudo wire termination.
1748 *
1749 * @param l2Tunnel the tunnel
1750 * @param egress the egress connect point
1751 * @param future the async task
1752 * @param direction the direction of the tunnel
1753 */
1754 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1755 ConnectPoint egress,
1756 CompletableFuture<ObjectiveError> future,
1757 Direction direction) {
1758
1759 String key = generateKey(l2Tunnel.tunnelId(), direction);
1760 if (!l2TerminationNextObjStore.containsKey(key)) {
1761 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1762 if (future != null) {
1763 future.complete(null);
1764 }
1765 return;
1766 }
1767 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1768 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1769 l2Tunnel.tunnelId(),
1770 egress.port(),
1771 nextObjective.id());
1772 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1773 log.debug("FwdObj for {} {}, " +
1774 "direction {} removed",
1775 TERMINATION,
1776 l2Tunnel.tunnelId(),
1777 direction),
1778 (objective, error) ->
1779 log.warn("Failed to remove fwdObj " +
1780 "for {} {}" +
1781 ", direction {}",
1782 TERMINATION,
1783 l2Tunnel.tunnelId(),
1784 error,
1785 direction));
1786 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1787
1788 // un-comment in case you want to delete groups used by the pw
1789 // however, this will break the update of pseudowires cause the L2 interface group can
1790 // not be deleted (it is referenced by other groups)
1791 /*
1792 context = new ObjectiveContext() {
1793 @Override
1794 public void onSuccess(Objective objective) {
1795 log.debug("Previous {} next for {} removed", TERMINATION, key);
1796 if (future != null) {
1797 future.complete(null);
1798 }
1799 }
1800
1801 @Override
1802 public void onError(Objective objective, ObjectiveError error) {
1803 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1804 if (future != null) {
1805 future.complete(error);
1806 }
1807 }
1808 };
1809 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1810 */
1811
1812 // delete the extra filtering objective for terminating
1813 // spine-spine pws
1814 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1815
1816 // determine the input port at the
1817 PortNumber inPort;
1818
1819 if (egress.deviceId().
1820 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1821 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1822 } else {
1823 inPort = l2Tunnel.pathUsed().get(0).src().port();
1824 }
1825
1826 MacAddress dstMac;
1827 try {
1828 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1829 } catch (Exception e) {
1830 log.info("Device not found in configuration, no programming of MAC address");
1831 dstMac = null;
1832 }
1833
1834 log.info("Removing filtering objective for pseudowire transport" +
1835 " with vlan = {}, port = {}, mac = {}",
1836 l2Tunnel.transportVlan(),
1837 inPort,
1838 dstMac);
1839 FilteringObjective.Builder filteringObjectiveBuilder =
1840 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1841 context = new DefaultObjectiveContext(( objective ) ->
1842 log.debug("Special filtObj for " + "for {} removed",
1843 l2Tunnel.tunnelId()), ( objective, error ) ->
1844 log.warn("Failed to populate " + "special filtObj " +
1845 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1846 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1847 filteringObjectiveBuilder.withMeta(treatment.build());
1848 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1849 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1850 l2Tunnel.tunnelId(),
1851 inPort);
1852 }
1853
1854 l2TerminationNextObjStore.remove(key);
1855 future.complete(null);
1856 }
1857
1858 /**
1859 * Utilities to generate pw key.
1860 *
1861 * @param tunnelId the tunnel id
1862 * @param direction the direction of the pw
1863 * @return the key of the store
1864 */
1865 private String generateKey(long tunnelId, Direction direction) {
1866 return String.format("%s-%s", tunnelId, direction);
1867 }
1868
1869}