blob: 2e6fa5f956a80d75126ff791e1e467a793fa79d9 [file] [log] [blame]
Carmelo Cascone87b9b392017-10-02 18:33:20 +02001/*
2 * Copyright 2017-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.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.testing.EqualsTester;
22import org.junit.Before;
23import org.junit.Test;
24import org.onlab.packet.MacAddress;
25import org.onlab.util.ImmutableByteSequence;
26import org.onosproject.TestApplicationId;
27import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.DefaultApplicationId;
30import org.onosproject.core.GroupId;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.instructions.Instructions;
40import org.onosproject.net.group.DefaultGroup;
41import org.onosproject.net.group.DefaultGroupBucket;
42import org.onosproject.net.group.DefaultGroupDescription;
43import org.onosproject.net.group.Group;
44import org.onosproject.net.group.GroupBucket;
45import org.onosproject.net.group.GroupBuckets;
46import org.onosproject.net.group.GroupDescription;
47import org.onosproject.net.pi.model.DefaultPiPipeconf;
48import org.onosproject.net.pi.model.PiPipeconf;
49import org.onosproject.net.pi.model.PiPipeconfId;
50import org.onosproject.net.pi.model.PiPipelineInterpreter;
51import org.onosproject.net.pi.runtime.PiAction;
52import org.onosproject.net.pi.runtime.PiActionGroup;
53import org.onosproject.net.pi.runtime.PiActionGroupMember;
54import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
55import org.onosproject.net.pi.runtime.PiActionId;
56import org.onosproject.net.pi.runtime.PiActionParam;
57import org.onosproject.net.pi.runtime.PiActionParamId;
58import org.onosproject.net.pi.runtime.PiActionProfileId;
59import org.onosproject.net.pi.runtime.PiGroupKey;
60import org.onosproject.net.pi.runtime.PiTableAction;
61import org.onosproject.net.pi.runtime.PiTableEntry;
62import org.onosproject.net.pi.runtime.PiTableId;
63import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
64
65import java.util.Collection;
66import java.util.List;
67import java.util.Optional;
68import java.util.Random;
69
70import static org.hamcrest.CoreMatchers.equalTo;
71import static org.hamcrest.CoreMatchers.is;
72import static org.hamcrest.MatcherAssert.assertThat;
73import static org.onlab.util.ImmutableByteSequence.copyFrom;
74import static org.onlab.util.ImmutableByteSequence.fit;
75import static org.onosproject.net.group.GroupDescription.Type.SELECT;
76import static org.onosproject.net.pi.impl.MockInterpreter.*;
77import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
78
79/**
80 * Tests for {@link PiFlowRuleTranslator}.
81 */
82@SuppressWarnings("ConstantConditions")
83public class PiTranslatorServiceTest {
84
85 private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
86 private static final short IN_PORT_MASK = 0x01ff; // 9-bit mask
87 private static final short ETH_TYPE_MASK = (short) 0xffff;
88 private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:dummy:1");
89 private static final ApplicationId APP_ID = TestApplicationId.create("dummy");
90 private static final PiTableId ECMP_TABLE_ID = PiTableId.of("ecmp");
91 private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");
92 private static final GroupId GROUP_ID = GroupId.valueOf(1);
93 private static final PiActionId EGRESS_PORT_ACTION_ID = PiActionId.of("set_egress_port");
94 private static final int PORT_BITWIDTH = 9;
95 private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
96 private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(outputBucket(1),
97 outputBucket(2),
98 outputBucket(3)
99 );
100 private static final PiGroupKey GROUP_KEY = new PiGroupKey(ECMP_TABLE_ID, ACT_PROF_ID, GROUP_ID.id());
101 private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
102 private static final GroupDescription GROUP_DESC =
103 new DefaultGroupDescription(DEVICE_ID, SELECT, BUCKETS, GROUP_KEY, GROUP_ID.id(), APP_ID);
104 private static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_DESC);
105 private static final int DEFAULT_MEMBER_WEIGHT = 1;
106 private static final int BASE_MEM_ID = 65535;
107 private Collection<PiActionGroupMember> expectedMembers;
108
109 private Random random = new Random();
110 private PiPipeconf pipeconf;
111
112 @Before
113 public void setUp() throws Exception {
114 pipeconf = DefaultPiPipeconf.builder()
115 .withId(new PiPipeconfId("mock-pipeconf"))
116 .withPipelineModel(Bmv2PipelineModelParser.parse(this.getClass().getResource(BMV2_JSON_PATH)))
117 .addBehaviour(PiPipelineInterpreter.class, MockInterpreter.class)
118 .build();
119
120 expectedMembers = ImmutableSet.of(outputMember(1),
121 outputMember(2),
122 outputMember(3));
123 }
124
125 @Test
126 public void testTranslateFlowRules() throws Exception {
127
128 ApplicationId appId = new DefaultApplicationId(1, "test");
129 int tableId = 0;
130 MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
131 MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
132 short ethType = (short) (0x0000FFFF & random.nextInt());
133 short outPort = (short) random.nextInt(65);
134 short inPort = (short) random.nextInt(65);
135 int timeout = random.nextInt(100);
136 int priority = random.nextInt(100);
137
138 TrafficSelector matchInPort1 = DefaultTrafficSelector
139 .builder()
140 .matchInPort(PortNumber.portNumber(inPort))
141 .matchEthDst(ethDstMac)
142 .matchEthSrc(ethSrcMac)
143 .matchEthType(ethType)
144 .build();
145
146 TrafficTreatment outPort2 = DefaultTrafficTreatment
147 .builder()
148 .setOutput(PortNumber.portNumber(outPort))
149 .build();
150
151 FlowRule rule1 = DefaultFlowRule.builder()
152 .forDevice(DEVICE_ID)
153 .forTable(tableId)
154 .fromApp(appId)
155 .withSelector(matchInPort1)
156 .withTreatment(outPort2)
157 .makeTemporary(timeout)
158 .withPriority(priority)
159 .build();
160
161 FlowRule rule2 = DefaultFlowRule.builder()
162 .forDevice(DEVICE_ID)
163 .forTable(tableId)
164 .fromApp(appId)
165 .withSelector(matchInPort1)
166 .withTreatment(outPort2)
167 .makeTemporary(timeout)
168 .withPriority(priority)
169 .build();
170
171 PiTableEntry entry1 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
172 PiTableEntry entry2 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
173
174 // check equality, i.e. same rules must produce same entries
175 new EqualsTester()
176 .addEqualityGroup(rule1, rule2)
177 .addEqualityGroup(entry1, entry2)
178 .testEquals();
179
180 int numMatchParams = pipeconf.pipelineModel().table(TABLE0).get().matchFields().size();
181 // parse values stored in entry1
182 PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(IN_PORT_ID).get();
183 PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_DST_ID).get();
184 PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_SRC_ID).get();
185 PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_TYPE_ID).get();
186 Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TABLE0).get().supportsAging()
187 ? Optional.of((double) rule1.timeout()) : Optional.empty();
188
189 // check that the number of parameters in the entry is the same as the number of table keys
190 assertThat("Incorrect number of match parameters",
191 entry1.matchKey().fieldMatches().size(), is(equalTo(numMatchParams)));
192
193 // check that values stored in entry are the same used for the flow rule
194 assertThat("Incorrect inPort match param value",
195 inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
196 assertThat("Incorrect inPort match param mask",
197 inPortParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(IN_PORT_MASK)));
198 assertThat("Incorrect ethDestMac match param value",
199 ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
200 assertThat("Incorrect ethDestMac match param mask",
201 ethDstParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
202 assertThat("Incorrect ethSrcMac match param value",
203 ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
204 assertThat("Incorrect ethSrcMac match param mask",
205 ethSrcParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
206 assertThat("Incorrect ethType match param value",
207 ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
208 assertThat("Incorrect ethType match param mask",
209 ethTypeParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(ETH_TYPE_MASK)));
210 assertThat("Incorrect priority value",
211 entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
212 assertThat("Incorrect timeout value",
213 entry1.timeout(), is(equalTo(expectedTimeout)));
214
215 }
216
217 private static GroupBucket outputBucket(int portNum) {
218 ImmutableByteSequence paramVal = copyFrom(portNum);
219 PiActionParam param = new PiActionParam(PiActionParamId.of(PORT_PARAM_ID.name()), paramVal);
220 PiTableAction action = PiAction.builder().withId(EGRESS_PORT_ACTION_ID).withParameter(param).build();
221 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
222 .add(Instructions.piTableAction(action))
223 .build();
224 return DefaultGroupBucket.createSelectGroupBucket(treatment);
225 }
226
227 private static PiActionGroupMember outputMember(int portNum)
228 throws ImmutableByteSequence.ByteSequenceTrimException {
229 PiActionParam param = new PiActionParam(PORT_PARAM_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
230 PiAction piAction = PiAction.builder()
231 .withId(EGRESS_PORT_ACTION_ID)
232 .withParameter(param).build();
233 return PiActionGroupMember.builder()
234 .withAction(piAction)
235 .withId(PiActionGroupMemberId.of(BASE_MEM_ID + portNum))
236 .withWeight(DEFAULT_MEMBER_WEIGHT)
237 .build();
238 }
239
240 /**
241 * Test add group with buckets.
242 */
243 @Test
244 public void testTranslateGroups() throws Exception {
245
246 PiActionGroup piGroup1 = PiGroupTranslator.translate(GROUP, pipeconf, null);
247 PiActionGroup piGroup2 = PiGroupTranslator.translate(GROUP, pipeconf, null);
248
249 new EqualsTester()
250 .addEqualityGroup(piGroup1, piGroup2)
251 .testEquals();
252
253 assertThat("Group ID must be equal",
254 piGroup1.id().id(), is(equalTo(GROUP_ID.id())));
255 assertThat("Group type must be SELECT",
256 piGroup1.type(), is(equalTo(PiActionGroup.Type.SELECT)));
257 assertThat("Action profile ID must be equal",
258 piGroup1.actionProfileId(), is(equalTo(ACT_PROF_ID)));
259
260 // members installed
261 Collection<PiActionGroupMember> members = piGroup1.members();
262 assertThat("The number of group members must be equal",
263 piGroup1.members().size(), is(expectedMembers.size()));
264 assertThat("Group members must be equal",
265 members.containsAll(expectedMembers) && expectedMembers.containsAll(members));
266 }
267}