blob: 5e2817bb9f7a20cb8f341b529adcfcdadb06f161 [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 Pantelopoulosff691b72018-03-12 16:30:20 -0700425 * Returns true if path size is valid according to the current logic.
426 *
427 * @param pathSize The size of the path
428 * @return True if path size is valid, false otherwise.
429 */
430 private boolean isValidPathSize(int pathSize) {
431 return ((pathSize >= 1) && (pathSize <= 4));
432 }
433
434 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800435 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800436 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700437 * @param pw The pseudowire to deploy
438 * @param removeFromPending if to remove the pseudowire from the pending store
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800439 * @return result of pseudowire deployment
440 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700441 private Result deployPseudowire(L2TunnelDescription pw, boolean removeFromPending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800442
443 Result result;
444 long l2TunnelId;
445
446 l2TunnelId = pw.l2Tunnel().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800447 // The tunnel id cannot be 0.
448 if (l2TunnelId == 0) {
449 log.warn("Tunnel id id must be > 0");
450 return Result.ADDITION_ERROR;
451 }
452
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700453 // leafSpinePw determines if this is a leaf-leaf
454 // or leaf-spine pseudowire
455 boolean leafSpinePw;
456 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
457 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
458 try {
459 // differentiate between leaf-leaf pseudowires and leaf-spine
460 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
461 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
462 log.error("Can not deploy pseudowire from spine to spine!");
463 return Result.INTERNAL_ERROR;
464 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
465 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
466 leafSpinePw = false;
467 } else {
468 leafSpinePw = true;
469 }
470 } catch (DeviceConfigNotFoundException e) {
471 log.error("A device for pseudowire connection points does not exist.");
472 return Result.INTERNAL_ERROR;
473 }
474
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800475 // get path here, need to use the same for fwd and rev direction
476 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
477 pw.l2TunnelPolicy().cP2());
478 if (path == null) {
479 log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
480 return WRONG_PARAMETERS;
481 }
482
483 Link fwdNextHop;
484 Link revNextHop;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700485 if (!isValidPathSize(path.size())) {
486 log.error("Deploying process : Path size for pseudowire should be of" +
487 " one of the following sizes = [1, 2, 3, 4], for pseudowire {}",
488 l2TunnelId);
489 return INTERNAL_ERROR;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800490 }
491
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700492 // oneHope flag is used to determine if we need to
493 // install transit mpls rules
494 boolean oneHop = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700495 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700496 oneHop = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700497 }
498
499 fwdNextHop = path.get(0);
500 revNextHop = reverseLink(path.get(path.size() - 1));
501
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800502 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700503 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800504
505 // next hops for next objectives
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800506 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
507
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800508 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
509 pw.l2TunnelPolicy().cP1InnerTag(),
510 pw.l2TunnelPolicy().cP2OuterTag(),
511 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800512 // We establish the tunnel.
513 // result.nextId will be used in fwd
514 result = deployPseudoWireInit(pw.l2Tunnel(),
515 pw.l2TunnelPolicy().cP1(),
516 pw.l2TunnelPolicy().cP2(),
517 FWD,
518 fwdNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700519 leafSpinePw,
520 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800521 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800522 if (result != SUCCESS) {
523 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
524 return Result.ADDITION_ERROR;
525 }
526
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800527 // We create the policy.
528 result = deployPolicy(l2TunnelId,
529 pw.l2TunnelPolicy().cP1(),
530 pw.l2TunnelPolicy().cP1InnerTag(),
531 pw.l2TunnelPolicy().cP1OuterTag(),
532 egressVlan,
533 result.nextId);
534 if (result != SUCCESS) {
535 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
536 return Result.ADDITION_ERROR;
537 }
538
539 // We terminate the tunnel
540 result = deployPseudoWireTerm(pw.l2Tunnel(),
541 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800542 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800543 FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700544 leafSpinePw,
545 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800546
547 if (result != SUCCESS) {
548 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
549 return Result.ADDITION_ERROR;
550
551 }
552
553 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
554
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800555 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
556 pw.l2TunnelPolicy().cP2InnerTag(),
557 pw.l2TunnelPolicy().cP1OuterTag(),
558 pw.l2TunnelPolicy().cP1InnerTag());
559
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800560 // We establish the reverse tunnel.
561 result = deployPseudoWireInit(pw.l2Tunnel(),
562 pw.l2TunnelPolicy().cP2(),
563 pw.l2TunnelPolicy().cP1(),
564 REV,
565 revNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700566 leafSpinePw,
567 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800568 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800569 if (result != SUCCESS) {
570 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
571 return Result.ADDITION_ERROR;
572 }
573
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800574
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800575 result = deployPolicy(l2TunnelId,
576 pw.l2TunnelPolicy().cP2(),
577 pw.l2TunnelPolicy().cP2InnerTag(),
578 pw.l2TunnelPolicy().cP2OuterTag(),
579 egressVlan,
580 result.nextId);
581 if (result != SUCCESS) {
582 log.info("Deploying process : Error in deploying policy for CP2");
583 return Result.ADDITION_ERROR;
584 }
585
586 result = deployPseudoWireTerm(pw.l2Tunnel(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700587 pw.l2TunnelPolicy().cP1(),
588 egressVlan,
589 REV,
590 leafSpinePw,
591 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800592
593 if (result != SUCCESS) {
594 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
595 return Result.ADDITION_ERROR;
596 }
597
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700598 result = manageIntermediateFiltering(pw, leafSpinePw);
599 if (result != SUCCESS) {
600 log.info("Deploying process : Error in installing intermediate rules for tagged transport!");
601 return Result.ADDITION_ERROR;
602 }
603
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800604 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
605
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700606 // Populate stores as the final step of the process
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800607 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
608 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700609 // if removeFromPending then remove the information from the pending stores.
610 if (removeFromPending) {
611 // check existence of tunnels/policy in the pending store, if one is missing abort!
612 Versioned<L2Tunnel> l2TunnelVersioned = pendingL2TunnelStore.get(Long.toString(l2TunnelId));
613 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pendingL2PolicyStore.get(Long.toString(l2TunnelId));
614 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
615 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {} in pending store",
616 l2TunnelId);
617 } else {
618 pendingL2TunnelStore.remove(Long.toString(l2TunnelId));
619 pendingL2PolicyStore.remove(Long.toString(l2TunnelId));
620 }
621 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800622
623 return Result.SUCCESS;
624 }
625
626 /**
627 * To deploy a number of pseudo wires.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800628 *
629 * @param pwToAdd the set of pseudo wires to add
630 */
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800631 public void deploy(Set<L2TunnelDescription> pwToAdd) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800632
633 Result result;
634
635 for (L2TunnelDescription currentL2Tunnel : pwToAdd) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700636 // add pseudowires one by one
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800637 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700638 result = deployPseudowire(currentL2Tunnel, false);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800639 switch (result) {
640 case INTERNAL_ERROR:
641 log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
642 break;
643 case WRONG_PARAMETERS:
644 log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
645 break;
646 case ADDITION_ERROR:
647 log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
648 break;
649 default:
650 log.info("Pseudowire with {} succesfully deployed!", tunnelId);
651 break;
652 }
653 }
654 }
655
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800656 /**
657 * Helper function to update a pw.
658 * <p>
659 * Called upon configuration changes that update existing pseudowires and
660 * when links fail. Checking of mastership for CP1 is mandatory because it is
661 * called in multiple instances for both cases.
662 * <p>
663 * Meant to call asynchronously for various events, thus this call can not block and need
664 * to perform asynchronous operations.
665 * <p>
666 * For this reason error checking is omitted.
667 *
668 * @param oldPw the pseudo wire to remove
669 * @param newPw the pseudo wire to add
670 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700671 /*
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800672 private void updatePw(L2TunnelDescription oldPw,
673 L2TunnelDescription newPw) {
674 ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
675 long tunnelId = oldPw.l2Tunnel().tunnelId();
676
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800677 // only determine if the new pseudowire is leaf-spine, because
678 // removal process is the same for both leaf-leaf and leaf-spine pws
679 boolean newPwSpine;
680 try {
681 newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
682 !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
683 } catch (DeviceConfigNotFoundException e) {
684 // if exception is caught treat the new pw as leaf-leaf
685 newPwSpine = false;
686 }
687
688 // copy the variable here because we need to use it in lambda thus it needs to be final
689 boolean finalNewPwSpine = newPwSpine;
690
691 log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
692
693 // The async tasks to orchestrate the next and forwarding update
694 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
695 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
696 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
697 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
698 CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
699 CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
700
701 // first delete all information from our stores, we can not do it asynchronously
702 l2PolicyStore.remove(Long.toString(tunnelId));
703
704 // grab the old l2 tunnel from the store, since it carries information which is not exposed
705 // to the user configuration and set it to oldPw.
706 oldPw.setL2Tunnel(l2TunnelStore.get(Long.toString(tunnelId)).value());
707 VlanId transportVlan = l2TunnelStore.get(Long.toString(tunnelId)).value().transportVlan();
708 l2TunnelStore.remove(Long.toString(tunnelId));
709
710 // remove the reserved transport vlan, if one is used
711 if (!transportVlan.equals(UNTAGGED_TRANSPORT_VLAN)) {
712 vlanStore.remove(transportVlan);
713 }
714
715 // First we remove both policy.
716 log.debug("Start deleting fwd policy for {}", tunnelId);
717 VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
718 oldPw.l2TunnelPolicy().cP1InnerTag(),
719 oldPw.l2TunnelPolicy().cP2OuterTag(),
720 oldPw.l2TunnelPolicy().cP2InnerTag());
721 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP1(),
722 oldPw.l2TunnelPolicy().cP1InnerTag(),
723 oldPw.l2TunnelPolicy().cP1OuterTag(),
724 egressVlan,
725 fwdInitNextFuture,
726 FWD);
727
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800728 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP2(),
729 oldPw.l2TunnelPolicy().cP2InnerTag(),
730 oldPw.l2TunnelPolicy().cP2OuterTag(),
731 egressVlan, revInitNextFuture,
732 REV);
733
734 // Finally we remove both the tunnels.
735 fwdInitNextFuture.thenAcceptAsync(status -> {
736 if (status == null) {
737 log.debug("Update process : Fwd policy removed. " +
738 "Now remove fwd {} for {}", INITIATION, tunnelId);
739 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP1(), fwdTermNextFuture, FWD);
740 }
741 });
742 revInitNextFuture.thenAcceptAsync(status -> {
743 if (status == null) {
744 log.debug("Update process : Rev policy removed. " +
745 "Now remove rev {} for {}", INITIATION, tunnelId);
746 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP2(), revTermNextFuture, REV);
747 }
748 });
749 fwdTermNextFuture.thenAcceptAsync(status -> {
750 if (status == null) {
751 log.debug("Update process : Fwd {} removed. " +
752 "Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
753 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP2(), fwdPwFuture, FWD);
754 }
755 });
756 revTermNextFuture.thenAcceptAsync(status -> {
757 if (status == null) {
758 log.debug("Update process : Rev {} removed. " +
759 "Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
760 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP1(), revPwFuture, REV);
761 }
762 });
763
764 // get path here, need to use the same for fwd and rev direction
765 List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
766 newPw.l2TunnelPolicy().cP2());
767 if (path == null) {
768 log.error("Update process : " +
769 "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
770 return;
771 }
772
773 Link fwdNextHop, revNextHop;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700774 if (!isValidPathSize(path.size())) {
775 log.error("Deploying process : Path size for pseudowire should be of one of the following sizes" +
776 " = [1, 2, 3, 4], for pseudowire {}",
777 newPw.l2Tunnel().tunnelId());
778 return;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800779 }
780
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700781 // spinePw signifies if we have a leaf-spine pw
782 // thus only one label should be pushed (that of pw)
783 // if size>1 we need to push intermediate labels also.
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700784 boolean oneHope = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700785 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700786 oneHope = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700787 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700788 final boolean oneHopeFinal = oneHope;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700789
790 fwdNextHop = path.get(0);
791 revNextHop = reverseLink(path.get(path.size() - 1));
792
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800793 // set new path and transport vlan.
794 newPw.l2Tunnel().setPath(path);
795 newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
796
797 // At the end we install the updated PW.
798 fwdPwFuture.thenAcceptAsync(status -> {
799 if (status == null) {
800
801 // Upgrade stores and book keeping information, need to move this here
802 // cause this call is asynchronous.
803 l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
804 l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
805
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800806 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
807 newPw.l2TunnelPolicy().cP1InnerTag(),
808 newPw.l2TunnelPolicy().cP2OuterTag(),
809 newPw.l2TunnelPolicy().cP2InnerTag());
810
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800811 log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
812 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP1(),
813 newPw.l2TunnelPolicy().cP2(), FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700814 fwdNextHop, finalNewPwSpine, oneHopeFinal, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800815 if (lamdaResult != SUCCESS) {
816 return;
817 }
818
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800819 lamdaResult = deployPolicy(tunnelId, newPw.l2TunnelPolicy().cP1(),
820 newPw.l2TunnelPolicy().cP1InnerTag(),
821 newPw.l2TunnelPolicy().cP1OuterTag(),
822 egressVlanId, lamdaResult.nextId);
823 if (lamdaResult != SUCCESS) {
824 return;
825 }
826 deployPseudoWireTerm(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700827 egressVlanId, FWD, finalNewPwSpine, oneHopeFinal);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800828
829 }
830 });
831 revPwFuture.thenAcceptAsync(status -> {
832 if (status == null) {
833
834 log.debug("Update process : Deploying new rev pw for {}", tunnelId);
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800835
836 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
837 newPw.l2TunnelPolicy().cP2InnerTag(),
838 newPw.l2TunnelPolicy().cP1OuterTag(),
839 newPw.l2TunnelPolicy().cP1InnerTag());
840
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800841 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
842 newPw.l2TunnelPolicy().cP2(),
843 newPw.l2TunnelPolicy().cP1(),
844 REV,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700845 revNextHop, finalNewPwSpine, oneHopeFinal, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800846 if (lamdaResult != SUCCESS) {
847 return;
848 }
849
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800850 lamdaResult = deployPolicy(tunnelId,
851 newPw.l2TunnelPolicy().cP2(),
852 newPw.l2TunnelPolicy().cP2InnerTag(),
853 newPw.l2TunnelPolicy().cP2OuterTag(),
854 egressVlanId,
855 lamdaResult.nextId);
856 if (lamdaResult != SUCCESS) {
857 return;
858 }
859 deployPseudoWireTerm(newPw.l2Tunnel(),
860 newPw.l2TunnelPolicy().cP1(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800861 egressVlanId,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700862 REV, finalNewPwSpine, oneHopeFinal);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800863 }
864 });
865 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700866 */
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800867
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800868 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700869 * Tears down connection points of pseudowires. We can either tear down both connection points,
870 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800871 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700872 * @param l2TunnelId The tunnel id for this pseudowire.
873 * @param tearDownFirst Boolean, true if we want to tear down cp1
874 * @param tearDownSecond Boolean, true if we want to tear down cp2
875 * @return
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800876 */
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700877 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst, boolean tearDownSecond) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800878
879 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
880 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
881
882 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
883 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
884
885 if (l2TunnelId == 0) {
886 log.warn("Removal process : Tunnel id cannot be 0");
887 return Result.WRONG_PARAMETERS;
888 }
889
890 // check existence of tunnels/policy in the store, if one is missing abort!
891 Versioned<L2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
892 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
893 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
894 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
895 return Result.REMOVAL_ERROR;
896 }
897
898 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700899 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800900
901 // remove the tunnels and the policies from the store
902 l2PolicyStore.remove(Long.toString(l2TunnelId));
903 l2TunnelStore.remove(Long.toString(l2TunnelId));
904
905 // remove the reserved transport vlan
906 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
907 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
908 }
909
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700910 if (tearDownFirst) {
911 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800912
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700913 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
914 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
915 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
916 pwToRemove.l2TunnelPolicy().cP2InnerTag());
917 deletePolicy(l2TunnelId,
918 pwToRemove.l2TunnelPolicy().cP1(),
919 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
920 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
921 egressVlan,
922 fwdInitNextFuture,
923 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800924
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700925 fwdInitNextFuture.thenAcceptAsync(status -> {
926 if (status == null) {
927 // Finally we will tear down the pseudo wire.
928 tearDownPseudoWireInit(l2TunnelId,
929 pwToRemove.l2TunnelPolicy().cP1(),
930 fwdTermNextFuture,
931 FWD);
932 }
933 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800934
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700935 fwdTermNextFuture.thenAcceptAsync(status -> {
936 if (status == null) {
937 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
938 pwToRemove.l2TunnelPolicy().cP2(),
939 null,
940 FWD);
941 }
942 });
943 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800944
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700945 if (tearDownSecond) {
946 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800947
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700948 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
949 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
950 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
951 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800952
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700953 // We do the same operations on the reverse side.
954 deletePolicy(l2TunnelId,
955 pwToRemove.l2TunnelPolicy().cP2(),
956 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
957 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
958 egressVlan,
959 revInitNextFuture,
960 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800961
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700962 revInitNextFuture.thenAcceptAsync(status -> {
963 if (status == null) {
964 tearDownPseudoWireInit(l2TunnelId,
965 pwToRemove.l2TunnelPolicy().cP2(),
966 revTermNextFuture,
967 REV);
968 }
969 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800970
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700971 revTermNextFuture.thenAcceptAsync(status -> {
972 if (status == null) {
973 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
974 pwToRemove.l2TunnelPolicy().cP1(),
975 null,
976 REV);
977 }
978 });
979 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800980
981 return Result.SUCCESS;
982 }
983
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700984 /**
985 * Helper function for removing a single pseudowire.
986 * <p>
987 * No mastership of CP1 is checked, because it can be called from
988 * the CLI for removal of pseudowires.
989 *
990 * @param l2TunnelId the id of the pseudowire to tear down
991 * @return Returns SUCCESS if no error is obeserved or an appropriate
992 * error on a failure
993 */
994 private Result tearDownPseudowire(long l2TunnelId) {
995 return tearDownConnectionPoints(l2TunnelId, true, true);
996 }
997
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800998 @Override
999 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
1000
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001001 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001002
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001003 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001004 log.info("Removing pseudowire {}", tunnelId);
1005
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001006 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001007 switch (result) {
1008 case WRONG_PARAMETERS:
1009 log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
1010 tunnelId);
1011 break;
1012 case REMOVAL_ERROR:
1013 log.warn("Error in pseudowire removal with tunnel id {}!", tunnelId);
1014 break;
1015 default:
1016 log.warn("Pseudowire with tunnel id {} was removed successfully", tunnelId);
1017 }
1018 }
1019 }
1020
1021 /**
1022 * Handles the policy establishment which consists in
1023 * create the filtering and forwarding objectives related
1024 * to the initiation and termination.
1025 *
1026 * @param tunnelId the tunnel id
1027 * @param ingress the ingress point
1028 * @param ingressInner the ingress inner tag
1029 * @param ingressOuter the ingress outer tag
1030 * @param nextId the next objective id
1031 * @param egressVlan Vlan-id to set, depends on ingress vlan
1032 * combinations. For example, if pw is double tagged
1033 * then this is the value of the outer vlan, if single
1034 * tagged then it is the new value of the single tag.
1035 * Should be None for untagged traffic.
1036 * @return the result of the operation
1037 */
1038 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
1039 VlanId ingressOuter, VlanId egressVlan, int nextId) {
1040
1041 List<Objective> objectives = Lists.newArrayList();
1042 // We create the forwarding objective for supporting
1043 // the l2 tunnel.
1044 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1045 // We create and add objective context.
1046 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1047 log.debug("FwdObj for tunnel {} populated", tunnelId),
1048 (objective, error) ->
1049 log.warn("Failed to populate fwdrObj " +
1050 "for tunnel {}", tunnelId, error));
1051 objectives.add(fwdBuilder.add(context));
1052
1053 // We create the filtering objective to define the
1054 // permit traffic in the switch
1055 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1056
1057 // We add the metadata.
1058 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1059 .builder()
1060 .setTunnelId(tunnelId)
1061 .setVlanId(egressVlan);
1062 filtBuilder.withMeta(treatment.build());
1063
1064 // We create and add objective context.
1065 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
1066 (objective, error) -> log.warn("Failed to populate filterObj for " +
1067 "tunnel {}", tunnelId, error));
1068 objectives.add(filtBuilder.add(context));
1069
1070 for (Objective objective : objectives) {
1071 if (objective instanceof ForwardingObjective) {
1072 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1073 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
1074 } else {
1075 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1076 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
1077 }
1078 }
1079 return SUCCESS;
1080 }
1081
1082 /**
1083 * Handles the tunnel establishment which consists in
1084 * create the next objectives related to the initiation.
1085 *
1086 * @param l2Tunnel the tunnel to deploy
1087 * @param ingress the ingress connect point
1088 * @param egress the egress connect point
1089 * @param direction the direction of the pw
1090 * @param spinePw if the pseudowire involves a spine switch
1091 * @return the result of the operation
1092 */
1093 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001094 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001095 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001096
1097 if (nextHop == null) {
1098 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
1099 return WRONG_PARAMETERS;
1100 }
1101
1102 // We create the next objective without the metadata
1103 // context and id. We check if it already exists in the
1104 // store. If not we store as it is in the store.
1105 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
1106 nextHop.src(),
1107 nextHop.dst(),
1108 l2Tunnel,
1109 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001110 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001111 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001112 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001113
1114 if (nextObjectiveBuilder == null) {
1115 return INTERNAL_ERROR;
1116 }
1117 // We set the metadata. We will use this metadata
1118 // to inform the driver we are doing a l2 tunnel.
1119 TrafficSelector metadata = DefaultTrafficSelector
1120 .builder()
1121 .matchTunnelId(l2Tunnel.tunnelId())
1122 .build();
1123 nextObjectiveBuilder.withMeta(metadata);
1124 int nextId = srManager.flowObjectiveService.allocateNextId();
1125 if (nextId < 0) {
1126 log.warn("Not able to allocate a next id for initiation");
1127 return INTERNAL_ERROR;
1128 }
1129 nextObjectiveBuilder.withId(nextId);
1130 String key = generateKey(l2Tunnel.tunnelId(), direction);
1131 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
1132 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1133 log.debug("Initiation l2 tunnel rule " +
1134 "for {} populated",
1135 l2Tunnel.tunnelId()),
1136 (objective, error) ->
1137 log.warn("Failed to populate Initiation " +
1138 "l2 tunnel rule for {}: {}",
1139 l2Tunnel.tunnelId(), error));
1140 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1141 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1142 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
1143 l2Tunnel.tunnelId(), nextObjective.id());
1144 Result result = SUCCESS;
1145 result.nextId = nextObjective.id();
1146 return result;
1147 }
1148
1149 /**
1150 * Handles the tunnel termination, which consists in the creation
1151 * of a forwarding objective and a next objective.
1152 *
1153 * @param l2Tunnel the tunnel to terminate
1154 * @param egress the egress point
1155 * @param egressVlan the expected vlan at egress
1156 * @param direction the direction
1157 * @param spinePw if the pseudowire involves a spine switch
1158 * @return the result of the operation
1159 */
1160 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001161 VlanId egressVlan, Direction direction, boolean spinePw, boolean oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001162
1163 // We create the group relative to the termination.
1164 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1165 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001166 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001167 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001168 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001169 if (nextObjectiveBuilder == null) {
1170 return INTERNAL_ERROR;
1171 }
1172 TrafficSelector metadata = DefaultTrafficSelector
1173 .builder()
1174 .matchVlanId(egressVlan)
1175 .build();
1176 nextObjectiveBuilder.withMeta(metadata);
1177 int nextId = srManager.flowObjectiveService.allocateNextId();
1178 if (nextId < 0) {
1179 log.warn("Not able to allocate a next id for initiation");
1180 return INTERNAL_ERROR;
1181 }
1182 nextObjectiveBuilder.withId(nextId);
1183 String key = generateKey(l2Tunnel.tunnelId(), direction);
1184 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1185 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1186 "for {} populated",
1187 l2Tunnel.tunnelId()),
1188 (objective, error) -> log.warn("Failed to populate " +
1189 "termination l2 tunnel " +
1190 "rule for {}: {}",
1191 l2Tunnel.tunnelId(),
1192 error));
1193 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1194 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1195 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1196 l2Tunnel.tunnelId(), nextObjective.id());
1197
1198 // We create the flow relative to the termination.
1199 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1200 egress.port(), nextObjective.id());
1201 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1202 l2Tunnel.tunnelId()),
1203 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1204 " for tunnel termination {}",
1205 l2Tunnel.tunnelId(), error));
1206 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1207 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1208 nextId, l2Tunnel.tunnelId());
1209
1210 if (spinePw) {
1211
1212 // determine the input port at the
1213 PortNumber inPort;
1214
1215 if (egress.deviceId().
1216 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1217 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1218 } else {
1219 inPort = l2Tunnel.pathUsed().get(0).src().port();
1220 }
1221
1222 MacAddress dstMac;
1223 try {
1224 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1225 } catch (Exception e) {
1226 log.info("Device not found in configuration, no programming of MAC address");
1227 dstMac = null;
1228 }
1229
1230 log.info("Populating filtering objective for pseudowire transport" +
1231 " with vlan = {}, port = {}, mac = {}",
1232 l2Tunnel.transportVlan(),
1233 inPort,
1234 dstMac);
1235 FilteringObjective.Builder filteringObjectiveBuilder =
1236 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1237 context = new DefaultObjectiveContext(( objective ) ->
1238 log.debug("Special filtObj for " + "for {} populated",
1239 l2Tunnel.tunnelId()),
1240 ( objective, error ) ->
1241 log.warn("Failed to populate " +
1242 "special filtObj " +
1243 "rule for {}: {}",
1244 l2Tunnel.tunnelId(), error));
1245 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1246 filteringObjectiveBuilder.withMeta(treatment.build());
1247 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1248 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1249 l2Tunnel.tunnelId(),
1250 inPort);
1251 }
1252
1253 return SUCCESS;
1254 }
1255
1256
1257 /**
1258 * Creates the filtering objective according to a given port and vlanid.
1259 *
1260 * @param inPort the in port
1261 * @param vlanId the inner vlan tag
1262 * @return the filtering objective
1263 */
1264 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1265 VlanId vlanId,
1266 MacAddress dstMac) {
1267
1268 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1269 vlanId,
1270 inPort,
1271 dstMac);
1272 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1273 .builder()
1274 .withKey(Criteria.matchInPort(inPort))
1275 .addCondition(Criteria.matchVlanId(vlanId))
1276 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1277 .permit()
1278 .fromApp(srManager.appId());
1279
1280 if (dstMac != null) {
1281 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1282 }
1283
1284 return fwdBuilder;
1285 }
1286
1287 /**
1288 * Creates the filtering objective according to a given policy.
1289 *
1290 * @param inPort the in port
1291 * @param innerTag the inner vlan tag
1292 * @param outerTag the outer vlan tag
1293 * @return the filtering objective
1294 */
1295 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1296
1297 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1298 return DefaultFilteringObjective
1299 .builder()
1300 .withKey(Criteria.matchInPort(inPort))
1301 .addCondition(Criteria.matchInnerVlanId(innerTag))
1302 .addCondition(Criteria.matchVlanId(outerTag))
1303 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1304 .permit()
1305 .fromApp(srManager.appId());
1306 }
1307
1308 /**
1309 * Creates the forwarding objective for the termination.
1310 *
1311 * @param pwLabel the pseudo wire label
1312 * @param tunnelId the tunnel id
1313 * @param egressPort the egress port
1314 * @param nextId the next step
1315 * @return the forwarding objective to support the termination
1316 */
1317 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1318 PortNumber egressPort, int nextId) {
1319
1320 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1321 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1322 // The flow has to match on the pw label and bos
1323 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1324 trafficSelector.matchMplsLabel(pwLabel);
1325 trafficSelector.matchMplsBos(true);
1326 // The flow has to decrement ttl, restore ttl in
1327 // pop mpls, set tunnel id and port.
1328 trafficTreatment.decMplsTtl();
1329 trafficTreatment.copyTtlIn();
1330 trafficTreatment.popMpls();
1331 trafficTreatment.setTunnelId(tunnelId);
1332 trafficTreatment.setOutput(egressPort);
1333
1334 return DefaultForwardingObjective
1335 .builder()
1336 .fromApp(srManager.appId())
1337 .makePermanent()
1338 .nextStep(nextId)
1339 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1340 .withSelector(trafficSelector.build())
1341 .withTreatment(trafficTreatment.build())
1342 .withFlag(VERSATILE);
1343 }
1344
1345 /**
1346 * Creates the forwarding objective for the initiation.
1347 *
1348 * @param tunnelId the tunnel id
1349 * @param inPort the input port
1350 * @param nextId the next step
1351 * @return the forwarding objective to support the initiation.
1352 */
1353 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1354
1355 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1356
1357 // The flow has to match on the mpls logical
1358 // port and the tunnel id.
1359 trafficSelector.matchTunnelId(tunnelId);
1360 trafficSelector.matchInPort(inPort);
1361
1362 return DefaultForwardingObjective
1363 .builder()
1364 .fromApp(srManager.appId())
1365 .makePermanent()
1366 .nextStep(nextId)
1367 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1368 .withSelector(trafficSelector.build())
1369 .withFlag(VERSATILE);
1370
1371 }
1372
1373 /**
1374 * Creates the next objective according to a given
1375 * pipeline. We don't set the next id and we don't
1376 * create the final meta to check if we are re-using
1377 * the same next objective for different tunnels.
1378 *
1379 * @param pipeline the pipeline to support
1380 * @param srcCp the source port
1381 * @param dstCp the destination port
1382 * @param l2Tunnel the tunnel to support
1383 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001384 * @param oneHop if the pw only has one hop, push only pw label
1385 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1386 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001387 * @return the next objective to support the pipeline
1388 */
1389 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1390 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001391 DeviceId egressId, boolean leafSpinePw,
1392 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001393 NextObjective.Builder nextObjBuilder;
1394 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1395 if (pipeline == INITIATION) {
1396 nextObjBuilder = DefaultNextObjective
1397 .builder()
1398 .withType(NextObjective.Type.SIMPLE)
1399 .fromApp(srManager.appId());
1400 // The pw label is the bottom of stack. It has to
1401 // be different -1.
1402 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1403 log.warn("Pw label not configured");
1404 return null;
1405 }
1406 treatmentBuilder.pushMpls();
1407 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1408 treatmentBuilder.setMplsBos(true);
1409 treatmentBuilder.copyTtlOut();
1410
1411 // If the inter-co label is present we have to set the label.
1412 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1413 treatmentBuilder.pushMpls();
1414 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1415 treatmentBuilder.setMplsBos(false);
1416 treatmentBuilder.copyTtlOut();
1417 }
1418
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001419 // if not oneHop install transit mpls labels also
1420 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001421 // We retrieve the sr label from the config
1422 // specific for pseudowire traffic
1423 // using the egress leaf device id.
1424 MplsLabel srLabel;
1425 try {
1426 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1427
1428 } catch (DeviceConfigNotFoundException e) {
1429 log.warn("Sr label for pw traffic not configured");
1430 return null;
1431 }
1432
1433 treatmentBuilder.pushMpls();
1434 treatmentBuilder.setMpls(srLabel);
1435 treatmentBuilder.setMplsBos(false);
1436 treatmentBuilder.copyTtlOut();
1437 }
1438
1439 // We have to rewrite the src and dst mac address.
1440 MacAddress ingressMac;
1441 try {
1442 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1443 } catch (DeviceConfigNotFoundException e) {
1444 log.warn("Was not able to find the ingress mac");
1445 return null;
1446 }
1447 treatmentBuilder.setEthSrc(ingressMac);
1448 MacAddress neighborMac;
1449 try {
1450 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1451 } catch (DeviceConfigNotFoundException e) {
1452 log.warn("Was not able to find the neighbor mac");
1453 return null;
1454 }
1455 treatmentBuilder.setEthDst(neighborMac);
1456
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001457 // if true we need to pop the vlan because
1458 // we instantiate a leaf to leaf pseudowire
1459 if (!leafSpinePw) {
1460 log.info("We should carry this traffic UNTAGGED!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001461 treatmentBuilder.popVlan();
1462 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001463
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001464 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001465 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001466 } else {
1467 // We create the next objective which
1468 // will be a simple l2 group.
1469 nextObjBuilder = DefaultNextObjective
1470 .builder()
1471 .withType(NextObjective.Type.SIMPLE)
1472 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001473
1474 // for termination point we use the outer vlan of the
1475 // encapsulated packet
1476 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001477 }
1478
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001479 treatmentBuilder.setOutput(srcCp.port());
1480 nextObjBuilder.addTreatment(treatmentBuilder.build());
1481 return nextObjBuilder;
1482 }
1483
1484 /**
1485 * Reverses a link.
1486 *
1487 * @param link link to be reversed
1488 * @return the reversed link
1489 */
1490 private Link reverseLink(Link link) {
1491
1492 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1493
1494 linkBuilder.src(link.dst());
1495 linkBuilder.dst(link.src());
1496 linkBuilder.type(link.type());
1497 linkBuilder.providerId(link.providerId());
1498
1499 return linkBuilder.build();
1500 }
1501
1502 /**
1503 * Returns the path betwwen two connect points.
1504 *
1505 * @param srcCp source connect point
1506 * @param dstCp destination connect point
1507 * @return the path
1508 */
1509 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
1510 /* TODO We retrieve a set of paths in case of a link failure, what happens
1511 * if the TopologyService gets the link notification AFTER us and has not updated the paths?
1512 *
1513 * TODO This has the potential to act on old topology.
1514 * Maybe we should make SRManager be a listener on topology events instead raw link events.
1515 */
1516 Set<Path> paths = srManager.topologyService.getPaths(
1517 srManager.topologyService.currentTopology(),
1518 srcCp.deviceId(), dstCp.deviceId());
1519
1520 log.debug("Paths obtained from topology service {}", paths);
1521
1522 // We randomly pick a path.
1523 if (paths.isEmpty()) {
1524 return null;
1525 }
1526 int size = paths.size();
1527 int index = RandomUtils.nextInt(0, size);
1528
1529 List<Link> result = Iterables.get(paths, index).links();
1530 log.debug("Randomly picked a path {}", result);
1531
1532 return result;
1533 }
1534
1535 /**
1536 * Deletes a given policy using the parameter supplied.
1537 *
1538 * @param tunnelId the tunnel id
1539 * @param ingress the ingress point
1540 * @param ingressInner the ingress inner vlan id
1541 * @param ingressOuter the ingress outer vlan id
1542 * @param future to perform the async operation
1543 * @param direction the direction: forward or reverse
1544 */
1545 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1546 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1547
1548 String key = generateKey(tunnelId, direction);
1549 if (!l2InitiationNextObjStore.containsKey(key)) {
1550 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1551 if (future != null) {
1552 future.complete(null);
1553 }
1554 return;
1555 }
1556 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1557 int nextId = nextObjective.id();
1558 List<Objective> objectives = Lists.newArrayList();
1559 // We create the forwarding objective.
1560 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1561 ObjectiveContext context = new ObjectiveContext() {
1562 @Override
1563 public void onSuccess(Objective objective) {
1564 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1565 if (future != null) {
1566 future.complete(null);
1567 }
1568 }
1569
1570 @Override
1571 public void onError(Objective objective, ObjectiveError error) {
1572 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1573 if (future != null) {
1574 future.complete(error);
1575 }
1576 }
1577 };
1578 objectives.add(fwdBuilder.remove(context));
1579 // We create the filtering objective to define the
1580 // permit traffic in the switch
1581 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1582 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1583 .builder()
1584 .setTunnelId(tunnelId)
1585 .setVlanId(egressVlan);
1586 filtBuilder.withMeta(treatment.build());
1587 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1588 (objective, error) ->
1589 log.warn("Failed to revoke filterObj for policy {}",
1590 tunnelId, error));
1591 objectives.add(filtBuilder.remove(context));
1592
1593 for (Objective objective : objectives) {
1594 if (objective instanceof ForwardingObjective) {
1595 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1596 } else {
1597 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1598 }
1599 }
1600 }
1601
1602 /**
1603 * Deletes the pseudo wire initiation.
1604 *
1605 * @param l2TunnelId the tunnel id
1606 * @param ingress the ingress connect point
1607 * @param future to perform an async operation
1608 * @param direction the direction: reverse of forward
1609 */
1610 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1611 CompletableFuture<ObjectiveError> future, Direction direction) {
1612
1613 String key = generateKey(l2TunnelId, direction);
1614 if (!l2InitiationNextObjStore.containsKey(key)) {
1615 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1616 if (future != null) {
1617 future.complete(null);
1618 }
1619 return;
1620 }
1621 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1622
1623 // un-comment in case you want to delete groups used by the pw
1624 // however, this will break the update of pseudowires cause the L2 interface group can
1625 // not be deleted (it is referenced by other groups)
1626 /*
1627 ObjectiveContext context = new ObjectiveContext() {
1628 @Override
1629 public void onSuccess(Objective objective) {
1630 log.debug("Previous {} next for {} removed", INITIATION, key);
1631 if (future != null) {
1632 future.complete(null);
1633 }
1634 }
1635
1636 @Override
1637 public void onError(Objective objective, ObjectiveError error) {
1638 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1639 if (future != null) {
1640 future.complete(error);
1641 }
1642 }
1643 };
1644 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1645 */
1646
1647 future.complete(null);
1648 l2InitiationNextObjStore.remove(key);
1649 }
1650
1651 /**
1652 * Deletes the pseudo wire termination.
1653 *
1654 * @param l2Tunnel the tunnel
1655 * @param egress the egress connect point
1656 * @param future the async task
1657 * @param direction the direction of the tunnel
1658 */
1659 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1660 ConnectPoint egress,
1661 CompletableFuture<ObjectiveError> future,
1662 Direction direction) {
1663
1664 String key = generateKey(l2Tunnel.tunnelId(), direction);
1665 if (!l2TerminationNextObjStore.containsKey(key)) {
1666 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1667 if (future != null) {
1668 future.complete(null);
1669 }
1670 return;
1671 }
1672 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1673 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1674 l2Tunnel.tunnelId(),
1675 egress.port(),
1676 nextObjective.id());
1677 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1678 log.debug("FwdObj for {} {}, " +
1679 "direction {} removed",
1680 TERMINATION,
1681 l2Tunnel.tunnelId(),
1682 direction),
1683 (objective, error) ->
1684 log.warn("Failed to remove fwdObj " +
1685 "for {} {}" +
1686 ", direction {}",
1687 TERMINATION,
1688 l2Tunnel.tunnelId(),
1689 error,
1690 direction));
1691 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1692
1693 // un-comment in case you want to delete groups used by the pw
1694 // however, this will break the update of pseudowires cause the L2 interface group can
1695 // not be deleted (it is referenced by other groups)
1696 /*
1697 context = new ObjectiveContext() {
1698 @Override
1699 public void onSuccess(Objective objective) {
1700 log.debug("Previous {} next for {} removed", TERMINATION, key);
1701 if (future != null) {
1702 future.complete(null);
1703 }
1704 }
1705
1706 @Override
1707 public void onError(Objective objective, ObjectiveError error) {
1708 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1709 if (future != null) {
1710 future.complete(error);
1711 }
1712 }
1713 };
1714 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1715 */
1716
1717 // delete the extra filtering objective for terminating
1718 // spine-spine pws
1719 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1720
1721 // determine the input port at the
1722 PortNumber inPort;
1723
1724 if (egress.deviceId().
1725 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1726 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1727 } else {
1728 inPort = l2Tunnel.pathUsed().get(0).src().port();
1729 }
1730
1731 MacAddress dstMac;
1732 try {
1733 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1734 } catch (Exception e) {
1735 log.info("Device not found in configuration, no programming of MAC address");
1736 dstMac = null;
1737 }
1738
1739 log.info("Removing filtering objective for pseudowire transport" +
1740 " with vlan = {}, port = {}, mac = {}",
1741 l2Tunnel.transportVlan(),
1742 inPort,
1743 dstMac);
1744 FilteringObjective.Builder filteringObjectiveBuilder =
1745 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1746 context = new DefaultObjectiveContext(( objective ) ->
1747 log.debug("Special filtObj for " + "for {} removed",
1748 l2Tunnel.tunnelId()), ( objective, error ) ->
1749 log.warn("Failed to populate " + "special filtObj " +
1750 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1751 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1752 filteringObjectiveBuilder.withMeta(treatment.build());
1753 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1754 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1755 l2Tunnel.tunnelId(),
1756 inPort);
1757 }
1758
1759 l2TerminationNextObjStore.remove(key);
1760 future.complete(null);
1761 }
1762
1763 /**
1764 * Utilities to generate pw key.
1765 *
1766 * @param tunnelId the tunnel id
1767 * @param direction the direction of the pw
1768 * @return the key of the store
1769 */
1770 private String generateKey(long tunnelId, Direction direction) {
1771 return String.format("%s-%s", tunnelId, direction);
1772 }
1773
1774}