Modified warden to create borrowed cells on-demand and to scrap returned ones.

Change-Id: If32a0da18ff9d4c05645017e5cc7481bbd1ab0cd
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 1825264..3034375 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -113,17 +113,16 @@
     case "$cell" in
     "borrow")
         aux="/tmp/cell-$$"
-        curl -sS -X POST "http://$CELL_WARDEN:4321/?user=$(id -un)&duration=${2:-0}" \
+        curl -sS -X POST "http://$CELL_WARDEN:4321/?duration=${2:-0}&user=${3:-$(id -un)}" \
             -d "$(cat ~/.ssh/id_rsa.pub)" > $aux
         . $aux
         rm -f $aux
         export ONOS_INSTANCES=$(env | grep 'OC[0-9]*=' | sort | cut -d= -f2)
         setPrimaryInstance 1 >/dev/null
-        cell
         onos-verify-cell
         ;;
     "return")
-        curl -sS -X DELETE "http://$CELL_WARDEN:4321/?user=$(id -un)"
+        curl -sS -X DELETE "http://$CELL_WARDEN:4321/?user=${2:-$(id -un)}"
         unset ONOS_CELL ONOS_NIC ONOS_IP ONOS_APPS ONOS_BOOT_FEATURES
         unset OCI OCN OCT ONOS_INSTANCES ONOS_FEATURES
         unset $(env | sed -n 's:\(^OC[0-9]\{1,\}\)=.*:\1 :g p')
diff --git a/tools/test/bin/onos-verify-cell b/tools/test/bin/onos-verify-cell
index 4b3a0ee..ec34cab 100755
--- a/tools/test/bin/onos-verify-cell
+++ b/tools/test/bin/onos-verify-cell
@@ -6,6 +6,6 @@
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
-for node in $(env | sort | egrep "OC[0-9N]+" | cut -d= -f2); do
-    printf "%s: " $node; ssh -n -o PasswordAuthentication=no $ONOS_USER@$node date
+for node in $OCN $(env | sort | egrep "OC[0-9]+" | cut -d= -f2); do
+    printf "%s: " $node; ssh -n -o StrictHostKeyChecking=no -o PasswordAuthentication=no $ONOS_USER@$node hostname
 done
diff --git a/utils/warden/bin/cell-def b/utils/warden/bin/cell-def
index 12fd284..c7c7116 100755
--- a/utils/warden/bin/cell-def
+++ b/utils/warden/bin/cell-def
@@ -8,7 +8,7 @@
 echo "export ONOS_NIC=\"10.128.11.*\""
 
 sudo lxc-ls -F "name,ipv4" --fancy | grep $name | \
-    sed "s/^$name/OC/" | tr "[:lower:]" "[:upper:]" | \
+    sed "s/^$name-/OC/" | tr "[:lower:]" "[:upper:]" | \
     sed -r 's/[ ]+/\=/;s/^/export /'
 
 echo "export OCT=\"\$OC1\""
diff --git a/utils/warden/bin/clone-node b/utils/warden/bin/clone-node
index 8e60d40..2a3465a 100755
--- a/utils/warden/bin/clone-node
+++ b/utils/warden/bin/clone-node
@@ -4,7 +4,8 @@
 base="$1"
 ip="$2"
 name="$3"
-key="$4"
+shift 3
+key="$@"
 
 sudo lxc-clone -o $base -n $name
 
diff --git a/utils/warden/bin/create-cell b/utils/warden/bin/create-cell
index f24a386..59ccb96 100755
--- a/utils/warden/bin/create-cell
+++ b/utils/warden/bin/create-cell
@@ -3,11 +3,12 @@
 
 name="$1"
 ipx="$2"
-key="$3"
+shift 2
+key="$@"
 
 cd $(dirname $0)
 
-./clone-node base-mininet ${ipx/x/0} $name-mn "$key"
+./clone-node base-mininet ${ipx/x/0} $name-n "$key"
 
 for n in {1..3}; do
     ./clone-node base-onos ${ipx/x/$n} $name-$n "$key"
diff --git a/utils/warden/bin/destroy-cell b/utils/warden/bin/destroy-cell
index c39d5fe..0af757e 100755
--- a/utils/warden/bin/destroy-cell
+++ b/utils/warden/bin/destroy-cell
@@ -5,7 +5,7 @@
 
 cd $(dirname $0)
 
-./destroy-node $name-mn
+./destroy-node $name-n
 
 for n in {1..3}; do
     ./destroy-node $name-$n
diff --git a/utils/warden/src/main/java/org/onlab/warden/Warden.java b/utils/warden/src/main/java/org/onlab/warden/Warden.java
index 6f6ada5..f6c091a 100644
--- a/utils/warden/src/main/java/org/onlab/warden/Warden.java
+++ b/utils/warden/src/main/java/org/onlab/warden/Warden.java
@@ -29,14 +29,11 @@
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Random;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import static com.google.common.base.Preconditions.*;
 
@@ -106,7 +103,6 @@
         return list != null ? ImmutableSet.copyOf(list) : ImmutableSet.of();
     }
 
-
     /**
      * Returns reservation for the specified user.
      *
@@ -170,28 +166,13 @@
             reservation = new Reservation(reservation.cellName, userName, now, minutes);
         }
 
-        reserveCell(reservation.cellName, reservation);
-        installUserKeys(reservation.cellName, userName, sshKey);
+        reserveCell(reservation);
+        createCell(reservation, sshKey);
         log(userName, reservation.cellName, "borrowed for " + reservation.duration + " minutes");
         return getCellDefinition(reservation.cellName);
     }
 
     /**
-     * Reserves the specified cell for the user the source file and writes the
-     * specified content to the target file.
-     *
-     * @param cellName    cell name
-     * @param reservation cell reservation record
-     */
-    private void reserveCell(String cellName, Reservation reservation) {
-        try (FileOutputStream stream = new FileOutputStream(new File(reserved, cellName))) {
-            stream.write(reservation.encode().getBytes(UTF_8));
-        } catch (IOException e) {
-            throw new IllegalStateException("Unable to reserve cell " + cellName, e);
-        }
-    }
-
-    /**
      * Returns the specified cell for the specified user and their public access key.
      *
      * @param userName user name
@@ -200,10 +181,61 @@
         checkNotNull(userName, USER_NOT_NULL);
         Reservation reservation = currentUserReservation(userName);
         checkState(reservation != null, "User %s has no cell reservations", userName);
+
+        unreserveCell(reservation);
+        destroyCell(reservation);
+        log(userName, reservation.cellName, "returned");
+    }
+
+    /**
+     * Reserves the specified cell for the user the source file and writes the
+     * specified content to the target file.
+     *
+     * @param reservation cell reservation record
+     */
+    private void reserveCell(Reservation reservation) {
+        File cellFile = new File(reserved, reservation.cellName);
+        try (FileOutputStream stream = new FileOutputStream(cellFile)) {
+            stream.write(reservation.encode().getBytes(UTF_8));
+        } catch (IOException e) {
+            throw new IllegalStateException("Unable to reserve cell " + reservation.cellName, e);
+        }
+    }
+
+    private String getCellDefinition(String cellName) {
+        return exec("bin/cell-def " + cellName);
+    }
+
+    /**
+     * Cancels the specified reservation.
+     *
+     * @param reservation reservation record
+     */
+    private void unreserveCell(Reservation reservation) {
         checkState(new File(reserved, reservation.cellName).delete(),
                    "Unable to return cell %s", reservation.cellName);
-        uninstallUserKeys(reservation.cellName);
-        log(userName, reservation.cellName, "returned");
+    }
+
+    /**
+     * Creates the cell for the specified user SSH key.
+     *
+     * @param reservation cell reservation
+     * @param sshKey      ssh key
+     */
+    private void createCell(Reservation reservation, String sshKey) {
+        String cellInfo = getCellInfo(reservation.cellName);
+        String cmd = String.format("bin/create-cell %s %s %s",
+                                   reservation.cellName, cellInfo, sshKey);
+        exec(cmd);
+    }
+
+    /**
+     * Destroys the specified cell.
+     *
+     * @param reservation reservation record
+     */
+    private void destroyCell(Reservation reservation) {
+        exec("bin/destroy-cell " + reservation.cellName);
     }
 
     /**
@@ -212,7 +244,7 @@
      * @param cellName cell name
      * @return cell definition
      */
-    String getCellDefinition(String cellName) {
+    String getCellInfo(String cellName) {
         File cellFile = new File(supported, cellName);
         try (InputStream stream = new FileInputStream(cellFile)) {
             return new String(ByteStreams.toByteArray(stream), UTF_8);
@@ -221,63 +253,15 @@
         }
     }
 
-    // Returns list of cell hosts, i.e. OC#, OCN
-    private List<String> cellHosts(String cellName) {
-        ImmutableList.Builder<String> builder = ImmutableList.builder();
-        Pattern pattern = Pattern.compile("export OC[0-9N]=(.*)");
-        for (String line : getCellDefinition(cellName).split("\n")) {
-            Matcher matcher = pattern.matcher(line);
-            if (matcher.matches()) {
-                builder.add(matcher.group(1).replaceAll("[\"']", ""));
-            }
-        }
-        return builder.build();
-    }
-
-    // Installs the specified user's key on all hosts of the given cell.
-    private void installUserKeys(String cellName, String userName, String sshKey) {
-        File authKeysFile = authKeys(sshKey);
-        for (String host : cellHosts(cellName)) {
-            installAuthorizedKey(host, authKeysFile.getPath());
-        }
-        checkState(authKeysFile.delete(), "Unable to install user keys");
-    }
-
-    // Uninstalls the user keys on the specified cell
-    private void uninstallUserKeys(String cellName) {
-        for (String host : cellHosts(cellName)) {
-            installAuthorizedKey(host, AUTHORIZED_KEYS);
-        }
-    }
-
-    // Installs the authorized keys on the specified host.
-    private void installAuthorizedKey(String host, String authorizedKeysFile) {
-        String cmd = "scp " + authorizedKeysFile + " sdn@" + host + ":.ssh/authorized_keys";
+    // Executes the specified command.
+    private String exec(String command) {
         try {
-            Process process = Runtime.getRuntime().exec(cmd);
+            Process process = Runtime.getRuntime().exec(command);
+            String output = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8);
             process.waitFor(TIMEOUT, TimeUnit.SECONDS);
+            return process.exitValue() == 0 ? output : null;
         } catch (Exception e) {
-            throw new IllegalStateException("Unable to set authorized keys for host " + host);
-        }
-    }
-
-    // Returns the file containing authorized keys that incudes the specified key.
-    private File authKeys(String sshKey) {
-        File keysFile = new File(AUTHORIZED_KEYS);
-        try {
-            File tmp = File.createTempFile("warden-", ".auth");
-            tmp.deleteOnExit();
-            try (InputStream stream = new FileInputStream(keysFile);
-                 PrintWriter output = new PrintWriter(tmp)) {
-                String baseKeys = new String(ByteStreams.toByteArray(stream), UTF_8);
-                output.println(baseKeys);
-                output.println(sshKey);
-                return tmp;
-            } catch (IOException e) {
-                throw new IllegalStateException("Unable to generate authorized keys", e);
-            }
-        } catch (IOException e) {
-            throw new IllegalStateException("Unable to generate authorized keys", e);
+            throw new IllegalStateException("Unable to execute " + command);
         }
     }