diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/core/net/src/test | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/core/net/src/test')
41 files changed, 10091 insertions, 0 deletions
diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java new file mode 100644 index 00000000..1ce31ac3 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java @@ -0,0 +1,198 @@ +/* + * 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.app.impl; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.app.ApplicationEvent; +import org.onosproject.app.ApplicationListener; +import org.onosproject.app.ApplicationState; +import org.onosproject.app.ApplicationStoreAdapter; +import org.onosproject.common.app.ApplicationArchive; +import org.onosproject.core.Application; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.DefaultApplication; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.common.event.impl.TestEventDispatcher; + +import java.io.InputStream; +import java.net.URI; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.onosproject.app.ApplicationEvent.Type.*; +import static org.onosproject.app.ApplicationState.ACTIVE; +import static org.onosproject.app.ApplicationState.INSTALLED; +import static org.onosproject.app.DefaultApplicationDescriptionTest.*; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; + +/** + * Test of the application manager implementation. + */ +public class ApplicationManagerTest { + + public static final DefaultApplicationId APP_ID = new DefaultApplicationId(1, APP_NAME); + + private ApplicationManager mgr = new ApplicationManager(); + private ApplicationListener listener = new TestListener(); + + @Before + public void setUp() { + injectEventDispatcher(mgr, new TestEventDispatcher()); + mgr.featuresService = new TestFeaturesService(); + mgr.store = new TestStore(); + mgr.activate(); + mgr.addListener(listener); + } + + @After + public void tearDown() { + mgr.removeListener(listener); + mgr.deactivate(); + } + + private void validate(Application app) { + assertEquals("incorrect name", APP_NAME, app.id().name()); + assertEquals("incorrect version", VER, app.version()); + assertEquals("incorrect origin", ORIGIN, app.origin()); + + assertEquals("incorrect description", DESC, app.description()); + assertEquals("incorrect features URI", FURL, app.featuresRepo().get()); + assertEquals("incorrect features", FEATURES, app.features()); + } + + @Test + public void install() { + InputStream stream = ApplicationArchive.class.getResourceAsStream("app.zip"); + Application app = mgr.install(stream); + validate(app); + assertEquals("incorrect features URI used", app.featuresRepo().get(), + ((TestFeaturesService) mgr.featuresService).uri); + assertEquals("incorrect app count", 1, mgr.getApplications().size()); + assertEquals("incorrect app", app, mgr.getApplication(APP_ID)); + assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID)); + } + + @Test + public void uninstall() { + install(); + mgr.uninstall(APP_ID); + assertEquals("incorrect app count", 0, mgr.getApplications().size()); + } + + @Test + public void activate() { + install(); + mgr.activate(APP_ID); + assertEquals("incorrect app state", ACTIVE, mgr.getState(APP_ID)); + } + + @Test + public void deactivate() { + activate(); + mgr.deactivate(APP_ID); + assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID)); + } + + + private class TestListener implements ApplicationListener { + private ApplicationEvent event; + + @Override + public void event(ApplicationEvent event) { + this.event = event; + } + } + + private class TestStore extends ApplicationStoreAdapter { + + private Application app; + private ApplicationState state; + + @Override + public Application create(InputStream appDescStream) { + app = new DefaultApplication(APP_ID, VER, DESC, ORIGIN, ROLE, PERMS, + Optional.of(FURL), FEATURES); + state = INSTALLED; + delegate.notify(new ApplicationEvent(APP_INSTALLED, app)); + return app; + } + + @Override + public Set<Application> getApplications() { + return app != null ? ImmutableSet.of(app) : ImmutableSet.of(); + } + + @Override + public Application getApplication(ApplicationId appId) { + return app; + } + + @Override + public void remove(ApplicationId appId) { + delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app)); + app = null; + state = null; + } + + @Override + public ApplicationState getState(ApplicationId appId) { + return state; + } + + @Override + public void activate(ApplicationId appId) { + state = ApplicationState.ACTIVE; + delegate.notify(new ApplicationEvent(APP_ACTIVATED, app)); + } + + @Override + public void deactivate(ApplicationId appId) { + state = INSTALLED; + delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app)); + } + } + + private class TestFeaturesService extends FeaturesServiceAdapter { + private URI uri; + private Set<String> features = new HashSet<>(); + + @Override + public void addRepository(URI uri) throws Exception { + this.uri = uri; + } + + @Override + public void removeRepository(URI uri) throws Exception { + this.uri = null; + } + + @Override + public void installFeature(String name) throws Exception { + features.add(name); + } + + @Override + public void uninstallFeature(String name) throws Exception { + features.remove(name); + } + } + +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java b/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java new file mode 100644 index 00000000..fc19b0a1 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java @@ -0,0 +1,168 @@ +/* + * 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.app.impl; + +import org.apache.karaf.features.Feature; +import org.apache.karaf.features.Repository; + +import java.net.URI; +import java.util.EnumSet; +import java.util.Set; + +/** + * Adapter for testing against Apache Karaf feature service. + */ +public class FeaturesServiceAdapter implements org.apache.karaf.features.FeaturesService { + @Override + public void validateRepository(URI uri) throws Exception { + + } + + @Override + public void addRepository(URI uri) throws Exception { + + } + + @Override + public void addRepository(URI uri, boolean install) throws Exception { + + } + + @Override + public void removeRepository(URI uri) throws Exception { + + } + + @Override + public void removeRepository(URI uri, boolean uninstall) throws Exception { + + } + + @Override + public void restoreRepository(URI uri) throws Exception { + + } + + @Override + public Repository[] listRepositories() { + return new Repository[0]; + } + + @Override + public Repository getRepository(String repoName) { + return null; + } + + @Override + public Repository getRepository(URI uri) { + return null; + } + + @Override + public String getRepositoryName(URI uri) { + return null; + } + + @Override + public void installFeature(String name) throws Exception { + + } + + @Override + public void installFeature(String name, EnumSet<Option> options) throws Exception { + + } + + @Override + public void installFeature(String name, String version) throws Exception { + + } + + @Override + public void installFeature(String name, String version, EnumSet<Option> options) throws Exception { + + } + + @Override + public void installFeature(Feature f, EnumSet<Option> options) throws Exception { + + } + + @Override + public void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception { + + } + + @Override + public void uninstallFeature(String name, EnumSet<Option> options) throws Exception { + + } + + @Override + public void uninstallFeature(String name) throws Exception { + + } + + @Override + public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception { + + } + + @Override + public void uninstallFeature(String name, String version) throws Exception { + + } + + @Override + public Feature[] listFeatures() throws Exception { + return new Feature[0]; + } + + @Override + public Feature[] listInstalledFeatures() { + return new Feature[0]; + } + + @Override + public boolean isInstalled(Feature f) { + return false; + } + + @Override + public Feature[] getFeatures(String name, String version) throws Exception { + return new Feature[0]; + } + + @Override + public Feature[] getFeatures(String name) throws Exception { + return new Feature[0]; + } + + @Override + public Feature getFeature(String name, String version) throws Exception { + return null; + } + + @Override + public Feature getFeature(String name) throws Exception { + return null; + } + + @Override + public void refreshRepository(URI uri) throws Exception { + + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ConfigPropertyDefinitionsTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ConfigPropertyDefinitionsTest.java new file mode 100644 index 00000000..66e9c7b4 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ConfigPropertyDefinitionsTest.java @@ -0,0 +1,49 @@ +/* + * 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.cfg.impl; + +import com.google.common.collect.ImmutableSet; +import org.junit.Test; +import org.onosproject.cfg.ConfigProperty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Set; + +import static org.junit.Assert.*; +import static org.onosproject.cfg.ConfigProperty.Type.STRING; +import static org.onosproject.cfg.ConfigProperty.defineProperty; +import static org.onosproject.cfg.impl.ConfigPropertyDefinitions.read; +import static org.onosproject.cfg.impl.ConfigPropertyDefinitions.write; + +/** + * Tests of the config property definitions utility. + */ +public class ConfigPropertyDefinitionsTest { + + @Test + public void basics() throws IOException { + Set<ConfigProperty> original = ImmutableSet + .of(defineProperty("foo", STRING, "dingo", "FOO"), + defineProperty("bar", STRING, "bat", "BAR")); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + write(out, original); + Set<ConfigProperty> read = read(new ByteArrayInputStream(out.toByteArray())); + assertEquals("incorrect defs", original, read); + } + +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java new file mode 100644 index 00000000..bf1a1ff3 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java @@ -0,0 +1,180 @@ +/* + * Copyright 2014-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.cluster.impl; + +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onosproject.cluster.ClusterService; +import org.onosproject.cluster.ClusterServiceAdapter; +import org.onosproject.cluster.ControllerNode; +import org.onosproject.cluster.DefaultControllerNode; +import org.onosproject.cluster.NodeId; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.mastership.MastershipService; +import org.onosproject.mastership.MastershipStore; +import org.onosproject.mastership.MastershipTermService; +import org.onosproject.net.DeviceId; +import org.onosproject.store.trivial.SimpleMastershipStore; + +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.onosproject.net.MastershipRole.MASTER; +import static org.onosproject.net.MastershipRole.NONE; +import static org.onosproject.net.MastershipRole.STANDBY; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; + +/** + * Test codifying the mastership service contracts. + */ +public class MastershipManagerTest { + + private static final NodeId NID_LOCAL = new NodeId("local"); + private static final NodeId NID_OTHER = new NodeId("foo"); + private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1"); + private static final DeviceId DEV_MASTER = DeviceId.deviceId("of:1"); + private static final DeviceId DEV_OTHER = DeviceId.deviceId("of:2"); + + private MastershipManager mgr; + protected MastershipService service; + + @Before + public void setUp() { + mgr = new MastershipManager(); + service = mgr; + injectEventDispatcher(mgr, new TestEventDispatcher()); + mgr.clusterService = new TestClusterService(); + mgr.store = new TestSimpleMastershipStore(mgr.clusterService); + mgr.activate(); + } + + @After + public void tearDown() { + mgr.deactivate(); + mgr.clusterService = null; + injectEventDispatcher(mgr, null); + mgr.store = null; + } + + @Test + public void setRole() { + mgr.setRole(NID_OTHER, DEV_MASTER, MASTER); + assertEquals("wrong local role:", NONE, mgr.getLocalRole(DEV_MASTER)); + assertEquals("wrong obtained role:", STANDBY, Futures.getUnchecked(mgr.requestRoleFor(DEV_MASTER))); + + //set to master + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("wrong local role:", MASTER, mgr.getLocalRole(DEV_MASTER)); + } + + @Test + public void relinquishMastership() { + //no backups - should just turn to NONE for device. + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("wrong role:", MASTER, mgr.getLocalRole(DEV_MASTER)); + mgr.relinquishMastership(DEV_MASTER); + assertNull("wrong master:", mgr.getMasterFor(DEV_OTHER)); + assertEquals("wrong role:", NONE, mgr.getLocalRole(DEV_MASTER)); + + //not master, nothing should happen + mgr.setRole(NID_LOCAL, DEV_OTHER, NONE); + mgr.relinquishMastership(DEV_OTHER); + assertNull("wrong role:", mgr.getMasterFor(DEV_OTHER)); + + //provide NID_OTHER as backup and relinquish + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER)); + mgr.setRole(NID_OTHER, DEV_MASTER, STANDBY); + mgr.relinquishMastership(DEV_MASTER); + assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_MASTER)); + } + + @Test + public void requestRoleFor() { + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + mgr.setRole(NID_OTHER, DEV_OTHER, MASTER); + + //local should be master for one but standby for other + assertEquals("wrong role:", MASTER, Futures.getUnchecked(mgr.requestRoleFor(DEV_MASTER))); + assertEquals("wrong role:", STANDBY, Futures.getUnchecked(mgr.requestRoleFor(DEV_OTHER))); + } + + @Test + public void getMasterFor() { + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + mgr.setRole(NID_OTHER, DEV_OTHER, MASTER); + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER)); + assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_OTHER)); + + //have NID_OTHER hand over DEV_OTHER to NID_LOCAL + mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_OTHER)); + } + + @Test + public void getDevicesOf() { + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY); + assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size()); + //hand both devices to NID_LOCAL + mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); + assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size()); + } + + @Test + public void termService() { + MastershipTermService ts = mgr; + + //term = 1 for both + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("inconsistent term: ", 1, ts.getMastershipTerm(DEV_MASTER).termNumber()); + + //hand devices to NID_LOCAL and back: term = 1 + 2 + mgr.setRole(NID_OTHER, DEV_MASTER, MASTER); + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("inconsistent terms: ", 3, ts.getMastershipTerm(DEV_MASTER).termNumber()); + } + + private final class TestClusterService extends ClusterServiceAdapter { + + ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST); + + @Override + public ControllerNode getLocalNode() { + return local; + } + + @Override + public Set<ControllerNode> getNodes() { + return Sets.newHashSet(); + } + + } + + private final class TestSimpleMastershipStore extends SimpleMastershipStore + implements MastershipStore { + + public TestSimpleMastershipStore(ClusterService clusterService) { + super.clusterService = clusterService; + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/DummyIdBlockAllocator.java b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/DummyIdBlockAllocator.java new file mode 100644 index 00000000..383e981a --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/DummyIdBlockAllocator.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 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.core.impl; + +import org.onosproject.core.IdBlock; + +public class DummyIdBlockAllocator implements IdBlockAllocator { + private long blockTop; + private static final long BLOCK_SIZE = 0x1000000L; + + /** + * Returns a block of IDs which are unique and unused. + * Range of IDs is fixed size and is assigned incrementally as this method + * called. + * + * @return an IdBlock containing a set of unique IDs + */ + @Override + public IdBlock allocateUniqueIdBlock() { + synchronized (this) { + long blockHead = blockTop; + long blockTail = blockTop + BLOCK_SIZE; + + IdBlock block = new IdBlock(blockHead, BLOCK_SIZE); + blockTop = blockTail; + + return block; + } + } + + @Override + public IdBlock allocateUniqueIdBlock(long range) { + throw new UnsupportedOperationException("Not supported yet"); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/IdBlockAllocatorBasedIdGeneratorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/IdBlockAllocatorBasedIdGeneratorTest.java new file mode 100644 index 00000000..8a4e6185 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/IdBlockAllocatorBasedIdGeneratorTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014 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.core.impl; + +import org.easymock.EasyMock; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.core.IdBlock; + +/** + * Suites of test of {@link org.onosproject.core.impl.BlockAllocatorBasedIdGenerator}. + */ +public class IdBlockAllocatorBasedIdGeneratorTest { + private IdBlockAllocator allocator; + private BlockAllocatorBasedIdGenerator sut; + + @Before + public void setUp() { + allocator = EasyMock.createMock(IdBlockAllocator.class); + + } + + /** + * Tests generated IntentId sequences using two {@link org.onosproject.core.IdBlock blocks}. + */ + @Test + public void testIds() { + EasyMock.expect(allocator.allocateUniqueIdBlock()) + .andReturn(new IdBlock(0, 3)) + .andReturn(new IdBlock(4, 3)); + + EasyMock.replay(allocator); + sut = new BlockAllocatorBasedIdGenerator(allocator); + + Assert.assertThat(sut.getNewId(), Matchers.is(0L)); + Assert.assertThat(sut.getNewId(), Matchers.is(1L)); + Assert.assertThat(sut.getNewId(), Matchers.is(2L)); + + Assert.assertThat(sut.getNewId(), Matchers.is(4L)); + Assert.assertThat(sut.getNewId(), Matchers.is(5L)); + Assert.assertThat(sut.getNewId(), Matchers.is(6L)); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/TestCoreManager.java b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/TestCoreManager.java new file mode 100644 index 00000000..474d1705 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/core/impl/TestCoreManager.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014-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.core.impl; + +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.core.IdGenerator; + +public class TestCoreManager extends CoreServiceAdapter { + + @Override + public IdGenerator getIdGenerator(String topic) { + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator(); + return new BlockAllocatorBasedIdGenerator(idBlockAllocator); + } + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/event/impl/CoreEventDispatcherTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/event/impl/CoreEventDispatcherTest.java new file mode 100644 index 00000000..9ba3db59 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/event/impl/CoreEventDispatcherTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2014 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.event.impl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.event.AbstractEvent; +import org.onosproject.event.EventSink; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; + +/** + * Test of the even dispatcher mechanism. + */ +public class CoreEventDispatcherTest { + + private final CoreEventDispatcher dispatcher = new CoreEventDispatcher(); + private final PrickleSink prickleSink = new PrickleSink(); + private final GooSink gooSink = new GooSink(); + + @Before + public void setUp() { + dispatcher.activate(); + dispatcher.addSink(Prickle.class, prickleSink); + dispatcher.addSink(Goo.class, gooSink); + } + + @After + public void tearDown() { + dispatcher.removeSink(Goo.class); + dispatcher.removeSink(Prickle.class); + dispatcher.deactivate(); + } + + @Test + public void post() throws Exception { + prickleSink.latch = new CountDownLatch(1); + dispatcher.post(new Prickle("yo")); + prickleSink.latch.await(100, TimeUnit.MILLISECONDS); + validate(prickleSink, "yo"); + validate(gooSink); + } + + @Test + public void postEventWithBadSink() throws Exception { + gooSink.latch = new CountDownLatch(1); + dispatcher.post(new Goo("boom")); + gooSink.latch.await(100, TimeUnit.MILLISECONDS); + validate(gooSink, "boom"); + validate(prickleSink); + } + + @Test + public void postEventWithNoSink() throws Exception { + dispatcher.post(new Thing("boom")); + validate(gooSink); + validate(prickleSink); + } + + private void validate(Sink sink, String... strings) { + int i = 0; + assertEquals("incorrect event count", strings.length, sink.subjects.size()); + for (String string : strings) { + assertEquals("incorrect event", string, sink.subjects.get(i++)); + } + } + + private enum Type { FOO } + + private static class Thing extends AbstractEvent<Type, String> { + protected Thing(String subject) { + super(Type.FOO, subject); + } + } + + private static class Prickle extends Thing { + protected Prickle(String subject) { + super(subject); + } + } + + private static class Goo extends Thing { + protected Goo(String subject) { + super(subject); + } + } + + private static class Sink { + final List<String> subjects = new ArrayList<>(); + CountDownLatch latch; + + protected void process(String subject) { + subjects.add(subject); + latch.countDown(); + } + } + + private static class PrickleSink extends Sink implements EventSink<Prickle> { + @Override + public void process(Prickle event) { + process(event.subject()); + } + } + + private static class GooSink extends Sink implements EventSink<Goo> { + @Override + public void process(Goo event) { + process(event.subject()); + throw new IllegalStateException("BOOM!"); + } + } + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/config/impl/NetworkConfigManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/config/impl/NetworkConfigManagerTest.java new file mode 100644 index 00000000..2d03bfc8 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/config/impl/NetworkConfigManagerTest.java @@ -0,0 +1,242 @@ +/* + * 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.config.impl; + +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.junit.TestUtils; +import org.onosproject.event.EventDeliveryServiceAdapter; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.net.config.SubjectFactory; +import org.onosproject.net.NetTestTools; +import org.onosproject.store.config.impl.DistributedNetworkConfigStore; +import org.onosproject.store.service.TestStorageService; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.testing.EqualsTester; + +/** + * Unit tests for network config registry. + */ +public class NetworkConfigManagerTest { + private NetworkConfigManager manager; + private NetworkConfigRegistry registry; + private NetworkConfigService configService; + private DistributedNetworkConfigStore configStore; + + /** + * Config classes for testing. + */ + public class BasicConfig1 extends Config<String> { } + public class BasicConfig2 extends Config<String> { } + + public class MockSubjectFactory extends SubjectFactory<String> { + protected MockSubjectFactory(Class<String> subjectClass, String subjectKey) { + super(subjectClass, subjectKey); + } + + @Override + public String createSubject(String subjectKey) { + return subjectKey + "-subject"; + } + } + + /** + * Config factory classes for testing. + */ + public class MockConfigFactory1 extends ConfigFactory<String, BasicConfig1> { + protected MockConfigFactory1(SubjectFactory<String> subjectFactory, + Class<BasicConfig1> configClass, String configKey) { + super(subjectFactory, configClass, configKey); + } + @Override + public BasicConfig1 createConfig() { + return new BasicConfig1(); + } + } + + public class MockConfigFactory2 extends ConfigFactory<String, BasicConfig2> { + protected MockConfigFactory2(SubjectFactory<String> subjectFactory, + Class<BasicConfig2> configClass, String configKey) { + super(subjectFactory, configClass, configKey); + } + @Override + public BasicConfig2 createConfig() { + return new BasicConfig2(); + } + } + + MockSubjectFactory factory1 = new MockSubjectFactory(String.class, + "key1"); + MockSubjectFactory factory2 = new MockSubjectFactory(String.class, + "key2"); + + MockConfigFactory1 config1Factory = new MockConfigFactory1(factory1, + BasicConfig1.class, "config1"); + MockConfigFactory2 config2Factory = new MockConfigFactory2(factory2, + BasicConfig2.class, "config2"); + + + @Before + public void setUp() throws Exception { + configStore = new DistributedNetworkConfigStore(); + TestUtils.setField(configStore, "storageService", new TestStorageService()); + configStore.activate(); + manager = new NetworkConfigManager(); + manager.store = configStore; + NetTestTools.injectEventDispatcher(manager, new EventDeliveryServiceAdapter()); + manager.activate(); + registry = manager; + configService = manager; + } + + @After + public void tearDown() { + configStore.deactivate(); + manager.deactivate(); + } + + @Test + public void testRegistry() { + assertThat(registry.getConfigFactories(), hasSize(0)); + assertThat(registry.getConfigFactories(String.class), hasSize(0)); + assertThat(registry.getConfigFactory(BasicConfig1.class), nullValue()); + + registry.registerConfigFactory(config1Factory); + registry.registerConfigFactory(config2Factory); + + assertThat(registry.getConfigFactories(), hasSize(2)); + assertThat(registry.getConfigFactories(String.class), hasSize(2)); + + ConfigFactory queried = registry.getConfigFactory(BasicConfig1.class); + assertThat(queried, is(config1Factory)); + + registry.unregisterConfigFactory(queried); + // Factory associations are not removed according to code documentation + assertThat(registry.getConfigFactories(), hasSize(1)); + assertThat(registry.getConfigFactories(String.class), hasSize(1)); + assertThat(registry.getConfigFactory(BasicConfig1.class), nullValue()); + } + + @Test + public void configIdEquals() { + NetworkConfigManager.ConfigIdentifier id1 = + new NetworkConfigManager.ConfigIdentifier("s1", "c1"); + NetworkConfigManager.ConfigIdentifier likeId1 = + new NetworkConfigManager.ConfigIdentifier("s1", "c1"); + NetworkConfigManager.ConfigIdentifier id2 = + new NetworkConfigManager.ConfigIdentifier("s1", "c2"); + NetworkConfigManager.ConfigIdentifier id3 = + new NetworkConfigManager.ConfigIdentifier("s2", "c1"); + + new EqualsTester().addEqualityGroup(id1, likeId1) + .addEqualityGroup(id2) + .addEqualityGroup(id3) + .testEquals(); + } + + @Test + public void configKeyEquals() { + NetworkConfigManager.ConfigKey key1 = + new NetworkConfigManager.ConfigKey(String.class, String.class); + NetworkConfigManager.ConfigKey likeKey1 = + new NetworkConfigManager.ConfigKey(String.class, String.class); + NetworkConfigManager.ConfigKey key2 = + new NetworkConfigManager.ConfigKey(String.class, Integer.class); + NetworkConfigManager.ConfigKey key3 = + new NetworkConfigManager.ConfigKey(Integer.class, String.class); + + new EqualsTester().addEqualityGroup(key1, likeKey1) + .addEqualityGroup(key2) + .addEqualityGroup(key3) + .testEquals(); + } + + /** + * Tests creation, query and removal of a factory. + */ + @Test + public void testAddConfig() { + + assertThat(configService.getSubjectFactory(String.class), nullValue()); + assertThat(configService.getSubjectFactory("key"), nullValue()); + + registry.registerConfigFactory(config1Factory); + registry.registerConfigFactory(config2Factory); + configService.addConfig("configKey", BasicConfig1.class); + + Config newConfig = configService.getConfig("configKey", BasicConfig1.class); + assertThat(newConfig, notNullValue()); + + assertThat(configService.getSubjectFactory(String.class), notNullValue()); + assertThat(configService.getSubjectFactory("key1"), notNullValue()); + + Set<Class> classes = configService.getSubjectClasses(); + assertThat(classes, hasSize(1)); + + Set<String> subjectsForClass = + configService.getSubjects(String.class); + assertThat(subjectsForClass, hasSize(1)); + + Set<String> subjectsForConfig = + configService.getSubjects(String.class, BasicConfig1.class); + assertThat(subjectsForConfig, hasSize(1)); + + Class queriedConfigClass = configService.getConfigClass("key1", "config1"); + assertThat(queriedConfigClass == BasicConfig1.class, is(true)); + + Set<? extends Config> configs = configService.getConfigs("configKey"); + assertThat(configs.size(), is(1)); + configs.forEach(c -> assertThat(c, instanceOf(BasicConfig1.class))); + + configService.removeConfig("configKey", BasicConfig1.class); + Config newConfigAfterRemove = configService.getConfig("configKey", BasicConfig1.class); + assertThat(newConfigAfterRemove, nullValue()); + } + + /** + * Tests creation, query and removal of a factory. + */ + @Test + public void testApplyConfig() { + + assertThat(configService.getSubjectFactory(String.class), nullValue()); + assertThat(configService.getSubjectFactory("key"), nullValue()); + + registry.registerConfigFactory(config1Factory); + registry.registerConfigFactory(config2Factory); + configService.applyConfig("configKey", BasicConfig1.class, new ObjectMapper().createObjectNode()); + + Config newConfig = configService.getConfig("configKey", BasicConfig1.class); + assertThat(newConfig, notNullValue()); + + assertThat(configService.getSubjectFactory(String.class), notNullValue()); + assertThat(configService.getSubjectFactory("key1"), notNullValue()); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/BasicDeviceOperatorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/BasicDeviceOperatorTest.java new file mode 100644 index 00000000..8827c558 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/BasicDeviceOperatorTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2014-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.device.impl; + +import static org.onosproject.net.Device.Type.SWITCH; +import static org.onosproject.net.Device.Type.ROADM; +import static org.junit.Assert.assertEquals; + +import java.net.URI; + +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.ChassisId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; +import org.onosproject.net.config.basics.BasicDeviceConfig; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DeviceId; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DeviceDescription; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +public class BasicDeviceOperatorTest { + + private static final String NAME1 = "of:foo"; + private static final String NAME2 = "of:bar"; + private static final String OWNER = "somebody"; + private static final URI DURI = URI.create(NAME1); + private static final String MFR = "whitebox"; + private static final String HW = "1.1.x"; + private static final String SW = "3.9.1"; + private static final String SN = "43311-12345"; + private static final ChassisId CID = new ChassisId(); + + private static final SparseAnnotations SA = DefaultAnnotations.builder() + .set(AnnotationKeys.DRIVER, NAME2).build(); + + private static final DeviceDescription DEV1 = new DefaultDeviceDescription( + DURI, SWITCH, MFR, HW, SW, SN, CID, SA); + + private final ConfigApplyDelegate delegate = new ConfigApplyDelegate() { + @Override + public void onApply(Config config) { + } + }; + private final ObjectMapper mapper = new ObjectMapper(); + + private static final BasicDeviceConfig SW_BDC = new BasicDeviceConfig(); + private static final BasicDeviceConfig RD_BDC = new BasicDeviceConfig(); + + @Before + public void setUp() { + SW_BDC.init(DeviceId.deviceId(NAME1), NAME1, JsonNodeFactory.instance.objectNode(), mapper, delegate); + SW_BDC.type(SWITCH).driver(NAME1).owner(OWNER); + RD_BDC.init(DeviceId.deviceId(NAME2), NAME2, JsonNodeFactory.instance.objectNode(), mapper, delegate); + RD_BDC.type(ROADM); + } + + @Test + public void testDescOps() { + DeviceDescription desc = BasicDeviceOperator.combine(null, DEV1); + assertEquals(desc, DEV1); + + // override driver name + desc = BasicDeviceOperator.combine(SW_BDC, DEV1); + assertEquals(NAME1, desc.annotations().value(AnnotationKeys.DRIVER)); + + // override Device Type + desc = BasicDeviceOperator.combine(RD_BDC, DEV1); + assertEquals(ROADM, desc.type()); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java new file mode 100644 index 00000000..04f266f0 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java @@ -0,0 +1,331 @@ +/* + * Copyright 2014-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.device.impl; + +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.ChassisId; +import org.onlab.packet.IpAddress; +import org.onosproject.cluster.ClusterServiceAdapter; +import org.onosproject.cluster.ControllerNode; +import org.onosproject.cluster.DefaultControllerNode; +import org.onosproject.cluster.NodeId; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.event.Event; +import org.onosproject.net.config.NetworkConfigServiceAdapter; +import org.onosproject.mastership.MastershipServiceAdapter; +import org.onosproject.mastership.MastershipTerm; +import org.onosproject.mastership.MastershipTermService; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DefaultPortDescription; +import org.onosproject.net.device.DeviceAdminService; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.trivial.SimpleDeviceStore; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import static org.junit.Assert.*; +import static org.onosproject.net.Device.Type.SWITCH; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.device.DeviceEvent.Type.*; + +/** + * Test codifying the device service & device provider service contracts. + */ +public class DeviceManagerTest { + + private static final ProviderId PID = new ProviderId("of", "foo"); + private static final DeviceId DID1 = deviceId("of:foo"); + private static final DeviceId DID2 = deviceId("of:bar"); + private static final String MFR = "whitebox"; + private static final String HW = "1.1.x"; + private static final String SW1 = "3.8.1"; + private static final String SW2 = "3.9.5"; + private static final String SN = "43311-12345"; + private static final ChassisId CID = new ChassisId(); + + private static final PortNumber P1 = PortNumber.portNumber(1); + private static final PortNumber P2 = PortNumber.portNumber(2); + private static final PortNumber P3 = PortNumber.portNumber(3); + private static final NodeId NID_LOCAL = new NodeId("local"); + private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1"); + + private DeviceManager mgr; + + protected DeviceService service; + protected DeviceAdminService admin; + protected DeviceProviderRegistry registry; + protected DeviceProviderService providerService; + protected TestProvider provider; + protected TestListener listener = new TestListener(); + + @Before + public void setUp() { + mgr = new DeviceManager(); + service = mgr; + admin = mgr; + registry = mgr; + mgr.store = new SimpleDeviceStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + TestMastershipManager mastershipManager = new TestMastershipManager(); + mgr.mastershipService = mastershipManager; + mgr.termService = mastershipManager; + mgr.clusterService = new TestClusterService(); + mgr.networkConfigService = new TestNetworkConfigService(); + mgr.activate(); + + + service.addListener(listener); + + provider = new TestProvider(); + providerService = registry.register(provider); + assertTrue("provider should be registered", + registry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + registry.unregister(provider); + assertFalse("provider should not be registered", + registry.getProviders().contains(provider.id())); + service.removeListener(listener); + mgr.deactivate(); + } + + private void connectDevice(DeviceId deviceId, String swVersion) { + DeviceDescription description = + new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, + HW, swVersion, SN, CID); + providerService.deviceConnected(deviceId, description); + assertNotNull("device should be found", service.getDevice(DID1)); + } + + @Test + public void deviceConnected() { + assertNull("device should not be found", service.getDevice(DID1)); + connectDevice(DID1, SW1); + validateEvents(DEVICE_ADDED); + + Iterator<Device> it = service.getDevices().iterator(); + assertNotNull("one device expected", it.next()); + assertFalse("only one device expected", it.hasNext()); + assertEquals("incorrect device count", 1, service.getDeviceCount()); + assertTrue("device should be available", service.isAvailable(DID1)); + } + + @Test + public void deviceDisconnected() { + connectDevice(DID1, SW1); + connectDevice(DID2, SW1); + validateEvents(DEVICE_ADDED, DEVICE_ADDED); + assertTrue("device should be available", service.isAvailable(DID1)); + + // Disconnect + providerService.deviceDisconnected(DID1); + assertNotNull("device should not be found", service.getDevice(DID1)); + assertFalse("device should not be available", service.isAvailable(DID1)); + validateEvents(DEVICE_AVAILABILITY_CHANGED); + + // Reconnect + connectDevice(DID1, SW1); + validateEvents(DEVICE_AVAILABILITY_CHANGED); + + assertEquals("incorrect device count", 2, service.getDeviceCount()); + } + + @Test + public void deviceUpdated() { + connectDevice(DID1, SW1); + validateEvents(DEVICE_ADDED); + + connectDevice(DID1, SW2); + validateEvents(DEVICE_UPDATED); + } + + @Test + public void getRole() { + connectDevice(DID1, SW1); + assertEquals("incorrect role", MastershipRole.MASTER, service.getRole(DID1)); + } + + @Test + public void updatePorts() { + connectDevice(DID1, SW1); + List<PortDescription> pds = new ArrayList<>(); + pds.add(new DefaultPortDescription(P1, true)); + pds.add(new DefaultPortDescription(P2, true)); + pds.add(new DefaultPortDescription(P3, true)); + providerService.updatePorts(DID1, pds); + validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED); + pds.clear(); + + pds.add(new DefaultPortDescription(P1, false)); + pds.add(new DefaultPortDescription(P3, true)); + providerService.updatePorts(DID1, pds); + validateEvents(PORT_UPDATED, PORT_REMOVED); + } + + @Test + public void updatePortStatus() { + connectDevice(DID1, SW1); + List<PortDescription> pds = new ArrayList<>(); + pds.add(new DefaultPortDescription(P1, true)); + pds.add(new DefaultPortDescription(P2, true)); + providerService.updatePorts(DID1, pds); + validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); + + providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false)); + validateEvents(PORT_UPDATED); + providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false)); + assertTrue("no events expected", listener.events.isEmpty()); + } + + @Test + public void getPorts() { + connectDevice(DID1, SW1); + List<PortDescription> pds = new ArrayList<>(); + pds.add(new DefaultPortDescription(P1, true)); + pds.add(new DefaultPortDescription(P2, true)); + providerService.updatePorts(DID1, pds); + validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); + assertEquals("wrong port count", 2, service.getPorts(DID1).size()); + + Port port = service.getPort(DID1, P1); + assertEquals("incorrect port", P1, service.getPort(DID1, P1).number()); + assertEquals("incorrect state", true, service.getPort(DID1, P1).isEnabled()); + } + + @Test + public void removeDevice() { + connectDevice(DID1, SW1); + connectDevice(DID2, SW2); + assertEquals("incorrect device count", 2, service.getDeviceCount()); + admin.removeDevice(DID1); + assertNull("device should not be found", service.getDevice(DID1)); + assertNotNull("device should be found", service.getDevice(DID2)); + assertEquals("incorrect device count", 1, service.getDeviceCount()); + + } + + protected void validateEvents(Enum... types) { + int i = 0; + assertEquals("wrong events received", types.length, listener.events.size()); + for (Event event : listener.events) { + assertEquals("incorrect event type", types[i], event.type()); + i++; + } + listener.events.clear(); + } + + + private class TestProvider extends AbstractProvider implements DeviceProvider { + private DeviceId deviceReceived; + private MastershipRole roleReceived; + + public TestProvider() { + super(PID); + } + + @Override + public void triggerProbe(DeviceId deviceId) { + } + + @Override + public void roleChanged(DeviceId device, MastershipRole newRole) { + deviceReceived = device; + roleReceived = newRole; + } + + @Override + public boolean isReachable(DeviceId device) { + return false; + } + } + + private static class TestListener implements DeviceListener { + final List<DeviceEvent> events = new ArrayList<>(); + + @Override + public void event(DeviceEvent event) { + events.add(event); + } + } + + private static class TestMastershipManager + extends MastershipServiceAdapter implements MastershipTermService { + @Override + public MastershipRole getLocalRole(DeviceId deviceId) { + return MastershipRole.MASTER; + } + + @Override + public Set<DeviceId> getDevicesOf(NodeId nodeId) { + return Sets.newHashSet(DID1, DID2); + } + + @Override + public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) { + return CompletableFuture.completedFuture(MastershipRole.MASTER); + } + + @Override + public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) { + return CompletableFuture.completedFuture(null); + } + + @Override + public MastershipTerm getMastershipTerm(DeviceId deviceId) { + // FIXME: just returning something not null + return MastershipTerm.of(NID_LOCAL, 1); + } + } + + // code clone + private final class TestClusterService extends ClusterServiceAdapter { + + ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST); + + @Override + public ControllerNode getLocalNode() { + return local; + } + + } + + private class TestNetworkConfigService extends NetworkConfigServiceAdapter { + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/OpticalPortOperatorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/OpticalPortOperatorTest.java new file mode 100644 index 00000000..78bc08e0 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/device/impl/OpticalPortOperatorTest.java @@ -0,0 +1,80 @@ +package org.onosproject.net.device.impl; + +import org.junit.Before; +import org.junit.Test; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; +import org.onosproject.net.config.basics.OpticalPortConfig; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DeviceId; +import org.onosproject.net.OduCltPort; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.device.OduCltPortDescription; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import static org.junit.Assert.assertEquals; + +public class OpticalPortOperatorTest { + private static final DeviceId DID = DeviceId.deviceId("op-test"); + private static final String TPNAME = "test-port-100"; + private static final String SPNAME = "out-port-200"; + private static final String CFGNAME = "cfg-name"; + + private static final PortNumber NAMED = PortNumber.portNumber(100, TPNAME); + private static final PortNumber UNNAMED = PortNumber.portNumber(101); + private static final ConnectPoint NCP = new ConnectPoint(DID, UNNAMED); + + private static final SparseAnnotations SA = DefaultAnnotations.builder() + .set(AnnotationKeys.STATIC_PORT, SPNAME) + .build(); + + private static final OduCltPortDescription N_DESC = new OduCltPortDescription( + NAMED, true, OduCltPort.SignalType.CLT_100GBE, SA); + private static final OduCltPortDescription FAULTY = new OduCltPortDescription( + null, true, OduCltPort.SignalType.CLT_100GBE); + + private final ConfigApplyDelegate delegate = new MockCfgDelegate(); + private final ObjectMapper mapper = new ObjectMapper(); + + private static final OpticalPortConfig N_OPC = new OpticalPortConfig(); + private static final OpticalPortConfig UNN_OPC = new OpticalPortConfig(); + + @Before + public void setUp() { + N_OPC.init(NCP, TPNAME, JsonNodeFactory.instance.objectNode(), mapper, delegate); + UNN_OPC.init(NCP, TPNAME, JsonNodeFactory.instance.objectNode(), mapper, delegate); + + N_OPC.portName(CFGNAME).portNumberName(101L).portType(Port.Type.ODUCLT).staticLambda(300L); + UNN_OPC.portType(Port.Type.ODUCLT); + } + + @Test(expected = RuntimeException.class) + public void testDescOps() { + // port-null desc + opc with port number name + OduCltPortDescription res = (OduCltPortDescription) OpticalPortOperator.combine(N_OPC, FAULTY); + assertEquals(CFGNAME, res.portNumber().name()); + // full desc + opc with name + assertEquals(TPNAME, N_DESC.portNumber().name()); + res = (OduCltPortDescription) OpticalPortOperator.combine(N_OPC, N_DESC); + long sl = Long.valueOf(res.annotations().value(AnnotationKeys.STATIC_LAMBDA)); + assertEquals(CFGNAME, res.portNumber().name()); + assertEquals(300L, sl); + // port-null desc + opc without port number name - throws RE + res = (OduCltPortDescription) OpticalPortOperator.combine(UNN_OPC, FAULTY); + } + + private class MockCfgDelegate implements ConfigApplyDelegate { + + @Override + public void onApply(@SuppressWarnings("rawtypes") Config config) { + config.apply(); + } + + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java new file mode 100644 index 00000000..319412fe --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java @@ -0,0 +1,514 @@ +/* + * 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.edgeservice.impl; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.event.Event; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultPort; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.NetTestTools; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.edge.EdgePortEvent; +import org.onosproject.net.edge.EdgePortListener; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.link.LinkEvent; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyEvent; +import org.onosproject.net.topology.TopologyListener; +import org.onosproject.net.topology.TopologyServiceAdapter; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.Assert.*; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.device.DeviceEvent.Type.*; +import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_ADDED; +import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_REMOVED; +import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; +import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED; +import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED; + +/** + * Test of the edge port manager. Each device has ports '0' through 'numPorts - 1' + * as specified by the variable 'numPorts'. + */ +public class EdgeManagerTest { + + private EdgeManager mgr; + private int totalPorts = 10; + private boolean alwaysReturnPorts = false; + private final Set<ConnectPoint> infrastructurePorts = Sets.newConcurrentHashSet(); + private List<EdgePortEvent> events = Lists.newArrayList(); + private final Map<DeviceId, Device> devices = Maps.newConcurrentMap(); + private Set<OutboundPacket> packets = Sets.newConcurrentHashSet(); + private final EdgePortListener testListener = new TestListener(events); + private TestTopologyManager testTopologyManager; + + @Before + public void setUp() { + mgr = new EdgeManager(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + testTopologyManager = new TestTopologyManager(infrastructurePorts); + mgr.topologyService = testTopologyManager; + mgr.deviceService = new TestDeviceManager(devices); + mgr.packetService = new TestPacketManager(); + mgr.activate(); + mgr.addListener(testListener); + + } + + + @After + public void tearDown() { + mgr.removeListener(testListener); + mgr.deactivate(); + } + + @Test + public void testBasics() { + //Setup + int numDevices = 20; + int numPorts = 4; + defaultPopulator(numDevices, numPorts); + + assertEquals("Unexpected number of ports", numDevices * numPorts, infrastructurePorts.size()); + + assertFalse("no ports expected", mgr.getEdgePoints().iterator().hasNext()); + + assertFalse("Expected isEdge to return false", + mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1))); + + removeInfraPort(NetTestTools.connectPoint(Integer.toString(1), 1)); + assertTrue("Expected isEdge to return false", + mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1))); + } + + @Test + public void testLinkUpdates() { + //Setup + ConnectPoint testPoint, referencePoint; + + //Testing link removal + List<Event> eventsToAdd = Lists.newArrayList(); + eventsToAdd.add(new LinkEvent(LINK_REMOVED, NetTestTools.link("a", 1, "b", 2))); + TopologyEvent event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + assertTrue("The list contained an unexpected number of events", events.size() == 2); + assertTrue("The first element is of the wrong type.", + events.get(0).type() == EDGE_PORT_ADDED); + assertTrue("The second element is of the wrong type.", + events.get(1).type() == EDGE_PORT_ADDED); + + testPoint = events.get(0).subject(); + referencePoint = NetTestTools.connectPoint("a", 1); + assertTrue("The port numbers of the first element are incorrect", + testPoint.port().toLong() == referencePoint.port().toLong()); + assertTrue("The device id of the first element is incorrect.", + testPoint.deviceId().equals(referencePoint.deviceId())); + + testPoint = events.get(1).subject(); + referencePoint = NetTestTools.connectPoint("b", 2); + assertTrue("The port numbers of the second element are incorrect", + testPoint.port().toLong() == referencePoint.port().toLong()); + assertTrue("The device id of the second element is incorrect.", + testPoint.deviceId().equals(referencePoint.deviceId())); + + //Rebroadcast event to ensure it results in no additional events + testTopologyManager.listener.event(event); + assertTrue("The list contained an unexpected number of events", events.size() == 2); + + //Testing link adding when links to remove exist + eventsToAdd.clear(); + events.clear(); + eventsToAdd.add(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2))); + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + assertTrue("The list contained an unexpected number of events", events.size() == 2); + assertTrue("The first element is of the wrong type.", + events.get(0).type() == EDGE_PORT_REMOVED); + assertTrue("The second element is of the wrong type.", + events.get(1).type() == EDGE_PORT_REMOVED); + + testPoint = events.get(0).subject(); + referencePoint = NetTestTools.connectPoint("a", 1); + assertTrue("The port numbers of the first element are incorrect", + testPoint.port().toLong() == referencePoint.port().toLong()); + assertTrue("The device id of the first element is incorrect.", + testPoint.deviceId().equals(referencePoint.deviceId())); + + testPoint = events.get(1).subject(); + referencePoint = NetTestTools.connectPoint("b", 2); + assertTrue("The port numbers of the second element are incorrect", + testPoint.port().toLong() == referencePoint.port().toLong()); + assertTrue("The device id of the second element is incorrect.", + testPoint.deviceId().equals(referencePoint.deviceId())); + + //Apparent duplicate of previous method tests removal when the elements have already been removed + eventsToAdd.clear(); + events.clear(); + eventsToAdd.add(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2))); + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + assertTrue("The list should contain no events, the removed elements don't exist.", events.size() == 0); + } + + @Test + public void testDeviceUpdates() { + //Setup + + Device referenceDevice; + TopologyEvent event; + List<Event> eventsToAdd = Lists.newArrayList(); + int numDevices = 10; + int numInfraPorts = 5; + totalPorts = 10; + defaultPopulator(numDevices, numInfraPorts); + + //Test response to device added events + referenceDevice = NetTestTools.device("1"); + eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, referenceDevice, + new DefaultPort(referenceDevice, PortNumber.portNumber(1), true))); + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + //Check that ports were populated correctly + assertTrue("Unexpected number of new ports added", + mgr.deviceService.getPorts(NetTestTools.did("1")).size() == 10); + + //Check that of the ten ports the half that are infrastructure ports aren't added + assertEquals("Unexpected number of new edge ports added", (totalPorts - numInfraPorts), events.size()); + + for (int index = 0; index < numInfraPorts; index++) { + assertTrue("Unexpected type of event", events.get(index).type() == EDGE_PORT_ADDED); + } + //Names here are irrelevant, the first 5 ports are populated as infrastructure, 6-10 are edge + for (int index = 0; index < events.size(); index++) { + assertEquals("Port added had unexpected port number.", + events.get(index).subject().port(), + NetTestTools.connectPoint("a", index + numInfraPorts + 1).port()); + } + events.clear(); + + //Repost the event to test repeated posts + testTopologyManager.listener.event(event); + assertEquals("The redundant notification should not have created additional notifications.", + 0, events.size()); + //Calculate the size of the returned iterable of edge points. + Iterable<ConnectPoint> pts = mgr.getEdgePoints(); + Iterator pointIterator = pts.iterator(); + int count = 0; + for (; pointIterator.hasNext(); count++) { + pointIterator.next(); + } + assertEquals("Unexpected number of edge points", totalPorts - numInfraPorts, count); + //Testing device removal + events.clear(); + eventsToAdd.clear(); + eventsToAdd.add(new DeviceEvent(DEVICE_REMOVED, referenceDevice, + new DefaultPort(referenceDevice, PortNumber.portNumber(1), true))); + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + assertEquals("There should be five new events from removal of edge points", + totalPorts - numInfraPorts, events.size()); + for (int index = 0; index < events.size(); index++) { + //Assert that the correct port numbers were removed in the correct order + assertEquals("Port removed had unexpected port number.", + events.get(index).subject().port(), + (NetTestTools.connectPoint("a", index + numInfraPorts + 1).port())); + //Assert that the events are of the correct type + assertEquals("Unexpected type of event", events.get(index).type(), EDGE_PORT_REMOVED); + } + events.clear(); + //Rebroadcast event to check that it triggers no new behavior + testTopologyManager.listener.event(event); + assertEquals("Rebroadcast of removal event should not produce additional events", + 0, events.size()); + + //Testing device status change, changed from unavailable to available + events.clear(); + eventsToAdd.clear(); + //Make sure that the devicemanager shows the device as available. + addDevice(referenceDevice, "1", 5); + devices.put(referenceDevice.id(), referenceDevice); + + eventsToAdd.add(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, referenceDevice)); + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + //An earlier setup set half of the reference device ports to infrastructure + assertEquals("An unexpected number of events were generated.", totalPorts - numInfraPorts, events.size()); + for (int i = 0; i < 5; i++) { + assertEquals("The event was not of the right type", events.get(i).type(), EDGE_PORT_ADDED); + } + events.clear(); + testTopologyManager.listener.event(event); + assertEquals("No events should have been generated for a set of existing ports.", 0, events.size()); + + //Test removal when state changes when the device becomes unavailable + + //Ensure that the deviceManager shows the device as unavailable + removeDevice(referenceDevice); + /*This variable copies the behavior of the topology by returning ports attached to an unavailable device + //this behavior is necessary for the following event to execute properly, if these statements are removed + no events will be generated since no ports will be provided in getPorts() to EdgeManager. + */ + alwaysReturnPorts = true; + testTopologyManager.listener.event(event); + alwaysReturnPorts = false; + assertEquals("An unexpected number of events were created.", totalPorts - numInfraPorts, events.size()); + for (int i = 0; i < 5; i++) { + EdgePortEvent edgeEvent = events.get(i); + assertEquals("The event is of an unexpected type.", + EdgePortEvent.Type.EDGE_PORT_REMOVED, edgeEvent.type()); + assertEquals("The event pertains to an unexpected port", PortNumber.portNumber(i + numInfraPorts + 1), + edgeEvent.subject().port()); + } + } + + @Test + public void testInternalCache() { + List<Event> eventsToAdd = Lists.newArrayList(); + int numDevices = 10; + //Number of infrastructure ports per device + int numPorts = 5; + //Total ports per device when requesting all devices + totalPorts = 10; + defaultPopulator(numDevices, numPorts); + for (int i = 0; i < numDevices; i++) { + Device newDevice = NetTestTools.device(Integer.toString(i)); + devices.put(newDevice.id(), newDevice); + eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, newDevice)); + } + TopologyEvent event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + //Check all ports have correct designations + ConnectPoint testPoint; + for (int deviceNum = 0; deviceNum < numDevices; deviceNum++) { + for (int portNum = 1; portNum <= totalPorts; portNum++) { + testPoint = NetTestTools.connectPoint(Integer.toString(deviceNum), portNum); + if (portNum <= numPorts) { + assertFalse("This should not be an edge point", mgr.isEdgePoint(testPoint)); + } else { + assertTrue("This should be an edge point", mgr.isEdgePoint(testPoint)); + } + } + } + int count = 0; + for (ConnectPoint ignored : mgr.getEdgePoints()) { + count++; + } + assertEquals("There are an unexpeceted number of edge points returned.", + (totalPorts - numPorts) * numDevices, count); + for (int deviceNumber = 0; deviceNumber < numDevices; deviceNumber++) { + count = 0; + for (ConnectPoint ignored : mgr.getEdgePoints(NetTestTools.did("1"))) { + count++; + } + assertEquals("This element has an unexpected number of edge points.", (totalPorts - numPorts), count); + } + } + + + @Test + public void testEmit() { + byte[] arr = new byte[10]; + Device referenceDevice; + TopologyEvent event; + List<Event> eventsToAdd = Lists.newArrayList(); + int numDevices = 10; + int numInfraPorts = 5; + totalPorts = 10; + defaultPopulator(numDevices, numInfraPorts); + for (byte byteIndex = 0; byteIndex < arr.length; byteIndex++) { + arr[byteIndex] = byteIndex; + } + for (int i = 0; i < numDevices; i++) { + referenceDevice = NetTestTools.device(Integer.toString(i)); + eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, referenceDevice, + new DefaultPort(referenceDevice, PortNumber.portNumber(1), true))); + } + event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd); + testTopologyManager.listener.event(event); + + mgr.emitPacket(ByteBuffer.wrap(arr), Optional.<TrafficTreatment>empty()); + + assertEquals("There were an unexpected number of emitted packets", + (totalPorts - numInfraPorts) * numDevices, packets.size()); + Iterator<OutboundPacket> packetIter = packets.iterator(); + OutboundPacket packet; + while (packetIter.hasNext()) { + packet = packetIter.next(); + assertEquals("The packet had an incorrect payload.", arr, packet.data().array()); + } + //Start testing emission to a specific device + packets.clear(); + mgr.emitPacket(NetTestTools.did(Integer.toString(1)), ByteBuffer.wrap(arr), Optional.<TrafficTreatment>empty()); + + assertEquals("Unexpected number of outbound packets were emitted.", + totalPorts - numInfraPorts, packets.size()); + packetIter = packets.iterator(); + while (packetIter.hasNext()) { + packet = packetIter.next(); + assertEquals("The packet had an incorrect payload", arr, packet.data().array()); + } + } + + + /** + * @param numDevices the number of devices to populate. + * @param numInfraPorts the number of ports to be set as infrastructure on each device, numbered base 0, ports 0 + * through numInfraPorts - 1 + */ + private void defaultPopulator(int numDevices, int numInfraPorts) { + for (int device = 0; device < numDevices; device++) { + String str = Integer.toString(device); + Device deviceToAdd = NetTestTools.device(str); + devices.put(deviceToAdd.id(), deviceToAdd); + for (int port = 1; port <= numInfraPorts; port++) { + infrastructurePorts.add(NetTestTools.connectPoint(str, port)); + } + } + } + + /** + * Adds the specified device with the specified number of edge ports so long as it is less than the total ports. + * + * @param device The device to be added + * @param deviceName The name given to generate the devices DID + * @param numInfraPorts The number of ports to be added numbered 1 ... numInfraPorts + */ + private void addDevice(Device device, String deviceName, int numInfraPorts) { + if (!devices.keySet().contains(device.id())) { + devices.put(device.id(), device); + for (int i = 1; i <= numInfraPorts && i <= totalPorts; i++) { + infrastructurePorts.add(NetTestTools.connectPoint(deviceName, i)); + } + } + } + + private void removeDevice(Device device) { + devices.remove(device.id()); + } + + private void removeInfraPort(ConnectPoint port) { + infrastructurePorts.remove(port); + } + + private class TestTopologyManager extends TopologyServiceAdapter { + private TopologyListener listener; + private Set<ConnectPoint> infrastructurePorts; + + public TestTopologyManager(Set<ConnectPoint> infrastructurePorts) { + this.infrastructurePorts = infrastructurePorts; + } + + @Override + public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { + return infrastructurePorts.contains(connectPoint); + } + + @Override + public void addListener(TopologyListener listener) { + this.listener = listener; + } + + @Override + public void removeListener(TopologyListener listener) { + this.listener = null; + } + } + + private class TestDeviceManager extends DeviceServiceAdapter { + + private Map<DeviceId, Device> devices; + + public TestDeviceManager(Map<DeviceId, Device> devices) { + this.devices = devices; + } + + @Override + public boolean isAvailable(DeviceId deviceId) { + for (DeviceId id : devices.keySet()) { + if (id.equals(deviceId)) { + return true; + } + } + return false; + } + + @Override + public List<Port> getPorts(DeviceId deviceId) { + List<Port> ports = new ArrayList<>(); + Device device = devices.get(deviceId); + if (device == null && !alwaysReturnPorts) { + return ports; + } + for (int portNum = 1; portNum <= totalPorts; portNum++) { + //String is generated using 'of:' + the passed name, this creates a + ports.add(new DefaultPort(device, PortNumber.portNumber(portNum), true)); + } + return ports; + } + + @Override + public Iterable<Device> getAvailableDevices() { + return devices.values(); + } + } + + private class TestPacketManager extends PacketServiceAdapter { + @Override + public void emit(OutboundPacket packet) { + packets.add(packet); + } + } + + private class TestListener implements EdgePortListener { + private List<EdgePortEvent> events; + + public TestListener(List<EdgePortEvent> events) { + this.events = events; + } + + @Override + public void event(EdgePortEvent event) { + events.add(event); + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java new file mode 100644 index 00000000..7ef8762c --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java @@ -0,0 +1,640 @@ +/* + * Copyright 2014-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.flow.impl; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.core.IdGenerator; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.Device; +import org.onosproject.net.Device.Type; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.flow.CompletedBatchOperation; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowEntry.FlowEntryState; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleBatchOperation; +import org.onosproject.net.flow.FlowRuleEvent; +import org.onosproject.net.flow.FlowRuleListener; +import org.onosproject.net.flow.FlowRuleProvider; +import org.onosproject.net.flow.FlowRuleProviderRegistry; +import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.StoredFlowEntry; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.trivial.SimpleFlowRuleStore; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADDED; +import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED; +import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED; +import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED; +import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_UPDATED; + +/** + * Test codifying the flow rule service & flow rule provider service contracts. + */ +public class FlowRuleManagerTest { + + + private static final ProviderId PID = new ProviderId("of", "foo"); + private static final DeviceId DID = DeviceId.deviceId("of:001"); + private static final int TIMEOUT = 10; + private static final Device DEV = new DefaultDevice( + PID, DID, Type.SWITCH, "", "", "", "", null); + + private FlowRuleManager mgr; + + protected FlowRuleService service; + protected FlowRuleProviderRegistry registry; + protected FlowRuleProviderService providerService; + protected TestProvider provider; + protected TestListener listener = new TestListener(); + private ApplicationId appId; + + + @Before + public void setUp() { + mgr = new FlowRuleManager(); + mgr.store = new SimpleFlowRuleStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + mgr.deviceService = new TestDeviceService(); + mgr.coreService = new TestCoreService(); + mgr.operationsService = MoreExecutors.newDirectExecutorService(); + mgr.deviceInstallers = MoreExecutors.newDirectExecutorService(); + mgr.cfgService = new ComponentConfigAdapter(); + service = mgr; + registry = mgr; + + mgr.activate(null); + mgr.addListener(listener); + provider = new TestProvider(PID); + providerService = registry.register(provider); + appId = new TestApplicationId(0, "FlowRuleManagerTest"); + assertTrue("provider should be registered", + registry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + registry.unregister(provider); + assertFalse("provider should not be registered", + registry.getProviders().contains(provider.id())); + service.removeListener(listener); + mgr.deactivate(); + injectEventDispatcher(mgr, null); + mgr.deviceService = null; + } + + private FlowRule flowRule(int tsval, int trval) { + TestSelector ts = new TestSelector(tsval); + TestTreatment tr = new TestTreatment(trval); + return DefaultFlowRule.builder() + .forDevice(DID) + .withSelector(ts) + .withTreatment(tr) + .withPriority(10) + .fromApp(appId) + .makeTemporary(TIMEOUT) + .build(); + } + + + private FlowRule addFlowRule(int hval) { + FlowRule rule = flowRule(hval, hval); + service.applyFlowRules(rule); + + assertNotNull("rule should be found", service.getFlowEntries(DID)); + return rule; + } + + private void validateEvents(FlowRuleEvent.Type... events) { + if (events == null) { + assertTrue("events generated", listener.events.isEmpty()); + } + + int i = 0; + System.err.println("events :" + listener.events); + for (FlowRuleEvent e : listener.events) { + assertEquals("unexpected event", events[i], e.type()); + i++; + } + + assertEquals("mispredicted number of events", + events.length, listener.events.size()); + + listener.events.clear(); + } + + private int flowCount() { + return Sets.newHashSet(service.getFlowEntries(DID)).size(); + } + + @Test + public void getFlowEntries() { + assertTrue("store should be empty", + Sets.newHashSet(service.getFlowEntries(DID)).isEmpty()); + FlowRule f1 = addFlowRule(1); + FlowRule f2 = addFlowRule(2); + + FlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + assertEquals("2 rules should exist", 2, flowCount()); + + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, + RULE_ADDED, RULE_ADDED); + + addFlowRule(1); + System.err.println("events :" + listener.events); + assertEquals("should still be 2 rules", 2, flowCount()); + + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1)); + validateEvents(RULE_UPDATED); + } + + private boolean validateState(Map<FlowRule, FlowEntryState> expected) { + Map<FlowRule, FlowEntryState> expectedToCheck = new HashMap<>(expected); + Iterable<FlowEntry> rules = service.getFlowEntries(DID); + for (FlowEntry f : rules) { + assertTrue("Unexpected FlowRule " + f, expectedToCheck.containsKey(f)); + assertEquals("FlowEntry" + f, expectedToCheck.get(f), f.state()); + expectedToCheck.remove(f); + } + assertEquals(Collections.emptySet(), expectedToCheck.entrySet()); + return true; + } + + @Test + public void applyFlowRules() { + + FlowRule r1 = flowRule(1, 1); + FlowRule r2 = flowRule(2, 2); + FlowRule r3 = flowRule(3, 3); + + assertTrue("store should be empty", + Sets.newHashSet(service.getFlowEntries(DID)).isEmpty()); + mgr.applyFlowRules(r1, r2, r3); + assertEquals("3 rules should exist", 3, flowCount()); + assertTrue("Entries should be pending add.", + validateState(ImmutableMap.of( + r1, FlowEntryState.PENDING_ADD, + r2, FlowEntryState.PENDING_ADD, + r3, FlowEntryState.PENDING_ADD))); + } + + @Test + public void removeFlowRules() { + FlowRule f1 = addFlowRule(1); + FlowRule f2 = addFlowRule(2); + FlowRule f3 = addFlowRule(3); + assertEquals("3 rules should exist", 3, flowCount()); + + FlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + FlowEntry fe3 = new DefaultFlowEntry(f3); + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, + RULE_ADDED, RULE_ADDED, RULE_ADDED); + + mgr.removeFlowRules(f1, f2); + //removing from north, so no events generated + validateEvents(RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED); + assertEquals("3 rule should exist", 3, flowCount()); + assertTrue("Entries should be pending remove.", + validateState(ImmutableMap.of( + f1, FlowEntryState.PENDING_REMOVE, + f2, FlowEntryState.PENDING_REMOVE, + f3, FlowEntryState.ADDED))); + + mgr.removeFlowRules(f1); + assertEquals("3 rule should still exist", 3, flowCount()); + } + + @Test + public void flowRemoved() { + + FlowRule f1 = addFlowRule(1); + FlowRule f2 = addFlowRule(2); + StoredFlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + + + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); + service.removeFlowRules(f1); + + fe1.setState(FlowEntryState.REMOVED); + + + + providerService.flowRemoved(fe1); + + + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, + RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED); + + providerService.flowRemoved(fe1); + validateEvents(); + + FlowRule f3 = flowRule(3, 3); + FlowEntry fe3 = new DefaultFlowEntry(f3); + service.applyFlowRules(f3); + + providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); + validateEvents(RULE_ADD_REQUESTED, RULE_ADDED); + + providerService.flowRemoved(fe3); + validateEvents(); + + } + + @Test + public void flowMetrics() { + FlowRule f1 = flowRule(1, 1); + FlowRule f2 = flowRule(2, 2); + FlowRule f3 = flowRule(3, 3); + + mgr.applyFlowRules(f1, f2, f3); + + FlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + + //FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); + //FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); + + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); + + assertTrue("Entries should be added.", + validateState(ImmutableMap.of( + f1, FlowEntryState.ADDED, + f2, FlowEntryState.ADDED, + f3, FlowEntryState.PENDING_ADD))); + + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, + RULE_ADDED, RULE_ADDED); + } + + @Test + public void extraneousFlow() { + FlowRule f1 = flowRule(1, 1); + FlowRule f2 = flowRule(2, 2); + FlowRule f3 = flowRule(3, 3); + mgr.applyFlowRules(f1, f2); + +// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); +// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); +// FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED); + FlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + FlowEntry fe3 = new DefaultFlowEntry(f3); + + + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); + + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, RULE_ADDED); + + } + + /* + * Tests whether a rule that was marked for removal but no flowRemoved was received + * is indeed removed at the next stats update. + */ + @Test + public void flowMissingRemove() { + FlowRule f1 = flowRule(1, 1); + FlowRule f2 = flowRule(2, 2); + FlowRule f3 = flowRule(3, 3); + +// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); +// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); + + FlowEntry fe1 = new DefaultFlowEntry(f1); + FlowEntry fe2 = new DefaultFlowEntry(f2); + mgr.applyFlowRules(f1, f2, f3); + + mgr.removeFlowRules(f3); + + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); + + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, + RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED); + + } + + @Test + public void getByAppId() { + FlowRule f1 = flowRule(1, 1); + FlowRule f2 = flowRule(2, 2); + mgr.applyFlowRules(f1, f2); + + assertTrue("should have two rules", + Lists.newLinkedList(mgr.getFlowRulesById(appId)).size() == 2); + } + + @Test + public void removeByAppId() { + FlowRule f1 = flowRule(1, 1); + FlowRule f2 = flowRule(2, 2); + mgr.applyFlowRules(f1, f2); + + + mgr.removeFlowRulesById(appId); + + //only check that we are in pending remove. Events and actual remove state will + // be set by flowRemoved call. + validateState(ImmutableMap.of( + f1, FlowEntryState.PENDING_REMOVE, + f2, FlowEntryState.PENDING_REMOVE)); + } + + private static class TestListener implements FlowRuleListener { + final List<FlowRuleEvent> events = new ArrayList<>(); + + @Override + public void event(FlowRuleEvent event) { + events.add(event); + } + } + + private static class TestDeviceService extends DeviceServiceAdapter { + + @Override + public int getDeviceCount() { + return 1; + } + + @Override + public Iterable<Device> getDevices() { + return Collections.singletonList(DEV); + } + + @Override + public Device getDevice(DeviceId deviceId) { + return DEV; + } + + @Override + public MastershipRole getRole(DeviceId deviceId) { + return null; + } + + @Override + public List<Port> getPorts(DeviceId deviceId) { + return null; + } + + @Override + public Port getPort(DeviceId deviceId, PortNumber portNumber) { + return null; + } + + @Override + public boolean isAvailable(DeviceId deviceId) { + return false; + } + + @Override + public void addListener(DeviceListener listener) { + } + + @Override + public void removeListener(DeviceListener listener) { + } + + } + + private class TestProvider extends AbstractProvider implements FlowRuleProvider { + + protected TestProvider(ProviderId id) { + super(PID); + } + + @Override + public void applyFlowRule(FlowRule... flowRules) { + } + + @Override + public void removeFlowRule(FlowRule... flowRules) { + } + + @Override + public void removeRulesById(ApplicationId id, FlowRule... flowRules) { + } + + @Override + public void executeBatch(FlowRuleBatchOperation batch) { + // TODO: need to call batchOperationComplete + } + + private class TestInstallationFuture + implements ListenableFuture<CompletedBatchOperation> { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public CompletedBatchOperation get() + throws InterruptedException, ExecutionException { + return new CompletedBatchOperation(true, Collections.<FlowRule>emptySet(), null); + } + + @Override + public CompletedBatchOperation get(long timeout, TimeUnit unit) + throws InterruptedException, + ExecutionException, TimeoutException { + return new CompletedBatchOperation(true, Collections.<FlowRule>emptySet(), null); + } + + @Override + public void addListener(Runnable task, Executor executor) { + if (isDone()) { + executor.execute(task); + } + } + } + + } + + private class TestSelector implements TrafficSelector { + + //for controlling hashcode uniqueness; + private final int testval; + + public TestSelector(int val) { + testval = val; + } + + @Override + public Set<Criterion> criteria() { + return null; + } + + @Override + public Criterion getCriterion( + org.onosproject.net.flow.criteria.Criterion.Type type) { + return null; + } + + @Override + public int hashCode() { + return testval; + } + + @Override + public boolean equals(Object o) { + if (o instanceof TestSelector) { + return this.testval == ((TestSelector) o).testval; + } + return false; + } + + } + + private class TestTreatment implements TrafficTreatment { + + //for controlling hashcode uniqueness; + private final int testval; + + public TestTreatment(int val) { + testval = val; + } + + @Override + public List<Instruction> deferred() { + return null; + } + + @Override + public List<Instruction> immediate() { + return null; + } + + @Override + public List<Instruction> allInstructions() { + return null; + } + + @Override + public Instructions.TableTypeTransition tableTransition() { + return null; + } + + @Override + public boolean clearedDeferred() { + return false; + } + + @Override + public int hashCode() { + return testval; + } + + @Override + public boolean equals(Object o) { + if (o instanceof TestTreatment) { + return this.testval == ((TestTreatment) o).testval; + } + return false; + } + + @Override + public MetadataInstruction writeMetadata() { + return null; + } + + @Override + public Instructions.MeterInstruction metered() { + return null; + } + + } + + public class TestApplicationId extends DefaultApplicationId { + public TestApplicationId(int id, String name) { + super(id, name); + } + } + + private class TestCoreService extends CoreServiceAdapter { + + @Override + public IdGenerator getIdGenerator(String topic) { + return new IdGenerator() { + private AtomicLong counter = new AtomicLong(0); + @Override + public long getNewId() { + return counter.getAndIncrement(); + } + }; + } + } + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java new file mode 100644 index 00000000..1e898d37 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java @@ -0,0 +1,603 @@ +/* + * Copyright 2014-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.flowobjective.impl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Test FlowObjectiveCompositionTree. + */ +@Ignore +public class FlowObjectiveCompositionTreeTest { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Before + public void setUp() {} + + @After + public void tearDown() {} + + /*@Test + public void testParallelComposition() { + FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31+32"); + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.1/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(1)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.2/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(2)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Parallel ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Parallel ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Parallel ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + } + + @Test + public void testSequentialComposition() { + FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31>32"); + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("0.0.0.0/2")) + .matchIPDst(IpPrefix.valueOf("3.0.0.0/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setIpDst(IpAddress.valueOf("2.0.0.1")) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(3) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("3.0.0.0/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setIpDst(IpAddress.valueOf("2.0.0.2")) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.1/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(1)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.2/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(2)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Sequential ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1")) + .matchIPDst(IpPrefix.valueOf("3.0.0.0/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setIpDst(IpAddress.valueOf("2.0.0.3")) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(3) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Sequential ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1")) + .matchIPDst(IpPrefix.valueOf("3.0.0.0/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setIpDst(IpAddress.valueOf("2.0.0.3")) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(3) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Sequential ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + } + + @Test + public void testOverrideComposition() { + FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31/32"); + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPSrc(IpPrefix.valueOf("1.0.0.0/32")) + .matchIPDst(IpPrefix.valueOf("2.0.0.1/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(31, "a")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.1/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(1)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.2/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(2)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + helper(policyTree, forwardingObjective); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(0) + .withSelector(selector) + .withTreatment(treatment) + .add(); + + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Override ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .add(); + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Override ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + + { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchIPDst(IpPrefix.valueOf("2.0.0.3/32")) + .build(); + + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(PortNumber.portNumber(3)) + .build(); + + ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder() + .fromApp(new DefaultApplicationId(32, "b")) + .makePermanent() + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(1) + .withSelector(selector) + .withTreatment(treatment) + .remove(); + helper(policyTree, forwardingObjective); + } + + System.out.println("---------- Override ----------"); + for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) { + System.out.println(forwardingObjectiveToString(fo)); + } + } + + private void helper(FlowObjectiveCompositionTree policyTree, ForwardingObjective forwardingObjective) { + log.info("before composition"); + log.info("\t{}", forwardingObjectiveToString(forwardingObjective)); + List<ForwardingObjective> forwardingObjectives + = policyTree.updateForward(forwardingObjective); + log.info("after composition"); + for (ForwardingObjective fo : forwardingObjectives) { + log.info("\t{}", forwardingObjectiveToString(fo)); + } + }*/ +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java new file mode 100644 index 00000000..ae7cc874 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java @@ -0,0 +1,536 @@ +/* + * 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.group.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.MacAddress; +import org.onlab.packet.MplsLabel; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.impl.DeviceManager; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.group.DefaultGroup; +import org.onosproject.net.group.DefaultGroupBucket; +import org.onosproject.net.group.DefaultGroupDescription; +import org.onosproject.net.group.DefaultGroupKey; +import org.onosproject.net.group.Group; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.onosproject.net.group.GroupEvent; +import org.onosproject.net.group.GroupKey; +import org.onosproject.net.group.GroupListener; +import org.onosproject.net.group.GroupOperation; +import org.onosproject.net.group.GroupOperations; +import org.onosproject.net.group.GroupProvider; +import org.onosproject.net.group.GroupProviderRegistry; +import org.onosproject.net.group.GroupProviderService; +import org.onosproject.net.group.GroupService; +import org.onosproject.net.group.StoredGroupEntry; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.trivial.SimpleGroupStore; + +import com.google.common.collect.Iterables; + +/** + * Test codifying the group service & group provider service contracts. + */ +public class GroupManagerTest { + + private static final ProviderId PID = new ProviderId("of", "groupfoo"); + private static final DeviceId DID = DeviceId.deviceId("of:001"); + + private GroupManager mgr; + private GroupService groupService; + private GroupProviderRegistry providerRegistry; + private TestGroupListener internalListener = new TestGroupListener(); + private GroupListener listener = internalListener; + private TestGroupProvider internalProvider; + private GroupProvider provider; + private GroupProviderService providerService; + private ApplicationId appId; + + @Before + public void setUp() { + mgr = new GroupManager(); + groupService = mgr; + mgr.deviceService = new DeviceManager(); + mgr.store = new SimpleGroupStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + providerRegistry = mgr; + + mgr.activate(); + mgr.addListener(listener); + + internalProvider = new TestGroupProvider(PID); + provider = internalProvider; + providerService = providerRegistry.register(provider); + appId = new DefaultApplicationId(2, "org.groupmanager.test"); + assertTrue("provider should be registered", + providerRegistry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + providerRegistry.unregister(provider); + assertFalse("provider should not be registered", + providerRegistry.getProviders().contains(provider.id())); + mgr.removeListener(listener); + mgr.deactivate(); + injectEventDispatcher(mgr, null); + } + + /** + * Tests group service north bound and south bound interfaces. + * The following operations are tested: + * a)Tests group creation before the device group AUDIT completes + * b)Tests initial device group AUDIT process + * c)Tests deletion process of any extraneous groups + * d)Tests execution of any pending group creation requests + * after the device group AUDIT completes + * e)Tests re-apply process of any missing groups + * f)Tests event notifications after receiving confirmation for + * any operations from data plane + * g)Tests group bucket modifications (additions and deletions) + * h)Tests group deletion + */ + @Test + public void testGroupService() { + // Test Group creation before AUDIT process + testGroupCreationBeforeAudit(); + + // Test initial group audit process + testInitialAuditWithPendingGroupRequests(); + + // Test audit with extraneous and missing groups + testAuditWithExtraneousMissingGroups(); + + // Test audit with confirmed groups + testAuditWithConfirmedGroups(); + + // Test group add bucket operations + testAddBuckets(); + + // Test group remove bucket operations + testRemoveBuckets(); + + // Test group remove operations + testRemoveGroup(); + } + + // Test Group creation before AUDIT process + private void testGroupCreationBeforeAudit() { + PortNumber[] ports1 = {PortNumber.portNumber(31), + PortNumber.portNumber(32)}; + PortNumber[] ports2 = {PortNumber.portNumber(41), + PortNumber.portNumber(42)}; + GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes()); + List<GroupBucket> buckets = new ArrayList<>(); + List<PortNumber> outPorts = new ArrayList<>(); + outPorts.addAll(Arrays.asList(ports1)); + outPorts.addAll(Arrays.asList(ports2)); + for (PortNumber portNumber: outPorts) { + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setOutput(portNumber) + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02")) + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01")) + .pushMpls() + .setMpls(MplsLabel.mplsLabel(106)); + buckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + } + GroupBuckets groupBuckets = new GroupBuckets(buckets); + GroupDescription newGroupDesc = new DefaultGroupDescription(DID, + Group.Type.SELECT, + groupBuckets, + key, + null, + appId); + groupService.addGroup(newGroupDesc); + internalProvider.validate(DID, null); + assertEquals(null, groupService.getGroup(DID, key)); + assertEquals(0, Iterables.size(groupService.getGroups(DID, appId))); + } + + // Test initial AUDIT process with pending group requests + private void testInitialAuditWithPendingGroupRequests() { + PortNumber[] ports1 = {PortNumber.portNumber(31), + PortNumber.portNumber(32)}; + PortNumber[] ports2 = {PortNumber.portNumber(41), + PortNumber.portNumber(42)}; + GroupId gId1 = new DefaultGroupId(1); + Group group1 = createSouthboundGroupEntry(gId1, + Arrays.asList(ports1), + 0); + GroupId gId2 = new DefaultGroupId(2); + // Non zero reference count will make the group manager to queue + // the extraneous groups until reference count is zero. + Group group2 = createSouthboundGroupEntry(gId2, + Arrays.asList(ports2), + 2); + List<Group> groupEntries = Arrays.asList(group1, group2); + providerService.pushGroupMetrics(DID, groupEntries); + // First group metrics would trigger the device audit completion + // post which all pending group requests are also executed. + GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes()); + Group createdGroup = groupService.getGroup(DID, key); + int createdGroupId = createdGroup.id().id(); + assertNotEquals(gId1.id(), createdGroupId); + assertNotEquals(gId2.id(), createdGroupId); + + List<GroupOperation> expectedGroupOps = Arrays.asList( + GroupOperation.createDeleteGroupOperation(gId1, + Group.Type.SELECT), + GroupOperation.createAddGroupOperation( + createdGroup.id(), + Group.Type.SELECT, + createdGroup.buckets())); + internalProvider.validate(DID, expectedGroupOps); + } + + // Test AUDIT process with extraneous groups and missing groups + private void testAuditWithExtraneousMissingGroups() { + PortNumber[] ports1 = {PortNumber.portNumber(31), + PortNumber.portNumber(32)}; + PortNumber[] ports2 = {PortNumber.portNumber(41), + PortNumber.portNumber(42)}; + GroupId gId1 = new DefaultGroupId(1); + Group group1 = createSouthboundGroupEntry(gId1, + Arrays.asList(ports1), + 0); + GroupId gId2 = new DefaultGroupId(2); + Group group2 = createSouthboundGroupEntry(gId2, + Arrays.asList(ports2), + 0); + List<Group> groupEntries = Arrays.asList(group1, group2); + providerService.pushGroupMetrics(DID, groupEntries); + GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes()); + Group createdGroup = groupService.getGroup(DID, key); + List<GroupOperation> expectedGroupOps = Arrays.asList( + GroupOperation.createDeleteGroupOperation(gId1, + Group.Type.SELECT), + GroupOperation.createDeleteGroupOperation(gId2, + Group.Type.SELECT), + GroupOperation.createAddGroupOperation(createdGroup.id(), + Group.Type.SELECT, + createdGroup.buckets())); + internalProvider.validate(DID, expectedGroupOps); + } + + // Test AUDIT with confirmed groups + private void testAuditWithConfirmedGroups() { + GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes()); + Group createdGroup = groupService.getGroup(DID, key); + createdGroup = new DefaultGroup(createdGroup.id(), + DID, + Group.Type.SELECT, + createdGroup.buckets()); + List<Group> groupEntries = Collections.singletonList(createdGroup); + providerService.pushGroupMetrics(DID, groupEntries); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADDED)); + } + + // Test group add bucket operations + private void testAddBuckets() { + GroupKey addKey = new DefaultGroupKey("group1AddBuckets".getBytes()); + + GroupKey prevKey = new DefaultGroupKey("group1BeforeAudit".getBytes()); + Group createdGroup = groupService.getGroup(DID, prevKey); + List<GroupBucket> buckets = new ArrayList<>(); + buckets.addAll(createdGroup.buckets().buckets()); + + PortNumber[] addPorts = {PortNumber.portNumber(51), + PortNumber.portNumber(52)}; + List<PortNumber> outPorts; + outPorts = new ArrayList<PortNumber>(); + outPorts.addAll(Arrays.asList(addPorts)); + List<GroupBucket> addBuckets; + addBuckets = new ArrayList<GroupBucket>(); + for (PortNumber portNumber: outPorts) { + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setOutput(portNumber) + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02")) + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01")) + .pushMpls() + .setMpls(MplsLabel.mplsLabel(106)); + addBuckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + buckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + } + GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets); + groupService.addBucketsToGroup(DID, + prevKey, + groupAddBuckets, + addKey, + appId); + GroupBuckets updatedBuckets = new GroupBuckets(buckets); + List<GroupOperation> expectedGroupOps = Collections.singletonList( + GroupOperation.createModifyGroupOperation(createdGroup.id(), + Group.Type.SELECT, + updatedBuckets)); + internalProvider.validate(DID, expectedGroupOps); + Group existingGroup = groupService.getGroup(DID, addKey); + List<Group> groupEntries = Collections.singletonList(existingGroup); + providerService.pushGroupMetrics(DID, groupEntries); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED)); + } + + // Test group remove bucket operations + private void testRemoveBuckets() { + GroupKey removeKey = new DefaultGroupKey("group1RemoveBuckets".getBytes()); + + GroupKey prevKey = new DefaultGroupKey("group1AddBuckets".getBytes()); + Group createdGroup = groupService.getGroup(DID, prevKey); + List<GroupBucket> buckets = new ArrayList<>(); + buckets.addAll(createdGroup.buckets().buckets()); + + PortNumber[] removePorts = {PortNumber.portNumber(31), + PortNumber.portNumber(32)}; + List<PortNumber> outPorts = new ArrayList<>(); + outPorts.addAll(Arrays.asList(removePorts)); + List<GroupBucket> removeBuckets = new ArrayList<>(); + for (PortNumber portNumber: outPorts) { + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setOutput(portNumber) + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02")) + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01")) + .pushMpls() + .setMpls(MplsLabel.mplsLabel(106)); + removeBuckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + buckets.remove(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + } + GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets); + groupService.removeBucketsFromGroup(DID, + prevKey, + groupRemoveBuckets, + removeKey, + appId); + GroupBuckets updatedBuckets = new GroupBuckets(buckets); + List<GroupOperation> expectedGroupOps = Collections.singletonList( + GroupOperation.createModifyGroupOperation(createdGroup.id(), + Group.Type.SELECT, + updatedBuckets)); + internalProvider.validate(DID, expectedGroupOps); + Group existingGroup = groupService.getGroup(DID, removeKey); + List<Group> groupEntries = Collections.singletonList(existingGroup); + providerService.pushGroupMetrics(DID, groupEntries); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED)); + } + + // Test group remove operations + private void testRemoveGroup() { + GroupKey currKey = new DefaultGroupKey("group1RemoveBuckets".getBytes()); + Group existingGroup = groupService.getGroup(DID, currKey); + groupService.removeGroup(DID, currKey, appId); + List<GroupOperation> expectedGroupOps = Collections.singletonList( + GroupOperation.createDeleteGroupOperation(existingGroup.id(), + Group.Type.SELECT)); + internalProvider.validate(DID, expectedGroupOps); + List<Group> groupEntries = Collections.emptyList(); + providerService.pushGroupMetrics(DID, groupEntries); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVED)); + } + + /** + * Test GroupOperationFailure function in Group Manager. + * a)GroupAddFailure + * b)GroupUpdateFailure + * c)GroupRemoteFailure + */ + @Test + public void testGroupOperationFailure() { + PortNumber[] ports1 = {PortNumber.portNumber(31), + PortNumber.portNumber(32)}; + PortNumber[] ports2 = {PortNumber.portNumber(41), + PortNumber.portNumber(42)}; + // Test Group creation before AUDIT process + GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes()); + List<GroupBucket> buckets = new ArrayList<>(); + List<PortNumber> outPorts = new ArrayList<>(); + outPorts.addAll(Arrays.asList(ports1)); + outPorts.addAll(Arrays.asList(ports2)); + for (PortNumber portNumber: outPorts) { + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setOutput(portNumber) + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02")) + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01")) + .pushMpls() + .setMpls(MplsLabel.mplsLabel(106)); + buckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + } + GroupBuckets groupBuckets = new GroupBuckets(buckets); + GroupDescription newGroupDesc = new DefaultGroupDescription(DID, + Group.Type.SELECT, + groupBuckets, + key, + null, + appId); + groupService.addGroup(newGroupDesc); + + // Test initial group audit process + GroupId gId1 = new DefaultGroupId(1); + Group group1 = createSouthboundGroupEntry(gId1, + Arrays.asList(ports1), + 0); + GroupId gId2 = new DefaultGroupId(2); + // Non zero reference count will make the group manager to queue + // the extraneous groups until reference count is zero. + Group group2 = createSouthboundGroupEntry(gId2, + Arrays.asList(ports2), + 2); + List<Group> groupEntries = Arrays.asList(group1, group2); + providerService.pushGroupMetrics(DID, groupEntries); + Group createdGroup = groupService.getGroup(DID, key); + + // Group Add failure test + GroupOperation groupAddOp = GroupOperation. + createAddGroupOperation(createdGroup.id(), + createdGroup.type(), + createdGroup.buckets()); + providerService.groupOperationFailed(DID, groupAddOp); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADD_FAILED)); + + // Group Mod failure test + groupService.addGroup(newGroupDesc); + createdGroup = groupService.getGroup(DID, key); + assertNotNull(createdGroup); + + GroupOperation groupModOp = GroupOperation. + createModifyGroupOperation(createdGroup.id(), + createdGroup.type(), + createdGroup.buckets()); + providerService.groupOperationFailed(DID, groupModOp); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATE_FAILED)); + + // Group Delete failure test + groupService.addGroup(newGroupDesc); + createdGroup = groupService.getGroup(DID, key); + assertNotNull(createdGroup); + + GroupOperation groupDelOp = GroupOperation. + createDeleteGroupOperation(createdGroup.id(), + createdGroup.type()); + providerService.groupOperationFailed(DID, groupDelOp); + internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVE_FAILED)); + + } + + private Group createSouthboundGroupEntry(GroupId gId, + List<PortNumber> ports, + long referenceCount) { + List<PortNumber> outPorts = new ArrayList<>(); + outPorts.addAll(ports); + + List<GroupBucket> buckets = new ArrayList<>(); + for (PortNumber portNumber: outPorts) { + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setOutput(portNumber) + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02")) + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01")) + .pushMpls() + .setMpls(MplsLabel.mplsLabel(106)); + buckets.add(DefaultGroupBucket.createSelectGroupBucket( + tBuilder.build())); + } + GroupBuckets groupBuckets = new GroupBuckets(buckets); + StoredGroupEntry group = new DefaultGroup( + gId, DID, Group.Type.SELECT, groupBuckets); + group.setReferenceCount(referenceCount); + return group; + } + + private static class TestGroupListener implements GroupListener { + final List<GroupEvent> events = new ArrayList<>(); + + @Override + public void event(GroupEvent event) { + events.add(event); + } + + public void validateEvent(List<GroupEvent.Type> expectedEvents) { + int i = 0; + System.err.println("events :" + events); + for (GroupEvent e : events) { + assertEquals("unexpected event", expectedEvents.get(i), e.type()); + i++; + } + assertEquals("mispredicted number of events", + expectedEvents.size(), events.size()); + events.clear(); + } + } + + private class TestGroupProvider + extends AbstractProvider implements GroupProvider { + DeviceId lastDeviceId; + List<GroupOperation> groupOperations = new ArrayList<>(); + + protected TestGroupProvider(ProviderId id) { + super(id); + } + + @Override + public void performGroupOperation(DeviceId deviceId, + GroupOperations groupOps) { + lastDeviceId = deviceId; + groupOperations.addAll(groupOps.operations()); + } + + public void validate(DeviceId expectedDeviceId, + List<GroupOperation> expectedGroupOps) { + if (expectedGroupOps == null) { + assertTrue("events generated", groupOperations.isEmpty()); + return; + } + + assertEquals(lastDeviceId, expectedDeviceId); + assertTrue((this.groupOperations.containsAll(expectedGroupOps) && + expectedGroupOps.containsAll(groupOperations))); + + groupOperations.clear(); + lastDeviceId = null; + } + + } + +} + + diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/BasicHostOperatorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/BasicHostOperatorTest.java new file mode 100644 index 00000000..e7f14b5d --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/BasicHostOperatorTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2014-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.host.impl; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; +import org.onosproject.net.config.basics.BasicHostConfig; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.DeviceId; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.PortNumber; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostDescription; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +public class BasicHostOperatorTest { + private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01"); + private static final VlanId VLAN = VlanId.vlanId((short) 10); + private static final IpAddress IP = IpAddress.valueOf("10.0.0.1"); + + private static final HostId ID = HostId.hostId(MAC); + private static final HostLocation LOC = new HostLocation( + DeviceId.deviceId("of:foo"), + PortNumber.portNumber(100), + 123L + ); + private static final HostDescription HOST = new DefaultHostDescription(MAC, VLAN, LOC, IP); + + private final ConfigApplyDelegate delegate = new ConfigApplyDelegate() { + @Override + public void onApply(Config config) { + } + }; + private final ObjectMapper mapper = new ObjectMapper(); + + private static final BasicHostConfig BHC = new BasicHostConfig(); + private static final String NAME = "testhost"; + private static final double LAT = 40.96; + + @Before + public void setUp() { + BHC.init(ID, "test", JsonNodeFactory.instance.objectNode(), mapper, delegate); + BHC.name(NAME).latitude(40.96); + } + + @Test + public void testDescOps() { + HostDescription desc = BasicHostOperator.combine(BHC, HOST); + assertEquals(NAME, desc.annotations().value(AnnotationKeys.NAME)); + assertEquals(null, desc.annotations().value(AnnotationKeys.LONGITUDE)); + assertEquals(String.valueOf(LAT), desc.annotations().value(AnnotationKeys.LATITUDE)); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java new file mode 100644 index 00000000..dbb807f8 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java @@ -0,0 +1,529 @@ +/* + * Copyright 2014-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.host.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; +import static org.onosproject.net.host.HostEvent.Type.HOST_MOVED; +import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; +import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED; + +import java.util.List; +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.event.Event; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.config.NetworkConfigServiceAdapter; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.PortNumber; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostDescription; +import org.onosproject.net.host.HostEvent; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.HostProviderRegistry; +import org.onosproject.net.host.HostProviderService; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.host.PortAddresses; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.trivial.SimpleHostStore; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +/** + * Test codifying the host service & host provider service contracts. + */ +public class HostManagerTest { + + private static final ProviderId PID = new ProviderId("of", "foo"); + + private static final VlanId VLAN1 = VlanId.vlanId((short) 1); + private static final VlanId VLAN2 = VlanId.vlanId((short) 2); + private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01"); + private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final MacAddress MAC3 = MacAddress.valueOf("00:00:33:00:00:03"); + private static final MacAddress MAC4 = MacAddress.valueOf("00:00:44:00:00:04"); + private static final HostId HID1 = HostId.hostId(MAC1, VLAN1); + private static final HostId HID2 = HostId.hostId(MAC2, VLAN1); + private static final HostId HID3 = HostId.hostId(MAC3, VLAN1); + private static final HostId HID4 = HostId.hostId(MAC4, VLAN1); + + private static final IpAddress IP1 = IpAddress.valueOf("10.0.0.1"); + private static final IpAddress IP2 = IpAddress.valueOf("10.0.0.2"); + private static final IpAddress IP3 = IpAddress.valueOf("2001::1"); + private static final IpAddress IP4 = IpAddress.valueOf("2001::2"); + + private static final DeviceId DID1 = DeviceId.deviceId("of:001"); + private static final DeviceId DID2 = DeviceId.deviceId("of:002"); + private static final PortNumber P1 = PortNumber.portNumber(100); + private static final PortNumber P2 = PortNumber.portNumber(200); + private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L); + private static final HostLocation LOC2 = new HostLocation(DID1, P2, 123L); + private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1); + private static final ConnectPoint CP2 = new ConnectPoint(DID2, P2); + + private static final InterfaceIpAddress IA1 = + new InterfaceIpAddress(IpAddress.valueOf("10.1.1.1"), + IpPrefix.valueOf("10.1.1.0/24")); + private static final InterfaceIpAddress IA2 = + new InterfaceIpAddress(IpAddress.valueOf("10.2.2.2"), + IpPrefix.valueOf("10.2.0.0/16")); + private static final InterfaceIpAddress IA3 = + new InterfaceIpAddress(IpAddress.valueOf("10.3.3.3"), + IpPrefix.valueOf("10.3.3.0/24")); + private static final InterfaceIpAddress IA4 = + new InterfaceIpAddress(IpAddress.valueOf("2001:100::1"), + IpPrefix.valueOf("2001:100::/56")); + private static final InterfaceIpAddress IA5 = + new InterfaceIpAddress(IpAddress.valueOf("2001:200::1"), + IpPrefix.valueOf("2001:200::/48")); + private static final InterfaceIpAddress IA6 = + new InterfaceIpAddress(IpAddress.valueOf("2001:300::1"), + IpPrefix.valueOf("2001:300::/56")); + + private HostManager mgr; + + protected TestListener listener = new TestListener(); + protected HostProviderRegistry registry; + protected TestHostProvider provider; + protected HostProviderService providerService; + + @Before + public void setUp() { + mgr = new HostManager(); + mgr.store = new SimpleHostStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + registry = mgr; + mgr.networkConfigService = new TestNetworkConfigService(); + mgr.activate(); + + mgr.addListener(listener); + + provider = new TestHostProvider(); + providerService = registry.register(provider); + assertTrue("provider should be registered", + registry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + registry.unregister(provider); + assertFalse("provider should not be registered", + registry.getProviders().contains(provider.id())); + + mgr.removeListener(listener); + mgr.deactivate(); + injectEventDispatcher(mgr, null); + } + + private void detect(HostId hid, MacAddress mac, VlanId vlan, + HostLocation loc, IpAddress ip) { + HostDescription descr = new DefaultHostDescription(mac, vlan, loc, ip); + providerService.hostDetected(hid, descr); + assertNotNull("host should be found", mgr.getHost(hid)); + } + + private void validateEvents(Enum... types) { + int i = 0; + assertEquals("wrong events received", types.length, listener.events.size()); + for (Event event : listener.events) { + assertEquals("incorrect event type", types[i], event.type()); + i++; + } + listener.events.clear(); + } + + @Test + public void hostDetected() { + assertNull("host shouldn't be found", mgr.getHost(HID1)); + + // host addition + detect(HID1, MAC1, VLAN1, LOC1, IP1); + assertEquals("exactly one should be found", 1, mgr.getHostCount()); + detect(HID2, MAC2, VLAN2, LOC2, IP1); + assertEquals("two hosts should be found", 2, mgr.getHostCount()); + validateEvents(HOST_ADDED, HOST_ADDED); + + // host motion + detect(HID1, MAC1, VLAN1, LOC2, IP1); + validateEvents(HOST_MOVED); + assertEquals("only two hosts should be found", 2, mgr.getHostCount()); + + // host update + detect(HID1, MAC1, VLAN1, LOC2, IP2); + validateEvents(HOST_UPDATED); + assertEquals("only two hosts should be found", 2, mgr.getHostCount()); + } + + @Test + public void hostDetectedIPv6() { + assertNull("host shouldn't be found", mgr.getHost(HID3)); + + // host addition + detect(HID3, MAC3, VLAN1, LOC1, IP3); + assertEquals("exactly one should be found", 1, mgr.getHostCount()); + detect(HID4, MAC4, VLAN2, LOC2, IP3); + assertEquals("two hosts should be found", 2, mgr.getHostCount()); + validateEvents(HOST_ADDED, HOST_ADDED); + + // host motion + detect(HID3, MAC3, VLAN1, LOC2, IP3); + validateEvents(HOST_MOVED); + assertEquals("only two hosts should be found", 2, mgr.getHostCount()); + + // host update + detect(HID3, MAC3, VLAN1, LOC2, IP4); + validateEvents(HOST_UPDATED); + assertEquals("only two hosts should be found", 2, mgr.getHostCount()); + } + + @Test + public void hostVanished() { + detect(HID1, MAC1, VLAN1, LOC1, IP1); + providerService.hostVanished(HID1); + validateEvents(HOST_ADDED, HOST_REMOVED); + + assertNull("host should have been removed", mgr.getHost(HID1)); + } + + @Test + public void hostVanishedIPv6() { + detect(HID3, MAC3, VLAN1, LOC1, IP3); + providerService.hostVanished(HID3); + validateEvents(HOST_ADDED, HOST_REMOVED); + + assertNull("host should have been removed", mgr.getHost(HID3)); + } + + private void validateHosts( + String msg, Iterable<Host> hosts, HostId... ids) { + Set<HostId> hids = Sets.newHashSet(ids); + for (Host h : hosts) { + assertTrue(msg, hids.remove(h.id())); + } + assertTrue("expected hosts not fetched from store", hids.isEmpty()); + } + + @Test + public void getHosts() { + detect(HID1, MAC1, VLAN1, LOC1, IP1); + detect(HID2, MAC2, VLAN1, LOC2, IP2); + + validateHosts("host not properly stored", mgr.getHosts(), HID1, HID2); + validateHosts("can't get hosts by VLAN", mgr.getHostsByVlan(VLAN1), HID1, HID2); + validateHosts("can't get hosts by MAC", mgr.getHostsByMac(MAC1), HID1); + validateHosts("can't get hosts by IP", mgr.getHostsByIp(IP1), HID1); + validateHosts("can't get hosts by location", mgr.getConnectedHosts(LOC1), HID1); + assertTrue("incorrect host location", mgr.getConnectedHosts(DID2).isEmpty()); + } + + @Test + public void getHostsIPv6() { + detect(HID3, MAC3, VLAN1, LOC1, IP3); + detect(HID4, MAC4, VLAN1, LOC2, IP4); + + validateHosts("host not properly stored", mgr.getHosts(), HID3, HID4); + validateHosts("can't get hosts by VLAN", mgr.getHostsByVlan(VLAN1), HID3, HID4); + validateHosts("can't get hosts by MAC", mgr.getHostsByMac(MAC3), HID3); + validateHosts("can't get hosts by IP", mgr.getHostsByIp(IP3), HID3); + validateHosts("can't get hosts by location", mgr.getConnectedHosts(LOC1), HID3); + assertTrue("incorrect host location", mgr.getConnectedHosts(DID2).isEmpty()); + } + + private static class TestHostProvider extends AbstractProvider + implements HostProvider { + + protected TestHostProvider() { + super(PID); + } + + @Override + public ProviderId id() { + return PID; + } + + @Override + public void triggerProbe(Host host) { + } + + } + + private static class TestListener implements HostListener { + + protected List<HostEvent> events = Lists.newArrayList(); + + @Override + public void event(HostEvent event) { + events.add(event); + } + + } + + @Test + public void bindAddressesToPort() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + // Add some more addresses and check that they're added correctly + PortAddresses add2 = + new PortAddresses(CP1, Sets.newHashSet(IA3), null, + VlanId.vlanId((short) 2)); + + mgr.bindAddressesToPort(add2); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(2, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + assertTrue(storedAddresses.contains(add2)); + + PortAddresses add3 = new PortAddresses(CP1, null, MAC2, VlanId.NONE); + + mgr.bindAddressesToPort(add3); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(3, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + assertTrue(storedAddresses.contains(add2)); + assertTrue(storedAddresses.contains(add3)); + } + + @Test + public void bindAddressesToPortIPv6() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA4, IA5), MAC3, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + // Add some more addresses and check that they're added correctly + PortAddresses add2 = + new PortAddresses(CP1, Sets.newHashSet(IA6), null, + VlanId.vlanId((short) 2)); + + mgr.bindAddressesToPort(add2); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(2, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + assertTrue(storedAddresses.contains(add2)); + + PortAddresses add3 = new PortAddresses(CP1, null, MAC4, VlanId.NONE); + + mgr.bindAddressesToPort(add3); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(3, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + assertTrue(storedAddresses.contains(add2)); + assertTrue(storedAddresses.contains(add3)); + } + + @Test + public void unbindAddressesFromPort() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + PortAddresses rem1 = + new PortAddresses(CP1, Sets.newHashSet(IA1), null, VlanId.NONE); + + mgr.unbindAddressesFromPort(rem1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + // It shouldn't have been removed because it didn't match the originally + // submitted address object + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + mgr.unbindAddressesFromPort(add1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertTrue(storedAddresses.isEmpty()); + } + + @Test + public void unbindAddressesFromPortIPv6() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA4, IA5), MAC3, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + PortAddresses rem1 = + new PortAddresses(CP1, Sets.newHashSet(IA4), null, VlanId.NONE); + + mgr.unbindAddressesFromPort(rem1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + // It shouldn't have been removed because it didn't match the originally + // submitted address object + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + mgr.unbindAddressesFromPort(add1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertTrue(storedAddresses.isEmpty()); + } + + @Test + public void clearAddresses() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + mgr.clearAddresses(CP1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertTrue(storedAddresses.isEmpty()); + } + + @Test + public void clearAddressesIPv6() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA4, IA5), MAC3, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + + mgr.clearAddresses(CP1); + storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertTrue(storedAddresses.isEmpty()); + } + + @Test + public void getAddressBindingsForPort() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + } + + @Test + public void getAddressBindingsForPortIPv6() { + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA4, IA5), MAC3, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1); + + assertEquals(1, storedAddresses.size()); + assertTrue(storedAddresses.contains(add1)); + } + + @Test + public void getAddressBindings() { + Set<PortAddresses> storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.isEmpty()); + + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + + storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.size() == 1); + + PortAddresses add2 = + new PortAddresses(CP2, Sets.newHashSet(IA3), MAC2, VlanId.NONE); + + mgr.bindAddressesToPort(add2); + + storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.size() == 2); + assertTrue(storedAddresses.equals(Sets.newHashSet(add1, add2))); + } + + @Test + public void getAddressBindingsIPv6() { + Set<PortAddresses> storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.isEmpty()); + + PortAddresses add1 = + new PortAddresses(CP1, Sets.newHashSet(IA4, IA5), MAC3, VlanId.NONE); + + mgr.bindAddressesToPort(add1); + + storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.size() == 1); + + PortAddresses add2 = + new PortAddresses(CP2, Sets.newHashSet(IA5), MAC4, VlanId.NONE); + + mgr.bindAddressesToPort(add2); + + storedAddresses = mgr.getAddressBindings(); + + assertTrue(storedAddresses.size() == 2); + assertTrue(storedAddresses.equals(Sets.newHashSet(add1, add2))); + } + + private class TestNetworkConfigService extends NetworkConfigServiceAdapter { + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java new file mode 100644 index 00000000..d6ff473a --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java @@ -0,0 +1,320 @@ +/* + * Copyright 2014-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.host.impl; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import org.junit.After; +import org.junit.Test; +import org.onlab.packet.ARP; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.host.PortAddresses; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.ProviderId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class HostMonitorTest { + + private static final IpAddress TARGET_IP_ADDR = + IpAddress.valueOf("10.0.0.1"); + private static final IpAddress SOURCE_ADDR = + IpAddress.valueOf("10.0.0.99"); + private static final InterfaceIpAddress IA1 = + new InterfaceIpAddress(SOURCE_ADDR, IpPrefix.valueOf("10.0.0.0/24")); + private MacAddress sourceMac = MacAddress.valueOf(1L); + + private HostMonitor hostMonitor; + + @After + public void shutdown() { + hostMonitor.shutdown(); + } + + @Test + public void testMonitorHostExists() throws Exception { + ProviderId id = new ProviderId("fake://", "id"); + + Host host = createMock(Host.class); + expect(host.providerId()).andReturn(id); + replay(host); + + HostManager hostManager = createMock(HostManager.class); + expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + .andReturn(Collections.singleton(host)); + replay(hostManager); + + HostProvider hostProvider = createMock(HostProvider.class); + expect(hostProvider.id()).andReturn(id).anyTimes(); + hostProvider.triggerProbe(host); + expectLastCall().once(); + replay(hostProvider); + + hostMonitor = new HostMonitor(null, hostManager, null); + + hostMonitor.registerHostProvider(hostProvider); + hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + + hostMonitor.run(null); + + verify(hostProvider); + } + + @Test + public void testMonitorHostDoesNotExist() throws Exception { + + HostManager hostManager = createMock(HostManager.class); + + DeviceId devId = DeviceId.deviceId("fake"); + + Device device = createMock(Device.class); + expect(device.id()).andReturn(devId).anyTimes(); + replay(device); + + PortNumber portNum = PortNumber.portNumber(1L); + + Port port = createMock(Port.class); + expect(port.number()).andReturn(portNum).anyTimes(); + replay(port); + + TestDeviceService deviceService = new TestDeviceService(); + deviceService.addDevice(device, Collections.singleton(port)); + + ConnectPoint cp = new ConnectPoint(devId, portNum); + PortAddresses pa = + new PortAddresses(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE); + + expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + .andReturn(Collections.emptySet()).anyTimes(); + replay(hostManager); + + InterfaceService interfaceService = createMock(InterfaceService.class); + expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR)) + .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE)) + .anyTimes(); + replay(interfaceService); + + TestPacketService packetService = new TestPacketService(); + + + // Run the test + hostMonitor = new HostMonitor(packetService, hostManager, interfaceService); + + hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + hostMonitor.run(null); + + + // Check that a packet was sent to our PacketService and that it has + // the properties we expect + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + // Check the output port is correct + assertEquals(1, packet.treatment().immediate().size()); + Instruction instruction = packet.treatment().immediate().get(0); + assertTrue(instruction instanceof OutputInstruction); + OutputInstruction oi = (OutputInstruction) instruction; + assertEquals(portNum, oi.port()); + + // Check the output packet is correct (well the important bits anyway) + final byte[] pktData = new byte[packet.data().remaining()]; + packet.data().get(pktData); + Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); + assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID()); + ARP arp = (ARP) eth.getPayload(); + assertArrayEquals(SOURCE_ADDR.toOctets(), + arp.getSenderProtocolAddress()); + assertArrayEquals(sourceMac.toBytes(), + arp.getSenderHardwareAddress()); + assertArrayEquals(TARGET_IP_ADDR.toOctets(), + arp.getTargetProtocolAddress()); + } + + @Test + public void testMonitorHostDoesNotExistWithVlan() throws Exception { + + HostManager hostManager = createMock(HostManager.class); + + DeviceId devId = DeviceId.deviceId("fake"); + short vlan = 5; + + Device device = createMock(Device.class); + expect(device.id()).andReturn(devId).anyTimes(); + replay(device); + + PortNumber portNum = PortNumber.portNumber(1L); + + Port port = createMock(Port.class); + expect(port.number()).andReturn(portNum).anyTimes(); + replay(port); + + TestDeviceService deviceService = new TestDeviceService(); + deviceService.addDevice(device, Collections.singleton(port)); + + ConnectPoint cp = new ConnectPoint(devId, portNum); + PortAddresses pa = + new PortAddresses(cp, Collections.singleton(IA1), sourceMac, + VlanId.vlanId(vlan)); + + expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + .andReturn(Collections.emptySet()).anyTimes(); + replay(hostManager); + + InterfaceService interfaceService = createMock(InterfaceService.class); + expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR)) + .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.vlanId(vlan))) + .anyTimes(); + replay(interfaceService); + + TestPacketService packetService = new TestPacketService(); + + + // Run the test + hostMonitor = new HostMonitor(packetService, hostManager, interfaceService); + + hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + hostMonitor.run(null); + + + // Check that a packet was sent to our PacketService and that it has + // the properties we expect + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + // Check the output port is correct + assertEquals(1, packet.treatment().immediate().size()); + Instruction instruction = packet.treatment().immediate().get(0); + assertTrue(instruction instanceof OutputInstruction); + OutputInstruction oi = (OutputInstruction) instruction; + assertEquals(portNum, oi.port()); + + // Check the output packet is correct (well the important bits anyway) + final byte[] pktData = new byte[packet.data().remaining()]; + packet.data().get(pktData); + Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); + assertEquals(vlan, eth.getVlanID()); + ARP arp = (ARP) eth.getPayload(); + assertArrayEquals(SOURCE_ADDR.toOctets(), + arp.getSenderProtocolAddress()); + assertArrayEquals(sourceMac.toBytes(), + arp.getSenderHardwareAddress()); + assertArrayEquals(TARGET_IP_ADDR.toOctets(), + arp.getTargetProtocolAddress()); + } + + class TestPacketService extends PacketServiceAdapter { + + List<OutboundPacket> packets = new ArrayList<>(); + + @Override + public void emit(OutboundPacket packet) { + packets.add(packet); + } + } + + class TestDeviceService extends DeviceServiceAdapter { + + List<Device> devices = Lists.newArrayList(); + Multimap<DeviceId, Port> devicePorts = HashMultimap.create(); + + void addDevice(Device device, Set<Port> ports) { + devices.add(device); + for (Port p : ports) { + devicePorts.put(device.id(), p); + } + } + + @Override + public int getDeviceCount() { + return 0; + } + + @Override + public Iterable<Device> getDevices() { + return devices; + } + + @Override + public Device getDevice(DeviceId deviceId) { + return null; + } + + @Override + public MastershipRole getRole(DeviceId deviceId) { + return null; + } + + @Override + public List<Port> getPorts(DeviceId deviceId) { + List<Port> ports = Lists.newArrayList(); + for (Port p : devicePorts.get(deviceId)) { + ports.add(p); + } + return ports; + } + + @Override + public Port getPort(DeviceId deviceId, PortNumber portNumber) { + return null; + } + + @Override + public boolean isAvailable(DeviceId deviceId) { + return false; + } + + @Override + public void addListener(DeviceListener listener) { + } + + @Override + public void removeListener(DeviceListener listener) { + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/LinksHaveEntryWithSourceDestinationPairMatcher.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/LinksHaveEntryWithSourceDestinationPairMatcher.java new file mode 100644 index 00000000..d34143c9 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/LinksHaveEntryWithSourceDestinationPairMatcher.java @@ -0,0 +1,97 @@ +/* + * Copyright 2014 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; + +import java.util.Collection; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.onosproject.net.EdgeLink; +import org.onosproject.net.Link; + +/** + * Matcher to determine if a Collection of Links contains a path between a source + * and a destination. + */ +public class LinksHaveEntryWithSourceDestinationPairMatcher extends + TypeSafeMatcher<Collection<Link>> { + private final String source; + private final String destination; + + /** + * Creates a matcher for a given path represented by a source and + * a destination. + * + * @param source string identifier for the source of the path + * @param destination string identifier for the destination of the path + */ + LinksHaveEntryWithSourceDestinationPairMatcher(String source, + String destination) { + this.source = source; + this.destination = destination; + } + + @Override + public boolean matchesSafely(Collection<Link> links) { + for (Link link : links) { + if (source.equals(destination) && link instanceof EdgeLink) { + EdgeLink edgeLink = (EdgeLink) link; + if (edgeLink.hostLocation().elementId() + .toString().endsWith(source)) { + return true; + } + } + + if (link.src().elementId().toString().endsWith(source) && + link.dst().elementId().toString().endsWith(destination)) { + return true; + } + } + + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("link lookup for source \""); + description.appendText(source); + description.appendText(" and destination "); + description.appendText(destination); + description.appendText("\""); + } + + @Override + public void describeMismatchSafely(Collection<Link> links, + Description mismatchDescription) { + mismatchDescription.appendText("was "). + appendText(links.toString()); + } + + /** + * Creates a link has path matcher. + * + * @param source string identifier for the source of the path + * @param destination string identifier for the destination of the path + * @return matcher to match the path + */ + public static LinksHaveEntryWithSourceDestinationPairMatcher linksHasPath( + String source, + String destination) { + return new LinksHaveEntryWithSourceDestinationPairMatcher(source, + destination); + } +} + diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentAccumulatorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentAccumulatorTest.java new file mode 100644 index 00000000..219d2fd5 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentAccumulatorTest.java @@ -0,0 +1,160 @@ +/* + * 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 java.util.Collection; +import java.util.List; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentBatchDelegate; +import org.onosproject.net.intent.IntentData; +import org.onosproject.net.intent.IntentState; +import org.onosproject.net.intent.IntentTestsMocks.MockIntent; +import org.onosproject.net.intent.IntentTestsMocks.MockTimestamp; +import org.onosproject.net.intent.MockIdGenerator; + +import com.google.common.collect.ImmutableList; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** + * Unit tests for the intent accumulator. + */ +public class IntentAccumulatorTest { + + Intent intent1; + Intent intent2; + Intent intent3; + IdGenerator mockGenerator; + + private static IntentDataMatcher containsIntent(Intent intent) { + return new IntentDataMatcher(intent); + } + + /** + * Creates mock intents used by the test. + */ + @Before + public void localSetup() { + mockGenerator = new MockIdGenerator(); + Intent.bindIdGenerator(mockGenerator); + + intent1 = new MockIntent(1L); + intent2 = new MockIntent(2L); + intent3 = new MockIntent(3L); + } + + /** + * Removes id generator from the Intent class. + */ + @After + public void localTearDown() { + Intent.unbindIdGenerator(mockGenerator); + } + + /** + * Hamcrest matcher to check that a collection of intent data objects + * contains an entry for a given intent. + */ + private static final class IntentDataMatcher + extends TypeSafeDiagnosingMatcher<Collection<IntentData>> { + + final Intent intent; + + public IntentDataMatcher(Intent intent) { + this.intent = intent; + } + + /** + * Check that the given collection of intent data contains a specific + * intent. + * + * @param operations collection of intent data + * @param description description + * @return true if the collection contains the intent, false otherwise. + */ + public boolean matchesSafely(Collection<IntentData> operations, + Description description) { + for (IntentData operation : operations) { + if (operation.key().equals(intent.key())) { + if (operation.state() != IntentState.INSTALLED) { + description.appendText("state was " + operation.state()); + return false; + } + return true; + } + } + description.appendText("key was not found " + intent.key()); + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("INSTALLED state intent with key " + intent.key()); + } + } + + /** + * Mock batch delegate class. Gets calls from the accumulator and checks + * that the operations have been properly compressed. + */ + private class MockIntentBatchDelegate + implements IntentBatchDelegate { + public void execute(Collection<IntentData> operations) { + assertThat(operations, hasSize(3)); + assertThat(operations, containsIntent(intent1)); + assertThat(operations, containsIntent(intent2)); + assertThat(operations, containsIntent(intent3)); + } + } + + /** + * Tests that the accumulator properly compresses operations on the same + * intents. + */ + @Test + public void checkAccumulator() { + + MockIntentBatchDelegate delegate = new MockIntentBatchDelegate(); + IntentAccumulator accumulator = new IntentAccumulator(delegate); + + List<IntentData> intentDataItems = ImmutableList.of( + new IntentData(intent1, IntentState.INSTALLING, + new MockTimestamp(1)), + new IntentData(intent2, IntentState.INSTALLING, + new MockTimestamp(1)), + new IntentData(intent3, IntentState.INSTALLED, + new MockTimestamp(1)), + new IntentData(intent2, IntentState.INSTALLED, + new MockTimestamp(1)), + new IntentData(intent2, IntentState.INSTALLED, + new MockTimestamp(1)), + new IntentData(intent1, IntentState.INSTALLED, + new MockTimestamp(1))); + + + accumulator.processItems(intentDataItems); + } + + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java new file mode 100644 index 00000000..0f6ce67c --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java @@ -0,0 +1,261 @@ +/* + * 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.SimpleIntentStore; +import org.onosproject.store.trivial.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 diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java new file mode 100644 index 00000000..15ee24e6 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java @@ -0,0 +1,285 @@ +/* + * 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.SimpleIntentStore; +import org.onosproject.store.trivial.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 diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java new file mode 100644 index 00000000..4bf32f43 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java @@ -0,0 +1,672 @@ +/* + * Copyright 2014-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.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.impl.TestCoreManager; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentCompiler; +import org.onosproject.net.intent.IntentData; +import org.onosproject.net.intent.IntentEvent; +import org.onosproject.net.intent.IntentEvent.Type; +import org.onosproject.net.intent.IntentExtensionService; +import org.onosproject.net.intent.IntentId; +import org.onosproject.net.intent.IntentListener; +import org.onosproject.net.intent.IntentService; +import org.onosproject.net.intent.IntentState; +import org.onosproject.net.intent.Key; +import org.onosproject.net.resource.link.LinkResourceAllocations; +import org.onosproject.store.trivial.SimpleIntentStore; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; +import static org.onlab.junit.TestTools.assertAfter; +import static org.onlab.util.Tools.delay; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.intent.IntentState.*; +import static org.onosproject.net.intent.IntentTestsMocks.MockFlowRule; +import static org.onosproject.net.intent.IntentTestsMocks.MockIntent; + +/** + * Test intent manager and transitions. + * + * TODO implement the following tests: + * - {submit, withdraw, update, replace} intent + * - {submit, update, recompiling} intent with failed compilation + * - failed reservation + * - push timeout recovery + * - failed items recovery + * + * in general, verify intents store, flow store, and work queue + */ + +public class IntentManagerTest { + + private static final int SUBMIT_TIMEOUT_MS = 1000; + private static final ApplicationId APPID = new TestApplicationId("manager-test"); + + private IntentManager manager; + private MockFlowRuleService flowRuleService; + + protected IntentService service; + protected IntentExtensionService extensionService; + protected TestListener listener = new TestListener(); + protected TestIntentCompiler compiler = new TestIntentCompiler(); + + private static class TestListener implements IntentListener { + final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create(); + Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap(); + + @Override + public void event(IntentEvent event) { + events.put(event.type(), event); + if (latchMap.containsKey(event.type())) { + latchMap.get(event.type()).countDown(); + } + } + + public int getCounts(IntentEvent.Type type) { + return events.get(type).size(); + } + + public void setLatch(int count, IntentEvent.Type type) { + latchMap.put(type, new CountDownLatch(count)); + } + + public void await(IntentEvent.Type type) { + try { + assertTrue("Timed out waiting for: " + type, + latchMap.get(type).await(5, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private static class TestIntentTracker implements ObjectiveTrackerService { + private TopologyChangeDelegate delegate; + @Override + public void setDelegate(TopologyChangeDelegate delegate) { + this.delegate = delegate; + } + + @Override + public void unsetDelegate(TopologyChangeDelegate delegate) { + if (delegate.equals(this.delegate)) { + this.delegate = null; + } + } + + @Override + public void addTrackedResources(Key key, Collection<NetworkResource> resources) { + //TODO + } + + @Override + public void removeTrackedResources(Key key, Collection<NetworkResource> resources) { + //TODO + } + + @Override + public void trackIntent(IntentData intentData) { + //TODO + } + } + + private static class MockInstallableIntent extends FlowRuleIntent { + + public MockInstallableIntent() { + super(APPID, Collections.singletonList(new MockFlowRule(100))); + } + } + + private static class TestIntentCompiler implements IntentCompiler<MockIntent> { + @Override + public List<Intent> compile(MockIntent intent, List<Intent> installable, + Set<LinkResourceAllocations> resources) { + return Lists.newArrayList(new MockInstallableIntent()); + } + } + + private static class TestIntentCompilerMultipleFlows implements IntentCompiler<MockIntent> { + @Override + public List<Intent> compile(MockIntent intent, List<Intent> installable, + Set<LinkResourceAllocations> resources) { + + return IntStream.rangeClosed(1, 5) + .mapToObj(mock -> (new MockInstallableIntent())) + .collect(Collectors.toList()); + } + } + + + private static class TestIntentCompilerError implements IntentCompiler<MockIntent> { + @Override + public List<Intent> compile(MockIntent intent, List<Intent> installable, + Set<LinkResourceAllocations> resources) { + throw new IntentCompilationException("Compilation always fails"); + } + } + + /** + * Hamcrest matcher to check that a collection of Intents contains an + * Intent with the specified Intent Id. + */ + public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> { + private final IntentId id; + + public EntryForIntentMatcher(IntentId idValue) { + id = idValue; + } + + @Override + public boolean matchesSafely(Collection<Intent> intents) { + for (Intent intent : intents) { + if (intent.id().equals(id)) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("an intent with id \" "). + appendText(id.toString()). + appendText("\""); + } + } + + private static EntryForIntentMatcher hasIntentWithId(IntentId id) { + return new EntryForIntentMatcher(id); + } + + @Before + public void setUp() { + manager = new IntentManager(); + flowRuleService = new MockFlowRuleService(); + manager.store = new SimpleIntentStore(); + injectEventDispatcher(manager, new TestEventDispatcher()); + manager.trackerService = new TestIntentTracker(); + manager.flowRuleService = flowRuleService; + manager.coreService = new TestCoreManager(); + service = manager; + extensionService = manager; + + manager.activate(); + service.addListener(listener); + extensionService.registerCompiler(MockIntent.class, compiler); + + assertTrue("store should be empty", + Sets.newHashSet(service.getIntents()).isEmpty()); + assertEquals(0L, flowRuleService.getFlowRuleCount()); + } + + public void verifyState() { + // verify that all intents are parked and the batch operation is unblocked + Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED, CORRUPT); + for (Intent i : service.getIntents()) { + IntentState state = service.getIntentState(i.key()); + assertTrue("Intent " + i.id() + " is in invalid state " + state, + parked.contains(state)); + } + //the batch has not yet been removed when we receive the last event + // FIXME: this doesn't guarantee to avoid the race + + //FIXME +// for (int tries = 0; tries < 10; tries++) { +// if (manager.batchService.getPendingOperations().isEmpty()) { +// break; +// } +// delay(10); +// } +// assertTrue("There are still pending batch operations.", +// manager.batchService.getPendingOperations().isEmpty()); + + } + + @After + public void tearDown() { + extensionService.unregisterCompiler(MockIntent.class); + service.removeListener(listener); + manager.deactivate(); + // TODO null the other refs? + } + + @Test + public void submitIntent() { + flowRuleService.setFuture(true); + + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.INSTALLED); + Intent intent = new MockIntent(MockIntent.nextId()); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALLED); + assertEquals(1L, service.getIntentCount()); + assertEquals(1L, flowRuleService.getFlowRuleCount()); + verifyState(); + } + + @Test + public void withdrawIntent() { + flowRuleService.setFuture(true); + + listener.setLatch(1, Type.INSTALLED); + Intent intent = new MockIntent(MockIntent.nextId()); + service.submit(intent); + listener.await(Type.INSTALLED); + assertEquals(1L, service.getIntentCount()); + assertEquals(1L, flowRuleService.getFlowRuleCount()); + + listener.setLatch(1, Type.WITHDRAWN); + service.withdraw(intent); + listener.await(Type.WITHDRAWN); + assertEquals(0L, flowRuleService.getFlowRuleCount()); + verifyState(); + } + + @Test + @Ignore("This is disabled because we are seeing intermittent failures on Jenkins") + public void stressSubmitWithdrawUnique() { + flowRuleService.setFuture(true); + + int count = 500; + Intent[] intents = new Intent[count]; + + listener.setLatch(count, Type.WITHDRAWN); + + for (int i = 0; i < count; i++) { + intents[i] = new MockIntent(MockIntent.nextId()); + service.submit(intents[i]); + } + + for (int i = 0; i < count; i++) { + service.withdraw(intents[i]); + } + + listener.await(Type.WITHDRAWN); + assertEquals(0L, flowRuleService.getFlowRuleCount()); + verifyState(); + } + + @Test + public void stressSubmitWithdrawSame() { + flowRuleService.setFuture(true); + + int count = 50; + + Intent intent = new MockIntent(MockIntent.nextId()); + for (int i = 0; i < count; i++) { + service.submit(intent); + service.withdraw(intent); + } + + assertAfter(SUBMIT_TIMEOUT_MS, () -> { + assertEquals(1L, service.getIntentCount()); + assertEquals(0L, flowRuleService.getFlowRuleCount()); + }); + verifyState(); + } + + + /** + * Tests for proper behavior of installation of an intent that triggers + * a compilation error. + */ + @Test + public void errorIntentCompile() { + final TestIntentCompilerError errorCompiler = new TestIntentCompilerError(); + extensionService.registerCompiler(MockIntent.class, errorCompiler); + MockIntent intent = new MockIntent(MockIntent.nextId()); + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.FAILED); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.FAILED); + verifyState(); + } + + /** + * Tests handling a future that contains an error as a result of + * installing an intent. + */ + @Ignore("skipping until we fix update ordering problem") + @Test + public void errorIntentInstallFromFlows() { + final Long id = MockIntent.nextId(); + flowRuleService.setFuture(false); + MockIntent intent = new MockIntent(id); + listener.setLatch(1, Type.FAILED); + listener.setLatch(1, Type.INSTALL_REQ); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.FAILED); + // FIXME the intent will be moved into INSTALLED immediately which overrides FAILED + // ... the updates come out of order + verifyState(); + } + + /** + * Tests handling a future that contains an unresolvable error as a result of + * installing an intent. + */ + @Test + public void errorIntentInstallNeverTrue() { + final Long id = MockIntent.nextId(); + flowRuleService.setFuture(false); + MockIntent intent = new MockIntent(id); + listener.setLatch(1, Type.CORRUPT); + listener.setLatch(1, Type.INSTALL_REQ); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + // The delay here forces the retry loop in the intent manager to time out + delay(100); + flowRuleService.setFuture(false); + service.withdraw(intent); + listener.await(Type.CORRUPT); + verifyState(); + } + + /** + * Tests that a compiler for a subclass of an intent that already has a + * compiler is automatically added. + */ + @Test + public void intentSubclassCompile() { + class MockIntentSubclass extends MockIntent { + public MockIntentSubclass(Long number) { + super(number); + } + } + flowRuleService.setFuture(true); + + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.INSTALLED); + Intent intent = new MockIntentSubclass(MockIntent.nextId()); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALLED); + assertEquals(1L, service.getIntentCount()); + assertEquals(1L, flowRuleService.getFlowRuleCount()); + + final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = + extensionService.getCompilers(); + assertEquals(2, compilers.size()); + assertNotNull(compilers.get(MockIntentSubclass.class)); + assertNotNull(compilers.get(MockIntent.class)); + verifyState(); + } + + /** + * Tests an intent with no compiler. + */ + @Test + public void intentWithoutCompiler() { + class IntentNoCompiler extends Intent { + IntentNoCompiler() { + super(APPID, null, Collections.emptyList(), + Intent.DEFAULT_INTENT_PRIORITY); + } + } + + Intent intent = new IntentNoCompiler(); + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.FAILED); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.FAILED); + verifyState(); + } + + /** + * Tests an intent with no installer. + */ + @Test + public void intentWithoutInstaller() { + MockIntent intent = new MockIntent(MockIntent.nextId()); + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.CORRUPT); + service.submit(intent); + listener.await(Type.INSTALL_REQ); + listener.await(Type.CORRUPT); + verifyState(); + } + + /** + * Tests that the intent fetching methods are correct. + */ + @Test + public void testIntentFetching() { + List<Intent> intents; + + flowRuleService.setFuture(true); + + intents = Lists.newArrayList(service.getIntents()); + assertThat(intents, hasSize(0)); + + final MockIntent intent1 = new MockIntent(MockIntent.nextId()); + final MockIntent intent2 = new MockIntent(MockIntent.nextId()); + + listener.setLatch(2, Type.INSTALL_REQ); + listener.setLatch(2, Type.INSTALLED); + service.submit(intent1); + service.submit(intent2); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALLED); + listener.await(Type.INSTALLED); + + intents = Lists.newArrayList(service.getIntents()); + assertThat(intents, hasSize(2)); + + assertThat(intents, hasIntentWithId(intent1.id())); + assertThat(intents, hasIntentWithId(intent2.id())); + verifyState(); + } + + /** + * Tests that removing all intents results in no flows remaining. + */ + @Test + public void testFlowRemoval() { + List<Intent> intents; + + flowRuleService.setFuture(true); + + intents = Lists.newArrayList(service.getIntents()); + assertThat(intents, hasSize(0)); + + final MockIntent intent1 = new MockIntent(MockIntent.nextId()); + final MockIntent intent2 = new MockIntent(MockIntent.nextId()); + + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.INSTALLED); + + service.submit(intent1); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALLED); + + + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.INSTALLED); + + service.submit(intent2); + listener.await(Type.INSTALL_REQ); + listener.await(Type.INSTALLED); + + assertThat(listener.getCounts(Type.INSTALLED), is(2)); + assertThat(flowRuleService.getFlowRuleCount(), is(2)); + + listener.setLatch(1, Type.WITHDRAWN); + service.withdraw(intent1); + listener.await(Type.WITHDRAWN); + + listener.setLatch(1, Type.WITHDRAWN); + service.withdraw(intent2); + listener.await(Type.WITHDRAWN); + + assertThat(listener.getCounts(Type.WITHDRAWN), is(2)); + assertThat(flowRuleService.getFlowRuleCount(), is(0)); + } + + /** + * Test failure to install an intent, then succeed on retry via IntentCleanup. + */ + @Test + public void testCorruptCleanup() { + IntentCleanup cleanup = new IntentCleanup(); + cleanup.service = manager; + cleanup.store = manager.store; + cleanup.cfgService = new ComponentConfigAdapter(); + + 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(1, Type.CORRUPT); + listener.setLatch(1, Type.INSTALLED); + + service.submit(intent1); + + listener.await(Type.INSTALL_REQ); + listener.await(Type.CORRUPT); + + flowRuleService.setFuture(true); + + listener.await(Type.INSTALLED); + + assertThat(listener.getCounts(Type.CORRUPT), is(1)); + assertThat(listener.getCounts(Type.INSTALLED), is(1)); + assertEquals(INSTALLED, manager.getIntentState(intent1.key())); + assertThat(flowRuleService.getFlowRuleCount(), is(5)); + } finally { + cleanup.deactivate(); + } + } + + /** + * 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 + @Ignore("MockFlowRule numbering issue") //test works if run independently + public void testFlowRemovalInstallError() { + final TestIntentCompilerMultipleFlows errorCompiler = new TestIntentCompilerMultipleFlows(); + extensionService.registerCompiler(MockIntent.class, errorCompiler); + List<Intent> intents; + + flowRuleService.setFuture(true); + //FIXME relying on "3" is brittle + flowRuleService.setErrorFlow(3); + + intents = Lists.newArrayList(service.getIntents()); + assertThat(intents, hasSize(0)); + + final MockIntent intent1 = new MockIntent(MockIntent.nextId()); + + listener.setLatch(1, Type.INSTALL_REQ); + listener.setLatch(1, Type.CORRUPT); + + service.submit(intent1); + listener.await(Type.INSTALL_REQ); + listener.await(Type.CORRUPT); + + assertThat(listener.getCounts(Type.CORRUPT), is(1)); + // in this test, there will still be flows abandoned on the data plane + //assertThat(flowRuleService.getFlowRuleCount(), is(0)); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/MockFlowRuleService.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/MockFlowRuleService.java new file mode 100644 index 00000000..8bd29bf8 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/MockFlowRuleService.java @@ -0,0 +1,116 @@ +/* + * Copyright 2014-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.onosproject.core.ApplicationId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleOperations; +import org.onosproject.net.flow.FlowRuleServiceAdapter; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import static org.onosproject.net.flow.FlowRuleOperation.Type.REMOVE; + + +public class MockFlowRuleService extends FlowRuleServiceAdapter { + + final Set<FlowRule> flows = Sets.newHashSet(); + boolean success; + + int errorFlow = -1; + public void setErrorFlow(int errorFlow) { + this.errorFlow = errorFlow; + } + + public void setFuture(boolean success) { + this.success = success; + } + + @Override + public void apply(FlowRuleOperations ops) { + AtomicBoolean thisSuccess = new AtomicBoolean(success); + ops.stages().forEach(stage -> stage.forEach(flow -> { + if (errorFlow == flow.rule().id().value()) { + thisSuccess.set(false); + } else { + switch (flow.type()) { + case ADD: + case MODIFY: //TODO is this the right behavior for modify? + flows.add(flow.rule()); + break; + case REMOVE: + flows.remove(flow.rule()); + break; + default: + break; + } + } + })); + if (thisSuccess.get()) { + ops.callback().onSuccess(ops); + } else { + ops.callback().onError(ops); + } + } + + @Override + public int getFlowRuleCount() { + return flows.size(); + } + + @Override + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { + return flows.stream() + .filter(flow -> flow.deviceId().equals(deviceId)) + .map(DefaultFlowEntry::new) + .collect(Collectors.toList()); + } + + @Override + public void applyFlowRules(FlowRule... flowRules) { + for (FlowRule flow : flowRules) { + flows.add(flow); + } + } + + @Override + public void removeFlowRules(FlowRule... flowRules) { + for (FlowRule flow : flowRules) { + flows.remove(flow); + } + } + + @Override + public Iterable<FlowRule> getFlowRulesById(ApplicationId id) { + return flows.stream() + .filter(flow -> flow.appId() == id.id()) + .collect(Collectors.toList()); + } + + @Override + public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) { + return flows.stream() + .filter(flow -> flow.appId() == appId.id() && flow.groupId().id() == groupId) + .collect(Collectors.toList()); + } +} + diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/ObjectiveTrackerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/ObjectiveTrackerTest.java new file mode 100644 index 00000000..58fa1292 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/ObjectiveTrackerTest.java @@ -0,0 +1,326 @@ +/* + * Copyright 2014-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 java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.junit.TestUtils; +import org.onlab.junit.TestUtils.TestUtilsException; +import org.onosproject.core.IdGenerator; +import org.onosproject.event.Event; +import org.onosproject.net.Device; +import org.onosproject.net.Link; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.Key; +import org.onosproject.net.intent.MockIdGenerator; +import org.onosproject.net.link.LinkEvent; +import org.onosproject.net.resource.link.LinkResourceEvent; +import org.onosproject.net.resource.link.LinkResourceListener; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyEvent; +import org.onosproject.net.topology.TopologyListener; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; + +import static org.easymock.EasyMock.createMock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.device; +import static org.onosproject.net.NetTestTools.link; + +/** + * Tests for the objective tracker. + */ +public class ObjectiveTrackerTest { + private static final int WAIT_TIMEOUT_SECONDS = 2; + private Topology topology; + private ObjectiveTracker tracker; + private TestTopologyChangeDelegate delegate; + private List<Event> reasons; + private TopologyListener listener; + private DeviceListener deviceListener; + private LinkResourceListener linkResourceListener; + private IdGenerator mockGenerator; + + /** + * Initialization shared by all test cases. + * + * @throws TestUtilsException if any filed look ups fail + */ + @Before + public void setUp() throws TestUtilsException { + topology = createMock(Topology.class); + tracker = new ObjectiveTracker(); + delegate = new TestTopologyChangeDelegate(); + tracker.setDelegate(delegate); + reasons = new LinkedList<>(); + listener = TestUtils.getField(tracker, "listener"); + deviceListener = TestUtils.getField(tracker, "deviceListener"); + linkResourceListener = TestUtils.getField(tracker, "linkResourceListener"); + mockGenerator = new MockIdGenerator(); + Intent.bindIdGenerator(mockGenerator); + } + + /** + * Code to clean up shared by all test case. + */ + @After + public void tearDown() { + tracker.unsetDelegate(delegate); + Intent.unbindIdGenerator(mockGenerator); + } + + /** + * Topology change delegate mock that tracks the events coming into it + * and saves them. It provides a latch so that tests can wait for events + * to be generated. + */ + static class TestTopologyChangeDelegate implements TopologyChangeDelegate { + + CountDownLatch latch = new CountDownLatch(1); + List<Key> intentIdsFromEvent; + boolean compileAllFailedFromEvent; + + @Override + public void triggerCompile(Iterable<Key> intentKeys, + boolean compileAllFailed) { + intentIdsFromEvent = Lists.newArrayList(intentKeys); + compileAllFailedFromEvent = compileAllFailed; + latch.countDown(); + } + } + + /** + * Tests an event with no associated reasons. + * + * @throws InterruptedException if the latch wait fails. + */ + @Test + public void testEventNoReasons() throws InterruptedException { + final TopologyEvent event = new TopologyEvent( + TopologyEvent.Type.TOPOLOGY_CHANGED, + topology, + null); + + listener.event(event); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(0)); + assertThat(delegate.compileAllFailedFromEvent, is(true)); + } + + /** + * Tests an event for a link down where none of the reasons match + * currently installed intents. + * + * @throws InterruptedException if the latch wait fails. + */ + @Test + public void testEventLinkDownNoMatches() throws InterruptedException { + final Link link = link("src", 1, "dst", 2); + final LinkEvent linkEvent = new LinkEvent(LinkEvent.Type.LINK_REMOVED, link); + reasons.add(linkEvent); + + final TopologyEvent event = new TopologyEvent( + TopologyEvent.Type.TOPOLOGY_CHANGED, + topology, + reasons); + + listener.event(event); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(0)); + assertThat(delegate.compileAllFailedFromEvent, is(false)); + } + + /** + * Tests an event for a link being added. + * + * @throws InterruptedException if the latch wait fails. + */ + @Test + public void testEventLinkAdded() throws InterruptedException { + final Link link = link("src", 1, "dst", 2); + final LinkEvent linkEvent = new LinkEvent(LinkEvent.Type.LINK_ADDED, link); + reasons.add(linkEvent); + + final TopologyEvent event = new TopologyEvent( + TopologyEvent.Type.TOPOLOGY_CHANGED, + topology, + reasons); + + listener.event(event); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(0)); + assertThat(delegate.compileAllFailedFromEvent, is(true)); + } + + /** + * Tests an event for a link down where the link matches existing intents. + * + * @throws InterruptedException if the latch wait fails. + */ + @Test + public void testEventLinkDownMatch() throws Exception { + final Link link = link("src", 1, "dst", 2); + final LinkEvent linkEvent = new LinkEvent(LinkEvent.Type.LINK_REMOVED, link); + reasons.add(linkEvent); + + final TopologyEvent event = new TopologyEvent( + TopologyEvent.Type.TOPOLOGY_CHANGED, + topology, + reasons); + + final Key key = Key.of(0x333L, APP_ID); + Collection<NetworkResource> resources = ImmutableSet.of(link); + tracker.addTrackedResources(key, resources); + + listener.event(event); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(1)); + assertThat(delegate.compileAllFailedFromEvent, is(false)); + assertThat(delegate.intentIdsFromEvent.get(0).toString(), + equalTo("0x333")); + } + + /** + * Tests a resource available event. + * + * @throws InterruptedException if the latch wait fails. + */ + @Test + public void testResourceEvent() throws Exception { + LinkResourceEvent event = new LinkResourceEvent( + LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE, + new HashSet<>()); + linkResourceListener.event(event); + + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(0)); + assertThat(delegate.compileAllFailedFromEvent, is(true)); + } + + /** + * Tests an event for a host becoming available that matches an intent. + * + * @throws InterruptedException if the latch wait fails. + */ + + @Test + public void testEventHostAvailableMatch() throws Exception { + final Device host = device("host1"); + + final DeviceEvent deviceEvent = + new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, host); + reasons.add(deviceEvent); + + final Key key = Key.of(0x333L, APP_ID); + Collection<NetworkResource> resources = ImmutableSet.of(host.id()); + tracker.addTrackedResources(key, resources); + + deviceListener.event(deviceEvent); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(1)); + assertThat(delegate.compileAllFailedFromEvent, is(true)); + assertThat(delegate.intentIdsFromEvent.get(0).toString(), + equalTo("0x333")); + } + + /** + * Tests an event for a host becoming unavailable that matches an intent. + * + * @throws InterruptedException if the latch wait fails. + */ + + @Test + public void testEventHostUnavailableMatch() throws Exception { + final Device host = device("host1"); + + final DeviceEvent deviceEvent = + new DeviceEvent(DeviceEvent.Type.DEVICE_REMOVED, host); + reasons.add(deviceEvent); + + final Key key = Key.of(0x333L, APP_ID); + Collection<NetworkResource> resources = ImmutableSet.of(host.id()); + tracker.addTrackedResources(key, resources); + + deviceListener.event(deviceEvent); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(1)); + assertThat(delegate.compileAllFailedFromEvent, is(false)); + assertThat(delegate.intentIdsFromEvent.get(0).toString(), + equalTo("0x333")); + } + + /** + * Tests an event for a host becoming available that matches an intent. + * + * @throws InterruptedException if the latch wait fails. + */ + + @Test + public void testEventHostAvailableNoMatch() throws Exception { + final Device host = device("host1"); + + final DeviceEvent deviceEvent = + new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, host); + reasons.add(deviceEvent); + + deviceListener.event(deviceEvent); + assertThat( + delegate.latch.await(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS), + is(true)); + + assertThat(delegate.intentIdsFromEvent, hasSize(0)); + assertThat(delegate.compileAllFailedFromEvent, is(true)); + } + + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompilerTest.java new file mode 100644 index 00000000..5588904d --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompilerTest.java @@ -0,0 +1,167 @@ +/* + * Copyright 2014-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.compiler; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.core.ApplicationId; +import org.onosproject.TestApplicationId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.HostService; +import org.onosproject.net.intent.AbstractIntentTest; +import org.onosproject.net.intent.HostToHostIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.PathIntent; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; + +import java.util.List; + +import static org.easymock.EasyMock.*; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.NetTestTools.hid; +import static org.onosproject.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath; + +/** + * Unit tests for the HostToHost intent compiler. + */ +public class HostToHostIntentCompilerTest extends AbstractIntentTest { + private static final String HOST_ONE_MAC = "00:00:00:00:00:01"; + private static final String HOST_TWO_MAC = "00:00:00:00:00:02"; + private static final String HOST_ONE_VLAN = "-1"; + private static final String HOST_TWO_VLAN = "-1"; + private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN; + private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN; + + private static final ApplicationId APPID = new TestApplicationId("foo"); + + private TrafficSelector selector = new IntentTestsMocks.MockSelector(); + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment(); + + private HostId hostOneId = HostId.hostId(HOST_ONE); + private HostId hostTwoId = HostId.hostId(HOST_TWO); + private HostService mockHostService; + + @Before + public void setUp() throws Exception { + super.setUp(); + Host hostOne = createMock(Host.class); + expect(hostOne.mac()).andReturn(new MacAddress(HOST_ONE_MAC.getBytes())).anyTimes(); + expect(hostOne.vlan()).andReturn(VlanId.vlanId()).anyTimes(); + replay(hostOne); + + Host hostTwo = createMock(Host.class); + expect(hostTwo.mac()).andReturn(new MacAddress(HOST_TWO_MAC.getBytes())).anyTimes(); + expect(hostTwo.vlan()).andReturn(VlanId.vlanId()).anyTimes(); + replay(hostTwo); + + mockHostService = createMock(HostService.class); + expect(mockHostService.getHost(eq(hostOneId))).andReturn(hostOne).anyTimes(); + expect(mockHostService.getHost(eq(hostTwoId))).andReturn(hostTwo).anyTimes(); + replay(mockHostService); + } + + /** + * Creates a HostToHost intent based on two host Ids. + * + * @param oneIdString string for host one id + * @param twoIdString string for host two id + * @return HostToHostIntent for the two hosts + */ + private HostToHostIntent makeIntent(String oneIdString, String twoIdString) { + return HostToHostIntent.builder() + .appId(APPID) + .one(hid(oneIdString)) + .two(hid(twoIdString)) + .selector(selector) + .treatment(treatment) + .build(); + } + + /** + * Creates a compiler for HostToHost intents. + * + * @param hops string array describing the path hops to use when compiling + * @return HostToHost intent compiler + */ + private HostToHostIntentCompiler makeCompiler(String[] hops) { + HostToHostIntentCompiler compiler = + new HostToHostIntentCompiler(); + compiler.pathService = new IntentTestsMocks.MockPathService(hops); + compiler.hostService = mockHostService; + return compiler; + } + + + /** + * Tests a pair of hosts with 8 hops between them. + */ + @Test + public void testSingleLongPathCompilation() { + + HostToHostIntent intent = makeIntent(HOST_ONE, + HOST_TWO); + assertThat(intent, is(notNullValue())); + + String[] hops = {HOST_ONE, "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", HOST_TWO}; + HostToHostIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(2)); + Intent forwardResultIntent = result.get(0); + assertThat(forwardResultIntent instanceof PathIntent, is(true)); + Intent reverseResultIntent = result.get(1); + assertThat(reverseResultIntent instanceof PathIntent, is(true)); + + if (forwardResultIntent instanceof PathIntent) { + PathIntent forwardPathIntent = (PathIntent) forwardResultIntent; + assertThat(forwardPathIntent.path().links(), hasSize(9)); + assertThat(forwardPathIntent.path().links(), linksHasPath(HOST_ONE, "h1")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h1", "h2")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h2", "h3")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h3", "h4")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h4", "h5")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h5", "h6")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h6", "h7")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h7", "h8")); + assertThat(forwardPathIntent.path().links(), linksHasPath("h8", HOST_TWO)); + } + + if (reverseResultIntent instanceof PathIntent) { + PathIntent reversePathIntent = (PathIntent) reverseResultIntent; + assertThat(reversePathIntent.path().links(), hasSize(9)); + assertThat(reversePathIntent.path().links(), linksHasPath("h1", HOST_ONE)); + assertThat(reversePathIntent.path().links(), linksHasPath("h2", "h1")); + assertThat(reversePathIntent.path().links(), linksHasPath("h3", "h2")); + assertThat(reversePathIntent.path().links(), linksHasPath("h4", "h3")); + assertThat(reversePathIntent.path().links(), linksHasPath("h5", "h4")); + assertThat(reversePathIntent.path().links(), linksHasPath("h6", "h5")); + assertThat(reversePathIntent.path().links(), linksHasPath("h7", "h6")); + assertThat(reversePathIntent.path().links(), linksHasPath("h8", "h7")); + assertThat(reversePathIntent.path().links(), linksHasPath(HOST_TWO, "h8")); + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java new file mode 100644 index 00000000..c5fa3719 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java @@ -0,0 +1,163 @@ +/* + * 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.compiler; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultLink; +import org.onosproject.net.Link; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentExtensionService; +import org.onosproject.net.intent.LinkCollectionIntent; +import org.onosproject.net.intent.MockIdGenerator; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.PID; +import static org.onosproject.net.NetTestTools.connectPoint; + +public class LinkCollectionIntentCompilerTest { + + private final ApplicationId appId = new TestApplicationId("test"); + + private final ConnectPoint d1p1 = connectPoint("s1", 0); + private final ConnectPoint d2p0 = connectPoint("s2", 0); + private final ConnectPoint d2p1 = connectPoint("s2", 1); + private final ConnectPoint d3p1 = connectPoint("s3", 1); + private final ConnectPoint d3p0 = connectPoint("s3", 10); + private final ConnectPoint d1p0 = connectPoint("s1", 10); + + private final Set<Link> links = ImmutableSet.of( + new DefaultLink(PID, d1p1, d2p0, DIRECT), + new DefaultLink(PID, d2p1, d3p1, DIRECT), + new DefaultLink(PID, d1p1, d3p1, DIRECT)); + + private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); + private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + private CoreService coreService; + private IntentExtensionService intentExtensionService; + private IdGenerator idGenerator = new MockIdGenerator(); + + private LinkCollectionIntent intent; + + private LinkCollectionIntentCompiler sut; + + @Before + public void setUp() { + sut = new LinkCollectionIntentCompiler(); + coreService = createMock(CoreService.class); + expect(coreService.registerApplication("org.onosproject.net.intent")) + .andReturn(appId); + sut.coreService = coreService; + + Intent.bindIdGenerator(idGenerator); + + intent = LinkCollectionIntent.builder() + .appId(APP_ID) + .selector(selector) + .treatment(treatment) + .links(links) + .ingressPoints(ImmutableSet.of(d1p1)) + .egressPoints(ImmutableSet.of(d3p1)) + .build(); + intentExtensionService = createMock(IntentExtensionService.class); + intentExtensionService.registerCompiler(LinkCollectionIntent.class, sut); + intentExtensionService.unregisterCompiler(LinkCollectionIntent.class); + sut.intentManager = intentExtensionService; + + replay(coreService, intentExtensionService); + } + + @After + public void tearDown() { + Intent.unbindIdGenerator(idGenerator); + } + + @Test + public void testCompile() { + sut.activate(); + + List<Intent> compiled = sut.compile(intent, Collections.emptyList(), Collections.emptySet()); + assertThat(compiled, hasSize(1)); + + Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); + assertThat(rules, hasSize(links.size())); + + // if not found, get() raises an exception + FlowRule rule1 = rules.stream() + .filter(rule -> rule.deviceId().equals(d1p0.deviceId())) + .findFirst() + .get(); + assertThat(rule1.selector(), is( + DefaultTrafficSelector.builder(intent.selector()).matchInPort(d1p1.port()).build() + )); + assertThat(rule1.treatment(), is( + DefaultTrafficTreatment.builder(intent.treatment()).setOutput(d1p1.port()).build() + )); + assertThat(rule1.priority(), is(intent.priority())); + + FlowRule rule2 = rules.stream() + .filter(rule -> rule.deviceId().equals(d2p0.deviceId())) + .findFirst() + .get(); + assertThat(rule2.selector(), is( + DefaultTrafficSelector.builder(intent.selector()).matchInPort(d2p0.port()).build() + )); + assertThat(rule2.treatment(), is( + DefaultTrafficTreatment.builder().setOutput(d2p1.port()).build() + )); + assertThat(rule2.priority(), is(intent.priority())); + + FlowRule rule3 = rules.stream() + .filter(rule -> rule.deviceId().equals(d3p0.deviceId())) + .findFirst() + .get(); + assertThat(rule3.selector(), is( + DefaultTrafficSelector.builder(intent.selector()).matchInPort(d3p1.port()).build() + )); + assertThat(rule3.treatment(), is( + DefaultTrafficTreatment.builder().setOutput(d3p1.port()).build() + )); + assertThat(rule3.priority(), is(intent.priority())); + + sut.deactivate(); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompilerTest.java new file mode 100644 index 00000000..76b26f46 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompilerTest.java @@ -0,0 +1,188 @@ +package org.onosproject.net.intent.impl.compiler; + +import java.util.List; +import java.util.Optional; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +import org.onlab.packet.MplsLabel; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Link; +import org.onosproject.net.Path; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.AbstractIntentTest; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.MplsIntent; +import org.onosproject.net.intent.MplsPathIntent; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.connectPoint; +import static org.onosproject.net.PortNumber.portNumber; +import static org.onosproject.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath; + +/** + * Unit tests for the HostToHost intent compiler. + */ +public class MplsIntentCompilerTest extends AbstractIntentTest { + + private static final ApplicationId APPID = new TestApplicationId("foo"); + + private TrafficSelector selector = new IntentTestsMocks.MockSelector(); + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment(); + + /** + * Creates a PointToPoint intent based on ingress and egress device Ids. + * + * @param ingressIdString string for id of ingress device + * @param egressIdString string for id of egress device + * @return PointToPointIntent for the two devices + */ + private MplsIntent makeIntent(String ingressIdString, Optional<MplsLabel> ingressLabel, + String egressIdString, Optional<MplsLabel> egressLabel) { + + return MplsIntent.builder() + .appId(APPID) + .selector(selector) + .treatment(treatment) + .ingressPoint(connectPoint(ingressIdString, 1)) + .ingressLabel(ingressLabel) + .egressPoint(connectPoint(egressIdString, 1)) + .egressLabel(egressLabel).build(); + } + /** + * Creates a compiler for HostToHost intents. + * + * @param hops string array describing the path hops to use when compiling + * @return HostToHost intent compiler + */ + private MplsIntentCompiler makeCompiler(String[] hops) { + MplsIntentCompiler compiler = + new MplsIntentCompiler(); + compiler.pathService = new IntentTestsMocks.MockPathService(hops); + return compiler; + } + + + /** + * Tests a pair of devices in an 8 hop path, forward direction. + */ + @Test + public void testForwardPathCompilation() { + Optional<MplsLabel> ingressLabel = Optional.of(MplsLabel.mplsLabel(10)); + Optional<MplsLabel> egressLabel = Optional.of(MplsLabel.mplsLabel(20)); + + MplsIntent intent = makeIntent("d1", ingressLabel, "d8", egressLabel); + assertThat(intent, is(notNullValue())); + + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"}; + MplsIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(1)); + Intent forwardResultIntent = result.get(0); + assertThat(forwardResultIntent instanceof MplsPathIntent, is(true)); + + // if statement suppresses static analysis warnings about unchecked cast + if (forwardResultIntent instanceof MplsPathIntent) { + MplsPathIntent forwardPathIntent = (MplsPathIntent) forwardResultIntent; + // 7 links for the hops, plus one default lnk on ingress and egress + assertThat(forwardPathIntent.path().links(), hasSize(hops.length + 1)); + assertThat(forwardPathIntent.path().links(), linksHasPath("d1", "d2")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d2", "d3")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d3", "d4")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d4", "d5")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d5", "d6")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d6", "d7")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d7", "d8")); + assertEquals(forwardPathIntent.egressLabel(), egressLabel); + assertEquals(forwardPathIntent.ingressLabel(), ingressLabel); + } + } + + /** + * Tests a pair of devices in an 8 hop path, forward direction. + */ + @Test + public void testReversePathCompilation() { + Optional<MplsLabel> ingressLabel = Optional.of(MplsLabel.mplsLabel(10)); + Optional<MplsLabel> egressLabel = Optional.of(MplsLabel.mplsLabel(20)); + + MplsIntent intent = makeIntent("d8", ingressLabel, "d1", egressLabel); + assertThat(intent, is(notNullValue())); + + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"}; + MplsIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(1)); + Intent reverseResultIntent = result.get(0); + assertThat(reverseResultIntent instanceof MplsPathIntent, is(true)); + + // if statement suppresses static analysis warnings about unchecked cast + if (reverseResultIntent instanceof MplsPathIntent) { + MplsPathIntent reversePathIntent = (MplsPathIntent) reverseResultIntent; + assertThat(reversePathIntent.path().links(), hasSize(hops.length + 1)); + assertThat(reversePathIntent.path().links(), linksHasPath("d2", "d1")); + assertThat(reversePathIntent.path().links(), linksHasPath("d3", "d2")); + assertThat(reversePathIntent.path().links(), linksHasPath("d4", "d3")); + assertThat(reversePathIntent.path().links(), linksHasPath("d5", "d4")); + assertThat(reversePathIntent.path().links(), linksHasPath("d6", "d5")); + assertThat(reversePathIntent.path().links(), linksHasPath("d7", "d6")); + assertThat(reversePathIntent.path().links(), linksHasPath("d8", "d7")); + assertEquals(reversePathIntent.egressLabel(), egressLabel); + assertEquals(reversePathIntent.ingressLabel(), ingressLabel); + } + } + + /** + * Tests compilation of the intent which designates two different ports on the same switch. + */ + @Test + public void testSameSwitchDifferentPortsIntentCompilation() { + ConnectPoint src = new ConnectPoint(deviceId("1"), portNumber(1)); + ConnectPoint dst = new ConnectPoint(deviceId("1"), portNumber(2)); + MplsIntent intent = MplsIntent.builder() + .appId(APP_ID) + .selector(selector) + .treatment(treatment) + .ingressPoint(src) + .ingressLabel(Optional.empty()) + .egressPoint(dst) + .egressLabel(Optional.empty()) + .build(); + + String[] hops = {"1"}; + MplsIntentCompiler sut = makeCompiler(hops); + + List<Intent> compiled = sut.compile(intent, null, null); + + assertThat(compiled, hasSize(1)); + assertThat(compiled.get(0), is(instanceOf(MplsPathIntent.class))); + Path path = ((MplsPathIntent) compiled.get(0)).path(); + + assertThat(path.links(), hasSize(2)); + Link firstLink = path.links().get(0); + assertThat(firstLink, is(createEdgeLink(src, true))); + Link secondLink = path.links().get(1); + assertThat(secondLink, is(createEdgeLink(dst, false))); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java new file mode 100644 index 00000000..771a9883 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java @@ -0,0 +1,142 @@ +/* + * 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.compiler; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.MplsLabel; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultLink; +import org.onosproject.net.DefaultPath; +import org.onosproject.net.Link; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentExtensionService; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.MockIdGenerator; +import org.onosproject.net.intent.MplsPathIntent; +import org.onosproject.store.trivial.SimpleLinkStore; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.PID; +import static org.onosproject.net.NetTestTools.connectPoint; + +public class MplsPathIntentCompilerTest { + + private final ApplicationId appId = new TestApplicationId("test"); + + private final ConnectPoint d1p1 = connectPoint("s1", 0); + private final ConnectPoint d2p0 = connectPoint("s2", 0); + private final ConnectPoint d2p1 = connectPoint("s2", 1); + private final ConnectPoint d3p1 = connectPoint("s3", 1); + + private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); + private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + private final Optional<MplsLabel> ingressLabel = + Optional.of(MplsLabel.mplsLabel(10)); + private final Optional<MplsLabel> egressLabel = + Optional.of(MplsLabel.mplsLabel(20)); + + private final List<Link> links = Arrays.asList( + new DefaultLink(PID, d1p1, d2p0, DIRECT), + new DefaultLink(PID, d2p1, d3p1, DIRECT) + ); + + private IdGenerator idGenerator = new MockIdGenerator(); + + private final int hops = links.size() - 1; + private MplsPathIntent intent; + private MplsPathIntentCompiler sut; + + @Before + public void setUp() { + sut = new MplsPathIntentCompiler(); + CoreService coreService = createMock(CoreService.class); + expect(coreService.registerApplication("org.onosproject.net.intent")) + .andReturn(appId); + sut.coreService = coreService; + sut.linkStore = new SimpleLinkStore(); + sut.resourceService = new IntentTestsMocks.MockResourceService(); + + Intent.bindIdGenerator(idGenerator); + + intent = MplsPathIntent.builder() + .appId(APP_ID) + .selector(selector) + .treatment(treatment) + .path(new DefaultPath(PID, links, hops)) + .ingressLabel(ingressLabel) + .egressLabel(egressLabel) + .priority(55) + .build(); + + IntentExtensionService intentExtensionService = createMock(IntentExtensionService.class); + intentExtensionService.registerCompiler(MplsPathIntent.class, sut); + intentExtensionService.unregisterCompiler(MplsPathIntent.class); + sut.intentExtensionService = intentExtensionService; + + replay(coreService, intentExtensionService); + } + + @After + public void tearDown() { + Intent.unbindIdGenerator(idGenerator); + } + + @Test + public void testCompile() { + sut.activate(); + + List<Intent> compiled = sut.compile(intent, Collections.emptyList(), Collections.emptySet()); + assertThat(compiled, hasSize(1)); + + Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); + assertThat(rules, hasSize(1)); + + FlowRule rule = rules.stream() + .filter(x -> x.deviceId().equals(d2p0.deviceId())) + .findFirst() + .get(); + assertThat(rule.deviceId(), is(d2p0.deviceId())); + + sut.deactivate(); + + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java new file mode 100644 index 00000000..eb7a3936 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java @@ -0,0 +1,276 @@ +/* + * Copyright 2014-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.compiler; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.ElementId; +import org.onosproject.net.Path; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.AbstractIntentTest; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.LinkCollectionIntent; +import org.onosproject.net.intent.MultiPointToSinglePointIntent; +import org.onosproject.net.topology.LinkWeight; +import org.onosproject.net.topology.PathService; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.NetTestTools.connectPoint; +import static org.onosproject.net.NetTestTools.createPath; +import static org.onosproject.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath; + +/** + * Unit tests for the MultiPointToSinglePoint intent compiler. + */ +public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTest { + + private static final ApplicationId APPID = new TestApplicationId("foo"); + + private TrafficSelector selector = new IntentTestsMocks.MockSelector(); + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment(); + + /** + * Mock path service for creating paths within the test. + */ + private static class MockPathService implements PathService { + + final String[] pathHops; + + /** + * Constructor that provides a set of hops to mock. + * + * @param pathHops path hops to mock + */ + MockPathService(String[] pathHops) { + this.pathHops = pathHops; + } + + @Override + public Set<Path> getPaths(ElementId src, ElementId dst) { + Set<Path> result = new HashSet<>(); + + String[] allHops = new String[pathHops.length + 1]; + allHops[0] = src.toString(); + if (pathHops.length != 0) { + System.arraycopy(pathHops, 0, allHops, 1, pathHops.length); + } + result.add(createPath(allHops)); + + return result; + } + + @Override + public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) { + return null; + } + } + + /** + * Mocks the device service so that a device appears available in the test. + */ + private static class MockDeviceService extends DeviceServiceAdapter { + @Override + public boolean isAvailable(DeviceId deviceId) { + return true; + } + } + + /** + * Creates a MultiPointToSinglePoint intent for a group of ingress points + * and an egress point. + * + * @param ingressIds array of ingress device ids + * @param egressId device id of the egress point + * @return MultiPointToSinglePoint intent + */ + private MultiPointToSinglePointIntent makeIntent(String[] ingressIds, String egressId) { + Set<ConnectPoint> ingressPoints = new HashSet<>(); + ConnectPoint egressPoint = connectPoint(egressId, 2); + + for (String ingressId : ingressIds) { + ingressPoints.add(connectPoint(ingressId, 1)); + } + + return MultiPointToSinglePointIntent.builder() + .appId(APPID) + .selector(selector) + .treatment(treatment) + .ingressPoints(ingressPoints) + .egressPoint(egressPoint) + .build(); + } + + /** + * Creates a compiler for MultiPointToSinglePoint intents. + * + * @param hops hops to use while computing paths for this intent + * @return MultiPointToSinglePoint intent + */ + private MultiPointToSinglePointIntentCompiler makeCompiler(String[] hops) { + MultiPointToSinglePointIntentCompiler compiler = + new MultiPointToSinglePointIntentCompiler(); + compiler.pathService = new MockPathService(hops); + compiler.deviceService = new MockDeviceService(); + return compiler; + } + + /** + * Tests a single ingress point with 8 hops to its egress point. + */ + @Test + public void testSingleLongPathCompilation() { + + String[] ingress = {"ingress"}; + String egress = "egress"; + + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress); + assertThat(intent, is(notNullValue())); + + String[] hops = {"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", + egress}; + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(1)); + Intent resultIntent = result.get(0); + assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); + + if (resultIntent instanceof LinkCollectionIntent) { + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; + assertThat(linkIntent.links(), hasSize(9)); + assertThat(linkIntent.links(), linksHasPath("ingress", "h1")); + assertThat(linkIntent.links(), linksHasPath("h1", "h2")); + assertThat(linkIntent.links(), linksHasPath("h2", "h3")); + assertThat(linkIntent.links(), linksHasPath("h4", "h5")); + assertThat(linkIntent.links(), linksHasPath("h5", "h6")); + assertThat(linkIntent.links(), linksHasPath("h7", "h8")); + assertThat(linkIntent.links(), linksHasPath("h8", "egress")); + } + } + + /** + * Tests a simple topology where two ingress points share some path segments + * and some path segments are not shared. + */ + @Test + public void testTwoIngressCompilation() { + String[] ingress = {"ingress1", "ingress2"}; + String egress = "egress"; + + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress); + assertThat(intent, is(notNullValue())); + + final String[] hops = {"inner1", "inner2", egress}; + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(notNullValue())); + assertThat(result, hasSize(1)); + Intent resultIntent = result.get(0); + assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); + + if (resultIntent instanceof LinkCollectionIntent) { + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; + assertThat(linkIntent.links(), hasSize(4)); + assertThat(linkIntent.links(), linksHasPath("ingress1", "inner1")); + assertThat(linkIntent.links(), linksHasPath("ingress2", "inner1")); + assertThat(linkIntent.links(), linksHasPath("inner1", "inner2")); + assertThat(linkIntent.links(), linksHasPath("inner2", "egress")); + } + } + + /** + * Tests a large number of ingress points that share a common path to the + * egress point. + */ + @Test + public void testMultiIngressCompilation() { + String[] ingress = {"i1", "i2", "i3", "i4", "i5", + "i6", "i7", "i8", "i9", "i10"}; + String egress = "e"; + + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress); + assertThat(intent, is(notNullValue())); + + final String[] hops = {"n1", egress}; + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(notNullValue())); + assertThat(result, hasSize(1)); + Intent resultIntent = result.get(0); + assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); + + if (resultIntent instanceof LinkCollectionIntent) { + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; + assertThat(linkIntent.links(), hasSize(ingress.length + 1)); + for (String ingressToCheck : ingress) { + assertThat(linkIntent.links(), + linksHasPath(ingressToCheck, + "n1")); + } + assertThat(linkIntent.links(), linksHasPath("n1", egress)); + } + } + + /** + * Tests ingress and egress on the same device. + */ + @Test + public void testSameDeviceCompilation() { + String[] ingress = {"i1", "i2"}; + String egress = "i1"; + + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress); + assertThat(intent, is(notNullValue())); + + final String[] hops = {"i1", "i2"}; + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); + assertThat(compiler, is(notNullValue())); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(notNullValue())); + assertThat(result, hasSize(1)); + Intent resultIntent = result.get(0); + assertThat(resultIntent, instanceOf(LinkCollectionIntent.class)); + + if (resultIntent instanceof LinkCollectionIntent) { + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; + assertThat(linkIntent.links(), hasSize(ingress.length)); + + assertThat(linkIntent.links(), linksHasPath("i2", "i1")); + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java new file mode 100644 index 00000000..2f40b37a --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java @@ -0,0 +1,147 @@ +/* + * 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.compiler; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultLink; +import org.onosproject.net.DefaultPath; +import org.onosproject.net.Link; +import org.onosproject.net.OchSignalType; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentExtensionService; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.MockIdGenerator; +import org.onosproject.net.intent.OpticalPathIntent; +import org.onosproject.net.provider.ProviderId; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.NetTestTools.PID; +import static org.onosproject.net.NetTestTools.connectPoint; +import static org.onosproject.net.NetTestTools.createLambda; + +public class OpticalPathIntentCompilerTest { + + private CoreService coreService; + private IntentExtensionService intentExtensionService; + private final IdGenerator idGenerator = new MockIdGenerator(); + private OpticalPathIntentCompiler sut; + + private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); + private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + private final ApplicationId appId = new TestApplicationId("test"); + private final ProviderId pid = new ProviderId("of", "test"); + private final ConnectPoint d1p1 = connectPoint("s1", 0); + private final ConnectPoint d2p0 = connectPoint("s2", 0); + private final ConnectPoint d2p1 = connectPoint("s2", 1); + private final ConnectPoint d3p1 = connectPoint("s3", 1); + private final ConnectPoint d3p0 = connectPoint("s3", 10); + private final ConnectPoint d1p0 = connectPoint("s1", 10); + + private final List<Link> links = Arrays.asList( + new DefaultLink(PID, d1p1, d2p0, DIRECT), + new DefaultLink(PID, d2p1, d3p1, DIRECT) + ); + private final int hops = links.size() + 1; + private OpticalPathIntent intent; + + @Before + public void setUp() { + sut = new OpticalPathIntentCompiler(); + coreService = createMock(CoreService.class); + expect(coreService.registerApplication("org.onosproject.net.intent")) + .andReturn(appId); + sut.coreService = coreService; + + Intent.bindIdGenerator(idGenerator); + + intent = OpticalPathIntent.builder() + .appId(appId) + .src(d1p1) + .dst(d3p1) + .path(new DefaultPath(PID, links, hops)) + .lambda(createLambda()) + .signalType(OchSignalType.FIXED_GRID) + .build(); + intentExtensionService = createMock(IntentExtensionService.class); + intentExtensionService.registerCompiler(OpticalPathIntent.class, sut); + intentExtensionService.unregisterCompiler(OpticalPathIntent.class); + sut.intentManager = intentExtensionService; + sut.resourceService = new IntentTestsMocks.MockResourceService(); + + replay(coreService, intentExtensionService); + } + + @After + public void tearDown() { + Intent.unbindIdGenerator(idGenerator); + } + + @Test + public void testCompiler() { + sut.activate(); + + List<Intent> compiled = sut.compile(intent, Collections.emptyList(), Collections.emptySet()); + assertThat(compiled, hasSize(1)); + + Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); + rules.stream() + .filter(x -> x.deviceId().equals(d1p1.deviceId())) + .findFirst() + .get(); + + rules.stream() + .filter(x -> x.deviceId().equals(d2p1.deviceId())) + .findFirst() + .get(); + + rules.stream() + .filter(x -> x.deviceId().equals(d3p1.deviceId())) + .findFirst() + .get(); + + rules.forEach(rule -> assertEquals("FlowRule priority is incorrect", + intent.priority(), rule.priority())); + + sut.deactivate(); + } + + //TODO test bidirectional optical paths and verify rules + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java new file mode 100644 index 00000000..f07bf42c --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java @@ -0,0 +1,172 @@ +/* + * 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.compiler; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultLink; +import org.onosproject.net.DefaultPath; +import org.onosproject.net.Link; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentExtensionService; +import org.onosproject.net.intent.MockIdGenerator; +import org.onosproject.net.intent.PathIntent; +import org.onosproject.net.provider.ProviderId; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.PID; +import static org.onosproject.net.NetTestTools.connectPoint; + +/** + * Unit tests for PathIntentCompiler. + */ +public class PathIntentCompilerTest { + + private CoreService coreService; + private IntentExtensionService intentExtensionService; + private IdGenerator idGenerator = new MockIdGenerator(); + private PathIntentCompiler sut; + + private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); + private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + private final ApplicationId appId = new TestApplicationId("test"); + private final ProviderId pid = new ProviderId("of", "test"); + private final ConnectPoint d1p1 = connectPoint("s1", 0); + private final ConnectPoint d2p0 = connectPoint("s2", 0); + private final ConnectPoint d2p1 = connectPoint("s2", 1); + private final ConnectPoint d3p1 = connectPoint("s3", 1); + private final ConnectPoint d3p0 = connectPoint("s3", 10); + private final ConnectPoint d1p0 = connectPoint("s1", 10); + private static final int PRIORITY = 555; + + private final List<Link> links = Arrays.asList( + createEdgeLink(d1p0, true), + new DefaultLink(PID, d1p1, d2p0, DIRECT), + new DefaultLink(PID, d2p1, d3p1, DIRECT), + createEdgeLink(d3p0, false) + ); + private final int hops = links.size() - 1; + private PathIntent intent; + + /** + * Configures objects used in all the test cases. + */ + @Before + public void setUp() { + sut = new PathIntentCompiler(); + coreService = createMock(CoreService.class); + expect(coreService.registerApplication("org.onosproject.net.intent")) + .andReturn(appId); + sut.coreService = coreService; + + Intent.bindIdGenerator(idGenerator); + + intent = PathIntent.builder() + .appId(APP_ID) + .selector(selector) + .treatment(treatment) + .priority(PRIORITY) + .path(new DefaultPath(pid, links, hops)) + .build(); + intentExtensionService = createMock(IntentExtensionService.class); + intentExtensionService.registerCompiler(PathIntent.class, sut); + intentExtensionService.unregisterCompiler(PathIntent.class); + sut.intentManager = intentExtensionService; + + replay(coreService, intentExtensionService); + } + + /** + * Tears down objects used in all the test cases. + */ + @After + public void tearDown() { + Intent.unbindIdGenerator(idGenerator); + } + + /** + * Tests the compilation behavior of the path intent compiler. + */ + @Test + public void testCompile() { + sut.activate(); + + List<Intent> compiled = sut.compile(intent, Collections.emptyList(), Collections.emptySet()); + assertThat(compiled, hasSize(1)); + + Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); + + FlowRule rule1 = rules.stream() + .filter(x -> x.deviceId().equals(d1p0.deviceId())) + .findFirst() + .get(); + assertThat(rule1.deviceId(), is(d1p0.deviceId())); + assertThat(rule1.selector(), + is(DefaultTrafficSelector.builder(selector).matchInPort(d1p0.port()).build())); + assertThat(rule1.treatment(), + is(DefaultTrafficTreatment.builder().setOutput(d1p1.port()).build())); + assertThat(rule1.priority(), is(intent.priority())); + + FlowRule rule2 = rules.stream() + .filter(x -> x.deviceId().equals(d2p0.deviceId())) + .findFirst() + .get(); + assertThat(rule2.deviceId(), is(d2p0.deviceId())); + assertThat(rule2.selector(), + is(DefaultTrafficSelector.builder(selector).matchInPort(d2p0.port()).build())); + assertThat(rule2.treatment(), + is(DefaultTrafficTreatment.builder().setOutput(d2p1.port()).build())); + assertThat(rule2.priority(), is(intent.priority())); + + FlowRule rule3 = rules.stream() + .filter(x -> x.deviceId().equals(d3p0.deviceId())) + .findFirst() + .get(); + assertThat(rule3.deviceId(), is(d3p1.deviceId())); + assertThat(rule3.selector(), + is(DefaultTrafficSelector.builder(selector).matchInPort(d3p1.port()).build())); + assertThat(rule3.treatment(), + is(DefaultTrafficTreatment.builder(treatment).setOutput(d3p0.port()).build())); + assertThat(rule3.priority(), is(intent.priority())); + + sut.deactivate(); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java new file mode 100644 index 00000000..e57d9dbe --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java @@ -0,0 +1,320 @@ +/* + * Copyright 2014-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.compiler; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.onlab.util.Bandwidth; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Link; +import org.onosproject.net.Path; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.AbstractIntentTest; +import org.onosproject.net.intent.Constraint; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentTestsMocks; +import org.onosproject.net.intent.PathIntent; +import org.onosproject.net.intent.PointToPointIntent; +import org.onosproject.net.intent.constraint.BandwidthConstraint; +import org.onosproject.net.intent.constraint.LambdaConstraint; +import org.onosproject.net.intent.impl.PathNotFoundException; +import org.onosproject.net.resource.link.BandwidthResource; +import org.onosproject.net.resource.link.LambdaResource; +import org.onosproject.net.resource.link.LinkResourceService; + +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.NetTestTools.APP_ID; +import static org.onosproject.net.NetTestTools.connectPoint; +import static org.onosproject.net.PortNumber.portNumber; +import static org.onosproject.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath; + +/** + * Unit tests for the HostToHost intent compiler. + */ +public class PointToPointIntentCompilerTest extends AbstractIntentTest { + + private static final ApplicationId APPID = new TestApplicationId("foo"); + + private TrafficSelector selector = new IntentTestsMocks.MockSelector(); + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment(); + + /** + * Creates a PointToPoint intent based on ingress and egress device Ids. + * + * @param ingressIdString string for id of ingress device + * @param egressIdString string for id of egress device + * @return PointToPointIntent for the two devices + */ + private PointToPointIntent makeIntent(String ingressIdString, + String egressIdString) { + return PointToPointIntent.builder() + .appId(APPID) + .selector(selector) + .treatment(treatment) + .ingressPoint(connectPoint(ingressIdString, 1)) + .egressPoint(connectPoint(egressIdString, 1)) + .build(); + } + + /** + * Creates a PointToPoint intent based on ingress and egress deviceIds and constraints. + * + * @param ingressIdString string for id of ingress device + * @param egressIdString string for id of egress device + * @param constraints constraints + * @return PointToPointIntent for the two device with constraints + */ + private PointToPointIntent makeIntent(String ingressIdString, + String egressIdString, List<Constraint> constraints) { + return PointToPointIntent.builder() + .appId(APPID) + .selector(selector) + .treatment(treatment) + .ingressPoint(connectPoint(ingressIdString, 1)) + .egressPoint(connectPoint(egressIdString, 1)) + .constraints(constraints) + .build(); + } + + /** + * Creates a compiler for HostToHost intents. + * + * @param hops string array describing the path hops to use when compiling + * @return HostToHost intent compiler + */ + private PointToPointIntentCompiler makeCompiler(String[] hops) { + PointToPointIntentCompiler compiler = new PointToPointIntentCompiler(); + compiler.pathService = new IntentTestsMocks.MockPathService(hops); + return compiler; + } + + /** + * Creates a point to point intent compiler for a three switch linear + * topology. + * + * @param resourceService service to use for resource allocation requests + * @return point to point compiler + */ + private PointToPointIntentCompiler makeCompiler(String[] hops, LinkResourceService resourceService) { + final PointToPointIntentCompiler compiler = new PointToPointIntentCompiler(); + compiler.resourceService = resourceService; + compiler.pathService = new IntentTestsMocks.MockPathService(hops); + return compiler; + } + + /** + * Tests a pair of devices in an 8 hop path, forward direction. + */ + @Test + public void testForwardPathCompilation() { + + PointToPointIntent intent = makeIntent("d1", "d8"); + + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"}; + PointToPointIntentCompiler compiler = makeCompiler(hops); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(1)); + Intent forwardResultIntent = result.get(0); + assertThat(forwardResultIntent instanceof PathIntent, is(true)); + + if (forwardResultIntent instanceof PathIntent) { + PathIntent forwardPathIntent = (PathIntent) forwardResultIntent; + // 7 links for the hops, plus one default lnk on ingress and egress + assertThat(forwardPathIntent.path().links(), hasSize(hops.length + 1)); + assertThat(forwardPathIntent.path().links(), linksHasPath("d1", "d2")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d2", "d3")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d3", "d4")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d4", "d5")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d5", "d6")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d6", "d7")); + assertThat(forwardPathIntent.path().links(), linksHasPath("d7", "d8")); + } + } + + /** + * Tests a pair of devices in an 8 hop path, forward direction. + */ + @Test + public void testReversePathCompilation() { + + PointToPointIntent intent = makeIntent("d8", "d1"); + + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"}; + PointToPointIntentCompiler compiler = makeCompiler(hops); + + List<Intent> result = compiler.compile(intent, null, null); + assertThat(result, is(Matchers.notNullValue())); + assertThat(result, hasSize(1)); + Intent reverseResultIntent = result.get(0); + assertThat(reverseResultIntent instanceof PathIntent, is(true)); + + if (reverseResultIntent instanceof PathIntent) { + PathIntent reversePathIntent = (PathIntent) reverseResultIntent; + assertThat(reversePathIntent.path().links(), hasSize(hops.length + 1)); + assertThat(reversePathIntent.path().links(), linksHasPath("d2", "d1")); + assertThat(reversePathIntent.path().links(), linksHasPath("d3", "d2")); + assertThat(reversePathIntent.path().links(), linksHasPath("d4", "d3")); + assertThat(reversePathIntent.path().links(), linksHasPath("d5", "d4")); + assertThat(reversePathIntent.path().links(), linksHasPath("d6", "d5")); + assertThat(reversePathIntent.path().links(), linksHasPath("d7", "d6")); + assertThat(reversePathIntent.path().links(), linksHasPath("d8", "d7")); + } + } + + /** + * Tests compilation of the intent which designates two different ports on the same switch. + */ + @Test + public void testSameSwitchDifferentPortsIntentCompilation() { + ConnectPoint src = new ConnectPoint(deviceId("1"), portNumber(1)); + ConnectPoint dst = new ConnectPoint(deviceId("1"), portNumber(2)); + PointToPointIntent intent = PointToPointIntent.builder() + .appId(APP_ID) + .selector(selector) + .treatment(treatment) + .ingressPoint(src) + .egressPoint(dst) + .build(); + + String[] hops = {"1"}; + PointToPointIntentCompiler sut = makeCompiler(hops); + + List<Intent> compiled = sut.compile(intent, null, null); + + assertThat(compiled, hasSize(1)); + assertThat(compiled.get(0), is(instanceOf(PathIntent.class))); + Path path = ((PathIntent) compiled.get(0)).path(); + + assertThat(path.links(), hasSize(2)); + Link firstLink = path.links().get(0); + assertThat(firstLink, is(createEdgeLink(src, true))); + Link secondLink = path.links().get(1); + assertThat(secondLink, is(createEdgeLink(dst, false))); + } + + /** + * Tests that requests with sufficient available bandwidth succeed. + */ + @Test + public void testBandwidthConstrainedIntentSuccess() { + + final LinkResourceService resourceService = + IntentTestsMocks.MockResourceService.makeBandwidthResourceService(1000.0); + final List<Constraint> constraints = + Collections.singletonList(new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(100.0)))); + + final PointToPointIntent intent = makeIntent("s1", "s3", constraints); + + String[] hops = {"s1", "s2", "s3"}; + final PointToPointIntentCompiler compiler = makeCompiler(hops, resourceService); + + final List<Intent> compiledIntents = compiler.compile(intent, null, null); + + assertThat(compiledIntents, Matchers.notNullValue()); + assertThat(compiledIntents, hasSize(1)); + } + + /** + * Tests that requests with insufficient available bandwidth fail. + */ + @Test + public void testBandwidthConstrainedIntentFailure() { + + final LinkResourceService resourceService = + IntentTestsMocks.MockResourceService.makeBandwidthResourceService(10.0); + final List<Constraint> constraints = + Collections.singletonList(new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(100.0)))); + + try { + final PointToPointIntent intent = makeIntent("s1", "s3", constraints); + + String[] hops = {"s1", "s2", "s3"}; + final PointToPointIntentCompiler compiler = makeCompiler(hops, resourceService); + + compiler.compile(intent, null, null); + + fail("Point to Point compilation with insufficient bandwidth does " + + "not throw exception."); + } catch (PathNotFoundException noPath) { + assertThat(noPath.getMessage(), containsString("No path")); + } + } + + /** + * Tests that requests for available lambdas are successful. + */ + @Test + public void testLambdaConstrainedIntentSuccess() { + + final List<Constraint> constraints = + Collections.singletonList(new LambdaConstraint(LambdaResource.valueOf(1))); + final LinkResourceService resourceService = + IntentTestsMocks.MockResourceService.makeLambdaResourceService(1); + + final PointToPointIntent intent = makeIntent("s1", "s3", constraints); + + String[] hops = {"s1", "s2", "s3"}; + final PointToPointIntentCompiler compiler = makeCompiler(hops, resourceService); + + final List<Intent> compiledIntents = + compiler.compile(intent, null, null); + + assertThat(compiledIntents, Matchers.notNullValue()); + assertThat(compiledIntents, hasSize(1)); + } + + /** + * Tests that requests for lambdas when there are no available lambdas + * fail. + */ + @Test + public void testLambdaConstrainedIntentFailure() { + + final List<Constraint> constraints = + Collections.singletonList(new LambdaConstraint(LambdaResource.valueOf(1))); + final LinkResourceService resourceService = + IntentTestsMocks.MockResourceService.makeBandwidthResourceService(10.0); + try { + final PointToPointIntent intent = makeIntent("s1", "s3", constraints); + + String[] hops = {"s1", "s2", "s3"}; + final PointToPointIntentCompiler compiler = makeCompiler(hops, resourceService); + + compiler.compile(intent, null, null); + + fail("Point to Point compilation with no available lambda does " + + "not throw exception."); + } catch (PathNotFoundException noPath) { + assertThat(noPath.getMessage(), containsString("No path")); + } + } + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/phase/CompilingTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/phase/CompilingTest.java new file mode 100644 index 00000000..c15ecaec --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/phase/CompilingTest.java @@ -0,0 +1,149 @@ +/* + * 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.phase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.IdGenerator; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultLink; +import org.onosproject.net.DefaultPath; +import org.onosproject.net.Link; +import org.onosproject.net.Path; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentData; +import org.onosproject.net.intent.MockIdGenerator; +import org.onosproject.net.intent.PathIntent; +import org.onosproject.net.intent.PointToPointIntent; +import org.onosproject.net.intent.impl.IntentCompilationException; +import org.onosproject.net.intent.impl.IntentProcessor; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.Timestamp; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.PortNumber.portNumber; +import static org.onosproject.net.intent.IntentState.INSTALL_REQ; + +/** + * Unit tests for Compiling phase. + */ +public class CompilingTest { + + private final ApplicationId appId = new TestApplicationId("test"); + private final ProviderId pid = new ProviderId("of", "test"); + private final TrafficSelector selector = DefaultTrafficSelector.emptySelector(); + private final TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment(); + private final ConnectPoint cp1 = new ConnectPoint(deviceId("1"), portNumber(1)); + private final ConnectPoint cp2 = new ConnectPoint(deviceId("1"), portNumber(2)); + private final ConnectPoint cp3 = new ConnectPoint(deviceId("2"), portNumber(1)); + private final ConnectPoint cp4 = new ConnectPoint(deviceId("2"), portNumber(2)); + + private final List<Link> links = Collections.singletonList(new DefaultLink(pid, cp2, cp4, DIRECT)); + private final Path path = new DefaultPath(pid, links, 10); + + private PointToPointIntent input; + private PathIntent compiled; + + private IdGenerator idGenerator; + private IntentProcessor processor; + private Timestamp version; + + @Before + public void setUp() { + processor = createMock(IntentProcessor.class); + version = createMock(Timestamp.class); + + idGenerator = new MockIdGenerator(); + + Intent.bindIdGenerator(idGenerator); + + // Intent creation should be placed after binding an ID generator + input = PointToPointIntent.builder() + .appId(appId) + .selector(selector) + .treatment(treatment) + .ingressPoint(cp1) + .egressPoint(cp3) + .build(); + compiled = PathIntent.builder() + .appId(appId) + .selector(selector) + .treatment(treatment) + .path(path) + .build(); + } + + + @After + public void tearDown() { + Intent.unbindIdGenerator(idGenerator); + } + + /** + * Tests a next phase when no exception occurs. + */ + @Test + public void testMoveToNextPhaseWithoutError() { + IntentData pending = new IntentData(input, INSTALL_REQ, version); + + expect(processor.compile(input, null)).andReturn(Collections.singletonList(compiled)); + replay(processor); + + Compiling sut = new Compiling(processor, pending, Optional.empty()); + + Optional<IntentProcessPhase> output = sut.execute(); + + verify(processor); + assertThat(output.get(), is(instanceOf(Installing.class))); + } + + /** + * Tests a next phase when IntentCompilationException occurs. + */ + @Test + public void testWhenIntentCompilationExceptionOccurs() { + IntentData pending = new IntentData(input, INSTALL_REQ, version); + + expect(processor.compile(input, null)).andThrow(new IntentCompilationException()); + replay(processor); + + Compiling sut = new Compiling(processor, pending, Optional.empty()); + + Optional<IntentProcessPhase> output = sut.execute(); + + verify(processor); + assertThat(output.get(), is(instanceOf(Failed.class))); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java new file mode 100644 index 00000000..fe9e37cd --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2014-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.link.impl; + +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.PortNumber.portNumber; +import static org.junit.Assert.assertEquals; + +import java.time.Duration; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; +import org.onosproject.net.config.basics.BasicLinkConfig; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; +import org.onosproject.net.PortNumber; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.link.DefaultLinkDescription; +import org.onosproject.net.link.LinkDescription; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +public class BasicLinkOperatorTest { + + private static final DeviceId DID1 = deviceId("of:foo"); + private static final DeviceId DID2 = deviceId("of:bar"); + private static final PortNumber P1 = portNumber(1); + + private static final ConnectPoint SRC = new ConnectPoint(DID1, P1); + private static final ConnectPoint DST = new ConnectPoint(DID2, P1); + private static final LinkKey LK = LinkKey.linkKey(SRC, DST); + private static final Duration NTIME = Duration.ofNanos(200); + + private static final SparseAnnotations SA = DefaultAnnotations.builder() + .set(AnnotationKeys.DURABLE, "true").build(); + private static final LinkDescription LD = new DefaultLinkDescription(SRC, DST, Link.Type.DIRECT, SA); + private final ConfigApplyDelegate delegate = new ConfigApplyDelegate() { + @Override + public void onApply(Config config) { + } + }; + private final ObjectMapper mapper = new ObjectMapper(); + + private static final BasicLinkConfig BLC = new BasicLinkConfig(); + + @Before + public void setUp() { + BLC.init(LK, "optest", JsonNodeFactory.instance.objectNode(), mapper, delegate); + BLC.latency(NTIME); + } + + @Test + public void testDescOps() { + LinkDescription desc = BasicLinkOperator.combine(BLC, LD); + assertEquals(NTIME.toString(), desc.annotations().value(AnnotationKeys.LATENCY)); + assertEquals("true", desc.annotations().value(AnnotationKeys.DURABLE)); + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java new file mode 100644 index 00000000..dad5429e --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2014 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.link.impl; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.event.Event; +import org.onosproject.net.config.NetworkConfigServiceAdapter; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Link; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.PortNumber; +import org.onosproject.net.link.DefaultLinkDescription; +import org.onosproject.net.link.LinkAdminService; +import org.onosproject.net.link.LinkEvent; +import org.onosproject.net.link.LinkListener; +import org.onosproject.net.link.LinkProvider; +import org.onosproject.net.link.LinkProviderRegistry; +import org.onosproject.net.link.LinkProviderService; +import org.onosproject.net.link.LinkService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.device.impl.DeviceManager; +import org.onosproject.store.trivial.SimpleLinkStore; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.*; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.Link.Type.INDIRECT; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; +import static org.onosproject.net.link.LinkEvent.Type.*; + +/** + * Test codifying the link service & link provider service contracts. + */ +public class LinkManagerTest { + + private static final ProviderId PID = new ProviderId("of", "foo"); + private static final DeviceId DID1 = deviceId("of:foo"); + private static final DeviceId DID2 = deviceId("of:bar"); + private static final DeviceId DID3 = deviceId("of:goo"); + private static final Device DEV1 = new DefaultDevice( + PID, DID1, Device.Type.SWITCH, "", "", "", "", null); + private static final Device DEV2 = new DefaultDevice( + PID, DID2, Device.Type.SWITCH, "", "", "", "", null); + private static final Device DEV3 = new DefaultDevice( + PID, DID2, Device.Type.SWITCH, "", "", "", "", null); + + private static final PortNumber P1 = PortNumber.portNumber(1); + private static final PortNumber P2 = PortNumber.portNumber(2); + private static final PortNumber P3 = PortNumber.portNumber(3); + private static final Map<DeviceId, Device> DEVICEIDMAP = new HashMap<>(); + + private LinkManager mgr; + + protected LinkService service; + protected LinkAdminService admin; + protected LinkProviderRegistry registry; + protected LinkProviderService providerService; + protected TestProvider provider; + protected TestListener listener = new TestListener(); + protected DeviceManager devmgr = new TestDeviceManager(); + + + + @Before + public void setUp() { + mgr = new LinkManager(); + service = mgr; + admin = mgr; + registry = mgr; + mgr.store = new SimpleLinkStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + mgr.deviceService = devmgr; + mgr.networkConfigService = new TestNetworkConfigService(); + mgr.activate(); + + DEVICEIDMAP.put(DID1, DEV1); + DEVICEIDMAP.put(DID2, DEV2); + DEVICEIDMAP.put(DID3, DEV3); + + service.addListener(listener); + + provider = new TestProvider(); + providerService = registry.register(provider); + assertTrue("provider should be registered", + registry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + registry.unregister(provider); + assertFalse("provider should not be registered", + registry.getProviders().contains(provider.id())); + service.removeListener(listener); + mgr.deactivate(); + } + + @Test + public void createLink() { + addLink(DID1, P1, DID2, P2, DIRECT); + addLink(DID2, P2, DID1, P1, DIRECT); + assertEquals("incorrect link count", 2, service.getLinkCount()); + + Iterator<Link> it = service.getLinks().iterator(); + it.next(); + it.next(); + assertFalse("incorrect link count", it.hasNext()); + } + + @Test + public void updateLink() { + addLink(DID1, P1, DID2, P2, DIRECT); + addLink(DID2, P2, DID1, P1, INDIRECT); + assertEquals("incorrect link count", 2, service.getLinkCount()); + + providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), DIRECT)); + validateEvents(LINK_UPDATED); + assertEquals("incorrect link count", 2, service.getLinkCount()); + + providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), INDIRECT)); + providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), DIRECT)); + assertEquals("no events expected", 0, listener.events.size()); + } + + @Test + public void removeLink() { + addLink(DID1, P1, DID2, P2, DIRECT); + addLink(DID2, P2, DID1, P1, DIRECT); + assertEquals("incorrect link count", 2, service.getLinkCount()); + + providerService.linkVanished(new DefaultLinkDescription(cp(DID1, P1), cp(DID2, P2), DIRECT)); + validateEvents(LINK_REMOVED); + assertEquals("incorrect link count", 1, service.getLinkCount()); + assertNull("link should not be found", service.getLink(cp(DID1, P1), cp(DID2, P2))); + assertNotNull("link should be found", service.getLink(cp(DID2, P2), cp(DID1, P1))); + + providerService.linkVanished(new DefaultLinkDescription(cp(DID1, P1), cp(DID2, P2), DIRECT)); + assertEquals("no events expected", 0, listener.events.size()); + } + + @Test + public void removeLinksByConnectionPoint() { + Link l1 = addLink(DID1, P1, DID2, P2, DIRECT); + Link l2 = addLink(DID2, P2, DID1, P1, DIRECT); + addLink(DID3, P3, DID2, P1, DIRECT); + addLink(DID2, P1, DID3, P3, DIRECT); + assertEquals("incorrect link count", 4, service.getLinkCount()); + + providerService.linksVanished(cp(DID1, P1)); + assertEquals("incorrect link count", 2, service.getLinkCount()); + assertNull("link should be gone", service.getLink(l1.src(), l1.dst())); + assertNull("link should be gone", service.getLink(l2.src(), l2.dst())); + } + + @Test + public void removeLinksByDevice() { + addLink(DID1, P1, DID2, P2, DIRECT); + addLink(DID2, P2, DID1, P1, DIRECT); + addLink(DID3, P3, DID2, P1, DIRECT); + addLink(DID2, P1, DID3, P3, DIRECT); + Link l5 = addLink(DID3, P1, DID1, P2, DIRECT); + Link l6 = addLink(DID1, P2, DID3, P1, DIRECT); + assertEquals("incorrect link count", 6, service.getLinkCount()); + + providerService.linksVanished(DID2); + assertEquals("incorrect link count", 2, service.getLinkCount()); + assertNotNull("link should not be gone", service.getLink(l5.src(), l5.dst())); + assertNotNull("link should not be gone", service.getLink(l6.src(), l6.dst())); + } + + @Test + public void removeLinksAsAdminByConnectionPoint() { + Link l1 = addLink(DID1, P1, DID2, P2, DIRECT); + Link l2 = addLink(DID2, P2, DID1, P1, DIRECT); + addLink(DID3, P3, DID2, P1, DIRECT); + addLink(DID2, P1, DID3, P3, DIRECT); + assertEquals("incorrect link count", 4, service.getLinkCount()); + + admin.removeLinks(cp(DID1, P1)); + assertEquals("incorrect link count", 2, service.getLinkCount()); + assertNull("link should be gone", service.getLink(l1.src(), l1.dst())); + assertNull("link should be gone", service.getLink(l2.src(), l2.dst())); + } + + @Test + public void removeLinksAsAdminByDevice() { + addLink(DID1, P1, DID2, P2, DIRECT); + addLink(DID2, P2, DID1, P1, DIRECT); + addLink(DID3, P3, DID2, P1, DIRECT); + addLink(DID2, P1, DID3, P3, DIRECT); + Link l5 = addLink(DID3, P1, DID1, P2, DIRECT); + Link l6 = addLink(DID1, P2, DID3, P1, DIRECT); + assertEquals("incorrect link count", 6, service.getLinkCount()); + + admin.removeLinks(DID2); + assertEquals("incorrect link count", 2, service.getLinkCount()); + assertNotNull("link should not be gone", service.getLink(l5.src(), l5.dst())); + assertNotNull("link should not be gone", service.getLink(l6.src(), l6.dst())); + } + + @Test + public void getLinks() { + Link l1 = addLink(DID1, P1, DID2, P2, DIRECT); + Link l2 = addLink(DID2, P2, DID1, P1, DIRECT); + Link l3 = addLink(DID3, P3, DID2, P1, DIRECT); + Link l4 = addLink(DID2, P1, DID3, P3, DIRECT); + assertEquals("incorrect link count", 4, service.getLinkCount()); + + Set<Link> links = service.getLinks(cp(DID1, P1)); + assertEquals("incorrect links", ImmutableSet.of(l1, l2), links); + links = service.getEgressLinks(cp(DID1, P1)); + assertEquals("incorrect links", ImmutableSet.of(l1), links); + links = service.getIngressLinks(cp(DID1, P1)); + assertEquals("incorrect links", ImmutableSet.of(l2), links); + + links = service.getDeviceLinks(DID2); + assertEquals("incorrect links", ImmutableSet.of(l1, l2, l3, l4), links); + links = service.getDeviceLinks(DID3); + assertEquals("incorrect links", ImmutableSet.of(l3, l4), links); + + links = service.getDeviceEgressLinks(DID2); + assertEquals("incorrect links", ImmutableSet.of(l2, l4), links); + links = service.getDeviceIngressLinks(DID2); + assertEquals("incorrect links", ImmutableSet.of(l1, l3), links); + } + + + private Link addLink(DeviceId sd, PortNumber sp, DeviceId dd, PortNumber dp, + Link.Type type) { + providerService.linkDetected(new DefaultLinkDescription(cp(sd, sp), cp(dd, dp), type)); + Link link = listener.events.get(0).subject(); + validateEvents(LINK_ADDED); + return link; + } + + private ConnectPoint cp(DeviceId id, PortNumber portNumber) { + return new ConnectPoint(id, portNumber); + } + + protected void validateEvents(Enum... types) { + int i = 0; + assertEquals("wrong events received", types.length, listener.events.size()); + for (Event event : listener.events) { + assertEquals("incorrect event type", types[i], event.type()); + i++; + } + listener.events.clear(); + } + + + private class TestProvider extends AbstractProvider implements LinkProvider { + private Device deviceReceived; + private MastershipRole roleReceived; + + public TestProvider() { + super(PID); + } + } + + private static class TestListener implements LinkListener { + final List<LinkEvent> events = new ArrayList<>(); + + @Override + public void event(LinkEvent event) { + events.add(event); + } + } + + private static class TestDeviceManager extends DeviceManager { + + @Override + public MastershipRole getRole(DeviceId deviceId) { + return MastershipRole.MASTER; + } + + @Override + public Device getDevice(DeviceId deviceId) { + return DEVICEIDMAP.get(deviceId); + } + + } + private class TestNetworkConfigService extends NetworkConfigServiceAdapter { + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java new file mode 100644 index 00000000..1a160d98 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java @@ -0,0 +1,667 @@ +/* + * Copyright 2014-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.proxyarp.impl; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.ARP; +import org.onlab.packet.Ethernet; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultHost; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.Link; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.edgeservice.impl.EdgeManager; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.host.HostService; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.link.LinkListener; +import org.onosproject.net.link.LinkService; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.proxyarp.ProxyArpStore; +import org.onosproject.net.proxyarp.ProxyArpStoreDelegate; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for the {@link ProxyArpManager} class. + */ +public class ProxyArpManagerTest { + + private static final int NUM_DEVICES = 6; + private static final int NUM_PORTS_PER_DEVICE = 3; + private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2; + private static final int NUM_FLOOD_PORTS = 3; + + private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1"); + private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2"); + + private static final ProviderId PID = new ProviderId("of", "foo"); + + private static final VlanId VLAN1 = VlanId.vlanId((short) 1); + private static final VlanId VLAN2 = VlanId.vlanId((short) 2); + private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01"); + private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final HostId HID1 = HostId.hostId(MAC1, VLAN1); + private static final HostId HID2 = HostId.hostId(MAC2, VLAN1); + + private static final DeviceId DID1 = getDeviceId(1); + private static final DeviceId DID2 = getDeviceId(2); + private static final PortNumber P1 = PortNumber.portNumber(1); + private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L); + private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L); + private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes(); + + //Return values used for various functions of the TestPacketService inner class. + private boolean isEdgePointReturn; + private List<ConnectPoint> getEdgePointsNoArg; + + + private ProxyArpManager proxyArp; + + private TestPacketService packetService; + private DeviceService deviceService; + private LinkService linkService; + private HostService hostService; + private InterfaceService interfaceService; + + @Before + public void setUp() throws Exception { + proxyArp = new ProxyArpManager(); + packetService = new TestPacketService(); + proxyArp.packetService = packetService; + proxyArp.store = new TestProxyArpStoreAdapter(); + + proxyArp.edgeService = new TestEdgePortService(); + + // Create a host service mock here. Must be replayed by tests once the + // expectations have been set up + hostService = createMock(HostService.class); + proxyArp.hostService = hostService; + + interfaceService = createMock(InterfaceService.class); + proxyArp.interfaceService = interfaceService; + + createTopology(); + proxyArp.deviceService = deviceService; + proxyArp.linkService = linkService; + + proxyArp.activate(); + } + + /** + * Creates a fake topology to feed into the ARP module. + * <p> + * The default topology is a unidirectional ring topology. Each switch has + * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1 + * is free (edge port). + * The first half of the switches have IP addresses configured on their + * free ports (port 1). The second half of the switches have no IP + * addresses configured. + */ + private void createTopology() { + deviceService = createMock(DeviceService.class); + linkService = createMock(LinkService.class); + + deviceService.addListener(anyObject(DeviceListener.class)); + linkService.addListener(anyObject(LinkListener.class)); + + createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE); + createLinks(NUM_DEVICES); + addAddressBindings(); + } + + /** + * Creates the devices for the fake topology. + */ + private void createDevices(int numDevices, int numPorts) { + List<Device> devices = new ArrayList<>(); + + for (int i = 1; i <= numDevices; i++) { + DeviceId devId = getDeviceId(i); + Device device = createMock(Device.class); + expect(device.id()).andReturn(devId).anyTimes(); + replay(device); + + devices.add(device); + + List<Port> ports = new ArrayList<>(); + for (int j = 1; j <= numPorts; j++) { + Port port = createMock(Port.class); + expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes(); + replay(port); + ports.add(port); + } + + expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes(); + expect(deviceService.getDevice(devId)).andReturn(device).anyTimes(); + } + + expect(deviceService.getDevices()).andReturn(devices).anyTimes(); + replay(deviceService); + } + + /** + * Creates the links for the fake topology. + * NB: Only unidirectional links are created, as for this purpose all we + * need is to occupy the ports with some link. + */ + private void createLinks(int numDevices) { + List<Link> links = new ArrayList<>(); + + for (int i = 1; i <= numDevices; i++) { + ConnectPoint src = new ConnectPoint( + getDeviceId(i), + PortNumber.portNumber(2)); + ConnectPoint dst = new ConnectPoint( + getDeviceId((i + 1 > numDevices) ? 1 : i + 1), + PortNumber.portNumber(3)); + + Link link = createMock(Link.class); + expect(link.src()).andReturn(src).anyTimes(); + expect(link.dst()).andReturn(dst).anyTimes(); + replay(link); + + links.add(link); + } + + expect(linkService.getLinks()).andReturn(links).anyTimes(); + replay(linkService); + } + + private void addAddressBindings() { + Set<Interface> interfaces = Sets.newHashSet(); + + for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) { + ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1); + Ip4Prefix prefix1 = + Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24"); + Ip4Address addr1 = + Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1"); + Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24"); + Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1"); + InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1); + InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2); + Interface intf1 = new Interface(cp, Sets.newHashSet(ia1), + MacAddress.valueOf(2 * i - 1), + VlanId.vlanId((short) 1)); + Interface intf2 = new Interface(cp, Sets.newHashSet(ia2), + MacAddress.valueOf(2 * i), + VlanId.NONE); + + interfaces.add(intf1); + interfaces.add(intf2); + + expect(interfaceService.getInterfacesByPort(cp)) + .andReturn(Sets.newHashSet(intf1, intf2)).anyTimes(); + } + + expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes(); + + for (int i = 1; i <= NUM_FLOOD_PORTS; i++) { + ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS), + P1); + + expect(interfaceService.getInterfacesByPort(cp)) + .andReturn(Collections.emptySet()).anyTimes(); + } + } + + /** + * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the + * case where the IP address is not known. + * Verifies the method returns false. + */ + @Test + public void testNotKnown() { + expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet()); + replay(hostService); + replay(interfaceService); + + assertFalse(proxyArp.isKnown(IP1)); + } + + /** + * Tests {@link ProxyArpManager#isKnown(org.onlab.packet.IpAddress)} in the + * case where the IP address is known. + * Verifies the method returns true. + */ + @Test + public void testKnown() { + Host host1 = createMock(Host.class); + Host host2 = createMock(Host.class); + + expect(hostService.getHostsByIp(IP1)) + .andReturn(Sets.newHashSet(host1, host2)); + replay(hostService); + replay(interfaceService); + + assertTrue(proxyArp.isKnown(IP1)); + } + + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct ARP reply is sent out the correct port. + */ + @Test + public void testReplyKnown() { + //Set the return value of isEdgePoint from the edgemanager. + isEdgePointReturn = true; + + Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4), + Collections.singleton(IP1)); + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), + Collections.singleton(IP2)); + + expect(hostService.getHostsByIp(IP1)) + .andReturn(Collections.singleton(replyer)); + expect(hostService.getHost(HID2)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); + + proxyArp.reply(arpRequest, getLocation(5)); + + assertEquals(1, packetService.packets.size()); + Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2); + verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0)); + } + + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the ARP request is flooded out the correct edge ports. + */ + @Test + public void testReplyUnknown() { + isEdgePointReturn = true; + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), + Collections.singleton(IP2)); + + expect(hostService.getHostsByIp(IP1)) + .andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(IP2)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID2)).andReturn(requestor); + + + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); + + //Setup the set of edge ports to be used in the reply method + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.reply(arpRequest, getLocation(6)); + + verifyFlood(arpRequest); + } + + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known for that IP address, but is not on the same + * VLAN as the source host. + * Verifies the ARP request is flooded out the correct edge ports. + */ + @Test + public void testReplyDifferentVlan() { + + Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4), + Collections.singleton(IP1)); + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), + Collections.singleton(IP2)); + + expect(hostService.getHostsByIp(IP1)) + .andReturn(Collections.singleton(replyer)); + expect(interfaceService.getInterfacesByIp(IP2)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID2)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); + + //Setup for flood test + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + proxyArp.reply(arpRequest, getLocation(6)); + + verifyFlood(arpRequest); + } + + @Test + public void testReplyToRequestForUs() { + Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254"); + Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1"); + Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1"); + MacAddress firstMac = MacAddress.valueOf(1L); + MacAddress secondMac = MacAddress.valueOf(2L); + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, + Collections.singleton(theirIp)); + + expect(hostService.getHost(HID2)).andReturn(requestor); + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp); + isEdgePointReturn = true; + proxyArp.reply(arpRequest, LOC1); + + assertEquals(1, packetService.packets.size()); + Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp); + verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); + + // Test a request for the second address on that port + packetService.packets.clear(); + arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp); + + proxyArp.reply(arpRequest, LOC1); + + assertEquals(1, packetService.packets.size()); + arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp); + verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); + } + + @Test + public void testReplyExternalPortBadRequest() { + replay(hostService); // no further host service expectations + replay(interfaceService); + + Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254"); + + // Request for a valid external IP address but coming in the wrong port + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, + Ip4Address.valueOf("10.0.3.1")); + proxyArp.reply(arpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + + // Request for a valid internal IP address but coming in an external port + packetService.packets.clear(); + arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1); + proxyArp.reply(arpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + } + + @Test + public void testReplyToRequestFromUs() { + Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1"); + MacAddress ourMac = MacAddress.valueOf(1L); + Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100"); + + expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(ourIp)) + .andReturn(Collections.singleton(new Interface(getLocation(1), + Collections.singleton(new InterfaceIpAddress(ourIp, IpPrefix.valueOf("10.0.1.1/24"))), + ourMac, VLAN1))); + expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null); + replay(hostService); + replay(interfaceService); + + // This is a request from something inside our network (like a BGP + // daemon) to an external host. + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp); + //Ensure the packet is allowed through (it is not to an internal port) + isEdgePointReturn = true; + + proxyArp.reply(arpRequest, getLocation(5)); + assertEquals(1, packetService.packets.size()); + verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0)); + + // The same request from a random external port should fail + packetService.packets.clear(); + proxyArp.reply(arpRequest, getLocation(2)); + assertEquals(0, packetService.packets.size()); + } + + /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct ARP request is sent out the correct port. + */ + @Test + public void testForwardToHost() { + Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1, + Collections.singleton(IP1)); + Host host2 = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC2, + Collections.singleton(IP2)); + + expect(hostService.getHost(HID1)).andReturn(host1); + expect(hostService.getHost(HID2)).andReturn(host2); + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1); + + proxyArp.forward(arpRequest, LOC2); + + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + verifyPacketOut(arpRequest, LOC1, packet); + } + + /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the correct ARP request is flooded out the correct edge ports. + */ + @Test + public void testForwardFlood() { + expect(hostService.getHost(HID1)).andReturn(null); + replay(hostService); + replay(interfaceService); + + Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1); + + //populate the list of edges when so that when forward hits flood in the manager it contains the values + //that should continue on + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("3"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.forward(arpRequest, getLocation(6)); + + verifyFlood(arpRequest); + } + + /** + * Verifies that the given packet was flooded out all available edge ports, + * except for the input port. + * + * @param packet the packet that was expected to be flooded + */ + private void verifyFlood(Ethernet packet) { + // There should be 1 less than NUM_FLOOD_PORTS; the inPort should be excluded. + assertEquals(NUM_FLOOD_PORTS - 1, packetService.packets.size()); + + Collections.sort(packetService.packets, + (o1, o2) -> o1.sendThrough().uri().compareTo(o2.sendThrough().uri())); + + + for (int i = 0; i < NUM_FLOOD_PORTS - 1; i++) { + ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1), + PortNumber.portNumber(1)); + + OutboundPacket outboundPacket = packetService.packets.get(i); + verifyPacketOut(packet, cp, outboundPacket); + } + } + + /** + * Verifies the given packet was sent out the given port. + * + * @param expected the packet that was expected to be sent + * @param outPort the port the packet was expected to be sent out + * @param actual the actual OutboundPacket to verify + */ + private void verifyPacketOut(Ethernet expected, ConnectPoint outPort, + OutboundPacket actual) { + assertArrayEquals(expected.serialize(), actual.data().array()); + assertEquals(1, actual.treatment().immediate().size()); + assertEquals(outPort.deviceId(), actual.sendThrough()); + Instruction instruction = actual.treatment().immediate().get(0); + assertTrue(instruction instanceof OutputInstruction); + assertEquals(outPort.port(), ((OutputInstruction) instruction).port()); + } + + /** + * Returns the device ID of the ith device. + * + * @param i device to get the ID of + * @return the device ID + */ + private static DeviceId getDeviceId(int i) { + return DeviceId.deviceId("" + i); + } + + private static HostLocation getLocation(int i) { + return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L); + } + + /** + * Builds an ARP packet with the given parameters. + * + * @param opcode opcode of the ARP packet + * @param srcMac source MAC address + * @param dstMac destination MAC address, or null if this is a request + * @param srcIp source IP address + * @param dstIp destination IP address + * @return the ARP packet + */ + private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac, + Ip4Address srcIp, Ip4Address dstIp) { + Ethernet eth = new Ethernet(); + + if (dstMac == null) { + eth.setDestinationMACAddress(MacAddress.BROADCAST); + } else { + eth.setDestinationMACAddress(dstMac); + } + + eth.setSourceMACAddress(srcMac); + eth.setEtherType(Ethernet.TYPE_ARP); + eth.setVlanID(VLAN1.toShort()); + + ARP arp = new ARP(); + arp.setOpCode(opcode); + arp.setProtocolType(ARP.PROTO_TYPE_IP); + arp.setHardwareType(ARP.HW_TYPE_ETHERNET); + + arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH); + arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); + arp.setSenderHardwareAddress(srcMac.toBytes()); + + if (dstMac == null) { + arp.setTargetHardwareAddress(ZERO_MAC_ADDRESS); + } else { + arp.setTargetHardwareAddress(dstMac.toBytes()); + } + + arp.setSenderProtocolAddress(srcIp.toOctets()); + arp.setTargetProtocolAddress(dstIp.toOctets()); + + eth.setPayload(arp); + return eth; + } + + /** + * Test PacketService implementation that simply stores OutboundPackets + * passed to {@link #emit(OutboundPacket)} for later verification. + */ + class TestPacketService extends PacketServiceAdapter { + + List<OutboundPacket> packets = new ArrayList<>(); + + @Override + public void emit(OutboundPacket packet) { + packets.add(packet); + } + + } + + class TestEdgePortService extends EdgeManager { + + @Override + public boolean isEdgePoint(ConnectPoint connectPoint) { + return isEdgePointReturn; + } + + @Override + public Iterable<ConnectPoint> getEdgePoints() { + return getEdgePointsNoArg; + } + } + + private class TestProxyArpStoreAdapter implements ProxyArpStore { + @Override + public void forward(ConnectPoint outPort, Host subject, ByteBuffer packet) { + TrafficTreatment tt = DefaultTrafficTreatment.builder().setOutput(outPort.port()).build(); + packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), tt, packet)); + } + + @Override + public void setDelegate(ProxyArpStoreDelegate delegate) { + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/DefaultTopologyProviderTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/DefaultTopologyProviderTest.java new file mode 100644 index 00000000..022df23d --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/DefaultTopologyProviderTest.java @@ -0,0 +1,194 @@ +/* + * Copyright 2014-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.topology.impl; + +import com.google.common.collect.ImmutableSet; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.event.Event; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.Device; +import org.onosproject.net.Link; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.impl.DeviceManager; +import org.onosproject.net.link.LinkEvent; +import org.onosproject.net.link.impl.LinkManager; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.GraphDescription; +import org.onosproject.net.topology.TopologyProvider; +import org.onosproject.net.topology.TopologyProviderRegistry; +import org.onosproject.net.topology.TopologyProviderService; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.Phaser; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import static org.onosproject.net.NetTestTools.device; +import static org.onosproject.net.NetTestTools.link; +import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED; +import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; + +/** + * Test of the default topology provider implementation. + */ +public class DefaultTopologyProviderTest { + + private DefaultTopologyProvider provider = new DefaultTopologyProvider(); + private TestTopoRegistry topologyService = new TestTopoRegistry(); + private TestDeviceService deviceService = new TestDeviceService(); + private TestLinkService linkService = new TestLinkService(); + private TestTopoProviderService providerService; + + // phase corresponds to number of topologyChanged called + private Phaser topologyChangedCounts = new Phaser(1); + + @Before + public void setUp() { + provider.deviceService = deviceService; + provider.linkService = linkService; + provider.providerRegistry = topologyService; + provider.cfgService = new ComponentConfigAdapter(); + provider.activate(null); + } + + @After + public void tearDown() { + provider.deactivate(null); + provider.providerRegistry = null; + provider.deviceService = null; + provider.linkService = null; + provider.cfgService = null; + } + + private void validateSubmission() { + assertNotNull("registration expected", providerService); + assertEquals("incorrect provider", provider, providerService.provider()); + assertNotNull("topo change should be submitted", providerService.graphDesc); + assertEquals("incorrect vertex count", 6, providerService.graphDesc.vertexes().size()); + assertEquals("incorrect edge count", 10, providerService.graphDesc.edges().size()); + } + + @Test + public void basics() throws InterruptedException, TimeoutException { + assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS)); + validateSubmission(); + } + + @Test + public void eventDriven() throws InterruptedException, TimeoutException { + assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS)); + validateSubmission(); + + deviceService.postEvent(new DeviceEvent(DEVICE_ADDED, device("z"), null)); + linkService.postEvent(new LinkEvent(LINK_ADDED, link("z", 1, "a", 4))); + assertThat(topologyChangedCounts.awaitAdvanceInterruptibly(1, 1, TimeUnit.SECONDS), + is(greaterThanOrEqualTo(2))); + // Note: posting event, to trigger topologyChanged call, + // but dummy topology will not change. + validateSubmission(); + } + + + private class TestTopoRegistry implements TopologyProviderRegistry { + + @Override + public TopologyProviderService register(TopologyProvider provider) { + providerService = new TestTopoProviderService(provider); + return providerService; + } + + @Override + public void unregister(TopologyProvider provider) { + } + + @Override + public Set<ProviderId> getProviders() { + return null; + } + } + + private class TestTopoProviderService + extends AbstractProviderService<TopologyProvider> + implements TopologyProviderService { + GraphDescription graphDesc; + + protected TestTopoProviderService(TopologyProvider provider) { + super(provider); + } + + @Override + public void topologyChanged(GraphDescription graphDescription, List<Event> reasons) { + graphDesc = graphDescription; + topologyChangedCounts.arrive(); + } + } + + private class TestDeviceService extends DeviceManager { + TestDeviceService() { + eventDispatcher = new TestEventDispatcher(); + eventDispatcher.addSink(DeviceEvent.class, listenerRegistry); + } + + @Override + public Iterable<Device> getDevices() { + return ImmutableSet.of(device("a"), device("b"), + device("c"), device("d"), + device("e"), device("f")); + } + + @Override + public Iterable<Device> getAvailableDevices() { + return getDevices(); + } + + void postEvent(DeviceEvent event) { + eventDispatcher.post(event); + } + } + + private class TestLinkService extends LinkManager { + TestLinkService() { + eventDispatcher = new TestEventDispatcher(); + eventDispatcher.addSink(LinkEvent.class, listenerRegistry); + } + + @Override + public Iterable<Link> getLinks() { + return ImmutableSet.of(link("a", 1, "b", 1), link("b", 1, "a", 1), + link("b", 2, "c", 1), link("c", 1, "b", 2), + link("c", 2, "d", 1), link("d", 1, "c", 2), + link("d", 2, "a", 2), link("a", 2, "d", 2), + link("e", 1, "f", 1), link("f", 1, "e", 1)); + } + + @Override + public Iterable<Link> getActiveLinks() { + return getLinks(); + } + + void postEvent(LinkEvent event) { + eventDispatcher.post(event); + } + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java new file mode 100644 index 00000000..2a2d0b54 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java @@ -0,0 +1,164 @@ +/* + * Copyright 2014 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.topology.impl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.net.DeviceId; +import org.onosproject.net.ElementId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.Path; +import org.onosproject.net.host.HostService; +import org.onosproject.net.host.HostServiceAdapter; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.LinkWeight; +import org.onosproject.net.topology.PathService; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyService; +import org.onosproject.net.topology.TopologyServiceAdapter; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.onosproject.net.NetTestTools.*; + +/** + * Test of the path selection subsystem. + */ +public class PathManagerTest { + + private static final ProviderId PID = new ProviderId("of", "foo"); + + private PathManager mgr; + private PathService service; + + private FakeTopoMgr fakeTopoMgr = new FakeTopoMgr(); + private FakeHostMgr fakeHostMgr = new FakeHostMgr(); + + @Before + public void setUp() { + mgr = new PathManager(); + service = mgr; + mgr.topologyService = fakeTopoMgr; + mgr.hostService = fakeHostMgr; + mgr.activate(); + } + + @After + public void tearDown() { + mgr.deactivate(); + } + + @Test + public void infraToInfra() { + DeviceId src = did("src"); + DeviceId dst = did("dst"); + fakeTopoMgr.paths.add(createPath("src", "middle", "dst")); + Set<Path> paths = service.getPaths(src, dst); + validatePaths(paths, 1, 2, src, dst); + } + + @Test + public void infraToEdge() { + DeviceId src = did("src"); + HostId dst = hid("12:34:56:78:90:ab/1"); + fakeTopoMgr.paths.add(createPath("src", "middle", "edge")); + fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ab/1", "edge")); + Set<Path> paths = service.getPaths(src, dst); + validatePaths(paths, 1, 3, src, dst); + } + + @Test + public void edgeToInfra() { + HostId src = hid("12:34:56:78:90:ab/1"); + DeviceId dst = did("dst"); + fakeTopoMgr.paths.add(createPath("edge", "middle", "dst")); + fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge")); + Set<Path> paths = service.getPaths(src, dst); + validatePaths(paths, 1, 3, src, dst); + } + + @Test + public void edgeToEdge() { + HostId src = hid("12:34:56:78:90:ab/1"); + HostId dst = hid("12:34:56:78:90:ef/1"); + fakeTopoMgr.paths.add(createPath("srcEdge", "middle", "dstEdge")); + fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "srcEdge")); + fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "dstEdge")); + Set<Path> paths = service.getPaths(src, dst); + validatePaths(paths, 1, 4, src, dst); + } + + @Test + public void edgeToEdgeDirect() { + HostId src = hid("12:34:56:78:90:ab/1"); + HostId dst = hid("12:34:56:78:90:ef/1"); + fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge")); + fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "edge")); + Set<Path> paths = service.getPaths(src, dst); + validatePaths(paths, 1, 2, src, dst); + } + + @Test + public void noEdge() { + Set<Path> paths = service.getPaths(hid("12:34:56:78:90:ab/1"), + hid("12:34:56:78:90:ef/1")); + assertTrue("there should be no paths", paths.isEmpty()); + } + + // Makes sure the set of paths meets basic expectations. + private void validatePaths(Set<Path> paths, int count, int length, + ElementId src, ElementId dst) { + assertEquals("incorrect path count", count, paths.size()); + for (Path path : paths) { + assertEquals("incorrect length", length, path.links().size()); + assertEquals("incorrect source", src, path.src().elementId()); + assertEquals("incorrect destination", dst, path.dst().elementId()); + } + } + + // Fake entity to give out paths. + private class FakeTopoMgr extends TopologyServiceAdapter implements TopologyService { + Set<Path> paths = new HashSet<>(); + + @Override + public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) { + return paths; + } + + @Override + public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst, LinkWeight weight) { + return paths; + } + } + + // Fake entity to give out hosts. + private class FakeHostMgr extends HostServiceAdapter implements HostService { + private Map<HostId, Host> hosts = new HashMap<>(); + + @Override + public Host getHost(HostId hostId) { + return hosts.get(hostId); + } + } + +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java new file mode 100644 index 00000000..f3cd28df --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java @@ -0,0 +1,215 @@ +/* + * Copyright 2014-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.topology.impl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.event.Event; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.Link; +import org.onosproject.net.Path; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.DefaultGraphDescription; +import org.onosproject.net.topology.GraphDescription; +import org.onosproject.net.topology.LinkWeight; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyCluster; +import org.onosproject.net.topology.TopologyEvent; +import org.onosproject.net.topology.TopologyGraph; +import org.onosproject.net.topology.TopologyListener; +import org.onosproject.net.topology.TopologyProvider; +import org.onosproject.net.topology.TopologyProviderRegistry; +import org.onosproject.net.topology.TopologyProviderService; +import org.onosproject.net.topology.TopologyService; +import org.onosproject.store.trivial.SimpleTopologyStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static com.google.common.collect.ImmutableSet.of; +import static org.junit.Assert.*; +import static org.onosproject.net.NetTestTools.*; +import static org.onosproject.net.PortNumber.portNumber; +import static org.onosproject.net.topology.ClusterId.clusterId; +import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED; + +/** + * Test of the topology subsystem. + */ +public class TopologyManagerTest { + + private static final ProviderId PID = new ProviderId("of", "foo"); + + private TopologyManager mgr; + + protected TopologyService service; + protected TopologyProviderRegistry registry; + protected TopologyProviderService providerService; + protected TestProvider provider; + protected TestListener listener = new TestListener(); + + @Before + public void setUp() { + mgr = new TopologyManager(); + service = mgr; + registry = mgr; + + mgr.store = new SimpleTopologyStore(); + injectEventDispatcher(mgr, new TestEventDispatcher()); + mgr.activate(); + + service.addListener(listener); + + provider = new TestProvider(); + providerService = registry.register(provider); + + assertTrue("provider should be registered", + registry.getProviders().contains(provider.id())); + } + + @After + public void tearDown() { + mgr.deactivate(); + service.removeListener(listener); + } + + @Test + public void basics() { + Topology topology = service.currentTopology(); + assertNull("no topo expected", topology); + submitTopologyGraph(); + validateEvents(TOPOLOGY_CHANGED); + topology = service.currentTopology(); + assertTrue("should be latest", service.isLatest(topology)); + + submitTopologyGraph(); + validateEvents(TOPOLOGY_CHANGED); + assertFalse("should be latest", service.isLatest(topology)); + } + + private void submitTopologyGraph() { + Set<Device> devices = of(device("a"), device("b"), + device("c"), device("d"), + device("e"), device("f")); + Set<Link> links = of(link("a", 1, "b", 1), link("b", 1, "a", 1), + link("b", 2, "c", 1), link("c", 1, "b", 2), + link("c", 2, "d", 1), link("d", 1, "c", 2), + link("d", 2, "a", 2), link("a", 2, "d", 2), + link("e", 1, "f", 1), link("f", 1, "e", 1)); + GraphDescription data = new DefaultGraphDescription(4321L, devices, links); + providerService.topologyChanged(data, null); + } + + @Test + public void clusters() { + submitTopologyGraph(); + Topology topology = service.currentTopology(); + assertNotNull("topo expected", topology); + assertEquals("wrong cluster count", 2, topology.clusterCount()); + assertEquals("wrong device count", 6, topology.deviceCount()); + assertEquals("wrong link count", 10, topology.linkCount()); + + assertEquals("wrong cluster count", 2, service.getClusters(topology).size()); + + TopologyCluster cluster = service.getCluster(topology, clusterId(0)); + assertEquals("wrong device count", 4, cluster.deviceCount()); + assertEquals("wrong device count", 4, service.getClusterDevices(topology, cluster).size()); + assertEquals("wrong link count", 8, cluster.linkCount()); + assertEquals("wrong link count", 8, service.getClusterLinks(topology, cluster).size()); + } + + @Test + public void structure() { + submitTopologyGraph(); + Topology topology = service.currentTopology(); + + assertTrue("should be infrastructure point", + service.isInfrastructure(topology, new ConnectPoint(did("a"), portNumber(1)))); + assertFalse("should not be infrastructure point", + service.isInfrastructure(topology, new ConnectPoint(did("a"), portNumber(3)))); + + assertTrue("should be broadcast point", + service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(3)))); + } + + @Test + public void graph() { + submitTopologyGraph(); + Topology topology = service.currentTopology(); + TopologyGraph graph = service.getGraph(topology); + assertEquals("wrong vertex count", 6, graph.getVertexes().size()); + assertEquals("wrong edge count", 10, graph.getEdges().size()); + } + + @Test + public void precomputedPath() { + submitTopologyGraph(); + Topology topology = service.currentTopology(); + Set<Path> paths = service.getPaths(topology, did("a"), did("c")); + assertEquals("wrong path count", 2, paths.size()); + Path path = paths.iterator().next(); + assertEquals("wrong path length", 2, path.links().size()); + assertEquals("wrong path cost", 2, path.cost(), 0.01); + } + + @Test + public void onDemandPath() { + submitTopologyGraph(); + Topology topology = service.currentTopology(); + LinkWeight weight = edge -> 3.3; + + Set<Path> paths = service.getPaths(topology, did("a"), did("c"), weight); + assertEquals("wrong path count", 2, paths.size()); + Path path = paths.iterator().next(); + assertEquals("wrong path length", 2, path.links().size()); + assertEquals("wrong path cost", 6.6, path.cost(), 0.01); + } + + protected void validateEvents(Enum... types) { + int i = 0; + assertEquals("wrong events received", types.length, listener.events.size()); + for (Event event : listener.events) { + assertEquals("incorrect event type", types[i], event.type()); + i++; + } + listener.events.clear(); + } + + private class TestProvider extends AbstractProvider implements TopologyProvider { + public TestProvider() { + super(PID); + } + + @Override + public void triggerRecompute() { + } + } + + private static class TestListener implements TopologyListener { + final List<TopologyEvent> events = new ArrayList<>(); + + @Override + public void event(TopologyEvent event) { + events.add(event); + } + } + +} |