blob: 369b7c56198c1e5fe47627daf6bbefe22326b592 [file] [log] [blame]
Esin Karaman971fb7f2017-12-28 13:44:52 +00001/*
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 */
17
18package org.onosproject.drivers.bmv2.impl;
19
20import com.fasterxml.jackson.databind.ObjectMapper;
21import com.google.common.collect.Sets;
22import org.onosproject.core.GroupId;
23import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreGroup;
24import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreJsonGroups;
25import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreNode;
26import org.onosproject.net.PortNumber;
27import org.onosproject.net.flow.TrafficTreatment;
28import org.onosproject.net.flow.instructions.Instructions;
29import org.onosproject.net.group.Group;
30import org.onosproject.net.group.GroupDescription;
31
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.HashSet;
36import java.util.List;
37import java.util.Set;
38import java.util.SortedSet;
39import java.util.TreeSet;
40
41/**
42 * Implementation of BMv2 PRE group translation logic.
43 */
44public final class Bmv2PreGroupTranslatorImpl {
45
46 private static final int BMV2_PORT_MAP_SIZE = 256;
47
48 //hidden constructor
49 private Bmv2PreGroupTranslatorImpl() {
50 }
51
52 /**
53 * Returns a BMv2 PRE group equivalent to given group.
54 *
55 * @param group group
56 * @return a BMv2 PRE group
57 */
58 public static Bmv2PreGroup translate(Group group) {
59 if (!group.type().equals(GroupDescription.Type.ALL)) {
Ray Milkeydbd38212018-07-02 09:18:09 -070060 throw new IllegalStateException("Unable to translate the group to BMv2 PRE group." +
Esin Karaman971fb7f2017-12-28 13:44:52 +000061 "A BMv2 PRE group is to be of ALL type. GroupId="
62 + group.id());
63 }
64
65 Bmv2PreGroup.Bmv2PreGroupBuilder bmv2PreGroupBuilder = Bmv2PreGroup.builder();
66 bmv2PreGroupBuilder.withGroupId(group.id().id());
67 // RID is generated per an MG since L2 broadcast can be considered as a MG that have a single RID.
68 // for simplicity RID will be assigned to zero for any node in an MG.
69 int replicationId = 0;
70
71 Set<PortNumber> outPorts = Sets.newHashSet();
72 group.buckets().buckets().forEach(groupBucket -> {
73 //get output instructions of the bucket
74 Set<Instructions.OutputInstruction> outputInstructions = getOutputInstructions(groupBucket.treatment());
75 //check validity of output instructions
76 checkOutputInstructions(group.id(), outputInstructions);
77 outPorts.add(outputInstructions.iterator().next().port());
78 });
79 validatePorts(outPorts);
80 bmv2PreGroupBuilder.addNode(Bmv2PreNode.builder()
81 .withRid(replicationId)
82 .withPortMap(buildPortMap(outPorts))
83 .build());
84 return bmv2PreGroupBuilder.build();
85 }
86
87 /**
88 * Converts a PRE group list in JSON notation to list of Bmv2PreGroups.
89 *
90 * @param groupListJson group list string ing JSON notation
91 * @return list of Bmv2PreGroups
92 * @throws IOException in case JSON string can not be parsed
93 */
94 public static List<Bmv2PreGroup> translate(String groupListJson) throws IOException {
95 List<Bmv2PreGroup> preGroups = new ArrayList<>();
96 if (groupListJson == null) {
97 return preGroups;
98 }
99 ObjectMapper mapper = new ObjectMapper();
100 Bmv2PreJsonGroups bmv2PreJsonGroups = mapper.readValue(groupListJson, Bmv2PreJsonGroups.class);
101
102 Bmv2PreGroup.Bmv2PreGroupBuilder bmv2PreGroupBuilder;
103 for (Bmv2PreJsonGroups.Mgrp mgrp : bmv2PreJsonGroups.mgrps) {
104
105 bmv2PreGroupBuilder = Bmv2PreGroup.builder();
106 bmv2PreGroupBuilder.withGroupId(mgrp.id);
107
108 for (int l1handleId : mgrp.l1handles) {
109 Bmv2PreJsonGroups.L1Handle l1handle = getL1Handle(l1handleId, bmv2PreJsonGroups.l1handles);
110 if (l1handle == null) {
111 continue;
112 }
113 Bmv2PreJsonGroups.L2Handle l2handle = getL2Handle(l1handle.l2handle, bmv2PreJsonGroups.l2handles);
114 if (l2handle == null) {
115 continue;
116 }
117 bmv2PreGroupBuilder.addNode(Bmv2PreNode.builder()
118 .withRid(l1handle.rid)
119 .withPortMap(buildPortMap(l2handle.ports))
120 .withL1Handle(l1handleId)
121 .build());
122 }
123 preGroups.add(bmv2PreGroupBuilder.build());
124 }
125 return preGroups;
126 }
127
128 /**
129 * Retrieves L1Handle object pointed by given L1 handle pointer from L1 handles list.
130 *
131 * @param l1handlePointer pointer to a L1 handle
132 * @param l1handles list of L1 handles
133 * @return L1 handle object if exists; null otherwise
134 */
135 private static Bmv2PreJsonGroups.L1Handle getL1Handle(int l1handlePointer,
136 Bmv2PreJsonGroups.L1Handle[] l1handles) {
137 for (Bmv2PreJsonGroups.L1Handle l1Handle : l1handles) {
138 if (l1handlePointer == l1Handle.handle) {
139 return l1Handle;
140 }
141 }
142 return null;
143 }
144
145 /**
146 * Retrieves L2Handle object pointed by given L2 handle pointer from L2 handles list.
147 *
148 * @param l2handlePointer pointer to a L2 handle
149 * @param l2handles list of L2 handles
150 * @return L2 handle object if exists; null otherwise
151 */
152 private static Bmv2PreJsonGroups.L2Handle getL2Handle(int l2handlePointer,
153 Bmv2PreJsonGroups.L2Handle[] l2handles) {
154 for (Bmv2PreJsonGroups.L2Handle l2handle : l2handles) {
155 if (l2handlePointer == l2handle.handle) {
156 return l2handle;
157 }
158 }
159 return null;
160 }
161
162 /**
163 * Builds a port map string composing of 1 and 0s.
164 * BMv2 API reads a port map from most significant to least significant char.
165 * Index of 1s indicates port numbers.
166 * For example, if port numbers are 4,3 and 1, the generated port map string looks like 11010.
167 *
168 * @param outPorts set of output port numbers
169 * @return port map string
170 */
171 private static String buildPortMap(Set<PortNumber> outPorts) {
172 //Sorts port numbers in descending order
173 SortedSet<PortNumber> outPortsSorted = new TreeSet<>((o1, o2) -> (int) (o2.toLong() - o1.toLong()));
174 outPortsSorted.addAll(outPorts);
175 PortNumber biggestPort = outPortsSorted.iterator().next();
176 int portMapSize = (int) biggestPort.toLong() + 1;
177 //init and fill port map with zero characters
178 char[] portMap = new char[portMapSize];
179 Arrays.fill(portMap, '0');
180 //fill in the ports with 1
181 outPortsSorted.forEach(portNumber -> portMap[portMapSize - (int) portNumber.toLong() - 1] = '1');
182 return String.valueOf(portMap);
183 }
184
185 /**
186 * Builds a port map string composing of 1 and 0s.
187 * BMv2 API reads a port map from most significant to least significant char.
188 * The index of 1s indicates port numbers.
189 * For example, if port numbers are 4,3 and 1, the generated port map string looks like 11010.
190 *
191 * @param portArr array of output port numbers
192 * @return port map string
193 */
194 private static String buildPortMap(int[] portArr) {
195 Set<PortNumber> outPorts = new HashSet<>();
196 for (int port : portArr) {
197 outPorts.add(PortNumber.portNumber(port));
198 }
199 return buildPortMap(outPorts);
200 }
201
202 /**
203 * Retrieves output instructions out of the instruction set of the given traffic treatment.
204 *
205 * @param trafficTreatment
206 * @return set of output instructions
207 */
208 private static Set<Instructions.OutputInstruction> getOutputInstructions(TrafficTreatment trafficTreatment) {
209 if (trafficTreatment == null ||
210 trafficTreatment.allInstructions() == null) {
211 return Sets.newHashSet();
212 }
213 Set<Instructions.OutputInstruction> resultList = Sets.newHashSet();
214 trafficTreatment.allInstructions().forEach(instruction -> {
215 if (instruction instanceof Instructions.OutputInstruction) {
216 resultList.add((Instructions.OutputInstruction) instruction);
217 }
218 });
219 return resultList;
220 }
221
222 /**
223 * Checks validity of output instructions of a bucket.
224 * A bucket of a an ALL group must only have one output instruction.
225 * Other conditions can not pass the validation.
226 *
227 * @param groupId group identifier
228 * @param outputInstructions set of output instructions
229 * @throws RuntimeException if the instructions can not be validated
230 */
231 private static void checkOutputInstructions(GroupId groupId,
232 Set<Instructions.OutputInstruction> outputInstructions) {
233 if (outputInstructions.isEmpty()) {
Ray Milkeydbd38212018-07-02 09:18:09 -0700234 throw new IllegalStateException(String.format("Group bucket contains no output instruction. GroupId=%s",
Esin Karaman971fb7f2017-12-28 13:44:52 +0000235 groupId));
236 }
237 if (outputInstructions.size() != 1) {
Ray Milkeydbd38212018-07-02 09:18:09 -0700238 throw new IllegalStateException(String.format("Group bucket contains more than one output instructions. " +
Esin Karaman971fb7f2017-12-28 13:44:52 +0000239 "Only one is supported. GroupId=%s", groupId));
240 }
241 }
242
243 /**
244 * Checks whether a port number is a valid physical BMv2 port or not.
245 * If not, throws RuntimeException; does nothing otherwise.
246 *
247 * @param portNumber port number
248 * @throws RuntimeException iff the port number can not be validated
249 */
250 private static void validatePort(PortNumber portNumber) {
251 if (portNumber.toLong() < 0 || portNumber.toLong() >= BMV2_PORT_MAP_SIZE) {
Ray Milkeydbd38212018-07-02 09:18:09 -0700252 throw new IllegalStateException(String.format("Port number %d is not a valid BMv2 physical port number." +
Esin Karaman971fb7f2017-12-28 13:44:52 +0000253 "Valid port range is [0,255]", portNumber));
254 }
255 }
256
257 /**
258 * Checks whether a port number is a valid physical BMv2 port or not.
259 * If not, throws RuntimeException; does nothing otherwise.
260 *
261 * @param portNumbers port number set
262 * @throws RuntimeException iff a port number can not be validated
263 */
264 private static void validatePorts(Set<PortNumber> portNumbers) {
265 //validate one by one
266 for (PortNumber portNumber : portNumbers) {
267 validatePort(portNumber);
268 }
269 }
270}