Updated Branches:
  refs/heads/master ccb432398 -> a906f9f4e

JCLOUDS-138. Add CloudStackImageExtension support.


Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/a906f9f4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/a906f9f4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/a906f9f4

Branch: refs/heads/master
Commit: a906f9f4ec2bbb58931076bfba118b51c9674c08
Parents: ccb4323
Author: Andrew Bayer <[email protected]>
Authored: Thu Jun 20 16:59:13 2013 -0700
Committer: Andrew Bayer <[email protected]>
Committed: Wed Jun 26 13:24:16 2013 -0700

----------------------------------------------------------------------
 .../CloudStackComputeServiceContextModule.java  |  12 ++
 .../extensions/CloudStackImageExtension.java    | 163 +++++++++++++++
 .../CloudStackImageExtensionExpectTest.java     | 200 +++++++++++++++++++
 .../CloudStackImageExtensionLiveTest.java       |  44 ++++
 .../createtemplateresponse-imageextension.json  |   1 +
 .../test/resources/createtemplateresponse.json  |   1 +
 .../listtemplatesresponse-imageextension.json   |   1 +
 ...tvirtualmachinesresponse-imageextension.json |   2 +
 .../listvolumesreponse-imageextension.json      |   2 +
 .../listvolumesresponse-imageextension.json     |   1 +
 ...tresponse-createtemplate-imageextension.json |   2 +
 ...ponse-stopvirtualmachine-imageextension.json |   2 +
 ...opvirtualmachineresponse-imageextension.json |   1 +
 13 files changed, 432 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
 
b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
index 8f054cf..92e0b6c 100644
--- 
a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
+++ 
b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
@@ -31,6 +31,7 @@ import javax.inject.Singleton;
 
 import org.jclouds.cloudstack.CloudStackClient;
 import org.jclouds.cloudstack.compute.CloudStackComputeService;
+import org.jclouds.cloudstack.compute.extensions.CloudStackImageExtension;
 import org.jclouds.cloudstack.compute.functions.OrphanedGroupsByZoneId;
 import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware;
 import org.jclouds.cloudstack.compute.functions.TemplateToImage;
@@ -73,6 +74,7 @@ import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.extensions.ImageExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.Location;
 import org.jclouds.rest.AuthorizationException;
@@ -80,6 +82,7 @@ import 
org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExc
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
 import com.google.common.cache.CacheBuilder;
@@ -136,6 +139,9 @@ public class CloudStackComputeServiceContextModule extends
       bind(new TypeLiteral<Function<Set<? extends NodeMetadata>,  
Multimap<String, String>>>() {
       }).to(OrphanedGroupsByZoneId.class);
 
+      bind(new TypeLiteral<ImageExtension>() {
+      }).to(CloudStackImageExtension.class);
+
       // to have the compute service adapter override default locations
       install(new LocationsFromComputeServiceAdapterModule<VirtualMachine, 
ServiceOffering, Template, Zone>(){});
    }
@@ -252,4 +258,10 @@ public class CloudStackComputeServiceContextModule extends
          NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(),
          NetworkType.BASIC, new BasicNetworkOptionsConverter());
    }
+
+   @Override
+   protected Optional<ImageExtension> provideImageExtension(Injector i) {
+      return Optional.of(i.getInstance(ImageExtension.class));
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtension.java
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtension.java
 
b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtension.java
new file mode 100644
index 0000000..c2d27eb
--- /dev/null
+++ 
b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtension.java
@@ -0,0 +1,163 @@
+/*
+ * 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.compute.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.find;
+import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
+import static org.jclouds.location.predicates.LocationPredicates.idEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.cloudstack.CloudStackClient;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.Template;
+import org.jclouds.cloudstack.domain.TemplateMetadata;
+import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.domain.Volume;
+import org.jclouds.cloudstack.options.CreateTemplateOptions;
+import org.jclouds.cloudstack.options.ListVolumesOptions;
+import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.CloneImageTemplate;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.ImageTemplate;
+import org.jclouds.compute.domain.ImageTemplateBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.Atomics;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
+/**
+ * CloudStack implementation of {@link ImageExtension}
+ * 
+ * @author Andrew Bayer
+ *
+ */
+@Singleton
+public class CloudStackImageExtension implements ImageExtension {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final CloudStackClient client;
+   private final ListeningExecutorService userExecutor;
+   private final Supplier<Set<? extends Location>> locations;
+   private final Predicate<AtomicReference<Image>> imageAvailablePredicate;
+   private final BlockUntilJobCompletesAndReturnResult 
blockUntilJobCompletesAndReturnResult;
+   private final Predicate<String> jobComplete;
+
+   @Inject
+   public CloudStackImageExtension(CloudStackClient client,
+                                   @Named(Constants.PROPERTY_USER_THREADS) 
ListeningExecutorService userExecutor,
+                                   @Memoized Supplier<Set<? extends Location>> 
locations,
+                                   @Named(TIMEOUT_IMAGE_AVAILABLE) 
Predicate<AtomicReference<Image>> imageAvailablePredicate,
+                                   BlockUntilJobCompletesAndReturnResult 
blockUntilJobCompletesAndReturnResult,
+                                   Predicate<String> jobComplete) {
+      this.client = checkNotNull(client, "client");
+      this.userExecutor = checkNotNull(userExecutor, "userExecutor");
+      this.locations = checkNotNull(locations, "locations");
+      this.imageAvailablePredicate = checkNotNull(imageAvailablePredicate, 
"imageAvailablePredicate");
+      this.blockUntilJobCompletesAndReturnResult = 
checkNotNull(blockUntilJobCompletesAndReturnResult,
+                                                                
"blockUntilJobCompletesAndReturnResult");
+      this.jobComplete = checkNotNull(jobComplete, "jobComplete");
+   }
+
+   @Override
+   public ImageTemplate buildImageTemplateFromNode(String name, String id) {
+      VirtualMachine vm = 
client.getVirtualMachineClient().getVirtualMachine(id);
+      if (vm == null)
+         throw new NoSuchElementException("Cannot find vm with id: " + id);
+      CloneImageTemplate template = new 
ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(name).build();
+      return template;
+   }
+
+   @Override
+   public ListenableFuture<Image> createImage(ImageTemplate template) {
+      checkState(template instanceof CloneImageTemplate,
+               " cloudstack only currently supports creating images through 
cloning.");
+      CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
+
+      VirtualMachine vm = 
client.getVirtualMachineClient().getVirtualMachine(cloneTemplate.getSourceNodeId());
+      String stopJob = 
client.getVirtualMachineClient().stopVirtualMachine(vm.getId());
+      jobComplete.apply(stopJob);
+
+      Set<Volume> volumes = 
client.getVolumeClient().listVolumes(ListVolumesOptions.Builder.virtualMachineId(vm.getId()));
+      Volume volume = Iterables.getOnlyElement(volumes);
+      
+      CreateTemplateOptions options = 
CreateTemplateOptions.Builder.volumeId(volume.getId());
+      AsyncCreateResponse templateJob = 
client.getTemplateClient().createTemplate(TemplateMetadata.builder()
+                                                                               
   .name(cloneTemplate.getName())
+                                                                               
   .osTypeId(vm.getGuestOSId())
+                                                                               
   .displayText(cloneTemplate.getName())
+                                                                               
   .build(), options);
+      Template newTemplate = 
blockUntilJobCompletesAndReturnResult.<Template>apply(templateJob);
+
+      logger.info(">> Registered new template %s, waiting for it to become 
available.", newTemplate.getId());
+
+      final AtomicReference<Image> image = Atomics.newReference(new 
ImageBuilder()
+                                                                
.location(find(locations.get(), idEquals(vm.getZoneId())))
+                                                                
.id(newTemplate.getId())
+                                                                
.providerId(newTemplate.getId())
+                                                                
.description(cloneTemplate.getName())
+                                                                
.operatingSystem(OperatingSystem.builder().description(cloneTemplate.getName()).build())
+                                                                
.status(Image.Status.PENDING).build());
+
+      return userExecutor.submit(new Callable<Image>() {
+         @Override
+         public Image call() throws Exception {
+            if (imageAvailablePredicate.apply(image))
+               return image.get();
+            // TODO: get rid of the expectation that the image will be 
available, as it is very brittle
+            throw new UncheckedTimeoutException("Image was not created within 
the time limit: " + image.get());
+         }
+      });
+   }
+
+   @Override
+   public boolean deleteImage(String id) {
+      try {
+         AsyncCreateResponse deleteJob = 
client.getTemplateClient().deleteTemplate(id);
+         jobComplete.apply(deleteJob.getJobId());
+         return true;
+      } catch (Exception e) {
+         return false;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionExpectTest.java
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionExpectTest.java
 
b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionExpectTest.java
new file mode 100644
index 0000000..c550a59
--- /dev/null
+++ 
b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionExpectTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.compute.extensions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.compute.CloudStackComputeService;
+import 
org.jclouds.cloudstack.internal.BaseCloudStackComputeServiceContextExpectTest;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageTemplate;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.util.concurrent.Futures;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Andrew Bayer
+ */
+@Test(groups = "unit", testName = "CloudStackImageExtensionExpectTest")
+public class CloudStackImageExtensionExpectTest extends 
BaseCloudStackComputeServiceContextExpectTest<ComputeService> {
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty("jclouds.zones", "MTV-Zone1");
+      return overrides;
+   }
+   
+   public void testCreateImage() {
+      HttpRequest listVM = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "listVirtualMachines")
+         .addQueryParam("listAll", "true")
+         .addQueryParam("id", "3239ade9-fd25-405c-8eda-59f0313a3fb0")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "Qq7Br3qNsyr5ifWZHIrLAslhwm0%3D")
+         .addHeader("Accept", "application/json")
+         .build(); 
+
+      HttpResponse listVMResponse = HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/listvirtualmachinesresponse-imageextension.json"))
+         .build();
+
+      HttpRequest stopVM = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "stopVirtualMachine")
+         .addQueryParam("id", "3239ade9-fd25-405c-8eda-59f0313a3fb0")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "y9vxRK61K8sDoWtvSJHIx5WO9AE%3D")
+         .addHeader("Accept", "application/json")
+         .build(); 
+
+      HttpResponse stopVMResponse = HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/stopvirtualmachineresponse-imageextension.json"))
+         .build();
+
+      HttpRequest stopAsyncJobResult = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "queryAsyncJobResult")
+         .addQueryParam("jobid", "a7d5127b-24a2-4a44-a4a7-25a6d057b453")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "CVpnN%2FSbx%2FMCOOyj%2FoVAt3bn684%3D")
+         .addHeader("Accept", "application/json")
+         .build();
+
+      HttpResponse stopAsyncJobResultResponse = 
HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/queryasyncjobresultresponse-stopvirtualmachine-imageextension.json"))
+         .build();
+
+      HttpRequest listVolumes = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "listVolumes")
+         .addQueryParam("listAll", "true")
+         .addQueryParam("virtualmachineid", 
"3239ade9-fd25-405c-8eda-59f0313a3fb0")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "drLPf9NE9ROZPOfeDkASiKa50t8%3D")
+         .addHeader("Accept", "application/json")
+         .build(); 
+
+      HttpResponse listVolumesResponse = HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/listvolumesresponse-imageextension.json"))
+         .build();
+
+
+      HttpRequest createTemplate = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "createTemplate")
+         .addQueryParam("volumeid", "fe1ada16-57a0-40ae-b577-01a153690fb4")
+         .addQueryParam("name", "temp-template-ignore")
+         .addQueryParam("ostypeid", "45de18f1-87c6-4646-8099-95c61f2a300a")
+         .addQueryParam("displaytext", "temp-template-ignore")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "madHsBgxjYbM6JnZKYWajOlfPlY%3D")
+         .addHeader("Accept", "application/json")
+         .build(); 
+
+      HttpResponse createTemplateResponse = 
HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/createtemplateresponse-imageextension.json"))
+         .build();
+
+      HttpRequest createAsyncJobResult = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "queryAsyncJobResult")
+         .addQueryParam("jobid", "4e345230-8fcc-48a3-8a37-c5fe960df671")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "6mTKL9fjz7bn6C7tOaZBzKdZwHs%3D")
+         .addHeader("Accept", "application/json")
+         .build();
+
+      HttpResponse createAsyncJobResultResponse = 
HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/queryasyncjobresultresponse-createtemplate-imageextension.json"))
+         .build();
+
+      HttpRequest getTemplate = HttpRequest.builder().method("GET")
+         .endpoint("http://localhost:8080/client/api";)
+         .addQueryParam("response", "json")
+         .addQueryParam("command", "listTemplates")
+         .addQueryParam("listAll", "true")
+         .addQueryParam("templatefilter", "executable")
+         .addQueryParam("id", "3dc6ce25-a6cf-4d60-a664-3499993b511b")
+         .addQueryParam("apiKey", "APIKEY")
+         .addQueryParam("signature", "dXv%2Bl04EDd7hmrWv5CdW8v298RE%3D")
+         .addHeader("Accept", "application/json")
+         .build(); 
+
+      HttpResponse getTemplateResponse = HttpResponse.builder().statusCode(200)
+         
.payload(payloadFromResource("/listtemplatesresponse-imageextension.json"))
+         .build();
+
+      Map<HttpRequest, HttpResponse> requestResponseMap = 
ImmutableMap.<HttpRequest, HttpResponse> builder()
+            .put(listTemplates, listTemplatesResponse)
+            .put(listOsTypes, listOsTypesResponse)
+            .put(listOsCategories, listOsCategoriesResponse)
+            .put(listZones, listZonesResponse)
+            .put(listServiceOfferings, listServiceOfferingsResponse)
+            .put(listAccounts, listAccountsResponse)
+            .put(listNetworks, listNetworksResponse)
+            .put(getZone, getZoneResponse)
+            .put(listVM, listVMResponse)
+            .put(stopVM, stopVMResponse)
+            .put(stopAsyncJobResult, stopAsyncJobResultResponse)
+            .put(listVolumes, listVolumesResponse)
+            .put(createTemplate, createTemplateResponse)
+            .put(createAsyncJobResult, createAsyncJobResultResponse)
+            .put(getTemplate, getTemplateResponse)
+            .build();
+
+      ImageExtension apiThatCreatesImage = 
requestsSendResponses(requestResponseMap).getImageExtension().get();
+      
+      ImageTemplate newImageTemplate = 
apiThatCreatesImage.buildImageTemplateFromNode("temp-template-ignore", 
"3239ade9-fd25-405c-8eda-59f0313a3fb0");
+      
+      Image image = 
Futures.getUnchecked(apiThatCreatesImage.createImage(newImageTemplate));
+      assertEquals(image.getId(), "3dc6ce25-a6cf-4d60-a664-3499993b511b");
+   }
+
+   @Override
+   public ComputeService createClient(Function<HttpRequest, HttpResponse> fn, 
Module module, Properties props) {
+      return clientFrom(createInjector(fn, module, 
props).getInstance(CloudStackContext.class));
+   }
+
+   @Override
+   protected ComputeService clientFrom(CloudStackContext context) {
+      return context.getComputeService();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionLiveTest.java
 
b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionLiveTest.java
new file mode 100644
index 0000000..2367489
--- /dev/null
+++ 
b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/extensions/CloudStackImageExtensionLiveTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.compute.extensions;
+
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+/**
+ * Live test for CloudStack {@link ImageExtension} implementation.
+ * 
+ * @author Andrew Bayer
+ * 
+ */
+@Test(groups = "live", singleThreaded = true, testName = 
"CloudStackImageExtensionLiveTest")
+public class CloudStackImageExtensionLiveTest extends 
BaseImageExtensionLiveTest {
+
+   public CloudStackImageExtensionLiveTest() {
+      provider = "cloudstack";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/createtemplateresponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/createtemplateresponse-imageextension.json 
b/apis/cloudstack/src/test/resources/createtemplateresponse-imageextension.json
new file mode 100644
index 0000000..bcbba70
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/createtemplateresponse-imageextension.json
@@ -0,0 +1 @@
+{ "createtemplateresponse" : 
{"id":"3dc6ce25-a6cf-4d60-a664-3499993b511b","jobid":"4e345230-8fcc-48a3-8a37-c5fe960df671"}
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/createtemplateresponse.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/createtemplateresponse.json 
b/apis/cloudstack/src/test/resources/createtemplateresponse.json
new file mode 100644
index 0000000..bcbba70
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/createtemplateresponse.json
@@ -0,0 +1 @@
+{ "createtemplateresponse" : 
{"id":"3dc6ce25-a6cf-4d60-a664-3499993b511b","jobid":"4e345230-8fcc-48a3-8a37-c5fe960df671"}
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/listtemplatesresponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/listtemplatesresponse-imageextension.json 
b/apis/cloudstack/src/test/resources/listtemplatesresponse-imageextension.json
new file mode 100644
index 0000000..1643700
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/listtemplatesresponse-imageextension.json
@@ -0,0 +1 @@
+{ "listtemplatesresponse" : { "count":1 ,"template" : [  
{"id":"3dc6ce25-a6cf-4d60-a664-3499993b511b","name":"temp-template-ignore","displaytext":"sometext","ispublic":false,"created":"2013-06-21T12:37:01-0700","isready":true,"passwordenabled":false,"format":"QCOW2","isfeatured":false,"crossZones":false,"ostypeid":"be820210-c741-4c36-9ba1-f38363bd37d5","ostypename":"CentOS
 5.6 (32-bit)","account":"andrew","zoneid":"1","zonename":"San Jose 
1","status":"Download 
Complete","size":26843545600,"templatetype":"USER","hypervisor":"KVM","domain":"ROOT","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","isextractable":false,"checksum":"6dd5d7f073ff951b033a8cadb877bde2","sourcetemplateid":"9d597afb-4d3d-4a69-bf45-f95b0d1ee160"}
 ] } }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/listvirtualmachinesresponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/listvirtualmachinesresponse-imageextension.json
 
b/apis/cloudstack/src/test/resources/listvirtualmachinesresponse-imageextension.json
new file mode 100644
index 0000000..aae4d48
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/listvirtualmachinesresponse-imageextension.json
@@ -0,0 +1,2 @@
+{ "listvirtualmachinesresponse" : { "count":1 ,"virtualmachine" : [
+{"id":"3239ade9-fd25-405c-8eda-59f0313a3fb0","name":"apb-cent32-bld","displayname":"apb-cent32-bld","account":"andrew","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","domain":"ROOT","created":"2013-04-16T16:25:57-0700","state":"Stopped","haenable":false,"zoneid":"1","zonename":"San
 Jose 
1","hostid":"0","templateid":"9d597afb-4d3d-4a69-bf45-f95b0d1ee160","templatename":"centos-6.2-x32-template","templatedisplaytext":"CentOS
 6.2 x32 
Template","passwordenabled":false,"serviceofferingid":"7cc4f8c3-7c56-4155-9916-9f42072ea712","serviceofferingname":"Tiny","cpunumber":1,"cpuspeed":1600,"memory":1024,"cpuused":"0.01%","networkkbsread":319420,"networkkbswrite":3,"guestosid":"45de18f1-87c6-4646-8099-95c61f2a300a","rootdeviceid":0,"rootdevicetype":"Filesystem","securitygroup":[{"id":"50288900-28eb-4d7f-9c33-e4c26e3b66d2","name":"default","description":"Default
 Security 
Group"}],"nic":[{"id":"fd65b6fd-2c3e-4c8a-a0bc-6a5db4e8007a","networkid":"0c82cd9f-ca64-4df4-9300-b33d81562403","netmask":
 
"255.255.254.0","gateway":"10.20.92.3","ipaddress":"10.20.92.96","traffictype":"Guest","type":"Shared","isdefault":true,"macaddress":"06:ec:68:00:0f:04"}],"hypervisor":"KVM","instancename":"i-4-69942-VM"}
 ] } }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/listvolumesreponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/listvolumesreponse-imageextension.json 
b/apis/cloudstack/src/test/resources/listvolumesreponse-imageextension.json
new file mode 100644
index 0000000..420f365
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listvolumesreponse-imageextension.json
@@ -0,0 +1,2 @@
+{ "listvolumesresponse" : { "count":1 ,"volume" : [
+{"id":"fe1ada16-57a0-40ae-b577-01a153690fb4","name":"ROOT-69942","zoneid":"1","zonename":"San
 Jose 
1","type":"ROOT","deviceid":0,"virtualmachineid":"3239ade9-fd25-405c-8eda-59f0313a3fb0","vmname":"apb-cent32-bld","vmdisplayname":"apb-cent32-bld","vmstate":"Stopped","size":139264,"created":"2013-04-16T16:25:57-0700","state":"Ready","account":"andrew","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","domain":"ROOT","storagetype":"local","hypervisor":"KVM","storage":"c2422.halxg.cloudera.com","destroyed":false,"serviceofferingid":"7cc4f8c3-7c56-4155-9916-9f42072ea712","serviceofferingname":"Tiny","serviceofferingdisplaytext":"Tiny
 (1 core, 1GB RAM)","isextractable":false} ] } }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/listvolumesresponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/listvolumesresponse-imageextension.json 
b/apis/cloudstack/src/test/resources/listvolumesresponse-imageextension.json
new file mode 100644
index 0000000..5fa8fd1
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listvolumesresponse-imageextension.json
@@ -0,0 +1 @@
+{ "listvolumesresponse" : { "count":1 ,"volume" : [  
{"id":"fe1ada16-57a0-40ae-b577-01a153690fb4","name":"ROOT-69942","zoneid":"7dbc4787-ec2f-498d-95f0-848c8c81e5da","zonename":"MTV-Zone1","type":"ROOT","deviceid":0,"virtualmachineid":"3239ade9-fd25-405c-8eda-59f0313a3fb0","vmname":"apb-cent32-bld","vmdisplayname":"apb-cent32-bld","vmstate":"Stopped","size":139264,"created":"2013-04-16T16:25:57-0700","state":"Ready","account":"andrew","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","domain":"ROOT","storagetype":"local","hypervisor":"KVM","storage":"c2422.halxg.cloudera.com","destroyed":false,"serviceofferingid":"7cc4f8c3-7c56-4155-9916-9f42072ea712","serviceofferingname":"Tiny","serviceofferingdisplaytext":"Tiny
 (1 core, 1GB RAM)","isextractable":false} ] } }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-createtemplate-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-createtemplate-imageextension.json
 
b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-createtemplate-imageextension.json
new file mode 100644
index 0000000..891f9fe
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-createtemplate-imageextension.json
@@ -0,0 +1,2 @@
+{ "queryasyncjobresultresponse" :
+{"accountid":"ee092437-d681-499e-b78b-f4e449128247","userid":"0c9608c8-188c-47df-a934-d66dc96b96e3","cmd":"com.cloud.api.commands.CreateTemplateCmd","jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"template":{"id":"3dc6ce25-a6cf-4d60-a664-3499993b511b","name":"temp-template-ignore","displaytext":"temp-template-ignore","ispublic":false,"created":"2013-06-21T12:37:01-0700","isready":true,"passwordenabled":false,"format":"QCOW2","isfeatured":false,"crossZones":false,"ostypeid":"be820210-c741-4c36-9ba1-f38363bd37d5","ostypename":"CentOS
 5.6 (32-bit)","account":"andrew","zoneid":"1","zonename":"San Jose 
1","status":"Download 
Complete","size":26843545600,"templatetype":"USER","hypervisor":"KVM","domain":"ROOT","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","isextractable":false,"checksum":"6dd5d7f073ff951b033a8cadb877bde2","sourcetemplateid":"9d597afb-4d3d-4a69-bf45-f95b0d1ee160"}},"created":"2013-06-21T12:31:33-0700","jobid":"4e345230-8fcc-48a3-
 8a37-c5fe960df671"} }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-stopvirtualmachine-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-stopvirtualmachine-imageextension.json
 
b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-stopvirtualmachine-imageextension.json
new file mode 100644
index 0000000..f11c9eb
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-stopvirtualmachine-imageextension.json
@@ -0,0 +1,2 @@
+{ "queryasyncjobresultresponse" :
+{"accountid":"ee092437-d681-499e-b78b-f4e449128247","userid":"0c9608c8-188c-47df-a934-d66dc96b96e3","cmd":"com.cloud.api.commands.StopVMCmd","jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"virtualmachine":{"id":"3239ade9-fd25-405c-8eda-59f0313a3fb0","name":"apb-cent32-bld","displayname":"apb-cent32-bld","account":"andrew","domainid":"41a4917b-7952-499d-ba7f-4c57464d3dc8","domain":"ROOT","created":"2013-04-16T16:25:57-0700","state":"Stopped","haenable":false,"zoneid":"1","zonename":"San
 Jose 
1","hostid":"0","templateid":"9d597afb-4d3d-4a69-bf45-f95b0d1ee160","templatename":"centos-6.2-x32-template","templatedisplaytext":"CentOS
 6.2 x32 
Template","passwordenabled":false,"serviceofferingid":"7cc4f8c3-7c56-4155-9916-9f42072ea712","serviceofferingname":"Tiny","cpunumber":1,"cpuspeed":1600,"memory":1024,"cpuused":"-65.18%","networkkbsread":319420,"networkkbswrite":3,"guestosid":"45de18f1-87c6-4646-8099-95c61f2a300a","rootdeviceid":0,"rootdevicetype
 
":"Filesystem","securitygroup":[{"id":"50288900-28eb-4d7f-9c33-e4c26e3b66d2","name":"default","description":"Default
 Security 
Group"}],"nic":[{"id":"fd65b6fd-2c3e-4c8a-a0bc-6a5db4e8007a","networkid":"0c82cd9f-ca64-4df4-9300-b33d81562403","netmask":"255.255.254.0","gateway":"10.20.92.3","ipaddress":"10.20.92.96","traffictype":"Guest","type":"Shared","isdefault":true,"macaddress":"06:ec:68:00:0f:04"}],"hypervisor":"KVM","instancename":"i-4-69942-VM"}},"created":"2013-06-21T12:49:59-0700","jobid":"a7d5127b-24a2-4a44-a4a7-25a6d057b453"}
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a906f9f4/apis/cloudstack/src/test/resources/stopvirtualmachineresponse-imageextension.json
----------------------------------------------------------------------
diff --git 
a/apis/cloudstack/src/test/resources/stopvirtualmachineresponse-imageextension.json
 
b/apis/cloudstack/src/test/resources/stopvirtualmachineresponse-imageextension.json
new file mode 100644
index 0000000..b526cd1
--- /dev/null
+++ 
b/apis/cloudstack/src/test/resources/stopvirtualmachineresponse-imageextension.json
@@ -0,0 +1 @@
+{ "stopvirtualmachineresponse" : 
{"jobid":"a7d5127b-24a2-4a44-a4a7-25a6d057b453"} }

Reply via email to