blob: 5f0ed351bea7dffe9b9ccf0359acda3852530d0c [file] [log] [blame]
Pier Ventref34966c2016-11-07 16:21:04 -08001/*
2 * Copyright 2016-present Open Networking Laboratory
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
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;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.MplsLabel;
24import org.onlab.packet.VlanId;
25import org.onlab.util.KryoNamespace;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.DisjointPath;
29import org.onosproject.net.Link;
30import org.onosproject.net.PortNumber;
Pier Ventref34966c2016-11-07 16:21:04 -080031import org.onosproject.net.config.NetworkConfigEvent;
Pier Ventre42287df2016-11-09 14:17:26 -080032import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.TrafficSelector;
35import org.onosproject.net.flow.TrafficTreatment;
36import org.onosproject.net.flow.criteria.Criteria;
37import org.onosproject.net.flowobjective.DefaultFilteringObjective;
38import org.onosproject.net.flowobjective.DefaultForwardingObjective;
39import org.onosproject.net.flowobjective.DefaultNextObjective;
40import org.onosproject.net.flowobjective.DefaultObjectiveContext;
41import org.onosproject.net.flowobjective.FilteringObjective;
42import org.onosproject.net.flowobjective.ForwardingObjective;
43import org.onosproject.net.flowobjective.NextObjective;
44import org.onosproject.net.flowobjective.Objective;
45import org.onosproject.net.flowobjective.ObjectiveContext;
46import org.onosproject.net.flowobjective.ObjectiveError;
Pier Ventref34966c2016-11-07 16:21:04 -080047import org.onosproject.segmentrouting.SegmentRoutingManager;
Pier Ventre42287df2016-11-09 14:17:26 -080048import org.onosproject.segmentrouting.SegmentRoutingService;
49import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Pier Ventref34966c2016-11-07 16:21:04 -080050import org.onosproject.segmentrouting.config.PwaasConfig;
Pier Ventre42287df2016-11-09 14:17:26 -080051import org.onosproject.store.serializers.KryoNamespaces;
52import org.onosproject.store.service.ConsistentMap;
53import org.onosproject.store.service.Serializer;
Pier Ventref34966c2016-11-07 16:21:04 -080054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
Pier Ventre42287df2016-11-09 14:17:26 -080057import java.util.List;
58import java.util.Set;
59import java.util.concurrent.CompletableFuture;
60import java.util.stream.Collectors;
61
62import static com.google.common.base.Preconditions.checkState;
63import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
64import static org.onosproject.segmentrouting.pwaas.L2Mode.TAGGED;
65import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
66import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
67
Pier Ventref34966c2016-11-07 16:21:04 -080068/**
69 * Handles pwaas related events.
70 */
71public class L2TunnelHandler {
Pier Ventre42287df2016-11-09 14:17:26 -080072
Pier Ventref34966c2016-11-07 16:21:04 -080073 private static final Logger log = LoggerFactory.getLogger(L2TunnelHandler.class);
Pier Ventre42287df2016-11-09 14:17:26 -080074
75 private static final String FWD = "f";
76 private static final String REV = "r";
77
Pier Ventref34966c2016-11-07 16:21:04 -080078 private static final String NOT_MASTER = "Not master controller";
Pier Ventre42287df2016-11-09 14:17:26 -080079 private static final String WRONG_TOPOLOGY = "Path in leaf-spine topology" +
80 " should always be two hops: ";
81
Pier Ventref34966c2016-11-07 16:21:04 -080082 private final SegmentRoutingManager srManager;
83
Pier Ventre42287df2016-11-09 14:17:26 -080084 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
85
86 /**
87 * TODO a proper store is necessary to handle the policies and collisions.
88 */
89 private final KryoNamespace.Builder l2TunnelKryo;
90
91 /**
92 * Create a l2 tunnel handler for the deploy and
93 * for the tear down of pseudo wires.
94 *
95 * @param segmentRoutingManager the segment routing manager
96 */
97 public L2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
98 srManager = segmentRoutingManager;
99 l2TunnelKryo = new KryoNamespace.Builder()
100 .register(KryoNamespaces.API);
101
102 l2InitiationNextObjStore = srManager.storageService
103 .<String, NextObjective>consistentMapBuilder()
104 .withName("onos-l2initiation-nextobj-store")
105 .withSerializer(Serializer.using(l2TunnelKryo.build()))
106 .build();
Pier Ventref34966c2016-11-07 16:21:04 -0800107 }
108
109 /**
110 * Processes Pwaas Config added event.
111 *
Pier Ventre42287df2016-11-09 14:17:26 -0800112 * @param event network config add event
Pier Ventref34966c2016-11-07 16:21:04 -0800113 */
114 public void processPwaasConfigAdded(NetworkConfigEvent event) {
115 log.info("Processing Pwaas CONFIG_ADDED");
116 PwaasConfig config = (PwaasConfig) event.config().get();
Pier Ventre42287df2016-11-09 14:17:26 -0800117 Set<DefaultL2TunnelDescription> pwToAdd = config.getPwIds()
118 .stream()
119 .map(config::getPwDescription)
120 .collect(Collectors.toSet());
121 // We deploy all the pseudo wire deployed
122 deploy(pwToAdd);
123 }
124
125 private void deploy(Set<DefaultL2TunnelDescription> pwToAdd) {
126 Result result;
127 long l2TunnelId;
128 for (DefaultL2TunnelDescription currentL2Tunnel : pwToAdd) {
129 l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
130 // The tunnel id cannot be 0.
131 if (l2TunnelId == 0) {
132 log.warn("Tunnel id cannot be 0");
133 continue;
134 }
135 // We do a sanity check of the pseudo wire.
136 result = verifyPseudoWire(currentL2Tunnel);
137 if (result != SUCCESS) {
138 continue;
139 }
140 // We establish the tunnel.
141 result = deployPseudoWire(
142 currentL2Tunnel.l2Tunnel(),
143 currentL2Tunnel.l2TunnelPolicy().cP1(),
144 currentL2Tunnel.l2TunnelPolicy().cP2(),
145 FWD
146 );
147 if (result != SUCCESS) {
148 continue;
149 }
150 // We create the policy.
151 result = deployPolicy(
152 l2TunnelId,
153 currentL2Tunnel.l2TunnelPolicy().cP1(),
154 currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
155 currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
156 result.nextId
157 );
158 if (result != SUCCESS) {
159 continue;
160 }
161 // We establish the reverse tunnel.
162 result = deployPseudoWire(
163 currentL2Tunnel.l2Tunnel(),
164 currentL2Tunnel.l2TunnelPolicy().cP2(),
165 currentL2Tunnel.l2TunnelPolicy().cP1(),
166 REV
167 );
168 if (result != SUCCESS) {
169 continue;
170 }
171 deployPolicy(
172 l2TunnelId,
173 currentL2Tunnel.l2TunnelPolicy().cP2(),
174 currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
175 currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
176 result.nextId
177 );
178 }
Pier Ventref34966c2016-11-07 16:21:04 -0800179 }
180
181 /**
182 * Processes Pwaas Config updated event.
183 *
184 * @param event network config updated event
185 */
186 public void processPwaasConfigUpdated(NetworkConfigEvent event) {
187 log.info("Processing Pwaas CONFIG_UPDATED");
Pier Ventre42287df2016-11-09 14:17:26 -0800188 // We retrieve the old pseudo wires.
189 PwaasConfig prevConfig = (PwaasConfig) event.prevConfig().get();
190 Set<Long> prevPws = prevConfig.getPwIds();
191 // We retrieve the new pseudo wires.
Pier Ventref34966c2016-11-07 16:21:04 -0800192 PwaasConfig config = (PwaasConfig) event.config().get();
Pier Ventre42287df2016-11-09 14:17:26 -0800193 Set<Long> newPws = config.getPwIds();
194 // We compute the pseudo wires to update.
195 Set<Long> updPws = newPws.stream()
196 .filter(tunnelId -> prevPws.contains(tunnelId) &&
197 !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
198 .collect(Collectors.toSet());
199 // The pseudo wires to remove.
200 Set<DefaultL2TunnelDescription> pwToRemove = prevPws.stream()
201 .filter(tunnelId -> !newPws.contains(tunnelId))
202 .map(prevConfig::getPwDescription)
203 .collect(Collectors.toSet());
204 tearDown(pwToRemove);
205 // The pseudo wires to add.
206 Set<DefaultL2TunnelDescription> pwToAdd = newPws.stream()
207 .filter(tunnelId -> !prevPws.contains(tunnelId))
208 .map(config::getPwDescription)
209 .collect(Collectors.toSet());
210 deploy(pwToAdd);
211 // The pseudo wires to update.
212 updPws.forEach(tunnelId -> {
213 updatePw(
214 prevConfig.getPwDescription(tunnelId),
215 config.getPwDescription(tunnelId)
216 );
217 });
218 }
219
220 /**
221 * Helper function to update a pw.
222 *
223 * @param oldPw the pseudo wire to remove
224 * @param newPw the pseudo wirte to add
225 */
226 private void updatePw(DefaultL2TunnelDescription oldPw,
227 DefaultL2TunnelDescription newPw) {
228
229 long tunnelId = oldPw.l2Tunnel().tunnelId();
230 String fwdKey = generateKey(tunnelId, FWD);
231 String revKey = generateKey(tunnelId, REV);
232 Result result;
233 NextObjective fwdNextObjective;
234 NextObjective revNextObjective;
235 // The async tasks to orchestrate the next and
236 // forwarding update.
237 CompletableFuture<ObjectiveError> revPolicyFuture = new CompletableFuture<>();
238 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
239 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
240 CompletableFuture<ObjectiveError> newPwFuture = new CompletableFuture<>();
241
242 result = verifyPseudoWire(newPw);
243 if (result != SUCCESS) {
244 return;
245 }
246 if (!l2InitiationNextObjStore.containsKey(fwdKey)) {
247 log.warn("NextObj for {} does not exist in the store.", fwdKey);
248 return;
249 }
250 fwdNextObjective = l2InitiationNextObjStore.get(fwdKey).value();
251 if (!l2InitiationNextObjStore.containsKey(revKey)) {
252 log.warn("NextObj for {} does not exist in the store.", revKey);
253 return;
254 }
255 // First we remove both policy.
256 revNextObjective = l2InitiationNextObjStore.get(revKey).value();
257 log.debug("Start deleting fwd policy for {}", tunnelId);
258 deletePolicy(
259 tunnelId,
260 oldPw.l2TunnelPolicy().cP1(),
261 oldPw.l2TunnelPolicy().cP1InnerTag(),
262 oldPw.l2TunnelPolicy().cP1OuterTag(),
263 fwdNextObjective.id(),
264 revPolicyFuture
265 );
266 revPolicyFuture.thenAcceptAsync(status -> {
267 if (status == null) {
268 log.debug("Fwd policy removed. Now remove rev policy for {}", tunnelId);
269 deletePolicy(
270 tunnelId,
271 oldPw.l2TunnelPolicy().cP2(),
272 oldPw.l2TunnelPolicy().cP2InnerTag(),
273 oldPw.l2TunnelPolicy().cP2OuterTag(),
274 revNextObjective.id(),
275 fwdInitNextFuture
276 );
277 }
278 });
279 // Finally we remove both the tunnels.
280 fwdInitNextFuture.thenAcceptAsync(status -> {
281 if (status == null) {
282 log.debug("Rev policy removed. Now remove fwd pw for {}", tunnelId);
283 tearDownPseudoWire(
284 fwdKey,
285 fwdNextObjective,
286 oldPw.l2TunnelPolicy().cP1(),
287 oldPw.l2TunnelPolicy().cP2(),
288 revInitNextFuture
289 );
290 }
291 });
292 revInitNextFuture.thenAcceptAsync(status -> {
293 if (status == null) {
294 log.debug("Fwd tunnel removed. Now remove rev pw for {}", tunnelId);
295 tearDownPseudoWire(
296 revKey,
297 revNextObjective,
298 oldPw.l2TunnelPolicy().cP2(),
299 oldPw.l2TunnelPolicy().cP1(),
300 newPwFuture
301 );
302
303 }
304 });
305 // At the end we install the new pw.
306 newPwFuture.thenAcceptAsync(status -> {
307 if (status == null) {
308 log.debug("Deploying new fwd pw for {}", tunnelId);
309 Result lamdaResult = deployPseudoWire(
310 newPw.l2Tunnel(),
311 newPw.l2TunnelPolicy().cP1(),
312 newPw.l2TunnelPolicy().cP2(),
313 FWD
314 );
315 if (lamdaResult != SUCCESS) {
316 return;
317 }
318 lamdaResult = deployPolicy(
319 tunnelId,
320 newPw.l2TunnelPolicy().cP1(),
321 newPw.l2TunnelPolicy().cP1InnerTag(),
322 newPw.l2TunnelPolicy().cP1OuterTag(),
323 lamdaResult.nextId
324 );
325 log.debug("Deploying new rev pw for {}", tunnelId);
326 lamdaResult = deployPseudoWire(
327 newPw.l2Tunnel(),
328 newPw.l2TunnelPolicy().cP2(),
329 newPw.l2TunnelPolicy().cP1(),
330 REV
331 );
332 if (lamdaResult != SUCCESS) {
333 return;
334 }
335 lamdaResult = deployPolicy(
336 tunnelId,
337 newPw.l2TunnelPolicy().cP2(),
338 newPw.l2TunnelPolicy().cP2InnerTag(),
339 newPw.l2TunnelPolicy().cP2OuterTag(),
340 lamdaResult.nextId
341 );
342 }
Pier Ventref34966c2016-11-07 16:21:04 -0800343 });
344 }
345
346 /**
347 * Processes Pwaas Config removed event.
348 *
349 * @param event network config removed event
350 */
351 public void processPwaasConfigRemoved(NetworkConfigEvent event) {
352 log.info("Processing Pwaas CONFIG_REMOVED");
Pier Ventre42287df2016-11-09 14:17:26 -0800353 PwaasConfig config = (PwaasConfig) event.prevConfig().get();
354 Set<DefaultL2TunnelDescription> pwToRemove = config.getPwIds()
355 .stream()
356 .map(config::getPwDescription)
357 .collect(Collectors.toSet());
358 // We teardown all the pseudo wire deployed
359 tearDown(pwToRemove);
Pier Ventref34966c2016-11-07 16:21:04 -0800360 }
Pier Ventre42287df2016-11-09 14:17:26 -0800361
362 /**
363 * Helper function to handle the pw removal.
364 *
365 * @param pwToRemove the pseudo wires to remove
366 */
367 private void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
368 Result result;
369 int nextId;
370 NextObjective nextObjective;
371 long l2TunnelId;
372 // We remove all the pw in the configuration
373 // file.
374 for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
375 l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
376 if (l2TunnelId == 0) {
377 log.warn("Tunnel id cannot be 0");
378 continue;
379 }
380 result = verifyPseudoWire(currentL2Tunnel);
381 if (result != SUCCESS) {
382 continue;
383 }
384 String key = generateKey(l2TunnelId, FWD);
385 if (!l2InitiationNextObjStore.containsKey(key)) {
386 log.warn("NextObj for {} does not exist in the store.", key);
387 continue;
388 }
389 nextObjective = l2InitiationNextObjStore.get(key).value();
390 nextId = nextObjective.id();
391 // First all we have to delete the policy.
392 deletePolicy(
393 l2TunnelId,
394 currentL2Tunnel.l2TunnelPolicy().cP1(),
395 currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
396 currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
397 nextId,
398 null
399 );
400 // Finally we will tear down the pseudo wire.
401 tearDownPseudoWire(
402 key,
403 nextObjective,
404 currentL2Tunnel.l2TunnelPolicy().cP1(),
405 currentL2Tunnel.l2TunnelPolicy().cP2(),
406 null
407 );
408 // We do the same operations on the reverse side.
409 key = generateKey(l2TunnelId, REV);
410 if (!l2InitiationNextObjStore.containsKey(key)) {
411 log.warn("NextObj for {} does not exist in the store.", key);
412 continue;
413 }
414 nextObjective = l2InitiationNextObjStore.get(key).value();
415 nextId = nextObjective.id();
416 deletePolicy(
417 l2TunnelId,
418 currentL2Tunnel.l2TunnelPolicy().cP2(),
419 currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
420 currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
421 nextId,
422 null
423 );
424 tearDownPseudoWire(
425 key,
426 nextObjective,
427 currentL2Tunnel.l2TunnelPolicy().cP2(),
428 currentL2Tunnel.l2TunnelPolicy().cP1(),
429 null
430 );
431 }
432
433 }
434
435 /**
436 * Helper method to verify the integrity of the pseudo wire.
437 *
438 * @param l2TunnelDescription the pseudo wire description
439 * @return the result of the check
440 */
441 private Result verifyPseudoWire(DefaultL2TunnelDescription l2TunnelDescription) {
442 Result result;
443 DefaultL2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
444 DefaultL2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
445 result = verifyTunnel(l2Tunnel);
446 if (result != SUCCESS) {
447 log.warn("Tunnel {} did not pass the validation", l2Tunnel.tunnelId());
448 return result;
449 }
450 result = verifyPolicy(
451 l2TunnelPolicy.isAllVlan(),
452 l2TunnelPolicy.cP1InnerTag(),
453 l2TunnelPolicy.cP1OuterTag(),
454 l2TunnelPolicy.cP2InnerTag(),
455 l2TunnelPolicy.cP2OuterTag()
456 );
457 if (result != SUCCESS) {
458 log.warn("Policy for tunnel {} did not pass the validation", l2Tunnel.tunnelId());
459 return result;
460 }
461
462 return SUCCESS;
463 }
464
465 /**
466 * TODO Operation on the policies store.
467 *
468 * Handles the policy establishment which consists in
469 * create the filtering and forwarding objectives related
470 * to the initiation and termination.
471 *
472 * @param tunnelId the tunnel id
473 * @param ingress the ingress point
474 * @param ingressInner the ingress inner tag
475 * @param ingressOuter the ingress outer tag
476 * @param nextId the next objective id
477 * @return SUCCESS if the policy has been deployed.
478 * Otherwise an error according to the failure
479 * scenario.
480 */
481 private Result deployPolicy(long tunnelId,
482 ConnectPoint ingress,
483 VlanId ingressInner,
484 VlanId ingressOuter,
485 int nextId) {
486
487 ForwardingObjective.Builder fwdBuilder;
488 FilteringObjective.Builder filtBuilder;
489 List<Objective> objectives = Lists.newArrayList();
490 if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
491 log.info("Abort creation of policy for L2 tunnel {}: {}", tunnelId, NOT_MASTER);
492 return SUCCESS;
493 }
494 // We create the forwarding objective for supporting
495 // the l2 tunnel.
496 fwdBuilder = createFwdObjective(
497 INITIATION,
498 tunnelId,
499 ingress.port(),
500 nextId
501 );
502 // We create and add objective context.
503 ObjectiveContext context = new DefaultObjectiveContext(
504 (objective)
505 -> log.debug("FwdObj for tunnel {} populated", tunnelId),
506 (objective, error)
507 -> log.warn("Failed to populate fwdrObj for tunnel {}", tunnelId, error));
508 objectives.add(fwdBuilder.add(context));
509 // We create the filtering objective to define the
510 // permit traffic in the switch
511 filtBuilder = createFiltObjective(
512 ingress.port(),
513 ingressInner,
514 ingressOuter
515 );
516 // We add the metadata.
517 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
518 .setTunnelId(tunnelId);
519 filtBuilder.withMeta(treatment.build());
520 // We create and add objective context.
521 context = new DefaultObjectiveContext(
522 (objective)
523 -> log.debug("FilterObj for tunnel {} populated", tunnelId),
524 (objective, error)
525 -> log.warn("Failed to populate filterObj for tunnel {}", tunnelId, error));
526 objectives.add(filtBuilder.add(context));
527
528 for (Objective objective : objectives) {
529 if (objective instanceof ForwardingObjective) {
530 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
531 log.debug("Creating new FwdObj for NextObj with id={} for tunnel {}", nextId, tunnelId);
532 } else {
533 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
534 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
535 }
536 }
537 return SUCCESS;
538 }
539
540 /**
541 * Helper method to verify if the policy is whether or not
542 * supported.
543 *
544 * @param isAllVlan all vlan mode
545 * @param ingressInner the ingress inner tag
546 * @param ingressOuter the ingress outer tag
547 * @param egressInner the egress inner tag
548 * @param egressOuter the egress outer tag
549 * @return the result of verification
550 */
551 private Result verifyPolicy(boolean isAllVlan,
552 VlanId ingressInner,
553 VlanId ingressOuter,
554 VlanId egressInner,
555 VlanId egressOuter) {
556 // AllVlan mode is not supported yet.
557 if (isAllVlan) {
558 log.warn("AllVlan not supported yet");
559 return UNSUPPORTED;
560 }
561 // The vlan tags for cP1 and cP2 have to be different from
562 // vlan none.
563 if (ingressInner.equals(VlanId.NONE) ||
564 ingressOuter.equals(VlanId.NONE) ||
565 egressInner.equals(VlanId.NONE) ||
566 egressOuter.equals(VlanId.NONE)) {
567 log.warn("The vlan tags for the connect point have to be" +
568 "different from vlan none");
569 return WRONG_PARAMETERS;
570 }
571 return SUCCESS;
572 }
573
574 /**
575 * TODO Operation on the policies store.
576 *
577 * Handles the tunnel establishment which consists in
578 * create the next objectives related to the initiation
579 * and termination.
580 *
581 * @param l2Tunnel the tunnel to deploy
582 * @param ingress the ingress connect point
583 * @param egress the egress connect point
584 * @param direction the direction of the pw
585 * @return SUCCESS if the tunnel has been created.
586 * Otherwise an error according to the failure
587 * scenario
588 */
589 private Result deployPseudoWire(DefaultL2Tunnel l2Tunnel,
590 ConnectPoint ingress,
591 ConnectPoint egress,
592 String direction) {
593 Link nextHop;
594 NextObjective.Builder nextObjectiveBuilder;
595 NextObjective nextObjective;
596 int nextId;
597 Result result;
598 if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
599 log.info("Abort initiation creation of L2 tunnel {}: {}",
600 l2Tunnel.tunnelId(), NOT_MASTER);
601 return SUCCESS;
602 }
603 // We need at least a path between ingress and egress.
604 nextHop = getNextHop(ingress, egress);
605 if (nextHop == null) {
606 log.warn("No path between ingress and egress");
607 return WRONG_PARAMETERS;
608 }
609 // We create the next objective without the metadata
610 // context and id. We check if it already exists in the
611 // store. If not we store as it is in the store ?
612 nextObjectiveBuilder = createNextObjective(
613 INITIATION,
614 nextHop,
615 l2Tunnel,
616 egress.deviceId()
617 );
618 if (nextObjectiveBuilder == null) {
619 return INTERNAL_ERROR;
620 }
621 // We set the metadata. We will use this metadata
622 // to inform the driver we are doing a l2 tunnel.
623 TrafficSelector metadata = DefaultTrafficSelector
624 .builder()
625 .matchTunnelId(l2Tunnel.tunnelId())
626 .build();
627 nextObjectiveBuilder.withMeta(metadata);
628 nextId = srManager.flowObjectiveService.allocateNextId();
629 if (nextId < 0) {
630 log.warn("Not able to allocate a next id for initiation");
631 return INTERNAL_ERROR;
632 }
633 nextObjectiveBuilder.withId(nextId);
634 String key = generateKey(l2Tunnel.tunnelId(), direction);
635 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
636 ObjectiveContext context = new DefaultObjectiveContext(
637 (objective)
638 -> log.debug("Initiation l2 tunnel rule for {} populated",
639 l2Tunnel.tunnelId()),
640 (objective, error)
641 -> log.warn("Failed to populate Initiation l2 tunnel rule for {}: {}",
642 l2Tunnel.tunnelId(), error));
643 nextObjective = nextObjectiveBuilder.add(context);
644 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
645 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
646 l2Tunnel.tunnelId(),
647 nextObjective.id()
648 );
649 result = SUCCESS;
650 result.nextId = nextObjective.id();
651 return result;
652 }
653
654 /**
655 * Helper method to verify if the tunnel is whether or not
656 * supported.
657 *
658 * @param l2Tunnel the tunnel to verify
659 * @return the result of the verification
660 */
661 private Result verifyTunnel(DefaultL2Tunnel l2Tunnel) {
662 // Service delimiting tag not supported yet.
663 if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
664 log.warn("Service delimiting tag not supported yet");
665 return UNSUPPORTED;
666 }
667 // Tag mode not supported yet.
668 if (l2Tunnel.pwMode() == TAGGED) {
669 log.warn("Tagged mode not supported yet");
670 return UNSUPPORTED;
671 }
672 // Raw mode without service delimiting tag
673 // is the only mode supported for now.
674 return SUCCESS;
675 }
676
677 /**
678 * Create the filtering objective according to a given policy.
679 *
680 * @param inPort the in port
681 * @param innerTag the inner vlan tag
682 * @param outerTag the outer vlan tag
683 * @return the filtering objective
684 */
685 private FilteringObjective.Builder createFiltObjective(PortNumber inPort,
686 VlanId innerTag,
687 VlanId outerTag) {
688 return DefaultFilteringObjective.builder()
689 .withKey(Criteria.matchInPort(inPort))
690 .addCondition(Criteria.matchInnerVlanId(innerTag))
691 .addCondition(Criteria.matchVlanId(outerTag))
692 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
693 .permit()
694 .fromApp(srManager.appId);
695 }
696
697 /**
698 * Create the forwarding objective according to a given pipeline.
699 *
700 * @param pipeline the pipeline
701 * @param tunnelId the tunnel id
702 * @param nextId the next step
703 * @return the forwarding objective to support the pipeline.
704 */
705 private ForwardingObjective.Builder createFwdObjective(Pipeline pipeline,
706 long tunnelId,
707 PortNumber inPort,
708 int nextId) {
709 ForwardingObjective.Builder fwdBuilder = null;
710 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector
711 .builder();
712 if (pipeline == INITIATION) {
713 // The flow has to match on the mpls logical
714 // port and the tunnel id.
715 trafficSelector.matchTunnelId(tunnelId);
716 trafficSelector.matchInPort(inPort);
717 fwdBuilder = DefaultForwardingObjective.builder()
718 .fromApp(srManager.appId)
719 .makePermanent()
720 .nextStep(nextId)
721 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
722 .withSelector(trafficSelector.build())
723 .withFlag(VERSATILE);
724 }
725 return fwdBuilder;
726
727 }
728
729 /**
730 * Creates the next objective according to a given
731 * pipeline. We don't set the next id and we don't
732 * create the final meta to check if we are re-using
733 * the same next objective for different tunnels.
734 *
735 * @param pipeline the pipeline to support
736 * @param nextHop the next hop towards the destination
737 * @param l2Tunnel the tunnel to support
738 * @param egressId the egress device id
739 * @return the next objective to support the pipeline
740 */
741 private NextObjective.Builder createNextObjective(Pipeline pipeline,
742 Link nextHop,
743 DefaultL2Tunnel l2Tunnel,
744 DeviceId egressId) {
745 NextObjective.Builder nextObjBuilder;
746 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
747 if (pipeline == INITIATION) {
748 nextObjBuilder = DefaultNextObjective
749 .builder()
750 .withType(NextObjective.Type.SIMPLE)
751 .fromApp(srManager.appId);
752 // The pw label is the bottom of stack. It has to
753 // be different -1.
754 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
755 log.warn("Pw label not configured");
756 return null;
757 }
758 treatmentBuilder.pushMpls();
759 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
760 treatmentBuilder.setMplsBos(true);
761 treatmentBuilder.copyTtlOut();
762 // If the inter-co label is present we have to set the label.
763 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
764 treatmentBuilder.pushMpls();
765 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
766 treatmentBuilder.setMplsBos(false);
767 treatmentBuilder.copyTtlOut();
768 }
769 // We retrieve the sr label from the config
770 // using the egress leaf device id.
771 MplsLabel srLabel;
772 try {
773 srLabel = MplsLabel.mplsLabel(
774 srManager.deviceConfiguration.getIPv4SegmentId(egressId)
775 );
776 } catch (DeviceConfigNotFoundException e) {
777 log.warn("Sr label not configured");
778 return null;
779 }
780 treatmentBuilder.pushMpls();
781 treatmentBuilder.setMpls(srLabel);
782 treatmentBuilder.setMplsBos(false);
783 treatmentBuilder.copyTtlOut();
784 // We have to rewrite the src and dst mac address.
785 MacAddress ingressMac;
786 try {
787 ingressMac = srManager
788 .deviceConfiguration
789 .getDeviceMac(nextHop.src().deviceId());
790 } catch (DeviceConfigNotFoundException e) {
791 log.warn("Was not able to find the ingress mac");
792 return null;
793 }
794 treatmentBuilder.setEthSrc(ingressMac);
795 MacAddress neighborMac;
796 try {
797 neighborMac = srManager
798 .deviceConfiguration
799 .getDeviceMac(nextHop.dst().deviceId());
800 } catch (DeviceConfigNotFoundException e) {
801 log.warn("Was not able to find the neighbor mac");
802 return null;
803 }
804 treatmentBuilder.setEthDst(neighborMac);
805 } else {
806 nextObjBuilder = DefaultNextObjective
807 .builder()
808 .withType(NextObjective.Type.SIMPLE)
809 .fromApp(srManager.appId);
810
811 }
812 treatmentBuilder.setOutput(nextHop.src().port());
813 nextObjBuilder.addTreatment(treatmentBuilder.build());
814 return nextObjBuilder;
815 }
816
817 /**
818 * Returns the next hop.
819 *
820 * @param srcCp the ingress connect point
821 * @param dstCp the egress connect point
822 * @return the next hop
823 */
824 private Link getNextHop(ConnectPoint srcCp, ConnectPoint dstCp) {
825 // We retrieve a set of disjoint paths.
826 Set<DisjointPath> paths = srManager.pathService.getDisjointPaths(
827 srcCp.elementId(),
828 dstCp.elementId()
829 );
830 // We randmly pick a path.
831 if (paths.isEmpty()) {
832 return null;
833 }
834 int size = paths.size();
835 int index = RandomUtils.nextInt(0, size);
836 // We verify if the path is ok and there is not
837 // a misconfiguration.
838 List<Link> links = Iterables.get(paths, index).links();
839 checkState(links.size() == 2, WRONG_TOPOLOGY, links);
840 return links.get(0);
841 }
842
843 /**
844 * TODO Operation on the store.
845 * Deletes a given policy using the parameter supplied.
846 *
847 * @param tunnelId the tunnel id
848 * @param ingress the ingress point
849 * @param ingressInner the ingress inner vlan id
850 * @param ingressOuter the ingress outer vlan id
851 * @param nextId the next objective id
852 */
853 private void deletePolicy(long tunnelId,
854 ConnectPoint ingress,
855 VlanId ingressInner,
856 VlanId ingressOuter,
857 int nextId,
858 CompletableFuture<ObjectiveError> fwdFuture) {
859
860 ForwardingObjective.Builder fwdBuilder;
861 FilteringObjective.Builder filtBuilder;
862 List<Objective> objectives = Lists.newArrayList();
863 if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
864 log.info("Abort delete of policy for L2 tunnel {}: {}", tunnelId, NOT_MASTER);
865 return;
866 }
867 // We create the forwarding objective.
868 fwdBuilder = createFwdObjective(
869 INITIATION,
870 tunnelId,
871 ingress.port(),
872 nextId
873 );
874 ObjectiveContext context = new ObjectiveContext() {
875 @Override
876 public void onSuccess(Objective objective) {
877 log.debug("Previous FwdObj for policy {} removed", tunnelId);
878 if (fwdFuture != null) {
879 fwdFuture.complete(null);
880 }
881 }
882
883 @Override
884 public void onError(Objective objective, ObjectiveError error) {
885 log.warn("Failed to remove previous FwdObj for policy {}: {}", tunnelId, error);
886 if (fwdFuture != null) {
887 fwdFuture.complete(error);
888 }
889 }
890 };
891 objectives.add(fwdBuilder.remove(context));
892 // We create the filtering objective to define the
893 // permit traffic in the switch
894 filtBuilder = createFiltObjective(
895 ingress.port(),
896 ingressInner,
897 ingressOuter
898 );
899 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
900 .setTunnelId(tunnelId);
901 filtBuilder.withMeta(treatment.build());
902 context = new DefaultObjectiveContext(
903 (objective)
904 -> log.debug("FilterObj for policy {} revoked", tunnelId),
905 (objective, error)
906 -> log.warn("Failed to revoke filterObj for policy {}", tunnelId, error));
907 objectives.add(filtBuilder.remove(context));
908
909 for (Objective objective : objectives) {
910 if (objective instanceof ForwardingObjective) {
911 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
912 } else {
913 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
914 }
915 }
916 }
917
918 /**
919 * TODO Operation on the store.
920 * Deletes a given pseudo wire using the parameter supplied.
921 *
922 * @param key the key of the store
923 * @param nextObjective the next objective representing the pw
924 * @param ingress the ingress connect point
925 * @param egress the egress connect point
926 */
927 private void tearDownPseudoWire(String key,
928 NextObjective nextObjective,
929 ConnectPoint ingress,
930 ConnectPoint egress,
931 CompletableFuture<ObjectiveError> nextFutureForInit) {
932 if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
933 log.info("Abort delete of {} for {}: {}", INITIATION, key, NOT_MASTER);
934 return;
935 }
936 ObjectiveContext context = new ObjectiveContext() {
937 @Override
938 public void onSuccess(Objective objective) {
939 log.debug("Previous {} NextObj for {} removed", INITIATION, key);
940 if (nextFutureForInit != null) {
941 nextFutureForInit.complete(null);
942 }
943 }
944
945 @Override
946 public void onError(Objective objective, ObjectiveError error) {
947 log.warn("Failed to remove previous {} NextObj for {}: {}", INITIATION, key, error);
948 if (nextFutureForInit != null) {
949 nextFutureForInit.complete(error);
950 }
951 }
952 };
953 srManager.flowObjectiveService.next(
954 ingress.deviceId(),
955 (NextObjective) nextObjective.copy().remove(context)
956 );
957 l2InitiationNextObjStore.remove(key);
958 }
959
960 /**
961 * Utilities to generate pw key.
962 *
963 * @param tunnelId the tunnel id
964 * @param direction the direction of the pw
965 * @return the key of the store
966 */
967 private String generateKey(long tunnelId, String direction) {
968 return String.format("%s-%s", tunnelId, direction);
969 }
970
971 /**
972 * VPWS pipelines.
973 */
974 protected enum Pipeline {
975 /**
976 * The initiation pipeline.
977 */
978 INITIATION,
979 /**
980 * The termination pipeline.
981 */
982 TERMINATION;
983 }
984
985 /**
986 * Enum helper to carry the outcomes of an operation.
987 */
988 public enum Result {
989 /**
990 * Happy ending scenario it has been created.
991 */
992 SUCCESS(0, "It has been Created"),
993 /**
994 * We have problems with the supplied parameters.
995 */
996 WRONG_PARAMETERS(1, "Wrong parameters"),
997 /**
998 * It already exists.
999 */
1000 ID_EXISTS(2, "The id already exists"),
1001 /**
1002 * We have an internal error during the deployment
1003 * phase.
1004 */
1005 INTERNAL_ERROR(3, "Internal error"),
1006 /**
1007 * The operation is not supported.
1008 */
1009 UNSUPPORTED(4, "Unsupported");
1010
1011 private final int code;
1012 private final String description;
1013 private int nextId;
1014
1015 private Result(int code, String description) {
1016 this.code = code;
1017 this.description = description;
1018 }
1019
1020 public String getDescription() {
1021 return description;
1022 }
1023
1024 public int getCode() {
1025 return code;
1026 }
1027
1028 @Override
1029 public String toString() {
1030 return code + ": " + description;
1031 }
1032 }
1033
Pier Ventref34966c2016-11-07 16:21:04 -08001034}