blob: c736614c9b4f426e0c21e77dc5d2264c380c8720 [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;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070048import org.onosproject.net.topology.LinkWeigher;
49import org.onosproject.segmentrouting.SRLinkWeigher;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080050import org.onosproject.segmentrouting.SegmentRoutingManager;
51import org.onosproject.segmentrouting.SegmentRoutingService;
52import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080053import org.onosproject.store.serializers.KryoNamespaces;
54import org.onosproject.store.service.ConsistentMap;
55import org.onosproject.store.service.DistributedSet;
56import org.onosproject.store.service.Serializer;
57import org.onosproject.store.service.Versioned;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
61import java.util.ArrayList;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070062import java.util.HashSet;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080063import java.util.List;
64import java.util.Set;
65import java.util.concurrent.CompletableFuture;
66import java.util.stream.Collectors;
67
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080068import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
69import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
70import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
71import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
72import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
73import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -070074import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080075
76/**
77 * Handles pwaas related events.
78 */
79public class DefaultL2TunnelHandler implements L2TunnelHandler {
80
81 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
82
83 private final SegmentRoutingManager srManager;
84 /**
85 * To store the next objectives related to the initiation.
86 */
87 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
88 /**
89 * To store the next objectives related to the termination.
90 */
91 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
92
93 /**
94 * To store policies.
95 */
96 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
97
98 /**
99 * To store tunnels.
100 */
101 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
102
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700103 /**
104 * To store pending tunnels that need to be installed.
105 */
106 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
107
108 /**
109 * To store pending policies that need to be installed.
110 */
111 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
112
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800113 private final KryoNamespace.Builder l2TunnelKryo;
114
115 /**
116 * Contains transport vlans used for spine-leaf pseudowires.
117 */
118 private final DistributedSet<VlanId> vlanStore;
119
120 /**
121 * Used for determining transport vlans for leaf-spine.
122 */
123 private short transportVlanUpper = 4093, transportVlanLower = 3500;
124
125 private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
126
127 /**
128 * Create a l2 tunnel handler for the deploy and
129 * for the tear down of pseudo wires.
130 *
131 * @param segmentRoutingManager the segment routing manager
132 */
133 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
134 srManager = segmentRoutingManager;
135 l2TunnelKryo = new KryoNamespace.Builder()
136 .register(KryoNamespaces.API)
137 .register(L2Tunnel.class,
138 L2TunnelPolicy.class,
139 DefaultL2Tunnel.class,
140 DefaultL2TunnelPolicy.class,
141 L2Mode.class,
142 MplsLabel.class,
143 VlanId.class,
144 ConnectPoint.class);
145
146 l2InitiationNextObjStore = srManager.
147 storageService.
148 <String, NextObjective>consistentMapBuilder().
149 withName("onos-l2initiation-nextobj-store").
150 withSerializer(Serializer.using(l2TunnelKryo.build())).
151 build();
152
153 l2TerminationNextObjStore = srManager.storageService.
154 <String, NextObjective>consistentMapBuilder()
155 .withName("onos-l2termination-nextobj-store")
156 .withSerializer(Serializer.using(l2TunnelKryo.build()))
157 .build();
158
159 l2PolicyStore = srManager.storageService
160 .<String, L2TunnelPolicy>consistentMapBuilder()
161 .withName("onos-l2-policy-store")
162 .withSerializer(Serializer.using(l2TunnelKryo.build()))
163 .build();
164
165 l2TunnelStore = srManager.storageService
166 .<String, L2Tunnel>consistentMapBuilder()
167 .withName("onos-l2-tunnel-store")
168 .withSerializer(Serializer.using(l2TunnelKryo.build()))
169 .build();
170
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700171 pendingL2PolicyStore = srManager.storageService
172 .<String, L2TunnelPolicy>consistentMapBuilder()
173 .withName("onos-l2-pending-policy-store")
174 .withSerializer(Serializer.using(l2TunnelKryo.build()))
175 .build();
176
177 pendingL2TunnelStore = srManager.storageService
178 .<String, L2Tunnel>consistentMapBuilder()
179 .withName("onos-l2-pending-tunnel-store")
180 .withSerializer(Serializer.using(l2TunnelKryo.build()))
181 .build();
182
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800183 vlanStore = srManager.storageService.<VlanId>setBuilder()
184 .withName("onos-transport-vlan-store")
185 .withSerializer(Serializer.using(
186 new KryoNamespace.Builder()
187 .register(KryoNamespaces.API)
188 .build()))
189 .build()
190 .asDistributedSet();
191 }
192
193 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800194 * Used by manager only in initialization.
195 */
196 @Override
197 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800198 // Since we have no pseudowires in netcfg there
199 // is nothing to do in initialization.
200 // I leave it here because potentially we might need to
201 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800202 }
203
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700204 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700205 public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700206 // get pending tunnels/policies OR installed tunnels/policies
207 List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
208 List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700209 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700210 .map(l2Tunnel -> {
211 L2TunnelPolicy policy = null;
212 for (L2TunnelPolicy l2Policy : policies) {
213 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
214 policy = l2Policy;
215 break;
216 }
217 }
218
219 return new DefaultL2TunnelDescription(l2Tunnel, policy);
220 })
221 .collect(Collectors.toSet());
222 }
223
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800224 @Override
225 public List<L2TunnelPolicy> getL2Policies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800226 return new ArrayList<>(l2PolicyStore
227 .values()
228 .stream()
229 .map(Versioned::value)
230 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800231 }
232
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800233 @Override
234 public List<L2Tunnel> getL2Tunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800235 return new ArrayList<>(l2TunnelStore
236 .values()
237 .stream()
238 .map(Versioned::value)
239 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800240 }
241
242 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700243 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700244 return new ArrayList<>(pendingL2PolicyStore
245 .values()
246 .stream()
247 .map(Versioned::value)
248 .collect(Collectors.toList()));
249 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800250
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700251 @Override
252 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700253 return new ArrayList<>(pendingL2TunnelStore
254 .values()
255 .stream()
256 .map(Versioned::value)
257 .collect(Collectors.toList()));
258 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800259
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700260 @Override
261 public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
262
263 // get both added and pending pseudowires
264 List<L2TunnelDescription> newPseudowires = new ArrayList<>();
265 newPseudowires.addAll(getL2Descriptions(false));
266 newPseudowires.addAll(getL2Descriptions(true));
267 // add the new one
268 newPseudowires.add(pwToAdd);
269
270 return configurationValidity(newPseudowires);
271 }
272
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700273 /**
274 * Manages intermediate filtering rules.
275 *
276 * For leaf-spine-spine pseudowires we need to install a special filtering
277 * rule in the intermediate spine for the appropriate transport vlan.
278 *
279 * @param pw The pseudowire, it will have the path and the transport vlan.
280 */
281 private Result manageIntermediateFiltering(L2TunnelDescription pw, boolean leafSpinePw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800282
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700283 // only leaf-spine-spine should need intermediate rules for now
284 if (!leafSpinePw) {
285 return Result.SUCCESS;
286 }
287 if (pw.l2Tunnel().pathUsed().size() != 2) {
288 return Result.SUCCESS;
289 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800290
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700291 List<Link> path = pw.l2Tunnel().pathUsed();
292 DeviceId intermediateSpineId = pw.l2Tunnel().pathUsed().get(0).dst().deviceId();
293 L2Tunnel l2Tunnel = pw.l2Tunnel();
294
295 log.info("Installing intermediate filtering rules for spine {} , for pseudowire {}",
296 intermediateSpineId, pw.l2Tunnel().tunnelId());
297
298 MacAddress dstMac;
299 try {
300 dstMac = srManager.deviceConfiguration().getDeviceMac(intermediateSpineId);
301 } catch (Exception e) {
302 log.info("Device not found in configuration, no programming of MAC address");
303 dstMac = null;
304 }
305
306 PortNumber inPort;
307
308 inPort = path.get(0).dst().port();
309
310 log.debug("Populating filtering objective for pseudowire transport" +
311 " with vlan = {}, port = {}, mac = {} for device {}",
312 l2Tunnel.transportVlan(),
313 inPort,
314 dstMac,
315 intermediateSpineId);
316
317 FilteringObjective.Builder filteringObjectiveBuilder =
318 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
319 DefaultObjectiveContext context = new DefaultObjectiveContext((objective) ->
320 log.debug("Special filtObj for " +
321 "for {} populated",
322 l2Tunnel.tunnelId()),
323 (objective, error) ->
324 log.warn("Failed to populate " +
325 "special filtObj " +
326 "rule for {}: {}",
327 l2Tunnel.tunnelId(), error));
328 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
329 filteringObjectiveBuilder.withMeta(treatment.build());
330 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
331
332 inPort = path.get(1).src().port();
333
334 log.debug("Populating filtering objective for pseudowire transport" +
335 " with vlan = {}, port = {}, mac = {} for device {}",
336 l2Tunnel.transportVlan(),
337 inPort,
338 dstMac,
339 intermediateSpineId);
340
341 filteringObjectiveBuilder =
342 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
343 context = new DefaultObjectiveContext((objective) ->
344 log.debug("Special filtObj for " + "for {} populated",
345 l2Tunnel.tunnelId()),
346 (objective, error) ->
347 log.warn("Failed to populate " +
348 "special filtObj " +
349 "rule for {}: {}",
350 l2Tunnel.tunnelId(), error));
351 treatment = DefaultTrafficTreatment.builder();
352 filteringObjectiveBuilder.withMeta(treatment.build());
353 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
354
355 return Result.SUCCESS;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800356 }
357
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800358 /**
359 * Returns the new vlan id for an ingress point of a
360 * pseudowire. For double tagged, it is the outer,
361 * For single tagged it is the single tag, and for
362 * inner it is None.
363 *
364 * @param ingressOuter vlanid of ingress outer
365 * @param ingressInner vlanid of ingress inner
366 * @param egressOuter vlanid of egress outer
367 * @param egressInner vlanid of egress inner
368 * @return returns the vlan id which will be installed at vlan table 1.
369 */
370 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
371 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800372 // validity of vlan combinations was checked at verifyPseudowire
373 if (!(ingressOuter.equals(VlanId.NONE))) {
374 return egressOuter;
375 } else if (!(ingressInner.equals(VlanId.NONE))) {
376 return egressInner;
377 } else {
378 return VlanId.vlanId("None");
379 }
380 }
381
382 /**
383 * Determines vlan used for transporting the pw traffic.
384 *
385 * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
386 * and also make sure to add the popVlan instruction.
387 * For spine-leaf pws we choose the highest vlan value available from a certain range.
388 *
389 * @param spinePw if the pw is leaf-spine.
390 * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
391 * then the pw is transported untagged.
392 */
393 private VlanId determineTransportVlan(boolean spinePw) {
394
395 if (!spinePw) {
396
397 log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
398 return UNTAGGED_TRANSPORT_VLAN;
399 } else {
400 for (short i = transportVlanUpper; i > transportVlanLower; i--) {
401
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700402 VlanId vlanToUse = VlanId.vlanId(i);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800403 if (!vlanStore.contains(vlanToUse)) {
404
405 vlanStore.add(vlanToUse);
406 log.info("Transport vlan {} for pseudowire!", vlanToUse);
407 return vlanToUse;
408 }
409 }
410
411 log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
412 "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
413 return UNTAGGED_TRANSPORT_VLAN;
414 }
415 }
416
417 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700418 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700419 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700420 * @param path The path to traverse.
421 * @return The devices on the path with the order they
422 * are traversed.
423 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700424 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700425
426 // iterate over links and get all devices in the order
427 // we find them
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700428 List<DeviceId> deviceList = new ArrayList<>();
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700429 for (Link link : path) {
430 if (!deviceList.contains(link.src().deviceId())) {
431 deviceList.add(link.src().deviceId());
432 }
433 if (!deviceList.contains(link.dst().deviceId())) {
434 deviceList.add(link.dst().deviceId());
435 }
436 }
437
438 return deviceList;
439 }
440
441 /**
442 * Returns true if path is valid according to the current logic.
443 * For example : leaf to spine pseudowires can be either leaf-spine or
444 * leaf-spine-spine. However, in the configuration we might specify spine device
445 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
446 * two cases and need to provision for them.
447 *
448 * If we have a leaf to leaf pseudowire then all the intermediate devices must
449 * be spines. However, in case of paired switches we can have two leafs interconnected
450 * with each other directly.
451 *
452 * @param path the chosen path
453 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700454 * @return True if path size is valid, false otherwise.
455 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700456 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
457
458 List<DeviceId> devices = getDevicesOnPath(path);
459 if (devices.size() < 2) {
460 log.error("Path size for pseudowire should be greater than 1!");
461 return false;
462 }
463
464 try {
465 if (leafSpinePw) {
466 // can either be leaf-spine-spine or leaf-spine
467 // first device is leaf, all other must be spines
468 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
469 // if first device is a leaf then all other must be spines
470 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
471 devices.remove(0);
472 for (DeviceId devId : devices) {
473 log.debug("Device {} should be a spine!", devId);
474 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
475 return false;
476 }
477 }
478 } else {
479 // if first device is spine, last device must be a leaf
480 // all other devices must be spines
481 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
482 return false;
483 }
484 devices.remove(devices.size() - 1);
485 for (DeviceId devId : devices) {
486 log.debug("Device {} should be a spine!", devId);
487 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
488 return false;
489 }
490 }
491 }
492 } else {
493 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
494 // or leaf-spine-spine-leaf
495 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
496 // check first device, needs to be a leaf
497 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
498 return false;
499 }
500 // check last device, needs to be a leaf
501 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
502 return false;
503 }
504 // remove these devices, rest must all be spines
505 devices.remove(0);
506 devices.remove(devices.size() - 1);
507 for (DeviceId devId : devices) {
508 log.debug("Device {} should be a spine!", devId);
509 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
510 return false;
511 }
512 }
513
514 }
515 } catch (DeviceConfigNotFoundException e) {
516 log.error("Device not found in configuration : {}", e);
517 return false;
518 }
519
520 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700521 }
522
523 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800524 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800525 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700526 * @param pw The pseudowire to deploy
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800527 * @return result of pseudowire deployment
528 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700529 public Result deployPseudowire(L2TunnelDescription pw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800530
531 Result result;
532 long l2TunnelId;
533
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700534 log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
535 pw.l2Tunnel().tunnelId());
536
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800537 l2TunnelId = pw.l2Tunnel().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800538 // The tunnel id cannot be 0.
539 if (l2TunnelId == 0) {
540 log.warn("Tunnel id id must be > 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700541 return Result.WRONG_PARAMETERS
542 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800543 }
544
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700545 result = verifyGlobalValidity(pw);
546 if (result != SUCCESS) {
547 log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
548 return result;
549 }
550
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700551 // leafSpinePw determines if this is a leaf-leaf
552 // or leaf-spine pseudowire
553 boolean leafSpinePw;
554 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
555 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
556 try {
557 // differentiate between leaf-leaf pseudowires and leaf-spine
558 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
559 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
560 log.error("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700561 return Result.WRONG_PARAMETERS
562 .appendError("Can not deploy pseudowire from spine to spine!");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700563 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
564 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
565 leafSpinePw = false;
566 } else {
567 leafSpinePw = true;
568 }
569 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700570 log.error("Device for pseudowire connection points does not exist in the configuration");
571 return Result.INTERNAL_ERROR
572 .appendError("Device for pseudowire connection points does not exist in the configuration");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700573 }
574
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -0700575 // reverse the policy in order for leaf switch to be at CP1
576 // this will help us for re-using SRLinkWeigher for computing valid paths
577 L2TunnelPolicy reversedPolicy = reverseL2TunnelPolicy(pw.l2TunnelPolicy());
578 if (reversedPolicy == null) {
579 log.error("Error in reversing policy, device configuration was not found!");
580 return INTERNAL_ERROR
581 .appendError("Device configuration not found when reversing the policy.");
582 }
583 pw.setL2TunnelPolicy(reversedPolicy);
584
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800585 // get path here, need to use the same for fwd and rev direction
586 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
587 pw.l2TunnelPolicy().cP2());
588 if (path == null) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700589 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
590 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800591 }
592
593 Link fwdNextHop;
594 Link revNextHop;
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700595 if (!isValidPath(path, leafSpinePw)) {
596 log.error("Deploying process : Path for pseudowire {} is not valid",
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700597 l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700598 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800599 }
600
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700601 // oneHope flag is used to determine if we need to
602 // install transit mpls rules
603 boolean oneHop = true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700604 if (path.size() > 1) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700605 oneHop = false;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700606 }
607
608 fwdNextHop = path.get(0);
609 revNextHop = reverseLink(path.get(path.size() - 1));
610
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800611 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700612 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800613
614 // next hops for next objectives
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800615 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
616
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800617 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
618 pw.l2TunnelPolicy().cP1InnerTag(),
619 pw.l2TunnelPolicy().cP2OuterTag(),
620 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800621 // We establish the tunnel.
622 // result.nextId will be used in fwd
623 result = deployPseudoWireInit(pw.l2Tunnel(),
624 pw.l2TunnelPolicy().cP1(),
625 pw.l2TunnelPolicy().cP2(),
626 FWD,
627 fwdNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700628 leafSpinePw,
629 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800630 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800631 if (result != SUCCESS) {
632 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700633 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800634 }
635
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800636 // We create the policy.
637 result = deployPolicy(l2TunnelId,
638 pw.l2TunnelPolicy().cP1(),
639 pw.l2TunnelPolicy().cP1InnerTag(),
640 pw.l2TunnelPolicy().cP1OuterTag(),
641 egressVlan,
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -0700642 result.getNextId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800643 if (result != SUCCESS) {
644 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700645 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800646 }
647
648 // We terminate the tunnel
649 result = deployPseudoWireTerm(pw.l2Tunnel(),
650 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800651 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800652 FWD,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700653 leafSpinePw,
654 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800655
656 if (result != SUCCESS) {
657 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700658 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800659 }
660
661 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
662
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800663 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
664 pw.l2TunnelPolicy().cP2InnerTag(),
665 pw.l2TunnelPolicy().cP1OuterTag(),
666 pw.l2TunnelPolicy().cP1InnerTag());
667
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800668 // We establish the reverse tunnel.
669 result = deployPseudoWireInit(pw.l2Tunnel(),
670 pw.l2TunnelPolicy().cP2(),
671 pw.l2TunnelPolicy().cP1(),
672 REV,
673 revNextHop,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700674 leafSpinePw,
675 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800676 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800677 if (result != SUCCESS) {
678 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700679 return Result.INTERNAL_ERROR
680 .appendError("Error in deploying pseudowire initiation for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800681 }
682
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800683
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800684 result = deployPolicy(l2TunnelId,
685 pw.l2TunnelPolicy().cP2(),
686 pw.l2TunnelPolicy().cP2InnerTag(),
687 pw.l2TunnelPolicy().cP2OuterTag(),
688 egressVlan,
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -0700689 result.getNextId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800690 if (result != SUCCESS) {
691 log.info("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700692 return Result.INTERNAL_ERROR
693 .appendError("Deploying process : Error in deploying policy for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800694 }
695
696 result = deployPseudoWireTerm(pw.l2Tunnel(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700697 pw.l2TunnelPolicy().cP1(),
698 egressVlan,
699 REV,
700 leafSpinePw,
701 oneHop);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800702
703 if (result != SUCCESS) {
704 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700705 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800706 }
707
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700708 result = manageIntermediateFiltering(pw, leafSpinePw);
709 if (result != SUCCESS) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700710 log.info("Deploying process : Error in installing intermediate rules for tagged transport");
711 return Result.INTERNAL_ERROR.appendError("Error in installing intermediate rules for tagged transport");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700712 }
713
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800714 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
715
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700716 // Populate stores as the final step of the process
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800717 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
718 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
719
720 return Result.SUCCESS;
721 }
722
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700723 @Override
724 public Result checkIfPwExists(long tunnelId, boolean pending) {
725
726 List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
727 .stream()
728 .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
729 .collect(Collectors.toList());
730
731 if (pseudowires.size() == 0) {
732 String store = ((pending) ? "pending" : "installed");
733 log.error("Pseudowire {} does not exist in {} store", tunnelId, store);
734 return Result.WRONG_PARAMETERS.
735 appendError("Pseudowire " + tunnelId + " does not exist in " + store);
736 } else {
737 return SUCCESS;
738 }
739 }
740
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800741 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700742 * Tears down connection points of pseudowires. We can either tear down both connection points,
743 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800744 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700745 * @param l2TunnelId The tunnel id for this pseudowire.
746 * @param tearDownFirst Boolean, true if we want to tear down cp1
747 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700748 * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
749 * in the network, else remove flows/groups in the devices also.
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700750 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700751 * a descriptive error otherwise.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800752 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700753 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
754 boolean tearDownSecond, boolean pending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800755
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700756 Result res;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800757 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
758 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800759 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
760 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
761
762 if (l2TunnelId == 0) {
763 log.warn("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700764 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800765 }
766
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700767 res = checkIfPwExists(l2TunnelId, pending);
768 if (res != Result.SUCCESS) {
769 return res;
770 }
771
772 // remove and get the tunnel and the policy from the appropriate store
773 // if null, return error.
774 Versioned<L2Tunnel> l2TunnelVersioned = pending ?
775 pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
776 l2TunnelStore.remove(Long.toString(l2TunnelId));
777 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
778 pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
779 l2PolicyStore.remove(Long.toString(l2TunnelId));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800780 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
781 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700782 return Result.INTERNAL_ERROR
783 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800784 }
785
786 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700787 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800788
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800789 // remove the reserved transport vlan
790 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
791 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
792 }
793
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700794 if (pending) {
795 // no need to remove flows / groups for a pseudowire
796 // in pending state
797 return Result.SUCCESS;
798 }
799
800 // remove flows/groups involving with this pseudowire
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700801 if (tearDownFirst) {
802 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800803
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700804 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
805 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
806 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
807 pwToRemove.l2TunnelPolicy().cP2InnerTag());
808 deletePolicy(l2TunnelId,
809 pwToRemove.l2TunnelPolicy().cP1(),
810 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
811 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
812 egressVlan,
813 fwdInitNextFuture,
814 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800815
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700816 fwdInitNextFuture.thenAcceptAsync(status -> {
817 if (status == null) {
818 // Finally we will tear down the pseudo wire.
819 tearDownPseudoWireInit(l2TunnelId,
820 pwToRemove.l2TunnelPolicy().cP1(),
821 fwdTermNextFuture,
822 FWD);
823 }
824 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800825
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700826 fwdTermNextFuture.thenAcceptAsync(status -> {
827 if (status == null) {
828 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
829 pwToRemove.l2TunnelPolicy().cP2(),
830 null,
831 FWD);
832 }
833 });
834 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800835
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700836 if (tearDownSecond) {
837 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800838
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700839 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
840 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
841 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
842 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800843
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700844 // We do the same operations on the reverse side.
845 deletePolicy(l2TunnelId,
846 pwToRemove.l2TunnelPolicy().cP2(),
847 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
848 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
849 egressVlan,
850 revInitNextFuture,
851 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800852
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700853 revInitNextFuture.thenAcceptAsync(status -> {
854 if (status == null) {
855 tearDownPseudoWireInit(l2TunnelId,
856 pwToRemove.l2TunnelPolicy().cP2(),
857 revTermNextFuture,
858 REV);
859 }
860 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800861
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700862 revTermNextFuture.thenAcceptAsync(status -> {
863 if (status == null) {
864 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
865 pwToRemove.l2TunnelPolicy().cP1(),
866 null,
867 REV);
868 }
869 });
870 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800871
872 return Result.SUCCESS;
873 }
874
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700875 /**
876 * Helper function for removing a single pseudowire.
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700877 *
878 * Tries to remove pseudowire from any store it might reside (pending or installed).
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700879 *
880 * @param l2TunnelId the id of the pseudowire to tear down
881 * @return Returns SUCCESS if no error is obeserved or an appropriate
882 * error on a failure
883 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700884 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700885
886 if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
887 return tearDownConnectionPoints(l2TunnelId, true, true, true);
888 } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
889 return tearDownConnectionPoints(l2TunnelId, true, true, false);
890 } else {
891 return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
892 + l2TunnelId
893 + " did not reside in any store!");
894 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700895 }
896
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700897 @Override
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700898 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800899 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
900
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800901 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800902
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700903 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800904 log.info("Removing pseudowire {}", tunnelId);
905
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700906 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700907 if (result != Result.SUCCESS) {
908 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800909 }
910 }
911 }
912
913 /**
914 * Handles the policy establishment which consists in
915 * create the filtering and forwarding objectives related
916 * to the initiation and termination.
917 *
918 * @param tunnelId the tunnel id
919 * @param ingress the ingress point
920 * @param ingressInner the ingress inner tag
921 * @param ingressOuter the ingress outer tag
922 * @param nextId the next objective id
923 * @param egressVlan Vlan-id to set, depends on ingress vlan
924 * combinations. For example, if pw is double tagged
925 * then this is the value of the outer vlan, if single
926 * tagged then it is the new value of the single tag.
927 * Should be None for untagged traffic.
928 * @return the result of the operation
929 */
930 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
931 VlanId ingressOuter, VlanId egressVlan, int nextId) {
932
933 List<Objective> objectives = Lists.newArrayList();
934 // We create the forwarding objective for supporting
935 // the l2 tunnel.
936 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
937 // We create and add objective context.
938 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
939 log.debug("FwdObj for tunnel {} populated", tunnelId),
940 (objective, error) ->
941 log.warn("Failed to populate fwdrObj " +
942 "for tunnel {}", tunnelId, error));
943 objectives.add(fwdBuilder.add(context));
944
945 // We create the filtering objective to define the
946 // permit traffic in the switch
947 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
948
949 // We add the metadata.
950 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
951 .builder()
952 .setTunnelId(tunnelId)
953 .setVlanId(egressVlan);
954 filtBuilder.withMeta(treatment.build());
955
956 // We create and add objective context.
957 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
958 (objective, error) -> log.warn("Failed to populate filterObj for " +
959 "tunnel {}", tunnelId, error));
960 objectives.add(filtBuilder.add(context));
961
962 for (Objective objective : objectives) {
963 if (objective instanceof ForwardingObjective) {
964 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
965 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
966 } else {
967 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
968 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
969 }
970 }
971 return SUCCESS;
972 }
973
974 /**
975 * Handles the tunnel establishment which consists in
976 * create the next objectives related to the initiation.
977 *
978 * @param l2Tunnel the tunnel to deploy
979 * @param ingress the ingress connect point
980 * @param egress the egress connect point
981 * @param direction the direction of the pw
982 * @param spinePw if the pseudowire involves a spine switch
983 * @return the result of the operation
984 */
985 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800986 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700987 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800988
989 if (nextHop == null) {
990 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
991 return WRONG_PARAMETERS;
992 }
993
994 // We create the next objective without the metadata
995 // context and id. We check if it already exists in the
996 // store. If not we store as it is in the store.
997 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
998 nextHop.src(),
999 nextHop.dst(),
1000 l2Tunnel,
1001 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001002 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001003 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001004 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001005
1006 if (nextObjectiveBuilder == null) {
1007 return INTERNAL_ERROR;
1008 }
1009 // We set the metadata. We will use this metadata
1010 // to inform the driver we are doing a l2 tunnel.
1011 TrafficSelector metadata = DefaultTrafficSelector
1012 .builder()
1013 .matchTunnelId(l2Tunnel.tunnelId())
1014 .build();
1015 nextObjectiveBuilder.withMeta(metadata);
1016 int nextId = srManager.flowObjectiveService.allocateNextId();
1017 if (nextId < 0) {
1018 log.warn("Not able to allocate a next id for initiation");
1019 return INTERNAL_ERROR;
1020 }
1021 nextObjectiveBuilder.withId(nextId);
1022 String key = generateKey(l2Tunnel.tunnelId(), direction);
1023 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
1024 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1025 log.debug("Initiation l2 tunnel rule " +
1026 "for {} populated",
1027 l2Tunnel.tunnelId()),
1028 (objective, error) ->
1029 log.warn("Failed to populate Initiation " +
1030 "l2 tunnel rule for {}: {}",
1031 l2Tunnel.tunnelId(), error));
1032 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1033 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1034 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
1035 l2Tunnel.tunnelId(), nextObjective.id());
1036 Result result = SUCCESS;
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -07001037 result.setNextId(nextObjective.id());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001038 return result;
1039 }
1040
1041 /**
1042 * Handles the tunnel termination, which consists in the creation
1043 * of a forwarding objective and a next objective.
1044 *
1045 * @param l2Tunnel the tunnel to terminate
1046 * @param egress the egress point
1047 * @param egressVlan the expected vlan at egress
1048 * @param direction the direction
1049 * @param spinePw if the pseudowire involves a spine switch
1050 * @return the result of the operation
1051 */
1052 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001053 VlanId egressVlan, Direction direction, boolean spinePw, boolean oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001054
1055 // We create the group relative to the termination.
1056 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1057 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001058 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001059 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001060 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001061 if (nextObjectiveBuilder == null) {
1062 return INTERNAL_ERROR;
1063 }
1064 TrafficSelector metadata = DefaultTrafficSelector
1065 .builder()
1066 .matchVlanId(egressVlan)
1067 .build();
1068 nextObjectiveBuilder.withMeta(metadata);
1069 int nextId = srManager.flowObjectiveService.allocateNextId();
1070 if (nextId < 0) {
1071 log.warn("Not able to allocate a next id for initiation");
1072 return INTERNAL_ERROR;
1073 }
1074 nextObjectiveBuilder.withId(nextId);
1075 String key = generateKey(l2Tunnel.tunnelId(), direction);
1076 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1077 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1078 "for {} populated",
1079 l2Tunnel.tunnelId()),
1080 (objective, error) -> log.warn("Failed to populate " +
1081 "termination l2 tunnel " +
1082 "rule for {}: {}",
1083 l2Tunnel.tunnelId(),
1084 error));
1085 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1086 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1087 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1088 l2Tunnel.tunnelId(), nextObjective.id());
1089
1090 // We create the flow relative to the termination.
1091 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1092 egress.port(), nextObjective.id());
1093 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1094 l2Tunnel.tunnelId()),
1095 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1096 " for tunnel termination {}",
1097 l2Tunnel.tunnelId(), error));
1098 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1099 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1100 nextId, l2Tunnel.tunnelId());
1101
1102 if (spinePw) {
1103
1104 // determine the input port at the
1105 PortNumber inPort;
1106
1107 if (egress.deviceId().
1108 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1109 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1110 } else {
1111 inPort = l2Tunnel.pathUsed().get(0).src().port();
1112 }
1113
1114 MacAddress dstMac;
1115 try {
1116 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1117 } catch (Exception e) {
1118 log.info("Device not found in configuration, no programming of MAC address");
1119 dstMac = null;
1120 }
1121
1122 log.info("Populating filtering objective for pseudowire transport" +
1123 " with vlan = {}, port = {}, mac = {}",
1124 l2Tunnel.transportVlan(),
1125 inPort,
1126 dstMac);
1127 FilteringObjective.Builder filteringObjectiveBuilder =
1128 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001129 context = new DefaultObjectiveContext((objective) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001130 log.debug("Special filtObj for " + "for {} populated",
1131 l2Tunnel.tunnelId()),
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001132 (objective, error) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001133 log.warn("Failed to populate " +
1134 "special filtObj " +
1135 "rule for {}: {}",
1136 l2Tunnel.tunnelId(), error));
1137 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1138 filteringObjectiveBuilder.withMeta(treatment.build());
1139 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1140 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1141 l2Tunnel.tunnelId(),
1142 inPort);
1143 }
1144
1145 return SUCCESS;
1146 }
1147
1148
1149 /**
1150 * Creates the filtering objective according to a given port and vlanid.
1151 *
1152 * @param inPort the in port
1153 * @param vlanId the inner vlan tag
1154 * @return the filtering objective
1155 */
1156 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1157 VlanId vlanId,
1158 MacAddress dstMac) {
1159
1160 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1161 vlanId,
1162 inPort,
1163 dstMac);
1164 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1165 .builder()
1166 .withKey(Criteria.matchInPort(inPort))
1167 .addCondition(Criteria.matchVlanId(vlanId))
1168 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1169 .permit()
1170 .fromApp(srManager.appId());
1171
1172 if (dstMac != null) {
1173 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1174 }
1175
1176 return fwdBuilder;
1177 }
1178
1179 /**
1180 * Creates the filtering objective according to a given policy.
1181 *
1182 * @param inPort the in port
1183 * @param innerTag the inner vlan tag
1184 * @param outerTag the outer vlan tag
1185 * @return the filtering objective
1186 */
1187 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1188
1189 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1190 return DefaultFilteringObjective
1191 .builder()
1192 .withKey(Criteria.matchInPort(inPort))
1193 .addCondition(Criteria.matchInnerVlanId(innerTag))
1194 .addCondition(Criteria.matchVlanId(outerTag))
1195 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1196 .permit()
1197 .fromApp(srManager.appId());
1198 }
1199
1200 /**
1201 * Creates the forwarding objective for the termination.
1202 *
1203 * @param pwLabel the pseudo wire label
1204 * @param tunnelId the tunnel id
1205 * @param egressPort the egress port
1206 * @param nextId the next step
1207 * @return the forwarding objective to support the termination
1208 */
1209 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1210 PortNumber egressPort, int nextId) {
1211
1212 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1213 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1214 // The flow has to match on the pw label and bos
1215 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1216 trafficSelector.matchMplsLabel(pwLabel);
1217 trafficSelector.matchMplsBos(true);
1218 // The flow has to decrement ttl, restore ttl in
1219 // pop mpls, set tunnel id and port.
1220 trafficTreatment.decMplsTtl();
1221 trafficTreatment.copyTtlIn();
1222 trafficTreatment.popMpls();
1223 trafficTreatment.setTunnelId(tunnelId);
1224 trafficTreatment.setOutput(egressPort);
1225
1226 return DefaultForwardingObjective
1227 .builder()
1228 .fromApp(srManager.appId())
1229 .makePermanent()
1230 .nextStep(nextId)
1231 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1232 .withSelector(trafficSelector.build())
1233 .withTreatment(trafficTreatment.build())
1234 .withFlag(VERSATILE);
1235 }
1236
1237 /**
1238 * Creates the forwarding objective for the initiation.
1239 *
1240 * @param tunnelId the tunnel id
1241 * @param inPort the input port
1242 * @param nextId the next step
1243 * @return the forwarding objective to support the initiation.
1244 */
1245 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1246
1247 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1248
1249 // The flow has to match on the mpls logical
1250 // port and the tunnel id.
1251 trafficSelector.matchTunnelId(tunnelId);
1252 trafficSelector.matchInPort(inPort);
1253
1254 return DefaultForwardingObjective
1255 .builder()
1256 .fromApp(srManager.appId())
1257 .makePermanent()
1258 .nextStep(nextId)
1259 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1260 .withSelector(trafficSelector.build())
1261 .withFlag(VERSATILE);
1262
1263 }
1264
1265 /**
1266 * Creates the next objective according to a given
1267 * pipeline. We don't set the next id and we don't
1268 * create the final meta to check if we are re-using
1269 * the same next objective for different tunnels.
1270 *
1271 * @param pipeline the pipeline to support
1272 * @param srcCp the source port
1273 * @param dstCp the destination port
1274 * @param l2Tunnel the tunnel to support
1275 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001276 * @param oneHop if the pw only has one hop, push only pw label
1277 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1278 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001279 * @return the next objective to support the pipeline
1280 */
1281 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1282 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001283 DeviceId egressId, boolean leafSpinePw,
1284 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001285 NextObjective.Builder nextObjBuilder;
1286 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1287 if (pipeline == INITIATION) {
1288 nextObjBuilder = DefaultNextObjective
1289 .builder()
1290 .withType(NextObjective.Type.SIMPLE)
1291 .fromApp(srManager.appId());
1292 // The pw label is the bottom of stack. It has to
1293 // be different -1.
1294 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1295 log.warn("Pw label not configured");
1296 return null;
1297 }
1298 treatmentBuilder.pushMpls();
1299 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1300 treatmentBuilder.setMplsBos(true);
1301 treatmentBuilder.copyTtlOut();
1302
1303 // If the inter-co label is present we have to set the label.
1304 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1305 treatmentBuilder.pushMpls();
1306 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1307 treatmentBuilder.setMplsBos(false);
1308 treatmentBuilder.copyTtlOut();
1309 }
1310
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001311 // if not oneHop install transit mpls labels also
1312 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001313 // We retrieve the sr label from the config
1314 // specific for pseudowire traffic
1315 // using the egress leaf device id.
1316 MplsLabel srLabel;
1317 try {
1318 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1319
1320 } catch (DeviceConfigNotFoundException e) {
1321 log.warn("Sr label for pw traffic not configured");
1322 return null;
1323 }
1324
1325 treatmentBuilder.pushMpls();
1326 treatmentBuilder.setMpls(srLabel);
1327 treatmentBuilder.setMplsBos(false);
1328 treatmentBuilder.copyTtlOut();
1329 }
1330
1331 // We have to rewrite the src and dst mac address.
1332 MacAddress ingressMac;
1333 try {
1334 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1335 } catch (DeviceConfigNotFoundException e) {
1336 log.warn("Was not able to find the ingress mac");
1337 return null;
1338 }
1339 treatmentBuilder.setEthSrc(ingressMac);
1340 MacAddress neighborMac;
1341 try {
1342 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1343 } catch (DeviceConfigNotFoundException e) {
1344 log.warn("Was not able to find the neighbor mac");
1345 return null;
1346 }
1347 treatmentBuilder.setEthDst(neighborMac);
1348
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001349 // if true we need to pop the vlan because
1350 // we instantiate a leaf to leaf pseudowire
1351 if (!leafSpinePw) {
1352 log.info("We should carry this traffic UNTAGGED!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001353 treatmentBuilder.popVlan();
1354 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001355
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001356 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001357 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001358 } else {
1359 // We create the next objective which
1360 // will be a simple l2 group.
1361 nextObjBuilder = DefaultNextObjective
1362 .builder()
1363 .withType(NextObjective.Type.SIMPLE)
1364 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001365
1366 // for termination point we use the outer vlan of the
1367 // encapsulated packet
1368 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001369 }
1370
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001371 treatmentBuilder.setOutput(srcCp.port());
1372 nextObjBuilder.addTreatment(treatmentBuilder.build());
1373 return nextObjBuilder;
1374 }
1375
1376 /**
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001377 * Reverse an l2 tunnel policy in order to have as CP1 the leaf switch,
1378 * in case one of the switches is a spine.
1379 *
1380 * This makes possible the usage of SRLinkWeigher for computing valid paths,
1381 * which cuts leaf-spine links from the path computation with a src different
1382 * than the source leaf.
1383 *
1384 * @param policy The policy to reverse, if needed
1385 * @return a l2TunnelPolicy containing the leaf at CP1, suitable for usage with
1386 * current SRLinkWeigher
1387 */
1388 private L2TunnelPolicy reverseL2TunnelPolicy(L2TunnelPolicy policy) {
1389 try {
1390 // if cp1 is a leaf, just return
1391 if (srManager.deviceConfiguration().isEdgeDevice(policy.cP1().deviceId())) {
1392 return policy;
1393 } else {
1394 // return a policy with reversed cp1 and cp2, and also with reversed tags
1395 return new DefaultL2TunnelPolicy(policy.tunnelId(),
1396 policy.cP2(), policy.cP2InnerTag(), policy.cP2OuterTag(),
1397 policy.cP1(), policy.cP1InnerTag(), policy.cP1OuterTag());
1398
1399 }
1400 } catch (DeviceConfigNotFoundException e) {
1401 // should never come here, since it has been checked before
1402 log.error("Configuration for device {}, does not exist!");
1403 return null;
1404 }
1405 }
1406
1407 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001408 * Reverses a link.
1409 *
1410 * @param link link to be reversed
1411 * @return the reversed link
1412 */
1413 private Link reverseLink(Link link) {
1414
1415 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1416
1417 linkBuilder.src(link.dst());
1418 linkBuilder.dst(link.src());
1419 linkBuilder.type(link.type());
1420 linkBuilder.providerId(link.providerId());
1421
1422 return linkBuilder.build();
1423 }
1424
1425 /**
1426 * Returns the path betwwen two connect points.
1427 *
1428 * @param srcCp source connect point
1429 * @param dstCp destination connect point
1430 * @return the path
1431 */
1432 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001433
1434 // use SRLinkWeigher to avoid pair links, and also
1435 // avoid going from the spine to the leaf and to the
1436 // spine again, we need to have the leaf as CP1 here.
1437 LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
1438
1439 Set<Path> paths = srManager.topologyService.
1440 getPaths(srManager.topologyService.currentTopology(),
1441 srcCp.deviceId(), dstCp.deviceId(), srLw);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001442
1443 log.debug("Paths obtained from topology service {}", paths);
1444
1445 // We randomly pick a path.
1446 if (paths.isEmpty()) {
1447 return null;
1448 }
1449 int size = paths.size();
1450 int index = RandomUtils.nextInt(0, size);
1451
1452 List<Link> result = Iterables.get(paths, index).links();
1453 log.debug("Randomly picked a path {}", result);
1454
1455 return result;
1456 }
1457
1458 /**
1459 * Deletes a given policy using the parameter supplied.
1460 *
1461 * @param tunnelId the tunnel id
1462 * @param ingress the ingress point
1463 * @param ingressInner the ingress inner vlan id
1464 * @param ingressOuter the ingress outer vlan id
1465 * @param future to perform the async operation
1466 * @param direction the direction: forward or reverse
1467 */
1468 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1469 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1470
1471 String key = generateKey(tunnelId, direction);
1472 if (!l2InitiationNextObjStore.containsKey(key)) {
1473 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1474 if (future != null) {
1475 future.complete(null);
1476 }
1477 return;
1478 }
1479 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1480 int nextId = nextObjective.id();
1481 List<Objective> objectives = Lists.newArrayList();
1482 // We create the forwarding objective.
1483 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1484 ObjectiveContext context = new ObjectiveContext() {
1485 @Override
1486 public void onSuccess(Objective objective) {
1487 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1488 if (future != null) {
1489 future.complete(null);
1490 }
1491 }
1492
1493 @Override
1494 public void onError(Objective objective, ObjectiveError error) {
1495 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1496 if (future != null) {
1497 future.complete(error);
1498 }
1499 }
1500 };
1501 objectives.add(fwdBuilder.remove(context));
1502 // We create the filtering objective to define the
1503 // permit traffic in the switch
1504 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1505 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1506 .builder()
1507 .setTunnelId(tunnelId)
1508 .setVlanId(egressVlan);
1509 filtBuilder.withMeta(treatment.build());
1510 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1511 (objective, error) ->
1512 log.warn("Failed to revoke filterObj for policy {}",
1513 tunnelId, error));
1514 objectives.add(filtBuilder.remove(context));
1515
1516 for (Objective objective : objectives) {
1517 if (objective instanceof ForwardingObjective) {
1518 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1519 } else {
1520 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1521 }
1522 }
1523 }
1524
1525 /**
1526 * Deletes the pseudo wire initiation.
1527 *
1528 * @param l2TunnelId the tunnel id
1529 * @param ingress the ingress connect point
1530 * @param future to perform an async operation
1531 * @param direction the direction: reverse of forward
1532 */
1533 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1534 CompletableFuture<ObjectiveError> future, Direction direction) {
1535
1536 String key = generateKey(l2TunnelId, direction);
1537 if (!l2InitiationNextObjStore.containsKey(key)) {
1538 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1539 if (future != null) {
1540 future.complete(null);
1541 }
1542 return;
1543 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001544
1545 // un-comment in case you want to delete groups used by the pw
1546 // however, this will break the update of pseudowires cause the L2 interface group can
1547 // not be deleted (it is referenced by other groups)
1548 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001549 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001550 ObjectiveContext context = new ObjectiveContext() {
1551 @Override
1552 public void onSuccess(Objective objective) {
1553 log.debug("Previous {} next for {} removed", INITIATION, key);
1554 if (future != null) {
1555 future.complete(null);
1556 }
1557 }
1558
1559 @Override
1560 public void onError(Objective objective, ObjectiveError error) {
1561 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1562 if (future != null) {
1563 future.complete(error);
1564 }
1565 }
1566 };
1567 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1568 */
1569
1570 future.complete(null);
1571 l2InitiationNextObjStore.remove(key);
1572 }
1573
1574 /**
1575 * Deletes the pseudo wire termination.
1576 *
1577 * @param l2Tunnel the tunnel
1578 * @param egress the egress connect point
1579 * @param future the async task
1580 * @param direction the direction of the tunnel
1581 */
1582 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1583 ConnectPoint egress,
1584 CompletableFuture<ObjectiveError> future,
1585 Direction direction) {
1586
1587 String key = generateKey(l2Tunnel.tunnelId(), direction);
1588 if (!l2TerminationNextObjStore.containsKey(key)) {
1589 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1590 if (future != null) {
1591 future.complete(null);
1592 }
1593 return;
1594 }
1595 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1596 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1597 l2Tunnel.tunnelId(),
1598 egress.port(),
1599 nextObjective.id());
1600 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1601 log.debug("FwdObj for {} {}, " +
1602 "direction {} removed",
1603 TERMINATION,
1604 l2Tunnel.tunnelId(),
1605 direction),
1606 (objective, error) ->
1607 log.warn("Failed to remove fwdObj " +
1608 "for {} {}" +
1609 ", direction {}",
1610 TERMINATION,
1611 l2Tunnel.tunnelId(),
1612 error,
1613 direction));
1614 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1615
1616 // un-comment in case you want to delete groups used by the pw
1617 // however, this will break the update of pseudowires cause the L2 interface group can
1618 // not be deleted (it is referenced by other groups)
1619 /*
1620 context = new ObjectiveContext() {
1621 @Override
1622 public void onSuccess(Objective objective) {
1623 log.debug("Previous {} next for {} removed", TERMINATION, key);
1624 if (future != null) {
1625 future.complete(null);
1626 }
1627 }
1628
1629 @Override
1630 public void onError(Objective objective, ObjectiveError error) {
1631 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1632 if (future != null) {
1633 future.complete(error);
1634 }
1635 }
1636 };
1637 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1638 */
1639
1640 // delete the extra filtering objective for terminating
1641 // spine-spine pws
1642 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1643
1644 // determine the input port at the
1645 PortNumber inPort;
1646
1647 if (egress.deviceId().
1648 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1649 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1650 } else {
1651 inPort = l2Tunnel.pathUsed().get(0).src().port();
1652 }
1653
1654 MacAddress dstMac;
1655 try {
1656 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1657 } catch (Exception e) {
1658 log.info("Device not found in configuration, no programming of MAC address");
1659 dstMac = null;
1660 }
1661
1662 log.info("Removing filtering objective for pseudowire transport" +
1663 " with vlan = {}, port = {}, mac = {}",
1664 l2Tunnel.transportVlan(),
1665 inPort,
1666 dstMac);
1667 FilteringObjective.Builder filteringObjectiveBuilder =
1668 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001669 context = new DefaultObjectiveContext((objective) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001670 log.debug("Special filtObj for " + "for {} removed",
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001671 l2Tunnel.tunnelId()), (objective, error) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001672 log.warn("Failed to populate " + "special filtObj " +
1673 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1674 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1675 filteringObjectiveBuilder.withMeta(treatment.build());
1676 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1677 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1678 l2Tunnel.tunnelId(),
1679 inPort);
1680 }
1681
1682 l2TerminationNextObjStore.remove(key);
1683 future.complete(null);
1684 }
1685
1686 /**
1687 * Utilities to generate pw key.
1688 *
1689 * @param tunnelId the tunnel id
1690 * @param direction the direction of the pw
1691 * @return the key of the store
1692 */
1693 private String generateKey(long tunnelId, Direction direction) {
1694 return String.format("%s-%s", tunnelId, direction);
1695 }
1696
1697}