Support JSON output in external router CLI, make router obj immutable

Change-Id: I6c76d9aafd64c1af7c3e28b42beabc268f824b88
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java
index d42d337..3f09108 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java
@@ -23,24 +23,62 @@
  * Representation of external peer router.
  */
 public interface ExternalPeerRouter {
+
     /**
-     * Returns external peer router ip address.
+     * Returns external peer router IP address.
      *
      * @return ip address.
      */
-    IpAddress externalPeerRouterIp();
+    IpAddress ipAddress();
 
     /**
-     * Returns external peer router mac address.
+     * Returns external peer router MAC address.
      *
      * @return mac address
      */
-    MacAddress externalPeerRouterMac();
+    MacAddress macAddress();
 
     /**
-     * Returns external peer router vlan id.
+     * Returns external peer router VLAN ID.
      *
      * @return vlan id
      */
-    VlanId externalPeerRouterVlanId();
+    VlanId vlanId();
+
+    /**
+     * Builder of new external peer router.
+     */
+    interface Builder {
+
+        /**
+         * Builds an immutable external peer router instance.
+         *
+         * @return external peer router
+         */
+        ExternalPeerRouter build();
+
+        /**
+         * Returns router builder with supplied IP address.
+         *
+         * @param ipAddress IP address of external peer router
+         * @return peer router builder
+         */
+        Builder ipAddress(IpAddress ipAddress);
+
+        /**
+         * Returns router builder with supplied MAC address.
+         *
+         * @param macAddress MAC address of external peer router
+         * @return peer router builder
+         */
+        Builder macAddress(MacAddress macAddress);
+
+        /**
+         * Returns router builder with supplied VLAN ID.
+         *
+         * @param vlanId VLAN ID of external peer router
+         * @return peer router builder
+         */
+        Builder vlanId(VlanId vlanId);
+    }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DeleteExternalPeerRouterCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DeleteExternalPeerRouterCommand.java
index 551a871..0ef3861 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DeleteExternalPeerRouterCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/DeleteExternalPeerRouterCommand.java
@@ -42,7 +42,7 @@
         OpenstackNetworkAdminService service = AbstractShellCommand.get(OpenstackNetworkAdminService.class);
 
         if (service.externalPeerRouters().stream()
-                .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
+                .noneMatch(router -> router.ipAddress().toString().equals(ipAddress))) {
             print(NO_ELEMENT);
             return;
         }
@@ -56,9 +56,9 @@
         List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
 
         for (ExternalPeerRouter router: routers) {
-            print(FORMAT, router.externalPeerRouterIp(),
-                    router.externalPeerRouterMac().toString(),
-                    router.externalPeerRouterVlanId());
+            print(FORMAT, router.ipAddress(),
+                    router.macAddress().toString(),
+                    router.vlanId());
         }
 
     }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
index ebd949c..ae30021 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
@@ -15,6 +15,9 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
@@ -23,6 +26,8 @@
 
 import java.util.List;
 
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
 /**
  * Lists external peer router lists.
  */
@@ -35,14 +40,25 @@
     @Override
     protected void execute() {
         OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
-
-        print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
         List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
 
-        for (ExternalPeerRouter router: routers) {
-            print(FORMAT, router.externalPeerRouterIp(),
-                    router.externalPeerRouterMac().toString(),
-                    router.externalPeerRouterVlanId());
+        if (outputJson()) {
+            print("%s", json(this, routers));
+        } else {
+            print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
+            for (ExternalPeerRouter router: routers) {
+                print(FORMAT, router.ipAddress(),
+                        router.macAddress().toString(),
+                        router.vlanId());
+            }
         }
     }
+
+    private JsonNode json(AbstractShellCommand context, List<ExternalPeerRouter> routers) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.enable(INDENT_OUTPUT).createArrayNode();
+        routers.forEach(r -> result.add(context.jsonForEntity(r, ExternalPeerRouter.class)));
+
+        return result;
+    }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java
index 4ae1dd4..bac877d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java
@@ -38,7 +38,7 @@
         StringsCompleter delegate = new StringsCompleter();
         OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
         Set<IpAddress> set = osNetService.externalPeerRouters().stream()
-                .map(ExternalPeerRouter::externalPeerRouterIp)
+                .map(ExternalPeerRouter::ipAddress)
                 .collect(Collectors.toSet());
         SortedSet<String> strings = delegate.getStrings();
 
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java
index 6e21de6..3b6e475 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java
@@ -38,7 +38,7 @@
         StringsCompleter delegate = new StringsCompleter();
         OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
         Set<MacAddress> set = osNetService.externalPeerRouters().stream()
-                .map(ExternalPeerRouter::externalPeerRouterMac)
+                .map(ExternalPeerRouter::macAddress)
                 .collect(Collectors.toSet());
         SortedSet<String> strings = delegate.getStrings();
 
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java
index 5667fa8..c4090e0 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java
@@ -62,7 +62,7 @@
             print(NO_ELEMENT);
             return;
         } else if (service.externalPeerRouters().stream()
-                .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
+                .noneMatch(router -> router.ipAddress().toString().equals(ipAddress))) {
             print(NO_ELEMENT);
             return;
         }
@@ -86,9 +86,9 @@
         List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
 
         for (ExternalPeerRouter router: routers) {
-            print(FORMAT, router.externalPeerRouterIp(),
-                    router.externalPeerRouterMac().toString(),
-                    router.externalPeerRouterVlanId());
+            print(FORMAT, router.ipAddress(),
+                    router.macAddress().toString(),
+                    router.vlanId());
         }
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
index 3dc14a7..79125b2 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
@@ -61,7 +61,7 @@
             print(NO_ELEMENT);
             return;
         } else if (osNetAdminService.externalPeerRouters().stream()
-                .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
+                .noneMatch(router -> router.ipAddress().toString().equals(ipAddress))) {
             print(NO_ELEMENT);
             return;
         }
@@ -104,9 +104,9 @@
         List<ExternalPeerRouter> routers = Lists.newArrayList(osNetAdminService.externalPeerRouters());
 
         for (ExternalPeerRouter r: routers) {
-            print(FORMAT, r.externalPeerRouterIp(),
-                    r.externalPeerRouterMac().toString(),
-                    r.externalPeerRouterVlanId());
+            print(FORMAT, r.ipAddress(),
+                    r.macAddress().toString(),
+                    r.vlanId());
         }
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java
index 0768859..8f64642 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java
@@ -38,7 +38,7 @@
         StringsCompleter delegate = new StringsCompleter();
         OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
         Set<VlanId> set = osNetService.externalPeerRouters().stream()
-                .map(ExternalPeerRouter::externalPeerRouterVlanId)
+                .map(ExternalPeerRouter::vlanId)
                 .collect(Collectors.toSet());
         SortedSet<String> strings = delegate.getStrings();
 
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodec.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodec.java
new file mode 100644
index 0000000..e77c0ef
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodec.java
@@ -0,0 +1,73 @@
+/*
+ * 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.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.impl.DefaultExternalPeerRouter;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Openstack external peer router codec used for serializing and de-serializing JSON string.
+ */
+public class ExternalPeerRouterCodec extends JsonCodec<ExternalPeerRouter> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String IP_ADDRESS = "ipAddress";
+    private static final String MAC_ADDRESS = "macAddress";
+    private static final String VLAN_ID = "vlanId";
+
+    private static final String MISSING_MESSAGE = " is required in ExternalPeerRouter";
+
+    @Override
+    public ObjectNode encode(ExternalPeerRouter router, CodecContext context) {
+        checkNotNull(router, "External peer router cannot be null");
+
+        return context.mapper().createObjectNode()
+                .put(IP_ADDRESS, router.ipAddress().toString())
+                .put(MAC_ADDRESS, router.macAddress().toString())
+                .put(VLAN_ID, router.vlanId().toString());
+    }
+
+    @Override
+    public ExternalPeerRouter decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String ipAddress = nullIsIllegal(json.get(IP_ADDRESS).asText(),
+                IP_ADDRESS + MISSING_MESSAGE);
+        String macAddress = nullIsIllegal(json.get(MAC_ADDRESS).asText(),
+                MAC_ADDRESS + MISSING_MESSAGE);
+        String vlanId = nullIsIllegal(json.get(VLAN_ID).asText(),
+                VLAN_ID + MISSING_MESSAGE);
+
+        return DefaultExternalPeerRouter.builder()
+                .ipAddress(IpAddress.valueOf(ipAddress))
+                .macAddress(MacAddress.valueOf(macAddress))
+                .vlanId(VlanId.vlanId(vlanId)).build();
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java
index 57b997c..0f44cb8 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java
@@ -23,34 +23,41 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 /**
  * Implementation of external peer router.
  */
 public final class DefaultExternalPeerRouter implements ExternalPeerRouter {
 
-    private final IpAddress externalPeerRouterIp;
-    private final MacAddress externalPeerRouterMac;
-    private final VlanId externalPeerRouterVlanId;
+    private final IpAddress ipAddress;
+    private final MacAddress macAddress;
+    private final VlanId vlanId;
 
-    public DefaultExternalPeerRouter(IpAddress externalPeerRouterIp,
-                                     MacAddress externalPeerRouterMac,
-                                     VlanId externalPeerRouterVlanId) {
-        this.externalPeerRouterIp = externalPeerRouterIp;
-        this.externalPeerRouterMac = externalPeerRouterMac;
-        this.externalPeerRouterVlanId = externalPeerRouterVlanId;
+    private static final String NOT_NULL_MSG = "External Peer Router % cannot be null";
+
+    // private constructor not intended for invoked from external
+    private DefaultExternalPeerRouter(IpAddress ipAddress,
+                                     MacAddress macAddress,
+                                     VlanId vlanId) {
+        this.ipAddress = ipAddress;
+        this.macAddress = macAddress;
+        this.vlanId = vlanId;
     }
 
     @Override
-    public IpAddress externalPeerRouterIp() {
-        return this.externalPeerRouterIp;
+    public IpAddress ipAddress() {
+        return this.ipAddress;
     }
+
     @Override
-    public MacAddress externalPeerRouterMac() {
-        return this.externalPeerRouterMac;
+    public MacAddress macAddress() {
+        return this.macAddress;
     }
+
     @Override
-    public VlanId externalPeerRouterVlanId() {
-        return this.externalPeerRouterVlanId;
+    public VlanId vlanId() {
+        return this.vlanId;
     }
 
     @Override
@@ -61,26 +68,75 @@
 
         if (obj instanceof DefaultExternalPeerRouter) {
             DefaultExternalPeerRouter that = (DefaultExternalPeerRouter) obj;
-            return Objects.equals(externalPeerRouterIp, that.externalPeerRouterIp) &&
-                    Objects.equals(externalPeerRouterMac, that.externalPeerRouterMac) &&
-                    Objects.equals(externalPeerRouterVlanId, that.externalPeerRouterVlanId);
+            return Objects.equals(ipAddress, that.ipAddress) &&
+                    Objects.equals(macAddress, that.macAddress) &&
+                    Objects.equals(vlanId, that.vlanId);
         }
         return false;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(externalPeerRouterIp,
-                externalPeerRouterMac,
-                externalPeerRouterVlanId);
+        return Objects.hash(ipAddress, macAddress, vlanId);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("externalPeerRouterIp", externalPeerRouterIp)
-                .add("externalPeerRouterMac", externalPeerRouterMac)
-                .add("externalPeerRouterVlanId", externalPeerRouterVlanId)
+                .add("ipAddress", ipAddress)
+                .add("macAddress", macAddress)
+                .add("vlanId", vlanId)
                 .toString();
     }
+
+    /**
+     * Obtains an external peer router builder.
+     *
+     * @return external peer router builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * A builder class for external peer router.
+     */
+    public static final class Builder implements ExternalPeerRouter.Builder {
+
+        private IpAddress ipAddress;
+        private MacAddress macAddress;
+        private VlanId vlanId;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        @Override
+        public ExternalPeerRouter build() {
+
+            checkArgument(ipAddress != null, NOT_NULL_MSG, "IP address");
+            checkArgument(macAddress != null, NOT_NULL_MSG, "MAC address");
+            checkArgument(vlanId != null, NOT_NULL_MSG, "VLAN ID");
+
+            return new DefaultExternalPeerRouter(ipAddress, macAddress, vlanId);
+        }
+
+        @Override
+        public Builder ipAddress(IpAddress ipAddress) {
+            this.ipAddress = ipAddress;
+            return this;
+        }
+
+        @Override
+        public Builder macAddress(MacAddress macAddress) {
+            this.macAddress = macAddress;
+            return this;
+        }
+
+        @Override
+        public Builder vlanId(VlanId vlanId) {
+            this.vlanId = vlanId;
+            return this;
+        }
+    }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index bceb9ea..3ca0232 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -442,7 +442,7 @@
 
         if (externalPeerRouterMap.containsKey(targetIp.toString()) &&
                 !externalPeerRouterMap.get(
-                        targetIp.toString()).value().externalPeerRouterMac().equals(MacAddress.NONE)) {
+                        targetIp.toString()).value().macAddress().equals(MacAddress.NONE)) {
             return;
         }
 
@@ -479,8 +479,12 @@
                 treatment,
                 ByteBuffer.wrap(ethRequest.serialize())));
 
-        externalPeerRouterMap.put(
-                targetIp.toString(), new DefaultExternalPeerRouter(targetIp, MacAddress.NONE, vlanId));
+        externalPeerRouterMap.put(targetIp.toString(),
+                                    DefaultExternalPeerRouter.builder()
+                                                    .ipAddress(targetIp)
+                                                    .macAddress(MacAddress.NONE)
+                                                    .vlanId(vlanId)
+                                                    .build());
 
         log.info("Initializes external peer router map with peer router IP {}", targetIp.toString());
     }
@@ -517,7 +521,11 @@
     public void updateExternalPeerRouterMac(IpAddress ipAddress, MacAddress macAddress) {
         try {
             externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                new DefaultExternalPeerRouter(ipAddress, macAddress, existing.externalPeerRouterVlanId()));
+                    DefaultExternalPeerRouter.builder()
+                            .ipAddress(ipAddress)
+                            .macAddress(macAddress)
+                            .vlanId(existing.vlanId())
+                            .build());
 
             log.info("Updated external peer router map {}",
                     externalPeerRouterMap.get(ipAddress.toString()).value().toString());
@@ -531,7 +539,12 @@
     public void updateExternalPeerRouter(IpAddress ipAddress, MacAddress macAddress, VlanId vlanId) {
         try {
             externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                new DefaultExternalPeerRouter(ipAddress, macAddress, vlanId));
+                    DefaultExternalPeerRouter.builder()
+                            .ipAddress(ipAddress)
+                            .macAddress(macAddress)
+                            .vlanId(vlanId)
+                            .build());
+
         } catch (Exception e) {
             log.error("Exception occurred because of {}", e.toString());
         }
@@ -545,7 +558,7 @@
             return null;
         }
         if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
-            return externalPeerRouterMap.get(ipAddress.toString()).value().externalPeerRouterMac();
+            return externalPeerRouterMap.get(ipAddress.toString()).value().macAddress();
         } else {
             throw new NoSuchElementException();
         }
@@ -556,7 +569,10 @@
 
         try {
             externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                    new DefaultExternalPeerRouter(ipAddress, existing.externalPeerRouterMac(), vlanId));
+                    DefaultExternalPeerRouter.builder()
+                            .ipAddress(ipAddress)
+                            .macAddress(existing.macAddress())
+                            .vlanId(vlanId).build());
 
         } catch (Exception e) {
             log.error("Exception occurred because of {}", e.toString());
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index f1440c1..dc83d98 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -267,7 +267,7 @@
             try {
 
                 Set<String> extRouterIps = osNetworkService.externalPeerRouters().
-                        stream().map(r -> r.externalPeerRouterIp().toString()).collect(Collectors.toSet());
+                        stream().map(r -> r.ipAddress().toString()).collect(Collectors.toSet());
 
                 // if SPA is NOT contained in existing external router IP set, we ignore it
                 if (!extRouterIps.contains(spa.toString())) {
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index 2c20f6a..578e7e4 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -479,8 +479,8 @@
                 .setEthDst(instPort.macAddress())
                 .setIpDst(instPort.ipAddress().getIp4Address());
 
-        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-            externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
+        if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+            externalSelectorBuilder.matchVlanId(externalPeerRouter.vlanId()).build();
             externalTreatmentBuilder.popVlan();
         }
 
@@ -542,14 +542,14 @@
             TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
                     .setIpSrc(floating.getIp4Address())
                     .setEthSrc(instPort.macAddress())
-                    .setEthDst(externalPeerRouter.externalPeerRouterMac());
+                    .setEthDst(externalPeerRouter.macAddress());
 
             if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
                 tBuilder.popVlan();
             }
 
-            if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-                tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
+            if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+                tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
             }
             osFlowRuleService.setRule(
                     appId,
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index 03f65aa..e5ecd1c 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -215,7 +215,7 @@
         });
 
         ExternalPeerRouter externalPeerRouter = osNetworkAdminService.externalPeerRouter(exGateway);
-        VlanId vlanId = externalPeerRouter == null ? VlanId.NONE : externalPeerRouter.externalPeerRouterVlanId();
+        VlanId vlanId = externalPeerRouter == null ? VlanId.NONE : externalPeerRouter.vlanId();
 
         if (exGateway == null) {
             deleteUnassociatedExternalPeerRouter();
@@ -240,12 +240,12 @@
 
             osNetworkAdminService.externalPeerRouters().stream()
                     .filter(externalPeerRouter ->
-                            !routerIps.contains(externalPeerRouter.externalPeerRouterIp().toString()))
+                            !routerIps.contains(externalPeerRouter.ipAddress().toString()))
                     .forEach(externalPeerRouter -> {
                         osNetworkAdminService
-                                .deleteExternalPeerRouter(externalPeerRouter.externalPeerRouterIp().toString());
+                                .deleteExternalPeerRouter(externalPeerRouter.ipAddress().toString());
                         log.trace("Deleted unassociated external peer router {}",
-                                externalPeerRouter.externalPeerRouterIp().toString());
+                                externalPeerRouter.ipAddress().toString());
                     });
         } catch (Exception e) {
             log.error("Exception occurred because of {}", e.toString());
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 e5289a2..ee90e0d 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
@@ -366,10 +366,10 @@
         Ethernet icmpRequestEth = new Ethernet();
         icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
                 .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
-                .setDestinationMACAddress(externalPeerRouter.externalPeerRouterMac());
+                .setDestinationMACAddress(externalPeerRouter.macAddress());
 
-        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-            icmpRequestEth.setVlanID(externalPeerRouter.externalPeerRouterVlanId().toShort());
+        if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+            icmpRequestEth.setVlanID(externalPeerRouter.vlanId().toShort());
         }
 
         icmpRequestEth.setPayload(ipPacket);
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index 4657c18..17f4c55 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -326,8 +326,8 @@
                 .setEthDst(packetIn.parsed().getSourceMAC())
                 .setIpDst(internalIp);
 
-        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-            sBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId());
+        if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+            sBuilder.matchVlanId(externalPeerRouter.vlanId());
             tBuilder.popVlan();
         }
 
@@ -429,22 +429,22 @@
                 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
                         .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
                 tBuilder.setTcpSrc(patPort)
-                        .setEthDst(externalPeerRouter.externalPeerRouterMac());
+                        .setEthDst(externalPeerRouter.macAddress());
                 break;
             case IPv4.PROTOCOL_UDP:
                 UDP udpPacket = (UDP) iPacket.getPayload();
                 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
                         .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
                 tBuilder.setUdpSrc(patPort)
-                        .setEthDst(externalPeerRouter.externalPeerRouterMac());
+                        .setEthDst(externalPeerRouter.macAddress());
                 break;
             default:
                 log.debug("Unsupported IPv4 protocol {}");
                 break;
         }
 
-        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-            tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
+        if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+            tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
         }
 
         tBuilder.setIpSrc(externalIp);
@@ -491,11 +491,11 @@
         iPacket.resetChecksum();
         iPacket.setParent(ethPacketIn);
         ethPacketIn.setSourceMACAddress(DEFAULT_GATEWAY_MAC);
-        ethPacketIn.setDestinationMACAddress(externalPeerRouter.externalPeerRouterMac());
+        ethPacketIn.setDestinationMACAddress(externalPeerRouter.macAddress());
         ethPacketIn.setPayload(iPacket);
 
-        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
-            ethPacketIn.setVlanID(externalPeerRouter.externalPeerRouterVlanId().toShort());
+        if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
+            ethPacketIn.setVlanID(externalPeerRouter.vlanId().toShort());
         }
 
         ethPacketIn.resetChecksum();
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodecTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodecTest.java
new file mode 100644
index 0000000..b416411
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterCodecTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.impl.DefaultExternalPeerRouter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.openstacknetworking.codec.ExternalPeerRouterJsonMatcher.matchesExternalPeerRouter;
+
+/**
+ * Unit tests for ExternalPeerRouter codec.
+ */
+public class ExternalPeerRouterCodecTest {
+
+    MockCodecContext context;
+    JsonCodec<ExternalPeerRouter> externalPeerRouterCodec;
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        externalPeerRouterCodec = new ExternalPeerRouterCodec();
+
+        assertThat(externalPeerRouterCodec, notNullValue());
+    }
+
+    /**
+     * Tests the external peer router encoding.
+     */
+    @Test
+    public void testExternalPeerRouterEncode() {
+        ExternalPeerRouter router = DefaultExternalPeerRouter.builder()
+                .ipAddress(IpAddress.valueOf("10.10.10.1"))
+                .macAddress(MacAddress.valueOf("11:22:33:44:55:66"))
+                .vlanId(VlanId.vlanId("1"))
+                .build();
+
+        ObjectNode routerJson = externalPeerRouterCodec.encode(router, context);
+        assertThat(routerJson, matchesExternalPeerRouter(router));
+    }
+
+    /**
+     * Tests the external peer router decoding.
+     * @throws IOException
+     */
+    @Test
+    public void testExternalPeerRouterDecode() throws IOException {
+        ExternalPeerRouter router = getPeerRouter("ExternalPeerRouter.json");
+
+        assertThat(router.ipAddress(), is(IpAddress.valueOf("10.10.10.1")));
+        assertThat(router.macAddress(), is(MacAddress.valueOf("11:22:33:44:55:66")));
+        assertThat(router.vlanId(), is(VlanId.vlanId("1")));
+    }
+
+    /**
+     * Reads in an external peer router from the given resource and decodes it.
+     *
+     * @param resourceName resource to use to read the JSON for the rule
+     * @return decoded external peer router
+     * @throws IOException if processing the resource fails
+     */
+    private ExternalPeerRouter getPeerRouter(String resourceName) throws IOException {
+        InputStream jsonStream = InstancePortCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        ExternalPeerRouter router = externalPeerRouterCodec.decode((ObjectNode) json, context);
+        assertThat(router, notNullValue());
+        return router;
+    }
+
+    /**
+     * Mock codec context for use in codec unit tests.
+     */
+    private class MockCodecContext implements CodecContext {
+        private final ObjectMapper mapper = new ObjectMapper();
+        private final CodecManager manager = new CodecManager();
+        private final Map<Class<?>, Object> services = new HashMap<>();
+
+        /**
+         * Constructs a new mock codec context.
+         */
+        public MockCodecContext() {
+            manager.activate();
+        }
+
+        @Override
+        public ObjectMapper mapper() {
+            return mapper;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            if (entityClass == InstancePort.class) {
+                return (JsonCodec<T>) externalPeerRouterCodec;
+            }
+            return manager.getCodec(entityClass);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T getService(Class<T> serviceClass) {
+            return (T) services.get(serviceClass);
+        }
+
+        // for registering mock services
+        public <T> void registerService(Class<T> serviceClass, T impl) {
+            services.put(serviceClass, impl);
+        }
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterJsonMatcher.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterJsonMatcher.java
new file mode 100644
index 0000000..3044335
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/ExternalPeerRouterJsonMatcher.java
@@ -0,0 +1,83 @@
+/*
+ * 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.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+
+/**
+ * Hamcrest matcher for external peer router.
+ */
+public final class ExternalPeerRouterJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private static final String MAC_ADDRESS = "macAddress";
+    private static final String IP_ADDRESS = "ipAddress";
+    private static final String VLAN_ID = "vlanId";
+
+    private final ExternalPeerRouter router;
+
+    private ExternalPeerRouterJsonMatcher(ExternalPeerRouter router) {
+        this.router = router;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check MAC address
+        String jsonMacAddress = jsonNode.get(MAC_ADDRESS).asText();
+        String macAddress = router.macAddress().toString();
+        if (!jsonMacAddress.equals(macAddress)) {
+            description.appendText("macAddress was " + jsonMacAddress);
+            return false;
+        }
+
+        // check IP address
+        String jsonIpAddress = jsonNode.get(IP_ADDRESS).asText();
+        String ipAddress = router.ipAddress().toString();
+        if (!jsonIpAddress.equals(ipAddress)) {
+            description.appendText("ipAddress was " + jsonIpAddress);
+            return false;
+        }
+
+        // check VLAN ID
+        String jsonVlanId = jsonNode.get(VLAN_ID).asText();
+        String vlanId = router.vlanId().toString();
+        if (!jsonVlanId.equals(vlanId)) {
+            description.appendText("VLAN ID was " + jsonIpAddress);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(router.toString());
+    }
+
+    /**
+     * Factory to allocate an external peer router matcher.
+     *
+     * @param router openstack external peer router we are looking for
+     * @return matcher
+     */
+    public static ExternalPeerRouterJsonMatcher
+                        matchesExternalPeerRouter(ExternalPeerRouter router) {
+        return new ExternalPeerRouterJsonMatcher(router);
+    }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouterTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouterTest.java
index c3aeab1..cec2ee8 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouterTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouterTest.java
@@ -56,9 +56,29 @@
      */
     @Before
     public void setUp() {
-        router1 = new DefaultExternalPeerRouter(ROUTER_IP_1, ROUTER_MAC_1, VLAN_ID_1);
-        sameAsRouter1 = new DefaultExternalPeerRouter(ROUTER_IP_1, ROUTER_MAC_1, VLAN_ID_1);
-        router2 = new DefaultExternalPeerRouter(ROUTER_IP_2, ROUTER_MAC_2, VLAN_ID_2);
+        ExternalPeerRouter.Builder builder1 = DefaultExternalPeerRouter.builder();
+
+        router1 = builder1
+                    .ipAddress(ROUTER_IP_1)
+                    .macAddress(ROUTER_MAC_1)
+                    .vlanId(VLAN_ID_1)
+                    .build();
+
+        ExternalPeerRouter.Builder builder2 = DefaultExternalPeerRouter.builder();
+
+        sameAsRouter1 = builder2
+                    .ipAddress(ROUTER_IP_1)
+                    .macAddress(ROUTER_MAC_1)
+                    .vlanId(VLAN_ID_1)
+                    .build();
+
+        ExternalPeerRouter.Builder builder3 = DefaultExternalPeerRouter.builder();
+
+        router2 = builder3
+                    .ipAddress(ROUTER_IP_2)
+                    .macAddress(ROUTER_MAC_2)
+                    .vlanId(VLAN_ID_2)
+                    .build();
     }
 
     /**
@@ -78,8 +98,8 @@
     public void testConstruction() {
         ExternalPeerRouter router = router1;
 
-        assertThat(router.externalPeerRouterIp(), is(router1.externalPeerRouterIp()));
-        assertThat(router.externalPeerRouterMac(), is(router1.externalPeerRouterMac()));
-        assertThat(router.externalPeerRouterVlanId(), is(router1.externalPeerRouterVlanId()));
+        assertThat(router.ipAddress(), is(router1.ipAddress()));
+        assertThat(router.macAddress(), is(router1.macAddress()));
+        assertThat(router.vlanId(), is(router1.vlanId()));
     }
 }
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/ExternalPeerRouter.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/ExternalPeerRouter.json
new file mode 100644
index 0000000..ddc37a5
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/ExternalPeerRouter.json
@@ -0,0 +1,5 @@
+{
+  "ipAddress": "10.10.10.1",
+  "macAddress": "11:22:33:44:55:66",
+  "vlanId": "1"
+}
\ No newline at end of file