ONOS-5182 Refactored SONA to cache network states

Change-Id: Ib316fa5fa5d36e9da370a1578ac55de4a8dd9b04
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
new file mode 100644
index 0000000..511e89e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack floating IP addresses.
+ */
+@Command(scope = "onos", name = "openstack-floatingips",
+        description = "Lists all OpenStack floating IP addresses")
+public class OpenstackFloatingIpListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s";
+
+    @Override
+    protected void execute() {
+        OpenstackRouterService service = AbstractShellCommand.get(OpenstackRouterService.class);
+        List<NetFloatingIP> floatingIps = Lists.newArrayList(service.floatingIps());
+        floatingIps.sort(Comparator.comparing(NetFloatingIP::getFloatingIpAddress));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(floatingIps)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Floating IP", "Fixed IP");
+        for (NetFloatingIP floatingIp: floatingIps) {
+            print(FORMAT, floatingIp.getId(),
+                    floatingIp.getFloatingIpAddress(),
+                    Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
+            "" : floatingIp.getFixedIpAddress());
+        }
+    }
+
+    private JsonNode json(List<NetFloatingIP> floatingIps) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (NetFloatingIP floatingIp: floatingIps) {
+            result.add(writeFloatingIp(floatingIp));
+        }
+        return result;
+    }
+
+    private ObjectNode writeFloatingIp(NetFloatingIP floatingIp) {
+        try {
+            String strFloatingIp = ObjectMapperSingleton.getContext(NeutronFloatingIP.class)
+                    .writerFor(NeutronFloatingIP.class)
+                    .writeValueAsString(floatingIp);
+            log.trace(strFloatingIp);
+            return (ObjectNode) mapper().readTree(strFloatingIp.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
deleted file mode 100644
index b2f5b8d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.openstacknetworking.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Purge Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-purge-flows",
-        description = "Purge data plane flows of existing VM.")
-public class OpenstackInstancePurgeFlowsCommand extends AbstractShellCommand {
-
-    @Option(name = "-a", aliases = "--all",
-            description = "HostIDs are all existing VM",
-            required = false, multiValued = false)
-    private Boolean allhost = false;
-
-    @Argument(index = 0, name = "hostId", description = "HostID(s)",
-            required = false, multiValued = true)
-    private String[] hostids = null;
-
-    @Override
-    protected void execute() {
-         HostService hostService = AbstractShellCommand.get(HostService.class);
-
-         OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
-         OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
-         OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
-         OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
-         if (allhost) {
-            switchingService.purgeVmFlow(null);
-            sgService.purgeVmFlow(null);
-            routingService.purgeVmFlow(null);
-            floatingIpService.purgeVmFlow(null);
-
-            hostService.getHosts().forEach(host -> {
-                printHost(host);
-            });
-         } else if (hostids != null) {
-            for (String hostid : hostids) {
-                Host host = hostService.getHost(HostId.hostId(hostid));
-                if (host == null) {
-                    continue;
-                }
-                switchingService.purgeVmFlow(host);
-                sgService.purgeVmFlow(host);
-                routingService.purgeVmFlow(host);
-                floatingIpService.purgeVmFlow(host);
-                printHost(host);
-            }
-         }
-    }
-
-    private void printHost(Host host) {
-        print("Purge data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
-              host.id(), host.annotations().value(VXLAN_ID),
-              host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
deleted file mode 100644
index eda334d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.openstacknetworking.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Re-Install Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-reinstall-flows",
-        description = "Re-install data plane flows of existing VM.")
-public class OpenstackInstanceReInstallFlowCommand extends AbstractShellCommand {
-
-    @Option(name = "-a", aliases = "--all",
-    description = "HostIDs are all existing VM", required = false, multiValued = false)
-    private Boolean allhost = false;
-
-    @Argument(index = 0, name = "hostId", description = "HostID(s)",
-            required = false, multiValued = true)
-    private String[] hostids = null;
-
-    @Override
-    protected void execute() {
-         HostService hostService = AbstractShellCommand.get(HostService.class);
-
-         OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
-         OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
-         OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
-         OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
-         if (allhost) {
-            hostService.getHosts().forEach(host -> {
-                switchingService.reinstallVmFlow(host);
-                sgService.reinstallVmFlow(host);
-                routingService.reinstallVmFlow(host);
-                floatingIpService.reinstallVmFlow(host);
-                printHost(host);
-
-            });
-         } else if (hostids != null) {
-            for (String hostid : hostids) {
-                Host host = hostService.getHost(HostId.hostId(hostid));
-                if (host == null) {
-                    continue;
-                }
-                switchingService.reinstallVmFlow(host);
-                sgService.reinstallVmFlow(host);
-                routingService.reinstallVmFlow(host);
-                floatingIpService.reinstallVmFlow(host);
-                printHost(host);
-            }
-         }
-    }
-
-    private void printHost(Host host) {
-        print("Re-install data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
-              host.id(), host.annotations().value(VXLAN_ID),
-              host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
new file mode 100644
index 0000000..8b489c9
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack networks.
+ */
+@Command(scope = "onos", name = "openstack-networks",
+        description = "Lists all OpenStack networks")
+public class OpenstackNetworkListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+        List<Network> networks = Lists.newArrayList(service.networks());
+        networks.sort(Comparator.comparing(Network::getName));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(networks)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Name", "VNI", "Subnets");
+        for (Network net: networks) {
+            List<String> subnets = service.subnets().stream()
+                    .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
+                    .map(Subnet::getCidr)
+                    .collect(Collectors.toList());
+            print(FORMAT, net.getId(),
+                    net.getName(),
+                    net.getProviderSegID(),
+                    subnets.isEmpty() ? "" : subnets);
+        }
+    }
+
+    private JsonNode json(List<Network> networks) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Network net: networks) {
+            result.add(writeNetwork(net));
+        }
+        return result;
+    }
+
+    private ObjectNode writeNetwork(Network net) {
+        try {
+            String strNet = ObjectMapperSingleton.getContext(NeutronNetwork.class)
+                    .writerFor(NeutronNetwork.class)
+                    .writeValueAsString(net);
+            return (ObjectNode) mapper().readTree(strNet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
new file mode 100644
index 0000000..58d44cd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack ports.
+ */
+@Command(scope = "onos", name = "openstack-ports",
+        description = "Lists all OpenStack ports")
+public class OpenstackPortListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    @Argument(name = "networkId", description = "Network ID")
+    private String networkId = null;
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+        List<Port> ports = Lists.newArrayList(service.ports());
+        ports.sort(Comparator.comparing(Port::getNetworkId));
+
+        if (!Strings.isNullOrEmpty(networkId)) {
+            ports.removeIf(port -> !port.getNetworkId().equals(networkId));
+        }
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(ports)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list ports in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+        for (Port port: ports) {
+            List<String> fixedIps = port.getFixedIps().stream()
+                    .map(IP::getIpAddress)
+                    .collect(Collectors.toList());
+            Network osNet = service.network(port.getNetworkId());
+            print(FORMAT, port.getId(),
+                    osNet.getName(),
+                    port.getMacAddress(),
+                    fixedIps.isEmpty() ? "" : fixedIps);
+        }
+    }
+
+    private JsonNode json(List<Port> ports) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Port port: ports) {
+            result.add(writePort(port));
+        }
+        return result;
+    }
+
+    private ObjectNode writePort(Port port) {
+        try {
+            String strPort = ObjectMapperSingleton.getContext(NeutronPort.class)
+                    .writerFor(NeutronPort.class)
+                    .writeValueAsString(port);
+            return (ObjectNode) mapper().readTree(strPort.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
new file mode 100644
index 0000000..4f7c8bd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack routers.
+ */
+@Command(scope = "onos", name = "openstack-routers",
+        description = "Lists all OpenStack routers")
+public class OpenstackRouterListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    private final OpenstackRouterService routerService =
+            AbstractShellCommand.get(OpenstackRouterService.class);
+    private final OpenstackNetworkService netService =
+            AbstractShellCommand.get(OpenstackNetworkService.class);
+
+    @Override
+    protected void execute() {
+        List<Router> routers = Lists.newArrayList(routerService.routers());
+        routers.sort(Comparator.comparing(Router::getName));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(routers)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list routers in JSON format");
+            }
+            return;
+        }
+        print(FORMAT, "ID", "Name", "External", "Internal");
+        for (Router router: routers) {
+            String exNetId = router.getExternalGatewayInfo() != null ?
+                router.getExternalGatewayInfo().getNetworkId() : null;
+
+            List<String> externals = Lists.newArrayList();
+            if (exNetId != null) {
+                // FIXME fix openstack4j to provide external gateway ip info
+                externals = netService.ports(exNetId).stream()
+                        .filter(port -> Objects.equals(port.getDeviceId(),
+                                router.getId()))
+                        .flatMap(port -> port.getFixedIps().stream())
+                        .map(IP::getIpAddress)
+                        .collect(Collectors.toList());
+            }
+
+            List<String> internals = Lists.newArrayList();
+            routerService.routerInterfaces(router.getId()).forEach(iface -> {
+                    internals.add(getRouterIfaceIp(iface));
+            });
+            print(FORMAT, router.getId(), router.getName(), externals, internals);
+        }
+    }
+
+    private String getRouterIfaceIp(RouterInterface iface) {
+        Port osPort = netService.port(iface.getPortId());
+        IP ipAddr = osPort.getFixedIps().stream()
+                .filter(ip -> ip.getSubnetId().equals(iface.getSubnetId()))
+                .findAny().orElse(null);
+        return ipAddr == null ? "" : ipAddr.getIpAddress();
+    }
+
+    private JsonNode json(List<Router> routers) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Router router: routers) {
+            result.add(writeRouter(router));
+        }
+        return result;
+    }
+
+    private ObjectNode writeRouter(Router osRouter) {
+        try {
+            String strNet = ObjectMapperSingleton.getContext(NeutronRouter.class)
+                    .writerFor(NeutronRouter.class)
+                    .writeValueAsString(osRouter);
+            return (ObjectNode) mapper().readTree(strNet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}