blob: 53fb9d267923e5912d5a020da902963aa110ae23 [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 Cascone9db4d5c2019-04-16 17:36:33 -070029import org.onosproject.net.pi.runtime.PiCloneSessionEntry;
Carmelo Cascone58136812018-07-19 03:40:16 +020030import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070031import org.onosproject.net.pi.runtime.PiPreEntry;
Carmelo Cascone58136812018-07-19 03:40:16 +020032import org.onosproject.net.pi.runtime.PiPreReplica;
33import org.onosproject.net.pi.service.PiTranslationException;
34
35import java.util.Collection;
36import java.util.List;
37import java.util.Map;
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020038import java.util.Optional;
Carmelo Cascone58136812018-07-19 03:40:16 +020039import java.util.Set;
40import java.util.stream.Collectors;
41
42import static com.google.common.base.Preconditions.checkNotNull;
43import static java.lang.String.format;
44
45/**
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070046 * Implementation of replication group translation logic.
Carmelo Cascone58136812018-07-19 03:40:16 +020047 */
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070048final class PiReplicationGroupTranslatorImpl {
Carmelo Cascone58136812018-07-19 03:40:16 +020049
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070050 private PiReplicationGroupTranslatorImpl() {
Carmelo Cascone58136812018-07-19 03:40:16 +020051 // Hides constructor.
52 }
53
54 /**
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070055 * Returns a PI PRE entry equivalent to the given group, for the given
56 * pipeconf and device.
Carmelo Cascone58136812018-07-19 03:40:16 +020057 * <p>
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070058 * The passed group is expected to have type {@link GroupDescription.Type#ALL}
59 * or {@link GroupDescription.Type#CLONE}.
Carmelo Cascone58136812018-07-19 03:40:16 +020060 *
61 * @param group group
Carmelo Cascone1e8843f2018-07-19 19:01:12 +020062 * @param pipeconf pipeconf
63 * @param device device
Carmelo Cascone58136812018-07-19 03:40:16 +020064 * @return PI PRE entry
65 * @throws PiTranslationException if the group cannot be translated
66 */
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070067 static PiPreEntry translate(Group group, PiPipeconf pipeconf, Device device)
Carmelo Cascone58136812018-07-19 03:40:16 +020068 throws PiTranslationException {
69
70 checkNotNull(group);
71
Carmelo Cascone58136812018-07-19 03:40:16 +020072 final List<Instruction> instructions = group.buckets().buckets().stream()
73 .flatMap(b -> b.treatment().allInstructions().stream())
74 .collect(Collectors.toList());
75
76 final boolean hasNonOutputInstr = instructions.stream()
77 .anyMatch(i -> !i.type().equals(Instruction.Type.OUTPUT));
78
79 if (instructions.size() != group.buckets().buckets().size()
80 || hasNonOutputInstr) {
81 throw new PiTranslationException(
82 "support only groups with just one OUTPUT instruction per bucket");
83 }
84
85 final List<OutputInstruction> outInstructions = instructions.stream()
86 .map(i -> (OutputInstruction) i)
87 .collect(Collectors.toList());
88
Carmelo Cascone9db4d5c2019-04-16 17:36:33 -070089 switch (group.type()) {
90 case ALL:
91 return PiMulticastGroupEntry.builder()
92 .withGroupId(group.id().id())
93 .addReplicas(getReplicas(outInstructions, device))
94 .build();
95 case CLONE:
96 return PiCloneSessionEntry.builder()
97 .withSessionId(group.id().id())
98 .addReplicas(getReplicas(outInstructions, device))
99 .build();
100 default:
101 throw new PiTranslationException(format(
102 "group type %s not supported", group.type()));
103 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200104 }
105
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200106 private static Set<PiPreReplica> getReplicas(
107 Collection<OutputInstruction> instructions, Device device)
108 throws PiTranslationException {
Carmelo Cascone58136812018-07-19 03:40:16 +0200109 // Account for multiple replicas for the same port.
110 final Map<PortNumber, Set<PiPreReplica>> replicaMap = Maps.newHashMap();
111 final List<PortNumber> ports = instructions.stream()
112 .map(OutputInstruction::port)
113 .collect(Collectors.toList());
114 for (PortNumber port : ports) {
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200115 if (port.isLogical()) {
116 port = logicalToPipelineSpecific(port, device);
117 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200118 // Use incremental instance IDs for replicas of the same port.
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200119 replicaMap.putIfAbsent(port, Sets.newHashSet());
Carmelo Cascone58136812018-07-19 03:40:16 +0200120 replicaMap.get(port).add(
Carmelo Casconedcd56ba2020-08-04 17:19:40 -0700121 new PiPreReplica(port, replicaMap.get(port).size()));
Carmelo Cascone58136812018-07-19 03:40:16 +0200122 }
123 return replicaMap.values().stream()
124 .flatMap(Collection::stream)
125 .collect(Collectors.toSet());
126 }
Carmelo Cascone1e8843f2018-07-19 19:01:12 +0200127
128 private static PortNumber logicalToPipelineSpecific(
129 PortNumber logicalPort, Device device)
130 throws PiTranslationException {
131 if (!device.is(PiPipelineInterpreter.class)) {
132 throw new PiTranslationException(
133 "missing interpreter, cannot map logical port " + logicalPort.toString());
134 }
135 final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
136 Optional<Integer> mappedPort = interpreter.mapLogicalPortNumber(logicalPort);
137 if (!mappedPort.isPresent()) {
138 throw new PiTranslationException(
139 "interpreter cannot map logical port " + logicalPort.toString());
140 }
141 return PortNumber.portNumber(mappedPort.get());
142 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200143}