blob: 1d0caee105fad0140c90545b6b6708b83e7e4370 [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;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020027import org.onosproject.core.ApplicationId;
28import org.onosproject.core.DefaultApplicationId;
29import org.onosproject.core.GroupId;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.PortNumber;
32import org.onosproject.net.flow.DefaultFlowRule;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flow.instructions.Instructions;
39import org.onosproject.net.group.DefaultGroup;
40import org.onosproject.net.group.DefaultGroupBucket;
41import org.onosproject.net.group.DefaultGroupDescription;
42import org.onosproject.net.group.Group;
43import org.onosproject.net.group.GroupBucket;
44import org.onosproject.net.group.GroupBuckets;
45import org.onosproject.net.group.GroupDescription;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020046import org.onosproject.net.pi.model.PiPipeconf;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020047import org.onosproject.net.pi.runtime.PiAction;
48import org.onosproject.net.pi.runtime.PiActionGroup;
49import org.onosproject.net.pi.runtime.PiActionGroupMember;
50import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020051import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020052import org.onosproject.net.pi.runtime.PiGroupKey;
Carmelo Casconef6c2f052018-03-23 18:02:15 -070053import org.onosproject.net.pi.runtime.PiMatchKey;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020054import org.onosproject.net.pi.runtime.PiTableAction;
55import org.onosproject.net.pi.runtime.PiTableEntry;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020056import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070057import org.onosproject.pipelines.basic.PipeconfLoader;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020058
59import java.util.Collection;
60import java.util.List;
61import java.util.Optional;
62import java.util.Random;
63
64import static org.hamcrest.CoreMatchers.equalTo;
65import static org.hamcrest.CoreMatchers.is;
66import static org.hamcrest.MatcherAssert.assertThat;
67import static org.onlab.util.ImmutableByteSequence.copyFrom;
68import static org.onlab.util.ImmutableByteSequence.fit;
69import static org.onosproject.net.group.GroupDescription.Type.SELECT;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070070import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
71import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
72import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_EGRESS_PORT_ID;
73import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_DST_ID;
74import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_SRC_ID;
75import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_TYPE_ID;
76import static org.onosproject.pipelines.basic.BasicConstants.HDR_IN_PORT_ID;
77import static org.onosproject.pipelines.basic.BasicConstants.PORT_BITWIDTH;
78import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
79import static org.onosproject.pipelines.basic.BasicConstants.TBL_WCMP_TABLE_ID;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020080
81/**
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080082 * Tests for {@link PiFlowRuleTranslatorImpl}.
Carmelo Cascone87b9b392017-10-02 18:33:20 +020083 */
84@SuppressWarnings("ConstantConditions")
85public class PiTranslatorServiceTest {
86
Carmelo Cascone87b9b392017-10-02 18:33:20 +020087 private static final short IN_PORT_MASK = 0x01ff; // 9-bit mask
88 private static final short ETH_TYPE_MASK = (short) 0xffff;
89 private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:dummy:1");
90 private static final ApplicationId APP_ID = TestApplicationId.create("dummy");
Carmelo Cascone87b9b392017-10-02 18:33:20 +020091 private static final GroupId GROUP_ID = GroupId.valueOf(1);
Carmelo Cascone87b9b392017-10-02 18:33:20 +020092 private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(outputBucket(1),
93 outputBucket(2),
94 outputBucket(3)
95 );
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070096 private static final PiGroupKey GROUP_KEY = new PiGroupKey(TBL_WCMP_TABLE_ID, ACT_PRF_WCMP_SELECTOR_ID,
97 GROUP_ID.id());
Carmelo Cascone87b9b392017-10-02 18:33:20 +020098 private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
99 private static final GroupDescription GROUP_DESC =
100 new DefaultGroupDescription(DEVICE_ID, SELECT, BUCKETS, GROUP_KEY, GROUP_ID.id(), APP_ID);
101 private static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_DESC);
102 private static final int DEFAULT_MEMBER_WEIGHT = 1;
103 private static final int BASE_MEM_ID = 65535;
104 private Collection<PiActionGroupMember> expectedMembers;
105
106 private Random random = new Random();
107 private PiPipeconf pipeconf;
108
109 @Before
110 public void setUp() throws Exception {
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700111 pipeconf = PipeconfLoader.BASIC_PIPECONF;
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200112 expectedMembers = ImmutableSet.of(outputMember(1),
113 outputMember(2),
114 outputMember(3));
115 }
116
117 @Test
118 public void testTranslateFlowRules() throws Exception {
119
120 ApplicationId appId = new DefaultApplicationId(1, "test");
121 int tableId = 0;
122 MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
123 MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
124 short ethType = (short) (0x0000FFFF & random.nextInt());
125 short outPort = (short) random.nextInt(65);
126 short inPort = (short) random.nextInt(65);
127 int timeout = random.nextInt(100);
128 int priority = random.nextInt(100);
129
130 TrafficSelector matchInPort1 = DefaultTrafficSelector
131 .builder()
132 .matchInPort(PortNumber.portNumber(inPort))
133 .matchEthDst(ethDstMac)
134 .matchEthSrc(ethSrcMac)
135 .matchEthType(ethType)
136 .build();
137
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700138 TrafficSelector emptySelector = DefaultTrafficSelector
139 .builder().build();
140
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200141 TrafficTreatment outPort2 = DefaultTrafficTreatment
142 .builder()
143 .setOutput(PortNumber.portNumber(outPort))
144 .build();
145
146 FlowRule rule1 = DefaultFlowRule.builder()
147 .forDevice(DEVICE_ID)
148 .forTable(tableId)
149 .fromApp(appId)
150 .withSelector(matchInPort1)
151 .withTreatment(outPort2)
152 .makeTemporary(timeout)
153 .withPriority(priority)
154 .build();
155
156 FlowRule rule2 = DefaultFlowRule.builder()
157 .forDevice(DEVICE_ID)
158 .forTable(tableId)
159 .fromApp(appId)
160 .withSelector(matchInPort1)
161 .withTreatment(outPort2)
162 .makeTemporary(timeout)
163 .withPriority(priority)
164 .build();
165
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700166 FlowRule defActionRule = DefaultFlowRule.builder()
167 .forDevice(DEVICE_ID)
168 .forTable(tableId)
169 .fromApp(appId)
170 .withSelector(emptySelector)
171 .withTreatment(outPort2)
172 .makeTemporary(timeout)
173 .withPriority(priority)
174 .build();
175
Carmelo Cascone326ad2d2017-11-28 18:09:13 -0800176 PiTableEntry entry1 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700177 PiTableEntry entry2 = PiFlowRuleTranslatorImpl.translate(rule2, pipeconf, null);
178 PiTableEntry defActionEntry = PiFlowRuleTranslatorImpl.translate(defActionRule, pipeconf, null);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200179
180 // check equality, i.e. same rules must produce same entries
181 new EqualsTester()
182 .addEqualityGroup(rule1, rule2)
183 .addEqualityGroup(entry1, entry2)
184 .testEquals();
185
Carmelo Cascone87892e22017-11-13 16:01:29 -0800186 int numMatchParams = pipeconf.pipelineModel().table(TBL_TABLE0_ID).get().matchFields().size();
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200187 // parse values stored in entry1
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700188 PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_IN_PORT_ID).get();
189 PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_DST_ID).get();
190 PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_SRC_ID).get();
191 PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_TYPE_ID).get();
Carmelo Cascone87892e22017-11-13 16:01:29 -0800192 Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TBL_TABLE0_ID).get().supportsAging()
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200193 ? Optional.of((double) rule1.timeout()) : Optional.empty();
194
195 // check that the number of parameters in the entry is the same as the number of table keys
196 assertThat("Incorrect number of match parameters",
197 entry1.matchKey().fieldMatches().size(), is(equalTo(numMatchParams)));
198
199 // check that values stored in entry are the same used for the flow rule
200 assertThat("Incorrect inPort match param value",
201 inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
202 assertThat("Incorrect inPort match param mask",
203 inPortParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(IN_PORT_MASK)));
204 assertThat("Incorrect ethDestMac match param value",
205 ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
206 assertThat("Incorrect ethDestMac match param mask",
207 ethDstParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
208 assertThat("Incorrect ethSrcMac match param value",
209 ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
210 assertThat("Incorrect ethSrcMac match param mask",
211 ethSrcParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
212 assertThat("Incorrect ethType match param value",
213 ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
214 assertThat("Incorrect ethType match param mask",
215 ethTypeParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(ETH_TYPE_MASK)));
Yi Tseng02c4c572018-01-22 17:52:10 -0800216 // FIXME: re-enable when P4Runtime priority handling will be moved out of transltion service
217 // see PiFlowRuleTranslatorImpl
218 // assertThat("Incorrect priority value",
219 // entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200220 assertThat("Incorrect timeout value",
221 entry1.timeout(), is(equalTo(expectedTimeout)));
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700222 assertThat("Match key should be empty",
223 defActionEntry.matchKey(), is(equalTo(PiMatchKey.EMPTY)));
224 assertThat("Priority should not be set", !defActionEntry.priority().isPresent());
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200225
226 }
227
228 private static GroupBucket outputBucket(int portNum) {
229 ImmutableByteSequence paramVal = copyFrom(portNum);
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700230 PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, paramVal);
231 PiTableAction action = PiAction.builder().withId(ACT_SET_EGRESS_PORT_ID).withParameter(param).build();
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200232 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
233 .add(Instructions.piTableAction(action))
234 .build();
235 return DefaultGroupBucket.createSelectGroupBucket(treatment);
236 }
237
238 private static PiActionGroupMember outputMember(int portNum)
239 throws ImmutableByteSequence.ByteSequenceTrimException {
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700240 PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200241 PiAction piAction = PiAction.builder()
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700242 .withId(ACT_SET_EGRESS_PORT_ID)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200243 .withParameter(param).build();
244 return PiActionGroupMember.builder()
245 .withAction(piAction)
246 .withId(PiActionGroupMemberId.of(BASE_MEM_ID + portNum))
247 .withWeight(DEFAULT_MEMBER_WEIGHT)
248 .build();
249 }
250
251 /**
252 * Test add group with buckets.
253 */
254 @Test
255 public void testTranslateGroups() throws Exception {
256
Carmelo Cascone326ad2d2017-11-28 18:09:13 -0800257 PiActionGroup piGroup1 = PiGroupTranslatorImpl.translate(GROUP, pipeconf, null);
258 PiActionGroup piGroup2 = PiGroupTranslatorImpl.translate(GROUP, pipeconf, null);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200259
260 new EqualsTester()
261 .addEqualityGroup(piGroup1, piGroup2)
262 .testEquals();
263
264 assertThat("Group ID must be equal",
265 piGroup1.id().id(), is(equalTo(GROUP_ID.id())));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200266 assertThat("Action profile ID must be equal",
Carmelo Casconeca94bcf2017-10-27 14:16:59 -0700267 piGroup1.actionProfileId(), is(equalTo(ACT_PRF_WCMP_SELECTOR_ID)));
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200268
269 // members installed
270 Collection<PiActionGroupMember> members = piGroup1.members();
271 assertThat("The number of group members must be equal",
272 piGroup1.members().size(), is(expectedMembers.size()));
273 assertThat("Group members must be equal",
274 members.containsAll(expectedMembers) && expectedMembers.containsAll(members));
275 }
276}