blob: 1ee881b47da0aeeeb196c35639b1df14e491d4fa [file] [log] [blame]
Yi Tseng82512da2017-08-16 19:46:36 -07001/*
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.p4runtime.ctl;
18
19import com.google.common.collect.ImmutableList;
Yi Tseng82512da2017-08-16 19:46:36 -070020import com.google.common.collect.Lists;
21import com.google.protobuf.ByteString;
22import io.grpc.ManagedChannel;
23import io.grpc.Server;
24import io.grpc.inprocess.InProcessChannelBuilder;
25import io.grpc.inprocess.InProcessServerBuilder;
26import io.grpc.internal.AbstractServerImplBuilder;
27import org.easymock.EasyMock;
Carmelo Casconec2be50a2019-04-10 00:15:39 -070028import org.junit.After;
Yi Tseng82512da2017-08-16 19:46:36 -070029import org.junit.AfterClass;
30import org.junit.Before;
31import org.junit.BeforeClass;
32import org.junit.Test;
33import org.onlab.util.ImmutableByteSequence;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.pi.model.DefaultPiPipeconf;
Carmelo Cascone87892e22017-11-13 16:01:29 -080036import org.onosproject.net.pi.model.PiActionId;
37import org.onosproject.net.pi.model.PiActionParamId;
38import org.onosproject.net.pi.model.PiActionProfileId;
Yi Tseng82512da2017-08-16 19:46:36 -070039import org.onosproject.net.pi.model.PiPipeconf;
40import org.onosproject.net.pi.model.PiPipeconfId;
41import org.onosproject.net.pi.model.PiPipelineModel;
Yi Tseng82512da2017-08-16 19:46:36 -070042import org.onosproject.net.pi.runtime.PiAction;
Yi Tseng82512da2017-08-16 19:46:36 -070043import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070044import org.onosproject.net.pi.runtime.PiActionProfileGroup;
45import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
46import org.onosproject.net.pi.runtime.PiActionProfileMember;
47import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
Miguel Borges de Freitas757307b2021-10-20 17:12:02 +010048import org.onosproject.p4runtime.api.P4RuntimeWriteClient;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080049import org.onosproject.p4runtime.ctl.client.P4RuntimeClientImpl;
50import org.onosproject.p4runtime.ctl.controller.P4RuntimeControllerImpl;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020051import p4.v1.P4RuntimeOuterClass.ActionProfileGroup;
52import p4.v1.P4RuntimeOuterClass.ActionProfileMember;
53import p4.v1.P4RuntimeOuterClass.Entity;
54import p4.v1.P4RuntimeOuterClass.Uint128;
55import p4.v1.P4RuntimeOuterClass.Update;
56import p4.v1.P4RuntimeOuterClass.WriteRequest;
Yi Tseng82512da2017-08-16 19:46:36 -070057
58import java.io.IOException;
59import java.net.URL;
60import java.util.Collection;
61import java.util.List;
62import java.util.concurrent.CompletableFuture;
63import java.util.concurrent.TimeUnit;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020064import java.util.stream.Collectors;
Yi Tseng82512da2017-08-16 19:46:36 -070065
Carmelo Cascone87b9b392017-10-02 18:33:20 +020066import static org.easymock.EasyMock.niceMock;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070067import static org.junit.Assert.assertEquals;
68import static org.junit.Assert.assertNotNull;
69import static org.junit.Assert.assertTrue;
Yi Tseng82512da2017-08-16 19:46:36 -070070import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020071import static p4.v1.P4RuntimeOuterClass.Action;
72import static p4.v1.P4RuntimeOuterClass.ReadResponse;
Yi Tseng82512da2017-08-16 19:46:36 -070073
74/**
75 * Tests for P4 Runtime Action Profile Group support.
76 */
77public class P4RuntimeGroupTest {
78 private static final String PIPECONF_ID = "p4runtime-mock-pipeconf";
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070079 private static final String P4INFO_PATH = "/test.p4info";
Yi Tseng82512da2017-08-16 19:46:36 -070080 private static final PiPipeconf PIPECONF = buildPipeconf();
81 private static final int P4_INFO_ACT_PROF_ID = 285227860;
82 private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");
Carmelo Casconecb4327a2018-09-11 15:17:23 -070083 private static final PiActionProfileGroupId GROUP_ID = PiActionProfileGroupId.of(1);
Yi Tseng82512da2017-08-16 19:46:36 -070084 private static final int DEFAULT_MEMBER_WEIGHT = 1;
85 private static final PiActionId EGRESS_PORT_ACTION_ID = PiActionId.of("set_egress_port");
86 private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
87 private static final int BASE_MEM_ID = 65535;
88 private static final List<Integer> MEMBER_IDS = ImmutableList.of(65536, 65537, 65538);
Carmelo Cascone99c59db2019-01-17 15:39:35 -080089 private static final List<PiActionProfileMember> GROUP_MEMBER_INSTANCES =
Carmelo Casconee44592f2018-09-12 02:24:47 -070090 Lists.newArrayList(
Yi Tseng82512da2017-08-16 19:46:36 -070091 outputMember((short) 1),
92 outputMember((short) 2),
93 outputMember((short) 3)
94 );
Carmelo Cascone99c59db2019-01-17 15:39:35 -080095 private static final List<PiActionProfileGroup.WeightedMember> GROUP_WEIGHTED_MEMBERS =
96 GROUP_MEMBER_INSTANCES.stream()
97 .map(m -> new PiActionProfileGroup.WeightedMember(m, DEFAULT_MEMBER_WEIGHT))
98 .collect(Collectors.toList());
Carmelo Casconecb4327a2018-09-11 15:17:23 -070099 private static final PiActionProfileGroup GROUP = PiActionProfileGroup.builder()
Yi Tseng82512da2017-08-16 19:46:36 -0700100 .withId(GROUP_ID)
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800101 .addMembers(GROUP_MEMBER_INSTANCES)
Yi Tseng82512da2017-08-16 19:46:36 -0700102 .withActionProfileId(ACT_PROF_ID)
Yi Tseng82512da2017-08-16 19:46:36 -0700103 .build();
104 private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:p4runtime:1");
105 private static final int P4_DEVICE_ID = 1;
106 private static final int SET_EGRESS_PORT_ID = 16794308;
107 private static final String GRPC_SERVER_NAME = "P4RuntimeGroupTest";
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200108 private static final long DEFAULT_TIMEOUT_TIME = 10;
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800109 private static final Uint128 DEFAULT_ELECTION_ID = Uint128.getDefaultInstance();
Yi Tseng82512da2017-08-16 19:46:36 -0700110
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800111 private org.onosproject.p4runtime.ctl.client.P4RuntimeClientImpl client;
Yi Tseng82512da2017-08-16 19:46:36 -0700112 private P4RuntimeControllerImpl controller;
113 private static MockP4RuntimeServer p4RuntimeServerImpl = new MockP4RuntimeServer();
114 private static Server grpcServer;
115 private static ManagedChannel grpcChannel;
116
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700117 private static PiActionProfileMember outputMember(short portNum) {
Yi Tseng82512da2017-08-16 19:46:36 -0700118 PiActionParam param = new PiActionParam(PORT_PARAM_ID,
119 ImmutableByteSequence.copyFrom(portNum));
120 PiAction piAction = PiAction.builder()
121 .withId(EGRESS_PORT_ACTION_ID)
122 .withParameter(param).build();
123
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700124 return PiActionProfileMember.builder()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700125 .forActionProfile(ACT_PROF_ID)
Yi Tseng82512da2017-08-16 19:46:36 -0700126 .withAction(piAction)
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700127 .withId(PiActionProfileMemberId.of(BASE_MEM_ID + portNum))
Yi Tseng82512da2017-08-16 19:46:36 -0700128 .build();
129 }
130
131 private static PiPipeconf buildPipeconf() {
132 final URL p4InfoUrl = P4RuntimeGroupTest.class.getResource(P4INFO_PATH);
133 return DefaultPiPipeconf.builder()
134 .withId(new PiPipeconfId(PIPECONF_ID))
135 .withPipelineModel(EasyMock.niceMock(PiPipelineModel.class))
136 .addExtension(P4_INFO_TEXT, p4InfoUrl)
137 .build();
138 }
139
140 @BeforeClass
141 public static void globalSetup() throws IOException {
142 AbstractServerImplBuilder builder = InProcessServerBuilder
143 .forName(GRPC_SERVER_NAME).directExecutor();
144 builder.addService(p4RuntimeServerImpl);
145 grpcServer = builder.build().start();
146 grpcChannel = InProcessChannelBuilder.forName(GRPC_SERVER_NAME)
147 .directExecutor()
Yi Tseng82512da2017-08-16 19:46:36 -0700148 .build();
149 }
150
151 @AfterClass
Carmelo Casconee5b28722018-06-22 17:28:28 +0200152 public static void globalTearDown() {
Yi Tseng82512da2017-08-16 19:46:36 -0700153 grpcServer.shutdown();
154 grpcChannel.shutdown();
155 }
156
157
158 @Before
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700159 public void setup() {
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800160 controller = niceMock(org.onosproject.p4runtime.ctl.controller.P4RuntimeControllerImpl.class);
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800161 client = new P4RuntimeClientImpl(
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700162 DEVICE_ID, grpcChannel, controller, new MockPipeconfService(),
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800163 new MockMasterElectionIdStore());
Yi Tseng82512da2017-08-16 19:46:36 -0700164 }
165
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700166 @After
167 public void teardown() {
168 client.shutdown();
169 }
170
Yi Tseng82512da2017-08-16 19:46:36 -0700171 @Test
Miguel Borges de Freitas757307b2021-10-20 17:12:02 +0100172 public void testInvalidPiActionProfileMember() {
173 PiActionParam param = new PiActionParam(PORT_PARAM_ID, "invalidString");
174 PiAction piAction = PiAction.builder()
175 .withId(EGRESS_PORT_ACTION_ID)
176 .withParameter(param).build();
177 PiActionProfileMember actionProfileMember = PiActionProfileMember.builder()
178 .forActionProfile(ACT_PROF_ID)
179 .withAction(piAction)
180 .withId(PiActionProfileMemberId.of(BASE_MEM_ID + 1))
181 .build();
182 P4RuntimeWriteClient.WriteRequest writeRequest = client.write(P4_DEVICE_ID, PIPECONF);
183 writeRequest.insert(actionProfileMember);
184 P4RuntimeWriteClient.WriteResponse response = writeRequest.submitSync();
185
186 assertEquals(false, response.isSuccess());
187 assertEquals(1, response.all().size());
Yi Tseng38213992022-03-17 16:04:06 -0700188 assertEquals("Wrong size for param 'port' of action 'set_egress_port', " +
189 "expected no more than 2 bytes, but found 13",
Miguel Borges de Freitas757307b2021-10-20 17:12:02 +0100190 response.all().iterator().next().explanation());
191 }
192
193 @Test
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700194 public void testInsertPiActionProfileGroup() throws Exception {
Yi Tseng82512da2017-08-16 19:46:36 -0700195 CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700196 client.write(P4_DEVICE_ID, PIPECONF).insert(GROUP).submitSync();
197 assertTrue(client.write(P4_DEVICE_ID, PIPECONF).insert(GROUP).submitSync().isSuccess());
Yi Tseng82512da2017-08-16 19:46:36 -0700198 complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
199 WriteRequest result = p4RuntimeServerImpl.getWriteReqs().get(0);
200 assertEquals(1, result.getDeviceId());
201 assertEquals(1, result.getUpdatesCount());
Yi Tseng3e7f1452017-10-20 10:31:53 -0700202 assertEquals(DEFAULT_ELECTION_ID, result.getElectionId());
Yi Tseng82512da2017-08-16 19:46:36 -0700203
204 Update update = result.getUpdatesList().get(0);
205 assertEquals(Update.Type.INSERT, update.getType());
206
207 Entity entity = update.getEntity();
208 ActionProfileGroup actionProfileGroup = entity.getActionProfileGroup();
209 assertNotNull(actionProfileGroup);
210
211 assertEquals(P4_INFO_ACT_PROF_ID, actionProfileGroup.getActionProfileId());
212 assertEquals(3, actionProfileGroup.getMembersCount());
213 List<ActionProfileGroup.Member> members = actionProfileGroup.getMembersList();
214
215 for (ActionProfileGroup.Member member : members) {
216 // XXX: We can't guarantee the order of member, just make sure we
217 // have these member ids
218 assertTrue(MEMBER_IDS.contains(member.getMemberId()));
219 assertEquals(DEFAULT_MEMBER_WEIGHT, member.getWeight());
220 }
221 }
222
223 @Test
224 public void testInsertPiActionMembers() throws Exception {
225 CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700226 assertTrue(client.write(P4_DEVICE_ID, PIPECONF).insert(GROUP_MEMBER_INSTANCES)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800227 .submitSync().isSuccess());
Yi Tseng82512da2017-08-16 19:46:36 -0700228 complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
229 WriteRequest result = p4RuntimeServerImpl.getWriteReqs().get(0);
230 assertEquals(1, result.getDeviceId());
231 assertEquals(3, result.getUpdatesCount());
Yi Tseng3e7f1452017-10-20 10:31:53 -0700232 assertEquals(DEFAULT_ELECTION_ID, result.getElectionId());
Yi Tseng82512da2017-08-16 19:46:36 -0700233
234 List<Update> updates = result.getUpdatesList();
235 for (Update update : updates) {
236 assertEquals(Update.Type.INSERT, update.getType());
237 Entity entity = update.getEntity();
238 ActionProfileMember member = entity.getActionProfileMember();
239 assertNotNull(member);
240 assertEquals(P4_INFO_ACT_PROF_ID, member.getActionProfileId());
241 assertTrue(MEMBER_IDS.contains(member.getMemberId()));
242 Action action = member.getAction();
243 assertEquals(SET_EGRESS_PORT_ID, action.getActionId());
244 assertEquals(1, action.getParamsCount());
245 Action.Param param = action.getParamsList().get(0);
246 assertEquals(1, param.getParamId());
247 byte outPort = (byte) (member.getMemberId() - BASE_MEM_ID);
Yi Tseng38213992022-03-17 16:04:06 -0700248 ByteString bs = ByteString.copyFrom(new byte[]{outPort});
Yi Tseng82512da2017-08-16 19:46:36 -0700249 assertEquals(bs, param.getValue());
250 }
251 }
252
253 @Test
254 public void testReadGroups() throws Exception {
255 ActionProfileGroup.Builder group = ActionProfileGroup.newBuilder()
256 .setGroupId(GROUP_ID.id())
Yi Tseng82512da2017-08-16 19:46:36 -0700257 .setActionProfileId(P4_INFO_ACT_PROF_ID);
258
Yi Tseng82512da2017-08-16 19:46:36 -0700259 MEMBER_IDS.forEach(id -> {
260 ActionProfileGroup.Member member = ActionProfileGroup.Member.newBuilder()
261 .setMemberId(id)
262 .setWeight(DEFAULT_MEMBER_WEIGHT)
263 .build();
264 group.addMembers(member);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800265 });
Yi Tseng82512da2017-08-16 19:46:36 -0700266
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800267 List<ReadResponse> responses = Lists.newArrayList();
268 responses.add(ReadResponse.newBuilder()
269 .addEntities(Entity.newBuilder().setActionProfileGroup(group))
270 .build()
271 );
272
273 p4RuntimeServerImpl.willReturnReadResult(responses);
274 CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700275 Collection<PiActionProfileGroup> groups = client.read(P4_DEVICE_ID, PIPECONF)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800276 .actionProfileGroups(ACT_PROF_ID)
277 .submitSync().all(PiActionProfileGroup.class);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800278 complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800279 assertEquals(1, groups.size());
280 PiActionProfileGroup piActionGroup = groups.iterator().next();
281 assertEquals(ACT_PROF_ID, piActionGroup.actionProfile());
282 assertEquals(GROUP_ID, piActionGroup.id());
283 assertEquals(3, piActionGroup.members().size());
284 assertTrue(GROUP_WEIGHTED_MEMBERS.containsAll(piActionGroup.members()));
285 assertTrue(piActionGroup.members().containsAll(GROUP_WEIGHTED_MEMBERS));
286 }
287
288 @Test
289 public void testReadMembers() throws Exception {
290 List<ActionProfileMember> members = Lists.newArrayList();
291
292 MEMBER_IDS.forEach(id -> {
Yi Tseng82512da2017-08-16 19:46:36 -0700293 byte outPort = (byte) (id - BASE_MEM_ID);
294 ByteString bs = ByteString.copyFrom(new byte[]{0, outPort});
295 Action.Param param = Action.Param.newBuilder()
296 .setParamId(1)
297 .setValue(bs)
298 .build();
299
300 Action action = Action.newBuilder()
301 .setActionId(SET_EGRESS_PORT_ID)
302 .addParams(param)
303 .build();
304
Yi Tseng82512da2017-08-16 19:46:36 -0700305 ActionProfileMember actProfMember =
306 ActionProfileMember.newBuilder()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700307 .setActionProfileId(P4_INFO_ACT_PROF_ID)
Yi Tseng82512da2017-08-16 19:46:36 -0700308 .setMemberId(id)
309 .setAction(action)
310 .build();
311 members.add(actProfMember);
312 });
313
314 List<ReadResponse> responses = Lists.newArrayList();
315 responses.add(ReadResponse.newBuilder()
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200316 .addAllEntities(members.stream()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700317 .map(m -> Entity.newBuilder()
318 .setActionProfileMember(m).build())
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200319 .collect(Collectors.toList()))
320 .build());
Yi Tseng82512da2017-08-16 19:46:36 -0700321
322 p4RuntimeServerImpl.willReturnReadResult(responses);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800323 CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700324 Collection<PiActionProfileMember> piMembers = client.read(P4_DEVICE_ID, PIPECONF)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800325 .actionProfileMembers(ACT_PROF_ID).submitSync()
326 .all(PiActionProfileMember.class);
Yi Tseng82512da2017-08-16 19:46:36 -0700327 complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800328 assertEquals(3, piMembers.size());
329 assertTrue(GROUP_MEMBER_INSTANCES.containsAll(piMembers));
330 assertTrue(piMembers.containsAll(GROUP_MEMBER_INSTANCES));
Yi Tseng82512da2017-08-16 19:46:36 -0700331 }
332}