Adding STC scenario for testing host intent-based connectivity.

Change-Id: I7375a9fdb121a6a288df5d54a23cfbd9d54258f3
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
index dceed7b..4181dd4 100644
--- a/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
@@ -33,6 +33,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onosproject.net.intent.IntentState.FAILED;
 import static org.onosproject.net.intent.IntentState.WITHDRAWN;
 
@@ -40,17 +41,17 @@
  * Removes an intent.
  */
 @Command(scope = "onos", name = "remove-intent",
-         description = "Removes the specified intent")
+        description = "Removes the specified intent")
 public class IntentRemoveCommand extends AbstractShellCommand {
 
     @Argument(index = 0, name = "app",
-              description = "Application ID",
-              required = true, multiValued = false)
+            description = "Application ID",
+            required = false, multiValued = false)
     String applicationIdString = null;
 
     @Argument(index = 1, name = "key",
-              description = "Intent Key",
-              required = true, multiValued = false)
+            description = "Intent Key",
+            required = false, multiValued = false)
     String keyString = null;
 
     @Option(name = "-p", aliases = "--purge",
@@ -69,7 +70,7 @@
         CoreService coreService = get(CoreService.class);
 
         ApplicationId appId = appId();
-        if (applicationIdString != null) {
+        if (!isNullOrEmpty(applicationIdString)) {
             appId = coreService.getAppId(applicationIdString);
             if (appId == null) {
                 print("Cannot find application Id %s", applicationIdString);
@@ -77,73 +78,87 @@
             }
         }
 
-        final Key key;
-        if (keyString.startsWith("0x")) {
-            // The intent uses a LongKey
-            keyString = keyString.replaceFirst("0x", "");
-            key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
-        } else {
-            // The intent uses a StringKey
-            key = Key.of(keyString, appId);
-        }
-
-        Intent intent = intentService.getIntent(key);
-        if (intent != null) {
-            IntentListener listener = null;
-            final CountDownLatch withdrawLatch, purgeLatch;
-            if (purgeAfterRemove || sync) {
-                // set up latch and listener to track uninstall progress
-                withdrawLatch = new CountDownLatch(1);
-                purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
-                listener = (IntentEvent event) -> {
-                    if (Objects.equals(event.subject().key(), key)) {
-                        if (event.type() == IntentEvent.Type.WITHDRAWN ||
-                                event.type() == IntentEvent.Type.FAILED) {
-                            withdrawLatch.countDown();
-                        } else if (purgeAfterRemove &&
-                                event.type() == IntentEvent.Type.PURGED) {
-                            purgeLatch.countDown();
-                        }
-                    }
-                };
-                intentService.addListener(listener);
-            } else {
-                purgeLatch = null;
-                withdrawLatch = null;
+        if (isNullOrEmpty(keyString)) {
+            for (Intent intent : intentService.getIntents()) {
+                if (intent.appId().equals(appId)) {
+                    removeIntent(intentService, intent);
+                }
             }
 
-            // request the withdraw
-            intentService.withdraw(intent);
+        } else {
+            final Key key;
+            if (keyString.startsWith("0x")) {
+                // The intent uses a LongKey
+                keyString = keyString.replaceFirst("0x", "");
+                key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
+            } else {
+                // The intent uses a StringKey
+                key = Key.of(keyString, appId);
+            }
 
-            if (purgeAfterRemove || sync) {
-                try { // wait for withdraw event
-                    withdrawLatch.await(5, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    print("Timed out waiting for intent {} withdraw", key);
+            Intent intent = intentService.getIntent(key);
+            if (intent != null) {
+                removeIntent(intentService, intent);
+            }
+        }
+    }
+
+    private void removeIntent(IntentService intentService, Intent intent) {
+        IntentListener listener = null;
+        Key key = intent.key();
+        final CountDownLatch withdrawLatch, purgeLatch;
+        if (purgeAfterRemove || sync) {
+            // set up latch and listener to track uninstall progress
+            withdrawLatch = new CountDownLatch(1);
+            purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
+            listener = (IntentEvent event) -> {
+                if (Objects.equals(event.subject().key(), key)) {
+                    if (event.type() == IntentEvent.Type.WITHDRAWN ||
+                            event.type() == IntentEvent.Type.FAILED) {
+                        withdrawLatch.countDown();
+                    } else if (purgeAfterRemove &&
+                            event.type() == IntentEvent.Type.PURGED) {
+                        purgeLatch.countDown();
+                    }
                 }
-                // double check the state
-                IntentState state = intentService.getIntentState(key);
-                if (purgeAfterRemove && (state == WITHDRAWN || state == FAILED)) {
-                    intentService.purge(intent);
-                }
-                if (sync) { // wait for purge event
+            };
+            intentService.addListener(listener);
+        } else {
+            purgeLatch = null;
+            withdrawLatch = null;
+        }
+
+        // request the withdraw
+        intentService.withdraw(intent);
+
+        if (purgeAfterRemove || sync) {
+            try { // wait for withdraw event
+                withdrawLatch.await(5, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                print("Timed out waiting for intent {} withdraw", key);
+            }
+            // double check the state
+            IntentState state = intentService.getIntentState(key);
+            if (purgeAfterRemove && (state == WITHDRAWN || state == FAILED)) {
+                intentService.purge(intent);
+            }
+            if (sync) { // wait for purge event
                     /* TODO
                        Technically, the event comes before map.remove() is called.
                        If we depend on sync and purge working together, we will
                        need to address this.
                     */
-                    try {
-                        purgeLatch.await(5, TimeUnit.SECONDS);
-                    } catch (InterruptedException e) {
-                        print("Timed out waiting for intent {} purge", key);
-                    }
+                try {
+                    purgeLatch.await(5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    print("Timed out waiting for intent {} purge", key);
                 }
             }
+        }
 
-            if (listener != null) {
-                // clean up the listener
-                intentService.removeListener(listener);
-            }
+        if (listener != null) {
+            // clean up the listener
+            intentService.removeListener(listener);
         }
     }
 }
diff --git a/tools/test/scenarios/net-host-intent.xml b/tools/test/scenarios/net-host-intent.xml
new file mode 100644
index 0000000..d256a58
--- /dev/null
+++ b/tools/test/scenarios/net-host-intent.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ 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.
+  -->
+<scenario name="net-host-intent" description="Network host intent connectivity test">
+    <!-- TODO: parametrize this via recipes -->
+    <group name="Host-Intent-Connectivity">
+        <step name="Uninstall-Reactive-Forwarding"
+              exec="onos ${OC1} app deactivate org.onosproject.fwd org.onosproject.ifwd"/>
+
+        <step name="Find-Host-1" requires="^"
+              exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect ."/>
+        <step name="Find-Host-2" requires="^"
+              exec="onos-mininet sendAndExpect h4 ping -c1 h1 --expect ."/>
+
+        <step name="Create-Intent" requires="^"
+              exec="onos ${OCI} add-host-intent 00:00:00:00:00:01/-1 00:00:00:00:00:04/-1"/>
+
+        <import file="${ONOS_SCENARIOS}/net-link-down-up.xml" namespace="Host-Intent"/>
+        <dependency name="Host-Intent.Net-Link-Down-Up" requires="Create-Intent"/>
+
+        <step name="Remove-Intent" requires="Host-Intent.Net-Link-Down-Up"
+              exec="onos ${OCI} remove-intent --purge"/>
+    </group>
+</scenario>
\ No newline at end of file
diff --git a/tools/test/scenarios/net-link-up-down.xml b/tools/test/scenarios/net-link-down-up.xml
similarity index 94%
rename from tools/test/scenarios/net-link-up-down.xml
rename to tools/test/scenarios/net-link-down-up.xml
index f421227..fda756a 100644
--- a/tools/test/scenarios/net-link-up-down.xml
+++ b/tools/test/scenarios/net-link-down-up.xml
@@ -13,9 +13,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<scenario name="net-link-up-down" description="Network link up-down test">
+<scenario name="net-link-down-up" description="Network link up-down test">
     <!-- TODO: parametrize this via recipes -->
-    <group name="Net-Link-Up-Down">
+    <group name="Net-Link-Down-Up">
         <step name="Ping-1"
               exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect \ 0% packet loss"/>
         <step name="Link-1-Down" requires="Ping-1"
diff --git a/tools/test/scenarios/net-smoke.xml b/tools/test/scenarios/net-smoke.xml
index fec1652..87bd38c 100644
--- a/tools/test/scenarios/net-smoke.xml
+++ b/tools/test/scenarios/net-smoke.xml
@@ -17,13 +17,16 @@
     <group name="Net-Smoke">
         <import file="${ONOS_SCENARIOS}/net-setup.xml"/>
 
-        <import file="${ONOS_SCENARIOS}/net-pingall.xml"/>
-        <dependency name="Net-Pingall" requires="Net-Setup"/>
+        <import file="${ONOS_SCENARIOS}/net-pingall.xml" namespace="Reactive-Forwarding"/>
+        <dependency name="Reactive-Forwarding.Net-Pingall" requires="Net-Setup"/>
 
-        <import file="${ONOS_SCENARIOS}/net-link-up-down.xml"/>
-        <dependency name="Net-Link-Up-Down" requires="~Net-Pingall"/>
+        <import file="${ONOS_SCENARIOS}/net-link-down-up.xml" namespace="Reactive-Forwarding"/>
+        <dependency name="Reactive-Forwarding.Net-Link-Down-Up" requires="~Reactive-Forwarding.Net-Pingall"/>
+
+        <import file="${ONOS_SCENARIOS}/net-host-intent.xml"/>
+        <dependency name="Host-Intent-Connectivity" requires="~Reactive-Forwarding.Net-Link-Down-Up"/>
 
         <import file="${ONOS_SCENARIOS}/net-teardown.xml"/>
-        <dependency name="Net-Teardown" requires="~Net-Link-Up-Down"/>
+        <dependency name="Net-Teardown" requires="~Host-Intent-Connectivity"/>
     </group>
 </scenario>
\ No newline at end of file