blob: 9d4f7e4427aca4e35f85264d86a038960271c52b [file] [log] [blame]
yoonseon94672112017-01-31 13:46:21 -08001/*
Thomas Vachuska52f2cd12018-11-08 21:20:04 -08002 * Copyright 2018-present Open Networking Foundation
yoonseon94672112017-01-31 13:46:21 -08003 *
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.incubator.net.virtual.impl;
18
19import com.google.common.collect.Maps;
20import org.apache.commons.lang3.tuple.Pair;
21import org.onlab.util.TriConsumer;
22import org.onosproject.incubator.net.virtual.NetworkId;
23import org.onosproject.incubator.net.virtual.VirtualNetworkMeterStore;
24import org.onosproject.incubator.net.virtual.VirtualNetworkService;
25import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
26import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
27import org.onosproject.incubator.net.virtual.provider.VirtualMeterProvider;
28import org.onosproject.incubator.net.virtual.provider.VirtualMeterProviderService;
29import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.meter.DefaultMeter;
32import org.onosproject.net.meter.Meter;
Wailok Shumfa3b3282021-08-22 19:35:34 +080033import org.onosproject.net.meter.MeterCellId;
yoonseon94672112017-01-31 13:46:21 -080034import org.onosproject.net.meter.MeterEvent;
35import org.onosproject.net.meter.MeterFailReason;
36import org.onosproject.net.meter.MeterFeatures;
37import org.onosproject.net.meter.MeterFeaturesKey;
38import org.onosproject.net.meter.MeterId;
39import org.onosproject.net.meter.MeterKey;
40import org.onosproject.net.meter.MeterListener;
41import org.onosproject.net.meter.MeterOperation;
42import org.onosproject.net.meter.MeterRequest;
43import org.onosproject.net.meter.MeterService;
44import org.onosproject.net.meter.MeterState;
45import org.onosproject.net.meter.MeterStoreDelegate;
46import org.onosproject.net.meter.MeterStoreResult;
47import org.onosproject.net.provider.ProviderId;
48import org.onosproject.store.service.AtomicCounter;
49import org.onosproject.store.service.StorageService;
50import org.slf4j.Logger;
51
52import java.util.Collection;
53import java.util.Map;
54import java.util.Set;
55import java.util.function.Function;
56import java.util.stream.Collectors;
57
58import static org.slf4j.LoggerFactory.getLogger;
59
60public class VirtualNetworkMeterManager
61 extends AbstractVirtualListenerManager<MeterEvent, MeterListener>
62 implements MeterService {
63
64 private static final String METERCOUNTERIDENTIFIER = "meter-id-counter-%s";
65 private final Logger log = getLogger(getClass());
66
67 protected StorageService coreStorageService;
68
69 protected VirtualNetworkMeterStore store;
70 private final MeterStoreDelegate storeDelegate = new InternalMeterStoreDelegate();
71
72 private VirtualProviderRegistryService providerRegistryService;
73 private InternalMeterProviderService innerProviderService;
74
75 private Map<DeviceId, AtomicCounter> meterIdCounters
76 = Maps.newConcurrentMap();
77
78 private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
79
80 /**
81 * Creates a new VirtualNetworkMeterManager object.
82 *
83 * @param manager virtual network manager service
84 * @param networkId a virtual network identifier
85 */
86 public VirtualNetworkMeterManager(VirtualNetworkService manager,
87 NetworkId networkId) {
88 super(manager, networkId, MeterEvent.class);
89
90 coreStorageService = serviceDirectory.get(StorageService.class);
91 providerRegistryService =
92 serviceDirectory.get(VirtualProviderRegistryService.class);
93
94 store = serviceDirectory.get(VirtualNetworkMeterStore.class);
95 store.setDelegate(networkId, this.storeDelegate);
96
97 innerProviderService = new InternalMeterProviderService();
98 providerRegistryService.registerProviderService(networkId(), innerProviderService);
99
100
101 onComplete = (request, result, error) -> {
102 request.context().ifPresent(c -> {
103 if (error != null) {
104 c.onError(request, MeterFailReason.UNKNOWN);
105 } else {
106 if (result.reason().isPresent()) {
107 c.onError(request, result.reason().get());
108 } else {
109 c.onSuccess(request);
110 }
111 }
112 });
113
114 };
115
116 log.info("Started");
117 }
118
119 @Override
120 public Meter submit(MeterRequest request) {
121
122 MeterId id = allocateMeterId(request.deviceId());
123
124 Meter.Builder mBuilder = DefaultMeter.builder()
125 .forDevice(request.deviceId())
126 .fromApp(request.appId())
127 .withBands(request.bands())
128 .withId(id)
129 .withUnit(request.unit());
130
131 if (request.isBurst()) {
132 mBuilder.burst();
133 }
134 DefaultMeter m = (DefaultMeter) mBuilder.build();
135 m.setState(MeterState.PENDING_ADD);
136 store.storeMeter(networkId(), m).whenComplete((result, error) ->
137 onComplete.accept(request, result, error));
138 return m;
139 }
140
141 @Override
142 public void withdraw(MeterRequest request, MeterId meterId) {
Wailok Shumfa3b3282021-08-22 19:35:34 +0800143 withdraw(request, (MeterCellId) meterId);
144 }
145
146 @Override
147 public void withdraw(MeterRequest request, MeterCellId meterCellId) {
yoonseon94672112017-01-31 13:46:21 -0800148 Meter.Builder mBuilder = DefaultMeter.builder()
149 .forDevice(request.deviceId())
150 .fromApp(request.appId())
151 .withBands(request.bands())
Wailok Shumfa3b3282021-08-22 19:35:34 +0800152 .withCellId(meterCellId)
yoonseon94672112017-01-31 13:46:21 -0800153 .withUnit(request.unit());
154
155 if (request.isBurst()) {
156 mBuilder.burst();
157 }
158
159 DefaultMeter m = (DefaultMeter) mBuilder.build();
160 m.setState(MeterState.PENDING_REMOVE);
161 store.deleteMeter(networkId(), m).whenComplete((result, error) ->
162 onComplete.accept(request, result, error));
163 }
164
165 @Override
166 public Meter getMeter(DeviceId deviceId, MeterId id) {
Wailok Shumfa3b3282021-08-22 19:35:34 +0800167 return getMeter(deviceId, (MeterCellId) id);
168 }
169
170 @Override
171 public Meter getMeter(DeviceId deviceId, MeterCellId id) {
yoonseon94672112017-01-31 13:46:21 -0800172 MeterKey key = MeterKey.key(deviceId, id);
173 return store.getMeter(networkId(), key);
174 }
175
176 @Override
177 public Collection<Meter> getMeters(DeviceId deviceId) {
178 return store.getAllMeters(networkId()).stream()
179 .filter(m -> m.deviceId().equals(deviceId)).collect(Collectors.toList());
180 }
181
182 @Override
183 public Collection<Meter> getAllMeters() {
184 return store.getAllMeters(networkId());
185 }
186
187 private long queryMeters(DeviceId device) {
188 //FIXME: how to decide maximum number of meters per virtual device?
189 return 1;
190 }
191
192 private AtomicCounter allocateCounter(DeviceId deviceId) {
193 return coreStorageService
194 .getAtomicCounter(String.format(METERCOUNTERIDENTIFIER, deviceId));
195 }
196
Pier Luigibdcd9672017-10-13 13:54:48 +0200197 public MeterId allocateMeterId(DeviceId deviceId) {
yoonseon94672112017-01-31 13:46:21 -0800198 long maxMeters = store.getMaxMeters(networkId(), MeterFeaturesKey.key(deviceId));
199 if (maxMeters == 0L) {
200 // MeterFeatures couldn't be retrieved, trying with queryMeters
201 maxMeters = queryMeters(deviceId);
202 }
203
204 if (maxMeters == 0L) {
205 throw new IllegalStateException("Meters not supported by device " + deviceId);
206 }
207
208 final long mmeters = maxMeters;
209 long id = meterIdCounters.compute(deviceId, (k, v) -> {
210 if (v == null) {
211 return allocateCounter(k);
212 }
213 if (v.get() >= mmeters) {
214 throw new IllegalStateException("Maximum number of meters " +
215 meterIdCounters.get(deviceId).get() +
216 " reached for device " + deviceId +
217 " virtual network " + networkId());
218 }
219 return v;
220 }).incrementAndGet();
221
222 return MeterId.meterId(id);
223 }
224
Pier Luigibdcd9672017-10-13 13:54:48 +0200225 @Override
226 public void freeMeterId(DeviceId deviceId, MeterId meterId) {
227 // Do nothing
228 }
229
Andrea Campanella23250502020-05-13 15:36:57 +0200230 @Override
231 public void purgeMeters(DeviceId deviceId) {
232 // Do nothing
233 }
234
yoonseon94672112017-01-31 13:46:21 -0800235 private class InternalMeterProviderService
236 extends AbstractVirtualProviderService<VirtualMeterProvider>
237 implements VirtualMeterProviderService {
238
239 /**
240 * Creates a provider service on behalf of the specified provider.
241 */
242 protected InternalMeterProviderService() {
243 Set<ProviderId> providerIds =
244 providerRegistryService.getProvidersByService(this);
245 ProviderId providerId = providerIds.stream().findFirst().get();
246 VirtualMeterProvider provider = (VirtualMeterProvider)
247 providerRegistryService.getProvider(providerId);
248 setProvider(provider);
249 }
250
251 @Override
252 public void meterOperationFailed(MeterOperation operation,
253 MeterFailReason reason) {
254 store.failedMeter(networkId(), operation, reason);
255 }
256
257 @Override
258 public void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries) {
259 //FIXME: FOLLOWING CODE CANNOT BE TESTED UNTIL SOMETHING THAT
260 //FIXME: IMPLEMENTS METERS EXISTS
261 Map<Pair<DeviceId, MeterId>, Meter> storedMeterMap =
262 store.getAllMeters(networkId()).stream()
263 .collect(Collectors.toMap(m -> Pair.of(m.deviceId(), m.id()), Function.identity()));
264
265 meterEntries.stream()
266 .filter(m -> storedMeterMap.remove(Pair.of(m.deviceId(), m.id())) != null)
267 .forEach(m -> store.updateMeterState(networkId(), m));
268
269 storedMeterMap.values().forEach(m -> {
270 if (m.state() == MeterState.PENDING_ADD) {
271 provider().performMeterOperation(networkId(), m.deviceId(),
272 new MeterOperation(m,
273 MeterOperation.Type.MODIFY));
274 } else if (m.state() == MeterState.PENDING_REMOVE) {
275 store.deleteMeterNow(networkId(), m);
276 }
277 });
278 }
279
280 @Override
281 public void pushMeterFeatures(DeviceId deviceId, MeterFeatures meterfeatures) {
282 store.storeMeterFeatures(networkId(), meterfeatures);
283 }
284
285 @Override
286 public void deleteMeterFeatures(DeviceId deviceId) {
287 store.deleteMeterFeatures(networkId(), deviceId);
288 }
289 }
290
291 private class InternalMeterStoreDelegate implements MeterStoreDelegate {
292
293 @Override
294 public void notify(MeterEvent event) {
295 DeviceId deviceId = event.subject().deviceId();
296 VirtualMeterProvider p = innerProviderService.provider();
297
298 switch (event.type()) {
299 case METER_ADD_REQ:
300 p.performMeterOperation(networkId(), deviceId,
301 new MeterOperation(event.subject(),
302 MeterOperation.Type.ADD));
303 break;
304 case METER_REM_REQ:
305 p.performMeterOperation(networkId(), deviceId,
306 new MeterOperation(event.subject(),
307 MeterOperation.Type.REMOVE));
308 break;
309 default:
310 log.warn("Unknown meter event {}", event.type());
311 }
312 }
313 }
314}