Brian O'Connor

Adding tests for IntentCleaner

Also, two small bug fixes for SimpleIntentStore
and one for IntentCleanup

Change-Id: I19c8246dd669d894ba258e04f4f963a97b9a7626
......@@ -128,7 +128,7 @@ public class IntentCleanup implements Runnable, IntentListener {
log.info("Settings: period={}", period);
}
private void adjustRate() {
protected void adjustRate() {
if (timerTask != null) {
timerTask.cancel();
}
......@@ -192,15 +192,18 @@ public class IntentCleanup implements Runnable, IntentListener {
*/
private void cleanup() {
int corruptCount = 0, stuckCount = 0, pendingCount = 0;
store.getIntentData(true, periodMs);
for (IntentData intentData : store.getIntentData(true, periodMs)) {
switch (intentData.state()) {
case CORRUPT:
resubmitCorrupt(intentData, false);
corruptCount++;
break;
case INSTALLING: //FALLTHROUGH
case WITHDRAWING:
resubmitPendingRequest(intentData);
stuckCount++;
break;
default:
//NOOP
break;
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.core.IdGenerator;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentEvent;
import org.onosproject.net.intent.IntentServiceAdapter;
import org.onosproject.net.intent.IntentStore;
import org.onosproject.net.intent.IntentStoreDelegate;
import org.onosproject.net.intent.MockIdGenerator;
import org.onosproject.store.Timestamp;
import org.onosproject.store.trivial.impl.SimpleIntentStore;
import org.onosproject.store.trivial.impl.SystemClockTimestamp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.intent.IntentState.*;
import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
/**
* Test intent cleanup.
*/
public class IntentCleanupTest {
private IntentCleanup cleanup;
private MockIntentService service;
private IntentStore store;
protected IdGenerator idGenerator; // global or one per test? per test for now.
private static class MockIntentService extends IntentServiceAdapter {
private int submitCounter = 0;
@Override
public void submit(Intent intent) {
submitCounter++;
}
public int submitCounter() {
return submitCounter;
}
}
@Before
public void setUp() {
service = new MockIntentService();
store = new SimpleIntentStore();
cleanup = new IntentCleanup();
idGenerator = new MockIdGenerator();
cleanup.cfgService = new ComponentConfigAdapter();
cleanup.service = service;
cleanup.store = store;
cleanup.period = 10;
cleanup.retryThreshold = 3;
cleanup.activate();
assertTrue("store should be empty",
Sets.newHashSet(cleanup.store.getIntents()).isEmpty());
Intent.bindIdGenerator(idGenerator);
}
@After
public void tearDown() {
cleanup.deactivate();
Intent.unbindIdGenerator(idGenerator);
}
/**
* Trigger resubmit of intent in CORRUPT during periodic poll.
*/
@Test
public void corruptPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
cleanup.run(); //FIXME broken?
assertEquals("Expect number of submits incorrect",
1, service.submitCounter());
}
/**
* Trigger resubmit of intent in INSTALL_REQ for too long.
*/
@Test
public void pendingPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
cleanup.run();
assertEquals("Expect number of submits incorrect",
1, service.submitCounter());
}
/**
* Trigger resubmit of intent in INSTALLING for too long.
*/
@Test
public void installingPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(INSTALLING);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
cleanup.run();
assertEquals("Expect number of submits incorrect",
1, service.submitCounter());
}
/**
* Only submit one of two intents because one is too new.
*/
@Test
public void skipPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
store.addPending(data);
Intent intent2 = new MockIntent(2L);
Timestamp version = new SystemClockTimestamp(1L);
data = new IntentData(intent2, INSTALL_REQ, version);
store.addPending(data);
cleanup.run();
assertEquals("Expect number of submits incorrect",
1, service.submitCounter());
}
/**
* Verify resubmit in response to CORRUPT event.
*/
@Test
public void corruptEvent() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
store.addPending(data);
assertEquals("Expect number of submits incorrect",
1, service.submitCounter());
}
/**
* Intent should not be retried because threshold is reached.
*/
@Test
public void corruptEventThreshold() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
intentData.setErrorCount(cleanup.retryThreshold);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
store.addPending(data);
assertEquals("Expect number of submits incorrect",
0, service.submitCounter());
}
}
\ No newline at end of file
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.core.IdGenerator;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentEvent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentStore;
import org.onosproject.net.intent.IntentStoreDelegate;
import org.onosproject.net.intent.MockIdGenerator;
import org.onosproject.store.Timestamp;
import org.onosproject.store.trivial.impl.SimpleIntentStore;
import org.onosproject.store.trivial.impl.SystemClockTimestamp;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.intent.IntentState.*;
import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
/**
* Test intent cleanup using Mocks.
* FIXME remove this or IntentCleanupTest
*/
public class IntentCleanupTestMock {
private IntentCleanup cleanup;
private IntentService service;
private IntentStore store;
protected IdGenerator idGenerator; // global or one per test? per test for now.
@Before
public void setUp() {
service = createMock(IntentService.class);
store = new SimpleIntentStore();
cleanup = new IntentCleanup();
idGenerator = new MockIdGenerator();
service.addListener(cleanup);
expectLastCall().once();
replay(service);
cleanup.cfgService = new ComponentConfigAdapter();
cleanup.service = service;
cleanup.store = store;
cleanup.period = 1000;
cleanup.retryThreshold = 3;
cleanup.activate();
verify(service);
reset(service);
assertTrue("store should be empty",
Sets.newHashSet(cleanup.store.getIntents()).isEmpty());
Intent.bindIdGenerator(idGenerator);
}
@After
public void tearDown() {
service.removeListener(cleanup);
expectLastCall().once();
replay(service);
cleanup.deactivate();
verify(service);
reset(service);
Intent.unbindIdGenerator(idGenerator);
}
/**
* Trigger resubmit of intent in CORRUPT during periodic poll.
*/
@Test
public void corruptPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
service.submit(intent);
expectLastCall().once();
replay(service);
cleanup.run(); //FIXME broken?
verify(service);
reset(service);
}
/**
* Trigger resubmit of intent in INSTALL_REQ for too long.
*/
@Test
public void pendingPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
service.submit(intent);
expectLastCall().once();
replay(service);
cleanup.run();
verify(service);
reset(service);
}
/**
* Trigger resubmit of intent in INSTALLING for too long.
*/
@Test
public void installingPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(INSTALLING);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
Timestamp version = new SystemClockTimestamp(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, version);
store.addPending(data);
service.submit(intent);
expectLastCall().once();
replay(service);
cleanup.run();
verify(service);
reset(service);
}
/**
* Only submit one of two intents because one is too new.
*/
@Test
public void skipPoll() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
store.addPending(data);
Intent intent2 = new MockIntent(2L);
Timestamp version = new SystemClockTimestamp(1L);
data = new IntentData(intent2, INSTALL_REQ, version);
store.addPending(data);
service.submit(intent2);
expectLastCall().once();
replay(service);
cleanup.run();
verify(service);
reset(service);
}
/**
* Verify resubmit in response to CORRUPT event.
*/
@Test
public void corruptEvent() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
service.submit(intent);
expectLastCall().once();
replay(service);
store.addPending(data);
verify(service);
reset(service);
}
/**
* Intent should not be retried because threshold is reached.
*/
@Test
public void corruptEventThreshold() {
IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
@Override
public void process(IntentData intentData) {
intentData.setState(CORRUPT);
intentData.setErrorCount(cleanup.retryThreshold);
store.write(intentData);
}
@Override
public void notify(IntentEvent event) {
cleanup.event(event);
}
};
store.setDelegate(mockDelegate);
Intent intent = new MockIntent(1L);
IntentData data = new IntentData(intent, INSTALL_REQ, null);
replay(service);
store.addPending(data);
verify(service);
reset(service);
}
}
\ No newline at end of file
......@@ -592,6 +592,48 @@ public class IntentManagerTest {
}
/**
* Test failure to install an intent, and verify retries.
*/
@Test
public void testCorruptRetry() {
IntentCleanup cleanup = new IntentCleanup();
cleanup.service = manager;
cleanup.store = manager.store;
cleanup.cfgService = new ComponentConfigAdapter();
cleanup.period = 1_000_000;
cleanup.retryThreshold = 3;
try {
cleanup.activate();
final TestIntentCompilerMultipleFlows errorCompiler = new TestIntentCompilerMultipleFlows();
extensionService.registerCompiler(MockIntent.class, errorCompiler);
List<Intent> intents;
flowRuleService.setFuture(false);
intents = Lists.newArrayList(service.getIntents());
assertThat(intents, hasSize(0));
final MockIntent intent1 = new MockIntent(MockIntent.nextId());
listener.setLatch(1, Type.INSTALL_REQ);
listener.setLatch(cleanup.retryThreshold, Type.CORRUPT);
listener.setLatch(1, Type.INSTALLED);
service.submit(intent1);
listener.await(Type.INSTALL_REQ);
listener.await(Type.CORRUPT);
assertEquals(CORRUPT, manager.getIntentState(intent1.key()));
assertThat(listener.getCounts(Type.CORRUPT), is(cleanup.retryThreshold));
} finally {
cleanup.deactivate();
}
}
/**
* Tests that an intent that fails installation results in no flows remaining.
*/
@Test
......
......@@ -80,11 +80,10 @@ public class SimpleIntentStore
if (localOnly || olderThan > 0) {
long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
final SystemClockTimestamp time = new SystemClockTimestamp(older);
return pending.values().stream()
return current.values().stream()
.filter(data -> data.version().isOlderThan(time) &&
(!localOnly || isMaster(data.key())))
.collect(Collectors.toList());
}
return Lists.newArrayList(current.values());
}
......@@ -174,7 +173,7 @@ public class SimpleIntentStore
existingData.version().compareTo(data.version()) < 0) {
pending.put(data.key(), data);
checkNotNull(delegate, "Store delegate is not set")
.process(data);
.process(new IntentData(data));
notifyDelegateIfNotNull(IntentEvent.getEvent(data));
} else {
log.debug("IntentData {} is older than existing: {}",
......