Added unit tests for OpenstackNode

Change-Id: I4e852145c945cd11586b39e06972fcba23942660
diff --git a/apps/openstacknode/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java b/apps/openstacknode/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
new file mode 100644
index 0000000..aadec13
--- /dev/null
+++ b/apps/openstacknode/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
@@ -0,0 +1,1062 @@
+/*
+ * Copyright 2017-present 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.openstacknode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.LeadershipServiceAdapter;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.behaviour.DefaultBridgeDescription;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceInterfaceDescription;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.Group;
+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.GroupService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.net.Device.Type.CONTROLLER;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.device.DeviceEvent.Type.*;
+import static org.onosproject.openstacknode.api.Constants.*;
+import static org.onosproject.openstacknode.api.NodeState.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
+
+/**
+ * Unit test for DefaultOpenstackNodeHandler.
+ */
+public class DefaultOpenstackNodeHandlerTest {
+
+    private static final ApplicationId TEST_APP_ID = new DefaultApplicationId(1, "test");
+    private static final String ERR_STATE_NOT_MATCH = "Node state did not match";
+    private static final NodeId LOCAL_NODE_ID = new NodeId("local");
+    private static final ControllerNode LOCAL_CTRL =
+            new DefaultControllerNode(LOCAL_NODE_ID, IpAddress.valueOf("127.0.0.1"));
+
+    private static final BridgeDescription ROUT_BRIDGE = DefaultBridgeDescription.builder()
+            .name(ROUTER_BRIDGE)
+            .failMode(BridgeDescription.FailMode.SECURE)
+            .disableInBand()
+            .build();
+
+    private static final PortDescription PATCH_ROUT = new DefaultPortDescription(
+            PortNumber.portNumber(1),
+            true,
+            DefaultAnnotations.builder()
+                    .set(PORT_NAME, PATCH_ROUT_BRIDGE)
+                    .build()
+    );
+
+    private static final String COMPUTE_1_HOSTNAME = "compute_1";
+    private static final String COMPUTE_2_HOSTNAME = "compute_2";
+    private static final String COMPUTE_3_HOSTNAME = "compute_3";
+    private static final String COMPUTE_4_HOSTNAME = "compute_4";
+    private static final String GATEWAY_1_HOSTNAME = "gateway_1";
+    private static final String GATEWAY_2_HOSTNAME = "gateway_2";
+    private static final String GATEWAY_3_HOSTNAME = "gateway_3";
+    private static final String GATEWAY_4_HOSTNAME = "gateway_4";
+
+    private static final IpAddress COMPUTE_1_IP = IpAddress.valueOf("10.100.0.1");
+    private static final IpAddress COMPUTE_2_IP = IpAddress.valueOf("10.100.0.2");
+    private static final IpAddress COMPUTE_3_IP = IpAddress.valueOf("10.100.0.3");
+    private static final IpAddress COMPUTE_4_IP = IpAddress.valueOf("10.100.0.4");
+    private static final IpAddress GATEWAY_1_IP = IpAddress.valueOf("10.100.0.5");
+    private static final IpAddress GATEWAY_2_IP = IpAddress.valueOf("10.100.0.6");
+    private static final IpAddress GATEWAY_3_IP = IpAddress.valueOf("10.100.0.7");
+    private static final IpAddress GATEWAY_4_IP = IpAddress.valueOf("10.100.0.8");
+
+    private static final Device COMPUTE_1_INTG_DEVICE = createOpenFlowDevice(1, INTEGRATION_BRIDGE);
+    private static final Device COMPUTE_2_INTG_DEVICE = createOpenFlowDevice(2, INTEGRATION_BRIDGE);
+    private static final Device COMPUTE_3_INTG_DEVICE = createOpenFlowDevice(3, INTEGRATION_BRIDGE);
+    private static final Device COMPUTE_4_INTG_DEVICE = createOpenFlowDevice(4, INTEGRATION_BRIDGE);
+    private static final Device GATEWAY_1_INTG_DEVICE = createOpenFlowDevice(5, INTEGRATION_BRIDGE);
+    private static final Device GATEWAY_1_ROUT_DEVICE = createOpenFlowDevice(6, ROUTER_BRIDGE);
+    private static final Device GATEWAY_2_INTG_DEVICE = createOpenFlowDevice(7, INTEGRATION_BRIDGE);
+    private static final Device GATEWAY_2_ROUT_DEVICE = createOpenFlowDevice(8, ROUTER_BRIDGE);
+    private static final Device GATEWAY_3_INTG_DEVICE = createOpenFlowDevice(9, INTEGRATION_BRIDGE);
+    private static final Device GATEWAY_3_ROUT_DEVICE = createOpenFlowDevice(10, ROUTER_BRIDGE);
+    private static final Device GATEWAY_4_INTG_DEVICE = createOpenFlowDevice(11, INTEGRATION_BRIDGE);
+    private static final Device GATEWAY_4_ROUT_DEVICE = createOpenFlowDevice(12, ROUTER_BRIDGE);
+
+    private static final Device COMPUTE_1_OVSDB_DEVICE = createOvsdbDevice(COMPUTE_1_IP);
+    private static final Device COMPUTE_2_OVSDB_DEVICE = createOvsdbDevice(COMPUTE_2_IP);
+    private static final Device COMPUTE_3_OVSDB_DEVICE = createOvsdbDevice(COMPUTE_3_IP);
+    private static final Device COMPUTE_4_OVSDB_DEVICE = createOvsdbDevice(COMPUTE_4_IP);
+    private static final Device GATEWAY_1_OVSDB_DEVICE = createOvsdbDevice(GATEWAY_1_IP);
+    private static final Device GATEWAY_2_OVSDB_DEVICE = createOvsdbDevice(GATEWAY_2_IP);
+
+    private static final OpenstackNode COMPUTE_1 = createNode(
+            COMPUTE_1_HOSTNAME,
+            COMPUTE,
+            COMPUTE_1_INTG_DEVICE,
+            COMPUTE_1_IP,
+            INIT
+    );
+
+    private static final OpenstackNode COMPUTE_2 = createNode(
+            COMPUTE_2_HOSTNAME,
+            COMPUTE,
+            COMPUTE_2_INTG_DEVICE,
+            COMPUTE_2_IP,
+            DEVICE_CREATED
+    );
+
+    private static final OpenstackNode COMPUTE_3 = createNode(
+            COMPUTE_3_HOSTNAME,
+            COMPUTE,
+            COMPUTE_3_INTG_DEVICE,
+            COMPUTE_3_IP,
+            PORT_CREATED
+    );
+
+    private static final OpenstackNode COMPUTE_4 = createNode(
+            COMPUTE_4_HOSTNAME,
+            COMPUTE,
+            COMPUTE_4_INTG_DEVICE,
+            COMPUTE_4_IP,
+            COMPLETE
+    );
+
+    private static final OpenstackNode GATEWAY_1 = createNode(
+            GATEWAY_1_HOSTNAME,
+            GATEWAY,
+            GATEWAY_1_INTG_DEVICE,
+            GATEWAY_1_ROUT_DEVICE,
+            GATEWAY_1_IP,
+            INIT
+    );
+
+    private static final OpenstackNode GATEWAY_2 = createNode(
+            GATEWAY_2_HOSTNAME,
+            GATEWAY,
+            GATEWAY_2_INTG_DEVICE,
+            GATEWAY_2_ROUT_DEVICE,
+            GATEWAY_2_IP,
+            DEVICE_CREATED
+    );
+
+    private static final OpenstackNode GATEWAY_3 = createNode(
+            GATEWAY_3_HOSTNAME,
+            GATEWAY,
+            GATEWAY_3_INTG_DEVICE,
+            GATEWAY_3_ROUT_DEVICE,
+            GATEWAY_3_IP,
+            PORT_CREATED
+    );
+
+    private static final OpenstackNode GATEWAY_4 = createNode(
+            GATEWAY_4_HOSTNAME,
+            GATEWAY,
+            GATEWAY_4_INTG_DEVICE,
+            GATEWAY_4_ROUT_DEVICE,
+            GATEWAY_4_IP,
+            COMPLETE
+    );
+
+    private static final TestDeviceService TEST_DEVICE_SERVICE = new TestDeviceService();
+
+    private TestOpenstackNodeManager testNodeManager;
+    private DefaultOpenstackNodeHandler target;
+
+    @Before
+    public void setUp() throws Exception {
+        DeviceAdminService mockDeviceAdminService = createMock(DeviceAdminService.class);
+        mockDeviceAdminService.removeDevice(anyObject());
+        replay(mockDeviceAdminService);
+
+        OvsdbClientService mockOvsdbClient = createMock(OvsdbClientService.class);
+        expect(mockOvsdbClient.isConnected())
+                .andReturn(true)
+                .anyTimes();
+        replay(mockOvsdbClient);
+
+        OvsdbController mockOvsdbController = createMock(OvsdbController.class);
+        expect(mockOvsdbController.getOvsdbClient(anyObject()))
+                .andReturn(mockOvsdbClient)
+                .anyTimes();
+        replay(mockOvsdbController);
+
+        testNodeManager = new TestOpenstackNodeManager();
+        target = new DefaultOpenstackNodeHandler();
+
+        target.coreService = new TestCoreService();
+        target.leadershipService = new TestLeadershipService();
+        target.clusterService = new TestClusterService();
+        target.deviceService = TEST_DEVICE_SERVICE;
+        target.deviceAdminService = mockDeviceAdminService;
+        target.ovsdbController = mockOvsdbController;
+        target.groupService = new TestGroupService();
+        target.osNodeService = testNodeManager;
+        target.osNodeAdminService = testNodeManager;
+        target.componentConfigService = new TestComponentConfigService();
+        TestUtils.setField(target, "eventExecutor", MoreExecutors.newDirectExecutorService());
+        target.activate();
+    }
+
+    @After
+    public void tearDown() {
+        TEST_DEVICE_SERVICE.clear();
+        target.deactivate();
+        target = null;
+        testNodeManager = null;
+    }
+
+    /**
+     * Checks if the compute node state changes from INIT to DEVICE_CREATED
+     * after processing INIT state.
+     */
+    @Test
+    public void testComputeNodeProcessNodeInitState() {
+        testNodeManager.createNode(COMPUTE_1);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_1_OVSDB_DEVICE.id(), COMPUTE_1_OVSDB_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, INIT,
+                testNodeManager.node(COMPUTE_1_HOSTNAME).state());
+        target.processInitState(COMPUTE_1);
+        assertEquals(ERR_STATE_NOT_MATCH, DEVICE_CREATED,
+                testNodeManager.node(COMPUTE_1_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the gateway node state changes from INIT to DEVICE_CREATED
+     * after processing INIT state.
+     */
+    @Test
+    public void testGatewayNodeProcessNodeInitState() {
+        testNodeManager.createNode(GATEWAY_1);
+        TEST_DEVICE_SERVICE.devMap.put(GATEWAY_1_OVSDB_DEVICE.id(), GATEWAY_1_OVSDB_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, INIT,
+                testNodeManager.node(GATEWAY_1_HOSTNAME).state());
+        target.processInitState(GATEWAY_1);
+        assertEquals(ERR_STATE_NOT_MATCH, DEVICE_CREATED,
+                testNodeManager.node(GATEWAY_1_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the compute node state changes from DEVICE_CREATED to
+     * PORT_CREATED after processing DEVICE_CREATED state.
+     */
+    @Test
+    public void testComputeNodeProcessDeviceCreatedState() {
+        testNodeManager.createNode(COMPUTE_2);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_2_OVSDB_DEVICE.id(), COMPUTE_2_OVSDB_DEVICE);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_2_INTG_DEVICE.id(), COMPUTE_2_INTG_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, DEVICE_CREATED,
+                testNodeManager.node(COMPUTE_2_HOSTNAME).state());
+        target.processDeviceCreatedState(COMPUTE_2);
+        assertEquals(ERR_STATE_NOT_MATCH, PORT_CREATED,
+                testNodeManager.node(COMPUTE_2_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the gateway node state changes from DEVICE_CREATED to
+     * PORT_CREATED after processing DEVICE_CREATED state.
+     */
+    @Test
+    public void testGatewayNodeProcessDeviceCreatedState() {
+        testNodeManager.createNode(GATEWAY_2);
+        TEST_DEVICE_SERVICE.devMap.put(GATEWAY_2_OVSDB_DEVICE.id(), GATEWAY_2_OVSDB_DEVICE);
+        TEST_DEVICE_SERVICE.devMap.put(GATEWAY_2_INTG_DEVICE.id(), GATEWAY_2_INTG_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, DEVICE_CREATED,
+                testNodeManager.node(GATEWAY_2_HOSTNAME).state());
+        target.processDeviceCreatedState(GATEWAY_2);
+        assertEquals(ERR_STATE_NOT_MATCH, PORT_CREATED,
+                testNodeManager.node(GATEWAY_2_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the compute node state changes from PORT_CREATED to
+     * COMPLETE after processing PORT_CREATED state.
+     */
+    @Test
+    public void testComputeNodeProcessPortCreatedState() {
+        testNodeManager.createNode(COMPUTE_3);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_3_OVSDB_DEVICE.id(), COMPUTE_3_OVSDB_DEVICE);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_3_INTG_DEVICE.id(), COMPUTE_3_INTG_DEVICE);
+        TEST_DEVICE_SERVICE.portList.add(createPort(COMPUTE_3_INTG_DEVICE, DEFAULT_TUNNEL));
+
+        testNodeManager.createNode(GATEWAY_4);
+        TEST_DEVICE_SERVICE.devMap.put(GATEWAY_4_INTG_DEVICE.id(), GATEWAY_4_INTG_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, PORT_CREATED,
+                testNodeManager.node(COMPUTE_3_HOSTNAME).state());
+        target.processPortCreatedState(COMPUTE_3);
+        assertEquals(ERR_STATE_NOT_MATCH, COMPLETE,
+                testNodeManager.node(COMPUTE_3_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the gateway node state changes from PORT_CREATED to
+     * COMPLETE after processing PORT_CREATED state.
+     */
+    @Test
+    public void testGatewayNodeProcessPortCreatedState() {
+        testNodeManager.createNode(COMPUTE_4);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_4_OVSDB_DEVICE.id(), COMPUTE_4_OVSDB_DEVICE);
+        TEST_DEVICE_SERVICE.devMap.put(COMPUTE_4_INTG_DEVICE.id(), COMPUTE_4_INTG_DEVICE);
+        TEST_DEVICE_SERVICE.portList.add(createPort(COMPUTE_4_INTG_DEVICE, DEFAULT_TUNNEL));
+
+        testNodeManager.createNode(GATEWAY_3);
+        TEST_DEVICE_SERVICE.devMap.put(GATEWAY_3_INTG_DEVICE.id(), GATEWAY_4_INTG_DEVICE);
+
+        assertEquals(ERR_STATE_NOT_MATCH, PORT_CREATED,
+                testNodeManager.node(GATEWAY_3_HOSTNAME).state());
+        target.processPortCreatedState(GATEWAY_3);
+        assertEquals(ERR_STATE_NOT_MATCH, COMPLETE,
+                testNodeManager.node(GATEWAY_3_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the compute node state changes from COMPLETE to INCOMPLETE
+     * when integration bridge is disconnected.
+     */
+    @Test
+    public void testBackToIncompleteWhenBrIntDisconnected() {
+        testNodeManager.createNode(COMPUTE_4);
+
+        assertEquals(ERR_STATE_NOT_MATCH, COMPLETE,
+                testNodeManager.node(COMPUTE_4_HOSTNAME).state());
+        TEST_DEVICE_SERVICE.removeDevice(COMPUTE_4_INTG_DEVICE);
+        assertEquals(ERR_STATE_NOT_MATCH, INCOMPLETE,
+                testNodeManager.node(COMPUTE_4_HOSTNAME).state());
+    }
+
+    /**
+     * Checks if the compute node state changes from COMPLETE to INCOMPLETE
+     * when vxlan port is removed from integration bridge.
+     */
+    @Test
+    public void testBackToIncompleteWhenVxlanRemoved() {
+        testNodeManager.createNode(COMPUTE_4);
+
+        assertEquals(ERR_STATE_NOT_MATCH, COMPLETE,
+                testNodeManager.node(COMPUTE_4_HOSTNAME).state());
+        TEST_DEVICE_SERVICE.removePort(COMPUTE_4_INTG_DEVICE, createPort(
+                COMPUTE_4_INTG_DEVICE, DEFAULT_TUNNEL));
+        assertEquals(ERR_STATE_NOT_MATCH, INCOMPLETE,
+                testNodeManager.node(COMPUTE_4_HOSTNAME).state());
+
+    }
+
+    private static Device createOvsdbDevice(IpAddress ovsdbIp) {
+        return new TestDevice(new ProviderId("of", "foo"),
+                DeviceId.deviceId("ovsdb:" + ovsdbIp.toString()),
+                CONTROLLER,
+                "manufacturer",
+                "hwVersion",
+                "swVersion",
+                "serialNumber",
+                new ChassisId(1));
+    }
+
+    private static Device createOpenFlowDevice(long devIdNum, String type) {
+        return new TestDevice(new ProviderId("of", "foo"),
+                DeviceId.deviceId(String.format("of:%016d", devIdNum)),
+                SWITCH,
+                type,
+                "hwVersion",
+                "swVersion",
+                "serialNumber",
+                new ChassisId(1));
+    }
+
+    private static Port createPort(Device device, String portName) {
+        return new DefaultPort(device,
+                PortNumber.portNumber(1),
+                true,
+                DefaultAnnotations.builder().set(PORT_NAME, portName).build());
+    }
+
+    private static OpenstackNode createNode(String hostname,
+                                            OpenstackNode.NodeType type,
+                                            Device intgBridge,
+                                            IpAddress ipAddr,
+                                            NodeState state) {
+        return new TestOpenstackNode(
+                hostname,
+                type,
+                intgBridge.id(),
+                null,
+                ipAddr,
+                ipAddr,
+                null, state);
+    }
+
+    private static OpenstackNode createNode(String hostname,
+                                            OpenstackNode.NodeType type,
+                                            Device intgBridge,
+                                            Device routerBridge,
+                                            IpAddress ipAddr,
+                                            NodeState state) {
+        return new TestOpenstackNode(
+                hostname,
+                type,
+                intgBridge.id(),
+                routerBridge.id(),
+                ipAddr,
+                ipAddr,
+                null, state);
+    }
+
+    private static final class TestDevice extends DefaultDevice {
+        private TestDevice(ProviderId providerId,
+                           DeviceId id,
+                           Type type,
+                           String manufacturer,
+                           String hwVersion,
+                           String swVersion,
+                           String serialNumber,
+                           ChassisId chassisId,
+                           Annotations... annotations) {
+            super(providerId,
+                    id,
+                    type,
+                    manufacturer,
+                    hwVersion,
+                    swVersion,
+                    serialNumber,
+                    chassisId,
+                    annotations);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <B extends Behaviour> B as(Class<B> projectionClass) {
+            if (projectionClass.equals(BridgeConfig.class)) {
+                return (B) new TestBridgeConfig();
+            } else if (projectionClass.equals(InterfaceConfig.class)) {
+                return (B) new TestInterfaceConfig();
+            } else if (projectionClass.equals(ExtensionTreatmentResolver.class)) {
+                ExtensionTreatmentResolver treatmentResolver = createMock(ExtensionTreatmentResolver.class);
+                expect(treatmentResolver.getExtensionInstruction(anyObject()))
+                        .andReturn(new TestExtensionTreatment())
+                        .anyTimes();
+                replay(treatmentResolver);
+                return (B) treatmentResolver;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public <B extends Behaviour> boolean is(Class<B> projectionClass) {
+            return true;
+        }
+    }
+
+    private static final class TestOpenstackNode extends DefaultOpenstackNode {
+        private TestOpenstackNode(String hostname,
+                                  NodeType type,
+                                  DeviceId intgBridge,
+                                  DeviceId routerBridge,
+                                  IpAddress managementIp,
+                                  IpAddress dataIp,
+                                  String vlanIntf,
+                                  NodeState state) {
+            super(hostname,
+                    type,
+                    intgBridge,
+                    routerBridge,
+                    managementIp,
+                    dataIp,
+                    vlanIntf,
+                    state);
+        }
+
+        @Override
+        public PortNumber tunnelPortNum() {
+            return PortNumber.portNumber(1);
+        }
+
+        @Override
+        public PortNumber vlanPortNum() {
+            return PortNumber.portNumber(1);
+        }
+
+        @Override
+        public PortNumber patchPortNum() {
+            return PortNumber.portNumber(1);
+        }
+
+        @Override
+        public MacAddress vlanPortMac() {
+            return MacAddress.NONE;
+        }
+    }
+
+    private static class TestOpenstackNodeManager implements OpenstackNodeService, OpenstackNodeAdminService {
+        Map<String, OpenstackNode> osNodeMap = Maps.newHashMap();
+        List<OpenstackNodeListener> listeners = Lists.newArrayList();
+
+        @Override
+        public Set<OpenstackNode> nodes() {
+            return ImmutableSet.copyOf(osNodeMap.values());
+        }
+
+        @Override
+        public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
+            return osNodeMap.values().stream()
+                    .filter(osNode -> osNode.type() == type)
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public Set<OpenstackNode> completeNodes() {
+            return osNodeMap.values().stream()
+                    .filter(osNode -> osNode.state() == COMPLETE)
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
+            return osNodeMap.values().stream()
+                    .filter(osNode -> osNode.type() == type && osNode.state() == COMPLETE)
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public OpenstackNode node(String hostname) {
+            return osNodeMap.get(hostname);
+        }
+
+        @Override
+        public OpenstackNode node(DeviceId deviceId) {
+            return osNodeMap.values().stream()
+                    .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
+                            Objects.equals(osNode.ovsdb(), deviceId) ||
+                            Objects.equals(osNode.routerBridge(), deviceId))
+                    .findFirst().orElse(null);
+        }
+
+        @Override
+        public void addListener(OpenstackNodeListener listener) {
+            listeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(OpenstackNodeListener listener) {
+            listeners.remove(listener);
+        }
+
+        @Override
+        public void createNode(OpenstackNode osNode) {
+            osNodeMap.put(osNode.hostname(), osNode);
+        }
+
+        @Override
+        public void updateNode(OpenstackNode osNode) {
+            osNodeMap.put(osNode.hostname(), osNode);
+        }
+
+        @Override
+        public OpenstackNode removeNode(String hostname) {
+            return null;
+        }
+    }
+
+    private static class TestDeviceService extends DeviceServiceAdapter {
+        Map<DeviceId, Device> devMap = Maps.newHashMap();
+        List<Port> portList = Lists.newArrayList();
+        List<DeviceListener> listeners = Lists.newArrayList();
+
+        @Override
+        public void addListener(DeviceListener listener) {
+            listeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(DeviceListener listener) {
+            listeners.remove(listener);
+        }
+
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            return devMap.get(deviceId);
+        }
+
+        @Override
+        public List<Port> getPorts(DeviceId deviceId) {
+            return this.portList.stream()
+                    .filter(p -> p.element().id().equals(deviceId))
+                    .collect(Collectors.toList());
+        }
+
+        @Override
+        public boolean isAvailable(DeviceId deviceId) {
+            return devMap.containsKey(deviceId);
+        }
+
+        void addDevice(Device device) {
+            devMap.put(device.id(), device);
+            DeviceEvent event = new DeviceEvent(DEVICE_ADDED, device);
+            listeners.stream().filter(l -> l.isRelevant(event)).forEach(l -> l.event(event));
+        }
+
+        void removeDevice(Device device) {
+            devMap.remove(device.id());
+            DeviceEvent event = new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device);
+            listeners.stream().filter(l -> l.isRelevant(event)).forEach(l -> l.event(event));
+        }
+
+        void addPort(Device device, Port port) {
+            portList.add(port);
+            DeviceEvent event = new DeviceEvent(PORT_ADDED, device, port);
+            listeners.stream().filter(l -> l.isRelevant(event)).forEach(l -> l.event(event));
+        }
+
+        void removePort(Device device, Port port) {
+            portList.remove(port);
+            DeviceEvent event = new DeviceEvent(PORT_REMOVED, device, port);
+            listeners.stream().filter(l -> l.isRelevant(event)).forEach(l -> l.event(event));
+        }
+
+        void clear() {
+            this.listeners.clear();
+            this.devMap.clear();
+            this.portList.clear();
+        }
+    }
+
+    private static class TestBridgeConfig implements BridgeConfig {
+
+        @Override
+        public DriverData data() {
+            return null;
+        }
+
+        @Override
+        public void setData(DriverData data) {
+
+        }
+
+        @Override
+        public DriverHandler handler() {
+            return null;
+        }
+
+        @Override
+        public void setHandler(DriverHandler handler) {
+
+        }
+
+        @Override
+        public void addBridge(BridgeName bridgeName) {
+
+        }
+
+        @Override
+        public void addBridge(BridgeName bridgeName, String dpid, String exPortName) {
+
+        }
+
+        @Override
+        public boolean addBridge(BridgeName bridgeName, String dpid, List<ControllerInfo> controllers) {
+            return false;
+        }
+
+        @Override
+        public boolean addBridge(BridgeDescription bridge) {
+            TEST_DEVICE_SERVICE.addDevice(new DefaultDevice(new ProviderId("of", "foo"),
+                    DeviceId.deviceId("of:" + bridge.datapathId().get()),
+                    SWITCH,
+                    bridge.name(),
+                    "hwVersion",
+                    "swVersion",
+                    "serialNumber",
+                    new ChassisId(1)));
+            return true;
+        }
+
+        @Override
+        public void deleteBridge(BridgeName bridgeName) {
+
+        }
+
+        @Override
+        public Collection<BridgeDescription> getBridges() {
+            return ImmutableSet.of(ROUT_BRIDGE);
+        }
+
+        @Override
+        public void addPort(BridgeName bridgeName, String portName) {
+
+        }
+
+        @Override
+        public void deletePort(BridgeName bridgeName, String portName) {
+
+        }
+
+        @Override
+        public Collection<PortDescription> getPorts() {
+            return ImmutableSet.of(PATCH_ROUT);
+        }
+
+        @Override
+        public Set<PortNumber> getPortNumbers() {
+            return null;
+        }
+
+        @Override
+        public List<PortNumber> getLocalPorts(Iterable<String> ifaceIds) {
+            return null;
+        }
+    }
+
+    private static class TestInterfaceConfig implements InterfaceConfig {
+
+        @Override
+        public DriverData data() {
+            return null;
+        }
+
+        @Override
+        public void setData(DriverData data) {
+
+        }
+
+        @Override
+        public DriverHandler handler() {
+            return null;
+        }
+
+        @Override
+        public void setHandler(DriverHandler handler) {
+
+        }
+
+        @Override
+        public boolean addAccessInterface(DeviceId deviceId, String intf, VlanId vlanId) {
+            return false;
+        }
+
+        @Override
+        public boolean addAccessMode(String intf, VlanId vlanId) {
+            return false;
+        }
+
+        @Override
+        public boolean removeAccessInterface(DeviceId deviceId, String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean removeAccessMode(String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean addTrunkInterface(DeviceId deviceId, String intf, List<VlanId> vlanIds) {
+            return false;
+        }
+
+        @Override
+        public boolean addTrunkMode(String intf, List<VlanId> vlanIds) {
+            return false;
+        }
+
+        @Override
+        public boolean removeTrunkInterface(DeviceId deviceId, String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean removeTrunkMode(String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean addRateLimit(String intf, short limit) {
+            return false;
+        }
+
+        @Override
+        public boolean removeRateLimit(String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean addTunnelMode(String intf, TunnelDescription tunnelDesc) {
+            TEST_DEVICE_SERVICE.devMap.values().stream()
+                    .filter(device -> device.type() == SWITCH &&
+                            device.manufacturer().equals(INTEGRATION_BRIDGE))
+                    .forEach(device -> {
+                        TEST_DEVICE_SERVICE.addPort(device, createPort(device, intf));
+                    });
+            return true;
+        }
+
+        @Override
+        public boolean removeTunnelMode(String intf) {
+            return false;
+        }
+
+        @Override
+        public boolean addPatchMode(String ifaceName, PatchDescription patchInterface) {
+            if (ifaceName.equals(PATCH_INTG_BRIDGE)) {
+                TEST_DEVICE_SERVICE.devMap.values().stream()
+                        .filter(device -> device.type() == SWITCH &&
+                                device.manufacturer().equals(INTEGRATION_BRIDGE))
+                        .forEach(device -> {
+                            TEST_DEVICE_SERVICE.addPort(device, createPort(device, ifaceName));
+                        });
+            } else if (ifaceName.equals(PATCH_ROUT_BRIDGE)) {
+                TEST_DEVICE_SERVICE.devMap.values().stream()
+                        .filter(device -> device.type() == SWITCH &&
+                                device.manufacturer().equals(ROUTER_BRIDGE))
+                        .forEach(device -> {
+                            TEST_DEVICE_SERVICE.addPort(device, createPort(device, ifaceName));
+                        });
+            }
+            return true;
+        }
+
+        @Override
+        public boolean removePatchMode(String ifaceName) {
+            return false;
+        }
+
+        @Override
+        public List<DeviceInterfaceDescription> getInterfaces(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public List<DeviceInterfaceDescription> getInterfaces() {
+            return null;
+        }
+    }
+
+    private static class TestGroupService implements GroupService {
+        Map<GroupKey, Group> groupMap = Maps.newHashMap();
+        Map<GroupKey, GroupBuckets> groupBucketsMap = Maps.newHashMap();
+        List<GroupListener> listeners = Lists.newArrayList();
+
+        @Override
+        public void addListener(GroupListener listener) {
+            listeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(GroupListener listener) {
+            listeners.remove(listener);
+        }
+
+        @Override
+        public void addGroup(GroupDescription groupDesc) {
+            DefaultGroup group = new DefaultGroup(GroupId.valueOf(groupDesc.givenGroupId()), groupDesc);
+            group.setState(Group.GroupState.ADDED);
+            groupMap.put(groupDesc.appCookie(), group);
+            groupBucketsMap.put(groupDesc.appCookie(), groupDesc.buckets());
+
+            GroupEvent event = new GroupEvent(GroupEvent.Type.GROUP_ADDED, group);
+            listeners.stream().filter(listener -> listener.isRelevant(event))
+                    .forEach(listener -> listener.event(event));
+        }
+
+        @Override
+        public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
+            return groupMap.get(appCookie);
+        }
+
+        @Override
+        public void addBucketsToGroup(DeviceId deviceId, GroupKey oldCookie, GroupBuckets buckets,
+                                      GroupKey newCookie, ApplicationId appId) {
+
+        }
+
+        @Override
+        public void removeBucketsFromGroup(DeviceId deviceId, GroupKey oldCookie, GroupBuckets buckets,
+                                           GroupKey newCookie, ApplicationId appId) {
+
+        }
+
+        @Override
+        public void purgeGroupEntries(DeviceId deviceId) {
+
+        }
+
+        @Override
+        public void removeGroup(DeviceId deviceId, GroupKey appCookie, ApplicationId appId) {
+
+        }
+
+        @Override
+        public Iterable<Group> getGroups(DeviceId deviceId, ApplicationId appId) {
+            return null;
+        }
+
+        @Override
+        public Iterable<Group> getGroups(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public void setBucketsForGroup(DeviceId deviceId, GroupKey oldCookie, GroupBuckets buckets,
+                                       GroupKey newCookie, ApplicationId appId) {
+            groupBucketsMap.put(newCookie, buckets);
+            GroupEvent event = new GroupEvent(GroupEvent.Type.GROUP_UPDATED, groupMap.get(newCookie));
+            listeners.stream().filter(listener -> listener.isRelevant(event))
+                    .forEach(listener -> listener.event(event));
+        }
+
+    }
+
+    private static class TestExtensionTreatment implements ExtensionTreatment {
+        Ip4Address tunnelDst;
+
+        @Override
+        public ExtensionTreatmentType type() {
+            return null;
+        }
+
+        @Override
+        public <T> void setPropertyValue(String key, T value) throws ExtensionPropertyException {
+            tunnelDst = (Ip4Address) value;
+        }
+
+        @Override
+        public <T> T getPropertyValue(String key) throws ExtensionPropertyException {
+            return null;
+        }
+
+        @Override
+        public List<String> getProperties() {
+            return null;
+        }
+
+        @Override
+        public byte[] serialize() {
+            return new byte[0];
+        }
+
+        @Override
+        public void deserialize(byte[] data) {
+
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            TestExtensionTreatment that = (TestExtensionTreatment) obj;
+            return Objects.equals(tunnelDst, that.tunnelDst);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(tunnelDst);
+        }
+    }
+
+    private static class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public ApplicationId getAppId(String name) {
+            return TEST_APP_ID;
+        }
+    }
+
+    private static class TestLeadershipService extends LeadershipServiceAdapter {
+
+        @Override
+        public NodeId getLeader(String path) {
+            return LOCAL_NODE_ID;
+        }
+    }
+
+    private static class TestClusterService extends ClusterServiceAdapter {
+
+        @Override
+        public ControllerNode getLocalNode() {
+            return LOCAL_CTRL;
+        }
+    }
+
+    private class TestComponentConfigService extends ComponentConfigAdapter {
+
+    }
+}