blob: caaa751a479f5fad9d15805c93f9fc172e7f1028 [file] [log] [blame]
Frank Wangd7e3b4b2017-09-24 13:37:54 +09001/*
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.drivers.p4runtime;
18
Frank Wangd7e3b4b2017-09-24 13:37:54 +090019import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.CacheLoader;
Carmelo Casconee5b28722018-06-22 17:28:28 +020021import com.google.common.cache.LoadingCache;
Wailok Shumf013a782021-07-26 16:51:01 +080022import com.google.common.collect.Sets;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090023import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMeterMirror;
Wailok Shumf013a782021-07-26 16:51:01 +080024import org.onosproject.net.DeviceId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090025import org.onosproject.net.meter.Band;
26import org.onosproject.net.meter.DefaultBand;
27import org.onosproject.net.meter.DefaultMeter;
Wailok Shumf013a782021-07-26 16:51:01 +080028import org.onosproject.net.meter.DefaultMeterFeatures;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090029import org.onosproject.net.meter.Meter;
Wailok Shumf013a782021-07-26 16:51:01 +080030import org.onosproject.net.meter.MeterFeatures;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090031import org.onosproject.net.meter.MeterOperation;
32import org.onosproject.net.meter.MeterProgrammable;
Wailok Shumf013a782021-07-26 16:51:01 +080033import org.onosproject.net.meter.MeterScope;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090034import org.onosproject.net.meter.MeterState;
35import org.onosproject.net.pi.model.PiMeterId;
36import org.onosproject.net.pi.model.PiMeterModel;
37import org.onosproject.net.pi.model.PiPipelineModel;
38import org.onosproject.net.pi.runtime.PiMeterCellConfig;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080039import org.onosproject.net.pi.runtime.PiMeterCellHandle;
Wailok Shum96642092021-08-06 16:23:36 +080040import org.onosproject.net.pi.runtime.PiMeterCellId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090041import org.onosproject.net.pi.service.PiMeterTranslator;
42import org.onosproject.net.pi.service.PiTranslationException;
43
44import java.util.Collection;
45import java.util.Collections;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090046import java.util.HashSet;
Carmelo Casconee5b28722018-06-22 17:28:28 +020047import java.util.Set;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090048import java.util.concurrent.CompletableFuture;
Carmelo Casconee5b28722018-06-22 17:28:28 +020049import java.util.concurrent.TimeUnit;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090050import java.util.concurrent.locks.Lock;
51import java.util.concurrent.locks.ReentrantLock;
52import java.util.stream.Collectors;
53
Wailok Shumf013a782021-07-26 16:51:01 +080054import static com.google.common.base.Preconditions.checkNotNull;
Wailok Shum96642092021-08-06 16:23:36 +080055import static org.onosproject.net.meter.MeterOperation.Type.ADD;
56import static org.onosproject.net.meter.MeterOperation.Type.MODIFY;
57import static org.onosproject.net.meter.MeterOperation.Type.REMOVE;
Wailok Shumf013a782021-07-26 16:51:01 +080058
Frank Wangd7e3b4b2017-09-24 13:37:54 +090059/**
60 * Implementation of MeterProgrammable behaviour for P4Runtime.
61 */
62public class P4RuntimeMeterProgrammable extends AbstractP4RuntimeHandlerBehaviour implements MeterProgrammable {
63
64 private static final int METER_LOCK_EXPIRE_TIME_IN_MIN = 10;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080065 private static final LoadingCache<PiMeterCellHandle, Lock>
Frank Wangd7e3b4b2017-09-24 13:37:54 +090066 ENTRY_LOCKS = CacheBuilder.newBuilder()
67 .expireAfterAccess(METER_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
Carmelo Cascone4c289b72019-01-22 15:30:45 -080068 .build(new CacheLoader<PiMeterCellHandle, Lock>() {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090069 @Override
Carmelo Cascone4c289b72019-01-22 15:30:45 -080070 public Lock load(PiMeterCellHandle handle) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090071 return new ReentrantLock();
72 }
73 });
74
75 private PiMeterTranslator translator;
76 private P4RuntimeMeterMirror meterMirror;
77 private PiPipelineModel pipelineModel;
78
79 @Override
Carmelo Casconec32976e2019-04-08 14:50:52 -070080 protected boolean setupBehaviour(String opName) {
81 if (!super.setupBehaviour(opName)) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090082 return false;
83 }
84
Yi Tsengd7716482018-10-31 15:34:30 -070085 translator = translationService.meterTranslator();
Frank Wangd7e3b4b2017-09-24 13:37:54 +090086 meterMirror = handler().get(P4RuntimeMeterMirror.class);
87 pipelineModel = pipeconf.pipelineModel();
88 return true;
89 }
90
91 @Override
92 public CompletableFuture<Boolean> performMeterOperation(MeterOperation meterOp) {
93
Carmelo Casconec32976e2019-04-08 14:50:52 -070094 if (!setupBehaviour("performMeterOperation()")) {
Kevin Chuangc267df22018-05-02 16:26:04 +080095 return CompletableFuture.completedFuture(false);
96 }
97
Frank Wangd7e3b4b2017-09-24 13:37:54 +090098 return CompletableFuture.completedFuture(processMeterOp(meterOp));
99 }
100
101 private boolean processMeterOp(MeterOperation meterOp) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900102 PiMeterCellConfig piMeterCellConfig;
Wailok Shum96642092021-08-06 16:23:36 +0800103 switch (meterOp.type()) {
104 case ADD:
105 case MODIFY:
106 // Create a config for modify operation
107 try {
108 piMeterCellConfig = translator.translate(meterOp.meter(), pipeconf);
109 } catch (PiTranslationException e) {
110 log.warn("Unable translate meter, aborting meter operation {}: {}", meterOp.type(), e.getMessage());
111 log.debug("exception", e);
112 return false;
113 }
114 break;
115 case REMOVE:
116 // Create a empty config for reset operation
117 PiMeterCellId piMeterCellId = (PiMeterCellId) meterOp.meter().meterCellId();
118 piMeterCellConfig = PiMeterCellConfig.reset(piMeterCellId);
119 break;
120 default:
121 log.warn("Meter Operation type {} not supported", meterOp.type());
122 return false;
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900123 }
124
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800125 final PiMeterCellHandle handle = PiMeterCellHandle.of(deviceId, piMeterCellConfig);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900126 ENTRY_LOCKS.getUnchecked(handle).lock();
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700127 final boolean result = client.write(p4DeviceId, pipeconf)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800128 .modify(piMeterCellConfig).submitSync().isSuccess();
Carmelo Casconee5b28722018-06-22 17:28:28 +0200129 if (result) {
130 meterMirror.put(handle, piMeterCellConfig);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900131 }
Carmelo Casconee5b28722018-06-22 17:28:28 +0200132 ENTRY_LOCKS.getUnchecked(handle).unlock();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900133
134 return result;
135 }
136
137 @Override
138 public CompletableFuture<Collection<Meter>> getMeters() {
139
Carmelo Casconec32976e2019-04-08 14:50:52 -0700140 if (!setupBehaviour("getMeters()")) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900141 return CompletableFuture.completedFuture(Collections.emptyList());
142 }
143
144 Collection<PiMeterCellConfig> piMeterCellConfigs;
145
146 Set<PiMeterId> meterIds = new HashSet<>();
147 for (PiMeterModel mode : pipelineModel.meters()) {
148 meterIds.add(mode.id());
149 }
150
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700151 piMeterCellConfigs = client.read(p4DeviceId, pipeconf)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800152 .meterCells(meterIds).submitSync().all(PiMeterCellConfig.class);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900153
154 Collection<Meter> meters = piMeterCellConfigs.stream()
155 .map(p -> {
156 DefaultMeter meter = (DefaultMeter) DefaultMeter.builder()
157 .withBands(p.meterBands().stream().map(b -> DefaultBand.builder()
158 .withRate(b.rate())
159 .burstSize(b.burst())
160 .ofType(Band.Type.NONE)
161 .build()).collect(Collectors.toList()))
162 .withCellId(p.cellId()).build();
163 meter.setState(MeterState.ADDED);
164 return meter;
165 })
166 .collect(Collectors.toList());
167
168 return CompletableFuture.completedFuture(meters);
169 }
Wailok Shumf013a782021-07-26 16:51:01 +0800170
171 @Override
172 public CompletableFuture<Collection<MeterFeatures>> getMeterFeatures() {
173
174 if (!setupBehaviour("getMeterFeatures()")) {
175 return CompletableFuture.completedFuture(Collections.emptyList());
176 }
177
178 Collection<MeterFeatures> meterFeatures = new HashSet<>();
179 pipeconf.pipelineModel().meters().forEach(
180 m -> meterFeatures.add(new P4RuntimeMeterFeaturesBuilder(m, deviceId).build()));
181
182 return CompletableFuture.completedFuture(meterFeatures);
183 }
184
185 /**
186 * P4 meter features builder.
187 */
188 public class P4RuntimeMeterFeaturesBuilder {
189 private final PiMeterModel piMeterModel;
190 private DeviceId deviceId;
191
192 private static final long PI_METER_START_INDEX = 0L;
193 private static final short PI_METER_MAX_BAND = 2;
194 private static final short PI_METER_MAX_COLOR = 3;
195
196 public P4RuntimeMeterFeaturesBuilder(PiMeterModel piMeterModel, DeviceId deviceId) {
197 this.piMeterModel = checkNotNull(piMeterModel);
198 this.deviceId = deviceId;
199 }
200
201 /**
202 * To build a MeterFeatures using the PiMeterModel object
203 * retrieved from pipeconf.
204 *
205 * @return the meter features object
206 */
207 public MeterFeatures build() {
208 /*
209 * We set the basic values before to extract the other information.
210 */
211 MeterFeatures.Builder builder = DefaultMeterFeatures.builder()
212 .forDevice(deviceId)
213 // The scope value will be PiMeterId
214 .withScope(MeterScope.of(piMeterModel.id().id()))
215 .withMaxBands(PI_METER_MAX_BAND)
216 .withMaxColors(PI_METER_MAX_COLOR)
217 .withStartIndex(PI_METER_START_INDEX)
218 .withEndIndex(piMeterModel.size() - 1);
219 /*
220 * Pi meter only support NONE type
221 */
222 Set<Band.Type> bands = Sets.newHashSet();
223 bands.add(Band.Type.NONE);
224 builder.withBandTypes(bands);
225 /*
226 * We extract the supported units;
227 */
228 Set<Meter.Unit> units = Sets.newHashSet();
229 if (piMeterModel.unit() == PiMeterModel.Unit.BYTES) {
230 units.add(Meter.Unit.KB_PER_SEC);
231 } else if (piMeterModel.unit() == PiMeterModel.Unit.PACKETS) {
232 units.add(Meter.Unit.PKTS_PER_SEC);
233 }
234 builder.withUnits(units);
235 /*
236 * Burst is supported ?
237 */
238 builder.hasBurst(true);
239 /*
240 * Stats are supported ?
241 */
242 builder.hasStats(false);
243
244 return builder.build();
245 }
246
247 /**
248 * To build an empty meter features.
249 * @param deviceId the device id
250 * @return the meter features
251 */
252 public MeterFeatures noMeterFeatures(DeviceId deviceId) {
253 return DefaultMeterFeatures.noMeterFeatures(deviceId);
254 }
255 }
Carmelo Casconee5b28722018-06-22 17:28:28 +0200256}