restart vBNG based on XOS's record

re-set up the private IP address to public IP address mapping
based on the mapping record in XOS.

re-calculates and re-installs all the intents.

Change-Id: I89aa75662da596b9793e02ba41398a43517ccecf
diff --git a/apps/virtualbng/features.xml b/apps/virtualbng/features.xml
index 10fc32b..2b48bec 100644
--- a/apps/virtualbng/features.xml
+++ b/apps/virtualbng/features.xml
@@ -18,6 +18,7 @@
     <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
     <feature name="${project.artifactId}" version="${project.version}"
              description="${project.description}">
+        <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle>
         <bundle>mvn:${project.groupId}/onos-app-virtualbng/${project.version}</bundle>
         <feature>onos-thirdparty-web</feature>
     </feature>
diff --git a/apps/virtualbng/pom.xml b/apps/virtualbng/pom.xml
index d177014..e42f888 100644
--- a/apps/virtualbng/pom.xml
+++ b/apps/virtualbng/pom.xml
@@ -38,6 +38,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.19</version>
+        </dependency>
+        <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-cli</artifactId>
             <version>${project.version}</version>
@@ -75,6 +80,7 @@
                             javax.ws.rs,
                             javax.ws.rs.core,
                             com.sun.jersey.api.core,
+                            com.sun.jersey.api.client,
                             com.sun.jersey.spi.container.servlet,
                             com.sun.jersey.server.impl.container.servlet,
                             com.fasterxml.jackson.databind,
diff --git a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java
new file mode 100644
index 0000000..b5c1322
--- /dev/null
+++ b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/RestClient.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 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.virtualbng;
+
+import static com.google.common.net.MediaType.JSON_UTF_8;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+
+public class RestClient {
+    private final Logger log = getLogger(getClass());
+    private final String hostName = "10.254.1.22";
+    private final int xosServerPort = 8000;
+    private static final String UTF_8 = JSON_UTF_8.toString();
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+    private final String url = "http://" + hostName + ":" + xosServerPort
+            + "/xoslib/rs/vbng_mapping/";
+
+    /**
+     * Gets a client web resource builder.
+     *
+     * @param url the URL to access remote resource
+     * @return web resource builder
+     */
+    public WebResource.Builder getClientBuilder(String url) {
+        log.info("URL: {}", url);
+        Client client = Client.create();
+        WebResource resource = client.resource(url);
+        return resource.accept(UTF_8).type(UTF_8);
+    }
+
+    /**
+     * Builds a REST client and fetches XOS mapping data in JSON format.
+     *
+     * @return the vBNG map if REST GET succeeds, otherwise return null
+     */
+    public ObjectNode getRest() {
+        WebResource.Builder builder = getClientBuilder(url);
+        ClientResponse response = builder.get(ClientResponse.class);
+
+        if (response.getStatus() != HTTP_OK) {
+            log.info("REST GET request returned error code {}",
+                     response.getStatus());
+            return null;
+        }
+
+        String jsonString = response.getEntity(String.class);
+        log.info("Fetched JSON string: {}", jsonString);
+
+        JsonNode node;
+        try {
+            node = MAPPER.readTree(jsonString);
+        } catch (IOException e) {
+            log.error("Failed to read JSON string", e);
+            return null;
+        }
+
+        return (ObjectNode) node;
+    }
+}
diff --git a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
index 35f4542..653a87d 100644
--- a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
+++ b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
@@ -184,7 +184,7 @@
     }
 
     @Override
-    public IpAddress recycleAssignedPublicIpAddress(IpAddress
+    public synchronized IpAddress recycleAssignedPublicIpAddress(IpAddress
                                                     privateIpAddress) {
         IpAddress publicIpAddress = ipAddressMap.remove(privateIpAddress);
         if (publicIpAddress == null) {
@@ -210,6 +210,61 @@
         return Collections.unmodifiableMap(ipAddressMap);
     }
 
+    @Override
+    public synchronized boolean assignSpecifiedPublicIp(IpAddress publicIpAddress,
+                                  IpAddress privateIpAddress) {
+
+        // Judge whether this public IP address is in our public IP
+        // prefix/address list.
+        boolean isPublicIpExist = false;
+        for (Entry<IpPrefix, Boolean> prefix: localPublicIpPrefixes.entrySet()) {
+            if (prefix.getKey().contains(publicIpAddress)) {
+                isPublicIpExist = true;
+
+                // Judge whether this public IP address is already assigned
+                if (!prefix.getValue() ||
+                        isAssignedPublicIpAddress(publicIpAddress)) {
+                    log.info("The public IP address {} is already assigned, "
+                            + "and not available.", publicIpAddress);
+                    return false;
+                }
+
+                // The public IP address is still available
+                // Store the mapping from private IP address to public IP address
+                ipAddressMap.put(privateIpAddress, publicIpAddress);
+
+                // Update the prefix status
+                if (prefix.getKey().prefixLength() == 32) {
+                    updateIpPrefixStatus(prefix.getKey(), false);
+                    return true;
+                }
+
+                // Judge whether the prefix of this public IP address is used
+                // up, if so, update the IP prefix status.
+                int prefixLen = prefix.getKey().prefixLength();
+                int availableIpNum = (int) Math.pow(2,
+                        IpPrefix.MAX_INET_MASK_LENGTH - prefixLen) - 1;
+                int usedIpNum = 0;
+                for (Entry<IpAddress, IpAddress> ipAddressMapEntry:
+                    ipAddressMap.entrySet()) {
+                    if (prefix.getKey().contains(ipAddressMapEntry.getValue())) {
+                        usedIpNum = usedIpNum + 1;
+                    }
+                }
+                if (usedIpNum == availableIpNum) {
+                    updateIpPrefixStatus(prefix.getKey(), false);
+                }
+
+                return true;
+            }
+        }
+        if (!isPublicIpExist) {
+            log.info("The public IP address {} retrieved from XOS mapping does "
+                    + "not exist", publicIpAddress);
+        }
+        return false;
+    }
+
     /**
      * Generates a new IP address base on a given IP address plus a number to
      * increase.
diff --git a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java
index 31e6d4f..6f17ebc 100644
--- a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java
+++ b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationService.java
@@ -81,4 +81,16 @@
      * @return the address map from private IP address to public IP address
      */
     Map<IpAddress, IpAddress> getIpAddressMappings();
+
+    /**
+     * Tries to assign a given public IP address to a private IP address. If
+     * success, then sets up the mapping from this private IP address to the
+     * public IP address, and stores the mapping.
+     *
+     * @param publicIpAddress the public IP address try to assign
+     * @param privateIpAddress a private IP address
+     * @return true if this public IP address is available, otherwise false
+     */
+    boolean assignSpecifiedPublicIp(IpAddress publicIpAddress,
+                                    IpAddress privateIpAddress);
 }
diff --git a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
index 775cff4..8fae896 100644
--- a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
+++ b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngManager.java
@@ -17,8 +17,12 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+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.Maps;
 
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
@@ -66,6 +70,7 @@
 public class VbngManager implements VbngService {
 
     private static final String APP_NAME = "org.onosproject.virtualbng";
+    private static final String VBNG_MAP_NAME = "vbng_mapping";
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -113,6 +118,9 @@
         hostService.addListener(hostListener);
 
         log.info("vBNG Started");
+
+        // Recover the status before vBNG restarts
+        statusRecovery();
     }
 
     @Deactivate
@@ -122,6 +130,42 @@
     }
 
     /**
+     * Recovers from XOS record. Re-sets up the mapping between private IP
+     * address and public IP address, re-calculates intents and re-installs
+     * those intents.
+     */
+    private void statusRecovery() {
+        log.info("vBNG starts to recover from XOS record......");
+        RestClient restClient = new RestClient();
+        ObjectNode map = restClient.getRest();
+        if (map == null) {
+            log.info("Stop to recover vBNG status due to the vBNG map "
+                    + "is null!");
+            return;
+        }
+
+        log.info("Get record from XOS: {}", map);
+
+        ArrayNode array = (ArrayNode) map.get(VBNG_MAP_NAME);
+        Iterator<JsonNode> entries = array.elements();
+        while (entries.hasNext()) {
+            ObjectNode entry = (ObjectNode) entries.next();
+
+            IpAddress hostIpAdddress =
+                    IpAddress.valueOf(entry.get("private_ip").asText());
+            IpAddress publicIpAddress =
+                    IpAddress.valueOf(entry.get("routeable_subnet").asText());
+            MacAddress macAddress =
+                    MacAddress.valueOf(entry.get("mac").asText());
+            String hostName = entry.get("hostname").asText();
+
+            // Create vBNG
+            createVbng(hostIpAdddress, publicIpAddress, macAddress, hostName);
+
+        }
+    }
+
+    /**
      * Sets up mapping from hostname to connect point.
      */
     private void setupMap() {
@@ -136,6 +180,39 @@
                                         PortNumber.portNumber(47)));
     }
 
+    /**
+     * Creates a new vBNG.
+     *
+     * @param privateIpAddress a private IP address
+     * @param publicIpAddress the public IP address for the private IP address
+     * @param hostMacAddress the MAC address for the private IP address
+     * @param hostName the host name for the private IP address
+     */
+    private void createVbng(IpAddress privateIpAddress,
+                            IpAddress publicIpAddress,
+                            MacAddress hostMacAddress,
+                            String hostName) {
+        boolean result = vbngConfigurationService
+                .assignSpecifiedPublicIp(publicIpAddress, privateIpAddress);
+        if (!result) {
+            log.info("Assign public IP address {} for private IP address {} "
+                    + "failed!", publicIpAddress, privateIpAddress);
+            log.info("Failed to create vBNG for private IP address {}",
+                     privateIpAddress);
+            return;
+        }
+        log.info("[ADD] Private IP to Public IP mapping: {} --> {}",
+                 privateIpAddress, publicIpAddress);
+
+        // Setup paths between the host configured with private IP and
+        // next hop
+        if (!setupForwardingPaths(privateIpAddress, publicIpAddress,
+                                  hostMacAddress, hostName)) {
+            privateIpAddressMap.put(privateIpAddress,
+                                    new VcpeHost(hostMacAddress, hostName));
+        }
+    }
+
     @Override
     public IpAddress createVbng(IpAddress privateIpAddress,
                                 MacAddress hostMacAddress,