blob: 7d482097d54a0fa3cfb91b043c55837625680a65 [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.pi.impl;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
import org.onosproject.net.pi.runtime.PiPreReplica;
import org.onosproject.net.pi.service.PiTranslationException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
/**
* Implementation of multicast group translation logic.
*/
final class PiMulticastGroupTranslatorImpl {
private PiMulticastGroupTranslatorImpl() {
// Hides constructor.
}
/**
* Returns a PI PRE multicast group entry equivalent to the given group, for
* the given pipeconf and device.
* <p>
* The passed group is expected to have type {@link GroupDescription.Type#ALL}.
*
* @param group group
* @param pipeconf pipeconf
* @param device device
* @return PI PRE entry
* @throws PiTranslationException if the group cannot be translated
*/
static PiMulticastGroupEntry translate(Group group, PiPipeconf pipeconf, Device device)
throws PiTranslationException {
checkNotNull(group);
if (!group.type().equals(GroupDescription.Type.ALL)) {
throw new PiTranslationException(format(
"group type %s not supported", group.type()));
}
final List<Instruction> instructions = group.buckets().buckets().stream()
.flatMap(b -> b.treatment().allInstructions().stream())
.collect(Collectors.toList());
final boolean hasNonOutputInstr = instructions.stream()
.anyMatch(i -> !i.type().equals(Instruction.Type.OUTPUT));
if (instructions.size() != group.buckets().buckets().size()
|| hasNonOutputInstr) {
throw new PiTranslationException(
"support only groups with just one OUTPUT instruction per bucket");
}
final List<OutputInstruction> outInstructions = instructions.stream()
.map(i -> (OutputInstruction) i)
.collect(Collectors.toList());
return PiMulticastGroupEntry.builder()
.withGroupId(group.id().id())
.addReplicas(getReplicas(outInstructions, device))
.build();
}
private static Set<PiPreReplica> getReplicas(
Collection<OutputInstruction> instructions, Device device)
throws PiTranslationException {
// Account for multiple replicas for the same port.
final Map<PortNumber, Set<PiPreReplica>> replicaMap = Maps.newHashMap();
final List<PortNumber> ports = instructions.stream()
.map(OutputInstruction::port)
.collect(Collectors.toList());
for (PortNumber port : ports) {
if (port.isLogical()) {
port = logicalToPipelineSpecific(port, device);
}
// Use incremental instance IDs for replicas of the same port.
replicaMap.putIfAbsent(port, Sets.newHashSet());
replicaMap.get(port).add(
new PiPreReplica(port, replicaMap.get(port).size() + 1));
}
return replicaMap.values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}
private static PortNumber logicalToPipelineSpecific(
PortNumber logicalPort, Device device)
throws PiTranslationException {
if (!device.is(PiPipelineInterpreter.class)) {
throw new PiTranslationException(
"missing interpreter, cannot map logical port " + logicalPort.toString());
}
final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
Optional<Integer> mappedPort = interpreter.mapLogicalPortNumber(logicalPort);
if (!mappedPort.isPresent()) {
throw new PiTranslationException(
"interpreter cannot map logical port " + logicalPort.toString());
}
return PortNumber.portNumber(mappedPort.get());
}
}