blob: e40dcb8b6a4a48f354688c3282ec3671abe8eedc [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.DistributedSet;
57import org.onosproject.store.service.Serializer;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070058import org.onosproject.store.service.StorageException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080059import org.onosproject.store.service.Versioned;
60import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
63import java.util.ArrayList;
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -070064import java.util.HashSet;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080065import java.util.List;
66import java.util.Set;
67import java.util.concurrent.CompletableFuture;
68import java.util.stream.Collectors;
69
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080070import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
71import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
72import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
73import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
74import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
75import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -070076import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080077
78/**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070079 * Handler for pseudowire management.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080080 */
81public class DefaultL2TunnelHandler implements L2TunnelHandler {
82
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070083 private static final String LOCK_NAME = "l2-tunnel-handler-lock";
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080084 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
85
86 private final SegmentRoutingManager srManager;
87 /**
88 * To store the next objectives related to the initiation.
89 */
90 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
91 /**
92 * To store the next objectives related to the termination.
93 */
94 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
95
96 /**
97 * To store policies.
98 */
99 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
100
101 /**
102 * To store tunnels.
103 */
104 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
105
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700106 /**
107 * To store pending tunnels that need to be installed.
108 */
109 private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
110
111 /**
112 * To store pending policies that need to be installed.
113 */
114 private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
115
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800116 private final KryoNamespace.Builder l2TunnelKryo;
117
118 /**
119 * Contains transport vlans used for spine-leaf pseudowires.
120 */
121 private final DistributedSet<VlanId> vlanStore;
122
123 /**
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700124 * Lock used when creating or removing pseudowires.
125 */
126 private final DistributedLock pwLock;
127
128 private static final long LOCK_TIMEOUT = 2000;
129
130 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800131 * Used for determining transport vlans for leaf-spine.
132 */
133 private short transportVlanUpper = 4093, transportVlanLower = 3500;
134
135 private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
136
137 /**
138 * Create a l2 tunnel handler for the deploy and
139 * for the tear down of pseudo wires.
140 *
141 * @param segmentRoutingManager the segment routing manager
142 */
143 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
144 srManager = segmentRoutingManager;
145 l2TunnelKryo = new KryoNamespace.Builder()
146 .register(KryoNamespaces.API)
147 .register(L2Tunnel.class,
148 L2TunnelPolicy.class,
149 DefaultL2Tunnel.class,
150 DefaultL2TunnelPolicy.class,
151 L2Mode.class,
152 MplsLabel.class,
153 VlanId.class,
154 ConnectPoint.class);
155
156 l2InitiationNextObjStore = srManager.
157 storageService.
158 <String, NextObjective>consistentMapBuilder().
159 withName("onos-l2initiation-nextobj-store").
160 withSerializer(Serializer.using(l2TunnelKryo.build())).
161 build();
162
163 l2TerminationNextObjStore = srManager.storageService.
164 <String, NextObjective>consistentMapBuilder()
165 .withName("onos-l2termination-nextobj-store")
166 .withSerializer(Serializer.using(l2TunnelKryo.build()))
167 .build();
168
169 l2PolicyStore = srManager.storageService
170 .<String, L2TunnelPolicy>consistentMapBuilder()
171 .withName("onos-l2-policy-store")
172 .withSerializer(Serializer.using(l2TunnelKryo.build()))
173 .build();
174
175 l2TunnelStore = srManager.storageService
176 .<String, L2Tunnel>consistentMapBuilder()
177 .withName("onos-l2-tunnel-store")
178 .withSerializer(Serializer.using(l2TunnelKryo.build()))
179 .build();
180
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700181 pendingL2PolicyStore = srManager.storageService
182 .<String, L2TunnelPolicy>consistentMapBuilder()
183 .withName("onos-l2-pending-policy-store")
184 .withSerializer(Serializer.using(l2TunnelKryo.build()))
185 .build();
186
187 pendingL2TunnelStore = srManager.storageService
188 .<String, L2Tunnel>consistentMapBuilder()
189 .withName("onos-l2-pending-tunnel-store")
190 .withSerializer(Serializer.using(l2TunnelKryo.build()))
191 .build();
192
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800193 vlanStore = srManager.storageService.<VlanId>setBuilder()
194 .withName("onos-transport-vlan-store")
195 .withSerializer(Serializer.using(
196 new KryoNamespace.Builder()
197 .register(KryoNamespaces.API)
198 .build()))
199 .build()
200 .asDistributedSet();
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700201
202 pwLock = srManager.storageService.lockBuilder()
203 .withName(LOCK_NAME)
204 .build()
205 .asLock(LOCK_TIMEOUT);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800206 }
207
208 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800209 * Used by manager only in initialization.
210 */
211 @Override
212 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800213 // Since we have no pseudowires in netcfg there
214 // is nothing to do in initialization.
215 // I leave it here because potentially we might need to
216 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800217 }
218
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700219 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700220 public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700221 // get pending tunnels/policies OR installed tunnels/policies
222 List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
223 List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700224 return tunnels.stream()
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700225 .map(l2Tunnel -> {
226 L2TunnelPolicy policy = null;
227 for (L2TunnelPolicy l2Policy : policies) {
228 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
229 policy = l2Policy;
230 break;
231 }
232 }
233
234 return new DefaultL2TunnelDescription(l2Tunnel, policy);
235 })
236 .collect(Collectors.toSet());
237 }
238
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800239 @Override
240 public List<L2TunnelPolicy> getL2Policies() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800241 return new ArrayList<>(l2PolicyStore
242 .values()
243 .stream()
244 .map(Versioned::value)
245 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800246 }
247
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800248 @Override
249 public List<L2Tunnel> getL2Tunnels() {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800250 return new ArrayList<>(l2TunnelStore
251 .values()
252 .stream()
253 .map(Versioned::value)
254 .collect(Collectors.toList()));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800255 }
256
257 @Override
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700258 public List<L2TunnelPolicy> getL2PendingPolicies() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700259 return new ArrayList<>(pendingL2PolicyStore
260 .values()
261 .stream()
262 .map(Versioned::value)
263 .collect(Collectors.toList()));
264 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800265
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700266 @Override
267 public List<L2Tunnel> getL2PendingTunnels() {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700268 return new ArrayList<>(pendingL2TunnelStore
269 .values()
270 .stream()
271 .map(Versioned::value)
272 .collect(Collectors.toList()));
273 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800274
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700275 @Override
276 public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
277
278 // get both added and pending pseudowires
279 List<L2TunnelDescription> newPseudowires = new ArrayList<>();
280 newPseudowires.addAll(getL2Descriptions(false));
281 newPseudowires.addAll(getL2Descriptions(true));
282 // add the new one
283 newPseudowires.add(pwToAdd);
284
285 return configurationValidity(newPseudowires);
286 }
287
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700288 /**
289 * Manages intermediate filtering rules.
290 *
291 * For leaf-spine-spine pseudowires we need to install a special filtering
292 * rule in the intermediate spine for the appropriate transport vlan.
293 *
294 * @param pw The pseudowire, it will have the path and the transport vlan.
295 */
296 private Result manageIntermediateFiltering(L2TunnelDescription pw, boolean leafSpinePw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800297
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700298 // only leaf-spine-spine should need intermediate rules for now
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700299 if (!leafSpinePw || (pw.l2Tunnel().pathUsed().size() != 2)) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700300 return Result.SUCCESS;
301 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800302
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700303 List<Link> path = pw.l2Tunnel().pathUsed();
304 DeviceId intermediateSpineId = pw.l2Tunnel().pathUsed().get(0).dst().deviceId();
305 L2Tunnel l2Tunnel = pw.l2Tunnel();
306
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700307 log.debug("Installing intermediate filtering rules for spine {} , for pseudowire {}",
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700308 intermediateSpineId, pw.l2Tunnel().tunnelId());
309
310 MacAddress dstMac;
311 try {
312 dstMac = srManager.deviceConfiguration().getDeviceMac(intermediateSpineId);
313 } catch (Exception e) {
314 log.info("Device not found in configuration, no programming of MAC address");
315 dstMac = null;
316 }
317
318 PortNumber inPort;
319
320 inPort = path.get(0).dst().port();
321
322 log.debug("Populating filtering objective for pseudowire transport" +
323 " with vlan = {}, port = {}, mac = {} for device {}",
324 l2Tunnel.transportVlan(),
325 inPort,
326 dstMac,
327 intermediateSpineId);
328
329 FilteringObjective.Builder filteringObjectiveBuilder =
330 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
331 DefaultObjectiveContext context = new DefaultObjectiveContext((objective) ->
332 log.debug("Special filtObj for " +
333 "for {} populated",
334 l2Tunnel.tunnelId()),
335 (objective, error) ->
336 log.warn("Failed to populate " +
337 "special filtObj " +
338 "rule for {}: {}",
339 l2Tunnel.tunnelId(), error));
340 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
341 filteringObjectiveBuilder.withMeta(treatment.build());
342 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
343
344 inPort = path.get(1).src().port();
345
346 log.debug("Populating filtering objective for pseudowire transport" +
347 " with vlan = {}, port = {}, mac = {} for device {}",
348 l2Tunnel.transportVlan(),
349 inPort,
350 dstMac,
351 intermediateSpineId);
352
353 filteringObjectiveBuilder =
354 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
355 context = new DefaultObjectiveContext((objective) ->
356 log.debug("Special filtObj for " + "for {} populated",
357 l2Tunnel.tunnelId()),
358 (objective, error) ->
359 log.warn("Failed to populate " +
360 "special filtObj " +
361 "rule for {}: {}",
362 l2Tunnel.tunnelId(), error));
363 treatment = DefaultTrafficTreatment.builder();
364 filteringObjectiveBuilder.withMeta(treatment.build());
365 srManager.flowObjectiveService.filter(intermediateSpineId, filteringObjectiveBuilder.add(context));
366
367 return Result.SUCCESS;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800368 }
369
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800370 /**
371 * Returns the new vlan id for an ingress point of a
372 * pseudowire. For double tagged, it is the outer,
373 * For single tagged it is the single tag, and for
374 * inner it is None.
375 *
376 * @param ingressOuter vlanid of ingress outer
377 * @param ingressInner vlanid of ingress inner
378 * @param egressOuter vlanid of egress outer
379 * @param egressInner vlanid of egress inner
380 * @return returns the vlan id which will be installed at vlan table 1.
381 */
382 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
383 VlanId egressOuter, VlanId egressInner) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800384 // validity of vlan combinations was checked at verifyPseudowire
385 if (!(ingressOuter.equals(VlanId.NONE))) {
386 return egressOuter;
387 } else if (!(ingressInner.equals(VlanId.NONE))) {
388 return egressInner;
389 } else {
390 return VlanId.vlanId("None");
391 }
392 }
393
394 /**
395 * Determines vlan used for transporting the pw traffic.
396 *
397 * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
398 * and also make sure to add the popVlan instruction.
399 * For spine-leaf pws we choose the highest vlan value available from a certain range.
400 *
401 * @param spinePw if the pw is leaf-spine.
402 * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
403 * then the pw is transported untagged.
404 */
405 private VlanId determineTransportVlan(boolean spinePw) {
406
407 if (!spinePw) {
408
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700409 log.debug("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800410 return UNTAGGED_TRANSPORT_VLAN;
411 } else {
412 for (short i = transportVlanUpper; i > transportVlanLower; i--) {
413
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700414 VlanId vlanToUse = VlanId.vlanId(i);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800415 if (!vlanStore.contains(vlanToUse)) {
416
417 vlanStore.add(vlanToUse);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700418 log.debug("Transport vlan {} for pseudowire!", vlanToUse);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800419 return vlanToUse;
420 }
421 }
422
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700423 log.warn("No available transport vlan found, pseudowire traffic will be carried untagged " +
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800424 "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
425 return UNTAGGED_TRANSPORT_VLAN;
426 }
427 }
428
429 /**
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700430 * Returns the devices existing on a given path.
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700431 *
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700432 * @param path The path to traverse.
433 * @return The devices on the path with the order they
434 * are traversed.
435 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700436 private List<DeviceId> getDevicesOnPath(List<Link> path) {
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700437
438 // iterate over links and get all devices in the order
439 // we find them
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700440 List<DeviceId> deviceList = new ArrayList<>();
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700441 for (Link link : path) {
442 if (!deviceList.contains(link.src().deviceId())) {
443 deviceList.add(link.src().deviceId());
444 }
445 if (!deviceList.contains(link.dst().deviceId())) {
446 deviceList.add(link.dst().deviceId());
447 }
448 }
449
450 return deviceList;
451 }
452
453 /**
454 * Returns true if path is valid according to the current logic.
455 * For example : leaf to spine pseudowires can be either leaf-spine or
456 * leaf-spine-spine. However, in the configuration we might specify spine device
457 * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
458 * two cases and need to provision for them.
459 *
460 * If we have a leaf to leaf pseudowire then all the intermediate devices must
461 * be spines. However, in case of paired switches we can have two leafs interconnected
462 * with each other directly.
463 *
464 * @param path the chosen path
465 * @param leafSpinePw if it is a leaf to spine pseudowire
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700466 * @return True if path size is valid, false otherwise.
467 */
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700468 private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
469
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700470 log.debug("Checking path validity for pseudowire.");
Andreas Pantelopoulos6692fdd2018-03-18 23:17:55 -0700471 List<DeviceId> devices = getDevicesOnPath(path);
472 if (devices.size() < 2) {
473 log.error("Path size for pseudowire should be greater than 1!");
474 return false;
475 }
476
477 try {
478 if (leafSpinePw) {
479 // can either be leaf-spine-spine or leaf-spine
480 // first device is leaf, all other must be spines
481 log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
482 // if first device is a leaf then all other must be spines
483 if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
484 devices.remove(0);
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 } else {
492 // if first device is spine, last device must be a leaf
493 // all other devices must be spines
494 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
495 return false;
496 }
497 devices.remove(devices.size() - 1);
498 for (DeviceId devId : devices) {
499 log.debug("Device {} should be a spine!", devId);
500 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
501 return false;
502 }
503 }
504 }
505 } else {
506 // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
507 // or leaf-spine-spine-leaf
508 log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
509 // check first device, needs to be a leaf
510 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
511 return false;
512 }
513 // check last device, needs to be a leaf
514 if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
515 return false;
516 }
517 // remove these devices, rest must all be spines
518 devices.remove(0);
519 devices.remove(devices.size() - 1);
520 for (DeviceId devId : devices) {
521 log.debug("Device {} should be a spine!", devId);
522 if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
523 return false;
524 }
525 }
526
527 }
528 } catch (DeviceConfigNotFoundException e) {
529 log.error("Device not found in configuration : {}", e);
530 return false;
531 }
532
533 return true;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700534 }
535
536 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800537 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800538 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700539 * @param pw The pseudowire to deploy
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800540 * @return result of pseudowire deployment
541 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700542 public Result deployPseudowire(L2TunnelDescription pw) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800543
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700544 try {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700545 // take the lock
546 pwLock.lock();
547 Result result;
548 long l2TunnelId;
549 log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
550 pw.l2Tunnel().tunnelId());
551 l2TunnelId = pw.l2Tunnel().tunnelId();
552 // The tunnel id cannot be 0.
553 if (l2TunnelId == 0) {
554 log.warn("Tunnel id id must be > 0 in {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700555 return Result.WRONG_PARAMETERS
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700556 .appendError("Tunnel id id must be > 0");
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700557 }
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700558
559 result = verifyGlobalValidity(pw);
560 if (result != SUCCESS) {
561 log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
562 return result;
563 }
564
565 // leafSpinePw determines if this is a leaf-leaf
566 // or leaf-spine pseudowire
567 boolean leafSpinePw;
568 ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
569 ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
570 try {
571 // differentiate between leaf-leaf pseudowires and leaf-spine
572 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
573 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
574 log.error("Can not deploy pseudowire {} from spine to spine!", l2TunnelId);
575 return Result.WRONG_PARAMETERS
576 .appendError("Can not deploy pseudowire from spine to spine!");
577 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
578 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
579 leafSpinePw = false;
580 } else {
581 leafSpinePw = true;
582 }
583 } catch (DeviceConfigNotFoundException e) {
584 log.error("Device for pseudowire {} connection points does not exist in the configuration", l2TunnelId);
585 return Result.INTERNAL_ERROR
586 .appendError("Device for pseudowire connection points does not exist in the configuration");
587 }
588
589 // reverse the policy in order for leaf switch to be at CP1
590 // this will help us for re-using SRLinkWeigher for computing valid paths
591 L2TunnelPolicy reversedPolicy = reverseL2TunnelPolicy(pw.l2TunnelPolicy());
592 if (reversedPolicy == null) {
593 log.error("Error in reversing policy, device configuration was not found for pseudowire {}.",
594 l2TunnelId);
595 return INTERNAL_ERROR
596 .appendError("Device configuration not found when reversing the policy.");
597 }
598 pw.setL2TunnelPolicy(reversedPolicy);
599
600 // get path here, need to use the same for fwd and rev direction
601 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
602 pw.l2TunnelPolicy().cP2());
603 if (path == null) {
604 log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
605 return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
606 }
607
608 Link fwdNextHop;
609 Link revNextHop;
610 if (!isValidPath(path, leafSpinePw)) {
611 log.error("Deploying process : Path for pseudowire {} is not valid", l2TunnelId);
612 return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
613 }
614 // oneHope flag is used to determine if we need to
615 // install transit mpls rules
616 boolean oneHop = true;
617 if (path.size() > 1) {
618 oneHop = false;
619 }
620
621 fwdNextHop = path.get(0);
622 revNextHop = reverseLink(path.get(path.size() - 1));
623
624 pw.l2Tunnel().setPath(path);
625 pw.l2Tunnel().setTransportVlan(determineTransportVlan(leafSpinePw));
626
627 // next hops for next objectives
628 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
629
630 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
631 pw.l2TunnelPolicy().cP1InnerTag(),
632 pw.l2TunnelPolicy().cP2OuterTag(),
633 pw.l2TunnelPolicy().cP2InnerTag());
634 result = deployPseudoWireInit(pw.l2Tunnel(),
635 pw.l2TunnelPolicy().cP1(),
636 pw.l2TunnelPolicy().cP2(),
637 FWD,
638 fwdNextHop,
639 leafSpinePw,
640 oneHop,
641 egressVlan);
642 if (result != SUCCESS) {
643 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP1", l2TunnelId);
644 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
645 }
646
647 result = deployPolicy(l2TunnelId,
648 pw.l2TunnelPolicy().cP1(),
649 pw.l2TunnelPolicy().cP1InnerTag(),
650 pw.l2TunnelPolicy().cP1OuterTag(),
651 egressVlan,
652 result.getNextId());
653 if (result != SUCCESS) {
654 log.error("Deploying process : Error in deploying pseudowire {} policy for CP1", l2TunnelId);
655 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
656 }
657
658 PortNumber termPort = pw.l2Tunnel().pathUsed().get(pw.l2Tunnel().pathUsed().size() - 1).dst().port();
659 result = deployPseudoWireTerm(pw.l2Tunnel(),
660 pw.l2TunnelPolicy().cP2(),
661 egressVlan,
662 FWD,
663 leafSpinePw,
664 oneHop,
665 termPort);
666
667 if (result != SUCCESS) {
668 log.error("Deploying process : Error in deploying pseudowire {} termination for CP1", l2TunnelId);
669 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
670 }
671
672 // We establish the reverse tunnel.
673 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
674 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
675 pw.l2TunnelPolicy().cP2InnerTag(),
676 pw.l2TunnelPolicy().cP1OuterTag(),
677 pw.l2TunnelPolicy().cP1InnerTag());
678
679 result = deployPseudoWireInit(pw.l2Tunnel(),
680 pw.l2TunnelPolicy().cP2(),
681 pw.l2TunnelPolicy().cP1(),
682 REV,
683 revNextHop,
684 leafSpinePw,
685 oneHop,
686 egressVlan);
687 if (result != SUCCESS) {
688 log.error("Deploying process : Error in deploying pseudowire {} initiation for CP2", l2TunnelId);
689 return Result.INTERNAL_ERROR
690 .appendError("Error in deploying pseudowire initiation for CP2");
691 }
692
693 result = deployPolicy(l2TunnelId,
694 pw.l2TunnelPolicy().cP2(),
695 pw.l2TunnelPolicy().cP2InnerTag(),
696 pw.l2TunnelPolicy().cP2OuterTag(),
697 egressVlan,
698 result.getNextId());
699 if (result != SUCCESS) {
700 log.error("Deploying process : Error in deploying policy {} for CP2", l2TunnelId);
701 return Result.INTERNAL_ERROR
702 .appendError("Deploying process : Error in deploying policy for CP2");
703 }
704
705 termPort = pw.l2Tunnel().pathUsed().get(0).src().port();
706 result = deployPseudoWireTerm(pw.l2Tunnel(),
707 pw.l2TunnelPolicy().cP1(),
708 egressVlan,
709 REV,
710 leafSpinePw,
711 oneHop,
712 termPort);
713
714 if (result != SUCCESS) {
715 log.error("Deploying process : Error in deploying pseudowire {} termination for CP2", l2TunnelId);
716 return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
717 }
718
719 result = manageIntermediateFiltering(pw, leafSpinePw);
720 if (result != SUCCESS) {
721 log.error("Deploying process : Error in installing intermediate rules for " +
722 "tagged transport for pseudowire {}", l2TunnelId);
723 return Result.INTERNAL_ERROR.appendError("Error in installing intermediate rules for tagged transport");
724 }
725
726 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
727
728 // Populate stores as the final step of the process
729 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
730 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
731
732 return Result.SUCCESS;
733 } catch (StorageException.Timeout e) {
734 log.error("Can not acquire distributed lock for pseudowire {}!", pw.l2Tunnel().tunnelId());
735 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
736 } finally {
737 // release the lock
738 pwLock.unlock();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700739 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800740 }
741
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700742 @Override
743 public Result checkIfPwExists(long tunnelId, boolean pending) {
744
745 List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
746 .stream()
747 .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
748 .collect(Collectors.toList());
749
750 if (pseudowires.size() == 0) {
751 String store = ((pending) ? "pending" : "installed");
752 log.error("Pseudowire {} does not exist in {} store", tunnelId, store);
753 return Result.WRONG_PARAMETERS.
754 appendError("Pseudowire " + tunnelId + " does not exist in " + store);
755 } else {
756 return SUCCESS;
757 }
758 }
759
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800760 /**
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700761 * Tears down connection points of pseudowires. We can either tear down both connection points,
762 * or each one of them.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800763 *
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700764 * @param l2TunnelId The tunnel id for this pseudowire.
765 * @param tearDownFirst Boolean, true if we want to tear down cp1
766 * @param tearDownSecond Boolean, true if we want to tear down cp2
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700767 * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
768 * in the network, else remove flows/groups in the devices also.
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700769 * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700770 * a descriptive error otherwise.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800771 */
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700772 private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
773 boolean tearDownSecond, boolean pending) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800774
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700775 Result res;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800776 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
777 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800778 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
779 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
780
781 if (l2TunnelId == 0) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700782 log.error("Removal process : Tunnel id cannot be 0");
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700783 return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800784 }
785
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700786 res = checkIfPwExists(l2TunnelId, pending);
787 if (res != Result.SUCCESS) {
788 return res;
789 }
790
791 // remove and get the tunnel and the policy from the appropriate store
792 // if null, return error.
793 Versioned<L2Tunnel> l2TunnelVersioned = pending ?
794 pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
795 l2TunnelStore.remove(Long.toString(l2TunnelId));
796 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
797 pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
798 l2PolicyStore.remove(Long.toString(l2TunnelId));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800799 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
800 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700801 return Result.INTERNAL_ERROR
802 .appendError("Policy and/or tunnel missing for pseudowire!");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800803 }
804
805 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700806 l2TunnelPolicyVersioned.value());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800807
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800808 // remove the reserved transport vlan
809 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
810 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
811 }
812
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700813 if (pending) {
814 // no need to remove flows / groups for a pseudowire
815 // in pending state
816 return Result.SUCCESS;
817 }
818
819 // remove flows/groups involving with this pseudowire
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700820 if (tearDownFirst) {
821 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800822
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700823 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
824 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
825 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
826 pwToRemove.l2TunnelPolicy().cP2InnerTag());
827 deletePolicy(l2TunnelId,
828 pwToRemove.l2TunnelPolicy().cP1(),
829 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
830 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
831 egressVlan,
832 fwdInitNextFuture,
833 FWD);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800834
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700835 fwdInitNextFuture.thenAcceptAsync(status -> {
836 if (status == null) {
837 // Finally we will tear down the pseudo wire.
838 tearDownPseudoWireInit(l2TunnelId,
839 pwToRemove.l2TunnelPolicy().cP1(),
840 fwdTermNextFuture,
841 FWD);
842 }
843 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800844
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700845 fwdTermNextFuture.thenAcceptAsync(status -> {
846 if (status == null) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700847 PortNumber termPort = pwToRemove.l2Tunnel().pathUsed()
848 .get(pwToRemove.l2Tunnel().pathUsed().size() - 1).dst().port();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700849 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
850 pwToRemove.l2TunnelPolicy().cP2(),
851 null,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700852 FWD,
853 termPort);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700854 }
855 });
856 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800857
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700858 if (tearDownSecond) {
859 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800860
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700861 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
862 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
863 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
864 pwToRemove.l2TunnelPolicy().cP1InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800865
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700866 // We do the same operations on the reverse side.
867 deletePolicy(l2TunnelId,
868 pwToRemove.l2TunnelPolicy().cP2(),
869 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
870 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
871 egressVlan,
872 revInitNextFuture,
873 REV);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800874
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700875 revInitNextFuture.thenAcceptAsync(status -> {
876 if (status == null) {
877 tearDownPseudoWireInit(l2TunnelId,
878 pwToRemove.l2TunnelPolicy().cP2(),
879 revTermNextFuture,
880 REV);
881 }
882 });
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800883
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700884 revTermNextFuture.thenAcceptAsync(status -> {
885 if (status == null) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700886 PortNumber termPort = pwToRemove.l2Tunnel().pathUsed().get(0).src().port();
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700887 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
888 pwToRemove.l2TunnelPolicy().cP1(),
889 null,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700890 REV,
891 termPort);
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700892 }
893 });
894 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800895
896 return Result.SUCCESS;
897 }
898
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700899 /**
900 * Helper function for removing a single pseudowire.
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700901 *
902 * Tries to remove pseudowire from any store it might reside (pending or installed).
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700903 *
904 * @param l2TunnelId the id of the pseudowire to tear down
905 * @return Returns SUCCESS if no error is obeserved or an appropriate
906 * error on a failure
907 */
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700908 public Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700909
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700910 try {
911 // take the lock
912 pwLock.lock();
913
914 if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
915 return tearDownConnectionPoints(l2TunnelId, true, true, true);
916 } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
917 return tearDownConnectionPoints(l2TunnelId, true, true, false);
918 } else {
919 return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
920 + l2TunnelId
921 + " did not reside in any store!");
922 }
923 } catch (StorageException.Timeout e) {
924 log.error("Can not acquire distributed lock for pseudowire {}!", l2TunnelId);
925 return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
926 } finally {
927 // release the lock
928 pwLock.unlock();
Andreas Pantelopoulos935d59d2018-03-21 16:44:18 -0700929 }
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700930 }
931
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700932 @Override
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700933 @Deprecated
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800934 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
935
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800936 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800937
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700938 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800939 log.info("Removing pseudowire {}", tunnelId);
940
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700941 Result result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700942 if (result != Result.SUCCESS) {
943 log.error("Could not remove pseudowire {}!", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800944 }
945 }
946 }
947
948 /**
949 * Handles the policy establishment which consists in
950 * create the filtering and forwarding objectives related
951 * to the initiation and termination.
952 *
953 * @param tunnelId the tunnel id
954 * @param ingress the ingress point
955 * @param ingressInner the ingress inner tag
956 * @param ingressOuter the ingress outer tag
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800957 * @param egressVlan Vlan-id to set, depends on ingress vlan
958 * combinations. For example, if pw is double tagged
959 * then this is the value of the outer vlan, if single
960 * tagged then it is the new value of the single tag.
961 * Should be None for untagged traffic.
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700962 * @param nextId the next objective id
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800963 * @return the result of the operation
964 */
965 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
966 VlanId ingressOuter, VlanId egressVlan, int nextId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700967 log.debug("Starting deploying policy for pseudowire {}.", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800968
969 List<Objective> objectives = Lists.newArrayList();
970 // We create the forwarding objective for supporting
971 // the l2 tunnel.
972 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
973 // We create and add objective context.
974 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
975 log.debug("FwdObj for tunnel {} populated", tunnelId),
976 (objective, error) ->
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700977 log.warn("Failed to populate fwdObj " +
978 "for tunnel {} : {}",
979 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800980 objectives.add(fwdBuilder.add(context));
981
982 // We create the filtering objective to define the
983 // permit traffic in the switch
984 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
985
986 // We add the metadata.
987 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
988 .builder()
989 .setTunnelId(tunnelId)
990 .setVlanId(egressVlan);
991 filtBuilder.withMeta(treatment.build());
992
993 // We create and add objective context.
994 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
995 (objective, error) -> log.warn("Failed to populate filterObj for " +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700996 "tunnel {} : {}",
997 tunnelId, error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800998 objectives.add(filtBuilder.add(context));
999
1000 for (Objective objective : objectives) {
1001 if (objective instanceof ForwardingObjective) {
1002 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1003 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
1004 } else {
1005 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1006 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
1007 }
1008 }
1009 return SUCCESS;
1010 }
1011
1012 /**
1013 * Handles the tunnel establishment which consists in
1014 * create the next objectives related to the initiation.
1015 *
1016 * @param l2Tunnel the tunnel to deploy
1017 * @param ingress the ingress connect point
1018 * @param egress the egress connect point
1019 * @param direction the direction of the pw
1020 * @param spinePw if the pseudowire involves a spine switch
1021 * @return the result of the operation
1022 */
1023 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001024 ConnectPoint egress, Direction direction,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001025 Link nextHop, boolean spinePw, boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001026 log.debug("Started deploying init next objectives for pseudowire {} for tunnel {} -> {}.",
1027 l2Tunnel.tunnelId(), ingress, egress);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001028 if (nextHop == null) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001029 log.warn("No path between ingress and egress connection points for tunnel {}", l2Tunnel.tunnelId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001030 return WRONG_PARAMETERS;
1031 }
1032
1033 // We create the next objective without the metadata
1034 // context and id. We check if it already exists in the
1035 // store. If not we store as it is in the store.
1036 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
1037 nextHop.src(),
1038 nextHop.dst(),
1039 l2Tunnel,
1040 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001041 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001042 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001043 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001044
1045 if (nextObjectiveBuilder == null) {
1046 return INTERNAL_ERROR;
1047 }
1048 // We set the metadata. We will use this metadata
1049 // to inform the driver we are doing a l2 tunnel.
1050 TrafficSelector metadata = DefaultTrafficSelector
1051 .builder()
1052 .matchTunnelId(l2Tunnel.tunnelId())
1053 .build();
1054 nextObjectiveBuilder.withMeta(metadata);
1055 int nextId = srManager.flowObjectiveService.allocateNextId();
1056 if (nextId < 0) {
1057 log.warn("Not able to allocate a next id for initiation");
1058 return INTERNAL_ERROR;
1059 }
1060 nextObjectiveBuilder.withId(nextId);
1061 String key = generateKey(l2Tunnel.tunnelId(), direction);
1062 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
1063 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1064 log.debug("Initiation l2 tunnel rule " +
1065 "for {} populated",
1066 l2Tunnel.tunnelId()),
1067 (objective, error) ->
1068 log.warn("Failed to populate Initiation " +
1069 "l2 tunnel rule for {}: {}",
1070 l2Tunnel.tunnelId(), error));
1071 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1072 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1073 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
1074 l2Tunnel.tunnelId(), nextObjective.id());
1075 Result result = SUCCESS;
Andreas Pantelopoulosdaf02c82018-04-04 11:16:56 -07001076 result.setNextId(nextObjective.id());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001077 return result;
1078 }
1079
1080 /**
1081 * Handles the tunnel termination, which consists in the creation
1082 * of a forwarding objective and a next objective.
1083 *
1084 * @param l2Tunnel the tunnel to terminate
1085 * @param egress the egress point
1086 * @param egressVlan the expected vlan at egress
1087 * @param direction the direction
1088 * @param spinePw if the pseudowire involves a spine switch
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001089 * @param inputTermPort the input port at the termination point for the pseudowire, used for installing special
1090 * filtering rules at the termination
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001091 * @return the result of the operation
1092 */
1093 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001094 VlanId egressVlan, Direction direction,
1095 boolean spinePw, boolean oneHop, PortNumber inputTermPort) {
1096 log.debug("Started deploying termination objectives for pseudowire {} , direction {}.",
1097 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001098
1099 // We create the group relative to the termination.
1100 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
1101 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001102 spinePw,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001103 oneHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001104 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001105 if (nextObjectiveBuilder == null) {
1106 return INTERNAL_ERROR;
1107 }
1108 TrafficSelector metadata = DefaultTrafficSelector
1109 .builder()
1110 .matchVlanId(egressVlan)
1111 .build();
1112 nextObjectiveBuilder.withMeta(metadata);
1113 int nextId = srManager.flowObjectiveService.allocateNextId();
1114 if (nextId < 0) {
1115 log.warn("Not able to allocate a next id for initiation");
1116 return INTERNAL_ERROR;
1117 }
1118 nextObjectiveBuilder.withId(nextId);
1119 String key = generateKey(l2Tunnel.tunnelId(), direction);
1120 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1121 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1122 "for {} populated",
1123 l2Tunnel.tunnelId()),
1124 (objective, error) -> log.warn("Failed to populate " +
1125 "termination l2 tunnel " +
1126 "rule for {}: {}",
1127 l2Tunnel.tunnelId(),
1128 error));
1129 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1130 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1131 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1132 l2Tunnel.tunnelId(), nextObjective.id());
1133
1134 // We create the flow relative to the termination.
1135 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1136 egress.port(), nextObjective.id());
1137 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1138 l2Tunnel.tunnelId()),
1139 (objective, error) -> log.warn("Failed to populate fwdrObj" +
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001140 " for tunnel termination {} : {}",
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001141 l2Tunnel.tunnelId(), error));
1142 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1143 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1144 nextId, l2Tunnel.tunnelId());
1145
1146 if (spinePw) {
1147
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001148 MacAddress dstMac;
1149 try {
1150 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1151 } catch (Exception e) {
1152 log.info("Device not found in configuration, no programming of MAC address");
1153 dstMac = null;
1154 }
1155
1156 log.info("Populating filtering objective for pseudowire transport" +
1157 " with vlan = {}, port = {}, mac = {}",
1158 l2Tunnel.transportVlan(),
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001159 inputTermPort,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001160 dstMac);
1161 FilteringObjective.Builder filteringObjectiveBuilder =
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001162 createNormalPipelineFiltObjective(inputTermPort, l2Tunnel.transportVlan(), dstMac);
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001163 context = new DefaultObjectiveContext((objective) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001164 log.debug("Special filtObj for " + "for {} populated",
1165 l2Tunnel.tunnelId()),
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001166 (objective, error) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001167 log.warn("Failed to populate " +
1168 "special filtObj " +
1169 "rule for {}: {}",
1170 l2Tunnel.tunnelId(), error));
1171 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1172 filteringObjectiveBuilder.withMeta(treatment.build());
1173 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1174 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1175 l2Tunnel.tunnelId(),
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001176 inputTermPort);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001177 }
1178
1179 return SUCCESS;
1180 }
1181
1182
1183 /**
1184 * Creates the filtering objective according to a given port and vlanid.
1185 *
1186 * @param inPort the in port
1187 * @param vlanId the inner vlan tag
1188 * @return the filtering objective
1189 */
1190 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1191 VlanId vlanId,
1192 MacAddress dstMac) {
1193
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001194 log.debug("Creating filtering objective for pseudowire intermediate transport with vlan={}, port={}, mac={}",
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001195 vlanId,
1196 inPort,
1197 dstMac);
1198 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1199 .builder()
1200 .withKey(Criteria.matchInPort(inPort))
1201 .addCondition(Criteria.matchVlanId(vlanId))
1202 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1203 .permit()
1204 .fromApp(srManager.appId());
1205
1206 if (dstMac != null) {
1207 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1208 }
1209
1210 return fwdBuilder;
1211 }
1212
1213 /**
1214 * Creates the filtering objective according to a given policy.
1215 *
1216 * @param inPort the in port
1217 * @param innerTag the inner vlan tag
1218 * @param outerTag the outer vlan tag
1219 * @return the filtering objective
1220 */
1221 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1222
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001223 log.debug("Creating connection point filtering objective for vlans {} / {}", outerTag, innerTag);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001224 return DefaultFilteringObjective
1225 .builder()
1226 .withKey(Criteria.matchInPort(inPort))
1227 .addCondition(Criteria.matchInnerVlanId(innerTag))
1228 .addCondition(Criteria.matchVlanId(outerTag))
1229 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1230 .permit()
1231 .fromApp(srManager.appId());
1232 }
1233
1234 /**
1235 * Creates the forwarding objective for the termination.
1236 *
1237 * @param pwLabel the pseudo wire label
1238 * @param tunnelId the tunnel id
1239 * @param egressPort the egress port
1240 * @param nextId the next step
1241 * @return the forwarding objective to support the termination
1242 */
1243 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1244 PortNumber egressPort, int nextId) {
1245
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001246 log.debug("Creating forwarding objective for termination for tunnel {} : pwLabel {}, egressPort {}, nextId {}",
1247 tunnelId, pwLabel, egressPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001248 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1249 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1250 // The flow has to match on the pw label and bos
1251 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1252 trafficSelector.matchMplsLabel(pwLabel);
1253 trafficSelector.matchMplsBos(true);
1254 // The flow has to decrement ttl, restore ttl in
1255 // pop mpls, set tunnel id and port.
1256 trafficTreatment.decMplsTtl();
1257 trafficTreatment.copyTtlIn();
1258 trafficTreatment.popMpls();
1259 trafficTreatment.setTunnelId(tunnelId);
1260 trafficTreatment.setOutput(egressPort);
1261
1262 return DefaultForwardingObjective
1263 .builder()
1264 .fromApp(srManager.appId())
1265 .makePermanent()
1266 .nextStep(nextId)
1267 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1268 .withSelector(trafficSelector.build())
1269 .withTreatment(trafficTreatment.build())
1270 .withFlag(VERSATILE);
1271 }
1272
1273 /**
1274 * Creates the forwarding objective for the initiation.
1275 *
1276 * @param tunnelId the tunnel id
1277 * @param inPort the input port
1278 * @param nextId the next step
1279 * @return the forwarding objective to support the initiation.
1280 */
1281 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1282
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001283 log.debug("Creating forwarding objective for tunnel {} : Port {} , nextId {}", tunnelId, inPort, nextId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001284 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1285
1286 // The flow has to match on the mpls logical
1287 // port and the tunnel id.
1288 trafficSelector.matchTunnelId(tunnelId);
1289 trafficSelector.matchInPort(inPort);
1290
1291 return DefaultForwardingObjective
1292 .builder()
1293 .fromApp(srManager.appId())
1294 .makePermanent()
1295 .nextStep(nextId)
1296 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1297 .withSelector(trafficSelector.build())
1298 .withFlag(VERSATILE);
1299
1300 }
1301
1302 /**
1303 * Creates the next objective according to a given
1304 * pipeline. We don't set the next id and we don't
1305 * create the final meta to check if we are re-using
1306 * the same next objective for different tunnels.
1307 *
1308 * @param pipeline the pipeline to support
1309 * @param srcCp the source port
1310 * @param dstCp the destination port
1311 * @param l2Tunnel the tunnel to support
1312 * @param egressId the egress device id
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001313 * @param oneHop if the pw only has one hop, push only pw label
1314 * @param leafSpinePw true if we want instantiate a leaf-spine or leaf-spine-spine pw
1315 * @param termVlanId the outer vlan id of the packet exiting a termination point
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001316 * @return the next objective to support the pipeline
1317 */
1318 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1319 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001320 DeviceId egressId, boolean leafSpinePw,
1321 boolean oneHop, VlanId termVlanId) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001322 log.debug("Creating {} next objective for pseudowire {}.",
1323 pipeline == TERMINATION ? "termination" : "inititation");
1324
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001325 NextObjective.Builder nextObjBuilder;
1326 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1327 if (pipeline == INITIATION) {
1328 nextObjBuilder = DefaultNextObjective
1329 .builder()
1330 .withType(NextObjective.Type.SIMPLE)
1331 .fromApp(srManager.appId());
1332 // The pw label is the bottom of stack. It has to
1333 // be different -1.
1334 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001335 log.error("Pw label not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001336 return null;
1337 }
1338 treatmentBuilder.pushMpls();
1339 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1340 treatmentBuilder.setMplsBos(true);
1341 treatmentBuilder.copyTtlOut();
1342
1343 // If the inter-co label is present we have to set the label.
1344 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1345 treatmentBuilder.pushMpls();
1346 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1347 treatmentBuilder.setMplsBos(false);
1348 treatmentBuilder.copyTtlOut();
1349 }
1350
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001351 // if not oneHop install transit mpls labels also
1352 if (!oneHop) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001353 // We retrieve the sr label from the config
1354 // specific for pseudowire traffic
1355 // using the egress leaf device id.
1356 MplsLabel srLabel;
1357 try {
1358 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1359
1360 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001361 log.error("Sr label for pw traffic not configured");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001362 return null;
1363 }
1364
1365 treatmentBuilder.pushMpls();
1366 treatmentBuilder.setMpls(srLabel);
1367 treatmentBuilder.setMplsBos(false);
1368 treatmentBuilder.copyTtlOut();
1369 }
1370
1371 // We have to rewrite the src and dst mac address.
1372 MacAddress ingressMac;
1373 try {
1374 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1375 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001376 log.error("Was not able to find the ingress mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001377 return null;
1378 }
1379 treatmentBuilder.setEthSrc(ingressMac);
1380 MacAddress neighborMac;
1381 try {
1382 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1383 } catch (DeviceConfigNotFoundException e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001384 log.error("Was not able to find the neighbor mac");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001385 return null;
1386 }
1387 treatmentBuilder.setEthDst(neighborMac);
1388
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001389 // if true we need to pop the vlan because
1390 // we instantiate a leaf to leaf pseudowire
1391 if (!leafSpinePw) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001392 log.debug("We should carry traffic UNTAGGED for pseudowire {}", l2Tunnel.tunnelId());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001393 treatmentBuilder.popVlan();
1394 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001395
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -07001396 // set the appropriate transport vlan from tunnel information
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001397 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001398 } else {
1399 // We create the next objective which
1400 // will be a simple l2 group.
1401 nextObjBuilder = DefaultNextObjective
1402 .builder()
1403 .withType(NextObjective.Type.SIMPLE)
1404 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001405
1406 // for termination point we use the outer vlan of the
1407 // encapsulated packet
1408 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001409 }
1410
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001411 treatmentBuilder.setOutput(srcCp.port());
1412 nextObjBuilder.addTreatment(treatmentBuilder.build());
1413 return nextObjBuilder;
1414 }
1415
1416 /**
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001417 * Reverse an l2 tunnel policy in order to have as CP1 the leaf switch,
1418 * in case one of the switches is a spine.
1419 *
1420 * This makes possible the usage of SRLinkWeigher for computing valid paths,
1421 * which cuts leaf-spine links from the path computation with a src different
1422 * than the source leaf.
1423 *
1424 * @param policy The policy to reverse, if needed
1425 * @return a l2TunnelPolicy containing the leaf at CP1, suitable for usage with
1426 * current SRLinkWeigher
1427 */
1428 private L2TunnelPolicy reverseL2TunnelPolicy(L2TunnelPolicy policy) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001429
1430 log.debug("Reversing policy for pseudowire.");
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001431 try {
1432 // if cp1 is a leaf, just return
1433 if (srManager.deviceConfiguration().isEdgeDevice(policy.cP1().deviceId())) {
1434 return policy;
1435 } else {
1436 // return a policy with reversed cp1 and cp2, and also with reversed tags
1437 return new DefaultL2TunnelPolicy(policy.tunnelId(),
1438 policy.cP2(), policy.cP2InnerTag(), policy.cP2OuterTag(),
1439 policy.cP1(), policy.cP1InnerTag(), policy.cP1OuterTag());
1440
1441 }
1442 } catch (DeviceConfigNotFoundException e) {
1443 // should never come here, since it has been checked before
1444 log.error("Configuration for device {}, does not exist!");
1445 return null;
1446 }
1447 }
1448
1449 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001450 * Reverses a link.
1451 *
1452 * @param link link to be reversed
1453 * @return the reversed link
1454 */
1455 private Link reverseLink(Link link) {
1456
1457 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1458
1459 linkBuilder.src(link.dst());
1460 linkBuilder.dst(link.src());
1461 linkBuilder.type(link.type());
1462 linkBuilder.providerId(link.providerId());
1463
1464 return linkBuilder.build();
1465 }
1466
1467 /**
1468 * Returns the path betwwen two connect points.
1469 *
1470 * @param srcCp source connect point
1471 * @param dstCp destination connect point
1472 * @return the path
1473 */
1474 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
Andreas Pantelopoulos0616c562018-04-05 10:49:37 -07001475
1476 // use SRLinkWeigher to avoid pair links, and also
1477 // avoid going from the spine to the leaf and to the
1478 // spine again, we need to have the leaf as CP1 here.
1479 LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
1480
1481 Set<Path> paths = srManager.topologyService.
1482 getPaths(srManager.topologyService.currentTopology(),
1483 srcCp.deviceId(), dstCp.deviceId(), srLw);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001484
1485 log.debug("Paths obtained from topology service {}", paths);
1486
1487 // We randomly pick a path.
1488 if (paths.isEmpty()) {
1489 return null;
1490 }
1491 int size = paths.size();
1492 int index = RandomUtils.nextInt(0, size);
1493
1494 List<Link> result = Iterables.get(paths, index).links();
1495 log.debug("Randomly picked a path {}", result);
1496
1497 return result;
1498 }
1499
1500 /**
1501 * Deletes a given policy using the parameter supplied.
1502 *
1503 * @param tunnelId the tunnel id
1504 * @param ingress the ingress point
1505 * @param ingressInner the ingress inner vlan id
1506 * @param ingressOuter the ingress outer vlan id
1507 * @param future to perform the async operation
1508 * @param direction the direction: forward or reverse
1509 */
1510 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1511 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1512
1513 String key = generateKey(tunnelId, direction);
1514 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001515 log.error("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001516 if (future != null) {
1517 future.complete(null);
1518 }
1519 return;
1520 }
1521 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1522 int nextId = nextObjective.id();
1523 List<Objective> objectives = Lists.newArrayList();
1524 // We create the forwarding objective.
1525 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1526 ObjectiveContext context = new ObjectiveContext() {
1527 @Override
1528 public void onSuccess(Objective objective) {
1529 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1530 if (future != null) {
1531 future.complete(null);
1532 }
1533 }
1534
1535 @Override
1536 public void onError(Objective objective, ObjectiveError error) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001537 log.error("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001538 if (future != null) {
1539 future.complete(error);
1540 }
1541 }
1542 };
1543 objectives.add(fwdBuilder.remove(context));
1544 // We create the filtering objective to define the
1545 // permit traffic in the switch
1546 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1547 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1548 .builder()
1549 .setTunnelId(tunnelId)
1550 .setVlanId(egressVlan);
1551 filtBuilder.withMeta(treatment.build());
1552 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1553 (objective, error) ->
1554 log.warn("Failed to revoke filterObj for policy {}",
1555 tunnelId, error));
1556 objectives.add(filtBuilder.remove(context));
1557
1558 for (Objective objective : objectives) {
1559 if (objective instanceof ForwardingObjective) {
1560 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1561 } else {
1562 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1563 }
1564 }
1565 }
1566
1567 /**
1568 * Deletes the pseudo wire initiation.
1569 *
1570 * @param l2TunnelId the tunnel id
1571 * @param ingress the ingress connect point
1572 * @param future to perform an async operation
1573 * @param direction the direction: reverse of forward
1574 */
1575 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1576 CompletableFuture<ObjectiveError> future, Direction direction) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001577 log.debug("Starting tearing dowing initation of pseudowire {} for direction {}.",
1578 l2TunnelId, direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001579 String key = generateKey(l2TunnelId, direction);
1580 if (!l2InitiationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001581 log.error("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001582 if (future != null) {
1583 future.complete(null);
1584 }
1585 return;
1586 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001587
1588 // un-comment in case you want to delete groups used by the pw
1589 // however, this will break the update of pseudowires cause the L2 interface group can
1590 // not be deleted (it is referenced by other groups)
1591 /*
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -07001592 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001593 ObjectiveContext context = new ObjectiveContext() {
1594 @Override
1595 public void onSuccess(Objective objective) {
1596 log.debug("Previous {} next for {} removed", INITIATION, key);
1597 if (future != null) {
1598 future.complete(null);
1599 }
1600 }
1601
1602 @Override
1603 public void onError(Objective objective, ObjectiveError error) {
1604 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1605 if (future != null) {
1606 future.complete(error);
1607 }
1608 }
1609 };
1610 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1611 */
1612
1613 future.complete(null);
1614 l2InitiationNextObjStore.remove(key);
1615 }
1616
1617 /**
1618 * Deletes the pseudo wire termination.
1619 *
1620 * @param l2Tunnel the tunnel
1621 * @param egress the egress connect point
1622 * @param future the async task
1623 * @param direction the direction of the tunnel
1624 */
1625 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1626 ConnectPoint egress,
1627 CompletableFuture<ObjectiveError> future,
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001628 Direction direction,
1629 PortNumber inPort) {
1630 log.debug("Starting tearing down termination for pseudowire {} direction {}.",
1631 l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001632 String key = generateKey(l2Tunnel.tunnelId(), direction);
1633 if (!l2TerminationNextObjStore.containsKey(key)) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001634 log.error("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001635 if (future != null) {
1636 future.complete(null);
1637 }
1638 return;
1639 }
1640 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1641 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1642 l2Tunnel.tunnelId(),
1643 egress.port(),
1644 nextObjective.id());
1645 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1646 log.debug("FwdObj for {} {}, " +
1647 "direction {} removed",
1648 TERMINATION,
1649 l2Tunnel.tunnelId(),
1650 direction),
1651 (objective, error) ->
1652 log.warn("Failed to remove fwdObj " +
1653 "for {} {}" +
1654 ", direction {}",
1655 TERMINATION,
1656 l2Tunnel.tunnelId(),
1657 error,
1658 direction));
1659 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1660
1661 // un-comment in case you want to delete groups used by the pw
1662 // however, this will break the update of pseudowires cause the L2 interface group can
1663 // not be deleted (it is referenced by other groups)
1664 /*
1665 context = new ObjectiveContext() {
1666 @Override
1667 public void onSuccess(Objective objective) {
1668 log.debug("Previous {} next for {} removed", TERMINATION, key);
1669 if (future != null) {
1670 future.complete(null);
1671 }
1672 }
1673
1674 @Override
1675 public void onError(Objective objective, ObjectiveError error) {
1676 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1677 if (future != null) {
1678 future.complete(error);
1679 }
1680 }
1681 };
1682 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1683 */
1684
1685 // delete the extra filtering objective for terminating
1686 // spine-spine pws
1687 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1688
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001689 MacAddress dstMac;
1690 try {
1691 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1692 } catch (Exception e) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001693 log.error("Device not found in configuration, no programming of MAC address");
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001694 dstMac = null;
1695 }
1696
1697 log.info("Removing filtering objective for pseudowire transport" +
1698 " with vlan = {}, port = {}, mac = {}",
1699 l2Tunnel.transportVlan(),
1700 inPort,
1701 dstMac);
1702 FilteringObjective.Builder filteringObjectiveBuilder =
1703 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -07001704 context = new DefaultObjectiveContext((objective) ->
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001705 log.debug("Special filtObj for " + "for {} removed",
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -07001706 l2Tunnel.tunnelId()),
1707 (objective, error) ->
1708 log.warn("Failed to populate " + "special filtObj " +
1709 "rule for {}: {}",
1710 l2Tunnel.tunnelId(), error));
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001711 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1712 filteringObjectiveBuilder.withMeta(treatment.build());
1713 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1714 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1715 l2Tunnel.tunnelId(),
1716 inPort);
1717 }
1718
1719 l2TerminationNextObjStore.remove(key);
1720 future.complete(null);
1721 }
1722
1723 /**
1724 * Utilities to generate pw key.
1725 *
1726 * @param tunnelId the tunnel id
1727 * @param direction the direction of the pw
1728 * @return the key of the store
1729 */
1730 private String generateKey(long tunnelId, Direction direction) {
1731 return String.format("%s-%s", tunnelId, direction);
1732 }
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001733}