Adds unit test for OpenstackRoutingIcmpHandler.

Change-Id: I764aa769c25a21ff410fa431cdc7552d6af1c059
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
index ee90e0d..67cedcc 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -68,14 +68,16 @@
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
+import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
-
 /**
  * Handles ICMP packet received from a gateway node.
  * For a request for virtual network subnet gateway, it generates fake ICMP reply.
@@ -148,50 +150,25 @@
         log.info("Stopped");
     }
 
-    private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
-        IPv4 ipPacket = (IPv4) ethernet.getPayload();
-        ICMP icmp = (ICMP) ipPacket.getPayload();
-        log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
-                        "dest MAC:{}, dest IP:{}",
-                ethernet.getSourceMAC(),
-                IpAddress.valueOf(ipPacket.getSourceAddress()),
-                ethernet.getDestinationMAC(),
-                IpAddress.valueOf(ipPacket.getDestinationAddress()));
-
-        switch (icmp.getIcmpType()) {
-            case ICMP.TYPE_ECHO_REQUEST:
-                handleEchoRequest(
-                        context.inPacket().receivedFrom().deviceId(),
-                        ethernet.getSourceMAC(),
-                        ipPacket,
-                        icmp);
-                context.block();
-                break;
-            case ICMP.TYPE_ECHO_REPLY:
-                handleEchoReply(ipPacket, icmp);
-                context.block();
-                break;
-            default:
-                break;
-        }
-    }
-
     private void handleEchoRequest(DeviceId srcDevice, MacAddress srcMac, IPv4 ipPacket,
                                    ICMP icmp) {
         InstancePort instPort = instancePortService.instancePort(srcMac);
         if (instPort == null) {
-            log.info(ERR_REQ + "unknown source host(MAC:{})", srcMac);
+            log.warn(ERR_REQ + "unknown source host(MAC:{})", srcMac);
             return;
         }
 
         IpAddress srcIp = IpAddress.valueOf(ipPacket.getSourceAddress());
+        IpAddress dstIp = IpAddress.valueOf(ipPacket.getDestinationAddress());
+
         Subnet srcSubnet = getSourceSubnet(instPort, srcIp);
         if (srcSubnet == null) {
-            log.info(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
+            log.warn(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
             return;
         }
+
         if (Strings.isNullOrEmpty(srcSubnet.getGateway())) {
-            log.info(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
+            log.warn(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
                     srcSubnet.getId(), srcSubnet.getCidr());
             return;
         }
@@ -199,43 +176,69 @@
         if (isForSubnetGateway(IpAddress.valueOf(ipPacket.getDestinationAddress()),
                 srcSubnet)) {
             // this is a request for the subnet gateway
+            log.trace("Icmp request to gateway {} from {}", dstIp, srcIp);
             processRequestForGateway(ipPacket, instPort);
         } else {
-            ExternalPeerRouter externalPeerRouter = externalPeerRouter(srcSubnet);
-            if (externalPeerRouter == null) {
-                log.info(ERR_REQ + "failed to get external peer router");
+            // this is a request for the external network
+            log.trace("Icmp request to external {} from {}", dstIp, srcIp);
+
+            RouterInterface routerInterface = routerInterface(srcSubnet);
+            if (routerInterface == null) {
+                log.warn(ERR_REQ + "failed to get router interface");
                 return;
             }
-            // this is a request for the external network
-            IpAddress externalIp = getExternalIp(srcSubnet);
+
+            ExternalGateway externalGateway = externalGateway(routerInterface);
+            if (externalGateway == null) {
+                log.warn(ERR_REQ + "failed to get external gateway");
+                return;
+            }
+
+            ExternalPeerRouter externalPeerRouter = osNetworkService.externalPeerRouter(externalGateway);
+            if (externalPeerRouter == null) {
+                log.warn(ERR_REQ + "failed to get external peer router");
+                return;
+            }
+
+            IpAddress externalIp = getExternalIp(externalGateway, routerInterface);
             if (externalIp == null) {
+                log.warn(ERR_REQ + "failed to get external ip");
                 return;
             }
 
             sendRequestForExternal(ipPacket, srcDevice, externalIp, externalPeerRouter);
-            String icmpInfoKey = String.valueOf(getIcmpId(icmp))
-                    .concat(String.valueOf(externalIp.getIp4Address().toInt()))
-                    .concat(String.valueOf(ipPacket.getDestinationAddress()));
+
+            String icmpInfoKey = icmpInfoKey(icmp,
+                    externalIp.toString(),
+                    IPv4.fromIPv4Address(ipPacket.getDestinationAddress()));
+            log.trace("Created icmpInfo key is {}", icmpInfoKey);
+
             try {
                 icmpInfoMap.compute(icmpInfoKey, (id, existing) -> {
                     checkArgument(existing == null, ERR_DUPLICATE);
                     return instPort;
                 });
             } catch (IllegalArgumentException e) {
-                log.warn("Exception occurred because of {}", e.toString());
+                log.warn("IllegalArgumentException occurred because of {}", e.toString());
+                return;
             }
-
         }
     }
 
-    private ExternalPeerRouter externalPeerRouter(Subnet subnet) {
-        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+    private String icmpInfoKey(ICMP icmp, String srcIp, String dstIp) {
+        return String.valueOf(getIcmpId(icmp))
+                .concat(srcIp)
+                .concat(dstIp);
+    }
+    private RouterInterface routerInterface(Subnet subnet) {
+        checkNotNull(subnet);
+        return osRouterService.routerInterfaces().stream()
                 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
                 .findAny().orElse(null);
-        if (osRouterIface == null) {
-            return null;
-        }
+    }
 
+    private ExternalGateway externalGateway(RouterInterface osRouterIface) {
+        checkNotNull(osRouterIface);
         Router osRouter = osRouterService.router(osRouterIface.getId());
         if (osRouter == null) {
             return null;
@@ -243,16 +246,14 @@
         if (osRouter.getExternalGatewayInfo() == null) {
             return null;
         }
-
-        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
-
-        return osNetworkService.externalPeerRouter(exGatewayInfo);
+        return osRouter.getExternalGatewayInfo();
     }
 
     private void handleEchoReply(IPv4 ipPacket, ICMP icmp) {
-        String icmpInfoKey = String.valueOf(getIcmpId(icmp))
-                .concat(String.valueOf(ipPacket.getDestinationAddress()))
-                .concat(String.valueOf(ipPacket.getSourceAddress()));
+        String icmpInfoKey = icmpInfoKey(icmp,
+                IPv4.fromIPv4Address(ipPacket.getDestinationAddress()),
+                IPv4.fromIPv4Address(ipPacket.getSourceAddress()));
+        log.trace("Retrieved icmpInfo key is {}", icmpInfoKey);
 
         if (icmpInfoMap.get(icmpInfoKey) != null) {
             processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey).value());
@@ -263,13 +264,15 @@
     }
 
     private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
+        checkNotNull(instance);
+        checkNotNull(srcIp);
+
         Port osPort = osNetworkService.port(instance.portId());
         IP fixedIp = osPort.getFixedIps().stream()
                 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
                 .findAny().orElse(null);
-        if (fixedIp == null) {
-            return null;
-        }
+        checkNotNull(fixedIp);
+
         return osNetworkService.subnet(fixedIp.getSubnetId());
     }
 
@@ -293,34 +296,22 @@
         return routableGateways.contains(dstIp);
     }
 
-    private IpAddress getExternalIp(Subnet srcSubnet) {
-        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
-                .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
-                .findAny().orElse(null);
-        if (osRouterIface == null) {
-            final String error = String.format(ERR_REQ +
-                    "subnet(ID:%s, CIDR:%s) is not connected to any router",
-                    srcSubnet.getId(), srcSubnet.getCidr());
-            throw new IllegalStateException(error);
-        }
+    private IpAddress getExternalIp(ExternalGateway externalGateway, RouterInterface osRouterIface) {
+        checkNotNull(externalGateway);
+        checkNotNull(osRouterIface);
 
         Router osRouter = osRouterService.router(osRouterIface.getId());
-        if (osRouter.getExternalGatewayInfo() == null) {
-            final String error = String.format(ERR_REQ +
-                    "router(ID:%s, name:%s) does not have external gateway",
-                    osRouter.getId(), osRouter.getName());
-            throw new IllegalStateException(error);
+        if (osRouter == null) {
+            return null;
         }
 
-        // TODO fix openstack4j for ExternalGateway provides external fixed IP list
-        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
-        Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
+        Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
                 .stream()
                 .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
                 .findAny().orElse(null);
         if (exGatewayPort == null) {
             final String error = String.format(ERR_REQ +
-                    "no external gateway port for router (ID:%s, name:%s)",
+                            "no external gateway port for router (ID:%s, name:%s)",
                     osRouter.getId(), osRouter.getName());
             throw new IllegalStateException(error);
         }
@@ -329,7 +320,8 @@
             final String error = String.format(ERR_REQ +
                             "no external gateway IP address for router (ID:%s, name:%s)",
                     osRouter.getId(), osRouter.getName());
-            throw new IllegalStateException(error);
+            log.warn(error);
+            return null;
         }
 
         return IpAddress.valueOf(externalIpAddress.get().getIpAddress());
@@ -338,7 +330,7 @@
     private void processRequestForGateway(IPv4 ipPacket, InstancePort instPort) {
         ICMP icmpReq = (ICMP) ipPacket.getPayload();
         icmpReq.setChecksum((short) 0);
-        icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
+        icmpReq.setIcmpType(TYPE_ECHO_REPLY);
 
         int destinationAddress = ipPacket.getSourceAddress();
 
@@ -442,8 +434,9 @@
 
             if (context.isHandled()) {
                 return;
-            } else if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
-                // return if the packet is not from gateway nodes
+            }
+
+            if (!gateways.isEmpty() && !gateways.contains(context.inPacket().receivedFrom().deviceId())) {
                 return;
             }
 
@@ -458,5 +451,32 @@
                 eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
             }
         }
+
+        private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+            IPv4 ipPacket = (IPv4) ethernet.getPayload();
+            ICMP icmp = (ICMP) ipPacket.getPayload();
+            log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
+                            "dest MAC:{}, dest IP:{}",
+                    ethernet.getSourceMAC(),
+                    IpAddress.valueOf(ipPacket.getSourceAddress()),
+                    ethernet.getDestinationMAC(),
+                    IpAddress.valueOf(ipPacket.getDestinationAddress()));
+
+            switch (icmp.getIcmpType()) {
+                case TYPE_ECHO_REQUEST:
+                    handleEchoRequest(context.inPacket().receivedFrom().deviceId(),
+                            ethernet.getSourceMAC(),
+                            ipPacket,
+                            icmp);
+                    context.block();
+                    break;
+                case TYPE_ECHO_REPLY:
+                    handleEchoReply(ipPacket, icmp);
+                    context.block();
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/IcmpEcho.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/IcmpEcho.java
new file mode 100644
index 0000000..5b78719
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/IcmpEcho.java
@@ -0,0 +1,269 @@
+/*
+ * 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.impl;
+
+import org.onlab.packet.Data;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * ICMP packet class for echo purpose.
+ */
+public class IcmpEcho extends ICMP {
+    protected byte icmpType;
+    protected byte icmpCode;
+    protected short checksum;
+    protected short identifier;
+    protected short sequenceNum;
+
+    public static final short ICMP_HEADER_LENGTH = 8;
+
+    /**
+     * @return the icmpType
+     */
+    public byte getIcmpType() {
+        return this.icmpType;
+    }
+
+    /**
+     * @param icmpType to set
+     * @return this
+     */
+    public IcmpEcho setIcmpType(final byte icmpType) {
+        this.icmpType = icmpType;
+        return this;
+    }
+
+    /**
+     * @return the icmp code
+     */
+    public byte getIcmpCode() {
+        return this.icmpCode;
+    }
+
+    /**
+     * @param icmpCode code to set
+     * @return this
+     */
+    public IcmpEcho setIcmpCode(final byte icmpCode) {
+        this.icmpCode = icmpCode;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return this.checksum;
+    }
+
+    /**
+     * @param checksum the checksum to set
+     * @return this
+     */
+    public IcmpEcho setChecksum(final short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    /**
+     * Sets the identifier.
+     *
+     * @param identifier identifier
+     * @return this
+     */
+    public IcmpEcho setIdentifier(final short identifier) {
+        this.identifier = identifier;
+        return this;
+    }
+
+    /**
+     * Sets the sequencer number.
+     *
+     * @param sequenceNum sequence number
+     * @return this
+     */
+
+    public IcmpEcho setSequenceNum(final short sequenceNum) {
+        this.sequenceNum = sequenceNum;
+        return this;
+    }
+
+    /**
+     * Gets the identifier.
+     *
+     * @return identifier
+     */
+    public short getIdentifier() {
+        return this.identifier;
+    }
+
+    /**
+     * Gets the sequence number.
+     *
+     * @return sequence number
+     */
+    public short getSequenceNum() {
+        return this.sequenceNum;
+    }
+
+    /**
+     * Serializes the packet. Will compute and set the following fields if they
+     * are set to specific values at the time serialize is called: -checksum : 0
+     * -length : 0
+     */
+    @Override
+    public byte[] serialize() {
+        int length = 8;
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+            length += payloadData.length;
+        }
+
+        final byte[] data = new byte[length];
+        final ByteBuffer bb = ByteBuffer.wrap(data);
+
+        bb.put(this.icmpType);
+        bb.put(this.icmpCode);
+        bb.putShort(this.checksum);
+        bb.putShort(this.identifier);
+        bb.putShort(this.sequenceNum);
+        if (payloadData != null) {
+            bb.put(payloadData);
+        }
+
+        if (this.parent != null && this.parent instanceof IPv4) {
+            ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_ICMP);
+        }
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+
+            for (int i = 0; i < length / 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            // pad to an even number of shorts
+            if (length % 2 > 0) {
+                accumulation += (bb.get() & 0xff) << 8;
+            }
+
+            accumulation = (accumulation >> 16 & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(2, this.checksum);
+        }
+        return data;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 5807;
+        int result = super.hashCode();
+        result = prime * result + this.icmpType;
+        result = prime * result + this.icmpCode;
+        result = prime * result + this.checksum;
+        result = prime * result + this.identifier;
+        result = prime * result + this.sequenceNum;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof IcmpEcho)) {
+            return false;
+        }
+        final IcmpEcho other = (IcmpEcho) obj;
+        if (this.icmpType != other.icmpType) {
+            return false;
+        }
+        if (this.icmpCode != other.icmpCode) {
+            return false;
+        }
+        if (this.checksum != other.checksum) {
+            return false;
+        }
+        if (this.identifier != other.identifier) {
+            return false;
+        }
+        if (this.sequenceNum != other.sequenceNum) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Deserializer function for ICMP packets.
+     *
+     * @return deserializer function
+     */
+    public static Deserializer<ICMP> deserializer() {
+        return (data, offset, length) -> {
+            checkInput(data, offset, length, ICMP_HEADER_LENGTH);
+
+            IcmpEcho icmp = new IcmpEcho();
+
+            final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+            icmp.icmpType = bb.get();
+            icmp.icmpCode = bb.get();
+            icmp.checksum = bb.getShort();
+            icmp.identifier = bb.getShort();
+            icmp.sequenceNum = bb.getShort();
+
+            icmp.payload = Data.deserializer()
+                    .deserialize(data, bb.position(), bb.limit()
+                            - bb.position());
+            icmp.payload.setParent(icmp);
+            return icmp;
+        };
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(getClass())
+                .add("icmpType", Byte.toString(icmpType))
+                .add("icmpCode", Byte.toString(icmpCode))
+                .add("checksum", Short.toString(checksum))
+                .add("identifier", Short.toString(identifier))
+                .add("sequenceNumber", Short.toString(sequenceNum))
+                .toString();
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRouterServiceAdapter.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRouterServiceAdapter.java
new file mode 100644
index 0000000..34341d2
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRouterServiceAdapter.java
@@ -0,0 +1,79 @@
+/*
+ * 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.impl;
+
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.Set;
+
+/**
+ * Test adapter for OpenstackRouterService.
+ */
+public class OpenstackRouterServiceAdapter implements OpenstackRouterService {
+    @Override
+    public Router router(String osRouterId) {
+        return null;
+    }
+
+    @Override
+    public Set<Router> routers() {
+        return null;
+    }
+
+    @Override
+    public RouterInterface routerInterface(String osRouterIfaceId) {
+        return null;
+    }
+
+    @Override
+    public Set<RouterInterface> routerInterfaces() {
+        return null;
+    }
+
+    @Override
+    public Set<RouterInterface> routerInterfaces(String osRouterId) {
+        return null;
+    }
+
+    @Override
+    public NetFloatingIP floatingIp(String floatingIpId) {
+        return null;
+    }
+
+    @Override
+    public Set<NetFloatingIP> floatingIps() {
+        return null;
+    }
+
+    @Override
+    public Set<NetFloatingIP> floatingIps(String routerId) {
+        return null;
+    }
+
+    @Override
+    public void addListener(OpenstackRouterListener listener) {
+
+    }
+
+    @Override
+    public void removeListener(OpenstackRouterListener listener) {
+
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandlerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandlerTest.java
new file mode 100644
index 0000000..ece96ee
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandlerTest.java
@@ -0,0 +1,511 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.DeserializationException;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdapter;
+import org.onosproject.store.service.TestStorageService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.onlab.packet.Ethernet.TYPE_IPV4;
+import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
+import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
+import static org.onosproject.net.NetTestTools.connectPoint;
+
+public class OpenstackRoutingIcmpHandlerTest {
+    private OpenstackRoutingIcmpHandler icmpHandler;
+    private static final byte CODE_ECHO_REQUEST = 0x00;
+    private static final byte CODE_ECHO_REPLY = 0x00;
+
+    protected PacketProcessor packetProcessor;
+    private InstancePort instancePort1;
+    private InstancePort instancePort2;
+    private InstancePort instancePort3;
+    private RouterInterface routerInterface1;
+    private Router router1;
+
+    private MacAddress srcMacPort1 = MacAddress.valueOf("11:22:33:44:55:66");
+    private IpAddress srcIpPort1 = IpAddress.valueOf("10.0.0.3");
+    private DeviceId srcDeviceId1 = DeviceId.deviceId("of:000000000000000a");
+    private PortNumber srcPortNum1 = PortNumber.portNumber(1);
+    private IpAddress targetIpToGw = IpAddress.valueOf("10.0.0.1");
+    private IpAddress targetIpToExternal = IpAddress.valueOf("8.8.8.8");
+    private IpAddress sNatIp = IpAddress.valueOf("172.27.0.13");
+    private MacAddress targetMac = Constants.DEFAULT_GATEWAY_MAC;
+    private MacAddress peerRouterMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
+    private Port port1;
+    private Port externalPort;
+    private Subnet subnet1;
+
+    Map<String, Port> portMap = Maps.newHashMap();
+    Map<String, InstancePort> instancePortMap = Maps.newHashMap();
+    Map<String, Router> osRouterMap = Maps.newHashMap();
+    Map<String, RouterInterface> osRouterInterfaceMap = Maps.newHashMap();
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+
+        icmpHandler = new OpenstackRoutingIcmpHandler();
+
+        icmpHandler.coreService = new TestCoreService();
+        icmpHandler.packetService = new TestPacketService();
+        icmpHandler.storageService = new TestStorageService();
+        icmpHandler.osNodeService = new TestOpenstackNodeService();
+        icmpHandler.instancePortService = new TestInstancePortService();
+        icmpHandler.osNetworkService = new TestOpenstackNetworkService();
+        icmpHandler.osRouterService = new TestOpenstackRouterService();
+        TestUtils.setField(icmpHandler, "eventExecutor", MoreExecutors.newDirectExecutorService());
+        icmpHandler.activate();
+
+        createPort();
+        createSubnet();
+        createInstancePortMap();
+        createRouterInterfaceMap();
+        createRouterMap();
+    }
+
+    /**
+     * Tears down all of this unit test.
+     */
+    @After
+    public void tearDown() {
+        icmpHandler.deactivate();
+
+    }
+
+    /**
+     * Tests the icmp request to gateway.
+     */
+    @Test
+    public void testRequestToGw() {
+        Ethernet icmpRequest = constructIcmpRequestPacket(srcIpPort1,
+                srcMacPort1,
+                targetIpToGw,
+                targetMac,
+                TYPE_ECHO_REQUEST);
+        sendPacket(icmpRequest);
+    }
+
+    /**
+     * Tests the icmp request to external.
+     */
+    @Test
+    public void testRequestToExternal() {
+        Ethernet icmpRequest = constructIcmpRequestPacket(srcIpPort1,
+                srcMacPort1,
+                targetIpToExternal,
+                targetMac,
+                TYPE_ECHO_REQUEST);
+
+        sendPacket(icmpRequest);
+
+        Ethernet icmpResponse = constructIcmpRequestPacket(targetIpToExternal,
+                peerRouterMac,
+                sNatIp,
+                targetMac,
+                TYPE_ECHO_REPLY);
+
+        sendPacket(icmpResponse);
+    }
+
+    private void sendPacket(Ethernet ethernet) {
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(ethernet.serialize());
+        InboundPacket inPacket = new DefaultInboundPacket(connectPoint(srcDeviceId1.toString(),
+                Integer.parseInt(srcPortNum1.toString())),
+                ethernet,
+                byteBuffer);
+
+        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
+        packetProcessor.process(context);
+    }
+
+    private void validatePacket(Ethernet ethernet) {
+        IPv4 ipPacket = (IPv4) ethernet.getPayload();
+
+        if (IPv4.fromIPv4Address(ipPacket.getSourceAddress()).equals(targetIpToGw.toString())) {
+            validateIcmpReqToGw(ipPacket);
+        } else if (IPv4.fromIPv4Address(ipPacket.getSourceAddress())
+                .equals(sNatIp.toString())) {
+            validateIcmpReqToExternal(ipPacket);
+        } else if (IPv4.fromIPv4Address(ipPacket.getSourceAddress())
+                .equals(targetIpToExternal.toString())) {
+            validateIcmpRespFromExternal(ipPacket);
+        }
+    }
+
+    private void validateIcmpRespFromExternal(IPv4 ipPacket) {
+        ICMP icmpResp = (ICMP) ipPacket.getPayload();
+        short icmpId = ByteBuffer.wrap(icmpResp.serialize(), 4, 2).getShort();
+        short seqNum = ByteBuffer.wrap(icmpResp.serialize(), 6, 2).getShort();
+
+        assertEquals(icmpResp.getIcmpType(), TYPE_ECHO_REPLY);
+        assertEquals(icmpResp.getIcmpCode(), CODE_ECHO_REPLY);
+        assertEquals(icmpId, 0);
+        assertEquals(seqNum, 0);
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), targetIpToExternal.toString());
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), srcIpPort1.toString());
+    }
+
+    private void validateIcmpReqToExternal(IPv4 ipPacket) {
+        ICMP icmpReq = (ICMP) ipPacket.getPayload();
+        short icmpId = ByteBuffer.wrap(icmpReq.serialize(), 4, 2).getShort();
+        short seqNum = ByteBuffer.wrap(icmpReq.serialize(), 6, 2).getShort();
+
+
+        assertEquals(icmpReq.getIcmpType(), TYPE_ECHO_REQUEST);
+        assertEquals(icmpReq.getIcmpCode(), CODE_ECHO_REQUEST);
+        assertEquals(icmpId, 0);
+        assertEquals(seqNum, 0);
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), sNatIp.toString());
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), targetIpToExternal.toString());
+
+    }
+    private void validateIcmpReqToGw(IPv4 ipPacket) {
+        ICMP icmpReq = (ICMP) ipPacket.getPayload();
+        short icmpId = ByteBuffer.wrap(icmpReq.serialize(), 4, 2).getShort();
+        short seqNum = ByteBuffer.wrap(icmpReq.serialize(), 6, 2).getShort();
+
+        assertEquals(icmpReq.getIcmpType(), TYPE_ECHO_REPLY);
+        assertEquals(icmpReq.getIcmpCode(), CODE_ECHO_REPLY);
+        assertEquals(icmpId, 0);
+        assertEquals(seqNum, 0);
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), targetIpToGw.toString());
+        assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), srcIpPort1.toString());
+    }
+
+    private Ethernet constructIcmpRequestPacket(IpAddress srcIp,
+                                                MacAddress srcMac,
+                                                IpAddress dstIp,
+                                                MacAddress dstMac, byte icmpType) {
+        try {
+            IcmpEcho icmp = new IcmpEcho();
+            if (icmpType == TYPE_ECHO_REQUEST) {
+                icmp.setIcmpType(TYPE_ECHO_REQUEST)
+                        .setIcmpCode(CODE_ECHO_REQUEST);
+            } else {
+                icmp.setIcmpType(TYPE_ECHO_REPLY)
+                        .setIcmpCode(CODE_ECHO_REPLY);
+            }
+
+            icmp.setChecksum((short) 0)
+                    .setIdentifier((short) 0)
+                    .setSequenceNum((short) 0);
+
+            ByteBuffer bb = ByteBuffer.wrap(icmp.serialize());
+
+            IPv4 iPacket = new IPv4();
+            iPacket.setDestinationAddress(dstIp.toString());
+            iPacket.setSourceAddress(srcIp.toString());
+            iPacket.setTtl((byte) 64);
+            iPacket.setChecksum((short) 0);
+            iPacket.setDiffServ((byte) 0);
+            iPacket.setProtocol(IPv4.PROTOCOL_ICMP);
+
+            iPacket.setPayload(ICMP.deserializer().deserialize(bb.array(), 0, 8));
+
+            Ethernet ethPacket = new Ethernet();
+
+            ethPacket.setEtherType(TYPE_IPV4);
+            ethPacket.setSourceMACAddress(srcMac);
+            ethPacket.setDestinationMACAddress(dstMac);
+            ethPacket.setPayload(iPacket);
+
+            return ethPacket;
+        } catch (DeserializationException e) {
+            return null;
+        }
+    }
+
+    private class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return new DefaultApplicationId(200, "test");
+        }
+    }
+
+    /**
+     * Mocks the PacketService.
+     */
+    private class TestPacketService extends PacketServiceAdapter {
+        @Override
+        public void addProcessor(PacketProcessor processor, int priority) {
+            packetProcessor = processor;
+        }
+
+        @Override
+        public void emit(OutboundPacket packet) {
+            try {
+                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
+                        0, packet.data().array().length);
+                validatePacket(eth);
+            } catch (Exception e) {
+                fail(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Mocks the OpenstackNodeService.
+     */
+    private class TestOpenstackNodeService extends OpenstackNodeServiceAdapter {
+
+        @Override
+        public OpenstackNode node(DeviceId deviceId) {
+            return new TestOpenstackNode();
+        }
+    }
+
+    /**
+     * Mocks the InstancePortService.
+     */
+    private class TestInstancePortService extends InstancePortServiceAdapter {
+        @Override
+        public InstancePort instancePort(MacAddress macAddress) {
+            return instancePortMap.values().stream()
+                    .filter(port -> Objects.equals(port.macAddress(), macAddress))
+                    .findAny().orElse(null);
+        }
+
+        @Override
+        public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
+            return instancePortMap.values().stream()
+                    .filter(port -> port.networkId().equals(osNetId))
+                    .filter(port -> port.ipAddress().equals(ipAddress))
+                    .findFirst().orElse(null);
+        }
+
+        @Override
+        public InstancePort instancePort(String osPortId) {
+            return instancePortMap.get(osPortId);
+        }
+
+        @Override
+        public Set<InstancePort> instancePorts() {
+            return ImmutableSet.copyOf(instancePortMap.values());
+        }
+
+        @Override
+        public Set<InstancePort> instancePorts(String osNetId) {
+            Set<InstancePort> ports = instancePortMap.values().stream()
+                    .filter(port -> port.networkId().equals(osNetId))
+                    .collect(Collectors.toSet());
+
+            return ImmutableSet.copyOf(ports);
+        }
+    }
+
+    /**
+     * Mocks the OpenstackNetworkService.
+     */
+    private class TestOpenstackNetworkService extends OpenstackNetworkServiceAdapter {
+        @Override
+        public Set<Port> ports(String netId) {
+            return ImmutableSet.copyOf(portMap.values());
+        }
+        @Override
+        public Port port(String portId) {
+
+            return port1;
+        }
+
+        @Override
+        public Subnet subnet(String subnetId) {
+
+            return subnet1;
+        }
+
+        @Override
+        public ExternalPeerRouter externalPeerRouter(ExternalGateway externalGateway) {
+            return DefaultExternalPeerRouter.builder()
+                    .ipAddress(IpAddress.valueOf("172.27.0.1"))
+                    .macAddress(peerRouterMac)
+                    .vlanId(VlanId.NONE)
+                    .build();
+        }
+    }
+
+    /**
+     * Mocks the OpenstackRouterService.
+     */
+    private class TestOpenstackRouterService extends OpenstackRouterServiceAdapter {
+        @Override
+        public Set<RouterInterface> routerInterfaces() {
+            return ImmutableSet.copyOf(osRouterInterfaceMap.values());
+        }
+
+        @Override
+        public Router router(String osRouterId) {
+            return osRouterMap.get(osRouterId);
+        }
+        public Set<RouterInterface> routerInterfaces(String osRouterId) {
+            return osRouterInterfaceMap.values().stream()
+                    .filter(iface -> iface.getId().equals(osRouterId))
+                    .collect(Collectors.toSet());
+        }
+
+    }
+
+    /**
+     * Mocks the DefaultPacket context.
+     */
+    private final class TestPacketContext extends DefaultPacketContext {
+        private TestPacketContext(long time, InboundPacket inPkt,
+                                  OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+            // We don't send anything out.
+        }
+    }
+
+
+    private void createPort() {
+        InputStream portJsonStream1 = OpenstackRoutingIcmpHandlerTest.class
+                .getResourceAsStream("openstack-port-1.json");
+        port1 = (Port) OpenstackNetworkingUtil.jsonToModelEntity(portJsonStream1, NeutronPort.class);
+
+        InputStream portJsonStream2 = OpenstackRoutingIcmpHandlerTest.class
+                .getResourceAsStream("openstack-port-external.json");
+        externalPort = (Port) OpenstackNetworkingUtil.jsonToModelEntity(portJsonStream2, NeutronPort.class);
+
+        portMap.put(port1.getId(), port1);
+        portMap.put(externalPort.getId(), externalPort);
+
+    }
+
+    private void createSubnet() {
+        InputStream subnetJsonStream1 = OpenstackRoutingIcmpHandlerTest.class
+                .getResourceAsStream("openstack-subnet-1.json");
+        subnet1 = (Subnet) OpenstackNetworkingUtil.jsonToModelEntity(subnetJsonStream1, NeutronSubnet.class);
+    }
+
+    private void createRouterInterfaceMap() {
+        routerInterface1 = new TestRouterInterface("router-id-1",
+                "subnet-id-1",
+                "router-interface-id-1",
+                "tenant-id-1");
+
+        osRouterInterfaceMap.put(routerInterface1.getPortId(), routerInterface1);
+    }
+
+    private void createRouterMap() {
+        InputStream routerStream1 = OpenstackRoutingIcmpHandlerTest.class
+                .getResourceAsStream("openstack-router-1.json");
+        router1 = (Router)
+                OpenstackNetworkingUtil.jsonToModelEntity(routerStream1, NeutronRouter.class);
+        osRouterMap.put(router1.getId(), router1);
+
+    }
+    private void createInstancePortMap() {
+        instancePort1 = DefaultInstancePort.builder()
+                .networkId("net-id-1")
+                .portId("ce705c24-c1ef-408a-bda3-7bbd946164ab")
+                .deviceId(srcDeviceId1)
+                .portNumber(srcPortNum1)
+                .ipAddress(srcIpPort1)
+                .macAddress(srcMacPort1)
+                .state(InstancePort.State.valueOf("ACTIVE"))
+                .build();
+
+        instancePort2 = DefaultInstancePort.builder()
+                .networkId("net-id-2")
+                .portId("port-id-2")
+                .deviceId(DeviceId.deviceId("of:000000000000000b"))
+                .portNumber(PortNumber.portNumber(2))
+                .ipAddress(IpAddress.valueOf("10.10.10.2"))
+                .macAddress(MacAddress.valueOf("22:33:44:55:66:11"))
+                .state(InstancePort.State.valueOf("ACTIVE"))
+                .build();
+
+        instancePort3 = DefaultInstancePort.builder()
+                .networkId("net-id-3")
+                .portId("port-id-3")
+                .deviceId(DeviceId.deviceId("of:000000000000000c"))
+                .oldDeviceId(DeviceId.deviceId("of:000000000000000d"))
+                .oldPortNumber(PortNumber.portNumber(4, "tap-4"))
+                .portNumber(PortNumber.portNumber(3, "tap-3"))
+                .ipAddress(IpAddress.valueOf("10.10.10.3"))
+                .macAddress(MacAddress.valueOf("33:44:55:66:11:22"))
+                .state(InstancePort.State.valueOf("ACTIVE"))
+                .build();
+
+        instancePortMap.put(instancePort1.portId(), instancePort1);
+        instancePortMap.put(instancePort2.portId(), instancePort2);
+        instancePortMap.put(instancePort3.portId(), instancePort3);
+    }
+
+
+    private class TestOpenstackNode extends OpenstackNodeAdapter {
+        public TestOpenstackNode() {
+            super();
+        }
+        @Override
+        public PortNumber uplinkPortNum() {
+            return PortNumber.portNumber(1);
+        }
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/TestRouterInterface.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/TestRouterInterface.java
new file mode 100644
index 0000000..c23ccd8
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/TestRouterInterface.java
@@ -0,0 +1,56 @@
+/*
+ * 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.impl;
+
+import org.openstack4j.model.network.RouterInterface;
+
+/**
+ * Test implementation class of router interface.
+ */
+public final class TestRouterInterface implements RouterInterface {
+    private final String id;
+    private final String subnetId;
+    private final String portId;
+    private final String tenantId;
+
+    public TestRouterInterface(String id, String subnetId,
+                               String portId, String tenantId) {
+        this.id = id;
+        this.subnetId = subnetId;
+        this.portId = portId;
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getSubnetId() {
+        return subnetId;
+    }
+
+    @Override
+    public String getPortId() {
+        return portId;
+    }
+
+    @Override
+    public String getTenantId() {
+        return tenantId;
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-1.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-1.json
new file mode 100644
index 0000000..e70259f
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-1.json
@@ -0,0 +1,39 @@
+{
+  "port": {
+    "id": "port-id-1",
+    "admin_state_up": true,
+    "device_id": "device-id-1",
+    "device_owner": "compute:nova",
+    "fixed_ips": [
+      {
+        "ip_address": "10.0.0.3",
+        "subnet_id": "subnet-id-1"
+      }
+    ],
+    "allowed_address_pairs": [
+      {
+        "ip_address": "12.12.11.12",
+        "mac_address": "fa:14:2a:b3:cb:f0"
+      }
+    ],
+    "mac_address": "11:22:33:44:55:66",
+    "network_id": "network-id-1",
+    "status": "ACTIVE",
+    "tenant_id": "tenant-id-1",
+    "security_groups": [
+      "security-group-1"
+    ],
+    "extra_dhcp_opts": [
+      {
+        "opt_value": "pxelinux.0",
+        "opt_name": "bootfile-name"
+      }
+    ],
+    "port_security_enabled": true,
+    "binding:host_id": "4df8d9ff-6f6f-438f-90a1-ef660d4586ad",
+    "binding:vif_type": "unbound",
+    "binding:vif_details": {},
+    "binding:vnic_type": "other",
+    "binding:profile": {}
+  }
+}
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-external.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-external.json
new file mode 100644
index 0000000..37d0576
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-port-external.json
@@ -0,0 +1,34 @@
+{
+  "port": {
+    "id": "port-id-ext",
+    "admin_state_up": true,
+    "device_id": "router-id-1",
+    "device_owner": "compute:nova",
+    "fixed_ips": [
+      {
+        "ip_address": "172.27.0.13",
+        "subnet_id": "subnet-id-ext"
+      }
+    ],
+    "allowed_address_pairs": [],
+    "mac_address": "zz:xx:cc:vv:bb:nn",
+    "network_id": "network-id-ext",
+    "status": "ACTIVE",
+    "tenant_id": "tenant-id-1",
+    "security_groups": [
+      "security-group-1"
+    ],
+    "extra_dhcp_opts": [
+      {
+        "opt_value": "pxelinux.0",
+        "opt_name": "bootfile-name"
+      }
+    ],
+    "port_security_enabled": true,
+    "binding:host_id": "",
+    "binding:vif_type": "unbound",
+    "binding:vif_details": {},
+    "binding:vnic_type": "other",
+    "binding:profile": {}
+  }
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-router-1.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-router-1.json
new file mode 100644
index 0000000..53670d3
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-router-1.json
@@ -0,0 +1,20 @@
+{
+  "router": {
+    "id": "router-id-1",
+    "name": "router-1",
+    "status": "ACTIVE",
+    "external_gateway_info": {
+      "network_id": "network-id-ext",
+      "enable_snat": false
+    },
+    "admin_state_up": true,
+    "tenant_id": "tenant-id-1",
+    "routes": [
+      {
+        "destination": "string",
+        "nexthop": "string"
+      }
+    ],
+    "distributed": false
+  }
+}
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-subnet-1.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-subnet-1.json
new file mode 100644
index 0000000..6589346
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/impl/openstack-subnet-1.json
@@ -0,0 +1,27 @@
+{
+  "subnet": {
+    "id": "subnet-id-1",
+    "enable_dhcp": true,
+    "network_id": "network-id-1",
+    "tenant_id": "tenant-id-1",
+    "dns_nameservers": [
+      "8.8.8.8"
+    ],
+    "allocation_pools": [
+      {
+        "start": "10.0.0.2",
+        "end": "10.0.0.200"
+      }
+    ],
+    "host_routes": [
+      {
+        "destination": "",
+        "nexthop": ""
+      }
+    ],
+    "ip_version": 4,
+    "gateway_ip": "10.0.0.1",
+    "ipv6_address_mode": "",
+    "ipv6_ra_mode": ""
+  }
+}
diff --git a/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java b/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java
new file mode 100644
index 0000000..98a34a3
--- /dev/null
+++ b/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java
@@ -0,0 +1,146 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.group.GroupKey;
+
+import java.util.Collection;
+
+public class OpenstackNodeAdapter implements OpenstackNode {
+    public OpenstackNodeAdapter() {
+
+    }
+    @Override
+    public String hostname() {
+        return null;
+    }
+
+    @Override
+    public OpenstackNode.NodeType type() {
+        return null;
+    }
+
+    @Override
+    public DeviceId ovsdb() {
+        return null;
+    }
+
+    @Override
+    public DeviceId intgBridge() {
+        return null;
+    }
+
+    @Override
+    public IpAddress managementIp() {
+        return null;
+    }
+
+    @Override
+    public IpAddress dataIp() {
+        return null;
+    }
+
+    @Override
+    public String vlanIntf() {
+        return null;
+    }
+
+    @Override
+    public NodeState state() {
+        return null;
+    }
+
+    @Override
+    public GroupId gatewayGroupId(OpenstackNode.NetworkMode mode) {
+        return null;
+    }
+
+    @Override
+    public GroupKey gatewayGroupKey(OpenstackNode.NetworkMode mode) {
+        return null;
+    }
+
+    @Override
+    public PortNumber tunnelPortNum() {
+        return null;
+    }
+
+    @Override
+    public PortNumber vlanPortNum() {
+        return null;
+    }
+
+    @Override
+    public PortNumber patchPortNum() {
+        return null;
+    }
+
+    @Override
+    public MacAddress vlanPortMac() {
+        return null;
+    }
+
+    @Override
+    public String uplinkPort() {
+        return null;
+    }
+
+    @Override
+    public PortNumber uplinkPortNum() {
+        return null;
+    }
+
+    @Override
+    public OpenstackNode updateState(NodeState newState) {
+        return null;
+    }
+
+    @Override
+    public Collection<OpenstackPhyInterface> phyIntfs() {
+        return null;
+    }
+
+    @Override
+    public PortNumber phyIntfPortNum(String providerPhysnet) {
+        return null;
+    }
+
+    @Override
+    public OpenstackAuth authentication() {
+        return null;
+    }
+
+    @Override
+    public String endPoint() {
+        return null;
+    }
+
+    @Override
+    public Collection<ControllerInfo> controllers() {
+        return null;
+    }
+
+    @Override
+    public OpenstackSshAuth sshAuthInfo() {
+        return null;
+    }
+}