diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java
index 05f0ba6f00..1ad10297f2 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java
@@ -25,7 +25,6 @@ import static org.jclouds.io.Payloads.newPayload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.security.MessageDigest;
import java.util.Map;
import javax.inject.Inject;
@@ -128,12 +127,12 @@ public class BlobBuilderImpl implements BlobBuilder {
public class PayloadBlobBuilderImpl implements PayloadBlobBuilder {
private final BlobBuilder builder;
private final Payload payload;
- private MessageDigest digest;
+ private final Crypto crypto;
public PayloadBlobBuilderImpl(BlobBuilder builder, Payload payload, Crypto crypto) {
this.builder = checkNotNull(builder, "builder");
this.payload = checkNotNull(payload, "payload");
- this.digest = checkNotNull(crypto, "crypto").md5();
+ this.crypto = checkNotNull(crypto, "crypto");
}
@Override
@@ -158,7 +157,7 @@ public class BlobBuilderImpl implements BlobBuilder {
@Override
public PayloadBlobBuilder calculateMD5() throws IOException {
- return builder.payload(Payloads.calculateMD5(payload, digest));
+ return builder.payload(Payloads.calculateMD5(payload, crypto.md5()));
}
@Override
diff --git a/core/src/main/resources/rest.properties b/core/src/main/resources/rest.properties
index f260ec720c..cfba086eed 100644
--- a/core/src/main/resources/rest.properties
+++ b/core/src/main/resources/rest.properties
@@ -83,8 +83,8 @@ eucalyptus.propertiesbuilder=org.jclouds.eucalyptus.EucalyptusPropertiesBuilder
eucalyptus-partnercloud-ec2.contextbuilder=org.jclouds.epc.EucalyptusPartnerCloudContextBuilder
eucalyptus-partnercloud-ec2.propertiesbuilder=org.jclouds.epc.EucalyptusPartnerCloudPropertiesBuilder
-nova-ec2.contextbuilder=org.jclouds.ec2.EC2ContextBuilder
-nova-ec2.propertiesbuilder=org.jclouds.nova.ec2.NovaEC2PropertiesBuilder
+nova.contextbuilder=org.jclouds.openstack.nova.NovaContextBuilder
+nova.propertiesbuilder=org.jclouds.openstack.nova.NovaPropertiesBuilder
cloudservers-uk.contextbuilder=org.jclouds.cloudservers.CloudServersContextBuilder
cloudservers-uk.propertiesbuilder=org.jclouds.rackspace.cloudservers.CloudServersUKPropertiesBuilder
diff --git a/drivers/bouncycastle/pom.xml b/drivers/bouncycastle/pom.xml
index c3c4b5be11..509947753f 100644
--- a/drivers/bouncycastle/pom.xml
+++ b/drivers/bouncycastle/pom.xml
@@ -61,7 +61,7 @@
org.bouncycastle
bcprov-jdk16
- 1.45
+ 1.46
compile
diff --git a/sandbox-apis/nova-ec2/src/test/java/org/jclouds/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java b/sandbox-apis/nova-ec2/src/test/java/org/jclouds/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java
deleted file mode 100644
index 50a13a9120..0000000000
--- a/sandbox-apis/nova-ec2/src/test/java/org/jclouds/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- *
- * Copyright (C) 2010 Cloud Conscious, LLC.
- *
- * ====================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
-
-package org.jclouds.nova.ec2.compute;
-
-import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
-import static org.testng.Assert.assertEquals;
-
-import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest;
-import org.testng.annotations.Test;
-
-/**
- *
- * @author Adrian Cole
- */
-@Test(groups = "live", sequential = true, testName = "NovaEC2ComputeServiceLiveTest")
-public class NovaEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
-
- public NovaEC2ComputeServiceLiveTest() {
- provider = "nova-ec2";
- }
-
- @Override
- protected void assertDefaultWorks() {
- Template defaultTemplate = client.templateBuilder().build();
- assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate;
- assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04");
- assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
- assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
- assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "instance-store");
- assertEquals(defaultTemplate.getLocation().getId(), "nova");
- assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
- }
-
-}
diff --git a/sandbox-apis/nova-ec2/pom.xml b/sandbox-apis/nova/pom.xml
similarity index 72%
rename from sandbox-apis/nova-ec2/pom.xml
rename to sandbox-apis/nova/pom.xml
index eb521eac25..13e3dda25b 100644
--- a/sandbox-apis/nova-ec2/pom.xml
+++ b/sandbox-apis/nova/pom.xml
@@ -30,33 +30,38 @@
../../project/pom.xml
org.jclouds.api
- nova-ec2
- jclouds OpenStack Nova EC2 api
- EC2 implementation based on OpenStack Nova
+ nova
+ jcloud nova api
+ jclouds components to access an implementation of OpenStack Nova
- TODO
- 2010-06-15
- FIXME
- FIXME
+ https://auth.api.rackspacecloud.com
+ 1.1
+ FIXME
+ FIXME
- org.jclouds.api
- ec2
+ org.jclouds.common
+ openstack-common
${project.version}
- org.jclouds.api
- ec2
+ org.jclouds
+ jclouds-compute
+ ${project.version}
+
+
+ org.jclouds
+ jclouds-core
${project.version}
test-jar
test
- org.jclouds
- jclouds-core
+ org.jclouds.common
+ openstack-common
${project.version}
test-jar
test
@@ -111,20 +116,24 @@
- test.nova-ec2.endpoint
- ${test.nova-ec2.endpoint}
+ test.nova.endpoint
+ ${test.nova.endpoint}
- test.nova-ec2.apiversion
- ${test.nova-ec2.apiversion}
+ test.nova.apiversion
+ ${test.nova.apiversion}
- test.nova-ec2.identity
- ${test.nova-ec2.identity}
+ test.nova.identity
+ ${test.nova.identity}
- test.nova-ec2.credential
- ${test.nova-ec2.credential}
+ test.nova.credential
+ ${test.nova.credential}
+
+
+ test.initializer
+ ${test.initializer}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java
new file mode 100644
index 0000000000..833426d7ad
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java
@@ -0,0 +1,378 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.openstack.nova.binders.BindBackupScheduleToJsonPayload;
+import org.jclouds.openstack.nova.domain.Addresses;
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.openstack.nova.domain.Image;
+import org.jclouds.openstack.nova.domain.RebootType;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.SharedIpGroup;
+import org.jclouds.openstack.nova.options.CreateServerOptions;
+import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions;
+import org.jclouds.openstack.nova.options.ListOptions;
+import org.jclouds.openstack.nova.options.RebuildServerOptions;
+import org.jclouds.http.functions.ReturnFalseOn404;
+import org.jclouds.openstack.filters.AddTimestampQuery;
+import org.jclouds.openstack.filters.AuthenticateRequest;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Endpoint;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.Payload;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SkipEncoding;
+import org.jclouds.rest.annotations.Unwrap;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides asynchronous access to OpenStack Nova via their REST API.
+ *
+ * All commands return a ListenableFuture of the result from OpenStack Nova. Any exceptions incurred
+ * during processing will be wrapped in an {@link ExecutionException} as documented in
+ * {@link ListenableFuture#get()}.
+ *
+ * @see NovaClient
+ * @see
+ * @author Adrian Cole
+ */
+@SkipEncoding({ '/', '=' })
+@RequestFilters({ AuthenticateRequest.class, AddTimestampQuery.class })
+@Endpoint(ServerManagement.class)
+public interface NovaAsyncClient {
+
+ /**
+ * @see NovaClient#listServers
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listServers(ListOptions... options);
+
+ /**
+ * @see NovaClient#getServer
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @Path("/servers/{id}")
+ ListenableFuture getServer(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#deleteServer
+ */
+ @DELETE
+ @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
+ @Path("/servers/{id}")
+ ListenableFuture deleteServer(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#rebootServer
+ */
+ @POST
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/action")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"reboot\":%7B\"type\":\"{type}\"%7D%7D")
+ ListenableFuture rebootServer(@PathParam("id") int id, @PayloadParam("type") RebootType rebootType);
+
+ /**
+ * @see NovaClient#resizeServer
+ */
+ @POST
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/action")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"resize\":%7B\"flavorId\":{flavorId}%7D%7D")
+ ListenableFuture resizeServer(@PathParam("id") int id, @PayloadParam("flavorId") int flavorId);
+
+ /**
+ * @see NovaClient#confirmResizeServer
+ */
+ @POST
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/action")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("{\"confirmResize\":null}")
+ ListenableFuture confirmResizeServer(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#revertResizeServer
+ */
+ @POST
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/action")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("{\"revertResize\":null}")
+ ListenableFuture revertResizeServer(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#createServer
+ */
+ @POST
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers")
+ @MapBinder(CreateServerOptions.class)
+ ListenableFuture createServer(@PayloadParam("name") String name, @PayloadParam("imageId") int imageId,
+ @PayloadParam("flavorId") int flavorId, CreateServerOptions... options);
+
+ /**
+ * @see NovaClient#rebuildServer
+ */
+ @POST
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/action")
+ @MapBinder(RebuildServerOptions.class)
+ ListenableFuture rebuildServer(@PathParam("id") int id, RebuildServerOptions... options);
+
+ /**
+ * @see NovaClient#shareIp
+ */
+ @PUT
+ @Path("/servers/{id}/ips/public/{address}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"shareIp\":%7B\"sharedIpGroupId\":{sharedIpGroupId},\"configureServer\":{configureServer}%7D%7D")
+ ListenableFuture shareIp(@PathParam("address") String addressToShare,
+ @PathParam("id") int serverToTosignBindressTo, @PayloadParam("sharedIpGroupId") int sharedIpGroup,
+ @PayloadParam("configureServer") boolean configureServer);
+
+ /**
+ * @see NovaClient#unshareIp
+ */
+ @DELETE
+ @Path("/servers/{id}/ips/public/{address}")
+ @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
+ ListenableFuture unshareIp(@PathParam("address") String addressToShare,
+ @PathParam("id") int serverToTosignBindressTo);
+
+ /**
+ * @see NovaClient#changeAdminPass
+ */
+ @PUT
+ @Path("/servers/{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"server\":%7B\"adminPass\":\"{adminPass}\"%7D%7D")
+ ListenableFuture changeAdminPass(@PathParam("id") int id, @PayloadParam("adminPass") String adminPass);
+
+ /**
+ * @see NovaClient#renameServer
+ */
+ @PUT
+ @Path("/servers/{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"server\":%7B\"name\":\"{name}\"%7D%7D")
+ ListenableFuture renameServer(@PathParam("id") int id, @PayloadParam("name") String newName);
+
+ /**
+ * @see NovaClient#listFlavors
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/flavors")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listFlavors(ListOptions... options);
+
+ /**
+ * @see NovaClient#getFlavor
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/flavors/{id}")
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ ListenableFuture getFlavor(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#listImages
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/images")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listImages(ListOptions... options);
+
+ /**
+ * @see NovaClient#getImage
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/images/{id}")
+ ListenableFuture getImage(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#deleteImage
+ */
+ @DELETE
+ @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
+ @Path("/images/{id}")
+ ListenableFuture deleteImage(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#createImageFromServer
+ */
+ @POST
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/images")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Payload("%7B\"image\":%7B\"serverId\":{serverId},\"name\":\"{name}\"%7D%7D")
+ ListenableFuture createImageFromServer(@PayloadParam("name") String imageName,
+ @PayloadParam("serverId") int serverId);
+
+ /**
+ * @see NovaClient#listSharedIpGroups
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/shared_ip_groups")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listSharedIpGroups(ListOptions... options);
+
+ /**
+ * @see NovaClient#getSharedIpGroup
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/shared_ip_groups/{id}")
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ ListenableFuture getSharedIpGroup(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#createSharedIpGroup
+ */
+ @POST
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/shared_ip_groups")
+ @MapBinder(CreateSharedIpGroupOptions.class)
+ ListenableFuture createSharedIpGroup(@PayloadParam("name") String name,
+ CreateSharedIpGroupOptions... options);
+
+ /**
+ * @see NovaClient#deleteSharedIpGroup
+ */
+ @DELETE
+ @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
+ @Path("/shared_ip_groups/{id}")
+ ListenableFuture deleteSharedIpGroup(@PathParam("id") int id);
+
+ /**
+ * @see NovaClient#listBackupSchedule
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/backup_schedule")
+ ListenableFuture getBackupSchedule(@PathParam("id") int serverId);
+
+ /**
+ * @see NovaClient#deleteBackupSchedule
+ */
+ @DELETE
+ @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
+ @Path("/servers/{id}/backup_schedule")
+ ListenableFuture deleteBackupSchedule(@PathParam("id") int serverId);
+
+ /**
+ * @see NovaClient#replaceBackupSchedule
+ */
+ @POST
+ @ExceptionParser(ReturnFalseOn404.class)
+ @Path("/servers/{id}/backup_schedule")
+ ListenableFuture replaceBackupSchedule(@PathParam("id") int id,
+ @BinderParam(BindBackupScheduleToJsonPayload.class) BackupSchedule backupSchedule);
+
+ /**
+ * @see NovaClient#listAddresses
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/ips")
+ ListenableFuture getAddresses(@PathParam("id") int serverId);
+
+ /**
+ * @see NovaClient#listPublicAddresses
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/ips/public")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listPublicAddresses(@PathParam("id") int serverId);
+
+ /**
+ * @see NovaClient#listPrivateAddresses
+ */
+ @GET
+ @Unwrap
+ @Consumes(MediaType.APPLICATION_JSON)
+ @QueryParams(keys = "format", values = "json")
+ @Path("/servers/{id}/ips/private")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ ListenableFuture extends Set> listPrivateAddresses(@PathParam("id") int serverId);
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java
new file mode 100644
index 0000000000..d4543249b5
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaClient.java
@@ -0,0 +1,380 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.PathParam;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.openstack.nova.domain.Addresses;
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.openstack.nova.domain.Image;
+import org.jclouds.openstack.nova.domain.RebootType;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.SharedIpGroup;
+import org.jclouds.openstack.nova.options.CreateServerOptions;
+import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions;
+import org.jclouds.openstack.nova.options.ListOptions;
+import org.jclouds.openstack.nova.options.RebuildServerOptions;
+import org.jclouds.rest.ResourceNotFoundException;
+
+import java.util.concurrent.Future;
+
+/**
+ * Provides access to OpenStack Nova via their REST API.
+ *
+ * All commands return a Future of the result from OpenStack Nova. Any exceptions incurred
+ * during processing will be wrapped in an {@link ExecutionException} as documented in
+ * {@link Future#get()}.
+ *
+ * @see NovaAsyncClient
+ * @see
+ * @author Adrian Cole
+ */
+@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
+public interface NovaClient {
+
+ /**
+ *
+ * List all servers (IDs and names only)
+ *
+ * This operation provides a list of servers associated with your identity. Servers that have been
+ * deleted are not included in this list.
+ *
+ * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
+ * withDetails()}
+ */
+ Set listServers(ListOptions... options);
+
+ /**
+ *
+ * This operation returns details of the specified server.
+ *
+ * @return null, if the server is not found
+ * @see Server
+ */
+ Server getServer(@PathParam("id") int id);
+
+ /**
+ *
+ * This operation deletes a cloud server instance from the system.
+ *
+ * Note: When a server is deleted, all images created from that server are also removed.
+ *
+ * @return false if the server is not found
+ * @see Server
+ */
+ boolean deleteServer(@PathParam("id") int id);
+
+ /**
+ * The reboot function allows for either a soft or hard reboot of a server.
+ *
+ * Status Transition:
+ *
+ * ACTIVE - REBOOT - ACTIVE (soft reboot)
+ *
+ * ACTIVE - HARD_REBOOT - ACTIVE (hard reboot)
+ *
+ * @param rebootType
+ * With a soft reboot, the operating system is signaled to restart, which allows for a
+ * graceful shutdown of all processes. A hard reboot is the equivalent of power cycling
+ * the server.
+ */
+ void rebootServer(int id, RebootType rebootType);
+
+ /**
+ * The resize function converts an existing server to a different flavor, in essence, scaling the
+ * server up or down. The original server is saved for a period of time to allow rollback if
+ * there is a problem. All resizes should be tested and explicitly confirmed, at which time the
+ * original server is removed. All resizes are automatically confirmed after 24 hours if they are
+ * not confirmed or reverted.
+ *
+ * Status Transition:
+ *
+ * ACTIVE - QUEUE_RESIZE - PREP_RESIZE - VERIFY_RESIZE
+ *
+ * ACTIVE - QUEUE_RESIZE - ACTIVE (on error)
+ */
+ void resizeServer(int id, int flavorId);
+
+ /**
+ * The resize function converts an existing server to a different flavor, in essence, scaling the
+ * server up or down. The original server is saved for a period of time to allow rollback if
+ * there is a problem. All resizes should be tested and explicitly confirmed, at which time the
+ * original server is removed. All resizes are automatically confirmed after 24 hours if they are
+ * not confirmed or reverted.
+ *
+ * Status Transition:
+ *
+ * VERIFY_RESIZE - ACTIVE
+ */
+ void confirmResizeServer(int id);
+
+ /**
+ * The resize function converts an existing server to a different flavor, in essence, scaling the
+ * server up or down. The original server is saved for a period of time to allow rollback if
+ * there is a problem. All resizes should be tested and explicitly reverted, at which time the
+ * original server is removed. All resizes are automatically reverted after 24 hours if they are
+ * not reverted or reverted.
+ *
+ * Status Transition:
+ *
+ * VERIFY_RESIZE - ACTIVE
+ */
+ void revertResizeServer(int id);
+
+ /**
+ * This operation asynchronously provisions a new server. The progress of this operation depends
+ * on several factors including location of the requested image, network i/o, host load, and the
+ * selected flavor. The progress of the request can be checked by performing a GET on /server/id,
+ * which will return a progress attribute (0-100% completion). A password will be randomly
+ * generated for you and returned in the response object. For security reasons, it will not be
+ * returned in subsequent GET calls against a given server ID.
+ *
+ * @param options
+ * - used to specify extra files, metadata, or ip parameters during server creation.
+ */
+ Server createServer(String name, int imageId, int flavorId, CreateServerOptions... options);
+
+ /**
+ * The rebuild function removes all data on the server and replaces it with the specified image.
+ * Server ID and IP addresses remain the same.
+ *
+ * Status Transition:
+ *
+ * ACTIVE - REBUILD - ACTIVE
+ *
+ * ACTIVE - REBUILD - ERROR (on error)
+ *
+ *
+ * @param options
+ * - imageId is an optional argument. If it is not specified, the server is rebuilt
+ * with the original imageId.
+ */
+ void rebuildServer(int id, RebuildServerOptions... options);
+
+ /**
+ * /** This operation allows you share an IP address to the specified server
+ *
+ * This operation shares an IP from an existing server in the specified shared IP group to
+ * another specified server in the same group. The operation modifies cloud network restrictions
+ * to allow IP traffic for the given IP to/from the server specified.
+ *
+ *
+ * Status Transition: ACTIVE - SHARE_IP - ACTIVE (if configureServer is true) ACTIVE -
+ * SHARE_IP_NO_CONFIG - ACTIVE
+ *
+ * @param configureServer
+ *
+ * if set to true, the server is configured with the new address, though the address is
+ * not enabled. Note that configuring the server does require a reboot.
+ *
+ * If set to false, does not bind the IP to the server itself. A heartbeat facility
+ * (e.g. keepalived) can then be used within the servers to perform health checks and
+ * manage IP failover.
+ */
+ void shareIp(String addressToShare, int serverToTosignBindressTo, int sharedIpGroup,
+ boolean configureServer);
+
+ /**
+ * This operation removes a shared IP address from the specified server.
+ *
+ * Status Transition: ACTIVE - DELETE_IP - ACTIVE
+ *
+ * @param addressToShare
+ * @param serverToTosignBindressTo
+ * @return
+ */
+ void unshareIp(String addressToShare, int serverToTosignBindressTo);
+
+ /**
+ * This operation allows you to change the administrative password.
+ *
+ * Status Transition: ACTIVE - PASSWORD - ACTIVE
+ *
+ */
+ void changeAdminPass(int id, String adminPass);
+
+ /**
+ * This operation allows you to update the name of the server. This operation changes the name of
+ * the server in the OpenStack Nova system and does not change the server host name itself.
+ *
+ * Status Transition: ACTIVE - PASSWORD - ACTIVE
+ *
+ */
+ void renameServer(int id, String newName);
+
+ /**
+ *
+ * List available flavors (IDs and names only)
+ *
+ * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
+ * withDetails()}
+ */
+ Set listFlavors(ListOptions... options);
+
+ /**
+ *
+ * This operation returns details of the specified flavor.
+ *
+ * @return null, if the flavor is not found
+ * @see Flavor
+ */
+ Flavor getFlavor(int id);
+
+ /**
+ *
+ * List available images (IDs and names only)
+ *
+ * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
+ * withDetails()}
+ */
+ Set listImages(ListOptions... options);
+
+ /**
+ *
+ * This operation returns details of the specified image.
+ *
+ * @return null, if the image is not found
+ *
+ * @see Image
+ */
+ Image getImage(int id);
+
+ /**
+ *
+ * This operation deletes an image from the system.
+ *
+ * Note: Images are immediately removed. Currently, there are no state transitions to track the
+ * delete operation.
+ *
+ * @return false if the image is not found
+ * @see Image
+ */
+ boolean deleteImage(int id);
+
+ /**
+ *
+ * This operation creates a new image for the given server ID. Once complete, a new image will be
+ * available that can be used to rebuild or create servers. Specifying the same image name as an
+ * existing custom image replaces the image. The image creation status can be queried by
+ * performing a GET on /images/id and examining the status and progress attributes.
+ *
+ * Status Transition:
+ *
+ * QUEUED - PREPARING - SAVING - ACTIVE
+ *
+ * QUEUED - PREPARING - SAVING - FAILED (on error)
+ *
+ * Note: At present, image creation is an asynchronous operation, so coordinating the creation
+ * with data quiescence, etc. is currently not possible.
+ *
+ * @throws ResourceNotFoundException
+ * if the server is not found
+ * @see Image
+ */
+ Image createImageFromServer(String imageName, int serverId);
+
+ /**
+ *
+ * List shared IP groups (IDs and names only)
+ *
+ * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
+ * withDetails()}
+ */
+ Set listSharedIpGroups(ListOptions... options);
+
+ /**
+ *
+ * This operation returns details of the specified shared IP group.
+ *
+ * @return null, if the shared ip group is not found
+ *
+ * @see SharedIpGroup
+ */
+ SharedIpGroup getSharedIpGroup(int id);
+
+ /**
+ * This operation creates a new shared IP group. Please note, all responses to requests for
+ * shared_ip_groups return an array of servers. However, on a create request, the shared IP group
+ * can be created empty or can be initially populated with a single server. Use
+ * {@link CreateSharedIpGroupOptions} to specify an server.
+ */
+ SharedIpGroup createSharedIpGroup(String name, CreateSharedIpGroupOptions... options);
+
+ /**
+ * This operation deletes the specified shared IP group. This operation will ONLY succeed if 1)
+ * there are no active servers in the group (i.e. they have all been terminated) or 2) no servers
+ * in the group are actively sharing IPs.
+ *
+ * @return false if the shared ip group is not found
+ * @see SharedIpGroup
+ */
+ boolean deleteSharedIpGroup(int id);
+
+ /**
+ * List the backup schedule for the specified server
+ *
+ * @throws ResourceNotFoundException
+ * , if the server doesn't exist
+ */
+ BackupSchedule getBackupSchedule(int serverId);
+
+ /**
+ * Delete backup schedule for the specified server.
+ *
+ * Web Hosting #119571 currently disables the schedule, not deletes it.
+ *
+ * @return false if the schedule is not found
+ */
+ boolean deleteBackupSchedule(int serverId);
+
+ /**
+ * Enable/update the backup schedule for the specified server
+ *
+ */
+ void replaceBackupSchedule(int id, BackupSchedule backupSchedule);
+
+ /**
+ * List all server addresses
+ *
+ * returns empty set if the server doesn't exist
+ */
+ Addresses getAddresses(int serverId);
+
+ /**
+ * List all public server addresses
+ *
+ * returns empty set if the server doesn't exist
+ */
+ Set listPublicAddresses(int serverId);
+
+ /**
+ * List all private server addresses
+ *
+ * returns empty set if the server doesn't exist
+ */
+ Set listPrivateAddresses(int serverId);
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java
new file mode 100644
index 0000000000..1be627474b
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/NovaContextBuilder.java
@@ -0,0 +1,64 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.jclouds.compute.ComputeServiceContextBuilder;
+import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.jclouds.logging.jdk.config.JDKLoggingModule;
+import org.jclouds.openstack.nova.compute.config.NovaComputeServiceContextModule;
+import org.jclouds.openstack.nova.config.NovaRestClientModule;
+
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Creates {@link NovaComputeServiceContext} or {@link Injector} instances based on the most
+ * commonly requested arguments.
+ *
+ * Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
+ *
+ *
+ * If no Module
s are specified, the default {@link JDKLoggingModule logging} and
+ * {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed.
+ *
+ * @author Adrian Cole
+ * @see NovaComputeServiceContext
+ */
+public class NovaContextBuilder extends
+ ComputeServiceContextBuilder {
+
+ public NovaContextBuilder(Properties props) {
+ super(NovaClient.class, NovaAsyncClient.class, props);
+ }
+
+ @Override
+ protected void addContextModule(List modules) {
+ modules.add(new NovaComputeServiceContextModule());
+ }
+
+ @Override
+ protected void addClientModule(List modules) {
+ modules.add(new NovaRestClientModule());
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java
new file mode 100644
index 0000000000..d78bd10c31
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/ServerManagement.java
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Represents a component related to Rackspace OpenStack Nova.
+ *
+ * @see
+ * @author Adrian Cole
+ *
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+@Qualifier
+public @interface ServerManagement {
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java
new file mode 100644
index 0000000000..b4ed95d307
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/binders/BindBackupScheduleToJsonPayload.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+@Singleton
+public class BindBackupScheduleToJsonPayload extends BindToJsonPayload {
+
+ @Override
+ public R bindToRequest(R request, Map postParams) {
+ throw new IllegalStateException(
+ "Replace Backup Schedule needs an BackupSchedule object, not a Map");
+ }
+
+ @Override
+ public R bindToRequest(R request, Object toBind) {
+ checkArgument(toBind instanceof BackupSchedule,
+ "this binder is only valid for BackupSchedules!");
+ return super.bindToRequest(request, ImmutableMap.of("backupSchedule", toBind));
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java
new file mode 100644
index 0000000000..81ec46b2fc
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeStrategiesByClass.java
@@ -0,0 +1,77 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.config;
+
+import org.jclouds.compute.config.BindComputeStrategiesByClass;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.openstack.nova.compute.strategy.NovaCreateNodeWithGroupEncodedIntoName;
+import org.jclouds.openstack.nova.compute.strategy.NovaDestroyNodeStrategy;
+import org.jclouds.openstack.nova.compute.strategy.NovaGetNodeMetadataStrategy;
+import org.jclouds.openstack.nova.compute.strategy.NovaListNodesStrategy;
+import org.jclouds.openstack.nova.compute.strategy.NovaLifeCycleStrategy;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+public class NovaBindComputeStrategiesByClass extends BindComputeStrategiesByClass {
+
+ @Override
+ protected Class extends CreateNodeWithGroupEncodedIntoName> defineAddNodeWithTagStrategy() {
+ return NovaCreateNodeWithGroupEncodedIntoName.class;
+ }
+
+ @Override
+ protected Class extends DestroyNodeStrategy> defineDestroyNodeStrategy() {
+ return NovaDestroyNodeStrategy.class;
+ }
+
+ @Override
+ protected Class extends GetNodeMetadataStrategy> defineGetNodeMetadataStrategy() {
+ return NovaGetNodeMetadataStrategy.class;
+ }
+
+ @Override
+ protected Class extends ListNodesStrategy> defineListNodesStrategy() {
+ return NovaListNodesStrategy.class;
+ }
+
+ @Override
+ protected Class extends RebootNodeStrategy> defineRebootNodeStrategy() {
+ return NovaLifeCycleStrategy.class;
+ }
+
+ @Override
+ protected Class extends ResumeNodeStrategy> defineStartNodeStrategy() {
+ return NovaLifeCycleStrategy.class;
+ }
+
+ @Override
+ protected Class extends SuspendNodeStrategy> defineStopNodeStrategy() {
+ return NovaLifeCycleStrategy.class;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java
new file mode 100644
index 0000000000..57873f9316
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaBindComputeSuppliersByClass.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.config;
+
+import java.util.Set;
+
+import org.jclouds.openstack.nova.compute.suppliers.NovaHardwareSupplier;
+import org.jclouds.openstack.nova.compute.suppliers.NovaImageSupplier;
+import org.jclouds.compute.config.BindComputeSuppliersByClass;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.domain.Location;
+import org.jclouds.location.suppliers.JustProvider;
+
+import com.google.common.base.Supplier;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+public class NovaBindComputeSuppliersByClass extends BindComputeSuppliersByClass {
+ @Override
+ protected Class extends Supplier>> defineHardwareSupplier() {
+ return NovaHardwareSupplier.class;
+ }
+
+ @Override
+ protected Class extends Supplier>> defineImageSupplier() {
+ return NovaImageSupplier.class;
+ }
+
+ @Override
+ protected Class extends Supplier>> defineLocationSupplier() {
+ return JustProvider.class;
+ }
+}
diff --git a/sandbox-apis/nova-ec2/src/main/java/org/jclouds/nova/ec2/NovaEC2PropertiesBuilder.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java
similarity index 53%
rename from sandbox-apis/nova-ec2/src/main/java/org/jclouds/nova/ec2/NovaEC2PropertiesBuilder.java
rename to sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java
index 09563a1177..910a412c8c 100644
--- a/sandbox-apis/nova-ec2/src/main/java/org/jclouds/nova/ec2/NovaEC2PropertiesBuilder.java
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModule.java
@@ -17,31 +17,25 @@
* ====================================================================
*/
-package org.jclouds.nova.ec2;
+package org.jclouds.openstack.nova.compute.config;
-import static org.jclouds.Constants.PROPERTY_ENDPOINT;
-import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
-
-import java.util.Properties;
-
-import org.jclouds.ec2.EC2PropertiesBuilder;
+import org.jclouds.compute.config.BaseComputeServiceContextModule;
+import org.jclouds.compute.internal.BaseComputeService;
/**
- * Builds properties used in NovaEC2 Clients
+ * Configures the {@link NovaComputeServiceContext}; requires {@link BaseComputeService}
+ * bound.
*
* @author Adrian Cole
*/
-public class NovaEC2PropertiesBuilder extends EC2PropertiesBuilder {
- @Override
- protected Properties defaultProperties() {
- Properties properties = super.defaultProperties();
- properties.setProperty(PROPERTY_ENDPOINT, "YOU_MUST_SET_" + PROPERTY_ENDPOINT);
- properties.setProperty(PROPERTY_EC2_AMI_OWNERS, "*");
- return properties;
- }
+public class NovaComputeServiceContextModule extends BaseComputeServiceContextModule {
- public NovaEC2PropertiesBuilder(Properties properties) {
- super(properties);
+ @Override
+ protected void configure() {
+ install(new NovaComputeServiceDependenciesModule());
+ install(new NovaBindComputeStrategiesByClass());
+ install(new NovaBindComputeSuppliersByClass());
+ super.configure();
}
}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java
new file mode 100644
index 0000000000..c750ebbff5
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceDependenciesModule.java
@@ -0,0 +1,117 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.config;
+
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.openstack.nova.NovaAsyncClient;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.compute.functions.NovaImageToImage;
+import org.jclouds.openstack.nova.compute.functions.NovaImageToOperatingSystem;
+import org.jclouds.openstack.nova.compute.functions.FlavorToHardware;
+import org.jclouds.openstack.nova.compute.functions.ServerToNodeMetadata;
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.ComputeServiceContextImpl;
+import org.jclouds.rest.RestContext;
+import org.jclouds.rest.internal.RestContextImpl;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Configures the {@link NovaComputeServiceContext}; requires {@link BaseComputeService}
+ * bound.
+ *
+ * @author Adrian Cole
+ */
+public class NovaComputeServiceDependenciesModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(new TypeLiteral>() {
+ }).to(ServerToNodeMetadata.class);
+
+ bind(new TypeLiteral>() {
+ }).to(NovaImageToImage.class);
+
+ bind(new TypeLiteral>() {
+ }).to(NovaImageToOperatingSystem.class);
+
+ bind(new TypeLiteral>() {
+ }).to(FlavorToHardware.class);
+
+ bind(new TypeLiteral() {
+ }).to(new TypeLiteral>() {
+ }).in(Scopes.SINGLETON);
+ bind(new TypeLiteral>() {
+ }).to(new TypeLiteral>() {
+ }).in(Scopes.SINGLETON);
+ }
+
+ @VisibleForTesting
+ public static final Map serverToNodeState = ImmutableMap
+ . builder().put(ServerStatus.ACTIVE, NodeState.RUNNING)//
+ .put(ServerStatus.SUSPENDED, NodeState.SUSPENDED)//
+ .put(ServerStatus.DELETED, NodeState.TERMINATED)//
+ .put(ServerStatus.QUEUE_RESIZE, NodeState.PENDING)//
+ .put(ServerStatus.PREP_RESIZE, NodeState.PENDING)//
+ .put(ServerStatus.RESIZE, NodeState.PENDING)//
+ .put(ServerStatus.VERIFY_RESIZE, NodeState.PENDING)//
+ .put(ServerStatus.QUEUE_MOVE, NodeState.PENDING)//
+ .put(ServerStatus.PREP_MOVE, NodeState.PENDING)//
+ .put(ServerStatus.MOVE, NodeState.PENDING)//
+ .put(ServerStatus.VERIFY_MOVE, NodeState.PENDING)//
+ .put(ServerStatus.RESCUE, NodeState.PENDING)//
+ .put(ServerStatus.ERROR, NodeState.ERROR)//
+ .put(ServerStatus.BUILD, NodeState.PENDING)//
+ .put(ServerStatus.RESTORING, NodeState.PENDING)//
+ .put(ServerStatus.PASSWORD, NodeState.PENDING)//
+ .put(ServerStatus.REBUILD, NodeState.PENDING)//
+ .put(ServerStatus.DELETE_IP, NodeState.PENDING)//
+ .put(ServerStatus.SHARE_IP_NO_CONFIG, NodeState.PENDING)//
+ .put(ServerStatus.SHARE_IP, NodeState.PENDING)//
+ .put(ServerStatus.REBOOT, NodeState.PENDING)//
+ .put(ServerStatus.HARD_REBOOT, NodeState.PENDING)//
+ .put(ServerStatus.UNKNOWN, NodeState.UNRECOGNIZED)//
+ .put(ServerStatus.UNRECOGNIZED, NodeState.UNRECOGNIZED).build();
+
+ @Singleton
+ @Provides
+ Map provideServerToNodeState() {
+ return serverToNodeState;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java
new file mode 100644
index 0000000000..7e7b340899
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardware.java
@@ -0,0 +1,44 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.internal.VolumeImpl;
+import org.jclouds.openstack.nova.domain.Flavor;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class FlavorToHardware implements Function {
+ public Hardware apply(Flavor from) {
+ return new HardwareBuilder().ids(from.getId() + "").name(from.getName())
+ .processors(ImmutableList.of(new Processor(from.getDisk() / 10.0, 1.0))).ram(from.getRam())
+ .volumes(ImmutableList. of(new VolumeImpl((float) from.getDisk(), true, true))).build();
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java
new file mode 100644
index 0000000000..1cfdf0ac4c
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImage.java
@@ -0,0 +1,56 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.domain.Credentials;
+
+import com.google.common.base.Function;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaImageToImage implements Function {
+ private final Function imageToOs;
+
+ @Inject
+ NovaImageToImage(Function imageToOs) {
+ this.imageToOs = imageToOs;
+ }
+
+ public Image apply(org.jclouds.openstack.nova.domain.Image from) {
+ ImageBuilder builder = new ImageBuilder();
+ builder.ids(from.getId() + "");
+ builder.name(from.getName());
+ builder.description(from.getName());
+ builder.version(from.getUpdated().getTime() + "");
+ builder.operatingSystem(imageToOs.apply(from));
+ builder.defaultCredentials(new Credentials("root", null));
+ Image image = builder.build();
+ return image;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToOperatingSystem.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToOperatingSystem.java
new file mode 100644
index 0000000000..82d2530182
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/NovaImageToOperatingSystem.java
@@ -0,0 +1,92 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaImageToOperatingSystem implements
+ Function {
+ public static final Pattern DEFAULT_PATTERN = Pattern.compile("(([^ ]*) ([0-9.]+) ?.*)");
+ // Windows Server 2008 R2 x64
+ public static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows (.*) (x[86][64])");
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ private final Map> osVersionMap;
+
+ @Inject
+ public NovaImageToOperatingSystem(Map> osVersionMap) {
+ this.osVersionMap = osVersionMap;
+ }
+
+ public OperatingSystem apply(final org.jclouds.openstack.nova.domain.Image from) {
+ OsFamily osFamily = null;
+ String osName = null;
+ String osArch = null;
+ String osVersion = null;
+ String osDescription = from.getName();
+ boolean is64Bit = true;
+ if (from.getName().indexOf("Red Hat EL") != -1) {
+ osFamily = OsFamily.RHEL;
+ } else if (from.getName().indexOf("Oracle EL") != -1) {
+ osFamily = OsFamily.OEL;
+ } else if (from.getName().indexOf("Windows") != -1) {
+ osFamily = OsFamily.WINDOWS;
+ Matcher matcher = WINDOWS_PATTERN.matcher(from.getName());
+ if (matcher.find()) {
+ osVersion = ComputeServiceUtils.parseVersionOrReturnEmptyString(osFamily, matcher.group(1), osVersionMap);
+ is64Bit = matcher.group(2).equals("x64");
+ }
+ } else {
+ Matcher matcher = DEFAULT_PATTERN.matcher(from.getName());
+ if (matcher.find()) {
+ try {
+ osFamily = OsFamily.fromValue(matcher.group(2).toLowerCase());
+ } catch (IllegalArgumentException e) {
+ logger.debug("<< didn't match os(%s)", matcher.group(2));
+ }
+ osVersion = ComputeServiceUtils.parseVersionOrReturnEmptyString(osFamily, matcher.group(3), osVersionMap);
+ }
+ }
+ return new OperatingSystem(osFamily, osName, osVersion, osArch, osDescription, is64Bit);
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadata.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadata.java
new file mode 100644
index 0000000000..30dcfcef1d
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadata.java
@@ -0,0 +1,143 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName;
+
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class ServerToNodeMetadata implements Function {
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ protected final Supplier location;
+ protected final Map credentialStore;
+ protected final Map serverToNodeState;
+ protected final Supplier> images;
+ protected final Supplier> hardwares;
+
+ private static class FindImageForServer implements Predicate {
+ private final Server instance;
+
+ private FindImageForServer(Server instance) {
+ this.instance = instance;
+ }
+
+ @Override
+ public boolean apply(Image input) {
+ return input.getProviderId().equals(instance.getImageId() + "");
+ }
+ }
+
+ private static class FindHardwareForServer implements Predicate {
+ private final Server instance;
+
+ private FindHardwareForServer(Server instance) {
+ this.instance = instance;
+ }
+
+ @Override
+ public boolean apply(Hardware input) {
+ return input.getProviderId().equals(instance.getFlavorId() + "");
+ }
+ }
+
+ @Inject
+ ServerToNodeMetadata(Map serverStateToNodeState, Map credentialStore,
+ @Memoized Supplier> images, Supplier location,
+ @Memoized Supplier> hardwares) {
+ this.serverToNodeState = checkNotNull(serverStateToNodeState, "serverStateToNodeState");
+ this.credentialStore = checkNotNull(credentialStore, "credentialStore");
+ this.images = checkNotNull(images, "images");
+ this.location = checkNotNull(location, "location");
+ this.hardwares = checkNotNull(hardwares, "hardwares");
+ }
+
+ @Override
+ public NodeMetadata apply(Server from) {
+ NodeMetadataBuilder builder = new NodeMetadataBuilder();
+ builder.ids(from.getId() + "");
+ builder.name(from.getName());
+ builder.location(new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId()).description(
+ from.getHostId()).parent(location.get()).build());
+ builder.userMetadata(from.getMetadata());
+ builder.group(parseGroupFromName(from.getName()));
+ builder.imageId(from.getImageId() + "");
+ builder.operatingSystem(parseOperatingSystem(from));
+ builder.hardware(parseHardware(from));
+ builder.state(serverToNodeState.get(from.getStatus()));
+ builder.publicAddresses(from.getAddresses().getPublicAddresses());
+ builder.privateAddresses(from.getAddresses().getPrivateAddresses());
+ builder.credentials(credentialStore.get("node#" + from.getId()));
+ return builder.build();
+ }
+
+ protected Hardware parseHardware(Server from) {
+ try {
+ return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
+ } catch (NoSuchElementException e) {
+ logger.warn("could not find a matching hardware for server %s", from);
+ }
+ return null;
+ }
+
+ protected OperatingSystem parseOperatingSystem(Server from) {
+ try {
+ return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
+ } catch (NoSuchElementException e) {
+ logger.warn("could not find a matching image for server %s in location %s", from, location);
+ }
+ return null;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java
new file mode 100644
index 0000000000..72ce4ed5b2
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaCreateNodeWithGroupEncodedIntoName.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import org.jclouds.domain.Credentials;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Server;
+
+import com.google.common.base.Function;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaCreateNodeWithGroupEncodedIntoName implements CreateNodeWithGroupEncodedIntoName {
+ protected final NovaClient client;
+ protected final Map credentialStore;
+ protected final Function serverToNodeMetadata;
+
+ @Inject
+ protected NovaCreateNodeWithGroupEncodedIntoName(NovaClient client, Map credentialStore,
+ Function serverToNodeMetadata) {
+ this.client = checkNotNull(client, "client");
+ this.credentialStore = checkNotNull(credentialStore, "credentialStore");
+ this.serverToNodeMetadata = checkNotNull(serverToNodeMetadata, "serverToNodeMetadata");
+ }
+
+ @Override
+ public NodeMetadata createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
+ Server from = client.createServer(name, Integer.parseInt(template.getImage().getProviderId()), Integer
+ .parseInt(template.getHardware().getProviderId()));
+ credentialStore.put("node#" + from.getId(), new Credentials("root", from.getAdminPass()));
+ return serverToNodeMetadata.apply(from);
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java
new file mode 100644
index 0000000000..456f72eb52
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaDestroyNodeStrategy.java
@@ -0,0 +1,52 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.strategy;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.openstack.nova.NovaClient;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaDestroyNodeStrategy implements DestroyNodeStrategy {
+ private final NovaClient client;
+ private final GetNodeMetadataStrategy getNode;
+
+ @Inject
+ protected NovaDestroyNodeStrategy(NovaClient client, GetNodeMetadataStrategy getNode) {
+ this.client = client;
+ this.getNode = getNode;
+ }
+
+ @Override
+ public NodeMetadata destroyNode(String id) {
+ int serverId = Integer.parseInt(id);
+ // if false server wasn't around in the first place
+ client.deleteServer(serverId);
+ return getNode.getNode(id);
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java
new file mode 100644
index 0000000000..c551938696
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaGetNodeMetadataStrategy.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.strategy;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Server;
+
+import com.google.common.base.Function;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaGetNodeMetadataStrategy implements GetNodeMetadataStrategy {
+
+ private final NovaClient client;
+ private final Function serverToNodeMetadata;
+
+ @Inject
+ protected NovaGetNodeMetadataStrategy(NovaClient client,
+ Function serverToNodeMetadata) {
+ this.client = client;
+ this.serverToNodeMetadata = serverToNodeMetadata;
+ }
+
+ @Override
+ public NodeMetadata getNode(String id) {
+ int serverId = Integer.parseInt(id);
+ Server server = client.getServer(serverId);
+ return server == null ? null : serverToNodeMetadata.apply(server);
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java
new file mode 100644
index 0000000000..ccbcba6e37
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaLifeCycleStrategy.java
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.strategy;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.RebootType;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaLifeCycleStrategy implements RebootNodeStrategy, SuspendNodeStrategy, ResumeNodeStrategy {
+ private final NovaClient client;
+ private final GetNodeMetadataStrategy getNode;
+
+ @Inject
+ protected NovaLifeCycleStrategy(NovaClient client, GetNodeMetadataStrategy getNode) {
+ this.client = client;
+ this.getNode = getNode;
+ }
+
+ @Override
+ public NodeMetadata rebootNode(String id) {
+ int serverId = Integer.parseInt(id);
+ // if false server wasn't around in the first place
+ client.rebootServer(serverId, RebootType.HARD);
+ return getNode.getNode(id);
+ }
+
+ @Override
+ public NodeMetadata suspendNode(String id) {
+ throw new UnsupportedOperationException("suspend not supported");
+ }
+
+ @Override
+ public NodeMetadata resumeNode(String id) {
+ throw new UnsupportedOperationException("resume not supported");
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java
new file mode 100644
index 0000000000..220147c3d0
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/strategy/NovaListNodesStrategy.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.strategy;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.options.ListOptions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaListNodesStrategy implements ListNodesStrategy {
+ private final NovaClient client;
+ private final Function serverToNodeMetadata;
+
+ @Inject
+ protected NovaListNodesStrategy(NovaClient client,
+ Function serverToNodeMetadata) {
+ this.client = client;
+ this.serverToNodeMetadata = serverToNodeMetadata;
+ }
+
+ @Override
+ public Iterable extends ComputeMetadata> listNodes() {
+ return listDetailsOnNodesMatching(NodePredicates.all());
+ }
+
+ @Override
+ public Iterable extends NodeMetadata> listDetailsOnNodesMatching(Predicate filter) {
+ return Iterables.filter(Iterables.transform(client.listServers(ListOptions.Builder.withDetails()),
+ serverToNodeMetadata), filter);
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java
new file mode 100644
index 0000000000..0058669e7f
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaHardwareSupplier.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.suppliers;
+
+import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails;
+
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Flavor;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaHardwareSupplier implements Supplier> {
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+ private final NovaClient sync;
+ private final Function flavorToHardware;
+
+ @Inject
+ NovaHardwareSupplier(NovaClient sync, Function flavorToHardware) {
+ this.sync = sync;
+ this.flavorToHardware = flavorToHardware;
+ }
+
+ @Override
+ public Set extends Hardware> get() {
+ final Set hardware;
+ logger.debug(">> providing hardware");
+ hardware = Sets.newLinkedHashSet(Iterables.transform(sync.listFlavors(withDetails()), flavorToHardware));
+ logger.debug("<< hardware(%d)", hardware.size());
+ return hardware;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java
new file mode 100644
index 0000000000..9ad9af1c44
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/compute/suppliers/NovaImageSupplier.java
@@ -0,0 +1,71 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.suppliers;
+
+import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails;
+
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.nova.NovaClient;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaImageSupplier implements Supplier> {
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ protected final NovaClient sync;
+ protected final Function cloudServersImageToImage;
+
+ @Inject
+ NovaImageSupplier(NovaClient sync,
+ Function cloudServersImageToImage) {
+ this.sync = sync;
+ this.cloudServersImageToImage = cloudServersImageToImage;
+ }
+
+ @Override
+ public Set extends Image> get() {
+ Set images;
+ logger.debug(">> providing images");
+ images = Sets. newLinkedHashSet(Iterables.transform(sync.listImages(withDetails()),
+ cloudServersImageToImage));
+ logger.debug("<< images(%d)", images.size());
+ return images;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java
new file mode 100644
index 0000000000..c2180aa290
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java
@@ -0,0 +1,85 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.config;
+
+import java.net.URI;
+
+import javax.inject.Singleton;
+
+import org.jclouds.openstack.nova.NovaAsyncClient;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.ServerManagement;
+import org.jclouds.openstack.nova.handlers.ParseNovaErrorFromHttpResponse;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
+import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.reference.AuthHeaders;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.config.RestClientModule;
+
+import com.google.inject.Provides;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@ConfiguresRestClient
+@RequiresHttp
+public class NovaRestClientModule extends RestClientModule {
+
+ private final OpenStackAuthenticationModule module;
+
+ public NovaRestClientModule(OpenStackAuthenticationModule module) {
+ super(NovaClient.class, NovaAsyncClient.class);
+ this.module = module;
+ }
+
+ public NovaRestClientModule() {
+ this(new OpenStackAuthenticationModule());
+ }
+
+ @Override
+ protected void configure() {
+ install(module);
+ bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+ super.configure();
+ }
+
+ @Override
+ protected void bindErrorHandlers() {
+ bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseNovaErrorFromHttpResponse.class);
+ bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseNovaErrorFromHttpResponse.class);
+ bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseNovaErrorFromHttpResponse.class);
+ }
+
+ @Provides
+ @Singleton
+ @ServerManagement
+ protected URI provideServerUrl(AuthenticationResponse response) {
+ return response.getServices().get(AuthHeaders.SERVER_MANAGEMENT_URL);
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java
new file mode 100644
index 0000000000..c6f6cbcba1
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/AbsoluteLimit.java
@@ -0,0 +1,48 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+
+/**
+ *
+ * @author Adrian Cole
+ */
+public class AbsoluteLimit {
+
+ protected String name;
+ protected int value;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java
new file mode 100644
index 0000000000..cc35bb68fe
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Action.java
@@ -0,0 +1,28 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+public enum Action {
+ CONFIRM_RESIZE, REBOOT, REBUILD, RESIZE, REVERT_RESIZE
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java
new file mode 100644
index 0000000000..bf80d79b8a
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Addresses.java
@@ -0,0 +1,99 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+public class Addresses {
+
+ @SerializedName("public")
+ private Set publicAddresses = Sets.newLinkedHashSet();
+ @SerializedName("private")
+ private Set privateAddresses = Sets.newLinkedHashSet();
+
+ public Addresses() {
+ }
+
+ public Addresses(Set publicAddresses, Set privateAddresses) {
+ this.publicAddresses = publicAddresses;
+ this.privateAddresses = privateAddresses;
+ }
+
+ public void setPublicAddresses(Set publicAddresses) {
+ this.publicAddresses = publicAddresses;
+ }
+
+ public Set getPublicAddresses() {
+ return publicAddresses;
+ }
+
+ public void setPrivateAddresses(Set privateAddresses) {
+ this.privateAddresses = privateAddresses;
+ }
+
+ public Set getPrivateAddresses() {
+ return privateAddresses;
+ }
+
+ @Override
+ public String toString() {
+ return "Addresses [privateAddresses=" + privateAddresses + ", publicAddresses="
+ + publicAddresses + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((privateAddresses == null) ? 0 : privateAddresses.hashCode());
+ result = prime * result + ((publicAddresses == null) ? 0 : publicAddresses.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Addresses other = (Addresses) obj;
+ if (privateAddresses == null) {
+ if (other.privateAddresses != null)
+ return false;
+ } else if (!privateAddresses.equals(other.privateAddresses))
+ return false;
+ if (publicAddresses == null) {
+ if (other.publicAddresses != null)
+ return false;
+ } else if (!publicAddresses.equals(other.publicAddresses))
+ return false;
+ return true;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/BackupSchedule.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/BackupSchedule.java
new file mode 100644
index 0000000000..af9d2a5471
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/BackupSchedule.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ * A backup schedule can be defined to create server images at regular intervals (daily and weekly).
+ * Backup schedules are configurable per server.
+ *
+ * @author Adrian Cole
+ */
+public class BackupSchedule {
+ protected DailyBackup daily = DailyBackup.DISABLED;
+ protected boolean enabled;
+ protected WeeklyBackup weekly = WeeklyBackup.DISABLED;
+
+ public BackupSchedule() {
+ }
+
+ public BackupSchedule(WeeklyBackup weekly, DailyBackup daily, boolean enabled) {
+ this.weekly = weekly;
+ this.daily = daily;
+ this.enabled = enabled;
+ }
+
+ public DailyBackup getDaily() {
+ return daily;
+ }
+
+ public void setDaily(DailyBackup value) {
+ this.daily = value;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean value) {
+ this.enabled = value;
+ }
+
+ public WeeklyBackup getWeekly() {
+ return weekly;
+ }
+
+ public void setWeekly(WeeklyBackup value) {
+ this.weekly = value;
+ }
+
+ @Override
+ public String toString() {
+ return "[daily=" + daily + ", enabled=" + enabled + ", weekly=" + weekly + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((daily == null) ? 0 : daily.hashCode());
+ result = prime * result + (enabled ? 1231 : 1237);
+ result = prime * result + ((weekly == null) ? 0 : weekly.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BackupSchedule other = (BackupSchedule) obj;
+ if (daily == null) {
+ if (other.daily != null)
+ return false;
+ } else if (!daily.equals(other.daily))
+ return false;
+ if (enabled != other.enabled)
+ return false;
+ if (weekly == null) {
+ if (other.weekly != null)
+ return false;
+ } else if (!weekly.equals(other.weekly))
+ return false;
+ return true;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/DailyBackup.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/DailyBackup.java
new file mode 100644
index 0000000000..6841aca96c
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/DailyBackup.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public enum DailyBackup {
+
+ DISABLED, H_0000_0200, H_0200_0400, H_0400_0600, H_0600_0800, H_0800_1000, H_1000_1200, H_1200_1400, H_1400_1600, H_1600_1800, H_1800_2000, H_2000_2200, H_2200_0000, UNRECOGNIZED;
+
+ public String value() {
+ return name();
+ }
+
+ public static DailyBackup fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java
new file mode 100644
index 0000000000..9fe432f94c
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Flavor.java
@@ -0,0 +1,121 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ *
+ * A flavor is an available hardware configuration for a server. Each flavor has a unique
+ * combination of disk space and memory capacity.
+ *
+ * @author Adrian Cole
+ */
+public class Flavor {
+
+ public Flavor() {
+ }
+
+ @Override
+ public String toString() {
+ return "Flavor [disk=" + disk + ", id=" + id + ", name=" + name + ", ram=" + ram + "]";
+ }
+
+ public Flavor(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ private int id;
+ private String name;
+ private Integer disk;
+ private Integer ram;
+
+ public Integer getDisk() {
+ return disk;
+ }
+
+ public void setDisk(Integer value) {
+ this.disk = value;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int value) {
+ this.id = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ public Integer getRam() {
+ return ram;
+ }
+
+ public void setRam(Integer value) {
+ this.ram = value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((disk == null) ? 0 : disk.hashCode());
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((ram == null) ? 0 : ram.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Flavor other = (Flavor) obj;
+ if (disk == null) {
+ if (other.disk != null)
+ return false;
+ } else if (!disk.equals(other.disk))
+ return false;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (ram == null) {
+ if (other.ram != null)
+ return false;
+ } else if (!ram.equals(other.ram))
+ return false;
+ return true;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java
new file mode 100644
index 0000000000..adec2fa857
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Image.java
@@ -0,0 +1,146 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import java.util.Date;
+
+/**
+ * An image is a collection of files used to create or rebuild a server. Rackspace provides a number
+ * of pre-built OS images by default. You may also create custom images from cloud servers you have
+ * launched. These custom images are useful for backup purposes or for producing gold server images
+ * if you plan to deploy a particular server configuration frequently.
+ *
+ * @author Adrian Cole
+ */
+public class Image {
+
+ private Date created;
+ private int id;
+ private String name;
+ private Integer progress;
+ private Integer serverId;
+ private ImageStatus status;
+ private Date updated;
+
+ public Image() {
+ }
+
+ public Image(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setProgress(Integer progress) {
+ this.progress = progress;
+ }
+
+ public Integer getProgress() {
+ return progress;
+ }
+
+ public void setServerId(Integer serverId) {
+ this.serverId = serverId;
+ }
+
+ public Integer getServerId() {
+ return serverId;
+ }
+
+ public void setStatus(ImageStatus status) {
+ this.status = status;
+ }
+
+ public ImageStatus getStatus() {
+ return status;
+ }
+
+ public void setUpdated(Date updated) {
+ this.updated = updated;
+ }
+
+ public Date getUpdated() {
+ return updated;
+ }
+ /**
+ * note that this ignores the create time
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((serverId == null) ? 0 : serverId.hashCode());
+ return result;
+ }
+
+ /**
+ * note that this ignores the serverid and create time.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Image other = (Image) obj;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Image [created=" + created + ", id=" + id + ", name=" + name + ", serverId="
+ + serverId + "]";
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java
new file mode 100644
index 0000000000..2ee482f263
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ImageStatus.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ * In-flight images will have the status attribute set to SAVING and the conditional progress
+ * element (0- 100% completion) will also be returned. Other possible values for the status
+ * attribute include: UNKNOWN, PREPARING, ACTIVE QUEUED, FAILED. Images with an ACTIVE status are
+ * available for install.
+ *
+ * @author Adrian Cole
+ */
+public enum ImageStatus {
+
+ UNRECOGNIZED, UNKNOWN, ACTIVE, SAVING, PREPARING, QUEUED, FAILED;
+
+ public String value() {
+ return name();
+ }
+
+ public static ImageStatus fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java
new file mode 100644
index 0000000000..c56eb7ea62
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Limits.java
@@ -0,0 +1,47 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+public class Limits {
+
+ private List rate = Lists.newArrayList();
+ private List absolute = Lists.newArrayList();
+
+ public void setRate(List rate) {
+ this.rate = rate;
+ }
+
+ public List getRate() {
+ return rate;
+ }
+
+ public void setAbsolute(List absolute) {
+ this.absolute = absolute;
+ }
+
+ public List getAbsolute() {
+ return absolute;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java
new file mode 100644
index 0000000000..bb936e4d57
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimit.java
@@ -0,0 +1,90 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import javax.ws.rs.HttpMethod;
+
+/**
+ *
+ * RateLimit.
+ *
+ * we specify rate limits in terms of both a human readable wild-card URI and a machine processable
+ * regular expression. The regular expression boundary matcher '^' takes affect after the root URI
+ * path. For example, the regular expression ^/servers would match the bolded portion of the
+ * following URI: https://servers.api.rackspacecloud.com/v1.0/3542812 /servers .
+ *
+ * Rate limits are applied in order relative to the verb, going from least to most specific. For
+ * example, although the threshold for POST to /servers is 25 per day, one cannot POST to /servers
+ * more than 10 times within a single minute because the rate limits for any POST is 10/min. In the
+ * event you exceed the thresholds established for your identity, a 413 Rate Control HTTP response
+ * will be returned with a Reply-After header to notify the client when theyagain.
+ *
+ * @author Adrian Cole
+ */
+public class RateLimit {
+
+ private final String uri;
+ private final String regex;
+ private final int remaining;
+ private final long resetTime;
+ private final RateLimitUnit unit;
+ private final int value;
+ private final HttpMethod verb;
+
+ public RateLimit(String uri, String regex, int remaining, long resetTime, RateLimitUnit unit,
+ int value, HttpMethod verb) {
+ this.uri = uri;
+ this.regex = regex;
+ this.remaining = remaining;
+ this.resetTime = resetTime;
+ this.unit = unit;
+ this.value = value;
+ this.verb = verb;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
+
+ public int getRemaining() {
+ return remaining;
+ }
+
+ public long getResetTime() {
+ return resetTime;
+ }
+
+ public RateLimitUnit getUnit() {
+ return unit;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public HttpMethod getVerb() {
+ return verb;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java
new file mode 100644
index 0000000000..4cb1fde521
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RateLimitUnit.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public enum RateLimitUnit {
+
+ MINUTE, HOUR, DAY, UNRECOGNIZED;
+
+ public String value() {
+ return name();
+ }
+
+ public static RateLimitUnit fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java
new file mode 100644
index 0000000000..6fc7c2d88d
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/RebootType.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+public enum RebootType {
+
+ HARD, SOFT;
+
+ public String value() {
+ return name();
+ }
+
+ public static RebootType fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java
new file mode 100644
index 0000000000..a86e45a4fa
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Server.java
@@ -0,0 +1,230 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+/**
+ * A server is a virtual machine instance in the OpenStack Nova system. Flavor and image are
+ * requisite elements when creating a server.
+ *
+ * @author Adrian Cole
+ */
+public class Server {
+ private int id;
+ private String name;
+
+ private Map metadata = Maps.newHashMap();
+
+ private Addresses addresses;
+ private String adminPass;
+ private Integer flavorId;
+ private String hostId;
+ private Integer imageId;
+ private Integer sharedIpGroupId;
+
+ private Integer progress;
+ private ServerStatus status;
+
+ public Server() {
+ }
+
+ public Server(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+ }
+
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ public void setAddresses(Addresses addresses) {
+ this.addresses = addresses;
+ }
+
+ public Addresses getAddresses() {
+ return addresses;
+ }
+
+ public void setAdminPass(String adminPass) {
+ this.adminPass = adminPass;
+ }
+
+ public String getAdminPass() {
+ return adminPass;
+ }
+
+ public void setFlavorId(Integer flavorId) {
+ this.flavorId = flavorId;
+ }
+
+ public Integer getFlavorId() {
+ return flavorId;
+ }
+
+ public void setHostId(String hostId) {
+ this.hostId = hostId;
+ }
+
+ /**
+ * The OpenStack Nova provisioning algorithm has an anti-affinity property that attempts to spread
+ * out customer VMs across hosts. Under certain situations, VMs from the same customer may be
+ * placed on the same host. hostId represents the host your cloud server runs on and can be used
+ * to determine this scenario if it's relevant to your application.
+ *
+ * Note: hostId is unique PER ACCOUNT and is not globally unique.
+ */
+ public String getHostId() {
+ return hostId;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setImageId(Integer imageId) {
+ this.imageId = imageId;
+ }
+
+ public Integer getImageId() {
+ return imageId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setProgress(Integer progress) {
+ this.progress = progress;
+ }
+
+ public Integer getProgress() {
+ return progress;
+ }
+
+ public void setSharedIpGroupId(Integer sharedIpGroupId) {
+ this.sharedIpGroupId = sharedIpGroupId;
+ }
+
+ public Integer getSharedIpGroupId() {
+ return sharedIpGroupId;
+ }
+
+ public void setStatus(ServerStatus status) {
+ this.status = status;
+ }
+
+ /**
+ * Servers contain a status attribute that can be used as an indication of the current server
+ * state. Servers with an ACTIVE status are available for use.
+ */
+ public ServerStatus getStatus() {
+ return status;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
+ result = prime * result + ((adminPass == null) ? 0 : adminPass.hashCode());
+ result = prime * result + ((flavorId == null) ? 0 : flavorId.hashCode());
+ result = prime * result + ((hostId == null) ? 0 : hostId.hashCode());
+ result = prime * result + id;
+ result = prime * result + ((imageId == null) ? 0 : imageId.hashCode());
+ result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((sharedIpGroupId == null) ? 0 : sharedIpGroupId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Server other = (Server) obj;
+ if (addresses == null) {
+ if (other.addresses != null)
+ return false;
+ } else if (!addresses.equals(other.addresses))
+ return false;
+ if (adminPass == null) {
+ if (other.adminPass != null)
+ return false;
+ } else if (!adminPass.equals(other.adminPass))
+ return false;
+ if (flavorId == null) {
+ if (other.flavorId != null)
+ return false;
+ } else if (!flavorId.equals(other.flavorId))
+ return false;
+ if (hostId == null) {
+ if (other.hostId != null)
+ return false;
+ } else if (!hostId.equals(other.hostId))
+ return false;
+ if (id != other.id)
+ return false;
+ if (imageId == null) {
+ if (other.imageId != null)
+ return false;
+ } else if (!imageId.equals(other.imageId))
+ return false;
+ if (metadata == null) {
+ if (other.metadata != null)
+ return false;
+ } else if (!metadata.equals(other.metadata))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (sharedIpGroupId == null) {
+ if (other.sharedIpGroupId != null)
+ return false;
+ } else if (!sharedIpGroupId.equals(other.sharedIpGroupId))
+ return false;
+ return true;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorId="
+ + flavorId + ", hostId=" + hostId + ", id=" + id + ", imageId=" + imageId
+ + ", metadata=" + metadata + ", name=" + name + ", sharedIpGroupId="
+ + sharedIpGroupId + "]";
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java
new file mode 100644
index 0000000000..b7bae0170f
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ServerStatus.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+/**
+ *
+ * Servers contain a status attribute that can be used as an indication of the current server state.
+ * Servers with an ACTIVE status are available for use.
+ *
+ * Note
+ * When the system changes a server's status from BUILD to ACTIVE the system will not be immediately
+ * available. The 'ACTIVE' label is really misleading in the fact that it just means the system
+ * doesn't have any activity going on related to it's configuration.
+ *
+ * Processes such as ssh will not be available until 5-10 seconds following the phase ACTIVE
+ *
+ * - [Web Hosting #119335]
+ *
+ *
+ * @author Adrian Cole
+ */
+public enum ServerStatus {
+
+ ACTIVE, SUSPENDED, DELETED, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, QUEUE_MOVE, PREP_MOVE, MOVE, VERIFY_MOVE, RESCUE, ERROR, BUILD, RESTORING, PASSWORD, REBUILD, DELETE_IP, SHARE_IP_NO_CONFIG, SHARE_IP, REBOOT, HARD_REBOOT, UNKNOWN, UNRECOGNIZED;
+
+ public String value() {
+ return name();
+ }
+
+ public static ServerStatus fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java
new file mode 100644
index 0000000000..856d6539e6
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/ShareIp.java
@@ -0,0 +1,43 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public class ShareIp {
+
+ private boolean configureServer;
+ private int sharedIpGroupId;
+
+ public void setConfigureServer(boolean configureServer) {
+ this.configureServer = configureServer;
+ }
+
+ public boolean isConfigureServer() {
+ return configureServer;
+ }
+
+ public void setSharedIpGroupId(int sharedIpGroupId) {
+ this.sharedIpGroupId = sharedIpGroupId;
+ }
+
+ public int getSharedIpGroupId() {
+ return sharedIpGroupId;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/SharedIpGroup.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/SharedIpGroup.java
new file mode 100644
index 0000000000..5b3c9b280c
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/SharedIpGroup.java
@@ -0,0 +1,112 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+/**
+ * A shared IP group is a collection of servers that can share IPs with other members of the group.
+ * Any server in a group can share one or more public IPs with any other server in the group. With
+ * the exception of the first server in a shared IP group, servers must be launched into shared IP
+ * groups. A server may only be a member of one shared IP group.
+ *
+ * @author Adrian Cole
+ */
+public class SharedIpGroup {
+
+ private int id;
+ private String name;
+
+ private List servers = Lists.newArrayList();
+
+ public SharedIpGroup() {
+ }
+
+ public SharedIpGroup(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setServers(List servers) {
+ this.servers = servers;
+ }
+
+ public List getServers() {
+ return servers;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((servers == null) ? 0 : servers.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SharedIpGroup other = (SharedIpGroup) obj;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (servers == null) {
+ if (other.servers != null)
+ return false;
+ } else if (!servers.equals(other.servers))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SharedIpGroup [id=" + id + ", name=" + name + ", servers=" + servers + "]";
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Version.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Version.java
new file mode 100644
index 0000000000..d085078688
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/Version.java
@@ -0,0 +1,60 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public class Version {
+
+ private String docURL;
+ private String id = "v1.0";
+ private VersionStatus status;
+ private String wadl;
+
+ public void setDocURL(String docURL) {
+ this.docURL = docURL;
+ }
+
+ public String getDocURL() {
+ return docURL;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setStatus(VersionStatus status) {
+ this.status = status;
+ }
+
+ public VersionStatus getStatus() {
+ return status;
+ }
+
+ public void setWadl(String wadl) {
+ this.wadl = wadl;
+ }
+
+ public String getWadl() {
+ return wadl;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java
new file mode 100644
index 0000000000..3eecc8afc3
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/VersionStatus.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public enum VersionStatus {
+
+ BETA, CURRENT, DEPRECATED, UNRECOGNIZED;
+
+ public String value() {
+ return name();
+ }
+
+ public static VersionStatus fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/WeeklyBackup.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/WeeklyBackup.java
new file mode 100644
index 0000000000..7209c6c5c7
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/domain/WeeklyBackup.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+public enum WeeklyBackup {
+
+ DISABLED, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, UNRECOGNIZED;
+
+ public String value() {
+ return name();
+ }
+
+ public static WeeklyBackup fromValue(String v) {
+ try {
+ return valueOf(v);
+ } catch (IllegalArgumentException e) {
+ return UNRECOGNIZED;
+ }
+ }
+
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java
new file mode 100644
index 0000000000..cc08488b50
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/handlers/ParseNovaErrorFromHttpResponse.java
@@ -0,0 +1,96 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.handlers;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.jclouds.util.Strings2;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ *
+ * @author Adrian Cole
+ *
+ */
+public class ParseNovaErrorFromHttpResponse implements HttpErrorHandler {
+ @Resource
+ protected Logger logger = Logger.NULL;
+ public static final Pattern RESOURCE_PATTERN = Pattern
+ .compile("^/v1[^/]*/[0-9]+/([^/]+)/([0-9]+)");
+
+ public void handleError(HttpCommand command, HttpResponse response) {
+ Exception exception = new HttpResponseException(command, response);
+ try {
+ String content = parseErrorFromContentOrNull(command, response);
+ exception = content != null ? new HttpResponseException(command, response, content) : exception;
+ switch (response.getStatusCode()) {
+ case 401:
+ exception = new AuthorizationException(exception.getMessage(), exception);
+ break;
+ case 404:
+ if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+ String path = command.getCurrentRequest().getEndpoint().getPath();
+ Matcher matcher = RESOURCE_PATTERN.matcher(path);
+ String message;
+ if (matcher.find()) {
+ message = String.format("%s %s not found", matcher.group(1), matcher.group(2));
+ } else {
+ message = path;
+ }
+ exception = new ResourceNotFoundException(message);
+ }
+ break;
+ case 409:
+ exception = new IllegalStateException(content);
+ break;
+ default:
+ exception = new HttpResponseException(command, response, content);
+ break;
+ }
+ } finally {
+ releasePayload(response);
+ command.setException(exception);
+ }
+ }
+
+ String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) {
+ if (response.getPayload() != null) {
+ try {
+ return Strings2.toStringAndClose(response.getPayload().getInput());
+ } catch (IOException e) {
+ logger.warn(e, "exception reading error from response", response);
+ }
+ }
+ return null;
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java
new file mode 100644
index 0000000000..4a59f6a43b
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java
@@ -0,0 +1,241 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.options;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.jclouds.encryption.internal.Base64;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.openstack.nova.domain.Addresses;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+public class CreateServerOptions extends BindToJsonPayload {
+
+ static class File {
+ private final String path;
+ private final String contents;
+
+ public File(String path, byte[] contents) {
+ this.path = checkNotNull(path, "path");
+ this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
+ checkArgument(path.getBytes().length < 255, String.format(
+ "maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path
+ .getBytes().length));
+ checkArgument(contents.length < 10 * 1024, String.format(
+ "maximum size of the file is 10KB. Contents specified is %d bytes",
+ contents.length));
+ }
+
+ public String getContents() {
+ return contents;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ }
+
+ @SuppressWarnings("unused")
+ private class ServerRequest {
+ final String name;
+ final int imageId;
+ final int flavorId;
+ Map metadata;
+ List personality;
+ Integer sharedIpGroupId;
+ Addresses addresses;
+
+ private ServerRequest(String name, int imageId, int flavorId) {
+ this.name = name;
+ this.imageId = imageId;
+ this.flavorId = flavorId;
+ }
+
+ }
+
+ private Map metadata = Maps.newHashMap();
+ private List files = Lists.newArrayList();
+ private Integer sharedIpGroupId;
+ private String publicIp;
+
+ @Override
+ public R bindToRequest(R request, Map postParams) {
+ ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"),
+ "name parameter not present"), Integer.parseInt(checkNotNull(postParams
+ .get("imageId"), "imageId parameter not present")), Integer.parseInt(checkNotNull(
+ postParams.get("flavorId"), "flavorId parameter not present")));
+ if (metadata.size() > 0)
+ server.metadata = metadata;
+ if (files.size() > 0)
+ server.personality = files;
+ if (sharedIpGroupId != null)
+ server.sharedIpGroupId = this.sharedIpGroupId;
+ if (publicIp != null) {
+ server.addresses = new Addresses();
+ server.addresses.getPublicAddresses().add(publicIp);
+ server.addresses.setPrivateAddresses(null);
+ }
+ return bindToRequest(request, ImmutableMap.of("server", server));
+ }
+
+ /**
+ * You may further customize a cloud server by injecting data into the file system of the cloud
+ * server itself. This is useful, for example, for inserting ssh keys, setting configuration
+ * files, or storing data that you want to retrieve from within the instance itself. It is
+ * intended to provide a minimal amount of launch-time personalization. If significant
+ * customization is required, a custom image should be created. The max size of the file path
+ * data is 255 bytes while the max size of the file contents is 10KB. Note that the file contents
+ * should be encoded as a Base64 string and the 10KB limit refers to the number of bytes in the
+ * decoded data not the number of characters in the encoded data. The maximum number of file
+ * path/content pairs that can be supplied is 5. Any existing files that match the specified file
+ * will be renamed to include the extension bak followed by a time stamp. For example, the file
+ * /etc/passwd will be backed up as /etc/passwd.bak.1246036261.5785. All files will have root and
+ * the root group as owner and group owner, respectively and will allow user and group read
+ * access only (-r--r-----).
+ */
+ public CreateServerOptions withFile(String path, byte[] contents) {
+ checkState(files.size() < 5, "maximum number of files allowed is 5");
+ files.add(new File(path, contents));
+ return this;
+ }
+
+ /**
+ * A shared IP group is a collection of servers that can share IPs with other members of the
+ * group. Any server in a group can share one or more public IPs with any other server in the
+ * group. With the exception of the first server in a shared IP group, servers must be launched
+ * into shared IP groups. A server may only be a member of one shared IP group.
+ *
+ *
+ * Servers in the same shared IP group can share public IPs for various high availability and
+ * load balancing configurations. To launch an HA server, include the optional sharedIpGroupId
+ * element and the server will be launched into that shared IP group.
+ *
+ *
+ * Note: sharedIpGroupId is an optional parameter and for optimal performance, should ONLY be
+ * specified when intending to share IPs between servers.
+ *
+ * @see #withSharedIp(String)
+ */
+ public CreateServerOptions withSharedIpGroup(int id) {
+ checkArgument(id > 0, "id must be positive or zero. was: " + id);
+ this.sharedIpGroupId = id;
+ return this;
+ }
+
+ /**
+ * Custom cloud server metadata can also be supplied at launch time. This metadata is stored in
+ * the API system where it is retrievable by querying the API for server status. The maximum size
+ * of the metadata key and value is each 255 bytes and the maximum number of key-value pairs that
+ * can be supplied per server is 5.
+ */
+ public CreateServerOptions withMetadata(Map metadata) {
+ checkNotNull(metadata, "metadata");
+ checkArgument(metadata.size() <= 5,
+ "you cannot have more then 5 metadata values. You specified: " + metadata.size());
+ for (Entry entry : metadata.entrySet()) {
+ checkArgument(entry.getKey().getBytes().length < 255, String.format(
+ "maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
+ entry.getKey(), entry.getKey().getBytes().length));
+ checkArgument(
+ entry.getKey().getBytes().length < 255,
+ String
+ .format(
+ "maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
+ entry.getKey(), entry.getValue(),
+ entry.getValue().getBytes().length));
+ }
+ this.metadata = metadata;
+ return this;
+ }
+
+ /**
+ * Public IP addresses can be shared across multiple servers for use in various high availability
+ * scenarios. When an IP address is shared to another server, the cloud network restrictions are
+ * modified to allow each server to listen to and respond on that IP address (you may optionally
+ * specify that the target server network configuration be modified). Shared IP addresses can be
+ * used with many standard heartbeat facilities (e.g. keepalived) that monitor for failure and
+ * manage IP failover.
+ *
+ *
+ * If you intend to use a shared IP on the server being created and have no need for a separate
+ * public IP address, you may launch the server into a shared IP group and specify an IP address
+ * from that shared IP group to be used as its public IP. You can accomplish this by specifying
+ * the public shared IP address in your request. This is optional and is only valid if
+ * sharedIpGroupId is also supplied.
+ */
+ public CreateServerOptions withSharedIp(String publicIp) {
+ checkState(sharedIpGroupId != null,
+ "sharedIp is invalid unless a shared ip group is specified.");
+ this.publicIp = checkNotNull(publicIp, "ip");
+ return this;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see CreateServerOptions#withFile(String,byte [])
+ */
+ public static CreateServerOptions withFile(String path, byte[] contents) {
+ CreateServerOptions options = new CreateServerOptions();
+ return options.withFile(path, contents);
+ }
+
+ /**
+ * @see CreateServerOptions#withSharedIpGroup(int)
+ */
+ public static CreateServerOptions withSharedIpGroup(int id) {
+ CreateServerOptions options = new CreateServerOptions();
+ return options.withSharedIpGroup(id);
+ }
+
+ /**
+ * @see CreateServerOptions#withMetadata(Map)
+ */
+ public static CreateServerOptions withMetadata(Map metadata) {
+ CreateServerOptions options = new CreateServerOptions();
+ return options.withMetadata(metadata);
+ }
+
+ /**
+ * @see CreateServerOptions#withSharedIp(String)
+ */
+ public static CreateServerOptions withSharedIp(String publicIp) {
+ CreateServerOptions options = new CreateServerOptions();
+ return options.withSharedIp(publicIp);
+ }
+
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptions.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptions.java
new file mode 100644
index 0000000000..f56d018f6e
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateSharedIpGroupOptions.java
@@ -0,0 +1,88 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.options;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ *
+ *
+ * @author Adrian Cole
+ *
+ */
+public class CreateSharedIpGroupOptions extends BindToJsonPayload {
+ Integer serverId;
+
+ @SuppressWarnings("unused")
+ private static class SharedIpGroupRequest {
+ final String name;
+ Integer server;
+
+ private SharedIpGroupRequest(String name, @Nullable Integer serverId) {
+ this.name = name;
+ this.server = serverId;
+ }
+
+ }
+
+ @Override
+ public R bindToRequest(R request, Map postParams) {
+ SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams
+ .get("name")), serverId);
+ return super.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest));
+ }
+
+ @Override
+ public R bindToRequest(R request, Object toBind) {
+ throw new IllegalStateException("CreateSharedIpGroup is a POST operation");
+ }
+
+ /**
+ *
+ * @param id
+ * of the server to include with this request.
+ */
+ public CreateSharedIpGroupOptions withServer(int id) {
+ checkArgument(id > 0, "server id must be a positive number");
+ this.serverId = id;
+ return this;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see CreateSharedIpGroupOptions#withServer(int)
+ */
+ public static CreateSharedIpGroupOptions withServer(int id) {
+ CreateSharedIpGroupOptions options = new CreateSharedIpGroupOptions();
+ return options.withServer(id);
+ }
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java
new file mode 100644
index 0000000000..93345dcef4
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/ListOptions.java
@@ -0,0 +1,110 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.options;
+
+import java.util.Date;
+
+import org.jclouds.openstack.options.BaseListOptions;
+
+/**
+ * Options used to control the amount of detail in the request.
+ *
+ * @see BaseListOptions
+ * @see
+ * @author Adrian Cole
+ */
+public class ListOptions extends BaseListOptions {
+
+ public static final ListOptions NONE = new ListOptions();
+
+ /**
+ * unless used, only the name and id will be returned per row.
+ *
+ * @return
+ */
+ public ListOptions withDetails() {
+ this.pathSuffix = "/detail";
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ListOptions changesSince(Date ifModifiedSince) {
+ super.changesSince(ifModifiedSince);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ListOptions maxResults(int limit) {
+ super.maxResults(limit);
+ return this;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ListOptions startAt(long offset) {
+ super.startAt(offset);
+ return this;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see ListOptions#withDetails()
+ */
+ public static ListOptions withDetails() {
+ ListOptions options = new ListOptions();
+ return options.withDetails();
+ }
+
+ /**
+ * @see BaseListOptions#startAt(long)
+ */
+ public static ListOptions startAt(long prefix) {
+ ListOptions options = new ListOptions();
+ return options.startAt(prefix);
+ }
+
+ /**
+ * @see BaseListOptions#maxResults(long)
+ */
+ public static ListOptions maxResults(int maxKeys) {
+ ListOptions options = new ListOptions();
+ return options.maxResults(maxKeys);
+ }
+
+ /**
+ * @see BaseListOptions#changesSince(Date)
+ */
+ public static ListOptions changesSince(Date since) {
+ ListOptions options = new ListOptions();
+ return options.changesSince(since);
+ }
+
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java
new file mode 100644
index 0000000000..8e2c28c6a8
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java
@@ -0,0 +1,75 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.options;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Map;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+/**
+ *
+ *
+ * @author Adrian Cole
+ *
+ */
+public class RebuildServerOptions extends BindToJsonPayload {
+ Integer imageId;
+
+ @Override
+ public R bindToRequest(R request, Map postParams) {
+ Map image = Maps.newHashMap();
+ if (imageId != null)
+ image.put("imageId", imageId);
+ return super.bindToRequest(request, ImmutableMap.of("rebuild", image));
+ }
+
+ @Override
+ public R bindToRequest(R request, Object toBind) {
+ throw new IllegalStateException("RebuildServer is a POST operation");
+ }
+
+ /**
+ *
+ * @param id
+ * of the image to rebuild the server with.
+ */
+ public RebuildServerOptions withImage(int id) {
+ checkArgument(id > 0, "server id must be a positive number");
+ this.imageId = id;
+ return this;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see RebuildServerOptions#withImage(int)
+ */
+ public static RebuildServerOptions withImage(int id) {
+ RebuildServerOptions options = new RebuildServerOptions();
+ return options.withImage(id);
+ }
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java
new file mode 100644
index 0000000000..2206e3375f
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerActive.java
@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+
+/**
+ *
+ * Tests to see if a task succeeds.
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class ServerActive implements Predicate {
+
+ private final NovaClient client;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ public ServerActive(NovaClient client) {
+ this.client = client;
+ }
+
+ public boolean apply(Server server) {
+ logger.trace("looking for state on server %s", checkNotNull(server, "server"));
+ server = refresh(server);
+ if (server == null)
+ return false;
+ logger.trace("%s: looking for server state %s: currently: %s", server.getId(),
+ ServerStatus.ACTIVE, server.getStatus());
+ return server.getStatus() == ServerStatus.ACTIVE;
+ }
+
+ private Server refresh(Server server) {
+ return client.getServer(server.getId());
+ }
+}
diff --git a/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java
new file mode 100644
index 0000000000..31e78d2dbe
--- /dev/null
+++ b/sandbox-apis/nova/src/main/java/org/jclouds/openstack/nova/predicates/ServerDeleted.java
@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+
+/**
+ *
+ * Tests to see if a task succeeds.
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class ServerDeleted implements Predicate {
+
+ private final NovaClient client;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ public ServerDeleted(NovaClient client) {
+ this.client = client;
+ }
+
+ public boolean apply(Server server) {
+ logger.trace("looking for state on server %s", checkNotNull(server, "server"));
+ server = refresh(server);
+ if (server == null)
+ return true;
+ logger.trace("%s: looking for server state %s: currently: %s", server.getId(),
+ ServerStatus.DELETED, server.getStatus());
+ return server.getStatus() == ServerStatus.DELETED;
+ }
+
+ private Server refresh(Server server) {
+ return client.getServer(server.getId());
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java
new file mode 100644
index 0000000000..357c4bef6a
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java
@@ -0,0 +1,905 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile;
+import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withMetadata;
+import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withSharedIpGroup;
+import static org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions.Builder.withServer;
+import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince;
+import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails;
+import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.Date;
+import java.util.Properties;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.openstack.nova.config.NovaRestClientModule;
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.openstack.nova.domain.DailyBackup;
+import org.jclouds.openstack.nova.domain.RebootType;
+import org.jclouds.openstack.nova.domain.WeeklyBackup;
+import org.jclouds.openstack.nova.options.CreateServerOptions;
+import org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions;
+import org.jclouds.openstack.nova.options.ListOptions;
+import org.jclouds.openstack.nova.options.RebuildServerOptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.http.functions.ReleasePayloadAndReturn;
+import org.jclouds.http.functions.ReturnFalseOn404;
+import org.jclouds.http.functions.ReturnTrueIf2xx;
+import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
+import org.jclouds.openstack.TestOpenStackAuthenticationModule;
+import org.jclouds.openstack.filters.AddTimestampQuery;
+import org.jclouds.openstack.filters.AuthenticateRequest;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.RestClientTest;
+import org.jclouds.rest.RestContextFactory;
+import org.jclouds.rest.RestContextSpec;
+import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
+import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code NovaAsyncClient}
+ *
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "NovaAsyncClientTest")
+public class NovaAsyncClientTest extends RestClientTest {
+ private static final Class extends ListOptions[]> listOptionsVarargsClass = new ListOptions[] {}.getClass();
+ private static final Class extends CreateServerOptions[]> createServerOptionsVarargsClass = new CreateServerOptions[] {}
+ .getClass();
+
+ public void testCreateServer() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class,
+ createServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2, 1);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1}}",
+ "application/json", false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testCreateServerWithIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class,
+ createServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2, 1, withSharedIpGroup(2));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request,
+ "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"sharedIpGroupId\":2}}",
+ "application/json", false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testCreateServerWithFile() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class,
+ createServerOptionsVarargsClass);
+ HttpRequest request = processor
+ .createRequest(method, "ralphie", 2, 1, withFile("/etc/jclouds", "foo".getBytes()));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(
+ request,
+ "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"personality\":[{\"path\":\"/etc/jclouds\",\"contents\":\"Zm9v\"}]}}",
+ "application/json", false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testCreateServerWithMetadata() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class,
+ createServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2, 1,
+ withMetadata(ImmutableMap.of("foo", "bar")));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request,
+ "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"metadata\":{\"foo\":\"bar\"}}}",
+ "application/json", false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testCreateServerWithIpGroupAndSharedIp() throws IOException, SecurityException, NoSuchMethodException,
+ UnknownHostException {
+ Method method = NovaAsyncClient.class.getMethod("createServer", String.class, int.class, int.class,
+ createServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2, 1,
+ withSharedIpGroup(2).withSharedIp("127.0.0.1"));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(
+ request,
+ "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"sharedIpGroupId\":2,\"addresses\":{\"public\":[\"127.0.0.1\"]}}}",
+ "application/json", false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testDeleteImage() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("deleteImage", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE http://serverManagementUrl/images/2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListServers() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ Date now = new Date(10000000l);
+
+ public void testListServersOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/servers?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListServersDetail() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails());
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/detail?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetServer() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getServer", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListFlavors() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListFlavorsOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/flavors?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListFlavorsDetail() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails());
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors/detail?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListFlavorsDetailOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/flavors/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetFlavor() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getFlavor", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/flavors/2?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListImages() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/images?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListImagesDetail() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails());
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/images/detail?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListImagesOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/images?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListImagesDetailOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/images/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetImage() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getImage", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/images/2?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testDeleteServer() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("deleteServer", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testShareIpNoConfig() throws IOException, SecurityException, NoSuchMethodException, UnknownHostException {
+ Method method = NovaAsyncClient.class.getMethod("shareIp", String.class, int.class, int.class,
+ boolean.class);
+ HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, false);
+
+ assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"shareIp\":{\"sharedIpGroupId\":3,\"configureServer\":false}}",
+ MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testShareIpConfig() throws IOException, SecurityException, NoSuchMethodException, UnknownHostException {
+ Method method = NovaAsyncClient.class.getMethod("shareIp", String.class, int.class, int.class,
+ boolean.class);
+ HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, true);
+
+ assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"shareIp\":{\"sharedIpGroupId\":3,\"configureServer\":true}}",
+ MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testUnshareIpNoConfig() throws IOException, SecurityException, NoSuchMethodException,
+ UnknownHostException {
+ Method method = NovaAsyncClient.class.getMethod("unshareIp", String.class, int.class);
+ HttpRequest request = processor.createRequest(method, "127.0.0.1", 2, 3, false);
+
+ assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2/ips/public/127.0.0.1 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class);
+
+ checkFilters(request);
+
+ }
+
+ public void testReplaceBackupSchedule() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("replaceBackupSchedule", int.class, BackupSchedule.class);
+ HttpRequest request = processor.createRequest(method, 2, new BackupSchedule(WeeklyBackup.MONDAY,
+ DailyBackup.H_0800_1000, true));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/backup_schedule HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request,
+ "{\"backupSchedule\":{\"daily\":\"H_0800_1000\",\"enabled\":true,\"weekly\":\"MONDAY\"}}",
+ MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnFalseOn404.class);
+
+ checkFilters(request);
+
+ }
+
+ public void testDeleteBackupSchedule() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("deleteBackupSchedule", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE http://serverManagementUrl/servers/2/backup_schedule HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
+
+ checkFilters(request);
+
+ }
+
+ public void testChangeAdminPass() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("changeAdminPass", int.class, String.class);
+ HttpRequest request = processor.createRequest(method, 2, "foo");
+
+ assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"server\":{\"adminPass\":\"foo\"}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testChangeServerName() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("renameServer", int.class, String.class);
+ HttpRequest request = processor.createRequest(method, 2, "foo");
+
+ assertRequestLineEquals(request, "PUT http://serverManagementUrl/servers/2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"server\":{\"name\":\"foo\"}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testListSharedIpGroups() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListSharedIpGroupsOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/shared_ip_groups?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListSharedIpGroupsDetail() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails());
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups/detail?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListSharedIpGroupsDetailOptions() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listSharedIpGroups", listOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2));
+
+ assertRequestLineEquals(request,
+ "GET http://serverManagementUrl/shared_ip_groups/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getSharedIpGroup", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/shared_ip_groups/2?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ private static final Class extends CreateSharedIpGroupOptions[]> createSharedIpGroupOptionsVarargsClass = new CreateSharedIpGroupOptions[] {}
+ .getClass();
+
+ public void testCreateSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createSharedIpGroup", String.class,
+ createSharedIpGroupOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie");
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, "{\"sharedIpGroup\":{\"name\":\"ralphie\"}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testCreateSharedIpGroupWithIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createSharedIpGroup", String.class,
+ createSharedIpGroupOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, "ralphie", withServer(2));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/shared_ip_groups?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, "{\"sharedIpGroup\":{\"name\":\"ralphie\",\"server\":2}}",
+ MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testDeleteSharedIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("deleteSharedIpGroup", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE http://serverManagementUrl/shared_ip_groups/2 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReturnTrueIf2xx.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListAddresses() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getAddresses", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testListPublicAddresses() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listPublicAddresses", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips/public?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListPrivateAddresses() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("listPrivateAddresses", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/ips/private?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListBackupSchedule() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("getBackupSchedule", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET http://serverManagementUrl/servers/2/backup_schedule?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class);
+
+ checkFilters(request);
+ }
+
+ public void testCreateImageWithIpGroup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("createImageFromServer", String.class, int.class);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/images?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+ assertPayloadEquals(request, "{\"image\":{\"serverId\":2,\"name\":\"ralphie\"}}", MediaType.APPLICATION_JSON,
+ false);
+
+ assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ private static final Class extends RebuildServerOptions[]> rebuildServerOptionsVarargsClass = new RebuildServerOptions[] {}
+ .getClass();
+
+ public void testRebuildServer() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("rebuildServer", int.class,
+ rebuildServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, 3);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/3/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"rebuild\":{}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testRebuildServerWithImage() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("rebuildServer", int.class,
+ rebuildServerOptionsVarargsClass);
+ HttpRequest request = processor.createRequest(method, 3, withImage(2));
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/3/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"rebuild\":{\"imageId\":2}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testReboot() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("rebootServer", int.class, RebootType.class);
+ HttpRequest request = processor.createRequest(method, 2, RebootType.HARD);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"reboot\":{\"type\":\"HARD\"}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testResize() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("resizeServer", int.class, int.class);
+ HttpRequest request = processor.createRequest(method, 2, 3);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"resize\":{\"flavorId\":3}}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testConfirmResize() throws IOException, IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("confirmResizeServer", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"confirmResize\":null}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testRevertResize() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = NovaAsyncClient.class.getMethod("revertResizeServer", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "POST http://serverManagementUrl/servers/2/action?format=json HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, "{\"revertResize\":null}", MediaType.APPLICATION_JSON, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ @Override
+ protected TypeLiteral> createTypeLiteral() {
+ return new TypeLiteral>() {
+ };
+ }
+
+ @Override
+ protected void checkFilters(HttpRequest request) {
+ assertEquals(request.getFilters().size(), 2);
+ assertEquals(request.getFilters().get(0).getClass(), AuthenticateRequest.class);
+ assertEquals(request.getFilters().get(1).getClass(), AddTimestampQuery.class);
+
+ }
+
+ @Override
+ protected Module createModule() {
+ return new TestNovaRestClientModule();
+ }
+
+ @ConfiguresRestClient
+ @RequiresHttp
+ protected static class TestNovaRestClientModule extends NovaRestClientModule {
+ private TestNovaRestClientModule() {
+ super(new TestOpenStackAuthenticationModule());
+ }
+
+ @Override
+ protected URI provideServerUrl(AuthenticationResponse response) {
+ return URI.create("http://serverManagementUrl");
+ }
+
+ }
+
+ protected String provider = "nova";
+
+ @Override
+ public RestContextSpec, ?> createContextSpec() {
+ return new RestContextFactory(getProperties()).createContextSpec(provider, "user", "password", new Properties());
+ }
+
+ @Override
+ protected Properties getProperties() {
+ Properties overrides = new Properties();
+ overrides.setProperty(PROPERTY_REGIONS, "US");
+ overrides.setProperty(PROPERTY_API_VERSION, "1");
+ overrides.setProperty(provider + ".endpoint", "https://auth");
+ overrides.setProperty(provider + ".contextbuilder", NovaContextBuilder.class.getName());
+ return overrides;
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java
new file mode 100644
index 0000000000..ffeb07b0ad
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java
@@ -0,0 +1,639 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile;
+import static org.jclouds.openstack.nova.options.CreateSharedIpGroupOptions.Builder.withServer;
+import static org.jclouds.openstack.nova.options.ListOptions.Builder.withDetails;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.Constants;
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.openstack.nova.domain.DailyBackup;
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.openstack.nova.domain.Image;
+import org.jclouds.openstack.nova.domain.ImageStatus;
+import org.jclouds.openstack.nova.domain.RebootType;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+import org.jclouds.openstack.nova.domain.SharedIpGroup;
+import org.jclouds.openstack.nova.domain.WeeklyBackup;
+import org.jclouds.openstack.nova.options.RebuildServerOptions;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.io.Payload;
+import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.net.IPSocket;
+import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.predicates.SocketOpen;
+import org.jclouds.rest.RestContextFactory;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.ssh.SshException;
+import org.jclouds.ssh.jsch.config.JschSshClientModule;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Tests behavior of {@code NovaClient}
+ *
+ * @author Adrian Cole
+ */
+// disabled [Web Hosting #129069
+@Test(groups = "live", sequential = true)
+public class NovaClientLiveTest {
+
+ protected NovaClient client;
+ protected SshClient.Factory sshFactory;
+ private Predicate socketTester;
+ protected String provider = "nova";
+ protected String identity;
+ protected String credential;
+ protected String endpoint;
+ protected String apiversion;
+
+ protected void setupCredentials() {
+ identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
+ credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
+ + ".credential");
+ endpoint = System.getProperty("test." + provider + ".endpoint");
+ apiversion = System.getProperty("test." + provider + ".apiversion");
+ }
+
+ protected Properties setupProperties() {
+ Properties overrides = new Properties();
+ overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
+ overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
+ overrides.setProperty(provider + ".identity", identity);
+ overrides.setProperty(provider + ".credential", credential);
+ if (endpoint != null)
+ overrides.setProperty(provider + ".endpoint", endpoint);
+ if (apiversion != null)
+ overrides.setProperty(provider + ".apiversion", apiversion);
+ return overrides;
+ }
+
+ @BeforeGroups(groups = { "live" })
+ public void setupClient() {
+ setupCredentials();
+ Properties overrides = setupProperties();
+
+ Injector injector = new RestContextFactory().createContextBuilder(provider,
+ ImmutableSet. of(new Log4JLoggingModule(), new JschSshClientModule()), overrides)
+ .buildInjector();
+
+ client = injector.getInstance(NovaClient.class);
+ sshFactory = injector.getInstance(SshClient.Factory.class);
+ SocketOpen socketOpen = injector.getInstance(SocketOpen.class);
+ socketTester = new RetryablePredicate(socketOpen, 120, 1, TimeUnit.SECONDS);
+ injector.injectMembers(socketOpen); // add logger
+ }
+
+ public void testListServers() throws Exception {
+
+ Set response = client.listServers();
+ assert null != response;
+ long initialContainerCount = response.size();
+ assertTrue(initialContainerCount >= 0);
+
+ }
+
+ public void testListServersDetail() throws Exception {
+ Set response = client.listServers(withDetails());
+ assert null != response;
+ long initialContainerCount = response.size();
+ assertTrue(initialContainerCount >= 0);
+ }
+
+ public void testListImages() throws Exception {
+ Set response = client.listImages();
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 1);
+ for (Image image : response) {
+ assertTrue(image.getId() >= 0);
+ assert null != image.getName() : image;
+ }
+
+ }
+
+ public void testListImagesDetail() throws Exception {
+ Set response = client.listImages(withDetails());
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 0);
+ for (Image image : response) {
+ assertTrue(image.getId() >= 1);
+ assert null != image.getName() : image;
+ assert null != image.getStatus() : image;
+ }
+ }
+
+ public void testGetImagesDetail() throws Exception {
+ Set response = client.listImages(withDetails());
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 0);
+ for (Image image : response) {
+ try {
+ Image newDetails = client.getImage(image.getId());
+ assertEquals(image, newDetails);
+ } catch (HttpResponseException e) {// Ticket #9867
+ if (e.getResponse().getStatusCode() != 400)
+ throw e;
+ }
+ }
+ }
+
+ @Test
+ public void testGetImageDetailsNotFound() throws Exception {
+ assert client.getImage(12312987) == null;
+ }
+
+ @Test
+ public void testGetServerDetailsNotFound() throws Exception {
+ assert client.getServer(12312987) == null;
+ }
+
+ public void testGetServersDetail() throws Exception {
+ Set response = client.listServers(withDetails());
+ assert null != response;
+ long serverCount = response.size();
+ assertTrue(serverCount >= 0);
+ for (Server server : response) {
+ Server newDetails = client.getServer(server.getId());
+ assertEquals(server, newDetails);
+ }
+ }
+
+ public void testListFlavors() throws Exception {
+ Set response = client.listFlavors();
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 1);
+ for (Flavor flavor : response) {
+ assertTrue(flavor.getId() >= 0);
+ assert null != flavor.getName() : flavor;
+ }
+
+ }
+
+ public void testListFlavorsDetail() throws Exception {
+ Set response = client.listFlavors(withDetails());
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 0);
+ for (Flavor flavor : response) {
+ assertTrue(flavor.getId() >= 1);
+ assert null != flavor.getName() : flavor;
+ assert null != flavor.getDisk() : flavor;
+ assert null != flavor.getRam() : flavor;
+ }
+ }
+
+ public void testGetFlavorsDetail() throws Exception {
+ Set response = client.listFlavors(withDetails());
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 0);
+ for (Flavor flavor : response) {
+ Flavor newDetails = client.getFlavor(flavor.getId());
+ assertEquals(flavor, newDetails);
+ }
+ }
+
+ @Test
+ public void testGetFlavorDetailsNotFound() throws Exception {
+ assert client.getFlavor(12312987) == null;
+ }
+
+ public void testListSharedIpGroups() throws Exception {
+ Set response = client.listSharedIpGroups();
+ assert null != response;
+ long sharedIpGroupCount = response.size();
+ assertTrue(sharedIpGroupCount >= 0);
+ for (SharedIpGroup sharedIpGroup : response) {
+ assertTrue(sharedIpGroup.getId() >= 0);
+ assert null != sharedIpGroup.getName() : sharedIpGroup;
+ }
+
+ }
+
+ public void testListSharedIpGroupsDetail() throws Exception {
+ Set response = client.listSharedIpGroups(withDetails());
+ assert null != response;
+ long sharedIpGroupCount = response.size();
+ assertTrue(sharedIpGroupCount >= 0);
+ for (SharedIpGroup sharedIpGroup : response) {
+ assertTrue(sharedIpGroup.getId() >= 1);
+ assert null != sharedIpGroup.getName() : sharedIpGroup;
+ assert null != sharedIpGroup.getServers() : sharedIpGroup;
+ }
+ }
+
+ public void testGetSharedIpGroupsDetail() throws Exception {
+ Set response = client.listSharedIpGroups(withDetails());
+ assert null != response;
+ long sharedIpGroupCount = response.size();
+ assertTrue(sharedIpGroupCount >= 0);
+ for (SharedIpGroup sharedIpGroup : response) {
+ SharedIpGroup newDetails = client.getSharedIpGroup(sharedIpGroup.getId());
+ assertEquals(sharedIpGroup, newDetails);
+ }
+ }
+
+ @Test
+ public void testGetSharedIpGroupDetailsNotFound() throws Exception {
+ assert client.getSharedIpGroup(12312987) == null;
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer")
+ public void testCreateSharedIpGroup() throws Exception {
+ SharedIpGroup sharedIpGroup = null;
+ while (sharedIpGroup == null) {
+ String sharedIpGroupName = serverPrefix + "createSharedIpGroup" + new SecureRandom().nextInt();
+ try {
+ sharedIpGroup = client.createSharedIpGroup(sharedIpGroupName, withServer(serverId));
+ } catch (UndeclaredThrowableException e) {
+ HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ if (htpe.getResponse().getStatusCode() == 400)
+ continue;
+ throw e;
+ }
+ }
+ assertNotNull(sharedIpGroup.getName());
+ sharedIpGroupId = sharedIpGroup.getId();
+ // Response doesn't include the server id Web Hosting #119311
+ // assertEquals(sharedIpGroup.getServers(), ImmutableList.of(serverId));
+ }
+
+ private int sharedIpGroupId;
+
+ private String serverPrefix = System.getProperty("user.name") + ".cs";
+ private int serverId;
+ private String adminPass;
+ Map metadata = ImmutableMap.of("jclouds", "rackspace");
+ private String ip;
+ private int serverId2;
+ private String adminPass2;
+ private int imageId;
+
+ @Test(enabled = false)
+ public void testCreateServer() throws Exception {
+ int imageId = 14362;
+ int flavorId = 1;
+ Server server = null;
+ while (server == null) {
+ String serverName = serverPrefix + "createserver" + new SecureRandom().nextInt();
+ try {
+ server = client.createServer(serverName, imageId, flavorId, withFile("/etc/jclouds.txt",
+ "rackspace".getBytes()).withMetadata(metadata));
+ } catch (UndeclaredThrowableException e) {
+ HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ if (htpe.getResponse().getStatusCode() == 400)
+ continue;
+ throw e;
+ }
+ }
+ assertNotNull(server.getAdminPass());
+ serverId = server.getId();
+ adminPass = server.getAdminPass();
+ ip = server.getAddresses().getPublicAddresses().iterator().next();
+ assertEquals(server.getStatus(), ServerStatus.BUILD);
+ blockUntilServerActive(serverId);
+ }
+
+ private void blockUntilServerActive(int serverId) throws InterruptedException {
+ Server currentDetails = null;
+ for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = client
+ .getServer(serverId)) {
+ System.out.printf("blocking on status active%n%s%n", currentDetails);
+ Thread.sleep(5 * 1000);
+ }
+ }
+
+ private void blockUntilServerVerifyResize(int serverId) throws InterruptedException {
+ Server currentDetails = null;
+ for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.VERIFY_RESIZE; currentDetails = client
+ .getServer(serverId)) {
+ System.out.printf("blocking on status verify resize%n%s%n", currentDetails);
+ Thread.sleep(5 * 1000);
+ }
+ }
+
+ private void blockUntilImageActive(int imageId) throws InterruptedException {
+ Image currentDetails = null;
+ for (currentDetails = client.getImage(imageId); currentDetails.getStatus() != ImageStatus.ACTIVE; currentDetails = client
+ .getImage(imageId)) {
+ System.out.printf("blocking on status active%n%s%n", currentDetails);
+ Thread.sleep(5 * 1000);
+ }
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer")
+ public void testServerDetails() throws Exception {
+ Server server = client.getServer(serverId);
+
+ assertNotNull(server.getHostId());
+ assertEquals(server.getStatus(), ServerStatus.ACTIVE);
+ assert server.getProgress() >= 0 : "newDetails.getProgress()" + server.getProgress();
+ assertEquals(new Integer(14362), server.getImageId());
+ assertEquals(new Integer(1), server.getFlavorId());
+ assertNotNull(server.getAddresses());
+ // listAddresses tests..
+ assertEquals(client.getAddresses(serverId), server.getAddresses());
+ assertEquals(server.getAddresses().getPublicAddresses().size(), 1);
+ assertEquals(client.listPublicAddresses(serverId), server.getAddresses().getPublicAddresses());
+ assertEquals(server.getAddresses().getPrivateAddresses().size(), 1);
+ assertEquals(client.listPrivateAddresses(serverId), server.getAddresses().getPrivateAddresses());
+
+ // check metadata
+ assertEquals(server.getMetadata(), metadata);
+
+ checkPassOk(server, adminPass);
+ }
+
+ /**
+ * this tests "personality" as the file looked up was sent during server creation
+ */
+ private void checkPassOk(Server newDetails, String pass) throws IOException {
+ try {
+ doCheckPass(newDetails, pass);
+ } catch (SshException e) {// try twice in case there is a network timeout
+ try {
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException e1) {
+ }
+ doCheckPass(newDetails, pass);
+ }
+ }
+
+ private void doCheckPass(Server newDetails, String pass) throws IOException {
+ IPSocket socket = new IPSocket(Iterables.get(newDetails.getAddresses().getPublicAddresses(), 0), 22);
+ socketTester.apply(socket);
+
+ SshClient client = sshFactory.create(socket, new Credentials("root", pass));
+ try {
+ client.connect();
+ Payload etcPasswd = client.get("/etc/jclouds.txt");
+ String etcPasswdContents = Strings2.toStringAndClose(etcPasswd.getInput());
+ assertEquals("rackspace", etcPasswdContents.trim());
+ } finally {
+ if (client != null)
+ client.disconnect();
+ }
+ }
+
+ private ExecResponse exec(Server details, String pass, String command) throws IOException {
+ IPSocket socket = new IPSocket(Iterables.get(details.getAddresses().getPublicAddresses(), 0), 22);
+ socketTester.apply(socket);
+ SshClient client = sshFactory.create(socket, new Credentials("root", pass));
+ try {
+ client.connect();
+ return client.exec(command);
+ } finally {
+ if (client != null)
+ client.disconnect();
+ }
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer")
+ public void testRenameServer() throws Exception {
+ Server server = client.getServer(serverId);
+ String oldName = server.getName();
+ client.renameServer(serverId, oldName + "new");
+ blockUntilServerActive(serverId);
+ assertEquals(oldName + "new", client.getServer(serverId).getName());
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateServer")
+ public void testChangePassword() throws Exception {
+ client.changeAdminPass(serverId, "elmo");
+ blockUntilServerActive(serverId);
+ checkPassOk(client.getServer(serverId), "elmo");
+ this.adminPass = "elmo";
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateSharedIpGroup")
+ public void testCreateServerIp() throws Exception {
+ int imageId = 14362;
+ int flavorId = 1;
+ Server server = null;
+ while (server == null) {
+ String serverName = serverPrefix + "createserver" + new SecureRandom().nextInt();
+ try {
+ server = client
+ .createServer(serverName, imageId, flavorId, withFile("/etc/jclouds.txt", "rackspace".getBytes())
+ .withMetadata(metadata).withSharedIpGroup(sharedIpGroupId).withSharedIp(ip));
+ } catch (UndeclaredThrowableException e) {
+ HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ if (htpe.getResponse().getStatusCode() == 400)
+ continue;
+ throw e;
+ }
+ }
+ assertNotNull(server.getAdminPass());
+ serverId2 = server.getId();
+ adminPass2 = server.getAdminPass();
+ blockUntilServerActive(serverId2);
+ assertIpConfigured(server, adminPass2);
+ assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses() + " doesn't contain " + ip;
+ assertEquals(server.getSharedIpGroupId(), new Integer(sharedIpGroupId));
+ }
+
+ private void assertIpConfigured(Server server, String password) {
+ try {
+ ExecResponse response = exec(server, password, "ifconfig -a");
+ assert response.getOutput().indexOf(ip) > 0 : String.format("server %s didn't get ip %s%n%s", server, ip,
+ response);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } catch (AssertionError e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testCreateServerIp")
+ public void testUnshare() throws Exception {
+ client.unshareIp(ip, serverId2);
+ blockUntilServerActive(serverId2);
+ Server server = client.getServer(serverId2);
+ assert !server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses();
+ assertIpNotConfigured(server, adminPass2);
+ }
+
+ private void assertIpNotConfigured(Server server, String password) {
+ try {
+ ExecResponse response = exec(server, password, "ifconfig -a");
+ assert response.getOutput().indexOf(ip) == -1 : String.format("server %s still has get ip %s%n%s", server, ip,
+ response);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } catch (AssertionError e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testUnshare")
+ public void testShareConfig() throws Exception {
+ client.shareIp(ip, serverId2, sharedIpGroupId, true);
+ blockUntilServerActive(serverId2);
+ Server server = client.getServer(serverId2);
+ assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses();
+ assertIpConfigured(server, adminPass2);
+ testUnshare();
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testShareConfig")
+ public void testShareNoConfig() throws Exception {
+ client.shareIp(ip, serverId2, sharedIpGroupId, false);
+ blockUntilServerActive(serverId2);
+ Server server = client.getServer(serverId2);
+ assert server.getAddresses().getPublicAddresses().contains(ip) : server.getAddresses();
+ assertIpNotConfigured(server, adminPass2);
+ testUnshare();
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testShareNoConfig")
+ public void testBackup() throws Exception {
+ assertEquals(new BackupSchedule(), client.getBackupSchedule(serverId));
+ BackupSchedule dailyWeekly = new BackupSchedule();
+ dailyWeekly.setEnabled(true);
+ dailyWeekly.setWeekly(WeeklyBackup.FRIDAY);
+ dailyWeekly.setDaily(DailyBackup.H_0400_0600);
+ client.replaceBackupSchedule(serverId, dailyWeekly);
+ client.deleteBackupSchedule(serverId);
+ // disables, doesn't delete: Web Hosting #119571
+ assertEquals(client.getBackupSchedule(serverId).isEnabled(), false);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testBackup")
+ public void testCreateImage() throws Exception {
+ Image image = client.createImageFromServer("hoofie", serverId);
+ assertEquals("hoofie", image.getName());
+ assertEquals(new Integer(serverId), image.getServerId());
+ imageId = image.getId();
+ blockUntilImageActive(imageId);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testCreateImage")
+ public void testRebuildServer() throws Exception {
+ client.rebuildServer(serverId, new RebuildServerOptions().withImage(imageId));
+ blockUntilServerActive(serverId);
+ // issue Web Hosting #119580 imageId comes back incorrect after rebuild
+ // assertEquals(new Integer(imageId), client.getServer(serverId).getImageId());
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildServer")
+ public void testRebootHard() throws Exception {
+ client.rebootServer(serverId, RebootType.HARD);
+ blockUntilServerActive(serverId);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootHard")
+ public void testRebootSoft() throws Exception {
+ client.rebootServer(serverId, RebootType.SOFT);
+ blockUntilServerActive(serverId);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootSoft")
+ public void testRevertResize() throws Exception {
+ client.resizeServer(serverId, 2);
+ blockUntilServerVerifyResize(serverId);
+ client.revertResizeServer(serverId);
+ blockUntilServerActive(serverId);
+ assertEquals(new Integer(1), client.getServer(serverId).getFlavorId());
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootSoft")
+ public void testConfirmResize() throws Exception {
+ client.resizeServer(serverId2, 2);
+ blockUntilServerVerifyResize(serverId2);
+ client.confirmResizeServer(serverId2);
+ blockUntilServerActive(serverId2);
+ assertEquals(new Integer(2), client.getServer(serverId2).getFlavorId());
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = { "testRebootSoft", "testRevertResize",
+ "testConfirmResize" })
+ void deleteServer2() {
+ if (serverId2 > 0) {
+ client.deleteServer(serverId2);
+ assert client.getServer(serverId2) == null;
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "deleteServer2")
+ void testDeleteImage() {
+ if (imageId > 0) {
+ client.deleteImage(imageId);
+ assert client.getImage(imageId) == null;
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testDeleteImage")
+ void deleteServer1() {
+ if (serverId > 0) {
+ client.deleteServer(serverId);
+ assert client.getServer(serverId) == null;
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = { "deleteServer1" })
+ void testDeleteSharedIpGroup() {
+ if (sharedIpGroupId > 0) {
+ client.deleteSharedIpGroup(sharedIpGroupId);
+ assert client.getSharedIpGroup(sharedIpGroupId) == null;
+ }
+ }
+
+ @AfterTest
+ void deleteServersOnEnd() {
+ if (serverId > 0) {
+ client.deleteServer(serverId);
+ }
+ if (serverId2 > 0) {
+ client.deleteServer(serverId2);
+ }
+ if (sharedIpGroupId > 0) {
+ client.deleteSharedIpGroup(sharedIpGroupId);
+ }
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/NovaComputeServiceLiveTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/NovaComputeServiceLiveTest.java
new file mode 100644
index 0000000000..5d533d09cb
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/NovaComputeServiceLiveTest.java
@@ -0,0 +1,89 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.jclouds.openstack.nova.NovaAsyncClient;
+import org.jclouds.openstack.nova.NovaClient;
+import org.jclouds.compute.BaseComputeServiceLiveTest;
+import org.jclouds.compute.ComputeServiceContextFactory;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.rest.RestContext;
+import org.jclouds.ssh.jsch.config.JschSshClientModule;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * Generally disabled, as it incurs higher fees.
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "live", enabled = true, sequential = true)
+public class NovaComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+ public NovaComputeServiceLiveTest() {
+ provider = "nova";
+ }
+
+ @Override
+ protected JschSshClientModule getSshModule() {
+ return new JschSshClientModule();
+ }
+
+ public void testAssignability() throws Exception {
+ @SuppressWarnings("unused")
+ RestContext tmContext = new ComputeServiceContextFactory()
+ .createContext(provider, identity, credential).getProviderSpecificContext();
+ }
+
+ @Override
+ protected void checkNodes(Iterable extends NodeMetadata> nodes, String tag) throws IOException {
+ super.checkNodes(nodes, tag);
+ for (NodeMetadata node : nodes) {
+ assertEquals(node.getLocation().getScope(), LocationScope.HOST);
+ }
+ }
+
+ @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class)
+ public void testSuspendResume() throws Exception {
+ super.testSuspendResume();
+ }
+
+ @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+ @Override
+ public void testGetNodesWithDetails() throws Exception {
+ super.testGetNodesWithDetails();
+ }
+
+ @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+ @Override
+ public void testListNodes() throws Exception {
+ super.testListNodes();
+ }
+
+ @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails" })
+ @Override
+ public void testDestroyNodes() {
+ super.testDestroyNodes();
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java
new file mode 100644
index 0000000000..fb7202503c
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/config/NovaComputeServiceContextModuleTest.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.config;
+
+import org.jclouds.openstack.nova.domain.ServerStatus;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class NovaComputeServiceContextModuleTest {
+
+ public void testAllStatusCovered() {
+
+ for (ServerStatus state : ServerStatus.values()) {
+ assert NovaComputeServiceDependenciesModule.serverToNodeState.containsKey(state) : state;
+ }
+
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java
new file mode 100644
index 0000000000..72088797a1
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/FlavorToHardwareTest.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.UnknownHostException;
+
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.openstack.nova.functions.ParseFlavorFromJsonResponseTest;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.VolumeBuilder;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class FlavorToHardwareTest {
+ Location provider = new LocationBuilder().scope(LocationScope.ZONE).id("dallas").description("description").build();
+
+ @Test
+ public void test() throws UnknownHostException {
+ assertEquals(convertFlavor(), new HardwareBuilder().ids("1").name("256 MB Server").processors(
+ ImmutableList.of(new Processor(1.0, 1.0))).ram(256).volumes(
+ ImmutableList.of(new VolumeBuilder().type(Volume.Type.LOCAL).size(10.0f).durable(true).bootDevice(true)
+ .build())).build());
+ }
+
+ public static Hardware convertFlavor() {
+ Flavor flavor = ParseFlavorFromJsonResponseTest.parseFlavor();
+
+ FlavorToHardware parser = new FlavorToHardware();
+
+ return parser.apply(flavor);
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java
new file mode 100644
index 0000000000..516db3270f
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/NovaImageToImageTest.java
@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.UnknownHostException;
+
+import org.jclouds.compute.config.BaseComputeServiceContextModule;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystemBuilder;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Credentials;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.openstack.nova.functions.ParseImageFromJsonResponseTest;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class NovaImageToImageTest {
+
+ @Test
+ public void testApplyWhereImageNotFound() throws UnknownHostException {
+ assertEquals(
+ convertImage(),
+ new ImageBuilder()
+ .name("CentOS 5.2")
+ .operatingSystem(
+ new OperatingSystemBuilder().family(OsFamily.CENTOS).version("5.2").description("CentOS 5.2").is64Bit(true)
+ .build()).description("CentOS 5.2").defaultCredentials(new Credentials("root", null))
+ .ids("2").version("1286712000000").build());
+ }
+
+ public static Image convertImage() {
+ org.jclouds.openstack.nova.domain.Image image = ParseImageFromJsonResponseTest.parseImage();
+
+ NovaImageToImage parser = new NovaImageToImage(new NovaImageToOperatingSystem(new BaseComputeServiceContextModule() {
+ }.provideOsVersionMap(new ComputeServiceConstants.ReferenceData(), Guice.createInjector(new GsonModule())
+ .getInstance(Json.class))));
+
+ return parser.apply(image);
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java
new file mode 100644
index 0000000000..b6736c7f2b
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/compute/functions/ServerToNodeMetadataTest.java
@@ -0,0 +1,157 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.UnknownHostException;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.openstack.nova.compute.config.NovaComputeServiceDependenciesModule;
+import org.jclouds.openstack.nova.domain.Server;
+import org.jclouds.openstack.nova.domain.ServerStatus;
+import org.jclouds.openstack.nova.functions.ParseServerFromJsonResponseTest;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.OperatingSystemBuilder;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Volume;
+import org.jclouds.compute.domain.VolumeBuilder;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ServerToNodeMetadataTest {
+ Location provider = new LocationBuilder().scope(LocationScope.ZONE).id("dallas").description("description").build();
+
+ @Test
+ public void testApplyWhereImageAndHardwareNotFoundButCredentialsFound() throws UnknownHostException {
+ Credentials creds = new Credentials("root", "abdce");
+
+ Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState;
+ Set images = ImmutableSet.of();
+ Set hardwares = ImmutableSet.of();
+ Server server = ParseServerFromJsonResponseTest.parseServer();
+
+ ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap
+ . of("node#1234", creds), Suppliers.> ofInstance(images),
+ Suppliers.ofInstance(provider), Suppliers.> ofInstance(hardwares));
+
+ NodeMetadata metadata = parser.apply(server);
+
+ assertEquals(metadata, new NodeMetadataBuilder().state(NodeState.PENDING).publicAddresses(
+ ImmutableSet.of("67.23.10.132", "67.23.10.131")).privateAddresses(ImmutableSet.of("10.176.42.16"))
+ .imageId("2").id("1234").providerId("1234").name("sample-server").credentials(creds).location(
+ new LocationBuilder().scope(LocationScope.HOST).id("e4d909c290d0fb1ca068ffaddf22cbd0")
+ .description("e4d909c290d0fb1ca068ffaddf22cbd0").parent(provider).build())
+ .userMetadata(ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")).build());
+ }
+
+ @Test
+ public void testApplyWhereImageAndHardwareNotFound() throws UnknownHostException {
+ Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState;
+ Set images = ImmutableSet.of();
+ Set hardwares = ImmutableSet.of();
+ Server server = ParseServerFromJsonResponseTest.parseServer();
+
+ ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap
+ . of(), Suppliers.> ofInstance(images), Suppliers
+ .ofInstance(provider), Suppliers.> ofInstance(hardwares));
+
+ NodeMetadata metadata = parser.apply(server);
+
+ assertEquals(metadata, new NodeMetadataBuilder().state(NodeState.PENDING).publicAddresses(
+ ImmutableSet.of("67.23.10.132", "67.23.10.131")).privateAddresses(ImmutableSet.of("10.176.42.16"))
+ .imageId("2").id("1234").providerId("1234").name("sample-server").location(
+ new LocationBuilder().scope(LocationScope.HOST).id("e4d909c290d0fb1ca068ffaddf22cbd0")
+ .description("e4d909c290d0fb1ca068ffaddf22cbd0").parent(provider).build())
+ .userMetadata(ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")).build());
+
+ }
+
+ @Test
+ public void testApplyWhereImageFoundAndHardwareNotFound() throws UnknownHostException {
+ Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState;
+ org.jclouds.compute.domain.Image jcImage = NovaImageToImageTest.convertImage();
+ Set images = ImmutableSet.of(jcImage);
+ Set hardwares = ImmutableSet.of();
+ Server server = ParseServerFromJsonResponseTest.parseServer();
+
+ ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap
+ . of(), Suppliers.> ofInstance(images), Suppliers
+ .ofInstance(provider), Suppliers.> ofInstance(hardwares));
+
+ NodeMetadata metadata = parser.apply(server);
+
+ assertEquals(metadata, new NodeMetadataBuilder().state(NodeState.PENDING).publicAddresses(
+ ImmutableSet.of("67.23.10.132", "67.23.10.131")).privateAddresses(ImmutableSet.of("10.176.42.16"))
+ .imageId("2").operatingSystem(
+ new OperatingSystemBuilder().family(OsFamily.CENTOS).description("CentOS 5.2").version("5.2")
+ .is64Bit(true).build()).id("1234").providerId("1234").name("sample-server").location(
+ new LocationBuilder().scope(LocationScope.HOST).id("e4d909c290d0fb1ca068ffaddf22cbd0")
+ .description("e4d909c290d0fb1ca068ffaddf22cbd0").parent(provider).build())
+ .userMetadata(ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")).build());
+
+ }
+
+ @Test
+ public void testApplyWhereImageAndHardwareFound() throws UnknownHostException {
+ Map serverStateToNodeState = NovaComputeServiceDependenciesModule.serverToNodeState;
+ Set images = ImmutableSet.of(NovaImageToImageTest.convertImage());
+ Set hardwares = ImmutableSet.of(FlavorToHardwareTest.convertFlavor());
+ Server server = ParseServerFromJsonResponseTest.parseServer();
+
+ ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, ImmutableMap
+ . of(), Suppliers.> ofInstance(images), Suppliers
+ .ofInstance(provider), Suppliers.> ofInstance(hardwares));
+
+ NodeMetadata metadata = parser.apply(server);
+
+ assertEquals(metadata, new NodeMetadataBuilder().state(NodeState.PENDING).publicAddresses(
+ ImmutableSet.of("67.23.10.132", "67.23.10.131")).privateAddresses(ImmutableSet.of("10.176.42.16"))
+ .imageId("2").hardware(
+ new HardwareBuilder().ids("1").name("256 MB Server").processors(
+ ImmutableList.of(new Processor(1.0, 1.0))).ram(256).volumes(
+ ImmutableList.of(new VolumeBuilder().type(Volume.Type.LOCAL).size(10.0f).durable(true)
+ .bootDevice(true).build())).build()).operatingSystem(
+ new OperatingSystemBuilder().family(OsFamily.CENTOS).description("CentOS 5.2").version("5.2")
+ .is64Bit(true).build()).id("1234").providerId("1234").name("sample-server").location(
+ new LocationBuilder().scope(LocationScope.HOST).id("e4d909c290d0fb1ca068ffaddf22cbd0")
+ .description("e4d909c290d0fb1ca068ffaddf22cbd0").parent(provider).build())
+ .userMetadata(ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")).build());
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java
new file mode 100644
index 0000000000..0c93f0cb3b
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/domain/ServerTest.java
@@ -0,0 +1,49 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.domain;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+/**
+ * Tests behavior of {@code CreateImageBinder}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ServerTest {
+ public void testStatusDoesntAffectEquals() {
+ Server server1 = new Server(1, "hello");
+ server1.setStatus(ServerStatus.ACTIVE);
+ Server server2 = new Server(1, "hello");
+ server2.setStatus(ServerStatus.BUILD);
+ assertEquals(server1, server2);
+ }
+
+ public void testProgressDoesntAffectEquals() {
+ Server server1 = new Server(1, "hello");
+ server1.setProgress(1);
+ Server server2 = new Server(1, "hello");
+ server2.setProgress(2);
+ assertEquals(server1, server2);
+ }
+
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java
new file mode 100644
index 0000000000..a7eb137961
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseAddressesFromJsonResponseTest.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.net.UnknownHostException;
+import java.util.List;
+
+import org.jclouds.openstack.nova.domain.Addresses;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.io.Payloads;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code ParseAddressesFromJsonResponse}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ParseAddressesFromJsonResponseTest {
+ Injector i = Guice.createInjector(new GsonModule());
+
+ public void testApplyInputStreamDetails() throws UnknownHostException {
+ InputStream is = getClass().getResourceAsStream("/test_list_addresses.json");
+
+ UnwrapOnlyJsonValue parser = i.getInstance(Key.get(new TypeLiteral>() {
+ }));
+ Addresses response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
+ List publicAddresses = ImmutableList.of("67.23.10.132", "67.23.10.131");
+
+ List privateAddresses = ImmutableList.of("10.176.42.16");
+
+ assertEquals(response.getPublicAddresses(), publicAddresses);
+ assertEquals(response.getPrivateAddresses(), privateAddresses);
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseBackupScheduleFromJsonResponseTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseBackupScheduleFromJsonResponseTest.java
new file mode 100644
index 0000000000..3ce29a32e1
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseBackupScheduleFromJsonResponseTest.java
@@ -0,0 +1,69 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.net.UnknownHostException;
+
+import org.jclouds.openstack.nova.domain.BackupSchedule;
+import org.jclouds.openstack.nova.domain.DailyBackup;
+import org.jclouds.openstack.nova.domain.WeeklyBackup;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.io.Payloads;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code ParseBackupScheduleFromJsonResponse}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ParseBackupScheduleFromJsonResponseTest {
+ Injector i = Guice.createInjector(new GsonModule());
+
+ public void testApplyInputStreamDetails() throws UnknownHostException {
+ InputStream is = getClass().getResourceAsStream("/test_list_backupschedule.json");
+
+ UnwrapOnlyJsonValue parser = i.getInstance(Key
+ .get(new TypeLiteral>() {
+ }));
+ BackupSchedule response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
+ assertEquals(new BackupSchedule(WeeklyBackup.THURSDAY, DailyBackup.H_0400_0600, true), response);
+ }
+
+ public void testNoSchedule() throws UnknownHostException {
+
+ UnwrapOnlyJsonValue parser = i.getInstance(Key
+ .get(new TypeLiteral>() {
+ }));
+ BackupSchedule response = parser.apply(new HttpResponse(200, "ok", Payloads
+ .newStringPayload("{\"backupSchedule\":{\"enabled\" : false}}")));
+ assertEquals(new BackupSchedule(), response);
+ }
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java
new file mode 100644
index 0000000000..a7cc0dd176
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorFromJsonResponseTest.java
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.io.Payloads;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code ParseFlavorFromJsonResponse}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ParseFlavorFromJsonResponseTest {
+ public void test() {
+ Flavor response = parseFlavor();
+
+ String json = new Gson().toJson(response);
+
+ assertEquals(json, "{\"id\":1,\"name\":\"256 MB Server\",\"disk\":10,\"ram\":256}");
+ }
+
+ public static Flavor parseFlavor() {
+ Injector i = Guice.createInjector(new GsonModule());
+
+ InputStream is = ParseFlavorFromJsonResponseTest.class.getResourceAsStream("/test_get_flavor_details.json");
+
+ UnwrapOnlyJsonValue parser = i.getInstance(Key.get(new TypeLiteral>() {
+ }));
+ Flavor response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
+ return response;
+ }
+
+}
diff --git a/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java
new file mode 100644
index 0000000000..cf2bfbeb83
--- /dev/null
+++ b/sandbox-apis/nova/src/test/java/org/jclouds/openstack/nova/functions/ParseFlavorListFromJsonResponseTest.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.jclouds.openstack.nova.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.net.UnknownHostException;
+import java.util.List;
+
+import org.jclouds.openstack.nova.domain.Flavor;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.io.Payloads;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code ParseFlavorListFromJsonResponse}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ParseFlavorListFromJsonResponseTest {
+
+ Injector i = Guice.createInjector(new GsonModule());
+
+ public void testApplyInputStream() {
+ InputStream is = getClass().getResourceAsStream("/test_list_flavors.json");
+
+ List expects = ImmutableList.of(new Flavor(1, "256 MB Server"), new Flavor(2, "512 MB Server"));
+
+ UnwrapOnlyJsonValue> parser = i.getInstance(Key
+ .get(new TypeLiteral>>() {
+ }));
+ List response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
+ assertEquals(response, expects);
+ }
+
+ public void testApplyInputStreamDetails() throws UnknownHostException {
+ InputStream is = getClass().getResourceAsStream("/test_list_flavors_detail.json");
+
+ UnwrapOnlyJsonValue> parser = i.getInstance(Key
+ .get(new TypeLiteral