blob: 7d482097d54a0fa3cfb91b043c55837625680a65 [file] [log] [blame]
Carmelo Cascone58136812018-07-19 03:40:16 +02001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.net.pi.impl;
18
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020021import org.onosproject.net.Device;
Carmelo Cascone58136812018-07-19 03:40:16 +020022import org.onosproject.net.PortNumber;
23import org.onosproject.net.flow.instructions.Instruction;
24import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
25import org.onosproject.net.group.Group;
26import org.onosproject.net.group.GroupDescription;
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020027import org.onosproject.net.pi.model.PiPipeconf;
28import org.onosproject.net.pi.model.PiPipelineInterpreter;
Carmelo Cascone58136812018-07-19 03:40:16 +020029import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
30import org.onosproject.net.pi.runtime.PiPreReplica;
31import org.onosproject.net.pi.service.PiTranslationException;
32
33import java.util.Collection;
34import java.util.List;
35import java.util.Map;
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020036import java.util.Optional;
Carmelo Cascone58136812018-07-19 03:40:16 +020037import java.util.Set;
38import java.util.stream.Collectors;
39
40import static com.google.common.base.Preconditions.checkNotNull;
41import static java.lang.String.format;
42
43/**
44 * Implementation of multicast group translation logic.
45 */
46final class PiMulticastGroupTranslatorImpl {
47
48 private PiMulticastGroupTranslatorImpl() {
49 // Hides constructor.
50 }
51
52 /**
53 * Returns a PI PRE multicast group entry equivalent to the given group, for
54 * the given pipeconf and device.
55 * <p>
56 * The passed group is expected to have type {@link GroupDescription.Type#ALL}.
57 *
58 * @param group group
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020059 * @param pipeconf pipeconf
60 * @param device device
Carmelo Cascone58136812018-07-19 03:40:16 +020061 * @return PI PRE entry
62 * @throws PiTranslationException if the group cannot be translated
63 */
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020064 static PiMulticastGroupEntry translate(Group group, PiPipeconf pipeconf, Device device)
Carmelo Cascone58136812018-07-19 03:40:16 +020065 throws PiTranslationException {
66
67 checkNotNull(group);
68
69 if (!group.type().equals(GroupDescription.Type.ALL)) {
70 throw new PiTranslationException(format(
71 "group type %s not supported", group.type()));
72 }
73
74 final List<Instruction> instructions = group.buckets().buckets().stream()
75 .flatMap(b -> b.treatment().allInstructions().stream())
76 .collect(Collectors.toList());
77
78 final boolean hasNonOutputInstr = instructions.stream()
79 .anyMatch(i -> !i.type().equals(Instruction.Type.OUTPUT));
80
81 if (instructions.size() != group.buckets().buckets().size()
82 || hasNonOutputInstr) {
83 throw new PiTranslationException(
84 "support only groups with just one OUTPUT instruction per bucket");
85 }
86
87 final List<OutputInstruction> outInstructions = instructions.stream()
88 .map(i -> (OutputInstruction) i)
89 .collect(Collectors.toList());
90
91 return PiMulticastGroupEntry.builder()
92 .withGroupId(group.id().id())
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020093 .addReplicas(getReplicas(outInstructions, device))
Carmelo Cascone58136812018-07-19 03:40:16 +020094 .build();
95 }
96
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020097 private static Set<PiPreReplica> getReplicas(
98 Collection<OutputInstruction> instructions, Device device)
99 throws PiTranslationException {
Carmelo Cascone58136812018-07-19 03:40:16 +0200100 // Account for multiple replicas for the same port.
101 final Map<PortNumber, Set<PiPreReplica>> replicaMap = Maps.newHashMap();
102 final List<PortNumber> ports = instructions.stream()
103 .map(OutputInstruction::port)
104 .collect(Collectors.toList());
105 for (PortNumber port : ports) {
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200106 if (port.isLogical()) {
107 port = logicalToPipelineSpecific(port, device);
108 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200109 // Use incremental instance IDs for replicas of the same port.
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200110 replicaMap.putIfAbsent(port, Sets.newHashSet());
Carmelo Cascone58136812018-07-19 03:40:16 +0200111 replicaMap.get(port).add(
112 new PiPreReplica(port, replicaMap.get(port).size() + 1));
113 }
114 return replicaMap.values().stream()
115 .flatMap(Collection::stream)
116 .collect(Collectors.toSet());
117 }
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200118
119 private static PortNumber logicalToPipelineSpecific(
120 PortNumber logicalPort, Device device)
121 throws PiTranslationException {
122 if (!device.is(PiPipelineInterpreter.class)) {
123 throw new PiTranslationException(
124 "missing interpreter, cannot map logical port " + logicalPort.toString());
125 }
126 final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
127 Optional<Integer> mappedPort = interpreter.mapLogicalPortNumber(logicalPort);
128 if (!mappedPort.isPresent()) {
129 throw new PiTranslationException(
130 "interpreter cannot map logical port " + logicalPort.toString());
131 }
132 return PortNumber.portNumber(mappedPort.get());
133 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200134}