blob: dc8500d5a3b22159790c2fdaa73553d8995a97f2 [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;
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -070071import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080072
73/**
74 * Handles pwaas related events.
75 */
76public class DefaultL2TunnelHandler implements L2TunnelHandler {
77
78 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
79
80 private final SegmentRoutingManager srManager;
81 /**
82 * To store the next objectives related to the initiation.
83 */
84 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
85 /**
86 * To store the next objectives related to the termination.
87 */
88 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
89
90 /**
91 * To store policies.
92 */
93 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
94
95 /**
96 * To store tunnels.
97 */
98 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
99
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700100 /**
101 * To store pending tunnels that need to be installed.
102 */
103 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
104
105 /**
106 * To store pending policies that need to be installed.
107 */
108 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
109
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800110 private final KryoNamespace.Builder l2TunnelKryo;
111
112 /**
113 * Contains transport vlans used for spine-leaf pseudowires.
114 */
115 private final DistributedSet<VlanId> vlanStore;
116
117 /**
118 * Used for determining transport vlans for leaf-spine.
119 */
120 private short transportVlanUpper = 4093, transportVlanLower = 3500;
121
122 private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
123
124 /**
125 * Create a l2 tunnel handler for the deploy and
126 * for the tear down of pseudo wires.
127 *
128 * @param segmentRoutingManager the segment routing manager
129 */
130 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
131 srManager = segmentRoutingManager;
132 l2TunnelKryo = new KryoNamespace.Builder()
133 .register(KryoNamespaces.API)
134 .register(L2Tunnel.class,
135 L2TunnelPolicy.class,
136 DefaultL2Tunnel.class,
137 DefaultL2TunnelPolicy.class,
138 L2Mode.class,
139 MplsLabel.class,
140 VlanId.class,
141 ConnectPoint.class);
142
143 l2InitiationNextObjStore = srManager.
144 storageService.
145 <String, NextObjective>consistentMapBuilder().
146 withName("onos-l2initiation-nextobj-store").
147 withSerializer(Serializer.using(l2TunnelKryo.build())).
148 build();
149
150 l2TerminationNextObjStore = srManager.storageService.
151 <String, NextObjective>consistentMapBuilder()
152 .withName("onos-l2termination-nextobj-store")
153 .withSerializer(Serializer.using(l2TunnelKryo.build()))
154 .build();
155
156 l2PolicyStore = srManager.storageService
157 .<String, L2TunnelPolicy>consistentMapBuilder()
158 .withName("onos-l2-policy-store")
159 .withSerializer(Serializer.using(l2TunnelKryo.build()))
160 .build();
161
162 l2TunnelStore = srManager.storageService
163 .<String, L2Tunnel>consistentMapBuilder()
164 .withName("onos-l2-tunnel-store")
165 .withSerializer(Serializer.using(l2TunnelKryo.build()))
166 .build();
167
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700168 pendingL2PolicyStore = srManager.storageService
169 .<String, L2TunnelPolicy>consistentMapBuilder()
170 .withName("onos-l2-pending-policy-store")
171 .withSerializer(Serializer.using(l2TunnelKryo.build()))
172 .build();
173
174 pendingL2TunnelStore = srManager.storageService
175 .<String, L2Tunnel>consistentMapBuilder()
176 .withName("onos-l2-pending-tunnel-store")
177 .withSerializer(Serializer.using(l2TunnelKryo.build()))
178 .build();
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) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700203 // get pending tunnels/policies OR installed tunnels/policies
204 List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
205 List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700206 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700207 .map(l2Tunnel -> {
208 L2TunnelPolicy policy = null;
209 for (L2TunnelPolicy l2Policy : policies) {
210 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
211 policy = l2Policy;
212 break;
213 }
214 }
215
216 return new DefaultL2TunnelDescription(l2Tunnel, policy);
217 })
218 .collect(Collectors.toSet());
219 }
220
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800221 @Override
222 public List<L2TunnelPolicy> getL2Policies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800223 return new ArrayList<>(l2PolicyStore
224 .values()
225 .stream()
226 .map(Versioned::value)
227 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800228 }
229
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800230 @Override
231 public List<L2Tunnel> getL2Tunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800232 return new ArrayList<>(l2TunnelStore
233 .values()
234 .stream()
235 .map(Versioned::value)
236 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800237 }
238
239 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700240 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700241 return new ArrayList<>(pendingL2PolicyStore
242 .values()
243 .stream()
244 .map(Versioned::value)
245 .collect(Collectors.toList()));
246 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800247
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700248 @Override
249 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700250 return new ArrayList<>(pendingL2TunnelStore
251 .values()
252 .stream()
253 .map(Versioned::value)
254 .collect(Collectors.toList()));
255 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800256
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700257 @Override
258 public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
259
260 // get both added and pending pseudowires
261 List<L2TunnelDescription> newPseudowires = new ArrayList<>();
262 newPseudowires.addAll(getL2Descriptions(false));
263 newPseudowires.addAll(getL2Descriptions(true));
264 // add the new one
265 newPseudowires.add(pwToAdd);
266
267 return configurationValidity(newPseudowires);
268 }
269
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700270 /**
271 * Manages intermediate filtering rules.
272 *
273 * For leaf-spine-spine pseudowires we need to install a special filtering
274 * rule in the intermediate spine for the appropriate transport vlan.
275 *
276 * @param pw The pseudowire, it will have the path and the transport vlan.
277 */
278 private Result manageIntermediateFiltering(L2TunnelDescription pw, boolean leafSpinePw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800279
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700280 // only leaf-spine-spine should need intermediate rules for now
281 if (!leafSpinePw) {
282 return Result.SUCCESS;
283 }
284 if (pw.l2Tunnel().pathUsed().size() != 2) {
285 return Result.SUCCESS;
286 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800287
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700288 List<Link> path = pw.l2Tunnel().pathUsed();
289 DeviceId intermediateSpineId = pw.l2Tunnel().pathUsed().get(0).dst().deviceId();
290 L2Tunnel l2Tunnel = pw.l2Tunnel();
291
292 log.info("Installing intermediate filtering rules for spine {} , for pseudowire {}",
293 intermediateSpineId, pw.l2Tunnel().tunnelId());
294
295 MacAddress dstMac;
296 try {
297 dstMac = srManager.deviceConfiguration().getDeviceMac(intermediateSpineId);
298 } catch (Exception e) {
299 log.info("Device not found in configuration, no programming of MAC address");
300 dstMac = null;
301 }
302
303 PortNumber inPort;
304
305 inPort = path.get(0).dst().port();
306
307 log.debug("Populating filtering objective for pseudowire transport" +
308 " with vlan = {}, port = {}, mac = {} for device {}",
309 l2Tunnel.transportVlan(),
310 inPort,
311 dstMac,
312 intermediateSpineId);
313
314 FilteringObjective.Builder filteringObjectiveBuilder =
315 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
316 DefaultObjectiveContext context = new DefaultObjectiveContext((objective) ->
317 log.debug("Special filtObj for " +
318 "for {} populated",
319 l2Tunnel.tunnelId()),
320 (objective, error) ->
321 log.warn("Failed to populate " +
322 "special filtObj " +
323 "rule for {}: {}",
324 l2Tunnel.tunnelId(), error));
325 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
326 filteringObjectiveBuilder.withMeta(treatment.build());
327 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
328
329 inPort = path.get(1).src().port();
330
331 log.debug("Populating filtering objective for pseudowire transport" +
332 " with vlan = {}, port = {}, mac = {} for device {}",
333 l2Tunnel.transportVlan(),
334 inPort,
335 dstMac,
336 intermediateSpineId);
337
338 filteringObjectiveBuilder =
339 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
340 context = new DefaultObjectiveContext((objective) ->
341 log.debug("Special filtObj for " + "for {} populated",
342 l2Tunnel.tunnelId()),
343 (objective, error) ->
344 log.warn("Failed to populate " +
345 "special filtObj " +
346 "rule for {}: {}",
347 l2Tunnel.tunnelId(), error));
348 treatment = DefaultTrafficTreatment.builder();
349 filteringObjectiveBuilder.withMeta(treatment.build());
350 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
351
352 return Result.SUCCESS;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800353 }
354
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800355 /**
356 * Returns the new vlan id for an ingress point of a
357 * pseudowire. For double tagged, it is the outer,
358 * For single tagged it is the single tag, and for
359 * inner it is None.
360 *
361 * @param ingressOuter vlanid of ingress outer
362 * @param ingressInner vlanid of ingress inner
363 * @param egressOuter vlanid of egress outer
364 * @param egressInner vlanid of egress inner
365 * @return returns the vlan id which will be installed at vlan table 1.
366 */
367 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
368 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800369 // validity of vlan combinations was checked at verifyPseudowire
370 if (!(ingressOuter.equals(VlanId.NONE))) {
371 return egressOuter;
372 } else if (!(ingressInner.equals(VlanId.NONE))) {
373 return egressInner;
374 } else {
375 return VlanId.vlanId("None");
376 }
377 }
378
379 /**
380 * Determines vlan used for transporting the pw traffic.
381 *
382 * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
383 * and also make sure to add the popVlan instruction.
384 * For spine-leaf pws we choose the highest vlan value available from a certain range.
385 *
386 * @param spinePw if the pw is leaf-spine.
387 * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
388 * then the pw is transported untagged.
389 */
390 private VlanId determineTransportVlan(boolean spinePw) {
391
392 if (!spinePw) {
393
394 log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
395 return UNTAGGED_TRANSPORT_VLAN;
396 } else {
397 for (short i = transportVlanUpper; i > transportVlanLower; i--) {
398
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700399 VlanId vlanToUse = VlanId.vlanId(i);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800400 if (!vlanStore.contains(vlanToUse)) {
401
402 vlanStore.add(vlanToUse);
403 log.info("Transport vlan {} for pseudowire!", vlanToUse);
404 return vlanToUse;
405 }
406 }
407
408 log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
409 "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
410 return UNTAGGED_TRANSPORT_VLAN;
411 }
412 }
413
414 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700415 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700416 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700417 * @param path The path to traverse.
418 * @return The devices on the path with the order they
419 * are traversed.
420 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700421 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700422
423 // iterate over links and get all devices in the order
424 // we find them
425 List<DeviceId> deviceList = new ArrayList<DeviceId>();
426 for (Link link : path) {
427 if (!deviceList.contains(link.src().deviceId())) {
428 deviceList.add(link.src().deviceId());
429 }
430 if (!deviceList.contains(link.dst().deviceId())) {
431 deviceList.add(link.dst().deviceId());
432 }
433 }
434
435 return deviceList;
436 }
437
438 /**
439 * Returns true if path is valid according to the current logic.
440 * For example : leaf to spine pseudowires can be either leaf-spine or
441 * leaf-spine-spine. However, in the configuration we might specify spine device
442 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
443 * two cases and need to provision for them.
444 *
445 * If we have a leaf to leaf pseudowire then all the intermediate devices must
446 * be spines. However, in case of paired switches we can have two leafs interconnected
447 * with each other directly.
448 *
449 * @param path the chosen path
450 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700451 * @return True if path size is valid, false otherwise.
452 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700453 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
454
455 List<DeviceId> devices = getDevicesOnPath(path);
456 if (devices.size() < 2) {
457 log.error("Path size for pseudowire should be greater than 1!");
458 return false;
459 }
460
461 try {
462 if (leafSpinePw) {
463 // can either be leaf-spine-spine or leaf-spine
464 // first device is leaf, all other must be spines
465 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
466 // if first device is a leaf then all other must be spines
467 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
468 devices.remove(0);
469 for (DeviceId devId : devices) {
470 log.debug("Device {} should be a spine!", devId);
471 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
472 return false;
473 }
474 }
475 } else {
476 // if first device is spine, last device must be a leaf
477 // all other devices must be spines
478 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
479 return false;
480 }
481 devices.remove(devices.size() - 1);
482 for (DeviceId devId : devices) {
483 log.debug("Device {} should be a spine!", devId);
484 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
485 return false;
486 }
487 }
488 }
489 } else {
490 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
491 // or leaf-spine-spine-leaf
492 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
493 // check first device, needs to be a leaf
494 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
495 return false;
496 }
497 // check last device, needs to be a leaf
498 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
499 return false;
500 }
501 // remove these devices, rest must all be spines
502 devices.remove(0);
503 devices.remove(devices.size() - 1);
504 for (DeviceId devId : devices) {
505 log.debug("Device {} should be a spine!", devId);
506 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
507 return false;
508 }
509 }
510
511 }
512 } catch (DeviceConfigNotFoundException e) {
513 log.error("Device not found in configuration : {}", e);
514 return false;
515 }
516
517 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700518 }
519
520 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800521 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800522 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700523 * @param pw The pseudowire to deploy
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800524 * @return result of pseudowire deployment
525 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700526 public Result deployPseudowire(L2TunnelDescription pw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800527
528 Result result;
529 long l2TunnelId;
530
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700531 log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
532 pw.l2Tunnel().tunnelId());
533
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800534 l2TunnelId = pw.l2Tunnel().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800535 // The tunnel id cannot be 0.
536 if (l2TunnelId == 0) {
537 log.warn("Tunnel id id must be > 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700538 return Result.WRONG_PARAMETERS
539 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800540 }
541
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700542 result = verifyGlobalValidity(pw);
543 if (result != SUCCESS) {
544 log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
545 return result;
546 }
547
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700548 // leafSpinePw determines if this is a leaf-leaf
549 // or leaf-spine pseudowire
550 boolean leafSpinePw;
551 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
552 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
553 try {
554 // differentiate between leaf-leaf pseudowires and leaf-spine
555 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
556 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
557 log.error("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700558 return Result.WRONG_PARAMETERS
559 .appendError("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700560 } 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) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700567 log.error("Device for pseudowire connection points does not exist in the configuration");
568 return Result.INTERNAL_ERROR
569 .appendError("Device for pseudowire connection points does not exist in the configuration");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700570 }
571
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800572 // get path here, need to use the same for fwd and rev direction
573 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
574 pw.l2TunnelPolicy().cP2());
575 if (path == null) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700576 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
577 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800578 }
579
580 Link fwdNextHop;
581 Link revNextHop;
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700582 if (!isValidPath(path, leafSpinePw)) {
583 log.error("Deploying process : Path for pseudowire {} is not valid",
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700584 l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700585 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800586 }
587
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700588 // oneHope flag is used to determine if we need to
589 // install transit mpls rules
590 boolean oneHop = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700591 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700592 oneHop = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700593 }
594
595 fwdNextHop = path.get(0);
596 revNextHop = reverseLink(path.get(path.size() - 1));
597
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800598 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700599 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800600
601 // next hops for next objectives
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800602 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
603
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800604 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
605 pw.l2TunnelPolicy().cP1InnerTag(),
606 pw.l2TunnelPolicy().cP2OuterTag(),
607 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800608 // We establish the tunnel.
609 // result.nextId will be used in fwd
610 result = deployPseudoWireInit(pw.l2Tunnel(),
611 pw.l2TunnelPolicy().cP1(),
612 pw.l2TunnelPolicy().cP2(),
613 FWD,
614 fwdNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700615 leafSpinePw,
616 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800617 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800618 if (result != SUCCESS) {
619 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700620 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800621 }
622
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800623 // We create the policy.
624 result = deployPolicy(l2TunnelId,
625 pw.l2TunnelPolicy().cP1(),
626 pw.l2TunnelPolicy().cP1InnerTag(),
627 pw.l2TunnelPolicy().cP1OuterTag(),
628 egressVlan,
629 result.nextId);
630 if (result != SUCCESS) {
631 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700632 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800633 }
634
635 // We terminate the tunnel
636 result = deployPseudoWireTerm(pw.l2Tunnel(),
637 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800638 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800639 FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700640 leafSpinePw,
641 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800642
643 if (result != SUCCESS) {
644 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700645 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800646 }
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");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700666 return Result.INTERNAL_ERROR
667 .appendError("Error in deploying pseudowire initiation for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800668 }
669
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800670
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800671 result = deployPolicy(l2TunnelId,
672 pw.l2TunnelPolicy().cP2(),
673 pw.l2TunnelPolicy().cP2InnerTag(),
674 pw.l2TunnelPolicy().cP2OuterTag(),
675 egressVlan,
676 result.nextId);
677 if (result != SUCCESS) {
678 log.info("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700679 return Result.INTERNAL_ERROR
680 .appendError("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800681 }
682
683 result = deployPseudoWireTerm(pw.l2Tunnel(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700684 pw.l2TunnelPolicy().cP1(),
685 egressVlan,
686 REV,
687 leafSpinePw,
688 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800689
690 if (result != SUCCESS) {
691 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700692 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800693 }
694
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700695 result = manageIntermediateFiltering(pw, leafSpinePw);
696 if (result != SUCCESS) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700697 log.info("Deploying process : Error in installing intermediate rules for tagged transport");
698 return Result.INTERNAL_ERROR.appendError("Error in installing intermediate rules for tagged transport");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700699 }
700
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800701 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
702
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700703 // Populate stores as the final step of the process
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800704 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
705 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
706
707 return Result.SUCCESS;
708 }
709
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700710 @Override
711 public Result checkIfPwExists(long tunnelId, boolean pending) {
712
713 List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
714 .stream()
715 .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
716 .collect(Collectors.toList());
717
718 if (pseudowires.size() == 0) {
719 String store = ((pending) ? "pending" : "installed");
720 log.error("Pseudowire {} does not exist in {} store", tunnelId, store);
721 return Result.WRONG_PARAMETERS.
722 appendError("Pseudowire " + tunnelId + " does not exist in " + store);
723 } else {
724 return SUCCESS;
725 }
726 }
727
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800728 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700729 * Tears down connection points of pseudowires. We can either tear down both connection points,
730 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800731 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700732 * @param l2TunnelId The tunnel id for this pseudowire.
733 * @param tearDownFirst Boolean, true if we want to tear down cp1
734 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700735 * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
736 * in the network, else remove flows/groups in the devices also.
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700737 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700738 * a descriptive error otherwise.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800739 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700740 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
741 boolean tearDownSecond, boolean pending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800742
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700743 Result res;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800744 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
745 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800746 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
747 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
748
749 if (l2TunnelId == 0) {
750 log.warn("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700751 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800752 }
753
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700754 res = checkIfPwExists(l2TunnelId, pending);
755 if (res != Result.SUCCESS) {
756 return res;
757 }
758
759 // remove and get the tunnel and the policy from the appropriate store
760 // if null, return error.
761 Versioned<L2Tunnel> l2TunnelVersioned = pending ?
762 pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
763 l2TunnelStore.remove(Long.toString(l2TunnelId));
764 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
765 pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
766 l2PolicyStore.remove(Long.toString(l2TunnelId));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800767 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
768 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700769 return Result.INTERNAL_ERROR
770 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800771 }
772
773 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700774 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800775
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800776 // remove the reserved transport vlan
777 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
778 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
779 }
780
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700781 if (pending) {
782 // no need to remove flows / groups for a pseudowire
783 // in pending state
784 return Result.SUCCESS;
785 }
786
787 // remove flows/groups involving with this pseudowire
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700788 if (tearDownFirst) {
789 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800790
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700791 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
792 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
793 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
794 pwToRemove.l2TunnelPolicy().cP2InnerTag());
795 deletePolicy(l2TunnelId,
796 pwToRemove.l2TunnelPolicy().cP1(),
797 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
798 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
799 egressVlan,
800 fwdInitNextFuture,
801 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800802
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700803 fwdInitNextFuture.thenAcceptAsync(status -> {
804 if (status == null) {
805 // Finally we will tear down the pseudo wire.
806 tearDownPseudoWireInit(l2TunnelId,
807 pwToRemove.l2TunnelPolicy().cP1(),
808 fwdTermNextFuture,
809 FWD);
810 }
811 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800812
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700813 fwdTermNextFuture.thenAcceptAsync(status -> {
814 if (status == null) {
815 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
816 pwToRemove.l2TunnelPolicy().cP2(),
817 null,
818 FWD);
819 }
820 });
821 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800822
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700823 if (tearDownSecond) {
824 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800825
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700826 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
827 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
828 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
829 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800830
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700831 // We do the same operations on the reverse side.
832 deletePolicy(l2TunnelId,
833 pwToRemove.l2TunnelPolicy().cP2(),
834 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
835 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
836 egressVlan,
837 revInitNextFuture,
838 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800839
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700840 revInitNextFuture.thenAcceptAsync(status -> {
841 if (status == null) {
842 tearDownPseudoWireInit(l2TunnelId,
843 pwToRemove.l2TunnelPolicy().cP2(),
844 revTermNextFuture,
845 REV);
846 }
847 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800848
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700849 revTermNextFuture.thenAcceptAsync(status -> {
850 if (status == null) {
851 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
852 pwToRemove.l2TunnelPolicy().cP1(),
853 null,
854 REV);
855 }
856 });
857 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800858
859 return Result.SUCCESS;
860 }
861
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700862 /**
863 * Helper function for removing a single pseudowire.
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700864 *
865 * Tries to remove pseudowire from any store it might reside (pending or installed).
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700866 *
867 * @param l2TunnelId the id of the pseudowire to tear down
868 * @return Returns SUCCESS if no error is obeserved or an appropriate
869 * error on a failure
870 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700871 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700872
873 if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
874 return tearDownConnectionPoints(l2TunnelId, true, true, true);
875 } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
876 return tearDownConnectionPoints(l2TunnelId, true, true, false);
877 } else {
878 return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
879 + l2TunnelId
880 + " did not reside in any store!");
881 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700882 }
883
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700884 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800885 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
886
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800887 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800888
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700889 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800890 log.info("Removing pseudowire {}", tunnelId);
891
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700892 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700893 if (result != Result.SUCCESS) {
894 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800895 }
896 }
897 }
898
899 /**
900 * Handles the policy establishment which consists in
901 * create the filtering and forwarding objectives related
902 * to the initiation and termination.
903 *
904 * @param tunnelId the tunnel id
905 * @param ingress the ingress point
906 * @param ingressInner the ingress inner tag
907 * @param ingressOuter the ingress outer tag
908 * @param nextId the next objective id
909 * @param egressVlan Vlan-id to set, depends on ingress vlan
910 * combinations. For example, if pw is double tagged
911 * then this is the value of the outer vlan, if single
912 * tagged then it is the new value of the single tag.
913 * Should be None for untagged traffic.
914 * @return the result of the operation
915 */
916 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
917 VlanId ingressOuter, VlanId egressVlan, int nextId) {
918
919 List<Objective> objectives = Lists.newArrayList();
920 // We create the forwarding objective for supporting
921 // the l2 tunnel.
922 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
923 // We create and add objective context.
924 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
925 log.debug("FwdObj for tunnel {} populated", tunnelId),
926 (objective, error) ->
927 log.warn("Failed to populate fwdrObj " +
928 "for tunnel {}", tunnelId, error));
929 objectives.add(fwdBuilder.add(context));
930
931 // We create the filtering objective to define the
932 // permit traffic in the switch
933 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
934
935 // We add the metadata.
936 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
937 .builder()
938 .setTunnelId(tunnelId)
939 .setVlanId(egressVlan);
940 filtBuilder.withMeta(treatment.build());
941
942 // We create and add objective context.
943 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
944 (objective, error) -> log.warn("Failed to populate filterObj for " +
945 "tunnel {}", tunnelId, error));
946 objectives.add(filtBuilder.add(context));
947
948 for (Objective objective : objectives) {
949 if (objective instanceof ForwardingObjective) {
950 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
951 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
952 } else {
953 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
954 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
955 }
956 }
957 return SUCCESS;
958 }
959
960 /**
961 * Handles the tunnel establishment which consists in
962 * create the next objectives related to the initiation.
963 *
964 * @param l2Tunnel the tunnel to deploy
965 * @param ingress the ingress connect point
966 * @param egress the egress connect point
967 * @param direction the direction of the pw
968 * @param spinePw if the pseudowire involves a spine switch
969 * @return the result of the operation
970 */
971 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800972 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700973 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800974
975 if (nextHop == null) {
976 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
977 return WRONG_PARAMETERS;
978 }
979
980 // We create the next objective without the metadata
981 // context and id. We check if it already exists in the
982 // store. If not we store as it is in the store.
983 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
984 nextHop.src(),
985 nextHop.dst(),
986 l2Tunnel,
987 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800988 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700989 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800990 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800991
992 if (nextObjectiveBuilder == null) {
993 return INTERNAL_ERROR;
994 }
995 // We set the metadata. We will use this metadata
996 // to inform the driver we are doing a l2 tunnel.
997 TrafficSelector metadata = DefaultTrafficSelector
998 .builder()
999 .matchTunnelId(l2Tunnel.tunnelId())
1000 .build();
1001 nextObjectiveBuilder.withMeta(metadata);
1002 int nextId = srManager.flowObjectiveService.allocateNextId();
1003 if (nextId < 0) {
1004 log.warn("Not able to allocate a next id for initiation");
1005 return INTERNAL_ERROR;
1006 }
1007 nextObjectiveBuilder.withId(nextId);
1008 String key = generateKey(l2Tunnel.tunnelId(), direction);
1009 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
1010 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1011 log.debug("Initiation l2 tunnel rule " +
1012 "for {} populated",
1013 l2Tunnel.tunnelId()),
1014 (objective, error) ->
1015 log.warn("Failed to populate Initiation " +
1016 "l2 tunnel rule for {}: {}",
1017 l2Tunnel.tunnelId(), error));
1018 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1019 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1020 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
1021 l2Tunnel.tunnelId(), nextObjective.id());
1022 Result result = SUCCESS;
1023 result.nextId = nextObjective.id();
1024 return result;
1025 }
1026
1027 /**
1028 * Handles the tunnel termination, which consists in the creation
1029 * of a forwarding objective and a next objective.
1030 *
1031 * @param l2Tunnel the tunnel to terminate
1032 * @param egress the egress point
1033 * @param egressVlan the expected vlan at egress
1034 * @param direction the direction
1035 * @param spinePw if the pseudowire involves a spine switch
1036 * @return the result of the operation
1037 */
1038 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001039 VlanId egressVlan, Direction direction, boolean spinePw, boolean oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001040
1041 // We create the group relative to the termination.
1042 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1043 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001044 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001045 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001046 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001047 if (nextObjectiveBuilder == null) {
1048 return INTERNAL_ERROR;
1049 }
1050 TrafficSelector metadata = DefaultTrafficSelector
1051 .builder()
1052 .matchVlanId(egressVlan)
1053 .build();
1054 nextObjectiveBuilder.withMeta(metadata);
1055 int nextId = srManager.flowObjectiveService.allocateNextId();
1056 if (nextId < 0) {
1057 log.warn("Not able to allocate a next id for initiation");
1058 return INTERNAL_ERROR;
1059 }
1060 nextObjectiveBuilder.withId(nextId);
1061 String key = generateKey(l2Tunnel.tunnelId(), direction);
1062 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1063 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1064 "for {} populated",
1065 l2Tunnel.tunnelId()),
1066 (objective, error) -> log.warn("Failed to populate " +
1067 "termination l2 tunnel " +
1068 "rule for {}: {}",
1069 l2Tunnel.tunnelId(),
1070 error));
1071 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1072 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1073 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1074 l2Tunnel.tunnelId(), nextObjective.id());
1075
1076 // We create the flow relative to the termination.
1077 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1078 egress.port(), nextObjective.id());
1079 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1080 l2Tunnel.tunnelId()),
1081 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1082 " for tunnel termination {}",
1083 l2Tunnel.tunnelId(), error));
1084 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1085 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1086 nextId, l2Tunnel.tunnelId());
1087
1088 if (spinePw) {
1089
1090 // determine the input port at the
1091 PortNumber inPort;
1092
1093 if (egress.deviceId().
1094 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1095 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1096 } else {
1097 inPort = l2Tunnel.pathUsed().get(0).src().port();
1098 }
1099
1100 MacAddress dstMac;
1101 try {
1102 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1103 } catch (Exception e) {
1104 log.info("Device not found in configuration, no programming of MAC address");
1105 dstMac = null;
1106 }
1107
1108 log.info("Populating filtering objective for pseudowire transport" +
1109 " with vlan = {}, port = {}, mac = {}",
1110 l2Tunnel.transportVlan(),
1111 inPort,
1112 dstMac);
1113 FilteringObjective.Builder filteringObjectiveBuilder =
1114 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1115 context = new DefaultObjectiveContext(( objective ) ->
1116 log.debug("Special filtObj for " + "for {} populated",
1117 l2Tunnel.tunnelId()),
1118 ( objective, error ) ->
1119 log.warn("Failed to populate " +
1120 "special filtObj " +
1121 "rule for {}: {}",
1122 l2Tunnel.tunnelId(), error));
1123 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1124 filteringObjectiveBuilder.withMeta(treatment.build());
1125 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1126 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1127 l2Tunnel.tunnelId(),
1128 inPort);
1129 }
1130
1131 return SUCCESS;
1132 }
1133
1134
1135 /**
1136 * Creates the filtering objective according to a given port and vlanid.
1137 *
1138 * @param inPort the in port
1139 * @param vlanId the inner vlan tag
1140 * @return the filtering objective
1141 */
1142 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1143 VlanId vlanId,
1144 MacAddress dstMac) {
1145
1146 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1147 vlanId,
1148 inPort,
1149 dstMac);
1150 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1151 .builder()
1152 .withKey(Criteria.matchInPort(inPort))
1153 .addCondition(Criteria.matchVlanId(vlanId))
1154 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1155 .permit()
1156 .fromApp(srManager.appId());
1157
1158 if (dstMac != null) {
1159 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1160 }
1161
1162 return fwdBuilder;
1163 }
1164
1165 /**
1166 * Creates the filtering objective according to a given policy.
1167 *
1168 * @param inPort the in port
1169 * @param innerTag the inner vlan tag
1170 * @param outerTag the outer vlan tag
1171 * @return the filtering objective
1172 */
1173 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1174
1175 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1176 return DefaultFilteringObjective
1177 .builder()
1178 .withKey(Criteria.matchInPort(inPort))
1179 .addCondition(Criteria.matchInnerVlanId(innerTag))
1180 .addCondition(Criteria.matchVlanId(outerTag))
1181 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1182 .permit()
1183 .fromApp(srManager.appId());
1184 }
1185
1186 /**
1187 * Creates the forwarding objective for the termination.
1188 *
1189 * @param pwLabel the pseudo wire label
1190 * @param tunnelId the tunnel id
1191 * @param egressPort the egress port
1192 * @param nextId the next step
1193 * @return the forwarding objective to support the termination
1194 */
1195 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1196 PortNumber egressPort, int nextId) {
1197
1198 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1199 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1200 // The flow has to match on the pw label and bos
1201 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1202 trafficSelector.matchMplsLabel(pwLabel);
1203 trafficSelector.matchMplsBos(true);
1204 // The flow has to decrement ttl, restore ttl in
1205 // pop mpls, set tunnel id and port.
1206 trafficTreatment.decMplsTtl();
1207 trafficTreatment.copyTtlIn();
1208 trafficTreatment.popMpls();
1209 trafficTreatment.setTunnelId(tunnelId);
1210 trafficTreatment.setOutput(egressPort);
1211
1212 return DefaultForwardingObjective
1213 .builder()
1214 .fromApp(srManager.appId())
1215 .makePermanent()
1216 .nextStep(nextId)
1217 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1218 .withSelector(trafficSelector.build())
1219 .withTreatment(trafficTreatment.build())
1220 .withFlag(VERSATILE);
1221 }
1222
1223 /**
1224 * Creates the forwarding objective for the initiation.
1225 *
1226 * @param tunnelId the tunnel id
1227 * @param inPort the input port
1228 * @param nextId the next step
1229 * @return the forwarding objective to support the initiation.
1230 */
1231 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1232
1233 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1234
1235 // The flow has to match on the mpls logical
1236 // port and the tunnel id.
1237 trafficSelector.matchTunnelId(tunnelId);
1238 trafficSelector.matchInPort(inPort);
1239
1240 return DefaultForwardingObjective
1241 .builder()
1242 .fromApp(srManager.appId())
1243 .makePermanent()
1244 .nextStep(nextId)
1245 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1246 .withSelector(trafficSelector.build())
1247 .withFlag(VERSATILE);
1248
1249 }
1250
1251 /**
1252 * Creates the next objective according to a given
1253 * pipeline. We don't set the next id and we don't
1254 * create the final meta to check if we are re-using
1255 * the same next objective for different tunnels.
1256 *
1257 * @param pipeline the pipeline to support
1258 * @param srcCp the source port
1259 * @param dstCp the destination port
1260 * @param l2Tunnel the tunnel to support
1261 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001262 * @param oneHop if the pw only has one hop, push only pw label
1263 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1264 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001265 * @return the next objective to support the pipeline
1266 */
1267 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1268 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001269 DeviceId egressId, boolean leafSpinePw,
1270 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001271 NextObjective.Builder nextObjBuilder;
1272 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1273 if (pipeline == INITIATION) {
1274 nextObjBuilder = DefaultNextObjective
1275 .builder()
1276 .withType(NextObjective.Type.SIMPLE)
1277 .fromApp(srManager.appId());
1278 // The pw label is the bottom of stack. It has to
1279 // be different -1.
1280 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1281 log.warn("Pw label not configured");
1282 return null;
1283 }
1284 treatmentBuilder.pushMpls();
1285 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1286 treatmentBuilder.setMplsBos(true);
1287 treatmentBuilder.copyTtlOut();
1288
1289 // If the inter-co label is present we have to set the label.
1290 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1291 treatmentBuilder.pushMpls();
1292 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1293 treatmentBuilder.setMplsBos(false);
1294 treatmentBuilder.copyTtlOut();
1295 }
1296
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001297 // if not oneHop install transit mpls labels also
1298 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001299 // We retrieve the sr label from the config
1300 // specific for pseudowire traffic
1301 // using the egress leaf device id.
1302 MplsLabel srLabel;
1303 try {
1304 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1305
1306 } catch (DeviceConfigNotFoundException e) {
1307 log.warn("Sr label for pw traffic not configured");
1308 return null;
1309 }
1310
1311 treatmentBuilder.pushMpls();
1312 treatmentBuilder.setMpls(srLabel);
1313 treatmentBuilder.setMplsBos(false);
1314 treatmentBuilder.copyTtlOut();
1315 }
1316
1317 // We have to rewrite the src and dst mac address.
1318 MacAddress ingressMac;
1319 try {
1320 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1321 } catch (DeviceConfigNotFoundException e) {
1322 log.warn("Was not able to find the ingress mac");
1323 return null;
1324 }
1325 treatmentBuilder.setEthSrc(ingressMac);
1326 MacAddress neighborMac;
1327 try {
1328 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1329 } catch (DeviceConfigNotFoundException e) {
1330 log.warn("Was not able to find the neighbor mac");
1331 return null;
1332 }
1333 treatmentBuilder.setEthDst(neighborMac);
1334
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001335 // if true we need to pop the vlan because
1336 // we instantiate a leaf to leaf pseudowire
1337 if (!leafSpinePw) {
1338 log.info("We should carry this traffic UNTAGGED!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001339 treatmentBuilder.popVlan();
1340 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001341
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001342 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001343 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001344 } else {
1345 // We create the next objective which
1346 // will be a simple l2 group.
1347 nextObjBuilder = DefaultNextObjective
1348 .builder()
1349 .withType(NextObjective.Type.SIMPLE)
1350 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001351
1352 // for termination point we use the outer vlan of the
1353 // encapsulated packet
1354 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001355 }
1356
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001357 treatmentBuilder.setOutput(srcCp.port());
1358 nextObjBuilder.addTreatment(treatmentBuilder.build());
1359 return nextObjBuilder;
1360 }
1361
1362 /**
1363 * Reverses a link.
1364 *
1365 * @param link link to be reversed
1366 * @return the reversed link
1367 */
1368 private Link reverseLink(Link link) {
1369
1370 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1371
1372 linkBuilder.src(link.dst());
1373 linkBuilder.dst(link.src());
1374 linkBuilder.type(link.type());
1375 linkBuilder.providerId(link.providerId());
1376
1377 return linkBuilder.build();
1378 }
1379
1380 /**
1381 * Returns the path betwwen two connect points.
1382 *
1383 * @param srcCp source connect point
1384 * @param dstCp destination connect point
1385 * @return the path
1386 */
1387 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001388 Set<Path> paths = srManager.topologyService.getPaths(
1389 srManager.topologyService.currentTopology(),
1390 srcCp.deviceId(), dstCp.deviceId());
1391
1392 log.debug("Paths obtained from topology service {}", paths);
1393
1394 // We randomly pick a path.
1395 if (paths.isEmpty()) {
1396 return null;
1397 }
1398 int size = paths.size();
1399 int index = RandomUtils.nextInt(0, size);
1400
1401 List<Link> result = Iterables.get(paths, index).links();
1402 log.debug("Randomly picked a path {}", result);
1403
1404 return result;
1405 }
1406
1407 /**
1408 * Deletes a given policy using the parameter supplied.
1409 *
1410 * @param tunnelId the tunnel id
1411 * @param ingress the ingress point
1412 * @param ingressInner the ingress inner vlan id
1413 * @param ingressOuter the ingress outer vlan id
1414 * @param future to perform the async operation
1415 * @param direction the direction: forward or reverse
1416 */
1417 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1418 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1419
1420 String key = generateKey(tunnelId, direction);
1421 if (!l2InitiationNextObjStore.containsKey(key)) {
1422 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1423 if (future != null) {
1424 future.complete(null);
1425 }
1426 return;
1427 }
1428 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1429 int nextId = nextObjective.id();
1430 List<Objective> objectives = Lists.newArrayList();
1431 // We create the forwarding objective.
1432 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1433 ObjectiveContext context = new ObjectiveContext() {
1434 @Override
1435 public void onSuccess(Objective objective) {
1436 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1437 if (future != null) {
1438 future.complete(null);
1439 }
1440 }
1441
1442 @Override
1443 public void onError(Objective objective, ObjectiveError error) {
1444 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1445 if (future != null) {
1446 future.complete(error);
1447 }
1448 }
1449 };
1450 objectives.add(fwdBuilder.remove(context));
1451 // We create the filtering objective to define the
1452 // permit traffic in the switch
1453 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1454 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1455 .builder()
1456 .setTunnelId(tunnelId)
1457 .setVlanId(egressVlan);
1458 filtBuilder.withMeta(treatment.build());
1459 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1460 (objective, error) ->
1461 log.warn("Failed to revoke filterObj for policy {}",
1462 tunnelId, error));
1463 objectives.add(filtBuilder.remove(context));
1464
1465 for (Objective objective : objectives) {
1466 if (objective instanceof ForwardingObjective) {
1467 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1468 } else {
1469 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1470 }
1471 }
1472 }
1473
1474 /**
1475 * Deletes the pseudo wire initiation.
1476 *
1477 * @param l2TunnelId the tunnel id
1478 * @param ingress the ingress connect point
1479 * @param future to perform an async operation
1480 * @param direction the direction: reverse of forward
1481 */
1482 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1483 CompletableFuture<ObjectiveError> future, Direction direction) {
1484
1485 String key = generateKey(l2TunnelId, direction);
1486 if (!l2InitiationNextObjStore.containsKey(key)) {
1487 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1488 if (future != null) {
1489 future.complete(null);
1490 }
1491 return;
1492 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001493
1494 // un-comment in case you want to delete groups used by the pw
1495 // however, this will break the update of pseudowires cause the L2 interface group can
1496 // not be deleted (it is referenced by other groups)
1497 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001498 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001499 ObjectiveContext context = new ObjectiveContext() {
1500 @Override
1501 public void onSuccess(Objective objective) {
1502 log.debug("Previous {} next for {} removed", INITIATION, key);
1503 if (future != null) {
1504 future.complete(null);
1505 }
1506 }
1507
1508 @Override
1509 public void onError(Objective objective, ObjectiveError error) {
1510 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1511 if (future != null) {
1512 future.complete(error);
1513 }
1514 }
1515 };
1516 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1517 */
1518
1519 future.complete(null);
1520 l2InitiationNextObjStore.remove(key);
1521 }
1522
1523 /**
1524 * Deletes the pseudo wire termination.
1525 *
1526 * @param l2Tunnel the tunnel
1527 * @param egress the egress connect point
1528 * @param future the async task
1529 * @param direction the direction of the tunnel
1530 */
1531 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1532 ConnectPoint egress,
1533 CompletableFuture<ObjectiveError> future,
1534 Direction direction) {
1535
1536 String key = generateKey(l2Tunnel.tunnelId(), direction);
1537 if (!l2TerminationNextObjStore.containsKey(key)) {
1538 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1539 if (future != null) {
1540 future.complete(null);
1541 }
1542 return;
1543 }
1544 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1545 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1546 l2Tunnel.tunnelId(),
1547 egress.port(),
1548 nextObjective.id());
1549 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1550 log.debug("FwdObj for {} {}, " +
1551 "direction {} removed",
1552 TERMINATION,
1553 l2Tunnel.tunnelId(),
1554 direction),
1555 (objective, error) ->
1556 log.warn("Failed to remove fwdObj " +
1557 "for {} {}" +
1558 ", direction {}",
1559 TERMINATION,
1560 l2Tunnel.tunnelId(),
1561 error,
1562 direction));
1563 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1564
1565 // un-comment in case you want to delete groups used by the pw
1566 // however, this will break the update of pseudowires cause the L2 interface group can
1567 // not be deleted (it is referenced by other groups)
1568 /*
1569 context = new ObjectiveContext() {
1570 @Override
1571 public void onSuccess(Objective objective) {
1572 log.debug("Previous {} next for {} removed", TERMINATION, key);
1573 if (future != null) {
1574 future.complete(null);
1575 }
1576 }
1577
1578 @Override
1579 public void onError(Objective objective, ObjectiveError error) {
1580 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1581 if (future != null) {
1582 future.complete(error);
1583 }
1584 }
1585 };
1586 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1587 */
1588
1589 // delete the extra filtering objective for terminating
1590 // spine-spine pws
1591 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1592
1593 // determine the input port at the
1594 PortNumber inPort;
1595
1596 if (egress.deviceId().
1597 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1598 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1599 } else {
1600 inPort = l2Tunnel.pathUsed().get(0).src().port();
1601 }
1602
1603 MacAddress dstMac;
1604 try {
1605 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1606 } catch (Exception e) {
1607 log.info("Device not found in configuration, no programming of MAC address");
1608 dstMac = null;
1609 }
1610
1611 log.info("Removing filtering objective for pseudowire transport" +
1612 " with vlan = {}, port = {}, mac = {}",
1613 l2Tunnel.transportVlan(),
1614 inPort,
1615 dstMac);
1616 FilteringObjective.Builder filteringObjectiveBuilder =
1617 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1618 context = new DefaultObjectiveContext(( objective ) ->
1619 log.debug("Special filtObj for " + "for {} removed",
1620 l2Tunnel.tunnelId()), ( objective, error ) ->
1621 log.warn("Failed to populate " + "special filtObj " +
1622 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1623 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1624 filteringObjectiveBuilder.withMeta(treatment.build());
1625 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1626 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1627 l2Tunnel.tunnelId(),
1628 inPort);
1629 }
1630
1631 l2TerminationNextObjStore.remove(key);
1632 future.complete(null);
1633 }
1634
1635 /**
1636 * Utilities to generate pw key.
1637 *
1638 * @param tunnelId the tunnel id
1639 * @param direction the direction of the pw
1640 * @return the key of the store
1641 */
1642 private String generateKey(long tunnelId, Direction direction) {
1643 return String.format("%s-%s", tunnelId, direction);
1644 }
1645
1646}