Fixes OpenstackSwitchingDirectPortProvider well even it there's interface added to the device already.

Change-Id: Ie1bdb6a5d65a1b5e964a94921ba919b259e76328
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DirectPortListCompleter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DirectPortListCompleter.java
new file mode 100644
index 0000000..d44b8d4
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DirectPortListCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.openstacknetworking.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.Port;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
+
+/**
+ * Direct port completer.
+ */
+public class DirectPortListCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+        Set<String> set = osNetService.ports().stream()
+                .filter(port -> port.getvNicType().equals(DIRECT))
+                .map(Port::getId)
+                .collect(Collectors.toSet());
+
+        SortedSet<String> strings = delegate.getStrings();
+        Iterator<String> it = set.iterator();
+
+        while (it.hasNext()) {
+            strings.add(it.next().toString());
+        }
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortAddCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortAddCommand.java
new file mode 100644
index 0000000..399b564
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortAddCommand.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.openstacknetworking.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.openstack4j.model.network.Port;
+
+import java.util.Optional;
+
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+
+/**
+ * When SR-IOV-based VM is instantiated while the ovsdb connection to the device is lost,
+ * VM is instantiated but the related VF port can't be added.
+ * After recovering ovsdb connection, you can manually add VF ports by this CLI.
+ */
+@Command(scope = "onos", name = "openstack-direct-port-add",
+        description = "Manually adds OpenStack direct ports to the device")
+public class OpenstackDirectPortAddCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "port ID", description = "port ID", required = true)
+    private String portId = null;
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
+
+        Port port = osNetService.port(portId);
+        if (port == null) {
+            log.error("There's no port that matches the port ID {}", portId);
+            return;
+        }
+
+        Optional<OpenstackNode> osNode = osNodeService.completeNodes(COMPUTE).stream()
+                .filter(node -> node.hostname().equals(port.getHostId()))
+                .findAny();
+        if (!osNode.isPresent()) {
+            log.error("There's no openstackNode that matches hostname {}",
+                    port.getHostId());
+            return;
+        }
+
+        String intfName = OpenstackNetworkingUtil.getIntfNameFromPciAddress(port);
+        if (intfName == null) {
+            log.error("Failed to retrieve interface name from a port {}", portId);
+            return;
+        }
+
+        if (OpenstackNetworkingUtil.hasIntfAleadyInDevice(osNode.get().intgBridge(),
+                intfName, deviceService)) {
+            log.error("Interface {} is already added to the device {}", osNode.get().intgBridge());
+            return;
+        }
+
+        log.info("Adding interface {} to the device {}..", intfName,
+                osNode.get().intgBridge());
+
+        osNodeService.addVfPort(osNode.get(), intfName);
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortListCommand.java
index 319a7fa..9466481 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDirectPortListCommand.java
@@ -43,9 +43,7 @@
         OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
 
         List<Port> ports = service.ports().stream()
-
                 .filter(port -> port.getvNicType().equals(DIRECT))
-                .filter(port -> port.isAdminStateUp() && !port.getVifType().equals(UNBOUND))
                 .collect(Collectors.toList());
 
 
@@ -56,12 +54,21 @@
                     .collect(Collectors.toList());
 
             Network osNet = service.network(port.getNetworkId());
-            print(FORMAT, port.getId(),
-                    osNet.getName(),
-                    port.getMacAddress(),
-                    fixedIps.isEmpty() ? "" : fixedIps,
-                    port.getProfile() == null ? "" : port.getProfile().get(PCISLOT).toString(),
-                    getIntfNameFromPciAddress(port));
+            if (port.getVifType().equals(UNBOUND)) {
+                print(FORMAT, port.getId(),
+                        osNet.getName(),
+                        port.getMacAddress(),
+                        fixedIps.isEmpty() ? "" : fixedIps,
+                        UNBOUND, UNBOUND);
+            } else {
+                print(FORMAT, port.getId(),
+                        osNet.getName(),
+                        port.getMacAddress(),
+                        fixedIps.isEmpty() ? "" : fixedIps,
+                        port.getProfile().containsKey(PCISLOT) ? port.getProfile().get(PCISLOT).toString() : "",
+                        getIntfNameFromPciAddress(port));
+            }
+
         }
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenStackSwitchingDirectPortProvider.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenStackSwitchingDirectPortProvider.java
index bd8473c..76c0197 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenStackSwitchingDirectPortProvider.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenStackSwitchingDirectPortProvider.java
@@ -30,6 +30,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.openstacknetworking.api.InstancePort;
@@ -37,7 +38,10 @@
 import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
 import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.onosproject.ovsdb.controller.OvsdbController;
 import org.openstack4j.model.network.Port;
@@ -47,13 +51,18 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Dictionary;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
+import static java.lang.Thread.sleep;
 import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getIntfNameFromPciAddress;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hasIntfAleadyInDevice;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
 
 @Component(immediate = true)
 public final class OpenStackSwitchingDirectPortProvider {
@@ -63,6 +72,7 @@
     private static final int DEFAULT_OVSDB_PORT = 6640;
     private static final String UNBOUND = "unbound";
     private static final String PORT_NAME = "portName";
+    private static final long SLEEP_MS = 3000;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -99,6 +109,7 @@
     private int ovsdbPort = DEFAULT_OVSDB_PORT;
 
     private final OpenstackNetworkListener openstackNetworkListener = new InternalOpenstackNetworkListener();
+    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
 
     private NodeId localNodeId;
     private ApplicationId appId;
@@ -110,6 +121,7 @@
         leadershipService.runForLeadership(appId.name());
         osNetworkService.addListener(openstackNetworkListener);
         componentConfigService.registerProperties(getClass());
+        osNodeService.addListener(internalNodeListener);
 
         log.info("Started");
     }
@@ -119,6 +131,7 @@
         leadershipService.withdraw(appId.name());
         osNetworkService.removeListener(openstackNetworkListener);
         componentConfigService.unregisterProperties(getClass(), false);
+        osNodeService.removeListener(internalNodeListener);
 
         log.info("Stopped");
     }
@@ -136,19 +149,12 @@
     }
     private void processPortAdded(Port port) {
         if (!port.getvNicType().equals(DIRECT)) {
-            log.trace("processPortAdded skipped because of unsupported vNicType: {}", port.getvNicType());
             return;
         } else if (!port.isAdminStateUp() || port.getVifType().equals(UNBOUND)) {
             log.trace("processPortAdded skipped because of status: {}, adminStateUp: {}, vifType: {}",
                     port.getState(), port.isAdminStateUp(), port.getVifType());
             return;
         } else {
-            InstancePort instancePort = instancePortService.instancePort(port.getId());
-            //Skip this if the instance port for the port id is already created.
-            if (instancePort != null) {
-                return;
-            }
-
             Optional<OpenstackNode> osNode = osNodeService.completeNodes(COMPUTE).stream()
                     .filter(node -> node.hostname().equals(port.getHostId()))
                     .findAny();
@@ -164,16 +170,30 @@
                 log.error("Failed to execute processPortAdded because of null interface name");
                 return;
             }
-
             log.trace("Retrieved interface name: {}", intfName);
 
+            try {
+                //If a VF port has been already added to the device for some reason, we remove it first,
+                //and the add VF so that other handlers run their logic.
+                if (hasIntfAleadyInDevice(osNode.get().intgBridge(),
+                        intfName, deviceService)) {
+                    log.trace("Device {} has already has VF interface {}, so remove first.",
+                            osNode.get().intgBridge(),
+                            intfName);
+                    osNodeService.removeVfPort(osNode.get(), intfName);
+                    //we wait 3000ms because the ovsdb client can't deal with removal/add at the same time.
+                    sleep(SLEEP_MS);
+                }
+            } catch (InterruptedException e) {
+                log.error("Exception occurred because of {}", e.toString());
+            }
+
             osNodeService.addVfPort(osNode.get(), intfName);
         }
     }
 
     private void processPortRemoved(Port port) {
         if (!port.getvNicType().equals(DIRECT)) {
-            log.trace("processPortRemoved skipped because of unsupported vNicType: {}", port.getvNicType());
             return;
         } else if (instancePortService.instancePort(port.getId()) == null) {
             log.trace("processPortRemoved skipped because no instance port exist for portId: {}", port.getId());
@@ -243,4 +263,72 @@
             }
         }
     }
+
+    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+
+            if (event.subject().type() == CONTROLLER) {
+                return false;
+            }
+            // do not allow to proceed without mastership
+            Device device = deviceService.getDevice(event.subject().intgBridge());
+            if (device == null) {
+                return false;
+            }
+            return mastershipService.isLocalMaster(device.id());
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+
+            switch (event.type()) {
+                case OPENSTACK_NODE_COMPLETE:
+                    log.info("COMPLETE node {} is detected", osNode.hostname());
+                    processComputeState(event.subject());
+
+                    break;
+                case OPENSTACK_NODE_INCOMPLETE:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
+                    // not reacts to the events other than complete and incomplete states
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void processComputeState(OpenstackNode node) {
+            List<Port> ports = osNetworkService.ports().stream()
+                    .filter(port -> port.getvNicType().equals(DIRECT))
+                    .filter(port -> !port.getVifType().equals(UNBOUND))
+                    .filter(port -> port.getHostId().equals(node.hostname()))
+                    .collect(Collectors.toList());
+
+            ports.forEach(port -> {
+                addIntfToDevice(node, port);
+            });
+
+
+
+        }
+
+        private void addIntfToDevice(OpenstackNode node, Port port) {
+            String intfName = OpenstackNetworkingUtil.getIntfNameFromPciAddress(port);
+            if (intfName == null) {
+                log.error("Failed to retrieve interface name from a port {}", port.getId());
+            }
+
+            if (!hasIntfAleadyInDevice(node.intgBridge(), intfName, deviceService)) {
+                log.debug("Port {} is bound to the interface {} but not added in the bridge {}. Adding it..",
+                        port.getId(),
+                        intfName,
+                        node.intgBridge());
+                osNodeService.addVfPort(node, intfName);
+            }
+        }
+    }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 9ece612..0253e4d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -21,6 +21,7 @@
 import com.google.common.base.Strings;
 import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
@@ -56,12 +57,15 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
 
 import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
 import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
 import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
@@ -289,8 +293,6 @@
      * @return interface name
      */
     public static String getIntfNameFromPciAddress(Port port) {
-
-
         if (port.getProfile() == null || port.getProfile().isEmpty()) {
             log.error("Port profile is not found");
             return null;
@@ -337,6 +339,22 @@
     }
 
     /**
+     * Check if the given interface is added to the given device or not.
+     *
+     * @param deviceId device ID
+     * @param intfName interface name
+     * @param deviceService device service
+     * @return true if the given interface is added to the given device or false otherwise
+     */
+    public static boolean hasIntfAleadyInDevice(DeviceId deviceId, String intfName, DeviceService deviceService) {
+        checkNotNull(deviceId);
+        checkNotNull(intfName);
+
+        return deviceService.getPorts(deviceId).stream()
+                .anyMatch(port -> Objects.equals(port.annotations().value(PORT_NAME), intfName));
+    }
+
+    /**
      * Adds router interfaces to openstack admin service.
      * TODO fix the logic to add router interface to router
      *
diff --git a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index b362e1a..84aa7e1 100644
--- a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -89,9 +89,16 @@
                 <ref component-id="instancePortIdCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackDirectPortAddCommand" />
+            <completers>
+                <ref component-id="directPortCompleter"/>
+            </completers>
+        </command>
     </command-bundle>
 
     <bean id="ipAddressCompleter" class="org.onosproject.openstacknetworking.cli.IpAddressCompleter"/>
+    <bean id="directPortCompleter" class="org.onosproject.openstacknetworking.cli.DirectPortListCompleter"/>
     <bean id="macAddressCompleter" class="org.onosproject.openstacknetworking.cli.MacAddressCompleter"/>
     <bean id="vlanIdCompleter" class="org.onosproject.openstacknetworking.cli.VlanIdCompleter"/>
     <bean id="arpModeCompleter" class="org.onosproject.openstacknetworking.cli.ArpModeCompleter"/>
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtilTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtilTest.java
index 270fbd6..be4f6d1 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtilTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtilTest.java
@@ -17,6 +17,7 @@
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.testing.EqualsTester;
@@ -27,9 +28,13 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultPort;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
 import org.onosproject.openstacknetworking.web.OpenstackFloatingIpWebResourceTest;
@@ -45,11 +50,15 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
 
 public final class OpenstackNetworkingUtilTest {
@@ -243,7 +252,7 @@
     }
 
     /**
-     * tests swapStaleLocation method.
+     * Tests swapStaleLocation method.
      */
     @Test
     public void testSwapStaleLocation() {
@@ -254,6 +263,22 @@
 
     }
 
+    /**
+     * Tests hasIntfAleadyInDevice method.
+     */
+    @Test
+    public void testHasIntfAleadyInDevice() {
+        DeviceService deviceService = new TestDeviceService();
+        assertTrue(OpenstackNetworkingUtil.hasIntfAleadyInDevice(DeviceId.deviceId("deviceId"),
+                "port1", deviceService));
+        assertTrue(OpenstackNetworkingUtil.hasIntfAleadyInDevice(DeviceId.deviceId("deviceId"),
+                "port2", deviceService));
+        assertTrue(OpenstackNetworkingUtil.hasIntfAleadyInDevice(DeviceId.deviceId("deviceId"),
+                "port3", deviceService));
+        assertFalse(OpenstackNetworkingUtil.hasIntfAleadyInDevice(DeviceId.deviceId("deviceId"),
+                "port4", deviceService));
+    }
+
     private OpenstackNode genGateway(int index) {
 
         Device intgBrg = InternalOpenstackNodeTest.createDevice(index);
@@ -273,4 +298,33 @@
 
     protected class InternalOpenstackNodeTest extends OpenstackNodeTest {
     }
+
+    /**
+     * Mocks the DeviceService.
+     */
+    private class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public List<org.onosproject.net.Port> getPorts(DeviceId deviceId) {
+            List<org.onosproject.net.Port> ports = Lists.newArrayList();
+            DefaultAnnotations.Builder annotations1 = DefaultAnnotations.builder()
+                    .set(PORT_NAME, "port1");
+            DefaultAnnotations.Builder annotations2 = DefaultAnnotations.builder()
+                    .set(PORT_NAME, "port2");
+            DefaultAnnotations.Builder annotations3 = DefaultAnnotations.builder()
+                    .set(PORT_NAME, "port3");
+
+            org.onosproject.net.Port port1 = new DefaultPort(null, PortNumber.portNumber(1),
+                    true, annotations1.build());
+            org.onosproject.net.Port port2 = new DefaultPort(null, PortNumber.portNumber(2),
+                    true, annotations2.build());
+            org.onosproject.net.Port port3 = new DefaultPort(null, PortNumber.portNumber(3),
+                    true, annotations3.build());
+
+            ports.add(port1);
+            ports.add(port2);
+            ports.add(port3);
+
+            return ports;
+        }
+    }
 }