blob: 3c3727c363b0100d5dcd6ff1f6ed82be951c7960 [file] [log] [blame]
Pier Luigif094c612017-10-14 12:15:02 +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.incubator.store.meter.impl;
18
Charles Chan593acf92017-11-22 13:55:41 -080019import com.google.common.collect.Lists;
Pier Luigif094c612017-10-14 12:15:02 +020020import com.google.common.collect.Sets;
21import org.junit.After;
22import org.junit.Before;
23import org.junit.Test;
24import org.onlab.junit.TestUtils;
25import org.onlab.packet.IpAddress;
Charles Chan593acf92017-11-22 13:55:41 -080026import org.onlab.util.KryoNamespace;
27import org.onosproject.TestApplicationId;
Pier Luigif094c612017-10-14 12:15:02 +020028import org.onosproject.cluster.ClusterServiceAdapter;
29import org.onosproject.cluster.ControllerNode;
30import org.onosproject.cluster.DefaultControllerNode;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.mastership.MastershipServiceAdapter;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.behaviour.MeterQuery;
35import org.onosproject.net.driver.Behaviour;
36import org.onosproject.net.driver.Driver;
37import org.onosproject.net.driver.DriverData;
38import org.onosproject.net.driver.DriverHandler;
39import org.onosproject.net.driver.DriverServiceAdapter;
40import org.onosproject.net.meter.Band;
41import org.onosproject.net.meter.DefaultBand;
42import org.onosproject.net.meter.DefaultMeter;
43import org.onosproject.net.meter.DefaultMeterFeatures;
44import org.onosproject.net.meter.Meter;
45import org.onosproject.net.meter.MeterFeatures;
46import org.onosproject.net.meter.MeterFeaturesKey;
47import org.onosproject.net.meter.MeterId;
48import org.onosproject.net.meter.MeterKey;
49import org.onosproject.net.meter.MeterState;
Charles Chan593acf92017-11-22 13:55:41 -080050import org.onosproject.store.service.Serializer;
Pier Luigif094c612017-10-14 12:15:02 +020051import org.onosproject.store.service.TestStorageService;
52
53import java.util.Collections;
54import java.util.HashSet;
55import java.util.Set;
56import java.util.concurrent.CompletableFuture;
57import java.util.concurrent.ExecutionException;
58
59import static org.hamcrest.Matchers.is;
60import static org.junit.Assert.*;
61import static org.onosproject.net.NetTestTools.APP_ID;
62import static org.onosproject.net.NetTestTools.did;
63
64/**
65 * Meter store tests.
66 */
67public class DistributedMeterStoreTest {
68
69 // Test node id
70 private static final NodeId NID_LOCAL = new NodeId("local");
71
72 // Test ip address
73 private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
74
75 // Store under testing
76 private DistributedMeterStore meterStore;
77
78 // Device ids used during the tests
79 private DeviceId did1 = did("1");
80 private DeviceId did2 = did("2");
81 private DeviceId did3 = did("3");
82 private DeviceId did4 = did("4");
83
84 // Meter ids used during the tests
85 private MeterId mid1 = MeterId.meterId(1);
86 private MeterId mid2 = MeterId.meterId(2);
Pier Luigibdcd9672017-10-13 13:54:48 +020087 private MeterId mid10 = MeterId.meterId(10);
Pier Luigif094c612017-10-14 12:15:02 +020088
89 // Bands used during the tests
90 private Band b1 = DefaultBand.builder()
91 .ofType(Band.Type.DROP)
92 .withRate(500)
93 .build();
94
95 // Meters used during the tests
96 private Meter m1 = DefaultMeter.builder()
97 .forDevice(did1)
98 .fromApp(APP_ID)
99 .withId(mid1)
100 .withUnit(Meter.Unit.KB_PER_SEC)
101 .withBands(Collections.singletonList(b1))
102 .build();
103
104 // Meter features used during the tests
105 private MeterFeatures mef1 = DefaultMeterFeatures.builder().forDevice(did1)
106 .withMaxMeters(3L)
107 .withBandTypes(new HashSet<>())
108 .withUnits(new HashSet<>())
109 .hasStats(false)
110 .hasBurst(false)
111 .withMaxBands((byte) 0)
112 .withMaxColors((byte) 0)
113 .build();
114 private MeterFeatures mef2 = DefaultMeterFeatures.builder().forDevice(did2)
115 .withMaxMeters(10L)
116 .withBandTypes(new HashSet<>())
117 .withUnits(new HashSet<>())
118 .hasStats(false)
119 .hasBurst(false)
120 .withMaxBands((byte) 0)
121 .withMaxColors((byte) 0)
122 .build();
123
124 @Before
125 public void setup() {
126 // Init step
127 meterStore = new DistributedMeterStore();
128 // Let's initialize some internal services
129 TestUtils.setField(meterStore, "storageService", new TestStorageService());
130 TestUtils.setField(meterStore, "clusterService", new TestClusterService());
131 TestUtils.setField(meterStore, "mastershipService", new TestMastershipService());
132 TestUtils.setField(meterStore, "driverService", new TestDriverService());
Charles Chan593acf92017-11-22 13:55:41 -0800133
134 // Inject TestApplicationId into the DistributedMeterStore serializer
135 KryoNamespace.Builder testKryoBuilder = TestUtils.getField(meterStore, "APP_KRYO_BUILDER");
136 testKryoBuilder.register(TestApplicationId.class);
137 Serializer testSerializer = Serializer.using(Lists.newArrayList(testKryoBuilder.build()));
138 TestUtils.setField(meterStore, "serializer", testSerializer);
139
Pier Luigif094c612017-10-14 12:15:02 +0200140 // Activate the store
141 meterStore.activate();
142 }
143
144 @After
145 public void tearDown() {
146 // Deactivate the store
147 meterStore.deactivate();
148 }
149
150 private void initMeterStore() {
151 // Let's store feature for device 1
152 meterStore.storeMeterFeatures(mef1);
153 // Let's store feature for device 2
154 meterStore.storeMeterFeatures(mef2);
155 }
156
157 /**
158 * Test proper store of meter features.
159 */
160 @Test
161 public void testStoreMeterFeatures() {
162 // Let's store feature for device 1
163 meterStore.storeMeterFeatures(mef1);
164 // Verify store meter features
165 assertThat(meterStore.getMaxMeters(MeterFeaturesKey.key(did1)), is(3L));
166 // Let's store feature for device 1
167 meterStore.storeMeterFeatures(mef2);
168 // Verify store meter features
169 assertThat(meterStore.getMaxMeters(MeterFeaturesKey.key(did2)), is(10L));
170 }
171
172 /**
173 * Test proper delete of meter features.
174 */
175 @Test
176 public void testDeleteMeterFeatures() {
177 // Let's store feature for device 1
178 meterStore.storeMeterFeatures(mef1);
179 // Verify store meter features
180 assertThat(meterStore.getMaxMeters(MeterFeaturesKey.key(did1)), is(3L));
181 // Let's delete the features
182 meterStore.deleteMeterFeatures(did1);
183 // Verify delete meter features
184 assertThat(meterStore.getMaxMeters(MeterFeaturesKey.key(did1)), is(0L));
185 }
186
187 /**
188 * Test proper allocation of meter ids.
189 */
190 @Test
191 public void testAllocateId() {
192 // Init the store
193 initMeterStore();
194 // Allocate a meter id and verify is equal to mid1
195 assertThat(mid1, is(meterStore.allocateMeterId(did1)));
196 // Allocate a meter id and verify is equal to mid2
197 assertThat(mid2, is(meterStore.allocateMeterId(did1)));
198 }
199
200 /**
201 * Test proper free of meter ids.
202 */
203 @Test
204 public void testFreeId() {
205 // Init the store
206 initMeterStore();
207 // Allocate a meter id and verify is equal to mid1
208 assertThat(mid1, is(meterStore.allocateMeterId(did1)));
209 // Free the above id
210 meterStore.freeMeterId(did1, mid1);
211 // Allocate a meter id and verify is equal to mid1
212 assertThat(mid1, is(meterStore.allocateMeterId(did1)));
Pier Luigibdcd9672017-10-13 13:54:48 +0200213 // Free an id not allocated
214 meterStore.freeMeterId(did1, mid10);
215 // Allocate a meter id and verify is equal to mid2
216 assertThat(mid2, is(meterStore.allocateMeterId(did1)));
Pier Luigif094c612017-10-14 12:15:02 +0200217 }
218
219 /**
220 * Test proper reuse of meter ids.
221 */
222 @Test
223 public void testReuseId() {
224 // Init the store
225 initMeterStore();
226 // Reserve id 1
227 MeterId meterIdOne = meterStore.allocateMeterId(did2);
228 // Free the above id
229 meterStore.freeMeterId(did2, meterIdOne);
230 // Start an async reservation
231 CompletableFuture<MeterId> future = CompletableFuture.supplyAsync(
232 () -> meterStore.allocateMeterId(did2)
233 );
234 // Start another reservation
235 MeterId meterIdTwo = meterStore.allocateMeterId(did2);
236 try {
237 meterIdOne = future.get();
238 } catch (InterruptedException | ExecutionException e) {
239 e.printStackTrace();
240 }
241 // Ids should be different, otherwise we had clash in the store
242 assertNotEquals("Ids should be different", meterIdOne, meterIdTwo);
243
244 // Free the above id
245 meterStore.freeMeterId(did1, meterIdOne);
246 // Free the above id
247 meterStore.freeMeterId(did1, meterIdTwo);
248 // Reserve id 1
249 meterIdOne = meterStore.allocateMeterId(did2);
250 // Reserve id 2
251 meterStore.allocateMeterId(did2);
252 // Reserve id 3
253 MeterId meterIdThree = meterStore.allocateMeterId(did2);
254 // Reserve id 4
255 MeterId meterIdFour = meterStore.allocateMeterId(did2);
256 // Free the above id
257 meterStore.freeMeterId(did1, meterIdOne);
258 // Free the above id
259 meterStore.freeMeterId(did1, meterIdThree);
260 // Free the above id
261 meterStore.freeMeterId(did1, meterIdFour);
262 // Start an async reservation
263 future = CompletableFuture.supplyAsync(
264 () -> meterStore.allocateMeterId(did2)
265 );
266 // Start another reservation
267 MeterId meterAnotherId = meterStore.allocateMeterId(did2);
268 try {
269 meterAnotherId = future.get();
270 } catch (InterruptedException | ExecutionException e) {
271 e.printStackTrace();
272 }
273 // Ids should be different, otherwise we had clash in the store
274 assertNotEquals("Ids should be different", meterAnotherId, meterIdOne);
275 }
276
277 /**
278 * Test query meters mechanism.
279 */
280 @Test
281 public void testQueryMeters() {
282 // Init the store
283 initMeterStore();
284 // Let's test queryMeters
285 assertThat(mid1, is(meterStore.allocateMeterId(did3)));
286 // Let's test queryMeters error
287 assertNull(meterStore.allocateMeterId(did4));
288 }
289
290 /**
291 * Test max meter error.
292 */
293 @Test
294 public void testMaxMeterError() {
295 // Init the store
296 initMeterStore();
297 // Reserve id 1
298 assertThat(mid1, is(meterStore.allocateMeterId(did1)));
299 // Reserve id 2
300 assertThat(mid2, is(meterStore.allocateMeterId(did1)));
301 // Max meter error
302 assertNull(meterStore.allocateMeterId(did1));
303 }
304
305 /**
306 * Test store meter.
307 */
308 @Test
309 public void testStoreMeter() {
310 // Init the store
311 initMeterStore();
312 // Simulate the allocation of an id
313 MeterId idOne = meterStore.allocateMeterId(did1);
314 // Verify the allocation
315 assertThat(mid1, is(idOne));
316 // Let's create a meter
317 Meter meterOne = DefaultMeter.builder()
318 .forDevice(did1)
319 .fromApp(APP_ID)
320 .withId(mid1)
321 .withUnit(Meter.Unit.KB_PER_SEC)
322 .withBands(Collections.singletonList(b1))
323 .build();
324 // Set the state
325 ((DefaultMeter) meterOne).setState(MeterState.PENDING_ADD);
326 // Store the meter
327 meterStore.storeMeter(meterOne);
328 // Let's create meter key
329 MeterKey meterKey = MeterKey.key(did1, mid1);
330 // Verify the store
331 assertThat(1, is(meterStore.getAllMeters().size()));
332 assertThat(1, is(meterStore.getAllMeters(did1).size()));
333 assertThat(m1, is(meterStore.getMeter(meterKey)));
334 }
335
336 /**
337 * Test delete meter.
338 */
339 @Test
340 public void testDeleteMeter() {
341 // Init the store
342 initMeterStore();
343 // Simulate the allocation of an id
344 MeterId idOne = meterStore.allocateMeterId(did1);
345 // Verify the allocation
346 assertThat(mid1, is(idOne));
347 // Let's create a meter
348 Meter meterOne = DefaultMeter.builder()
349 .forDevice(did1)
350 .fromApp(APP_ID)
351 .withId(mid1)
352 .withUnit(Meter.Unit.KB_PER_SEC)
353 .withBands(Collections.singletonList(b1))
354 .build();
355 // Set the state
356 ((DefaultMeter) meterOne).setState(MeterState.PENDING_ADD);
357 // Store the meter
358 meterStore.storeMeter(meterOne);
359 // Set the state
360 ((DefaultMeter) meterOne).setState(MeterState.PENDING_REMOVE);
361 // Let's create meter key
362 MeterKey meterKey = MeterKey.key(did1, mid1);
363 // Delete meter
364 meterStore.deleteMeter(meterOne);
365 // Start an async delete, simulating the operation of the provider
366 CompletableFuture<Void> future = CompletableFuture.runAsync(
367 () -> meterStore.deleteMeterNow(meterOne)
368 );
369 // Let's wait
370 try {
371 future.get();
372 } catch (InterruptedException | ExecutionException e) {
373 e.printStackTrace();
374 }
375 // Verify delete
376 assertThat(0, is(meterStore.getAllMeters().size()));
377 assertThat(0, is(meterStore.getAllMeters(did1).size()));
378 assertNull(meterStore.getMeter(meterKey));
379 assertThat(mid1, is(meterStore.allocateMeterId(did1)));
380 }
381
382 /**
383 * Test no delete meter.
384 */
385 @Test
386 public void testNoDeleteMeter() {
387 // Init the store
388 initMeterStore();
389 // Simulate the allocation of an id
390 MeterId idOne = meterStore.allocateMeterId(did1);
391 // Create the key
392 MeterKey keyOne = MeterKey.key(did1, idOne);
393 // Let's create a meter
394 Meter meterOne = DefaultMeter.builder()
395 .forDevice(did1)
396 .fromApp(APP_ID)
397 .withId(mid1)
398 .withUnit(Meter.Unit.KB_PER_SEC)
399 .withBands(Collections.singletonList(b1))
400 .build();
401 // Set the state
402 ((DefaultMeter) meterOne).setState(MeterState.PENDING_REMOVE);
403 // Delete meter
404 meterStore.deleteMeter(meterOne);
405 // Verify No delete
406 assertThat(0, is(meterStore.getAllMeters().size()));
407 assertThat(0, is(meterStore.getAllMeters(did1).size()));
408 assertNull(meterStore.getMeter(keyOne));
409 }
410
411 // Test cluster service
412 private final class TestClusterService extends ClusterServiceAdapter {
413
414 private ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST);
415
416 @Override
417 public ControllerNode getLocalNode() {
418 return local;
419 }
420
421 @Override
422 public Set<ControllerNode> getNodes() {
423 return Sets.newHashSet();
424 }
425
426 }
427
428 // Test mastership service
429 private final class TestMastershipService extends MastershipServiceAdapter {
430 @Override
431 public NodeId getMasterFor(DeviceId deviceId) {
432 return NID_LOCAL;
433 }
434 }
435
436 // Test class for driver service.
437 private class TestDriverService extends DriverServiceAdapter {
438 @Override
439 public DriverHandler createHandler(DeviceId deviceId, String... credentials) {
440 return deviceId.equals(did3) ? new TestDriverHandler() : null;
441 }
442 }
443
444 // Test class for driver handler.
445 private class TestDriverHandler implements DriverHandler {
446
447 @Override
448 public Driver driver() {
449 return null;
450 }
451
452 @Override
453 public DriverData data() {
454 return null;
455 }
456
457 @Override
458 @SuppressWarnings("unchecked")
459 public <T extends Behaviour> T behaviour(Class<T> behaviourClass) {
460 return (T) new TestMeterQuery();
461 }
462
463 @Override
464 public <T> T get(Class<T> serviceClass) {
465 return null;
466 }
467
468 @Override
469 public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
470 return true;
471 }
472 }
473
474 // Test meter query
475 private class TestMeterQuery implements MeterQuery {
476
477 @Override
478 public DriverData data() {
479 return null;
480 }
481
482 @Override
483 public void setData(DriverData data) {
484
485 }
486 @Override
487 public DriverHandler handler() {
488 return null;
489 }
490
491 @Override
492 public void setHandler(DriverHandler handler) {
493
494 }
495
496 @Override
497 public long getMaxMeters() {
498 return 100;
499 }
500 }
501
502}