blob: 1da39971083321e224bd763b36e6e8f610b6f2de [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.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.testing.EqualsTester;
22import org.junit.Before;
Daniele Morod900fe42021-02-11 16:12:57 +010023import org.junit.Rule;
Carmelo Cascone58136812018-07-19 03:40:16 +020024import org.junit.Test;
Daniele Morod900fe42021-02-11 16:12:57 +010025import org.junit.rules.ExpectedException;
Carmelo Cascone58136812018-07-19 03:40:16 +020026import org.onlab.util.ImmutableByteSequence;
27import org.onosproject.TestApplicationId;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.GroupId;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.TrafficTreatment;
33import org.onosproject.net.flow.instructions.Instructions;
34import org.onosproject.net.group.DefaultGroup;
35import org.onosproject.net.group.DefaultGroupBucket;
36import org.onosproject.net.group.DefaultGroupDescription;
37import org.onosproject.net.group.Group;
38import org.onosproject.net.group.GroupBucket;
39import org.onosproject.net.group.GroupBuckets;
40import org.onosproject.net.group.GroupDescription;
Daniele Morod900fe42021-02-11 16:12:57 +010041import org.onosproject.net.pi.model.DefaultPiPipeconf;
Carmelo Cascone58136812018-07-19 03:40:16 +020042import org.onosproject.net.pi.model.PiPipeconf;
Daniele Morod900fe42021-02-11 16:12:57 +010043import org.onosproject.net.pi.model.PiPipeconfId;
44import org.onosproject.net.pi.model.PiPipelineModel;
Carmelo Cascone58136812018-07-19 03:40:16 +020045import org.onosproject.net.pi.runtime.PiAction;
Carmelo Cascone58136812018-07-19 03:40:16 +020046import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070047import org.onosproject.net.pi.runtime.PiActionProfileGroup;
48import org.onosproject.net.pi.runtime.PiActionProfileMember;
49import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
Carmelo Cascone58136812018-07-19 03:40:16 +020050import org.onosproject.net.pi.runtime.PiGroupKey;
51import org.onosproject.net.pi.runtime.PiTableAction;
Daniele Morod900fe42021-02-11 16:12:57 +010052import org.onosproject.net.pi.service.PiTranslationException;
53import org.onosproject.p4runtime.model.P4InfoParser;
54import org.onosproject.p4runtime.model.P4InfoParserException;
Carmelo Cascone58136812018-07-19 03:40:16 +020055import org.onosproject.pipelines.basic.PipeconfLoader;
56
Daniele Morod900fe42021-02-11 16:12:57 +010057import java.net.URL;
Carmelo Cascone58136812018-07-19 03:40:16 +020058import java.util.Collection;
59import java.util.List;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080060import java.util.Objects;
61import java.util.stream.Collectors;
Carmelo Cascone58136812018-07-19 03:40:16 +020062
Daniele Morod900fe42021-02-11 16:12:57 +010063import static java.lang.String.format;
Carmelo Cascone58136812018-07-19 03:40:16 +020064import 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.onosproject.net.group.GroupDescription.Type.SELECT;
Daniele Morod900fe42021-02-11 16:12:57 +010069import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080070import static org.onosproject.net.pi.runtime.PiActionProfileGroup.WeightedMember.DEFAULT_WEIGHT;
Carmelo Cascone776be382018-12-12 19:03:57 -080071import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_SET_EGRESS_PORT;
72import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_WCMP_SELECTOR;
73import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_WCMP_TABLE;
74import static org.onosproject.pipelines.basic.BasicConstants.PORT;
Carmelo Cascone58136812018-07-19 03:40:16 +020075
76/**
77 * Test for {@link PiGroupTranslatorImpl}.
78 */
79public class PiGroupTranslatorImplTest {
80
81 private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:dummy:1");
82 private static final ApplicationId APP_ID = TestApplicationId.create("dummy");
83 private static final GroupId GROUP_ID = GroupId.valueOf(1);
84 private static final PiGroupKey GROUP_KEY = new PiGroupKey(
Carmelo Cascone776be382018-12-12 19:03:57 -080085 INGRESS_WCMP_CONTROL_WCMP_TABLE, INGRESS_WCMP_CONTROL_WCMP_SELECTOR, GROUP_ID.id());
Carmelo Cascone58136812018-07-19 03:40:16 +020086 private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(
87 selectOutputBucket(1),
88 selectOutputBucket(2),
89 selectOutputBucket(3));
90 private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
91 private static final GroupDescription SELECT_GROUP_DESC = new DefaultGroupDescription(
92 DEVICE_ID, SELECT, BUCKETS, GROUP_KEY, GROUP_ID.id(), APP_ID);
93 private static final Group SELECT_GROUP = new DefaultGroup(GROUP_ID, SELECT_GROUP_DESC);
94 private static final int DEFAULT_MEMBER_WEIGHT = 1;
Carmelo Casconea4a89fb2019-08-20 02:03:10 -070095 private static final int BASE_MEM_ID = 991;
Carmelo Cascone776be382018-12-12 19:03:57 -080096 private static final int PORT_BITWIDTH = 9;
Carmelo Cascone99c59db2019-01-17 15:39:35 -080097 private Collection<PiActionProfileMember> expectedMemberInstances;
98 private Collection<PiActionProfileGroup.WeightedMember> expectedWeightedMembers;
Carmelo Cascone58136812018-07-19 03:40:16 +020099
100 private PiPipeconf pipeconf;
101
Daniele Morod900fe42021-02-11 16:12:57 +0100102 // Derived from basic.p4info, with wcmp_table annotated with @oneshot
103 private static final String PATH_ONESHOT_P4INFO = "oneshot.p4info";
104 private static final PiPipeconfId ONE_SHOT_PIPECONF_ID = new PiPipeconfId("org.onosproject.pipelines.wcmp_oneshot");
105 private PiPipeconf pipeconfOneShot;
106
Carmelo Cascone58136812018-07-19 03:40:16 +0200107 @Before
108 public void setUp() throws Exception {
109 pipeconf = PipeconfLoader.BASIC_PIPECONF;
Daniele Morod900fe42021-02-11 16:12:57 +0100110 pipeconfOneShot = loadP4InfoPipeconf(ONE_SHOT_PIPECONF_ID, PATH_ONESHOT_P4INFO);
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800111 expectedMemberInstances = ImmutableSet.of(outputMember(1),
112 outputMember(2),
113 outputMember(3));
114 expectedWeightedMembers = expectedMemberInstances.stream()
115 .map(m -> new PiActionProfileGroup.WeightedMember(m, DEFAULT_WEIGHT))
116 .collect(Collectors.toSet());
117
Carmelo Cascone58136812018-07-19 03:40:16 +0200118 }
119
120 private static GroupBucket selectOutputBucket(int portNum) {
121 ImmutableByteSequence paramVal = copyFrom(portNum);
Carmelo Cascone776be382018-12-12 19:03:57 -0800122 PiActionParam param = new PiActionParam(PORT, paramVal);
123 PiTableAction action = PiAction.builder()
124 .withId(INGRESS_WCMP_CONTROL_SET_EGRESS_PORT)
125 .withParameter(param).build();
Carmelo Cascone58136812018-07-19 03:40:16 +0200126 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
127 .add(Instructions.piTableAction(action))
128 .build();
129 return DefaultGroupBucket.createSelectGroupBucket(treatment);
130 }
131
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700132 private static PiActionProfileMember outputMember(int portNum)
Carmelo Cascone58136812018-07-19 03:40:16 +0200133 throws ImmutableByteSequence.ByteSequenceTrimException {
Carmelo Cascone776be382018-12-12 19:03:57 -0800134 PiActionParam param = new PiActionParam(PORT, copyFrom(portNum).fit(PORT_BITWIDTH));
Carmelo Cascone58136812018-07-19 03:40:16 +0200135 PiAction piAction = PiAction.builder()
Carmelo Cascone776be382018-12-12 19:03:57 -0800136 .withId(INGRESS_WCMP_CONTROL_SET_EGRESS_PORT)
Carmelo Cascone58136812018-07-19 03:40:16 +0200137 .withParameter(param).build();
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700138 return PiActionProfileMember.builder()
Carmelo Cascone776be382018-12-12 19:03:57 -0800139 .forActionProfile(INGRESS_WCMP_CONTROL_WCMP_SELECTOR)
Carmelo Cascone58136812018-07-19 03:40:16 +0200140 .withAction(piAction)
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700141 .withId(PiActionProfileMemberId.of(BASE_MEM_ID + portNum))
Carmelo Cascone58136812018-07-19 03:40:16 +0200142 .build();
143 }
144
145 /**
146 * Test add group with buckets.
147 */
148 @Test
149 public void testTranslateGroups() throws Exception {
150
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700151 PiActionProfileGroup piGroup1 = PiGroupTranslatorImpl.translate(SELECT_GROUP, pipeconf, null);
152 PiActionProfileGroup piGroup2 = PiGroupTranslatorImpl.translate(SELECT_GROUP, pipeconf, null);
Carmelo Cascone58136812018-07-19 03:40:16 +0200153
154 new EqualsTester()
155 .addEqualityGroup(piGroup1, piGroup2)
156 .testEquals();
157
158 assertThat("Group ID must be equal",
159 piGroup1.id().id(), is(equalTo(GROUP_ID.id())));
160 assertThat("Action profile ID must be equal",
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800161 piGroup1.actionProfile(), is(equalTo(INGRESS_WCMP_CONTROL_WCMP_SELECTOR)));
Carmelo Cascone58136812018-07-19 03:40:16 +0200162
163 // members installed
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800164 Collection<PiActionProfileGroup.WeightedMember> weightedMembers = piGroup1.members();
165 Collection<PiActionProfileMember> memberInstances = weightedMembers.stream()
166 .map(PiActionProfileGroup.WeightedMember::instance)
167 .filter(Objects::nonNull)
168 .collect(Collectors.toSet());
Carmelo Cascone58136812018-07-19 03:40:16 +0200169 assertThat("The number of group members must be equal",
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800170 piGroup1.members().size(), is(expectedWeightedMembers.size()));
171 assertThat("Group weighted members must be equal",
172 weightedMembers.containsAll(expectedWeightedMembers)
173 && expectedWeightedMembers.containsAll(weightedMembers));
174 assertThat("Group member instances must be equal",
175 memberInstances.containsAll(expectedMemberInstances)
176 && expectedMemberInstances.containsAll(memberInstances));
177
Carmelo Cascone58136812018-07-19 03:40:16 +0200178 }
Daniele Morod900fe42021-02-11 16:12:57 +0100179
180 @Rule
181 public ExpectedException thrown = ExpectedException.none();
182
183 /**
184 * Test add group with buckets.
185 */
186 @Test
187 public void testTranslateGroupsOneShotError() throws Exception {
188 thrown.expect(PiTranslationException.class);
189 thrown.expectMessage(format("Table associated to action profile '%s' " +
190 "supports only one-shot action profile programming",
191 INGRESS_WCMP_CONTROL_WCMP_SELECTOR.id()));
192 PiGroupTranslatorImpl.translate(SELECT_GROUP, pipeconfOneShot, null);
193 }
194
195 private static PiPipeconf loadP4InfoPipeconf(PiPipeconfId pipeconfId, String p4infoPath) {
196 final URL p4InfoUrl = PiGroupTranslatorImpl.class.getResource(p4infoPath);
197 final PiPipelineModel pipelineModel;
198 try {
199 pipelineModel = P4InfoParser.parse(p4InfoUrl);
200 } catch (P4InfoParserException e) {
201 throw new IllegalStateException(e);
202 }
203 return DefaultPiPipeconf.builder()
204 .withId(pipeconfId)
205 .withPipelineModel(pipelineModel)
206 .addExtension(P4_INFO_TEXT, p4InfoUrl)
207 .build();
208 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200209}