blob: 9c9fb8f5efc761c69831217493d670a5b99a8005 [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;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070055import org.onosproject.store.service.DistributedLock;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080056import org.onosproject.store.service.Serializer;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070057import org.onosproject.store.service.StorageException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080058import org.onosproject.store.service.Versioned;
59import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import java.util.ArrayList;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070063import java.util.HashSet;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080064import java.util.List;
65import java.util.Set;
66import java.util.concurrent.CompletableFuture;
67import java.util.stream.Collectors;
68
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080069import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
70import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
71import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
72import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
73import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
74import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -070075import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080076
77/**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070078 * Handler for pseudowire management.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080079 */
80public class DefaultL2TunnelHandler implements L2TunnelHandler {
81
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070082 private static final String LOCK_NAME = "l2-tunnel-handler-lock";
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080083 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
84
85 private final SegmentRoutingManager srManager;
86 /**
87 * To store the next objectives related to the initiation.
88 */
89 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
90 /**
91 * To store the next objectives related to the termination.
92 */
93 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
94
95 /**
96 * To store policies.
97 */
98 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
99
100 /**
101 * To store tunnels.
102 */
103 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
104
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700105 /**
106 * To store pending tunnels that need to be installed.
107 */
108 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
109
110 /**
111 * To store pending policies that need to be installed.
112 */
113 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
114
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800115 private final KryoNamespace.Builder l2TunnelKryo;
116
117 /**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700118 * Lock used when creating or removing pseudowires.
119 */
120 private final DistributedLock pwLock;
121
122 private static final long LOCK_TIMEOUT = 2000;
123
124 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800125 * 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 Pantelopoulos5bf13662018-04-10 19:34:47 -0700180 pwLock = srManager.storageService.lockBuilder()
181 .withName(LOCK_NAME)
182 .build()
183 .asLock(LOCK_TIMEOUT);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800184 }
185
186 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800187 * Used by manager only in initialization.
188 */
189 @Override
190 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800191 // Since we have no pseudowires in netcfg there
192 // is nothing to do in initialization.
193 // I leave it here because potentially we might need to
194 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800195 }
196
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700197 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700198 public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700199 // get pending tunnels/policies OR installed tunnels/policies
200 List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
201 List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700202 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700203 .map(l2Tunnel -> {
204 L2TunnelPolicy policy = null;
205 for (L2TunnelPolicy l2Policy : policies) {
206 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
207 policy = l2Policy;
208 break;
209 }
210 }
211
212 return new DefaultL2TunnelDescription(l2Tunnel, policy);
213 })
214 .collect(Collectors.toSet());
215 }
216
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800217 @Override
218 public List<L2TunnelPolicy> getL2Policies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800219 return new ArrayList<>(l2PolicyStore
220 .values()
221 .stream()
222 .map(Versioned::value)
223 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800224 }
225
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800226 @Override
227 public List<L2Tunnel> getL2Tunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800228 return new ArrayList<>(l2TunnelStore
229 .values()
230 .stream()
231 .map(Versioned::value)
232 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800233 }
234
235 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700236 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700237 return new ArrayList<>(pendingL2PolicyStore
238 .values()
239 .stream()
240 .map(Versioned::value)
241 .collect(Collectors.toList()));
242 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800243
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700244 @Override
245 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700246 return new ArrayList<>(pendingL2TunnelStore
247 .values()
248 .stream()
249 .map(Versioned::value)
250 .collect(Collectors.toList()));
251 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800252
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700253 @Override
254 public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
255
256 // get both added and pending pseudowires
257 List<L2TunnelDescription> newPseudowires = new ArrayList<>();
258 newPseudowires.addAll(getL2Descriptions(false));
259 newPseudowires.addAll(getL2Descriptions(true));
260 // add the new one
261 newPseudowires.add(pwToAdd);
262
263 return configurationValidity(newPseudowires);
264 }
265
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700266 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800267 * Returns the new vlan id for an ingress point of a
268 * pseudowire. For double tagged, it is the outer,
269 * For single tagged it is the single tag, and for
270 * inner it is None.
271 *
272 * @param ingressOuter vlanid of ingress outer
273 * @param ingressInner vlanid of ingress inner
274 * @param egressOuter vlanid of egress outer
275 * @param egressInner vlanid of egress inner
276 * @return returns the vlan id which will be installed at vlan table 1.
277 */
278 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
279 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800280 // validity of vlan combinations was checked at verifyPseudowire
281 if (!(ingressOuter.equals(VlanId.NONE))) {
282 return egressOuter;
283 } else if (!(ingressInner.equals(VlanId.NONE))) {
284 return egressInner;
285 } else {
286 return VlanId.vlanId("None");
287 }
288 }
289
290 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700291 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700292 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700293 * @param path The path to traverse.
294 * @return The devices on the path with the order they
295 * are traversed.
296 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700297 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700298
299 // iterate over links and get all devices in the order
300 // we find them
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700301 List<DeviceId> deviceList = new ArrayList<>();
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700302 for (Link link : path) {
303 if (!deviceList.contains(link.src().deviceId())) {
304 deviceList.add(link.src().deviceId());
305 }
306 if (!deviceList.contains(link.dst().deviceId())) {
307 deviceList.add(link.dst().deviceId());
308 }
309 }
310
311 return deviceList;
312 }
313
314 /**
315 * Returns true if path is valid according to the current logic.
316 * For example : leaf to spine pseudowires can be either leaf-spine or
317 * leaf-spine-spine. However, in the configuration we might specify spine device
318 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
319 * two cases and need to provision for them.
320 *
321 * If we have a leaf to leaf pseudowire then all the intermediate devices must
322 * be spines. However, in case of paired switches we can have two leafs interconnected
323 * with each other directly.
324 *
325 * @param path the chosen path
326 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700327 * @return True if path size is valid, false otherwise.
328 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700329 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
330
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700331 log.debug("Checking path validity for pseudowire.");
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700332 List<DeviceId> devices = getDevicesOnPath(path);
333 if (devices.size() < 2) {
334 log.error("Path size for pseudowire should be greater than 1!");
335 return false;
336 }
337
338 try {
339 if (leafSpinePw) {
340 // can either be leaf-spine-spine or leaf-spine
341 // first device is leaf, all other must be spines
342 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
343 // if first device is a leaf then all other must be spines
344 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
345 devices.remove(0);
346 for (DeviceId devId : devices) {
347 log.debug("Device {} should be a spine!", devId);
348 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
349 return false;
350 }
351 }
352 } else {
353 // if first device is spine, last device must be a leaf
354 // all other devices must be spines
355 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
356 return false;
357 }
358 devices.remove(devices.size() - 1);
359 for (DeviceId devId : devices) {
360 log.debug("Device {} should be a spine!", devId);
361 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
362 return false;
363 }
364 }
365 }
366 } else {
367 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
368 // or leaf-spine-spine-leaf
369 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
370 // check first device, needs to be a leaf
371 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
372 return false;
373 }
374 // check last device, needs to be a leaf
375 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
376 return false;
377 }
378 // remove these devices, rest must all be spines
379 devices.remove(0);
380 devices.remove(devices.size() - 1);
381 for (DeviceId devId : devices) {
382 log.debug("Device {} should be a spine!", devId);
383 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
384 return false;
385 }
386 }
387
388 }
389 } catch (DeviceConfigNotFoundException e) {
390 log.error("Device not found in configuration : {}", e);
391 return false;
392 }
393
394 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700395 }
396
397 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800398 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800399 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700400 * @param pw The pseudowire to deploy
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800401 * @return result of pseudowire deployment
402 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700403 public Result deployPseudowire(L2TunnelDescription pw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800404
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700405 try {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700406 // take the lock
407 pwLock.lock();
408 Result result;
409 long l2TunnelId;
410 log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
411 pw.l2Tunnel().tunnelId());
412 l2TunnelId = pw.l2Tunnel().tunnelId();
413 // The tunnel id cannot be 0.
414 if (l2TunnelId == 0) {
415 log.warn("Tunnel id id must be > 0 in {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700416 return Result.WRONG_PARAMETERS
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700417 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700418 }
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700419
420 result = verifyGlobalValidity(pw);
421 if (result != SUCCESS) {
422 log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
423 return result;
424 }
425
426 // leafSpinePw determines if this is a leaf-leaf
427 // or leaf-spine pseudowire
428 boolean leafSpinePw;
429 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
430 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
431 try {
432 // differentiate between leaf-leaf pseudowires and leaf-spine
433 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
434 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
435 log.error("Can not deploy pseudowire {} from spine to spine!", l2TunnelId);
436 return Result.WRONG_PARAMETERS
437 .appendError("Can not deploy pseudowire from spine to spine!");
438 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
439 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
440 leafSpinePw = false;
441 } else {
442 leafSpinePw = true;
443 }
444 } catch (DeviceConfigNotFoundException e) {
445 log.error("Device for pseudowire {} connection points does not exist in the configuration", l2TunnelId);
446 return Result.INTERNAL_ERROR
447 .appendError("Device for pseudowire connection points does not exist in the configuration");
448 }
449
450 // reverse the policy in order for leaf switch to be at CP1
451 // this will help us for re-using SRLinkWeigher for computing valid paths
452 L2TunnelPolicy reversedPolicy = reverseL2TunnelPolicy(pw.l2TunnelPolicy());
453 if (reversedPolicy == null) {
454 log.error("Error in reversing policy, device configuration was not found for pseudowire {}.",
455 l2TunnelId);
456 return INTERNAL_ERROR
457 .appendError("Device configuration not found when reversing the policy.");
458 }
459 pw.setL2TunnelPolicy(reversedPolicy);
460
461 // get path here, need to use the same for fwd and rev direction
462 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
463 pw.l2TunnelPolicy().cP2());
464 if (path == null) {
465 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
466 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
467 }
468
469 Link fwdNextHop;
470 Link revNextHop;
471 if (!isValidPath(path, leafSpinePw)) {
472 log.error("Deploying process : Path for pseudowire {} is not valid", l2TunnelId);
473 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
474 }
475 // oneHope flag is used to determine if we need to
476 // install transit mpls rules
477 boolean oneHop = true;
478 if (path.size() > 1) {
479 oneHop = false;
480 }
481
482 fwdNextHop = path.get(0);
483 revNextHop = reverseLink(path.get(path.size() - 1));
484
485 pw.l2Tunnel().setPath(path);
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700486 pw.l2Tunnel().setTransportVlan(srManager.PSEUDOWIRE_VLAN);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700487
488 // next hops for next objectives
489 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
490
491 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
492 pw.l2TunnelPolicy().cP1InnerTag(),
493 pw.l2TunnelPolicy().cP2OuterTag(),
494 pw.l2TunnelPolicy().cP2InnerTag());
495 result = deployPseudoWireInit(pw.l2Tunnel(),
496 pw.l2TunnelPolicy().cP1(),
497 pw.l2TunnelPolicy().cP2(),
498 FWD,
499 fwdNextHop,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700500 oneHop,
501 egressVlan);
502 if (result != SUCCESS) {
503 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP1", l2TunnelId);
504 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
505 }
506
507 result = deployPolicy(l2TunnelId,
508 pw.l2TunnelPolicy().cP1(),
509 pw.l2TunnelPolicy().cP1InnerTag(),
510 pw.l2TunnelPolicy().cP1OuterTag(),
511 egressVlan,
512 result.getNextId());
513 if (result != SUCCESS) {
514 log.error("Deploying process : Error in deploying pseudowire {} policy for CP1", l2TunnelId);
515 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
516 }
517
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700518 result = deployPseudoWireTerm(pw.l2Tunnel(),
519 pw.l2TunnelPolicy().cP2(),
520 egressVlan,
521 FWD,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700522 oneHop);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700523
524 if (result != SUCCESS) {
525 log.error("Deploying process : Error in deploying pseudowire {} termination for CP1", l2TunnelId);
526 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
527 }
528
529 // We establish the reverse tunnel.
530 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
531 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
532 pw.l2TunnelPolicy().cP2InnerTag(),
533 pw.l2TunnelPolicy().cP1OuterTag(),
534 pw.l2TunnelPolicy().cP1InnerTag());
535
536 result = deployPseudoWireInit(pw.l2Tunnel(),
537 pw.l2TunnelPolicy().cP2(),
538 pw.l2TunnelPolicy().cP1(),
539 REV,
540 revNextHop,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700541 oneHop,
542 egressVlan);
543 if (result != SUCCESS) {
544 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP2", l2TunnelId);
545 return Result.INTERNAL_ERROR
546 .appendError("Error in deploying pseudowire initiation for CP2");
547 }
548
549 result = deployPolicy(l2TunnelId,
550 pw.l2TunnelPolicy().cP2(),
551 pw.l2TunnelPolicy().cP2InnerTag(),
552 pw.l2TunnelPolicy().cP2OuterTag(),
553 egressVlan,
554 result.getNextId());
555 if (result != SUCCESS) {
556 log.error("Deploying process : Error in deploying policy {} for CP2", l2TunnelId);
557 return Result.INTERNAL_ERROR
558 .appendError("Deploying process : Error in deploying policy for CP2");
559 }
560
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700561 result = deployPseudoWireTerm(pw.l2Tunnel(),
562 pw.l2TunnelPolicy().cP1(),
563 egressVlan,
564 REV,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700565 oneHop);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700566
567 if (result != SUCCESS) {
568 log.error("Deploying process : Error in deploying pseudowire {} termination for CP2", l2TunnelId);
569 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
570 }
571
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700572 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
573
574 // Populate stores as the final step of the process
575 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
576 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
577
578 return Result.SUCCESS;
579 } catch (StorageException.Timeout e) {
580 log.error("Can not acquire distributed lock for pseudowire {}!", pw.l2Tunnel().tunnelId());
581 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
582 } finally {
583 // release the lock
584 pwLock.unlock();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700585 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800586 }
587
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700588 @Override
589 public Result checkIfPwExists(long tunnelId, boolean pending) {
590
591 List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
592 .stream()
593 .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
594 .collect(Collectors.toList());
595
596 if (pseudowires.size() == 0) {
597 String store = ((pending) ? "pending" : "installed");
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700598 log.debug("Pseudowire {} does not exist in {} store", tunnelId, store);
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700599 return Result.WRONG_PARAMETERS.
600 appendError("Pseudowire " + tunnelId + " does not exist in " + store);
601 } else {
602 return SUCCESS;
603 }
604 }
605
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800606 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700607 * Tears down connection points of pseudowires. We can either tear down both connection points,
608 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800609 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700610 * @param l2TunnelId The tunnel id for this pseudowire.
611 * @param tearDownFirst Boolean, true if we want to tear down cp1
612 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700613 * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
614 * in the network, else remove flows/groups in the devices also.
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700615 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700616 * a descriptive error otherwise.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800617 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700618 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
619 boolean tearDownSecond, boolean pending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800620
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700621 Result res;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800622 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
623 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800624 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
625 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
626
627 if (l2TunnelId == 0) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700628 log.error("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700629 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800630 }
631
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700632 res = checkIfPwExists(l2TunnelId, pending);
633 if (res != Result.SUCCESS) {
634 return res;
635 }
636
637 // remove and get the tunnel and the policy from the appropriate store
638 // if null, return error.
639 Versioned<L2Tunnel> l2TunnelVersioned = pending ?
640 pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
641 l2TunnelStore.remove(Long.toString(l2TunnelId));
642 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
643 pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
644 l2PolicyStore.remove(Long.toString(l2TunnelId));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800645 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
646 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700647 return Result.INTERNAL_ERROR
648 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800649 }
650
651 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700652 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800653
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700654 if (pending) {
655 // no need to remove flows / groups for a pseudowire
656 // in pending state
657 return Result.SUCCESS;
658 }
659
660 // remove flows/groups involving with this pseudowire
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700661 if (tearDownFirst) {
662 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800663
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700664 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
665 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
666 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
667 pwToRemove.l2TunnelPolicy().cP2InnerTag());
668 deletePolicy(l2TunnelId,
669 pwToRemove.l2TunnelPolicy().cP1(),
670 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
671 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
672 egressVlan,
673 fwdInitNextFuture,
674 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800675
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700676 fwdInitNextFuture.thenAcceptAsync(status -> {
677 if (status == null) {
678 // Finally we will tear down the pseudo wire.
679 tearDownPseudoWireInit(l2TunnelId,
680 pwToRemove.l2TunnelPolicy().cP1(),
681 fwdTermNextFuture,
682 FWD);
683 }
684 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800685
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700686 fwdTermNextFuture.thenAcceptAsync(status -> {
687 if (status == null) {
688 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
689 pwToRemove.l2TunnelPolicy().cP2(),
690 null,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700691 FWD);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700692 }
693 });
694 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800695
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700696 if (tearDownSecond) {
697 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800698
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700699 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
700 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
701 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
702 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800703
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700704 // We do the same operations on the reverse side.
705 deletePolicy(l2TunnelId,
706 pwToRemove.l2TunnelPolicy().cP2(),
707 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
708 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
709 egressVlan,
710 revInitNextFuture,
711 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800712
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700713 revInitNextFuture.thenAcceptAsync(status -> {
714 if (status == null) {
715 tearDownPseudoWireInit(l2TunnelId,
716 pwToRemove.l2TunnelPolicy().cP2(),
717 revTermNextFuture,
718 REV);
719 }
720 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800721
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700722 revTermNextFuture.thenAcceptAsync(status -> {
723 if (status == null) {
724 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
725 pwToRemove.l2TunnelPolicy().cP1(),
726 null,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700727 REV);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700728 }
729 });
730 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800731
732 return Result.SUCCESS;
733 }
734
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700735 /**
736 * Helper function for removing a single pseudowire.
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700737 *
738 * Tries to remove pseudowire from any store it might reside (pending or installed).
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700739 *
740 * @param l2TunnelId the id of the pseudowire to tear down
741 * @return Returns SUCCESS if no error is obeserved or an appropriate
742 * error on a failure
743 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700744 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700745
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700746 try {
747 // take the lock
748 pwLock.lock();
749
750 if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
751 return tearDownConnectionPoints(l2TunnelId, true, true, true);
752 } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
753 return tearDownConnectionPoints(l2TunnelId, true, true, false);
754 } else {
755 return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
756 + l2TunnelId
757 + " did not reside in any store!");
758 }
759 } catch (StorageException.Timeout e) {
760 log.error("Can not acquire distributed lock for pseudowire {}!", l2TunnelId);
761 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
762 } finally {
763 // release the lock
764 pwLock.unlock();
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700765 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700766 }
767
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700768 @Override
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700769 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800770 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
771
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800772 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800773
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700774 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800775 log.info("Removing pseudowire {}", tunnelId);
776
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700777 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700778 if (result != Result.SUCCESS) {
779 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800780 }
781 }
782 }
783
784 /**
785 * Handles the policy establishment which consists in
786 * create the filtering and forwarding objectives related
787 * to the initiation and termination.
788 *
789 * @param tunnelId the tunnel id
790 * @param ingress the ingress point
791 * @param ingressInner the ingress inner tag
792 * @param ingressOuter the ingress outer tag
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800793 * @param egressVlan Vlan-id to set, depends on ingress vlan
794 * combinations. For example, if pw is double tagged
795 * then this is the value of the outer vlan, if single
796 * tagged then it is the new value of the single tag.
797 * Should be None for untagged traffic.
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700798 * @param nextId the next objective id
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800799 * @return the result of the operation
800 */
801 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
802 VlanId ingressOuter, VlanId egressVlan, int nextId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700803 log.debug("Starting deploying policy for pseudowire {}.", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800804
805 List<Objective> objectives = Lists.newArrayList();
806 // We create the forwarding objective for supporting
807 // the l2 tunnel.
808 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
809 // We create and add objective context.
810 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
811 log.debug("FwdObj for tunnel {} populated", tunnelId),
812 (objective, error) ->
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700813 log.warn("Failed to populate fwdObj " +
814 "for tunnel {} : {}",
815 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800816 objectives.add(fwdBuilder.add(context));
817
818 // We create the filtering objective to define the
819 // permit traffic in the switch
820 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
821
822 // We add the metadata.
823 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
824 .builder()
825 .setTunnelId(tunnelId)
826 .setVlanId(egressVlan);
827 filtBuilder.withMeta(treatment.build());
828
829 // We create and add objective context.
830 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
831 (objective, error) -> log.warn("Failed to populate filterObj for " +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700832 "tunnel {} : {}",
833 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800834 objectives.add(filtBuilder.add(context));
835
836 for (Objective objective : objectives) {
837 if (objective instanceof ForwardingObjective) {
838 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
839 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
840 } else {
841 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
842 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
843 }
844 }
845 return SUCCESS;
846 }
847
848 /**
849 * Handles the tunnel establishment which consists in
850 * create the next objectives related to the initiation.
851 *
852 * @param l2Tunnel the tunnel to deploy
853 * @param ingress the ingress connect point
854 * @param egress the egress connect point
855 * @param direction the direction of the pw
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700856 * @param nextHop next hop of the initiation point
857 * @param oneHop if this pseudowire has only one link
858 * @param termVlanId the termination vlan id
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800859 * @return the result of the operation
860 */
861 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800862 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700863 Link nextHop, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700864 log.debug("Started deploying init next objectives for pseudowire {} for tunnel {} -> {}.",
865 l2Tunnel.tunnelId(), ingress, egress);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800866 if (nextHop == null) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700867 log.warn("No path between ingress and egress connection points for tunnel {}", l2Tunnel.tunnelId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800868 return WRONG_PARAMETERS;
869 }
870
871 // We create the next objective without the metadata
872 // context and id. We check if it already exists in the
873 // store. If not we store as it is in the store.
874 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
875 nextHop.src(),
876 nextHop.dst(),
877 l2Tunnel,
878 egress.deviceId(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700879 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800880 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800881
882 if (nextObjectiveBuilder == null) {
883 return INTERNAL_ERROR;
884 }
885 // We set the metadata. We will use this metadata
886 // to inform the driver we are doing a l2 tunnel.
887 TrafficSelector metadata = DefaultTrafficSelector
888 .builder()
889 .matchTunnelId(l2Tunnel.tunnelId())
890 .build();
891 nextObjectiveBuilder.withMeta(metadata);
892 int nextId = srManager.flowObjectiveService.allocateNextId();
893 if (nextId < 0) {
894 log.warn("Not able to allocate a next id for initiation");
895 return INTERNAL_ERROR;
896 }
897 nextObjectiveBuilder.withId(nextId);
898 String key = generateKey(l2Tunnel.tunnelId(), direction);
899 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
900 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
901 log.debug("Initiation l2 tunnel rule " +
902 "for {} populated",
903 l2Tunnel.tunnelId()),
904 (objective, error) ->
905 log.warn("Failed to populate Initiation " +
906 "l2 tunnel rule for {}: {}",
907 l2Tunnel.tunnelId(), error));
908 NextObjective nextObjective = nextObjectiveBuilder.add(context);
909 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
910 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
911 l2Tunnel.tunnelId(), nextObjective.id());
912 Result result = SUCCESS;
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -0700913 result.setNextId(nextObjective.id());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800914 return result;
915 }
916
917 /**
918 * Handles the tunnel termination, which consists in the creation
919 * of a forwarding objective and a next objective.
920 *
921 * @param l2Tunnel the tunnel to terminate
922 * @param egress the egress point
923 * @param egressVlan the expected vlan at egress
924 * @param direction the direction
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800925 * @return the result of the operation
926 */
927 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700928 VlanId egressVlan, Direction direction,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -0700929 boolean oneHop) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700930 log.debug("Started deploying termination objectives for pseudowire {} , direction {}.",
931 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800932
933 // We create the group relative to the termination.
934 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
935 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700936 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800937 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800938 if (nextObjectiveBuilder == null) {
939 return INTERNAL_ERROR;
940 }
941 TrafficSelector metadata = DefaultTrafficSelector
942 .builder()
943 .matchVlanId(egressVlan)
944 .build();
945 nextObjectiveBuilder.withMeta(metadata);
946 int nextId = srManager.flowObjectiveService.allocateNextId();
947 if (nextId < 0) {
948 log.warn("Not able to allocate a next id for initiation");
949 return INTERNAL_ERROR;
950 }
951 nextObjectiveBuilder.withId(nextId);
952 String key = generateKey(l2Tunnel.tunnelId(), direction);
953 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
954 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
955 "for {} populated",
956 l2Tunnel.tunnelId()),
957 (objective, error) -> log.warn("Failed to populate " +
958 "termination l2 tunnel " +
959 "rule for {}: {}",
960 l2Tunnel.tunnelId(),
961 error));
962 NextObjective nextObjective = nextObjectiveBuilder.add(context);
963 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
964 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
965 l2Tunnel.tunnelId(), nextObjective.id());
966
967 // We create the flow relative to the termination.
968 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
969 egress.port(), nextObjective.id());
970 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
971 l2Tunnel.tunnelId()),
972 (objective, error) -> log.warn("Failed to populate fwdrObj" +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700973 " for tunnel termination {} : {}",
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800974 l2Tunnel.tunnelId(), error));
975 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
976 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
977 nextId, l2Tunnel.tunnelId());
978
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800979 return SUCCESS;
980 }
981
982
983 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800984 * Creates the filtering objective according to a given policy.
985 *
986 * @param inPort the in port
987 * @param innerTag the inner vlan tag
988 * @param outerTag the outer vlan tag
989 * @return the filtering objective
990 */
991 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
992
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700993 log.debug("Creating connection point filtering objective for vlans {} / {}", outerTag, innerTag);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800994 return DefaultFilteringObjective
995 .builder()
996 .withKey(Criteria.matchInPort(inPort))
997 .addCondition(Criteria.matchInnerVlanId(innerTag))
998 .addCondition(Criteria.matchVlanId(outerTag))
999 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1000 .permit()
1001 .fromApp(srManager.appId());
1002 }
1003
1004 /**
1005 * Creates the forwarding objective for the termination.
1006 *
1007 * @param pwLabel the pseudo wire label
1008 * @param tunnelId the tunnel id
1009 * @param egressPort the egress port
1010 * @param nextId the next step
1011 * @return the forwarding objective to support the termination
1012 */
1013 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1014 PortNumber egressPort, int nextId) {
1015
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001016 log.debug("Creating forwarding objective for termination for tunnel {} : pwLabel {}, egressPort {}, nextId {}",
1017 tunnelId, pwLabel, egressPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001018 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1019 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1020 // The flow has to match on the pw label and bos
1021 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1022 trafficSelector.matchMplsLabel(pwLabel);
1023 trafficSelector.matchMplsBos(true);
1024 // The flow has to decrement ttl, restore ttl in
1025 // pop mpls, set tunnel id and port.
1026 trafficTreatment.decMplsTtl();
1027 trafficTreatment.copyTtlIn();
1028 trafficTreatment.popMpls();
1029 trafficTreatment.setTunnelId(tunnelId);
1030 trafficTreatment.setOutput(egressPort);
1031
1032 return DefaultForwardingObjective
1033 .builder()
1034 .fromApp(srManager.appId())
1035 .makePermanent()
1036 .nextStep(nextId)
1037 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1038 .withSelector(trafficSelector.build())
1039 .withTreatment(trafficTreatment.build())
1040 .withFlag(VERSATILE);
1041 }
1042
1043 /**
1044 * Creates the forwarding objective for the initiation.
1045 *
1046 * @param tunnelId the tunnel id
1047 * @param inPort the input port
1048 * @param nextId the next step
1049 * @return the forwarding objective to support the initiation.
1050 */
1051 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1052
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001053 log.debug("Creating forwarding objective for tunnel {} : Port {} , nextId {}", tunnelId, inPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001054 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1055
1056 // The flow has to match on the mpls logical
1057 // port and the tunnel id.
1058 trafficSelector.matchTunnelId(tunnelId);
1059 trafficSelector.matchInPort(inPort);
1060
1061 return DefaultForwardingObjective
1062 .builder()
1063 .fromApp(srManager.appId())
1064 .makePermanent()
1065 .nextStep(nextId)
1066 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1067 .withSelector(trafficSelector.build())
1068 .withFlag(VERSATILE);
1069
1070 }
1071
1072 /**
1073 * Creates the next objective according to a given
1074 * pipeline. We don't set the next id and we don't
1075 * create the final meta to check if we are re-using
1076 * the same next objective for different tunnels.
1077 *
1078 * @param pipeline the pipeline to support
1079 * @param srcCp the source port
1080 * @param dstCp the destination port
1081 * @param l2Tunnel the tunnel to support
1082 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001083 * @param oneHop if the pw only has one hop, push only pw label
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001084 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001085 * @return the next objective to support the pipeline
1086 */
1087 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1088 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001089 DeviceId egressId, boolean oneHop,
1090 VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001091 log.debug("Creating {} next objective for pseudowire {}.",
1092 pipeline == TERMINATION ? "termination" : "inititation");
1093
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001094 NextObjective.Builder nextObjBuilder;
1095 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1096 if (pipeline == INITIATION) {
1097 nextObjBuilder = DefaultNextObjective
1098 .builder()
1099 .withType(NextObjective.Type.SIMPLE)
1100 .fromApp(srManager.appId());
1101 // The pw label is the bottom of stack. It has to
1102 // be different -1.
1103 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001104 log.error("Pw label not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001105 return null;
1106 }
1107 treatmentBuilder.pushMpls();
1108 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1109 treatmentBuilder.setMplsBos(true);
1110 treatmentBuilder.copyTtlOut();
1111
1112 // If the inter-co label is present we have to set the label.
1113 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1114 treatmentBuilder.pushMpls();
1115 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1116 treatmentBuilder.setMplsBos(false);
1117 treatmentBuilder.copyTtlOut();
1118 }
1119
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001120 // if not oneHop install transit mpls labels also
1121 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001122 // We retrieve the sr label from the config
1123 // specific for pseudowire traffic
1124 // using the egress leaf device id.
1125 MplsLabel srLabel;
1126 try {
1127 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1128
1129 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001130 log.error("Sr label for pw traffic not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001131 return null;
1132 }
1133
1134 treatmentBuilder.pushMpls();
1135 treatmentBuilder.setMpls(srLabel);
1136 treatmentBuilder.setMplsBos(false);
1137 treatmentBuilder.copyTtlOut();
1138 }
1139
1140 // We have to rewrite the src and dst mac address.
1141 MacAddress ingressMac;
1142 try {
1143 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1144 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001145 log.error("Was not able to find the ingress mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001146 return null;
1147 }
1148 treatmentBuilder.setEthSrc(ingressMac);
1149 MacAddress neighborMac;
1150 try {
1151 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1152 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001153 log.error("Was not able to find the neighbor mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001154 return null;
1155 }
1156 treatmentBuilder.setEthDst(neighborMac);
1157
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001158 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001159 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001160 } else {
1161 // We create the next objective which
1162 // will be a simple l2 group.
1163 nextObjBuilder = DefaultNextObjective
1164 .builder()
1165 .withType(NextObjective.Type.SIMPLE)
1166 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001167
1168 // for termination point we use the outer vlan of the
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001169 // encapsulated packet for the vlan
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001170 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001171 }
1172
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001173 treatmentBuilder.setOutput(srcCp.port());
1174 nextObjBuilder.addTreatment(treatmentBuilder.build());
1175 return nextObjBuilder;
1176 }
1177
1178 /**
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001179 * Reverse an l2 tunnel policy in order to have as CP1 the leaf switch,
1180 * in case one of the switches is a spine.
1181 *
1182 * This makes possible the usage of SRLinkWeigher for computing valid paths,
1183 * which cuts leaf-spine links from the path computation with a src different
1184 * than the source leaf.
1185 *
1186 * @param policy The policy to reverse, if needed
1187 * @return a l2TunnelPolicy containing the leaf at CP1, suitable for usage with
1188 * current SRLinkWeigher
1189 */
1190 private L2TunnelPolicy reverseL2TunnelPolicy(L2TunnelPolicy policy) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001191
1192 log.debug("Reversing policy for pseudowire.");
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001193 try {
1194 // if cp1 is a leaf, just return
1195 if (srManager.deviceConfiguration().isEdgeDevice(policy.cP1().deviceId())) {
1196 return policy;
1197 } else {
1198 // return a policy with reversed cp1 and cp2, and also with reversed tags
1199 return new DefaultL2TunnelPolicy(policy.tunnelId(),
1200 policy.cP2(), policy.cP2InnerTag(), policy.cP2OuterTag(),
1201 policy.cP1(), policy.cP1InnerTag(), policy.cP1OuterTag());
1202
1203 }
1204 } catch (DeviceConfigNotFoundException e) {
1205 // should never come here, since it has been checked before
1206 log.error("Configuration for device {}, does not exist!");
1207 return null;
1208 }
1209 }
1210
1211 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001212 * Reverses a link.
1213 *
1214 * @param link link to be reversed
1215 * @return the reversed link
1216 */
1217 private Link reverseLink(Link link) {
1218
1219 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1220
1221 linkBuilder.src(link.dst());
1222 linkBuilder.dst(link.src());
1223 linkBuilder.type(link.type());
1224 linkBuilder.providerId(link.providerId());
1225
1226 return linkBuilder.build();
1227 }
1228
1229 /**
1230 * Returns the path betwwen two connect points.
1231 *
1232 * @param srcCp source connect point
1233 * @param dstCp destination connect point
1234 * @return the path
1235 */
1236 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001237
1238 // use SRLinkWeigher to avoid pair links, and also
1239 // avoid going from the spine to the leaf and to the
1240 // spine again, we need to have the leaf as CP1 here.
1241 LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
1242
1243 Set<Path> paths = srManager.topologyService.
1244 getPaths(srManager.topologyService.currentTopology(),
1245 srcCp.deviceId(), dstCp.deviceId(), srLw);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001246
1247 log.debug("Paths obtained from topology service {}", paths);
1248
1249 // We randomly pick a path.
1250 if (paths.isEmpty()) {
1251 return null;
1252 }
1253 int size = paths.size();
1254 int index = RandomUtils.nextInt(0, size);
1255
1256 List<Link> result = Iterables.get(paths, index).links();
1257 log.debug("Randomly picked a path {}", result);
1258
1259 return result;
1260 }
1261
1262 /**
1263 * Deletes a given policy using the parameter supplied.
1264 *
1265 * @param tunnelId the tunnel id
1266 * @param ingress the ingress point
1267 * @param ingressInner the ingress inner vlan id
1268 * @param ingressOuter the ingress outer vlan id
1269 * @param future to perform the async operation
1270 * @param direction the direction: forward or reverse
1271 */
1272 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1273 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1274
1275 String key = generateKey(tunnelId, direction);
1276 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001277 log.error("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001278 if (future != null) {
1279 future.complete(null);
1280 }
1281 return;
1282 }
1283 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1284 int nextId = nextObjective.id();
1285 List<Objective> objectives = Lists.newArrayList();
1286 // We create the forwarding objective.
1287 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1288 ObjectiveContext context = new ObjectiveContext() {
1289 @Override
1290 public void onSuccess(Objective objective) {
1291 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1292 if (future != null) {
1293 future.complete(null);
1294 }
1295 }
1296
1297 @Override
1298 public void onError(Objective objective, ObjectiveError error) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001299 log.error("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001300 if (future != null) {
1301 future.complete(error);
1302 }
1303 }
1304 };
1305 objectives.add(fwdBuilder.remove(context));
1306 // We create the filtering objective to define the
1307 // permit traffic in the switch
1308 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1309 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1310 .builder()
1311 .setTunnelId(tunnelId)
1312 .setVlanId(egressVlan);
1313 filtBuilder.withMeta(treatment.build());
1314 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1315 (objective, error) ->
1316 log.warn("Failed to revoke filterObj for policy {}",
1317 tunnelId, error));
1318 objectives.add(filtBuilder.remove(context));
1319
1320 for (Objective objective : objectives) {
1321 if (objective instanceof ForwardingObjective) {
1322 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1323 } else {
1324 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1325 }
1326 }
1327 }
1328
1329 /**
1330 * Deletes the pseudo wire initiation.
1331 *
1332 * @param l2TunnelId the tunnel id
1333 * @param ingress the ingress connect point
1334 * @param future to perform an async operation
1335 * @param direction the direction: reverse of forward
1336 */
1337 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1338 CompletableFuture<ObjectiveError> future, Direction direction) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001339 log.debug("Starting tearing dowing initation of pseudowire {} for direction {}.",
1340 l2TunnelId, direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001341 String key = generateKey(l2TunnelId, direction);
1342 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001343 log.error("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001344 if (future != null) {
1345 future.complete(null);
1346 }
1347 return;
1348 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001349
1350 // un-comment in case you want to delete groups used by the pw
1351 // however, this will break the update of pseudowires cause the L2 interface group can
1352 // not be deleted (it is referenced by other groups)
1353 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001354 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001355 ObjectiveContext context = new ObjectiveContext() {
1356 @Override
1357 public void onSuccess(Objective objective) {
1358 log.debug("Previous {} next for {} removed", INITIATION, key);
1359 if (future != null) {
1360 future.complete(null);
1361 }
1362 }
1363
1364 @Override
1365 public void onError(Objective objective, ObjectiveError error) {
1366 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1367 if (future != null) {
1368 future.complete(error);
1369 }
1370 }
1371 };
1372 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1373 */
1374
1375 future.complete(null);
1376 l2InitiationNextObjStore.remove(key);
1377 }
1378
1379 /**
1380 * Deletes the pseudo wire termination.
1381 *
1382 * @param l2Tunnel the tunnel
1383 * @param egress the egress connect point
1384 * @param future the async task
1385 * @param direction the direction of the tunnel
1386 */
1387 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1388 ConnectPoint egress,
1389 CompletableFuture<ObjectiveError> future,
Andreas Pantelopoulos7fba8772018-05-02 16:21:20 -07001390 Direction direction) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001391 log.debug("Starting tearing down termination for pseudowire {} direction {}.",
1392 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001393 String key = generateKey(l2Tunnel.tunnelId(), direction);
1394 if (!l2TerminationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001395 log.error("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001396 if (future != null) {
1397 future.complete(null);
1398 }
1399 return;
1400 }
1401 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1402 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1403 l2Tunnel.tunnelId(),
1404 egress.port(),
1405 nextObjective.id());
1406 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1407 log.debug("FwdObj for {} {}, " +
1408 "direction {} removed",
1409 TERMINATION,
1410 l2Tunnel.tunnelId(),
1411 direction),
1412 (objective, error) ->
1413 log.warn("Failed to remove fwdObj " +
1414 "for {} {}" +
1415 ", direction {}",
1416 TERMINATION,
1417 l2Tunnel.tunnelId(),
1418 error,
1419 direction));
1420 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1421
1422 // un-comment in case you want to delete groups used by the pw
1423 // however, this will break the update of pseudowires cause the L2 interface group can
1424 // not be deleted (it is referenced by other groups)
1425 /*
1426 context = new ObjectiveContext() {
1427 @Override
1428 public void onSuccess(Objective objective) {
1429 log.debug("Previous {} next for {} removed", TERMINATION, key);
1430 if (future != null) {
1431 future.complete(null);
1432 }
1433 }
1434
1435 @Override
1436 public void onError(Objective objective, ObjectiveError error) {
1437 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1438 if (future != null) {
1439 future.complete(error);
1440 }
1441 }
1442 };
1443 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1444 */
1445
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001446 l2TerminationNextObjStore.remove(key);
1447 future.complete(null);
1448 }
1449
1450 /**
1451 * Utilities to generate pw key.
1452 *
1453 * @param tunnelId the tunnel id
1454 * @param direction the direction of the pw
1455 * @return the key of the store
1456 */
1457 private String generateKey(long tunnelId, Direction direction) {
1458 return String.format("%s-%s", tunnelId, direction);
1459 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001460}