blob: bbcabbb4de5537a33d93c0fe837dfd4c78baa16e [file] [log] [blame]
Pier Ventref34966c2016-11-07 16:21:04 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Pier Ventref34966c2016-11-07 16:21:04 -08003 *
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
Pier Ventre42287df2016-11-09 14:17:26 -080019import com.google.common.collect.Iterables;
20import com.google.common.collect.Lists;
21import org.apache.commons.lang3.RandomUtils;
Pier Ventre70d53ba2016-11-17 22:26:29 -080022import org.onlab.packet.Ethernet;
Pier Ventre42287df2016-11-09 14:17:26 -080023import org.onlab.packet.MacAddress;
24import org.onlab.packet.MplsLabel;
25import org.onlab.packet.VlanId;
26import org.onlab.util.KryoNamespace;
27import org.onosproject.net.ConnectPoint;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070028import org.onosproject.net.DefaultLink;
Pier Ventre42287df2016-11-09 14:17:26 -080029import org.onosproject.net.DeviceId;
30import org.onosproject.net.DisjointPath;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Pier Ventref34966c2016-11-07 16:21:04 -080033import org.onosproject.net.config.NetworkConfigEvent;
Pier Ventre42287df2016-11-09 14:17:26 -080034import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flow.criteria.Criteria;
39import org.onosproject.net.flowobjective.DefaultFilteringObjective;
40import org.onosproject.net.flowobjective.DefaultForwardingObjective;
41import org.onosproject.net.flowobjective.DefaultNextObjective;
42import org.onosproject.net.flowobjective.DefaultObjectiveContext;
43import org.onosproject.net.flowobjective.FilteringObjective;
44import org.onosproject.net.flowobjective.ForwardingObjective;
45import org.onosproject.net.flowobjective.NextObjective;
46import org.onosproject.net.flowobjective.Objective;
47import org.onosproject.net.flowobjective.ObjectiveContext;
48import org.onosproject.net.flowobjective.ObjectiveError;
Pier Ventref34966c2016-11-07 16:21:04 -080049import org.onosproject.segmentrouting.SegmentRoutingManager;
Pier Ventre42287df2016-11-09 14:17:26 -080050import org.onosproject.segmentrouting.SegmentRoutingService;
51import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Pier Ventref34966c2016-11-07 16:21:04 -080052import org.onosproject.segmentrouting.config.PwaasConfig;
Pier Ventre42287df2016-11-09 14:17:26 -080053import org.onosproject.store.serializers.KryoNamespaces;
54import org.onosproject.store.service.ConsistentMap;
55import org.onosproject.store.service.Serializer;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070056import org.onosproject.store.service.Versioned;
Pier Ventref34966c2016-11-07 16:21:04 -080057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
Pier Ventre42287df2016-11-09 14:17:26 -080060import java.util.List;
61import java.util.Set;
62import java.util.concurrent.CompletableFuture;
63import java.util.stream.Collectors;
64
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -080065import static com.google.common.base.Preconditions.checkArgument;
Pier Ventre42287df2016-11-09 14:17:26 -080066import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
Pier Ventre42287df2016-11-09 14:17:26 -080067import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
Pier Ventre70d53ba2016-11-17 22:26:29 -080068import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
Pier Ventre42287df2016-11-09 14:17:26 -080069import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
Pier Ventre70d53ba2016-11-17 22:26:29 -080070import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
71import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
Pier Ventre42287df2016-11-09 14:17:26 -080072
Pier Ventref34966c2016-11-07 16:21:04 -080073/**
74 * Handles pwaas related events.
75 */
76public class L2TunnelHandler {
Pier Ventre42287df2016-11-09 14:17:26 -080077
Pier Ventref34966c2016-11-07 16:21:04 -080078 private static final Logger log = LoggerFactory.getLogger(L2TunnelHandler.class);
Pier Ventre42287df2016-11-09 14:17:26 -080079
Pier Ventref34966c2016-11-07 16:21:04 -080080 private final SegmentRoutingManager srManager;
Pier Ventre42287df2016-11-09 14:17:26 -080081 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -080082 * To store the next objectives related to the initiation.
83 */
84 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
85 /**
86 * To store the next objectives related to the termination.
87 */
88 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070089
Pier Ventre70d53ba2016-11-17 22:26:29 -080090 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070091 * To store policies.
Pier Ventre42287df2016-11-09 14:17:26 -080092 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070093 private final ConsistentMap<String, DefaultL2TunnelPolicy> l2PolicyStore;
94
95 /**
96 * To store tunnels.
97 */
98 private final ConsistentMap<String, DefaultL2Tunnel> l2TunnelStore;
99
Pier Ventre42287df2016-11-09 14:17:26 -0800100 private final KryoNamespace.Builder l2TunnelKryo;
101
102 /**
103 * Create a l2 tunnel handler for the deploy and
104 * for the tear down of pseudo wires.
105 *
106 * @param segmentRoutingManager the segment routing manager
107 */
108 public L2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
109 srManager = segmentRoutingManager;
110 l2TunnelKryo = new KryoNamespace.Builder()
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700111 .register(KryoNamespaces.API)
112 .register(DefaultL2Tunnel.class,
113 DefaultL2TunnelPolicy.class,
114 L2Mode.class,
115 MplsLabel.class,
116 VlanId.class,
117 ConnectPoint.class);
Pier Ventre42287df2016-11-09 14:17:26 -0800118
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700119 l2InitiationNextObjStore = srManager.
120 storageService.
121 <String, NextObjective>consistentMapBuilder().
122 withName("onos-l2initiation-nextobj-store").
123 withSerializer(Serializer.using(l2TunnelKryo.build())).
124 build();
Pier Ventre70d53ba2016-11-17 22:26:29 -0800125
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700126 l2TerminationNextObjStore = srManager.storageService.
127 <String, NextObjective>consistentMapBuilder()
Pier Ventre70d53ba2016-11-17 22:26:29 -0800128 .withName("onos-l2termination-nextobj-store")
129 .withSerializer(Serializer.using(l2TunnelKryo.build()))
130 .build();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700131
132 l2PolicyStore = srManager.storageService
133 .<String, DefaultL2TunnelPolicy>consistentMapBuilder()
134 .withName("onos-l2-policy-store")
135 .withSerializer(Serializer.using(l2TunnelKryo.build()))
136 .build();
137
138 l2TunnelStore = srManager.storageService
139 .<String, DefaultL2Tunnel>consistentMapBuilder()
140 .withName("onos-l2-tunnel-store")
141 .withSerializer(Serializer.using(l2TunnelKryo.build()))
142 .build();
143 }
144
145 /**
146 * Returns all L2 Policies.
147 *
148 * @return List of policies
149 */
150 public List<DefaultL2TunnelPolicy> getL2Policies() {
151
152 return l2PolicyStore
153 .values()
154 .stream()
155 .map(Versioned::value)
156 .map(DefaultL2TunnelPolicy::new)
157 .collect(Collectors.toList());
158
159 }
160
161 /**
162 * Returns all L2 Tunnels.
163 *
164 * @return List of tunnels.
165 */
166 public List<DefaultL2Tunnel> getL2Tunnels() {
167
168 return l2TunnelStore
169 .values()
170 .stream()
171 .map(Versioned::value)
172 .map(DefaultL2Tunnel::new)
173 .collect(Collectors.toList());
174
175 }
176
177 /**
178 * Processes a link removal. Finds affected pseudowires and rewires them.
179 * TODO: Make it also take into account failures of links that are used for pw
180 * traffic in the spine.
181 * @param link The link that failed
182 */
183 public void processLinkDown(Link link) {
184
185 List<DefaultL2Tunnel> tunnels = getL2Tunnels();
186 List<DefaultL2TunnelPolicy> policies = getL2Policies();
187
188 // determine affected pseudowires and update them at once
189 Set<DefaultL2TunnelDescription> pwToUpdate = tunnels
190 .stream()
191 .filter(tun -> tun.pathUsed().contains(link))
192 .map(l2Tunnel -> {
193 DefaultL2TunnelPolicy policy = null;
194 for (DefaultL2TunnelPolicy l2Policy : policies) {
195 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
196 policy = l2Policy;
197 break;
198 }
199 }
200
201 return new DefaultL2TunnelDescription(l2Tunnel, policy);
202 })
203 .collect(Collectors.toSet());
204
205
206 log.info("Pseudowires affected by link failure : {}, rerouting them...", pwToUpdate);
207
208 // update all pseudowires
209 pwToUpdate.forEach(tun -> updatePw(tun, tun));
Pier Ventref34966c2016-11-07 16:21:04 -0800210 }
211
212 /**
213 * Processes Pwaas Config added event.
214 *
Pier Ventre42287df2016-11-09 14:17:26 -0800215 * @param event network config add event
Pier Ventref34966c2016-11-07 16:21:04 -0800216 */
217 public void processPwaasConfigAdded(NetworkConfigEvent event) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800218 checkArgument(event.config().isPresent(),
219 "Config is not presented in PwaasConfigAdded event {}", event);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700220
221 log.info("Network event : Pseudowire configuration added!");
Pier Ventref34966c2016-11-07 16:21:04 -0800222 PwaasConfig config = (PwaasConfig) event.config().get();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700223
224 // gather pseudowires
225 Set<DefaultL2TunnelDescription> pwToAdd = config
226 .getPwIds()
Pier Ventre42287df2016-11-09 14:17:26 -0800227 .stream()
228 .map(config::getPwDescription)
229 .collect(Collectors.toSet());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700230
231 // deploy pseudowires
Pier Ventre42287df2016-11-09 14:17:26 -0800232 deploy(pwToAdd);
233 }
234
Pier Ventre70d53ba2016-11-17 22:26:29 -0800235 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700236 * Returns the new vlan id for an ingress point of a
237 * pseudowire. For double tagged, it is the outer,
238 * For single tagged it is the single tag, and for
239 * inner it is None.
240 *
241 * @param ingressOuter vlanid of ingress outer
242 * @param ingressInner vlanid of ingress inner
243 * @param egressOuter vlanid of egress outer
244 * @param egressInner vlanid of egress inner
245 * @return returns the vlan id which will be installed at vlan table 1.
246 */
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800247 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700248 VlanId egressOuter, VlanId egressInner) {
249
250 // validity of vlan combinations was checked at verifyPseudowire
251 if (!(ingressOuter.equals(VlanId.NONE))) {
252 return egressOuter;
253 } else if (!(ingressInner.equals(VlanId.NONE))) {
254 return egressInner;
255 } else {
256 return VlanId.vlanId("None");
257 }
258 }
259
260 /**
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800261 * Adds a single pseudowire from leaf to a leaf.
262 * This method can be called from cli commands
Charles Chanf7ce5092018-01-06 17:57:27 -0800263 * without configuration updates, thus it does not check for mastership
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700264 * of the ingress pseudowire device.
265 *
266 * @param pw The pseudowire
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800267 * @param spinePw True if pseudowire is from leaf to spine
268 * @return result of pseudowire deployment
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700269 */
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800270 private Result deployPseudowire(DefaultL2TunnelDescription pw, boolean spinePw) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700271
272 Result result;
273 long l2TunnelId;
274
275 l2TunnelId = pw.l2Tunnel().tunnelId();
276
277 // The tunnel id cannot be 0.
278 if (l2TunnelId == 0) {
279 log.warn("Tunnel id id must be > 0");
280 return Result.ADDITION_ERROR;
281 }
282
283 // get path here, need to use the same for fwd and rev direction
284 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
285 pw.l2TunnelPolicy().cP2());
286 if (path == null) {
287 log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
288 return WRONG_PARAMETERS;
289 }
290
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800291 Link fwdNextHop;
292 Link revNextHop;
293 if (!spinePw) {
294 if (path.size() != 2) {
295 log.info("Deploying process : Path between two leafs should have size of 2, for pseudowire {}",
296 l2TunnelId);
297 return INTERNAL_ERROR;
298 }
299
300 fwdNextHop = path.get(0);
301 revNextHop = reverseLink(path.get(1));
302 } else {
303 if (path.size() != 1) {
Charles Chanf7ce5092018-01-06 17:57:27 -0800304 log.info("Deploying process : Path between leaf spine should equal to 1, for pseudowire {}",
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800305 l2TunnelId);
306 return INTERNAL_ERROR;
307 }
308
309 fwdNextHop = path.get(0);
310 revNextHop = reverseLink(path.get(0));
311 }
312
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700313 pw.l2Tunnel().setPath(path);
314
315 // next hops for next objectives
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700316
317 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
318
319 // We establish the tunnel.
320 // result.nextId will be used in fwd
321 result = deployPseudoWireInit(pw.l2Tunnel(),
322 pw.l2TunnelPolicy().cP1(),
323 pw.l2TunnelPolicy().cP2(),
324 FWD,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800325 fwdNextHop,
326 spinePw);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700327 if (result != SUCCESS) {
328 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
329 return Result.ADDITION_ERROR;
330 }
331
332 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
333 pw.l2TunnelPolicy().cP1InnerTag(),
334 pw.l2TunnelPolicy().cP2OuterTag(),
335 pw.l2TunnelPolicy().cP2InnerTag());
336
337 // We create the policy.
338 result = deployPolicy(l2TunnelId,
339 pw.l2TunnelPolicy().cP1(),
340 pw.l2TunnelPolicy().cP1InnerTag(),
341 pw.l2TunnelPolicy().cP1OuterTag(),
342 egressVlan,
343 result.nextId);
344 if (result != SUCCESS) {
345 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
346 return Result.ADDITION_ERROR;
347 }
348
349 // We terminate the tunnel
350 result = deployPseudoWireTerm(pw.l2Tunnel(),
351 pw.l2TunnelPolicy().cP2(),
352 pw.l2TunnelPolicy().cP2OuterTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800353 FWD,
354 spinePw);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700355
356 if (result != SUCCESS) {
357 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
358 return Result.ADDITION_ERROR;
359
360 }
361
362 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
363
364 // We establish the reverse tunnel.
365 result = deployPseudoWireInit(pw.l2Tunnel(),
366 pw.l2TunnelPolicy().cP2(),
367 pw.l2TunnelPolicy().cP1(),
368 REV,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800369 revNextHop,
370 spinePw);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700371 if (result != SUCCESS) {
372 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
373 return Result.ADDITION_ERROR;
374 }
375
376 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
377 pw.l2TunnelPolicy().cP2InnerTag(),
378 pw.l2TunnelPolicy().cP1OuterTag(),
379 pw.l2TunnelPolicy().cP1InnerTag());
380 result = deployPolicy(l2TunnelId,
381 pw.l2TunnelPolicy().cP2(),
382 pw.l2TunnelPolicy().cP2InnerTag(),
383 pw.l2TunnelPolicy().cP2OuterTag(),
384 egressVlan,
385 result.nextId);
386 if (result != SUCCESS) {
387 log.info("Deploying process : Error in deploying policy for CP2");
388 return Result.ADDITION_ERROR;
389 }
390
391 result = deployPseudoWireTerm(pw.l2Tunnel(),
392 pw.l2TunnelPolicy().cP1(),
393 pw.l2TunnelPolicy().cP1OuterTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800394 REV,
395 spinePw);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700396
397 if (result != SUCCESS) {
398 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
399 return Result.ADDITION_ERROR;
400 }
401
402 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
403
404 // Populate stores
405 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
406 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
407
408 return Result.SUCCESS;
409 }
410
411 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -0800412 * To deploy a number of pseudo wires.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700413 * <p>
414 * Called ONLY when configuration changes, thus the check
415 * for the mastership of the device.
416 * <p>
417 * Only the master of CP1 will deploy this pseudowire.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800418 *
419 * @param pwToAdd the set of pseudo wires to add
420 */
Pier Ventre42287df2016-11-09 14:17:26 -0800421 private void deploy(Set<DefaultL2TunnelDescription> pwToAdd) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700422
Pier Ventre42287df2016-11-09 14:17:26 -0800423 Result result;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700424
Pier Ventre42287df2016-11-09 14:17:26 -0800425 for (DefaultL2TunnelDescription currentL2Tunnel : pwToAdd) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800426 ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
427 ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
428 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700429
430 // only the master of CP1 will program this pseudowire
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800431 if (!srManager.isMasterOf(cp1)) {
432 log.debug("Not the master of {}. Ignore pseudo wire deployment id={}", cp1, tunnelId);
Pier Ventre42287df2016-11-09 14:17:26 -0800433 continue;
434 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700435
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800436 try {
437 // differentiate between leaf-leaf pseudowires and leaf-spine
438 // and pass the appropriate flag in them.
439 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
440 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
441 log.warn("Can not deploy pseudowire from spine to spine!");
442 result = Result.INTERNAL_ERROR;
443 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
444 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
445 log.info("Deploying a leaf-leaf pseudowire {}", tunnelId);
446 result = deployPseudowire(currentL2Tunnel, false);
447 } else {
448 log.info("Deploying a leaf-spine pseudowire {}", tunnelId);
449 result = deployPseudowire(currentL2Tunnel, true);
450 }
451 } catch (DeviceConfigNotFoundException e) {
452 log.error("Exception caught when deploying pseudowire", e.toString());
453 result = Result.INTERNAL_ERROR;
454 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700455
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700456 switch (result) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800457 case INTERNAL_ERROR:
458 log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
459 break;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700460 case WRONG_PARAMETERS:
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800461 log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700462 break;
463 case ADDITION_ERROR:
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800464 log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700465 break;
466 default:
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800467 log.info("Pseudowire with {} succesfully deployed!", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700468 break;
Pier Ventre42287df2016-11-09 14:17:26 -0800469 }
Pier Ventre42287df2016-11-09 14:17:26 -0800470 }
Pier Ventref34966c2016-11-07 16:21:04 -0800471 }
472
473 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -0800474 * Processes PWaaS Config updated event.
Pier Ventref34966c2016-11-07 16:21:04 -0800475 *
476 * @param event network config updated event
477 */
478 public void processPwaasConfigUpdated(NetworkConfigEvent event) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800479 checkArgument(event.config().isPresent(),
480 "Config is not presented in PwaasConfigUpdated event {}", event);
481 checkArgument(event.prevConfig().isPresent(),
482 "PrevConfig is not presented in PwaasConfigUpdated event {}", event);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700483
484 log.info("Pseudowire configuration updated.");
485
Pier Ventre42287df2016-11-09 14:17:26 -0800486 // We retrieve the old pseudo wires.
487 PwaasConfig prevConfig = (PwaasConfig) event.prevConfig().get();
488 Set<Long> prevPws = prevConfig.getPwIds();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700489
Pier Ventre42287df2016-11-09 14:17:26 -0800490 // We retrieve the new pseudo wires.
Pier Ventref34966c2016-11-07 16:21:04 -0800491 PwaasConfig config = (PwaasConfig) event.config().get();
Pier Ventre42287df2016-11-09 14:17:26 -0800492 Set<Long> newPws = config.getPwIds();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700493
Pier Ventre42287df2016-11-09 14:17:26 -0800494 // We compute the pseudo wires to update.
495 Set<Long> updPws = newPws.stream()
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700496 .filter(tunnelId -> prevPws.contains(tunnelId)
497 && !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
Pier Ventre42287df2016-11-09 14:17:26 -0800498 .collect(Collectors.toSet());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700499
Pier Ventre42287df2016-11-09 14:17:26 -0800500 // The pseudo wires to remove.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700501 Set<Long> rmvPWs = prevPws.stream()
502 .filter(tunnelId -> !newPws.contains(tunnelId)).collect(Collectors.toSet());
503
504 Set<DefaultL2TunnelDescription> pwToRemove = rmvPWs.stream()
Pier Ventre42287df2016-11-09 14:17:26 -0800505 .map(prevConfig::getPwDescription)
506 .collect(Collectors.toSet());
507 tearDown(pwToRemove);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700508
Pier Ventre42287df2016-11-09 14:17:26 -0800509 // The pseudo wires to add.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700510 Set<Long> addedPWs = newPws.stream()
Pier Ventre42287df2016-11-09 14:17:26 -0800511 .filter(tunnelId -> !prevPws.contains(tunnelId))
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700512 .collect(Collectors.toSet());
513 Set<DefaultL2TunnelDescription> pwToAdd = addedPWs.stream()
Pier Ventre42287df2016-11-09 14:17:26 -0800514 .map(config::getPwDescription)
515 .collect(Collectors.toSet());
516 deploy(pwToAdd);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700517
518
Pier Ventre42287df2016-11-09 14:17:26 -0800519 // The pseudo wires to update.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700520 updPws.forEach(tunnelId -> updatePw(prevConfig.getPwDescription(tunnelId),
521 config.getPwDescription(tunnelId)));
522
523 log.info("Pseudowires removed : {}, Pseudowires updated : {}, Pseudowires added : {}", rmvPWs,
524 updPws, addedPWs);
Pier Ventre42287df2016-11-09 14:17:26 -0800525 }
526
527 /**
528 * Helper function to update a pw.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700529 * <p>
530 * Called upon configuration changes that update existing pseudowires and
531 * when links fail. Checking of mastership for CP1 is mandatory because it is
532 * called in multiple instances for both cases.
533 * <p>
534 * Meant to call asynchronously for various events, thus this call can not block and need
535 * to perform asynchronous operations.
536 * <p>
537 * For this reason error checking is omitted.
Pier Ventre42287df2016-11-09 14:17:26 -0800538 *
539 * @param oldPw the pseudo wire to remove
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700540 * @param newPw the pseudo wire to add
Pier Ventre42287df2016-11-09 14:17:26 -0800541 */
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800542 private void updatePw(DefaultL2TunnelDescription oldPw,
543 DefaultL2TunnelDescription newPw) {
544 ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
Pier Ventre42287df2016-11-09 14:17:26 -0800545 long tunnelId = oldPw.l2Tunnel().tunnelId();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700546
547 // only the master of CP1 will update this pseudowire
548 if (!srManager.isMasterOf(oldPw.l2TunnelPolicy().cP1())) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800549 log.debug("Not the master of {}. Ignore pseudo wire update id={}", oldCp1, tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700550 return;
551 }
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800552 // only determine if the new pseudowire is leaf-spine, because
553 // removal process is the same for both leaf-leaf and leaf-spine
554 // pws.
555 boolean newPwSpine;
556 try {
557 newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
558 !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
559 } catch (DeviceConfigNotFoundException e) {
560 // if exception is caught treat the newpw as leaf-leaf
561 newPwSpine = false;
562 }
563
564 // copy the variable here because we need to
565 // use it in lambda thus it needs to be final
566 boolean finalNewPwSpine = newPwSpine;
567
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700568
569 log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
570
Pier Ventre42287df2016-11-09 14:17:26 -0800571 // The async tasks to orchestrate the next and
572 // forwarding update.
Pier Ventre42287df2016-11-09 14:17:26 -0800573 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
574 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
Pier Ventre70d53ba2016-11-17 22:26:29 -0800575 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
576 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
577 CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
578 CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
Pier Ventre42287df2016-11-09 14:17:26 -0800579
Pier Ventre42287df2016-11-09 14:17:26 -0800580 // First we remove both policy.
Pier Ventre42287df2016-11-09 14:17:26 -0800581 log.debug("Start deleting fwd policy for {}", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700582
583 // first delete all information from our stores
584 // we can not do it asynchronously
585 l2PolicyStore.remove(Long.toString(tunnelId));
586 l2TunnelStore.remove(Long.toString(tunnelId));
587
588 VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
589 oldPw.l2TunnelPolicy().cP1InnerTag(),
590 oldPw.l2TunnelPolicy().cP2OuterTag(),
591 oldPw.l2TunnelPolicy().cP2InnerTag());
592 deletePolicy(tunnelId,
593 oldPw.l2TunnelPolicy().cP1(),
594 oldPw.l2TunnelPolicy().cP1InnerTag(),
595 oldPw.l2TunnelPolicy().cP1OuterTag(),
596 egressVlan,
597 fwdInitNextFuture,
598 FWD);
599
600 log.debug("Update process : Start deleting rev policy for {}", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700601 egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP2OuterTag(),
602 oldPw.l2TunnelPolicy().cP2InnerTag(),
603 oldPw.l2TunnelPolicy().cP1OuterTag(),
604 oldPw.l2TunnelPolicy().cP1InnerTag());
605 deletePolicy(tunnelId,
606 oldPw.l2TunnelPolicy().cP2(),
607 oldPw.l2TunnelPolicy().cP2InnerTag(),
608 oldPw.l2TunnelPolicy().cP2OuterTag(),
609 egressVlan, revInitNextFuture,
610 REV);
611
Pier Ventre42287df2016-11-09 14:17:26 -0800612 // Finally we remove both the tunnels.
613 fwdInitNextFuture.thenAcceptAsync(status -> {
614 if (status == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700615 log.debug("Update process : Fwd policy removed. " +
616 "Now remove fwd {} for {}", INITIATION, tunnelId);
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800617 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP1(), fwdTermNextFuture, FWD);
Pier Ventre42287df2016-11-09 14:17:26 -0800618 }
619 });
620 revInitNextFuture.thenAcceptAsync(status -> {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700621 if (status == null) {
622 log.debug("Update process : Rev policy removed. " +
623 "Now remove rev {} for {}", INITIATION, tunnelId);
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800624 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP2(), revTermNextFuture, REV);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700625 }
Pier Ventre42287df2016-11-09 14:17:26 -0800626 });
Pier Ventre70d53ba2016-11-17 22:26:29 -0800627 fwdTermNextFuture.thenAcceptAsync(status -> {
628 if (status == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700629 log.debug("Update process : Fwd {} removed. " +
630 "Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800631 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP2(), fwdPwFuture, FWD);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800632 }
633 });
634 revTermNextFuture.thenAcceptAsync(status -> {
635 if (status == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700636 log.debug("Update process : Rev {} removed. " +
637 "Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800638 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP1(), revPwFuture, REV);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800639 }
640 });
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700641
642 // get path here, need to use the same for fwd and rev direction
643 List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
644 newPw.l2TunnelPolicy().cP2());
645 if (path == null) {
646 log.info("Deploying process : " +
647 "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
648 return;
649 }
650
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800651 Link fwdNextHop, revNextHop;
652 if (!finalNewPwSpine) {
653 if (path.size() != 2) {
654 log.info("Update process : Error, path between two leafs should have size of 2, for pseudowire {}",
655 newPw.l2Tunnel().tunnelId());
656 return;
657 }
658
659 fwdNextHop = path.get(0);
660 revNextHop = reverseLink(path.get(1));
661 } else {
662 if (path.size() != 1) {
Charles Chanf7ce5092018-01-06 17:57:27 -0800663 log.info("Update process : Error, path between leaf spine should equal to 1, for pseudowire {}",
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800664 newPw.l2Tunnel().tunnelId());
665 return;
666 }
667
668 fwdNextHop = path.get(0);
669 revNextHop = reverseLink(path.get(0));
670 }
671
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700672 newPw.l2Tunnel().setPath(path);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700673
674 // At the end we install the updated PW.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800675 fwdPwFuture.thenAcceptAsync(status -> {
Pier Ventre42287df2016-11-09 14:17:26 -0800676 if (status == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700677
678 // Upgrade stores and book keeping information, need to move this here
679 // cause this call is asynchronous.
680 l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
681 l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
682
683 log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800684 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP1(),
685 newPw.l2TunnelPolicy().cP2(), FWD,
686 fwdNextHop, finalNewPwSpine);
Pier Ventre42287df2016-11-09 14:17:26 -0800687 if (lamdaResult != SUCCESS) {
688 return;
689 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700690
691 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800692 newPw.l2TunnelPolicy().cP1InnerTag(),
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700693 newPw.l2TunnelPolicy().cP2OuterTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800694 newPw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700695
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800696 lamdaResult = deployPolicy(tunnelId, newPw.l2TunnelPolicy().cP1(),
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700697 newPw.l2TunnelPolicy().cP1InnerTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800698 newPw.l2TunnelPolicy().cP1OuterTag(),
699 egressVlanId, lamdaResult.nextId);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800700 if (lamdaResult != SUCCESS) {
701 return;
702 }
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800703 deployPseudoWireTerm(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP2(),
704 newPw.l2TunnelPolicy().cP2OuterTag(), FWD, finalNewPwSpine);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800705
706 }
707 });
708 revPwFuture.thenAcceptAsync(status -> {
709 if (status == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700710 log.debug("Update process : Deploying new rev pw for {}", tunnelId);
711 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
712 newPw.l2TunnelPolicy().cP2(),
713 newPw.l2TunnelPolicy().cP1(),
714 REV,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800715 revNextHop, finalNewPwSpine);
Pier Ventre42287df2016-11-09 14:17:26 -0800716 if (lamdaResult != SUCCESS) {
717 return;
718 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700719
720 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
721 newPw.l2TunnelPolicy().cP2InnerTag(),
722 newPw.l2TunnelPolicy().cP1OuterTag(),
723 newPw.l2TunnelPolicy().cP1InnerTag());
724 lamdaResult = deployPolicy(tunnelId,
725 newPw.l2TunnelPolicy().cP2(),
726 newPw.l2TunnelPolicy().cP2InnerTag(),
727 newPw.l2TunnelPolicy().cP2OuterTag(),
728 egressVlanId,
729 lamdaResult.nextId);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800730 if (lamdaResult != SUCCESS) {
731 return;
732 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700733 deployPseudoWireTerm(newPw.l2Tunnel(),
734 newPw.l2TunnelPolicy().cP1(),
735 newPw.l2TunnelPolicy().cP1OuterTag(),
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800736 REV, finalNewPwSpine);
Pier Ventre42287df2016-11-09 14:17:26 -0800737 }
Pier Ventref34966c2016-11-07 16:21:04 -0800738 });
739 }
740
741 /**
742 * Processes Pwaas Config removed event.
743 *
744 * @param event network config removed event
745 */
746 public void processPwaasConfigRemoved(NetworkConfigEvent event) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800747 checkArgument(event.prevConfig().isPresent(),
748 "PrevConfig is not presented in PwaasConfigRemoved event {}", event);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700749
750 log.info("Network event : Pseudowire configuration removed!");
Pier Ventre42287df2016-11-09 14:17:26 -0800751 PwaasConfig config = (PwaasConfig) event.prevConfig().get();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700752
753 Set<DefaultL2TunnelDescription> pwToRemove = config
754 .getPwIds()
Pier Ventre42287df2016-11-09 14:17:26 -0800755 .stream()
756 .map(config::getPwDescription)
757 .collect(Collectors.toSet());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700758
Pier Ventre42287df2016-11-09 14:17:26 -0800759 // We teardown all the pseudo wire deployed
760 tearDown(pwToRemove);
Pier Ventref34966c2016-11-07 16:21:04 -0800761 }
Pier Ventre42287df2016-11-09 14:17:26 -0800762
763 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700764 * Helper function for removing a single pseudowire.
765 * <p>
766 * No mastership of CP1 is checked, because it can be called from
767 * the CLI for removal of pseudowires.
Pier Ventre42287df2016-11-09 14:17:26 -0800768 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700769 * @param l2TunnelId the id of the pseudowire to tear down
770 * @return Returns SUCCESS if no error is obeserved or an appropriate
771 * error on a failure
Pier Ventre42287df2016-11-09 14:17:26 -0800772 */
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800773 private Result tearDownPseudowire(long l2TunnelId) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700774
775 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
776 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
777
778 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
779 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
780
781 if (l2TunnelId == 0) {
782 log.warn("Removal process : Tunnel id cannot be 0");
783 return Result.WRONG_PARAMETERS;
Pier Ventre42287df2016-11-09 14:17:26 -0800784 }
785
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700786 // check existence of tunnels/policy in the store, if one is missing abort!
787 Versioned<DefaultL2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
788 Versioned<DefaultL2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
789 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
790 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
791 return Result.REMOVAL_ERROR;
792 }
793
794 DefaultL2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
795 l2TunnelPolicyVersioned.value());
796
797 // remove the tunnels and the policies from the store
798 l2PolicyStore.remove(Long.toString(l2TunnelId));
799 l2TunnelStore.remove(Long.toString(l2TunnelId));
800
801 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
802
803 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
804 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
805 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
806 pwToRemove.l2TunnelPolicy().cP2InnerTag());
807 deletePolicy(l2TunnelId,
808 pwToRemove.l2TunnelPolicy().cP1(),
809 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
810 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
811 egressVlan,
812 fwdInitNextFuture,
813 FWD);
814
815 fwdInitNextFuture.thenAcceptAsync(status -> {
816 if (status == null) {
817 // Finally we will tear down the pseudo wire.
818 tearDownPseudoWireInit(l2TunnelId,
819 pwToRemove.l2TunnelPolicy().cP1(),
820 fwdTermNextFuture,
821 FWD);
822 }
823 });
824
825 fwdTermNextFuture.thenAcceptAsync(status -> {
826 if (status == null) {
827 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
828 pwToRemove.l2TunnelPolicy().cP2(),
829 null,
830 FWD);
831 }
832 });
833
834 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
835
836 egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
837 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
838 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
839 pwToRemove.l2TunnelPolicy().cP1InnerTag());
840
841 // We do the same operations on the reverse side.
842 deletePolicy(l2TunnelId,
843 pwToRemove.l2TunnelPolicy().cP2(),
844 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
845 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
846 egressVlan,
847 revInitNextFuture,
848 REV);
849
850 revInitNextFuture.thenAcceptAsync(status -> {
851 if (status == null) {
852 tearDownPseudoWireInit(l2TunnelId,
853 pwToRemove.l2TunnelPolicy().cP2(),
854 revTermNextFuture,
855 REV);
856 }
857 });
858
859 revTermNextFuture.thenAcceptAsync(status -> {
860 if (status == null) {
861 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
862 pwToRemove.l2TunnelPolicy().cP1(),
863 null,
864 REV);
865 }
866 });
867
868 return Result.SUCCESS;
Pier Ventre42287df2016-11-09 14:17:26 -0800869 }
870
871 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700872 * Helper function to handle the pw removal.
873 * <p>
874 * This method checks for the mastership of the device because it is
875 * used only from network configuration updates, thus we only want
876 * one instance only to program each pseudowire.
Pier Ventre42287df2016-11-09 14:17:26 -0800877 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700878 * @param pwToRemove the pseudo wires to remove
Pier Ventre42287df2016-11-09 14:17:26 -0800879 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700880 public void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
Pier Ventre42287df2016-11-09 14:17:26 -0800881
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700882 Result result;
883
884 // We remove all the pw in the configuration file.
885 for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800886 ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
887 ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
888 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700889
890 // only the master of CP1 will program this pseudowire
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800891 if (!srManager.isMasterOf(cp1)) {
892 log.debug("Not the master of {}. Ignore pseudo wire removal id={}", cp1, tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700893 continue;
894 }
895
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800896 // no need to differentiate here between leaf-leaf and leaf-spine, because
897 // the only change is in the groups, which we do not remove either way
898 log.info("Removing pseudowire {}", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700899
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800900 result = tearDownPseudowire(tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700901 switch (result) {
902 case WRONG_PARAMETERS:
903 log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800904 tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700905 break;
906 case REMOVAL_ERROR:
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800907 log.warn("Error in pseudowire removal with tunnel id {}!", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700908 break;
909 default:
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800910 log.warn("Pseudowire with tunnel id {} was removed successfully", tunnelId);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700911 }
912 }
Pier Ventre42287df2016-11-09 14:17:26 -0800913 }
914
915 /**
Pier Ventre42287df2016-11-09 14:17:26 -0800916 * Handles the policy establishment which consists in
917 * create the filtering and forwarding objectives related
918 * to the initiation and termination.
919 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700920 * @param tunnelId the tunnel id
921 * @param ingress the ingress point
Pier Ventre42287df2016-11-09 14:17:26 -0800922 * @param ingressInner the ingress inner tag
923 * @param ingressOuter the ingress outer tag
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700924 * @param nextId the next objective id
925 * @param egressVlan Vlan-id to set, depends on ingress vlan
926 * combinations. For example, if pw is double tagged
927 * then this is the value of the outer vlan, if single
928 * tagged then it is the new value of the single tag.
929 * Should be None for untagged traffic.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800930 * @return the result of the operation
Pier Ventre42287df2016-11-09 14:17:26 -0800931 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700932 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
933 VlanId ingressOuter, VlanId egressVlan, int nextId) {
934
Pier Ventre70d53ba2016-11-17 22:26:29 -0800935 List<Objective> objectives = Lists.newArrayList();
Pier Ventre42287df2016-11-09 14:17:26 -0800936 // We create the forwarding objective for supporting
937 // the l2 tunnel.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700938 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
Pier Ventre42287df2016-11-09 14:17:26 -0800939 // We create and add objective context.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700940 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
941 log.debug("FwdObj for tunnel {} populated", tunnelId),
942 (objective, error) ->
943 log.warn("Failed to populate fwdrObj " +
944 "for tunnel {}", tunnelId, error));
Pier Ventre42287df2016-11-09 14:17:26 -0800945 objectives.add(fwdBuilder.add(context));
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700946
Pier Ventre42287df2016-11-09 14:17:26 -0800947 // We create the filtering objective to define the
948 // permit traffic in the switch
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700949 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
950
Pier Ventre42287df2016-11-09 14:17:26 -0800951 // We add the metadata.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700952 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
953 .builder()
954 .setTunnelId(tunnelId)
955 .setVlanId(egressVlan);
Pier Ventre42287df2016-11-09 14:17:26 -0800956 filtBuilder.withMeta(treatment.build());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700957
Pier Ventre42287df2016-11-09 14:17:26 -0800958 // We create and add objective context.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700959 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
960 (objective, error) -> log.warn("Failed to populate filterObj for " +
961 "tunnel {}", tunnelId, error));
Pier Ventre42287df2016-11-09 14:17:26 -0800962 objectives.add(filtBuilder.add(context));
963
964 for (Objective objective : objectives) {
965 if (objective instanceof ForwardingObjective) {
966 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
Pier Ventre70d53ba2016-11-17 22:26:29 -0800967 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
Pier Ventre42287df2016-11-09 14:17:26 -0800968 } else {
969 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
970 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
971 }
972 }
973 return SUCCESS;
974 }
975
976 /**
Pier Ventre42287df2016-11-09 14:17:26 -0800977 * Handles the tunnel establishment which consists in
Pier Ventre70d53ba2016-11-17 22:26:29 -0800978 * create the next objectives related to the initiation.
Pier Ventre42287df2016-11-09 14:17:26 -0800979 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700980 * @param l2Tunnel the tunnel to deploy
981 * @param ingress the ingress connect point
982 * @param egress the egress connect point
Pier Ventre42287df2016-11-09 14:17:26 -0800983 * @param direction the direction of the pw
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800984 * @param spinePw if the pseudowire involves a spine switch
Pier Ventre70d53ba2016-11-17 22:26:29 -0800985 * @return the result of the operation
Pier Ventre42287df2016-11-09 14:17:26 -0800986 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700987 private Result deployPseudoWireInit(DefaultL2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -0800988 ConnectPoint egress, Direction direction, Link nextHop, boolean spinePw) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700989
Pier Ventre42287df2016-11-09 14:17:26 -0800990 if (nextHop == null) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700991 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
Pier Ventre42287df2016-11-09 14:17:26 -0800992 return WRONG_PARAMETERS;
993 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700994
Pier Ventre42287df2016-11-09 14:17:26 -0800995 // We create the next objective without the metadata
996 // context and id. We check if it already exists in the
Pier Ventre70d53ba2016-11-17 22:26:29 -0800997 // store. If not we store as it is in the store.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700998 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
999 nextHop.src(),
1000 nextHop.dst(),
1001 l2Tunnel,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001002 egress.deviceId(),
1003 spinePw);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001004
Pier Ventre42287df2016-11-09 14:17:26 -08001005 if (nextObjectiveBuilder == null) {
1006 return INTERNAL_ERROR;
1007 }
1008 // We set the metadata. We will use this metadata
1009 // to inform the driver we are doing a l2 tunnel.
1010 TrafficSelector metadata = DefaultTrafficSelector
1011 .builder()
1012 .matchTunnelId(l2Tunnel.tunnelId())
1013 .build();
1014 nextObjectiveBuilder.withMeta(metadata);
Pier Ventre70d53ba2016-11-17 22:26:29 -08001015 int nextId = srManager.flowObjectiveService.allocateNextId();
Pier Ventre42287df2016-11-09 14:17:26 -08001016 if (nextId < 0) {
1017 log.warn("Not able to allocate a next id for initiation");
1018 return INTERNAL_ERROR;
1019 }
1020 nextObjectiveBuilder.withId(nextId);
1021 String key = generateKey(l2Tunnel.tunnelId(), direction);
1022 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001023 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1024 log.debug("Initiation l2 tunnel rule " +
1025 "for {} populated",
1026 l2Tunnel.tunnelId()),
1027 (objective, error) ->
1028 log.warn("Failed to populate Initiation " +
1029 "l2 tunnel rule for {}: {}",
1030 l2Tunnel.tunnelId(), error));
Pier Ventre70d53ba2016-11-17 22:26:29 -08001031 NextObjective nextObjective = nextObjectiveBuilder.add(context);
Pier Ventre42287df2016-11-09 14:17:26 -08001032 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
1033 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001034 l2Tunnel.tunnelId(), nextObjective.id());
Pier Ventre70d53ba2016-11-17 22:26:29 -08001035 Result result = SUCCESS;
Pier Ventre42287df2016-11-09 14:17:26 -08001036 result.nextId = nextObjective.id();
1037 return result;
1038 }
1039
1040 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001041 * Handles the tunnel termination, which consists in the creation
1042 * of a forwarding objective and a next objective.
1043 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001044 * @param l2Tunnel the tunnel to terminate
1045 * @param egress the egress point
Pier Ventre70d53ba2016-11-17 22:26:29 -08001046 * @param egressVlan the expected vlan at egress
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001047 * @param direction the direction
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001048 * @param spinePw if the pseudowire involves a spine switch
Pier Ventre70d53ba2016-11-17 22:26:29 -08001049 * @return the result of the operation
1050 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001051 private Result deployPseudoWireTerm(DefaultL2Tunnel l2Tunnel, ConnectPoint egress,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001052 VlanId egressVlan, Direction direction, boolean spinePw) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001053
Pier Ventre70d53ba2016-11-17 22:26:29 -08001054 // We create the group relative to the termination.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001055 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001056 null, egress.deviceId(),
1057 spinePw);
Pier Ventre70d53ba2016-11-17 22:26:29 -08001058 if (nextObjectiveBuilder == null) {
1059 return INTERNAL_ERROR;
1060 }
1061 TrafficSelector metadata = DefaultTrafficSelector
1062 .builder()
1063 .matchVlanId(egressVlan)
1064 .build();
1065 nextObjectiveBuilder.withMeta(metadata);
1066 int nextId = srManager.flowObjectiveService.allocateNextId();
1067 if (nextId < 0) {
1068 log.warn("Not able to allocate a next id for initiation");
1069 return INTERNAL_ERROR;
1070 }
1071 nextObjectiveBuilder.withId(nextId);
1072 String key = generateKey(l2Tunnel.tunnelId(), direction);
1073 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001074 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1075 "for {} populated",
1076 l2Tunnel.tunnelId()),
1077 (objective, error) -> log.warn("Failed to populate " +
1078 "termination l2 tunnel " +
1079 "rule for {}: {}",
1080 l2Tunnel.tunnelId(),
1081 error));
Pier Ventre70d53ba2016-11-17 22:26:29 -08001082 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1083 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1084 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001085 l2Tunnel.tunnelId(), nextObjective.id());
1086
Pier Ventre70d53ba2016-11-17 22:26:29 -08001087 // We create the flow relative to the termination.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001088 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1089 egress.port(), nextObjective.id());
1090 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1091 l2Tunnel.tunnelId()),
1092 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1093 " for tunnel termination {}",
1094 l2Tunnel.tunnelId(), error));
Pier Ventre70d53ba2016-11-17 22:26:29 -08001095 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001096 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1097 nextId, l2Tunnel.tunnelId());
Pier Ventre70d53ba2016-11-17 22:26:29 -08001098 return SUCCESS;
1099
1100 }
1101
1102 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001103 * Creates the filtering objective according to a given policy.
Pier Ventre42287df2016-11-09 14:17:26 -08001104 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001105 * @param inPort the in port
Pier Ventre42287df2016-11-09 14:17:26 -08001106 * @param innerTag the inner vlan tag
1107 * @param outerTag the outer vlan tag
1108 * @return the filtering objective
1109 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001110 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1111
1112 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1113 return DefaultFilteringObjective
1114 .builder()
Pier Ventre42287df2016-11-09 14:17:26 -08001115 .withKey(Criteria.matchInPort(inPort))
1116 .addCondition(Criteria.matchInnerVlanId(innerTag))
1117 .addCondition(Criteria.matchVlanId(outerTag))
1118 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1119 .permit()
Ray Milkeye4afdb52017-04-05 09:42:04 -07001120 .fromApp(srManager.appId());
Pier Ventre42287df2016-11-09 14:17:26 -08001121 }
1122
1123 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001124 * Creates the forwarding objective for the termination.
Pier Ventre42287df2016-11-09 14:17:26 -08001125 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001126 * @param pwLabel the pseudo wire label
1127 * @param tunnelId the tunnel id
Pier Ventre70d53ba2016-11-17 22:26:29 -08001128 * @param egressPort the egress port
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001129 * @param nextId the next step
Pier Ventre70d53ba2016-11-17 22:26:29 -08001130 * @return the forwarding objective to support the termination
Pier Ventre42287df2016-11-09 14:17:26 -08001131 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001132 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1133 PortNumber egressPort, int nextId) {
1134
1135 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1136 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
Pier Ventre70d53ba2016-11-17 22:26:29 -08001137 // The flow has to match on the pw label and bos
1138 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1139 trafficSelector.matchMplsLabel(pwLabel);
1140 trafficSelector.matchMplsBos(true);
1141 // The flow has to decrement ttl, restore ttl in
1142 // pop mpls, set tunnel id and port.
1143 trafficTreatment.decMplsTtl();
1144 trafficTreatment.copyTtlIn();
1145 trafficTreatment.popMpls();
1146 trafficTreatment.setTunnelId(tunnelId);
1147 trafficTreatment.setOutput(egressPort);
1148
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001149 return DefaultForwardingObjective
1150 .builder()
Ray Milkeye4afdb52017-04-05 09:42:04 -07001151 .fromApp(srManager.appId())
Pier Ventre70d53ba2016-11-17 22:26:29 -08001152 .makePermanent()
1153 .nextStep(nextId)
1154 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1155 .withSelector(trafficSelector.build())
1156 .withTreatment(trafficTreatment.build())
1157 .withFlag(VERSATILE);
1158 }
1159
1160 /**
1161 * Creates the forwarding objective for the initiation.
1162 *
1163 * @param tunnelId the tunnel id
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001164 * @param inPort the input port
1165 * @param nextId the next step
Pier Ventre70d53ba2016-11-17 22:26:29 -08001166 * @return the forwarding objective to support the initiation.
1167 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001168 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1169
1170 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1171
Pier Ventre70d53ba2016-11-17 22:26:29 -08001172 // The flow has to match on the mpls logical
1173 // port and the tunnel id.
1174 trafficSelector.matchTunnelId(tunnelId);
1175 trafficSelector.matchInPort(inPort);
1176
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001177 return DefaultForwardingObjective
1178 .builder()
Ray Milkeye4afdb52017-04-05 09:42:04 -07001179 .fromApp(srManager.appId())
Pier Ventre70d53ba2016-11-17 22:26:29 -08001180 .makePermanent()
1181 .nextStep(nextId)
1182 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1183 .withSelector(trafficSelector.build())
1184 .withFlag(VERSATILE);
Pier Ventre42287df2016-11-09 14:17:26 -08001185
1186 }
1187
1188 /**
1189 * Creates the next objective according to a given
1190 * pipeline. We don't set the next id and we don't
1191 * create the final meta to check if we are re-using
1192 * the same next objective for different tunnels.
1193 *
1194 * @param pipeline the pipeline to support
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001195 * @param srcCp the source port
1196 * @param dstCp the destination port
Pier Ventre42287df2016-11-09 14:17:26 -08001197 * @param l2Tunnel the tunnel to support
1198 * @param egressId the egress device id
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001199 * @param spinePw if the pw involves a spine switch
Pier Ventre42287df2016-11-09 14:17:26 -08001200 * @return the next objective to support the pipeline
1201 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001202 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1203 ConnectPoint dstCp, DefaultL2Tunnel l2Tunnel,
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001204 DeviceId egressId, boolean spinePw) {
Pier Ventre42287df2016-11-09 14:17:26 -08001205 NextObjective.Builder nextObjBuilder;
1206 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1207 if (pipeline == INITIATION) {
1208 nextObjBuilder = DefaultNextObjective
1209 .builder()
1210 .withType(NextObjective.Type.SIMPLE)
Ray Milkeye4afdb52017-04-05 09:42:04 -07001211 .fromApp(srManager.appId());
Pier Ventre42287df2016-11-09 14:17:26 -08001212 // The pw label is the bottom of stack. It has to
1213 // be different -1.
1214 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1215 log.warn("Pw label not configured");
1216 return null;
1217 }
1218 treatmentBuilder.pushMpls();
1219 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1220 treatmentBuilder.setMplsBos(true);
1221 treatmentBuilder.copyTtlOut();
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001222
Pier Ventre42287df2016-11-09 14:17:26 -08001223 // If the inter-co label is present we have to set the label.
1224 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1225 treatmentBuilder.pushMpls();
1226 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1227 treatmentBuilder.setMplsBos(false);
1228 treatmentBuilder.copyTtlOut();
1229 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001230
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001231 // if pw is leaf-to-leaf we need to
1232 // add the routing label also
1233 if (!spinePw) {
1234 // We retrieve the sr label from the config
1235 // specific for pseudowire traffic
1236 // using the egress leaf device id.
1237 MplsLabel srLabel;
1238 try {
1239 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1240
1241 } catch (DeviceConfigNotFoundException e) {
1242 log.warn("Sr label for pw traffic not configured");
1243 return null;
1244 }
1245
1246 treatmentBuilder.pushMpls();
1247 treatmentBuilder.setMpls(srLabel);
1248 treatmentBuilder.setMplsBos(false);
1249 treatmentBuilder.copyTtlOut();
Pier Ventre42287df2016-11-09 14:17:26 -08001250 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001251
Pier Ventre42287df2016-11-09 14:17:26 -08001252 // We have to rewrite the src and dst mac address.
1253 MacAddress ingressMac;
1254 try {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001255 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
Pier Ventre42287df2016-11-09 14:17:26 -08001256 } catch (DeviceConfigNotFoundException e) {
1257 log.warn("Was not able to find the ingress mac");
1258 return null;
1259 }
1260 treatmentBuilder.setEthSrc(ingressMac);
1261 MacAddress neighborMac;
1262 try {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001263 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
Pier Ventre42287df2016-11-09 14:17:26 -08001264 } catch (DeviceConfigNotFoundException e) {
1265 log.warn("Was not able to find the neighbor mac");
1266 return null;
1267 }
1268 treatmentBuilder.setEthDst(neighborMac);
1269 } else {
Pier Ventre70d53ba2016-11-17 22:26:29 -08001270 // We create the next objective which
1271 // will be a simple l2 group.
Pier Ventre42287df2016-11-09 14:17:26 -08001272 nextObjBuilder = DefaultNextObjective
1273 .builder()
1274 .withType(NextObjective.Type.SIMPLE)
Ray Milkeye4afdb52017-04-05 09:42:04 -07001275 .fromApp(srManager.appId());
Pier Ventre42287df2016-11-09 14:17:26 -08001276 }
Pier Ventre70d53ba2016-11-17 22:26:29 -08001277 treatmentBuilder.setOutput(srcCp.port());
Pier Ventre42287df2016-11-09 14:17:26 -08001278 nextObjBuilder.addTreatment(treatmentBuilder.build());
1279 return nextObjBuilder;
1280 }
1281
1282 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001283 * Reverses a link.
Pier Ventre42287df2016-11-09 14:17:26 -08001284 *
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001285 * @param link link to be reversed
1286 * @return the reversed link
Pier Ventre42287df2016-11-09 14:17:26 -08001287 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001288 private Link reverseLink(Link link) {
1289
1290 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1291
1292 linkBuilder.src(link.dst());
1293 linkBuilder.dst(link.src());
1294 linkBuilder.type(link.type());
1295 linkBuilder.providerId(link.providerId());
1296
1297 return linkBuilder.build();
1298 }
1299
1300 /**
1301 * Returns the path betwwen two connect points.
1302 *
Andreas Pantelopoulos20474e02017-12-20 18:04:27 -08001303 * @param srcCp source connect point
1304 * @param dstCp destination connect point
1305 * @return the path
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001306 */
1307 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
1308
1309 /* We retrieve a set of disjoint paths.
1310 * We perform that in case of a link failure, what happens
1311 * if the PathService gets the link notification AFTER us and
1312 * has not updated the paths?
1313 */
1314 Set<DisjointPath> paths = srManager
1315 .pathService
1316 .getDisjointPaths(srcCp.elementId(), dstCp.elementId());
1317
1318 // We randomly pick a path.
Pier Ventre42287df2016-11-09 14:17:26 -08001319 if (paths.isEmpty()) {
1320 return null;
1321 }
1322 int size = paths.size();
1323 int index = RandomUtils.nextInt(0, size);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001324
1325 return Iterables.get(paths, index).links();
Pier Ventre42287df2016-11-09 14:17:26 -08001326 }
1327
1328 /**
Pier Ventre42287df2016-11-09 14:17:26 -08001329 * Deletes a given policy using the parameter supplied.
1330 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001331 * @param tunnelId the tunnel id
1332 * @param ingress the ingress point
Pier Ventre42287df2016-11-09 14:17:26 -08001333 * @param ingressInner the ingress inner vlan id
1334 * @param ingressOuter the ingress outer vlan id
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001335 * @param future to perform the async operation
1336 * @param direction the direction: forward or reverse
Pier Ventre42287df2016-11-09 14:17:26 -08001337 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001338 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1339 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1340
Pier Ventre70d53ba2016-11-17 22:26:29 -08001341 String key = generateKey(tunnelId, direction);
1342 if (!l2InitiationNextObjStore.containsKey(key)) {
1343 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1344 if (future != null) {
1345 future.complete(null);
1346 }
1347 return;
1348 }
1349 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1350 int nextId = nextObjective.id();
1351 List<Objective> objectives = Lists.newArrayList();
Pier Ventre42287df2016-11-09 14:17:26 -08001352 // We create the forwarding objective.
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001353 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
Pier Ventre42287df2016-11-09 14:17:26 -08001354 ObjectiveContext context = new ObjectiveContext() {
1355 @Override
1356 public void onSuccess(Objective objective) {
Pier Ventre70d53ba2016-11-17 22:26:29 -08001357 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1358 if (future != null) {
1359 future.complete(null);
Pier Ventre42287df2016-11-09 14:17:26 -08001360 }
1361 }
1362
1363 @Override
1364 public void onError(Objective objective, ObjectiveError error) {
Pier Ventre70d53ba2016-11-17 22:26:29 -08001365 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1366 if (future != null) {
1367 future.complete(error);
Pier Ventre42287df2016-11-09 14:17:26 -08001368 }
1369 }
1370 };
1371 objectives.add(fwdBuilder.remove(context));
1372 // We create the filtering objective to define the
1373 // permit traffic in the switch
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001374 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1375 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1376 .builder()
1377 .setTunnelId(tunnelId)
1378 .setVlanId(egressVlan);
Pier Ventre42287df2016-11-09 14:17:26 -08001379 filtBuilder.withMeta(treatment.build());
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001380 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1381 (objective, error) ->
1382 log.warn("Failed to revoke filterObj for policy {}",
1383 tunnelId, error));
Pier Ventre42287df2016-11-09 14:17:26 -08001384 objectives.add(filtBuilder.remove(context));
1385
1386 for (Objective objective : objectives) {
1387 if (objective instanceof ForwardingObjective) {
1388 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1389 } else {
1390 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1391 }
1392 }
1393 }
1394
1395 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001396 * Deletes the pseudo wire initiation.
Pier Ventre42287df2016-11-09 14:17:26 -08001397 *
Pier Ventre70d53ba2016-11-17 22:26:29 -08001398 * @param l2TunnelId the tunnel id
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001399 * @param ingress the ingress connect point
1400 * @param future to perform an async operation
1401 * @param direction the direction: reverse of forward
Pier Ventre42287df2016-11-09 14:17:26 -08001402 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001403 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1404 CompletableFuture<ObjectiveError> future, Direction direction) {
1405
Pier Ventre70d53ba2016-11-17 22:26:29 -08001406 String key = generateKey(l2TunnelId, direction);
Pier Ventre70d53ba2016-11-17 22:26:29 -08001407 if (!l2InitiationNextObjStore.containsKey(key)) {
1408 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1409 if (future != null) {
1410 future.complete(null);
1411 }
1412 return;
1413 }
1414 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001415 // un-comment in case you want to delete groups used by the pw
1416 // however, this will break the update of pseudowires cause the L2 interface group can
1417 // not be deleted (it is referenced by other groups)
1418 /*
Pier Ventre42287df2016-11-09 14:17:26 -08001419 ObjectiveContext context = new ObjectiveContext() {
1420 @Override
1421 public void onSuccess(Objective objective) {
Pier Ventre70d53ba2016-11-17 22:26:29 -08001422 log.debug("Previous {} next for {} removed", INITIATION, key);
1423 if (future != null) {
1424 future.complete(null);
Pier Ventre42287df2016-11-09 14:17:26 -08001425 }
1426 }
1427
1428 @Override
1429 public void onError(Objective objective, ObjectiveError error) {
Pier Ventre70d53ba2016-11-17 22:26:29 -08001430 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1431 if (future != null) {
1432 future.complete(error);
Pier Ventre42287df2016-11-09 14:17:26 -08001433 }
1434 }
1435 };
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001436 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1437 */
1438
1439 future.complete(null);
Pier Ventre42287df2016-11-09 14:17:26 -08001440 l2InitiationNextObjStore.remove(key);
1441 }
1442
1443 /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001444 * Deletes the pseudo wire termination.
1445 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001446 * @param l2Tunnel the tunnel
1447 * @param egress the egress connect point
1448 * @param future the async task
Pier Ventre70d53ba2016-11-17 22:26:29 -08001449 * @param direction the direction of the tunnel
1450 */
1451 private void tearDownPseudoWireTerm(DefaultL2Tunnel l2Tunnel,
1452 ConnectPoint egress,
1453 CompletableFuture<ObjectiveError> future,
1454 Direction direction) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001455
Pier Ventre70d53ba2016-11-17 22:26:29 -08001456 String key = generateKey(l2Tunnel.tunnelId(), direction);
Pier Ventre70d53ba2016-11-17 22:26:29 -08001457 if (!l2TerminationNextObjStore.containsKey(key)) {
1458 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1459 if (future != null) {
1460 future.complete(null);
1461 }
1462 return;
1463 }
1464 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001465 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1466 l2Tunnel.tunnelId(),
1467 egress.port(),
1468 nextObjective.id());
1469 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for {} {} removed",
1470 TERMINATION,
1471 l2Tunnel.tunnelId()),
1472 (objective, error) ->
1473 log.warn("Failed to remove fwdObj for {} {}",
1474 TERMINATION,
1475 l2Tunnel.tunnelId(),
1476 error));
Pier Ventre70d53ba2016-11-17 22:26:29 -08001477 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1478
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001479 // un-comment in case you want to delete groups used by the pw
1480 // however, this will break the update of pseudowires cause the L2 interface group can
1481 // not be deleted (it is referenced by other groups)
1482 /*
Pier Ventre70d53ba2016-11-17 22:26:29 -08001483 context = new ObjectiveContext() {
1484 @Override
1485 public void onSuccess(Objective objective) {
1486 log.debug("Previous {} next for {} removed", TERMINATION, key);
1487 if (future != null) {
1488 future.complete(null);
1489 }
1490 }
1491
1492 @Override
1493 public void onError(Objective objective, ObjectiveError error) {
1494 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1495 if (future != null) {
1496 future.complete(error);
1497 }
1498 }
1499 };
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001500 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1501 */
1502
1503 future.complete(null);
Pier Ventre70d53ba2016-11-17 22:26:29 -08001504 l2TerminationNextObjStore.remove(key);
1505 }
1506
1507 /**
Pier Ventre42287df2016-11-09 14:17:26 -08001508 * Utilities to generate pw key.
1509 *
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001510 * @param tunnelId the tunnel id
Pier Ventre42287df2016-11-09 14:17:26 -08001511 * @param direction the direction of the pw
1512 * @return the key of the store
1513 */
Pier Ventre70d53ba2016-11-17 22:26:29 -08001514 private String generateKey(long tunnelId, Direction direction) {
Pier Ventre42287df2016-11-09 14:17:26 -08001515 return String.format("%s-%s", tunnelId, direction);
1516 }
1517
1518 /**
Pier Luigi3bfe32c2017-01-30 09:47:36 -08001519 * Pwaas pipelines.
Pier Ventre42287df2016-11-09 14:17:26 -08001520 */
1521 protected enum Pipeline {
1522 /**
1523 * The initiation pipeline.
1524 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001525 INITIATION, /**
Pier Ventre42287df2016-11-09 14:17:26 -08001526 * The termination pipeline.
1527 */
Pier Ventre70d53ba2016-11-17 22:26:29 -08001528 TERMINATION
Pier Ventre42287df2016-11-09 14:17:26 -08001529 }
1530
1531 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001532 * Enum helper to carry results of various operations.
Pier Ventre42287df2016-11-09 14:17:26 -08001533 */
1534 public enum Result {
1535 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001536 * Happy ending scenario.
Pier Ventre42287df2016-11-09 14:17:26 -08001537 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001538 SUCCESS(0, "No error occurred"),
1539
Pier Ventre42287df2016-11-09 14:17:26 -08001540 /**
1541 * We have problems with the supplied parameters.
1542 */
1543 WRONG_PARAMETERS(1, "Wrong parameters"),
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001544
Pier Ventre42287df2016-11-09 14:17:26 -08001545 /**
1546 * We have an internal error during the deployment
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001547 * or removal phase.
Pier Ventre42287df2016-11-09 14:17:26 -08001548 */
1549 INTERNAL_ERROR(3, "Internal error"),
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001550
Pier Ventre42287df2016-11-09 14:17:26 -08001551 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001552 *
Pier Ventre42287df2016-11-09 14:17:26 -08001553 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001554 REMOVAL_ERROR(5, "Can not remove pseudowire from network configuration"),
1555
1556 /**
1557 *
1558 */
1559 ADDITION_ERROR(6, "Can not add pseudowire in network configuration"),
1560
1561 /**
1562 *
1563 */
1564 CONFIG_NOT_FOUND(7, "Can not find configuration class for pseudowires");
Pier Ventre42287df2016-11-09 14:17:26 -08001565
1566 private final int code;
1567 private final String description;
1568 private int nextId;
1569
Pier Ventre70d53ba2016-11-17 22:26:29 -08001570 Result(int code, String description) {
Pier Ventre42287df2016-11-09 14:17:26 -08001571 this.code = code;
1572 this.description = description;
1573 }
1574
1575 public String getDescription() {
1576 return description;
1577 }
1578
Pier Ventre42287df2016-11-09 14:17:26 -08001579 @Override
1580 public String toString() {
1581 return code + ": " + description;
1582 }
1583 }
1584
Pier Ventre70d53ba2016-11-17 22:26:29 -08001585 /**
1586 * Enum helper for handling the direction of the pw.
1587 */
1588 public enum Direction {
1589 /**
1590 * The forward direction of the pseudo wire.
1591 */
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001592 FWD, /**
Pier Ventre70d53ba2016-11-17 22:26:29 -08001593 * The reverse direction of the pseudo wire.
1594 */
Jon Hall8c7b06a2017-02-22 13:37:33 -08001595 REV
Pier Ventre70d53ba2016-11-17 22:26:29 -08001596 }
1597
Pier Ventref34966c2016-11-07 16:21:04 -08001598}