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