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;
+ }
+}