http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiExpectTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiExpectTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiExpectTest.java
new file mode 100644
index 0000000..493630b
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiExpectTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import static org.testng.Assert.assertNotNull;
+
+/**
+* Test the CloudStack VolumeApi
+*
+*/
+@Test(groups = "unit", testName = "VolumeApiExpectTest")
+public class VolumeApiExpectTest extends BaseCloudStackExpectTest<VolumeApi> {
+
+   public void testCreateVolumeFromCustomDiskOffering() throws 
NoSuchAlgorithmException, CertificateException {
+      VolumeApi client = requestSendsResponse(
+              HttpRequest.builder()
+                      .method("GET")
+                      .endpoint(
+                              
URI.create("http://localhost:8080/client/api?response=json&"; +
+                                      
"command=createVolume&name=VolumeApiExpectTest-jclouds-volume&diskofferingid=0473f5dd-bca5-4af4-a9b6-db9e8a88a2f6&zoneid=6f9a2921-b22a-4149-8b71-6ffc275a2177&size=1&apiKey=identity&signature=%2BoEjGobVFLr58k19LeAE81bZkKM%3D"))
+                      .addHeader("Accept", "application/json")
+                      .build(),
+              HttpResponse.builder()
+                      .statusCode(200)
+                      
.payload(payloadFromResource("/queryasyncjobresultresponse-createvolume.json"))
+                      .build());
+      
+      AsyncCreateResponse response = 
client.createVolumeFromCustomDiskOfferingInZone("VolumeApiExpectTest-jclouds-volume",
 "0473f5dd-bca5-4af4-a9b6-db9e8a88a2f6", 
"6f9a2921-b22a-4149-8b71-6ffc275a2177", 1);
+      assertNotNull(response);
+   }
+
+   @Override
+   protected VolumeApi clientFrom(CloudStackContext context) {
+      return context.getApi().getVolumeApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiLiveTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiLiveTest.java
new file mode 100644
index 0000000..7ffc3b4
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiLiveTest.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNotSame;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.DiskOffering;
+import org.jclouds.cloudstack.domain.Snapshot;
+import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.domain.Volume;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackApiLiveTest;
+import org.jclouds.cloudstack.options.ListVolumesOptions;
+import org.jclouds.logging.Logger;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * Tests behavior of {@code VolumeApi}
+ */
+@Test(groups = "live", singleThreaded = true, testName = "VolumeApiLiveTest")
+public class VolumeApiLiveTest extends BaseCloudStackApiLiveTest {
+
+   @Resource Logger logger = Logger.NULL;
+
+   protected String prefix = System.getProperty("user.name") + "-" + 
getClass().getSimpleName();
+
+   private String zoneId;
+
+   @BeforeMethod(groups = "live")
+   public void setZoneId() {
+      Set<Zone> zones = client.getZoneApi().listZones();
+      assertNotNull(zones);
+      assertFalse(zones.isEmpty());
+      zoneId = Iterables.get(zones, 0).getId();
+   }
+
+   public void testListVolumes() {
+      Set<Volume> volumes = client.getVolumeApi().listVolumes();
+      assertNotNull(volumes);
+      assertFalse(volumes.isEmpty());
+
+      for (Volume volume : volumes) {
+         checkVolume(volume);
+      }
+   }
+
+   public void testListVolumesById() {
+      Iterable<String> volumeIds = 
Iterables.transform(client.getVolumeApi().listVolumes(), new Function<Volume, 
String>() {
+         public String apply(Volume input) {
+            return input.getId();
+         }
+      });
+      assertNotNull(volumeIds);
+      assertFalse(Iterables.isEmpty(volumeIds));
+
+      for (String id : volumeIds) {
+         Set<Volume> found = 
client.getVolumeApi().listVolumes(ListVolumesOptions.Builder.id(id));
+         assertNotNull(found);
+         assertEquals(1, found.size());
+         Volume volume = Iterables.getOnlyElement(found);
+         assertEquals(id, volume.getId());
+         checkVolume(volume);
+      }
+   }
+
+   public void testListVolumesNonexistantId() {
+      Set<Volume> found = 
client.getVolumeApi().listVolumes(ListVolumesOptions.Builder.id("foo"));
+      assertNotNull(found);
+      assertTrue(found.isEmpty());
+   }
+
+   public void testGetVolumeById() {
+      Iterable<String> volumeIds = 
Iterables.transform(client.getVolumeApi().listVolumes(), new Function<Volume, 
String>() {
+         public String apply(Volume input) {
+            return input.getId();
+         }
+      });
+      assertNotNull(volumeIds);
+      assertFalse(Iterables.isEmpty(volumeIds));
+
+      for (String id : volumeIds) {
+         Volume found = client.getVolumeApi().getVolume(id);
+         assertNotNull(found);
+         assertEquals(id, found.getId());
+         checkVolume(found);
+      }
+   }
+
+   public void testGetVolumeNonexistantId() {
+      Volume found = client.getVolumeApi().getVolume("foo");
+      assertNull(found);
+   }
+
+   protected DiskOffering getPreferredDiskOffering() {
+      for (DiskOffering candidate : 
client.getOfferingApi().listDiskOfferings()) {
+         //any will do
+         return candidate;
+      }
+      throw new AssertionError("No suitable DiskOffering found.");
+   }
+   protected Snapshot getPreferredSnapshot() {
+      for (Snapshot candidate : client.getSnapshotApi().listSnapshots()) {
+         if (candidate.getState() == Snapshot.State.BACKED_UP)
+            return candidate;
+      }
+      throw new AssertionError("No suitable Snapshot found.");
+   }
+
+   protected VirtualMachine getPreferredVirtualMachine() {
+      for (VirtualMachine candidate : 
client.getVirtualMachineApi().listVirtualMachines()) {
+//         this is a guess::
+         if (candidate.getState() == VirtualMachine.State.RUNNING || 
candidate.getState() == VirtualMachine.State.STOPPED)
+            return candidate;
+      }
+      throw new AssertionError("No suitable VirtualMachine found.");
+   }
+
+   protected Volume createPreferredVolumeFromDisk() {
+      AsyncCreateResponse job = 
client.getVolumeApi().createVolumeFromDiskOfferingInZone(prefix + 
"-jclouds-volume",
+            getPreferredDiskOffering().getId(), zoneId);
+      assertTrue(jobComplete.apply(job.getJobId()));
+      logger.info("created volume " + job.getId());
+      return findVolumeWithId(job.getId());
+   }
+
+   public void testCreateVolumeFromDiskofferingInZoneAndDeleteVolume() {
+      logger.info("testCreateVolumeFromDiskofferingInZoneAndDeleteVolume");
+      Volume volume = createPreferredVolumeFromDisk();
+      checkVolume(volume);
+      client.getVolumeApi().deleteVolume(volume.getId());
+   }
+
+   /** Test requires a custom disk offering to be available */
+   public void testCreateVolumeFromCustomDiskOffering() {
+      final int size = 1;
+      DiskOffering offering = null;
+      for (DiskOffering candidate : 
client.getOfferingApi().listDiskOfferings()) {
+         if (candidate.isCustomized()) {
+            offering = candidate;
+            break;
+         }
+      }
+      
+      assertNotNull("No custom disk offering found!", offering);
+      
+      AsyncCreateResponse job = 
client.getVolumeApi().createVolumeFromCustomDiskOfferingInZone(
+                prefix + "-jclouds-volume", offering.getId(), zoneId, size);
+      assertTrue(jobComplete.apply(job.getJobId()));
+      logger.info("created volume " + job.getId());
+      
+      Volume volume = findVolumeWithId(job.getId());
+      try {
+         checkVolume(volume);
+         assertEquals(volume.getSize(), size * 1024 * 1024 * 1024);
+      } finally {
+         client.getVolumeApi().deleteVolume(volume.getId());
+      }
+   }
+
+   /** test requires that a VM exist */
+   public void 
testCreateVolumeFromDiskofferingInZoneAndAttachVolumeToVirtualMachineAndDetachAndDelete()
 {
+      
logger.info("testCreateVolumeFromDiskofferingInZoneAndAttachVolumeToVirtualMachineAndDetachAndDelete");
+      final Volume volume = createPreferredVolumeFromDisk();
+      try {
+
+         checkVolume(volume);
+
+         VirtualMachine virtualMachine = getPreferredVirtualMachine();
+
+         logger.info("attaching volume %s to vm %s", volume, virtualMachine);
+         AsyncCreateResponse job = 
client.getVolumeApi().attachVolume(volume.getId(), virtualMachine.getId());
+         assertTrue(jobComplete.apply(job.getJobId()));
+         Volume attachedVolume = findVolumeWithId(volume.getId());
+
+         checkVolume(attachedVolume);
+         assertEquals(virtualMachine.getId(), 
attachedVolume.getVirtualMachineId());
+         assertNotNull(attachedVolume.getAttached());
+
+         logger.info("detaching volume %s from vm %s", volume, virtualMachine);
+         job = client.getVolumeApi().detachVolume(volume.getId());
+         assertTrue(jobComplete.apply(job.getJobId()));
+         Volume detachedVolume = findVolumeWithId(volume.getId());
+
+         checkVolume(detachedVolume);
+         assertNull(detachedVolume.getAttached());
+
+      } finally {
+         client.getVolumeApi().deleteVolume(volume.getId());
+      }
+   }
+
+   public void testCreateVolumeFromSnapshotInZoneAndDeleteVolume() {
+      logger.info("testCreateVolumeFromSnapshotInZoneAndDeleteVolume (takes 
~3m)");
+      assertNotNull(getPreferredSnapshot());
+
+      AsyncCreateResponse job = 
client.getVolumeApi().createVolumeFromSnapshotInZone(prefix + "-jclouds-volume",
+            getPreferredSnapshot().getId(), zoneId);
+      assertTrue(jobComplete.apply(job.getJobId()));
+      Volume volume = findVolumeWithId(job.getId());
+
+      checkVolume(volume);
+      client.getVolumeApi().deleteVolume(volume.getId());
+   }
+
+   static void checkVolume(final Volume volume) {
+      assertNotNull(volume.getId());
+      assertNotNull(volume.getName());
+      assertNotSame(Volume.Type.UNRECOGNIZED, volume.getType());
+   }
+
+   Volume findVolumeWithId(final String id) {
+      return findVolumeWithId(client, id);
+   }
+
+   static Volume findVolumeWithId(final CloudStackApi client, final String id) 
{
+      for (Volume v : client.getVolumeApi().listVolumes())
+         if (v.getId().equals(id)) return v;
+      throw new NoSuchElementException("no volume with id " + id);
+   }
+
+//   //uncomment to force a cleanup of volumes (since test failures can leave 
messes) 
+//   public void deleteAllWeUsed() {
+//      for (Volume v: client.getVolumeApi().listVolumes()) {
+//         if (v.getName().startsWith(prefix)) {
+//            logger.warn("found apparent detritus, deleting: %s", v);
+//            try {
+//               client.getVolumeApi().deleteVolume(v.getId());
+//            } catch (Exception e) {
+//               logger.warn(e, "failed to delete %s: %s", v, e);
+//            }
+//         }
+//       }
+//   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiTest.java
new file mode 100644
index 0000000..9002f46
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/VolumeApiTest.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import static org.jclouds.reflect.Reflection2.method;
+
+import java.io.IOException;
+
+import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.cloudstack.internal.BaseCloudStackApiTest;
+import org.jclouds.cloudstack.options.ListVolumesOptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.Invokable;
+/**
+ * Tests behavior of {@code EventApi}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "VolumeApiTest")
+public class VolumeApiTest extends BaseCloudStackApiTest<VolumeApi> {
+
+   public void testListVolumes() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, "listVolumes", 
ListVolumesOptions[].class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listVolumes&listAll=true 
HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testGetVolume() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, "getVolume", 
String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(111L));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listVolumes&listAll=true&id=111
 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   HttpRequest createVolumeFromSnapshot = HttpRequest.builder().method("GET")
+                                                     
.endpoint("http://localhost:8080/client/api";)
+                                                     
.addQueryParam("response", "json")
+                                                     .addQueryParam("command", 
"createVolume")
+                                                     .addQueryParam("name", 
"jclouds-volume")
+                                                     
.addQueryParam("snapshotid", "999")
+                                                     .addQueryParam("zoneid", 
"111").build();
+
+   public void testCreateVolumeWithSnapshot() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, 
"createVolumeFromSnapshotInZone", String.class, String.class,
+            String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of("jclouds-volume", 999L, 111l));
+
+      assertRequestLineEquals(httpRequest, 
createVolumeFromSnapshot.getRequestLine());
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      checkFilters(httpRequest);
+
+   }
+
+   HttpRequest createVolumeFromDiskOffering = 
HttpRequest.builder().method("GET")
+                                                         
.endpoint("http://localhost:8080/client/api";)
+                                                         
.addQueryParam("response", "json")
+                                                         
.addQueryParam("command", "createVolume")
+                                                         
.addQueryParam("name", "jclouds-volume")
+                                                         
.addQueryParam("diskofferingid", "999")
+                                                         
.addQueryParam("zoneid", "111").build();
+
+   public void testCreateVolumeFromDiskOffering() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, 
"createVolumeFromDiskOfferingInZone", String.class, String.class,
+            String.class);
+
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of("jclouds-volume", 999L, 111L));
+
+      assertRequestLineEquals(httpRequest, 
createVolumeFromDiskOffering.getRequestLine());
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      checkFilters(httpRequest);
+
+   }
+
+   public void testAttachVolume() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, "attachVolume", 
String.class, String.class);
+
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(111L, 999L));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=attachVolume&id=111&virtualmachineid=999
 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      checkFilters(httpRequest);
+
+   }
+
+   public void testDetachVolume() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, "detachVolume", 
String.class);
+
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(111L));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=detachVolume&id=111 
HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      checkFilters(httpRequest);
+
+   }
+
+   public void testDeleteVolume() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(VolumeApi.class, "deleteVolume", 
String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(111L));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=deleteVolume&id=111 
HTTP/1.1");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+      checkFilters(httpRequest);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiExpectTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiExpectTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiExpectTest.java
new file mode 100644
index 0000000..2d709ad
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiExpectTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.domain.NetworkType;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Test the CloudStack ZoneApi
+ */
+@Test(groups = "unit", testName = "ZoneApiExpectTest")
+public class ZoneApiExpectTest extends BaseCloudStackExpectTest<ZoneApi> {
+
+
+   public void testListZonesWhenResponseIs2xx() {
+      ZoneApi client = requestSendsResponse(
+         HttpRequest.builder().method("GET")
+                    .endpoint("http://localhost:8080/client/api";)
+                    .addQueryParam("response", "json")
+                    .addQueryParam("command", "listZones")
+                    .addQueryParam("listAll", "true")
+                    .addQueryParam("apiKey", "identity")
+                    .addQueryParam("signature", 
"8iHCtck0qfxFTqJ8reyAObRf31I%3D")
+                    .addHeader("Accept", "application/json")
+                    .build(),
+         HttpResponse.builder()
+                     .statusCode(200)
+                     .payload(payloadFromResource("/listzonesresponse.json"))
+                     .build());
+
+      assertEquals(client.listZones(),
+         ImmutableSet.of(
+            Zone.builder()
+               .id("1")
+               .name("San Jose 1")
+               .networkType(NetworkType.ADVANCED)
+               .securityGroupsEnabled(false).build(),
+            Zone.builder()
+               .id("2")
+               .name("Chicago")
+               .networkType(NetworkType.ADVANCED)
+               .securityGroupsEnabled(true).build()));
+   }
+
+   public void testListZonesWhenResponseIs404() {
+      ZoneApi client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&"; +
+                  
"command=listZones&listAll=true&apiKey=identity&signature=8iHCtck0qfxFTqJ8reyAObRf31I%3D"))
+            .addHeader("Accept", "application/json")
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertEquals(client.listZones(), ImmutableSet.of());
+   }
+
+   @Override
+   protected ZoneApi clientFrom(CloudStackContext context) {
+      return context.getApi().getZoneApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiLiveTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiLiveTest.java
new file mode 100644
index 0000000..ae1b480
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiLiveTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.jclouds.cloudstack.domain.NetworkType;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackApiLiveTest;
+import org.jclouds.cloudstack.options.ListZonesOptions;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Tests behavior of {@code ZoneApiLiveTest}
+ */
+@Test(groups = "live", singleThreaded = true, testName = "ZoneApiLiveTest")
+public class ZoneApiLiveTest extends BaseCloudStackApiLiveTest {
+
+   public void testListZones() throws Exception {
+      Set<Zone> response = client.getZoneApi().listZones();
+      assert null != response;
+      long zoneCount = response.size();
+      assertTrue(zoneCount >= 0);
+      for (Zone zone : response) {
+         Zone newDetails = 
Iterables.getOnlyElement(client.getZoneApi().listZones(
+               ListZonesOptions.Builder.id(zone.getId())));
+         assertEquals(zone, newDetails);
+         assertEquals(zone, client.getZoneApi().getZone(zone.getId()));
+         assert zone.getId() != null : zone;
+         assert zone.getName() != null : zone;
+         assert zone.getNetworkType() != null && zone.getNetworkType() != 
NetworkType.UNRECOGNIZED : zone;
+         switch (zone.getNetworkType()) {
+         case ADVANCED:
+            // TODO
+            // assert zone.getVLAN() != null : zone;
+            // assert zone.getDomain() == null : zone;
+            // assert zone.getDomainId() == null : zone;
+            // assert zone.getGuestCIDRAddress() != null : zone;
+            break;
+         case BASIC:
+            assert zone.getVLAN() == null : zone;
+            assert zone.getDNS().size() >= 0 : zone;
+            assert zone.getInternalDNS().size() >= 0 : zone;
+            assert zone.getDomain() == null : zone;
+            assert zone.getDomainId() == null : zone;
+            assert zone.getGuestCIDRAddress() == null : zone;
+            break;
+         }
+
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiTest.java
new file mode 100644
index 0000000..bb5674f
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/features/ZoneApiTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.features;
+
+import static org.jclouds.reflect.Reflection2.method;
+
+import java.io.IOException;
+
+import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.cloudstack.internal.BaseCloudStackApiTest;
+import org.jclouds.cloudstack.options.ListZonesOptions;
+import org.jclouds.functions.IdentityFunction;
+import org.jclouds.http.functions.ParseFirstJsonValueNamed;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.Invokable;
+/**
+ * Tests behavior of {@code ZoneApi}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "ZoneApiTest")
+public class ZoneApiTest extends BaseCloudStackApiTest<ZoneApi> {
+   public void testListZones() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ZoneApi.class, "listZones", 
ListZonesOptions[].class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listZones&listAll=true 
HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      // now make sure request filters apply by replaying
+      httpRequest = (GeneratedHttpRequest) 
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+      httpRequest = (GeneratedHttpRequest) 
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+
+      assertRequestLineEquals(
+            httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listZones&listAll=true&apiKey=identity&signature=8iHCtck0qfxFTqJ8reyAObRf31I%3D
 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, 
ParseFirstJsonValueNamed.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testListZonesOptions() throws SecurityException, 
NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ZoneApi.class, "listZones", 
ListZonesOptions[].class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(ListZonesOptions.Builder.available(true).domainId("5")
+            .id("6")));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listZones&listAll=true&available=true&domainid=5&id=6
 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, 
ParseFirstJsonValueNamed.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testGetZone() throws SecurityException, NoSuchMethodException, 
IOException {
+      Invokable<?, ?> method = method(ZoneApi.class, "getZone", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, 
ImmutableList.<Object> of(6));
+
+      assertRequestLineEquals(httpRequest,
+            "GET 
http://localhost:8080/client/api?response=json&command=listZones&listAll=true&id=6
 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest,
+            Functions.compose(IdentityFunction.INSTANCE, 
IdentityFunction.INSTANCE).getClass());
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
new file mode 100644
index 0000000..2af22ba
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.filters;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.IntegrationTestClient;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.providers.AnonymousProviderMetadata;
+import org.jclouds.rest.internal.BaseRestApiTest.MockModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Tests behavior of {@code QuerySigner}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during
+// surefire
+@Test(groups = "unit", testName = "QuerySignerTest")
+public class QuerySignerTest {
+   public static final Injector INJECTOR = ContextBuilder
+         .newBuilder(
+               
AnonymousProviderMetadata.forApiOnEndpoint(IntegrationTestClient.class,
+                     "http://localhost:8080/client/api";))
+         .credentials("apiKey", "secretKey")
+         .apiVersion("2.2")
+         .modules(ImmutableList.<Module> of(new MockModule(), new 
NullLoggingModule())).buildInjector();
+
+
+   @Test
+   void testCreateStringToSign() {
+      QuerySigner filter = INJECTOR.getInstance(QuerySigner.class);
+
+      assertEquals(
+            filter.createStringToSign(HttpRequest.builder().method("GET")
+                  
.endpoint("http://localhost:8080/client/api?command=listZones";).build()),
+            "apikey=apikey&command=listzones");
+   }
+
+   @Test
+   void testCreateStringToSignWithBrackets() {
+      // This test asserts that key *names* are not URL-encoded - only values
+      // should be encoded, according to "CloudStack API Developer’s Guide".
+      QuerySigner filter = INJECTOR.getInstance(QuerySigner.class);
+
+      assertEquals(
+            filter.createStringToSign(HttpRequest.builder().method("GET")
+                  
.endpoint("http://localhost:8080/client/api?command=deployVirtualMachine&iptonetworklist[0].ip=127.0.0.1&iptonetworklist[0].networkid=1";).build()),
+            
"apikey=apikey&command=deployvirtualmachine&iptonetworklist[0].ip=127.0.0.1&iptonetworklist[0].networkid=1");
+   }
+
+   @Test
+   void testFilter() {
+      QuerySigner filter = INJECTOR.getInstance(QuerySigner.class);
+
+      assertEquals(
+               filter.filter(
+                        HttpRequest.builder().method("GET")
+                                 
.endpoint("http://localhost:8080/client/api?command=listZones";).build())
+                        .getRequestLine(),
+               "GET 
http://localhost:8080/client/api?command=listZones&apiKey=apiKey&signature=2UG8AcnMaozL3BINdjgkJ%2BRzjEY%3D
 HTTP/1.1");
+   }
+
+   @Test
+   void testFilterTwice() {
+      QuerySigner filter = INJECTOR.getInstance(QuerySigner.class);
+      HttpRequest request = HttpRequest.builder().method("GET")
+               
.endpoint("http://localhost:8080/client/api?command=listZones";).build();
+      for (int i = 0; i < 2; i++) {
+         request = filter.filter(request);
+         assertEquals(
+               request.getRequestLine(),
+               "GET 
http://localhost:8080/client/api?command=listZones&apiKey=apiKey&signature=2UG8AcnMaozL3BINdjgkJ%2BRzjEY%3D
 HTTP/1.1");
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
new file mode 100644
index 0000000..85c7c6c
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.AsyncJob;
+import org.jclouds.cloudstack.domain.AsyncJobError;
+import org.jclouds.cloudstack.domain.AsyncJobError.ErrorCode;
+import org.jclouds.cloudstack.features.AsyncJobApi;
+import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+@Test(groups = "unit", testName = "BlockUntilJobCompletesAndReturnResultTest")
+public class BlockUntilJobCompletesAndReturnResultTest {
+
+   public void testApply() {
+      String id = "1";
+      String jobId = "2";
+
+      CloudStackApi client = createMock(CloudStackApi.class);
+      Predicate<String> jobComplete = Predicates.alwaysTrue();
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+
+      expect(client.getAsyncJobApi()).andReturn(jobClient).atLeastOnce();
+      
expect(jobClient.getAsyncJob(jobId)).andReturn(AsyncJob.builder().id(jobId).result("foo").build()).atLeastOnce();
+
+      replay(client);
+      replay(jobClient);
+
+      assertEquals(
+            new BlockUntilJobCompletesAndReturnResult(client, 
jobComplete).<String>apply(AsyncCreateResponse.builder().id(id).jobId(
+                  jobId).build()), "foo");
+
+      verify(client);
+      verify(jobClient);
+
+   }
+
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testJobDoesntCompleteThrowsIllegalStateException() {
+      String id = "1";
+      String jobId = "2";
+
+      CloudStackApi client = createMock(CloudStackApi.class);
+      // the alwaysfalse predicate should blow up with IllegalStateException
+      Predicate<String> jobComplete = Predicates.alwaysFalse();
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+
+      expect(client.getAsyncJobApi()).andReturn(jobClient).atLeastOnce();
+      
expect(jobClient.getAsyncJob(jobId)).andReturn(AsyncJob.builder().id(jobId).result("foo").build()).atLeastOnce();
+
+      replay(client);
+      replay(jobClient);
+
+      assertEquals(
+            new BlockUntilJobCompletesAndReturnResult(client, 
jobComplete).<String>apply(
+                  AsyncCreateResponse.builder().id(id).jobId(jobId).build()), 
"foo");
+
+      verify(client);
+      verify(jobClient);
+
+   }
+
+   @Test(expectedExceptions = UncheckedExecutionException.class)
+   public void testJobWithErrorThrowsUncheckedExecutionException() {
+      String id = "1";
+      String jobId = "2";
+
+      CloudStackApi client = createMock(CloudStackApi.class);
+      Predicate<String> jobComplete = Predicates.alwaysTrue();
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+
+      expect(client.getAsyncJobApi()).andReturn(jobClient).atLeastOnce();
+      expect(jobClient.getAsyncJob(jobId)).andReturn(
+            AsyncJob.builder().id(jobId)
+                  
.error(AsyncJobError.builder().errorCode(ErrorCode.INTERNAL_ERROR).errorText("ERRROR").build())
+                  .result("foo").build())
+            .atLeastOnce();
+
+      replay(client);
+      replay(jobClient);
+
+      assertEquals(
+            new BlockUntilJobCompletesAndReturnResult(client, 
jobComplete).<String>apply(
+                  AsyncCreateResponse.builder().id(id).jobId(jobId).build()), 
"foo");
+
+      verify(client);
+      verify(jobClient);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
new file mode 100644
index 0000000..7d840dc
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+import static org.jclouds.util.Predicates2.retry;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import java.net.UnknownHostException;
+import javax.inject.Singleton;
+
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.IngressRule;
+import org.jclouds.cloudstack.domain.SecurityGroup;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
+import org.jclouds.cloudstack.features.AsyncJobApi;
+import org.jclouds.cloudstack.features.SecurityGroupApi;
+import org.jclouds.cloudstack.features.ZoneApi;
+import org.jclouds.cloudstack.predicates.JobComplete;
+import org.jclouds.cloudstack.suppliers.ZoneIdToZoneSupplier;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+@Test(groups = "unit", testName = "CreateSecurityGroupIfNeededTest")
+public class CreateSecurityGroupIfNeededTest {
+
+   @Test
+   public void testApply() throws UnknownHostException {
+      final CloudStackApi client = createMock(CloudStackApi.class);
+      SecurityGroupApi secClient = createMock(SecurityGroupApi.class);
+      ZoneApi zoneClient = createMock(ZoneApi.class);
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+      
+      SecurityGroup group = createMock(SecurityGroup.class);
+      
+      Zone zone = createMock(Zone.class);
+
+      expect(group.getIngressRules()).andReturn(ImmutableSet.<IngressRule> 
of());
+      expect(group.getId()).andReturn("sec-1234").anyTimes();
+      expect(zone.isSecurityGroupsEnabled()).andReturn(true);
+      
+      expect(client.getSecurityGroupApi()).andReturn(secClient)
+         .anyTimes();
+      expect(client.getZoneApi()).andReturn(zoneClient);
+      expect(client.getAsyncJobApi()).andReturn(jobClient).anyTimes();
+
+      expect(zoneClient.getZone("zone-abc1")).andReturn(zone);
+      expect(secClient.createSecurityGroup("group-1")).andReturn(group);
+      expect(secClient.authorizeIngressPortsToCIDRs("sec-1234",
+                                                    "TCP",
+                                                    22,
+                                                    22,
+                                                    
ImmutableSet.of("0.0.0.0/0"))).andReturn("job-1234");
+
+      replay(client, secClient, zoneClient, zone, group);
+
+      ZoneSecurityGroupNamePortsCidrs input = 
ZoneSecurityGroupNamePortsCidrs.builder()
+         .zone("zone-abc1")
+         .name("group-1")
+         .ports(ImmutableSet.of(22))
+         .cidrs(ImmutableSet.<String> of()).build();
+      
+      CreateSecurityGroupIfNeeded parser = Guice.createInjector(new 
AbstractModule() {
+
+            @Override
+            protected void configure() {
+               bind(new TypeLiteral<Supplier<String>>() {
+                  }).toInstance(Suppliers.ofInstance("1"));
+               bind(CloudStackApi.class).toInstance(client);
+               bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
+                  to(ZoneIdToZone.class);
+               bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() 
{}).
+                  to(ZoneIdToZoneSupplier.class);
+               
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
+            }
+            
+            @Provides
+            @Singleton
+            protected Predicate<String> jobComplete(JobComplete jobComplete) {
+               return retry(jobComplete, 1200, 1, 5, SECONDS);
+            }
+            
+         }).getInstance(CreateSecurityGroupIfNeeded.class);
+      
+      assertEquals(parser.apply(input), group);
+
+      verify(client, secClient, zoneClient, zone, group);
+   }
+
+   
+   @Test
+   public void testApplyGroupAlreadyExists() throws UnknownHostException {
+      final CloudStackApi client = createMock(CloudStackApi.class);
+      SecurityGroupApi secClient = createMock(SecurityGroupApi.class);
+      ZoneApi zoneClient = createMock(ZoneApi.class);
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+      
+      SecurityGroup group = createMock(SecurityGroup.class);
+      
+      Zone zone = createMock(Zone.class);
+
+      expect(group.getId()).andReturn("sec-1234").anyTimes();
+      expect(zone.isSecurityGroupsEnabled()).andReturn(true);
+      
+      expect(client.getSecurityGroupApi()).andReturn(secClient)
+         .anyTimes();
+      expect(client.getZoneApi()).andReturn(zoneClient);
+      expect(client.getAsyncJobApi()).andReturn(jobClient).anyTimes();
+
+      expect(zoneClient.getZone("zone-abc2")).andReturn(zone);
+      expect(secClient.createSecurityGroup("group-1")).andThrow(new 
IllegalStateException());
+      expect(secClient.getSecurityGroupByName("group-1")).andReturn(group);
+
+      replay(client, secClient, zoneClient, zone, group);
+
+      ZoneSecurityGroupNamePortsCidrs input = 
ZoneSecurityGroupNamePortsCidrs.builder()
+         .zone("zone-abc2")
+         .name("group-1")
+         .ports(ImmutableSet.of(22))
+         .cidrs(ImmutableSet.<String> of()).build();
+      
+      CreateSecurityGroupIfNeeded parser = Guice.createInjector(new 
AbstractModule() {
+
+            @Override
+            protected void configure() {
+               bind(new TypeLiteral<Supplier<String>>() {
+                  }).toInstance(Suppliers.ofInstance("1"));
+               bind(CloudStackApi.class).toInstance(client);
+               bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
+                  to(ZoneIdToZone.class);
+               bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() 
{}).
+                  to(ZoneIdToZoneSupplier.class);
+               
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
+            }
+            
+            @Provides
+            @Singleton
+            protected Predicate<String> jobComplete(JobComplete jobComplete) {
+               return retry(jobComplete, 1200, 1, 5, SECONDS);
+            }
+            
+         }).getInstance(CreateSecurityGroupIfNeeded.class);
+      
+      assertEquals(parser.apply(input), group);
+
+      verify(client, secClient, zoneClient, zone, group);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testApplyZoneNoSecurityGroups() throws UnknownHostException {
+      final CloudStackApi client = createMock(CloudStackApi.class);
+      SecurityGroupApi secClient = createMock(SecurityGroupApi.class);
+      ZoneApi zoneClient = createMock(ZoneApi.class);
+      AsyncJobApi jobClient = createMock(AsyncJobApi.class);
+      
+      SecurityGroup group = createMock(SecurityGroup.class);
+      
+      Zone zone = createMock(Zone.class);
+
+      expect(zone.isSecurityGroupsEnabled()).andReturn(false);
+      
+      expect(client.getZoneApi()).andReturn(zoneClient);
+
+      expect(zoneClient.getZone("zone-abc3")).andReturn(zone);
+
+      replay(client, zoneClient, zone);
+
+      ZoneSecurityGroupNamePortsCidrs input = 
ZoneSecurityGroupNamePortsCidrs.builder()
+         .zone("zone-abc3")
+         .name("group-1")
+         .ports(ImmutableSet.of(22))
+         .cidrs(ImmutableSet.<String> of()).build();
+      
+      CreateSecurityGroupIfNeeded parser = Guice.createInjector(new 
AbstractModule() {
+
+            @Override
+            protected void configure() {
+               bind(new TypeLiteral<Supplier<String>>() {
+                  }).toInstance(Suppliers.ofInstance("1"));
+               bind(CloudStackApi.class).toInstance(client);
+               bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
+                  to(ZoneIdToZone.class);
+               bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() 
{}).
+                  to(ZoneIdToZoneSupplier.class);
+               
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
+            }
+            
+            @Provides
+            @Singleton
+            protected Predicate<String> jobComplete(JobComplete jobComplete) {
+               return retry(jobComplete, 1200, 1, 5, SECONDS);
+            }
+            
+         }).getInstance(CreateSecurityGroupIfNeeded.class);
+
+      assertEquals(parser.apply(input), group);
+
+      verify(client, zoneClient, zone);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
new file mode 100644
index 0000000..04aeee3
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.InputStream;
+
+import org.jclouds.cloudstack.domain.AsyncJob;
+import org.jclouds.cloudstack.domain.AsyncJob.ResultCode;
+import org.jclouds.cloudstack.domain.AsyncJob.Status;
+import org.jclouds.cloudstack.domain.AsyncJobError;
+import org.jclouds.cloudstack.domain.AsyncJobError.ErrorCode;
+import org.jclouds.cloudstack.domain.IPForwardingRule;
+import org.jclouds.cloudstack.domain.PublicIPAddress;
+import org.jclouds.cloudstack.domain.Template;
+import org.jclouds.cloudstack.domain.TemplateExtraction;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+@Test(groups = "unit")
+public class ParseAsyncJobFromHttpResponseTest {
+
+   Injector i = Guice.createInjector(new GsonModule() {
+
+      @Override
+      protected void configure() {
+         bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+         super.configure();
+      }
+
+   });
+
+   public void testWithNoResult() {
+      String input = "{ \"queryasyncjobresultresponse\" : 
{\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0} }";
+
+      AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress>builder()
+         .id("860")
+         .status(Status.IN_PROGRESS)
+         .progress(0)
+         .resultCode(ResultCode.SUCCESS).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(input).build());
+
+      assertEquals(response, expects);
+   }
+
+   public void testWithSuccessTrueResultSetsNullResult() {
+      String input = "{ \"queryasyncjobresultresponse\" : 
{\"jobid\":1138,\"jobstatus\":1,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresulttype\":\"object\",\"jobresult\":{\"success\":true}}
 }";
+
+      AsyncJob<PublicIPAddress> expects = AsyncJob.<PublicIPAddress>builder()
+         .id("1138")
+         .status(Status.SUCCEEDED)
+         .progress(0)
+         .resultType("object")
+         .resultCode(ResultCode.SUCCESS).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(input).build());
+      assertEquals(response, expects);
+   }
+
+   public void testWithErrorSetsResultNullSoToAvoidClassCastExceptions() {
+      String input = "{ \"queryasyncjobresultresponse\" : 
{\"jobid\":1103,\"jobstatus\":2,\"jobprocstatus\":0,\"jobresultcode\":530,\"jobresulttype\":\"object\",\"jobresult\":{\"errorcode\":530,\"errortext\":\"Internal
 error executing command, please contact your system administrator\"}} }";
+
+      AsyncJob<PublicIPAddress> expects = AsyncJob
+         .<PublicIPAddress>builder()
+         .id("1103")
+         .status(Status.FAILED)
+         .progress(0)
+         .resultType("object")
+         
.error(AsyncJobError.builder().errorCode(ErrorCode.INTERNAL_ERROR).errorText("Internal
 error executing " +
+            "command, please contact your system administrator").build())
+         .resultCode(ResultCode.FAIL).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(input).build());
+      assertEquals(response, expects);
+   }
+
+   public void testWithUnknownResultReturnsStringifiedJson() {
+      String input = "{ \"queryasyncjobresultresponse\" : 
{\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1}}}}";
+
+      AsyncJob<?> expects = AsyncJob.builder()
+         .id("860")
+         .status(Status.IN_PROGRESS)
+         .progress(0)
+         .resultCode(ResultCode.SUCCESS)
+         .result("{\"bar\":1}")
+         .build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(input).build());
+      assertEquals(response, expects);
+   }
+
+   public void testWithBadResultReturnsMap() {
+      // Not the best result object, but this is an unexpected error case.
+      // Cloud.com have verified
+      // that this case will not happen. This code is only here to prevent
+      // exceptions from being
+      // thrown in case they change their minds.
+      String input = "{ \"queryasyncjobresultresponse\" : 
{\"jobid\":860,\"jobstatus\":0,\"jobprocstatus\":0,\"jobresultcode\":0,\"jobresult\":{\"foo\":{\"bar\":1},\"foo2\":{\"bar2\":2}}}}";
+
+      AsyncJob<?> expects = AsyncJob.builder()
+         .id("860")
+         .status(Status.IN_PROGRESS)
+         .progress(0)
+         .resultCode(ResultCode.SUCCESS)
+         .result(ImmutableMap.of("foo", new JsonBall("{\"bar\":1}"), "foo2", 
new JsonBall("{\"bar2\":2}"))).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(input).build());
+      assertEquals(response, expects);
+   }
+
+   public void testPublicIPAddress() {
+      InputStream is = 
getClass().getResourceAsStream("/queryasyncjobresultresponse-ipaddress.json");
+      AsyncJob<PublicIPAddress> expects = AsyncJob
+         .<PublicIPAddress>builder()
+         .id("860")
+         .status(Status.SUCCEEDED)
+         .progress(0)
+         .resultType("object")
+         .resultCode(ResultCode.SUCCESS)
+         .result(
+            PublicIPAddress
+               .builder()
+               .id("6")
+               .IPAddress("72.52.126.35")
+               .allocated(
+                  new 
SimpleDateFormatDateService().iso8601SecondsDateParse("2011-02-23T20:15:01-0800"))
+               .zoneId("1").zoneName("San Jose 
1").isSourceNAT(false).account("adrian").domainId("1")
+               
.domain("ROOT").usesVirtualNetwork(true).isStaticNAT(false).associatedNetworkId("204")
+               
.networkId("200").state(PublicIPAddress.State.ALLOCATING).build()
+
+         ).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<PublicIPAddress> response = (AsyncJob<PublicIPAddress>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(is).build());
+      assertEquals(response, expects);
+   }
+
+   public void testIPForwardingRule() {
+      InputStream is = 
getClass().getResourceAsStream("/queryasyncjobresultresponse-ipforwardingrule.json");
+      AsyncJob<IPForwardingRule> expects = AsyncJob
+         .<IPForwardingRule>builder()
+         .id("1133")
+         .status(Status.SUCCEEDED)
+         .progress(0)
+         .resultType("object")
+         .resultCode(ResultCode.SUCCESS)
+         .result(
+            
IPForwardingRule.builder().id("109").protocol("tcp").virtualMachineId("226")
+               
.virtualMachineName("i-3-226-VM").IPAddressId("36").IPAddress("72.52.126.65").startPort(22)
+               .endPort(22).state("Active").build()).build();
+
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      @SuppressWarnings("unchecked")
+      AsyncJob<IPForwardingRule> response = (AsyncJob<IPForwardingRule>) 
parser.apply(HttpResponse.builder()
+                                                                               
                 .statusCode(200).message("ok")
+                                                                               
                 .payload(is).build());
+      assertEquals(response, expects);
+   }
+
+   public void testOverloadedKeyName() {
+      InputStream is = 
getClass().getResourceAsStream("/queryasyncjobresultresponse-createtemplate.json");
+      ParseAsyncJobFromHttpResponse parser = 
i.getInstance(ParseAsyncJobFromHttpResponse.class);
+      AsyncJob<?> response = 
parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload(is).build());
+      assertTrue(response.getResult() instanceof Template, "response expected 
to be Template, actually is " + response.getResult().getClass());
+
+      is = 
getClass().getResourceAsStream("/queryasyncjobresultresponse-extracttemplate.json");
+      response = 
parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload(is).build());
+      assertTrue(response.getResult() instanceof TemplateExtraction, "response 
expected to be TemplateExtraction, actually is " + 
response.getResult().getClass());
+   }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobsFromHttpResponseTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobsFromHttpResponseTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobsFromHttpResponseTest.java
new file mode 100644
index 0000000..bb04433
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobsFromHttpResponseTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import org.jclouds.cloudstack.domain.AsyncJob;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+@Test(groups = "unit")
+public class ParseAsyncJobsFromHttpResponseTest {
+
+   Injector injector = Guice.createInjector(new GsonModule() {
+
+      @Override
+      protected void configure() {
+         bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+         super.configure();
+      }
+
+   });
+
+   public void testCanParse() {
+      InputStream is = 
getClass().getResourceAsStream("/listasyncjobsresponse.json");
+
+      ParseAsyncJobsFromHttpResponse parser = 
injector.getInstance(ParseAsyncJobsFromHttpResponse.class);
+      Set<AsyncJob<?>> response = 
parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload(is).build());
+
+      assertEquals(response.size(), 77);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
new file mode 100644
index 0000000..327e1c1
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static 
org.jclouds.cloudstack.options.AssociateIPAddressOptions.Builder.networkId;
+import static 
org.jclouds.cloudstack.options.ListPublicIPAddressesOptions.Builder.allocatedOnly;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.PublicIPAddress;
+import org.jclouds.cloudstack.features.AddressApi;
+import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+@Test(groups = "unit")
+public class ReuseOrAssociateNewPublicIPAddressTest {
+   String networkId = "99l";
+   String zoneId = "100l";
+   // note that it is associated network, not networkId
+   PublicIPAddress address = 
PublicIPAddress.builder().id("200").state(PublicIPAddress.State.ALLOCATED)
+         .associatedNetworkId(networkId).zoneId(zoneId).build();
+
+   public void testReuseWorks() throws SecurityException, 
NoSuchMethodException {
+
+      // create mocks
+      CloudStackApi client = createMock(CloudStackApi.class);
+      BlockUntilJobCompletesAndReturnResult 
blockUntilJobCompletesAndReturnResult = 
createMock(BlockUntilJobCompletesAndReturnResult.class);
+      AddressApi addressClient = createMock(AddressApi.class);
+      expect(client.getAddressApi()).andReturn(addressClient).atLeastOnce();
+
+      // an address is available
+      
expect(addressClient.listPublicIPAddresses(allocatedOnly(true).networkId(networkId))).andReturn(
+            ImmutableSet.<PublicIPAddress> of(address));
+
+      replay(client);
+      replay(blockUntilJobCompletesAndReturnResult);
+      replay(addressClient);
+
+      assertEquals(
+            new ReuseOrAssociateNewPublicIPAddress(client, 
blockUntilJobCompletesAndReturnResult).apply(Network
+                  .builder().id(networkId).zoneId(zoneId).build()), address);
+
+      verify(client);
+      verify(blockUntilJobCompletesAndReturnResult);
+      verify(addressClient);
+
+   }
+
+   public void testAssociateWorks() throws SecurityException, 
NoSuchMethodException {
+
+      // create mocks
+      CloudStackApi client = createMock(CloudStackApi.class);
+      BlockUntilJobCompletesAndReturnResult 
blockUntilJobCompletesAndReturnResult = 
createMock(BlockUntilJobCompletesAndReturnResult.class);
+      AddressApi addressClient = createMock(AddressApi.class);
+      expect(client.getAddressApi()).andReturn(addressClient).atLeastOnce();
+
+      // no ip addresses available
+      
expect(addressClient.listPublicIPAddresses(allocatedOnly(true).networkId(networkId))).andReturn(
+            ImmutableSet.<PublicIPAddress> of());
+
+      AsyncCreateResponse job = 
AsyncCreateResponse.builder().id("1").jobId("2").build();
+      // make sure we created the job relating to a new ip
+      expect(addressClient.associateIPAddressInZone(zoneId, 
networkId(networkId))).andReturn(job);
+
+      
expect(blockUntilJobCompletesAndReturnResult.apply(job)).andReturn(address);
+
+      replay(client);
+      replay(addressClient);
+      replay(blockUntilJobCompletesAndReturnResult);
+
+      assertEquals(
+            new ReuseOrAssociateNewPublicIPAddress(client, 
blockUntilJobCompletesAndReturnResult).apply(Network
+                  .builder().id(networkId).zoneId(zoneId).build()), address);
+
+      verify(client);
+      verify(addressClient);
+      verify(blockUntilJobCompletesAndReturnResult);
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
new file mode 100644
index 0000000..75bfd18
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.testng.Assert.assertEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.jclouds.cloudstack.domain.IPForwardingRule;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.PublicIPAddress;
+import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.features.NATApiLiveTest;
+import org.jclouds.cloudstack.features.VirtualMachineApiLiveTest;
+import org.jclouds.cloudstack.predicates.NetworkPredicates;
+import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.net.HostAndPort;
+
+/**
+ * Tests behavior of {@code StaticNATVirtualMachineInNetwork}
+ */
+@Test(groups = "live", singleThreaded = true, testName = 
"StaticNATVirtualMachineInNetworkLiveTest")
+public class StaticNATVirtualMachineInNetworkLiveTest extends NATApiLiveTest {
+   private PublicIPAddress ip = null;
+   private VirtualMachine vm;
+   private IPForwardingRule rule;
+   private Network network;
+   private boolean networksDisabled;
+
+   @BeforeGroups(groups = "live")
+   public void setupClient() {
+      super.setupContext();
+      prefix += "nat";
+      try {
+         network = find(client.getNetworkApi().listNetworks(), 
NetworkPredicates.supportsStaticNAT());
+         String defaultTemplate = template != null ? template.getImageId() : 
null;
+         vm = VirtualMachineApiLiveTest.createVirtualMachineInNetwork(network,
+               defaultTemplateOrPreferredInZone(defaultTemplate, client, 
network.getZoneId()), client, jobComplete,
+               virtualMachineRunning);
+         if (vm.getPassword() != null && 
!loginCredentials.getOptionalPassword().isPresent())
+            loginCredentials = 
loginCredentials.toBuilder().password(vm.getPassword()).build();
+      } catch (NoSuchElementException e) {
+         networksDisabled = true;
+      }
+   }
+
+   public void testCreateIPForwardingRule() throws Exception {
+      if (networksDisabled)
+         return;
+      BlockUntilJobCompletesAndReturnResult blocker = new 
BlockUntilJobCompletesAndReturnResult(client, jobComplete);
+      StaticNATVirtualMachineInNetwork fn = new 
StaticNATVirtualMachineInNetwork(client, reuseOrAssociate, network);
+      CreatePortForwardingRulesForIP createPortForwardingRulesForIP = new 
CreatePortForwardingRulesForIP(client,
+            blocker, CacheBuilder.newBuilder().<String, Set<IPForwardingRule>> 
build(
+                  new GetIPForwardingRulesByVirtualMachine(client)));
+
+      // logger
+      injector.injectMembers(blocker);
+      injector.injectMembers(fn);
+      injector.injectMembers(createPortForwardingRulesForIP);
+
+      ip = fn.apply(vm);
+
+      createPortForwardingRulesForIP.apply(ip, ImmutableSet.of(22));
+
+      rule = 
getOnlyElement(filter(client.getNATApi().getIPForwardingRulesForIPAddress(ip.getId()),
+            new Predicate<IPForwardingRule>() {
+               @Override
+               public boolean apply(IPForwardingRule rule) {
+                  return rule != null && rule.getStartPort() == 22;
+               }
+            }));
+      assertEquals(rule.getIPAddressId(), ip.getId());
+      assertEquals(rule.getVirtualMachineId(), vm.getId());
+      assertEquals(rule.getStartPort(), 22);
+      assertEquals(rule.getProtocol(), "tcp");
+      checkRule(rule);
+      HostAndPort socket = HostAndPort.fromParts(ip.getIPAddress(), 22);
+      checkSSH(socket);
+   }
+
+   @AfterGroups(groups = "live")
+   @Override
+   protected void tearDownContext() {
+      if (rule != null) {
+         client.getNATApi().deleteIPForwardingRule(rule.getId());
+      }
+      if (vm != null) {
+         
jobComplete.apply(client.getVirtualMachineApi().destroyVirtualMachine(vm.getId()));
+      }
+      if (ip != null) {
+         client.getAddressApi().disassociateIPAddress(ip.getId());
+      }
+      super.tearDownContext();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/WindowsLoginCredentialsFromEncryptedDataTest.java
----------------------------------------------------------------------
diff --git 
a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/WindowsLoginCredentialsFromEncryptedDataTest.java
 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/WindowsLoginCredentialsFromEncryptedDataTest.java
new file mode 100644
index 0000000..a59ecad
--- /dev/null
+++ 
b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/test/java/org/jclouds/cloudstack/functions/WindowsLoginCredentialsFromEncryptedDataTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jclouds.cloudstack.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.jclouds.cloudstack.domain.EncryptedPasswordAndPrivateKey;
+import org.jclouds.date.DateService;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.encryption.internal.JCECrypto;
+import org.testng.annotations.Test;
+
+public class WindowsLoginCredentialsFromEncryptedDataTest {
+
+   private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE 
KEY-----\n" +
+      
"MIIEowIBAAKCAQEAmN6GOSMnyGNWN19ETBh11tJB5OGs3Dps8kPWqAhF9RyL/mKwkW26vH+h/5Z5\n"
 +
+      
"cA5T80pK72kNnXObFaMHNoX3lavrc6yXF+8F3f1tlFX2Z+iB1pYXz1oBPqT6oOmc2XzcsJuJRakd\n"
 +
+      
"zwRwHDaqljpaW7+TZlxhMa1DmUkD/HHMxDCK8jbUIZDc6BZSrnj2uPwHwW737NRE4aC3fcu4LMwf\n"
 +
+      
"b2VotbNGNiAnNmrb/vtIIGkFE8NYEMpiz0WYTWX4eVKpJImv1PR6G1fMLSvudJs0ARObuLDvuonn\n"
 +
+      
"SCFFdkibrwMKYbHVGGh6FoY1Vy0sqI55dgQU1kSNouiDgOGxgx+TIwIDAQABAoIBAHCS/nk5QGS7\n"
 +
+      
"cpRYXa1EHhNSxx/MaUXM6MoH1x3q6cm1egqdlrWh/vAtdZkIsOkqQ/xX65Me493dcomegwNN6KOZ\n"
 +
+      
"9Uw7/xCq/sEZjga8vzaJ7IOgCGy0NVJyn/a70rv+zW5pO8/G2KLI+95rC3iSBFSoYd3xjcnNdIh/\n"
 +
+      
"UqYnD8oxYpKmf7418pMPsBrkglkFlbVBPiDXdpoSziqSN6uWQG4Yh0WR87aElhM9JJW50Hh6h7g5\n"
 +
+      
"OvgCBzS8G+KXCjqimk108+/ed5Nl6VhPAf79yCVZUueKBhaf2r0Kkyxg7M/Y+LJwcoUusIP7Cv7G\n"
 +
+      
"xyzG2vi21prWRCm2sVCUDyQy5qECgYEA92jGVAaB3OGEUIXn7eVE3U3FQH37XcJMGsHqBIzDG13p\n"
 +
+      
"C97HdN21rwRkz+G2eAsIxA+p9BsO7dSmtKC60kl6iMRgltS3W7xoC37N9BtjhpciHcLg8c70oyDx\n"
 +
+      
"qHiLKuDi90mZ1FPmWupO4FJnGEB3evHUKZSpTrVVMzt+tyEn/psCgYEAni1hrYoMkQgN3sEC3CKB\n"
 +
+      
"0jQkrOMvY219B8Tdf9LXSuP6z9POagDBDhkeT3xn8rAOmOfVGHYdO0CvPqmAkmXhf+g+OREdecQa\n"
 +
+      
"uY0FmvcTt+Dx0c6pRZmm5AhvUVXFXqONsSg79iviXbUy5Hik0k5HTs5E6B4obrh5W+xfMTUXghkC\n"
 +
+      
"gYBn92uAW8uumkYT4HF6EuJBbTD6zPYYjFGW3O4OQ2ip02jfSBrhDVoP1fTXNq6K+3gPi9WLcuNv\n"
 +
+      
"JfF37iMTwzTuzDcaqwDyV9YRHpRFhEzqfhAkGYSVmLZM5scmWKGCv0YhTJiMFUWz5sqGkZopIs4S\n"
 +
+      
"qBTT9FjBbooDIXk6U4CPCQKBgFdVBxEhnz6UC9RpDIMuKi88yuMJrChhUx7u+ryQVH3s0ZXdg6HT\n"
 +
+      
"OMPn6mxIa7v6qJSTq3wN+qW0WQ1n2Kz7wz0zpOctI/EO7RJ1YhrlP+XONLV6PMtIwnQ0lAF8MbTG\n"
 +
+      
"6HxfknugTyMd4DN0yMu0nHpOOI1P2VMIVzkBkK1CevBBAoGBALROGR7a+eijHdp0/A0chfUoBmud\n"
 +
+      
"/TsUt+0g/vf1p69rMt6DqEGMgMtp2jIRnwvLElS7gVqnCTEclxNU/0rCXR+V7ImJm8J4f0ff8m0Y\n"
 +
+      "Fir9nfCYStszo25NvLFfynS9d/aoBuvqGJaiQyNXiyBJ4MaxxFYagzAWTnDX+kzTlkZ2\n" 
+
+      "-----END RSA PRIVATE KEY-----";
+   private static final String ENCRYPTED_PASSWORD = 
"gO1oMoIjjIifv2iqcfIKiQD7ziOTVXsuaBJFEQrZdb8uJH/LsAiJXZeGKEeXlHl/oMoR3HEIoYuHxl+p5iHdrpP889RmxWBDGOWC5iTUzK6CRa5mFmF1I5Lpt7v2YeVoQWihSM8B19BEdBdY1svQp9nyhPB4AqLDrY28x/OrmRh/qYq953i6Y4Z8c76OHqqGcUYM4ePysRlcizSgQjdkEDmKC10Ak3OFRRx3/LqYsFIMiOHeg47APg+UANNTyRiTIia5FDhSeHJzaeYCBRQ7UYH0z2rg4cX3YjOz/MoznjHiaaN4MO+5N3v84VawnqwKOvlwPyI2bmz0+9Tr6DKzqA==";
+
+   protected final DateService dateService = new SimpleDateFormatDateService();
+
+   @Test
+   public void testApply() throws Exception {
+      WindowsLoginCredentialsFromEncryptedData f = new 
WindowsLoginCredentialsFromEncryptedData(new JCECrypto());
+
+      LoginCredentials credentials = f.apply(new 
EncryptedPasswordAndPrivateKey(ENCRYPTED_PASSWORD, PRIVATE_KEY));
+
+      assertEquals(credentials.getUser(), "Administrator");
+      assertEquals(credentials.getOptionalPassword().get(), "u4.y9mb;nR.");
+      assertFalse(credentials.getOptionalPrivateKey().isPresent());
+   }
+}

Reply via email to