Domain model is fixed to match the API v1.1

remaining parts of shared ip functionality is removed
This commit is contained in:
Dmitri Babaev 2011-04-15 19:30:12 +04:00 committed by Dmitri Babaev
parent 82e3c4ddf7
commit a131e2a34b
14 changed files with 1080 additions and 1001 deletions

View File

@ -18,22 +18,43 @@
*/ */
package org.jclouds.openstack.nova; package org.jclouds.openstack.nova;
import com.google.common.util.concurrent.ListenableFuture; import java.util.Set;
import org.jclouds.openstack.filters.AddTimestampQuery; import java.util.concurrent.ExecutionException;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.domain.*; 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.domain.Addresses;
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.options.CreateServerOptions; import org.jclouds.openstack.nova.options.CreateServerOptions;
import org.jclouds.openstack.nova.options.ListOptions; import org.jclouds.openstack.nova.options.ListOptions;
import org.jclouds.openstack.nova.options.RebuildServerOptions; import org.jclouds.openstack.nova.options.RebuildServerOptions;
import org.jclouds.rest.annotations.*; import org.jclouds.openstack.filters.AddTimestampQuery;
import org.jclouds.openstack.filters.AuthenticateRequest;
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.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import javax.ws.rs.*; import com.google.common.util.concurrent.ListenableFuture;
import javax.ws.rs.core.MediaType;
import java.util.Set;
import java.util.concurrent.ExecutionException;
/** /**
* Provides asynchronous access to OpenStack Nova via their REST API. * Provides asynchronous access to OpenStack Nova via their REST API.
@ -41,221 +62,221 @@ import java.util.concurrent.ExecutionException;
* All commands return a ListenableFuture of the result from OpenStack Nova. Any exceptions incurred * 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 * during processing will be wrapped in an {@link ExecutionException} as documented in
* {@link ListenableFuture#get()}. * {@link ListenableFuture#get()}.
* *
* @author Adrian Cole
* @see NovaClient * @see NovaClient
* @see <a href="http://wiki.openstack.org/OpenStackAPI_1-1" /> * @see <a href="http://wiki.openstack.org/OpenStackAPI_1-1" />
* @author Adrian Cole
*/ */
@SkipEncoding({'/', '='}) @SkipEncoding({ '/', '=' })
@RequestFilters({AuthenticateRequest.class, AddTimestampQuery.class}) @RequestFilters({ AuthenticateRequest.class, AddTimestampQuery.class })
@Endpoint(ServerManagement.class) @Endpoint(ServerManagement.class)
public interface NovaAsyncClient { public interface NovaAsyncClient {
/** /**
* @see NovaClient#listServers * @see NovaClient#listServers
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers") @Path("/servers")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<Server>> listServers(ListOptions... options); ListenableFuture<? extends Set<Server>> listServers(ListOptions... options);
/** /**
* @see NovaClient#getServer * @see NovaClient#getServer
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
@Path("/servers/{id}") @Path("/servers/{id}")
ListenableFuture<Server> getServer(@PathParam("id") int id); ListenableFuture<Server> getServer(@PathParam("id") int id);
/** /**
* @see NovaClient#deleteServer * @see NovaClient#deleteServer
*/ */
@DELETE @DELETE
@ExceptionParser(ReturnFalseOnNotFoundOr404.class) @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
@Path("/servers/{id}") @Path("/servers/{id}")
ListenableFuture<Boolean> deleteServer(@PathParam("id") int id); ListenableFuture<Boolean> deleteServer(@PathParam("id") int id);
/** /**
* @see NovaClient#rebootServer * @see NovaClient#rebootServer
*/ */
@POST @POST
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/action") @Path("/servers/{id}/action")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"reboot\":%7B\"type\":\"{type}\"%7D%7D") @Payload("%7B\"reboot\":%7B\"type\":\"{type}\"%7D%7D")
ListenableFuture<Void> rebootServer(@PathParam("id") int id, @PayloadParam("type") RebootType rebootType); ListenableFuture<Void> rebootServer(@PathParam("id") int id, @PayloadParam("type") RebootType rebootType);
/** /**
* @see NovaClient#resizeServer * @see NovaClient#resizeServer
*/ */
@POST @POST
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/action") @Path("/servers/{id}/action")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"resize\":%7B\"flavorId\":{flavorId}%7D%7D") @Payload("%7B\"resize\":%7B\"flavorId\":{flavorId}%7D%7D")
ListenableFuture<Void> resizeServer(@PathParam("id") int id, @PayloadParam("flavorId") int flavorId); ListenableFuture<Void> resizeServer(@PathParam("id") int id, @PayloadParam("flavorId") int flavorId);
/** /**
* @see NovaClient#confirmResizeServer * @see NovaClient#confirmResizeServer
*/ */
@POST @POST
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/action") @Path("/servers/{id}/action")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("{\"confirmResize\":null}") @Payload("{\"confirmResize\":null}")
ListenableFuture<Void> confirmResizeServer(@PathParam("id") int id); ListenableFuture<Void> confirmResizeServer(@PathParam("id") int id);
/** /**
* @see NovaClient#revertResizeServer * @see NovaClient#revertResizeServer
*/ */
@POST @POST
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/action") @Path("/servers/{id}/action")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("{\"revertResize\":null}") @Payload("{\"revertResize\":null}")
ListenableFuture<Void> revertResizeServer(@PathParam("id") int id); ListenableFuture<Void> revertResizeServer(@PathParam("id") int id);
/** /**
* @see NovaClient#createServer * @see NovaClient#createServer
*/ */
@POST @POST
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers") @Path("/servers")
@MapBinder(CreateServerOptions.class) @MapBinder(CreateServerOptions.class)
ListenableFuture<Server> createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef, ListenableFuture<Server> createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef,
@PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options); @PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options);
/** /**
* @see NovaClient#rebuildServer * @see NovaClient#rebuildServer
*/ */
@POST @POST
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/action") @Path("/servers/{id}/action")
@MapBinder(RebuildServerOptions.class) @MapBinder(RebuildServerOptions.class)
ListenableFuture<Void> rebuildServer(@PathParam("id") int id, RebuildServerOptions... options); ListenableFuture<Void> rebuildServer(@PathParam("id") int id, RebuildServerOptions... options);
/** /**
* @see NovaClient#changeAdminPass * @see NovaClient#changeAdminPass
*/ */
@PUT @PUT
@Path("/servers/{id}") @Path("/servers/{id}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"server\":%7B\"adminPass\":\"{adminPass}\"%7D%7D") @Payload("%7B\"server\":%7B\"adminPass\":\"{adminPass}\"%7D%7D")
ListenableFuture<Void> changeAdminPass(@PathParam("id") int id, @PayloadParam("adminPass") String adminPass); ListenableFuture<Void> changeAdminPass(@PathParam("id") int id, @PayloadParam("adminPass") String adminPass);
/** /**
* @see NovaClient#renameServer * @see NovaClient#renameServer
*/ */
@PUT @PUT
@Path("/servers/{id}") @Path("/servers/{id}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"server\":%7B\"name\":\"{name}\"%7D%7D") @Payload("%7B\"server\":%7B\"name\":\"{name}\"%7D%7D")
ListenableFuture<Void> renameServer(@PathParam("id") int id, @PayloadParam("name") String newName); ListenableFuture<Void> renameServer(@PathParam("id") int id, @PayloadParam("name") String newName);
/** /**
* @see NovaClient#listFlavors * @see NovaClient#listFlavors
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/flavors") @Path("/flavors")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<Flavor>> listFlavors(ListOptions... options); ListenableFuture<? extends Set<Flavor>> listFlavors(ListOptions... options);
/** /**
* @see NovaClient#getFlavor * @see NovaClient#getFlavor
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/flavors/{id}") @Path("/flavors/{id}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<Flavor> getFlavor(@PathParam("id") int id); ListenableFuture<Flavor> getFlavor(@PathParam("id") int id);
/** /**
* @see NovaClient#listImages * @see NovaClient#listImages
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/images") @Path("/images")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<Image>> listImages(ListOptions... options); ListenableFuture<? extends Set<Image>> listImages(ListOptions... options);
/** /**
* @see NovaClient#getImage * @see NovaClient#getImage
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/images/{id}") @Path("/images/{id}")
ListenableFuture<Image> getImage(@PathParam("id") int id); ListenableFuture<Image> getImage(@PathParam("id") int id);
/** /**
* @see NovaClient#deleteImage * @see NovaClient#deleteImage
*/ */
@DELETE @DELETE
@ExceptionParser(ReturnFalseOnNotFoundOr404.class) @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
@Path("/images/{id}") @Path("/images/{id}")
ListenableFuture<Boolean> deleteImage(@PathParam("id") int id); ListenableFuture<Boolean> deleteImage(@PathParam("id") int id);
/** /**
* @see NovaClient#createImageFromServer * @see NovaClient#createImageFromServer
*/ */
@POST @POST
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/images") @Path("/images")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"image\":%7B\"serverId\":{serverId},\"name\":\"{name}\"%7D%7D") @Payload("%7B\"image\":%7B\"serverId\":{serverId},\"name\":\"{name}\"%7D%7D")
ListenableFuture<Image> createImageFromServer(@PayloadParam("name") String imageName, ListenableFuture<Image> createImageFromServer(@PayloadParam("name") String imageName,
@PayloadParam("serverId") int serverId); @PayloadParam("serverId") int serverId);
/** /**
* @see NovaClient#getAddresses(int) * @see NovaClient#listAddresses
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/ips") @Path("/servers/{id}/ips")
ListenableFuture<Addresses> getAddresses(@PathParam("id") int serverId); ListenableFuture<Addresses> getAddresses(@PathParam("id") int serverId);
/** /**
* @see NovaClient#listPublicAddresses(int) * @see NovaClient#listPublicAddresses
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/ips/public") @Path("/servers/{id}/ips/public")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<String>> listPublicAddresses(@PathParam("id") int serverId); ListenableFuture<? extends Set<String>> listPublicAddresses(@PathParam("id") int serverId);
/** /**
* @see NovaClient#listPrivateAddresses * @see NovaClient#listPrivateAddresses
*/ */
@GET @GET
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@QueryParams(keys = "format", values = "json") @QueryParams(keys = "format", values = "json")
@Path("/servers/{id}/ips/private") @Path("/servers/{id}/ips/private")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<? extends Set<String>> listPrivateAddresses(@PathParam("id") int serverId); ListenableFuture<? extends Set<String>> listPrivateAddresses(@PathParam("id") int serverId);
} }

View File

@ -18,18 +18,24 @@
*/ */
package org.jclouds.openstack.nova; 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.concurrent.Timeout;
import org.jclouds.openstack.nova.domain.*; import org.jclouds.openstack.nova.domain.Addresses;
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.options.CreateServerOptions; import org.jclouds.openstack.nova.options.CreateServerOptions;
import org.jclouds.openstack.nova.options.ListOptions; import org.jclouds.openstack.nova.options.ListOptions;
import org.jclouds.openstack.nova.options.RebuildServerOptions; import org.jclouds.openstack.nova.options.RebuildServerOptions;
import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.rest.ResourceNotFoundException;
import javax.ws.rs.PathParam;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/** /**
* Provides access to OpenStack Nova via their REST API. * Provides access to OpenStack Nova via their REST API.
@ -37,224 +43,240 @@ import java.util.concurrent.TimeUnit;
* All commands return a Future of the result from OpenStack Nova. Any exceptions incurred * 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 * during processing will be wrapped in an {@link ExecutionException} as documented in
* {@link Future#get()}. * {@link Future#get()}.
* *
* @author Adrian Cole
* @see NovaAsyncClient * @see NovaAsyncClient
* @see <a href="http://wiki.openstack.org/OpenStackAPI_1-1" /> * @see <a href="http://wiki.openstack.org/OpenStackAPI_1-1" />
* @author Adrian Cole
*/ */
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
public interface NovaClient { public interface NovaClient {
/** /**
* List all servers (IDs and names only) *
* <p/> * 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. * This operation provides a list of servers associated with your identity. Servers that have been
* <p/> * deleted are not included in this list.
* in order to retrieve all details, pass the option {@link ListOptions#withDetails() * <p/>
* withDetails()} * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
*/ * withDetails()}
Set<Server> listServers(ListOptions... options); */
Set<Server> listServers(ListOptions... options);
/** /**
* This operation returns details of the specified server. *
* * This operation returns details of the specified server.
* @return null, if the server is not found *
* @see Server * @return null, if the server is not found
*/ * @see Server
Server getServer(@PathParam("id") int id); */
Server getServer(@PathParam("id") int id);
/** /**
* This operation deletes a cloud server instance from the system. *
* <p/> * 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. * <p/>
* * 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 * @return false if the server is not found
*/ * @see Server
boolean deleteServer(@PathParam("id") int id); */
boolean deleteServer(@PathParam("id") int id);
/** /**
* The reboot function allows for either a soft or hard reboot of a server. * The reboot function allows for either a soft or hard reboot of a server.
* <p/> * <p/>
* Status Transition: * Status Transition:
* <p/> * <p/>
* ACTIVE - REBOOT - ACTIVE (soft reboot) * ACTIVE - REBOOT - ACTIVE (soft reboot)
* <p/> * <p/>
* ACTIVE - HARD_REBOOT - ACTIVE (hard reboot) * ACTIVE - HARD_REBOOT - ACTIVE (hard reboot)
* *
* @param rebootType With a soft reboot, the operating system is signaled to restart, which allows for a * @param rebootType
* graceful shutdown of all processes. A hard reboot is the equivalent of power cycling * With a soft reboot, the operating system is signaled to restart, which allows for a
* the server. * graceful shutdown of all processes. A hard reboot is the equivalent of power cycling
*/ * the server.
void rebootServer(int id, RebootType rebootType); */
void rebootServer(int id, RebootType rebootType);
/** /**
* The resize function converts an existing server to a different flavor, in essence, scaling the * 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 * 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 * 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 * original server is removed. All resizes are automatically confirmed after 24 hours if they are
* not confirmed or reverted. * not confirmed or reverted.
* <p/> * <p/>
* Status Transition: * Status Transition:
* <p/> * <p/>
* ACTIVE - QUEUE_RESIZE - PREP_RESIZE - VERIFY_RESIZE * ACTIVE - QUEUE_RESIZE - PREP_RESIZE - VERIFY_RESIZE
* <p/> * <p/>
* ACTIVE - QUEUE_RESIZE - ACTIVE (on error) * ACTIVE - QUEUE_RESIZE - ACTIVE (on error)
*/ */
void resizeServer(int id, int flavorId); void resizeServer(int id, int flavorId);
/** /**
* The resize function converts an existing server to a different flavor, in essence, scaling the * 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 * 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 * 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 * original server is removed. All resizes are automatically confirmed after 24 hours if they are
* not confirmed or reverted. * not confirmed or reverted.
* <p/> * <p/>
* Status Transition: * Status Transition:
* <p/> * <p/>
* VERIFY_RESIZE - ACTIVE * VERIFY_RESIZE - ACTIVE
*/ */
void confirmResizeServer(int id); void confirmResizeServer(int id);
/** /**
* The resize function converts an existing server to a different flavor, in essence, scaling the * 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 * 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 * 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 * original server is removed. All resizes are automatically reverted after 24 hours if they are
* not reverted or reverted. * not reverted or reverted.
* <p/> * <p/>
* Status Transition: * Status Transition:
* <p/> * <p/>
* VERIFY_RESIZE - ACTIVE * VERIFY_RESIZE - ACTIVE
*/ */
void revertResizeServer(int id); void revertResizeServer(int id);
/** /**
* This operation asynchronously provisions a new server. The progress of this operation depends * 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 * 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, * 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 * 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 * 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. * returned in subsequent GET calls against a given server ID.
* *
* @param options - used to specify extra files, metadata, or ip parameters during server creation. * @param options
*/ * - used to specify extra files, metadata, or ip parameters during server creation.
Server createServer(String name, String imageRef, String flavorRef, CreateServerOptions... options); */
Server createServer(String name, String imageRef, String flavorRef, CreateServerOptions... options);
/** /**
* The rebuild function removes all data on the server and replaces it with the specified image. * The rebuild function removes all data on the server and replaces it with the specified image.
* Server ID and IP addresses remain the same. * Server ID and IP addresses remain the same.
* <p/> * <p/>
* Status Transition: * Status Transition:
* <p/> * <p/>
* ACTIVE - REBUILD - ACTIVE * ACTIVE - REBUILD - ACTIVE
* <p/> * <p/>
* ACTIVE - REBUILD - ERROR (on error) * ACTIVE - REBUILD - ERROR (on error)
* <p/> * <p/>
* *
* @param options - imageId is an optional argument. If it is not specified, the server is rebuilt * @param options
* with the original imageId. * - imageId is an optional argument. If it is not specified, the server is rebuilt
*/ * with the original imageId.
void rebuildServer(int id, RebuildServerOptions... options); */
void rebuildServer(int id, RebuildServerOptions... options);
/** /**
* This operation allows you to change the administrative password. * This operation allows you to change the administrative password.
* <p/> * <p/>
* Status Transition: ACTIVE - PASSWORD - ACTIVE * Status Transition: ACTIVE - PASSWORD - ACTIVE
*/ *
void changeAdminPass(int id, String adminPass); */
void changeAdminPass(int id, String adminPass);
/** /**
* This operation allows you to update the name of the server. This operation changes the name of * 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. * the server in the OpenStack Nova system and does not change the server host name itself.
* <p/> * <p/>
* Status Transition: ACTIVE - PASSWORD - ACTIVE * Status Transition: ACTIVE - PASSWORD - ACTIVE
*/ *
void renameServer(int id, String newName); */
void renameServer(int id, String newName);
/** /**
* List available flavors (IDs and names only) *
* <p/> * List available flavors (IDs and names only)
* in order to retrieve all details, pass the option {@link ListOptions#withDetails() *
* withDetails()} * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
*/ * withDetails()}
Set<Flavor> listFlavors(ListOptions... options); */
Set<Flavor> listFlavors(ListOptions... options);
/** /**
* This operation returns details of the specified flavor. *
* * This operation returns details of the specified flavor.
* @return null, if the flavor is not found *
* @see Flavor * @return null, if the flavor is not found
*/ * @see Flavor
Flavor getFlavor(int id); */
Flavor getFlavor(int id);
/** /**
* List available images (IDs and names only) *
* <p/> * List available images (IDs and names only)
* in order to retrieve all details, pass the option {@link ListOptions#withDetails() *
* withDetails()} * in order to retrieve all details, pass the option {@link ListOptions#withDetails()
*/ * withDetails()}
Set<Image> listImages(ListOptions... options); */
Set<Image> listImages(ListOptions... options);
/** /**
* This operation returns details of the specified image. *
* * This operation returns details of the specified image.
* @return null, if the image is not found *
* @see Image * @return null, if the image is not found
*/ *
Image getImage(int id); * @see Image
*/
Image getImage(int id);
/** /**
* This operation deletes an image from the system. *
* <p/> * This operation deletes an image from the system.
* Note: Images are immediately removed. Currently, there are no state transitions to track the * <p/>
* delete operation. * 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 * @return false if the image is not found
*/ * @see Image
boolean deleteImage(int id); */
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 * This operation creates a new image for the given server ID. Once complete, a new image will be
* existing custom image replaces the image. The image creation status can be queried by * available that can be used to rebuild or create servers. Specifying the same image name as an
* performing a GET on /images/id and examining the status and progress attributes. * existing custom image replaces the image. The image creation status can be queried by
* <p/> * performing a GET on /images/id and examining the status and progress attributes.
* Status Transition: *
* <p/> * Status Transition:
* QUEUED - PREPARING - SAVING - ACTIVE * <p/>
* <p/> * QUEUED - PREPARING - SAVING - ACTIVE
* QUEUED - PREPARING - SAVING - FAILED (on error) * <p/>
* <p/> * QUEUED - PREPARING - SAVING - FAILED (on error)
* Note: At present, image creation is an asynchronous operation, so coordinating the creation * <p/>
* with data quiescence, etc. is currently not possible. * 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 * @throws ResourceNotFoundException
*/ * if the server is not found
Image createImageFromServer(String imageName, int serverId); * @see Image
*/
Image createImageFromServer(String imageName, int serverId);
/** /**
* List all server addresses * List all server addresses
* <p/> *
* returns empty set if the server doesn't exist * returns empty set if the server doesn't exist
*/ */
Addresses getAddresses(int serverId); Addresses getAddresses(int serverId);
/** /**
* List all public server addresses * List all public server addresses
* <p/> *
* returns empty set if the server doesn't exist * returns empty set if the server doesn't exist
*/ */
Set<String> listPublicAddresses(int serverId); Set<String> listPublicAddresses(int serverId);
/** /**
* List all private server addresses * List all private server addresses
* <p/> *
* returns empty set if the server doesn't exist * returns empty set if the server doesn't exist
*/ */
Set<String> listPrivateAddresses(int serverId); Set<String> listPrivateAddresses(int serverId);
} }

View File

@ -18,119 +18,127 @@
*/ */
package org.jclouds.openstack.nova.compute.functions; package org.jclouds.openstack.nova.compute.functions;
import com.google.common.base.Function; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Predicate; import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables; 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.Address;
import org.jclouds.openstack.nova.domain.Server;
import org.jclouds.openstack.nova.domain.ServerStatus;
import org.jclouds.collect.Memoized; import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.*; 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.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.openstack.nova.domain.Server;
import org.jclouds.openstack.nova.domain.ServerStatus;
import javax.annotation.Resource; import com.google.common.base.Function;
import javax.inject.Inject; import com.google.common.base.Predicate;
import javax.inject.Named; import com.google.common.base.Supplier;
import javax.inject.Singleton; import com.google.common.collect.Iterables;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class ServerToNodeMetadata implements Function<Server, NodeMetadata> { public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
protected final Supplier<Location> location; protected final Supplier<Location> location;
protected final Map<String, Credentials> credentialStore; protected final Map<String, Credentials> credentialStore;
protected final Map<ServerStatus, NodeState> serverToNodeState; protected final Map<ServerStatus, NodeState> serverToNodeState;
protected final Supplier<Set<? extends Image>> images; protected final Supplier<Set<? extends Image>> images;
protected final Supplier<Set<? extends Hardware>> hardwares; protected final Supplier<Set<? extends Hardware>> hardwares;
private static class FindImageForServer implements Predicate<Image> { private static class FindImageForServer implements Predicate<Image> {
private final Server instance; private final Server instance;
private FindImageForServer(Server instance) { private FindImageForServer(Server instance) {
this.instance = instance; this.instance = instance;
} }
@Override @Override
public boolean apply(Image input) { public boolean apply(Image input) {
return input.getProviderId().equals(instance.getImageRef() + ""); return input.getProviderId().equals(instance.getImageRef() + "");
} }
} }
private static class FindHardwareForServer implements Predicate<Hardware> { private static class FindHardwareForServer implements Predicate<Hardware> {
private final Server instance; private final Server instance;
private FindHardwareForServer(Server instance) { private FindHardwareForServer(Server instance) {
this.instance = instance; this.instance = instance;
} }
@Override @Override
public boolean apply(Hardware input) { public boolean apply(Hardware input) {
return input.getProviderId().equals(instance.getFlavorRef() + ""); return input.getProviderId().equals(instance.getFlavorRef() + "");
} }
} }
@Inject @Inject
ServerToNodeMetadata(Map<ServerStatus, NodeState> serverStateToNodeState, Map<String, Credentials> credentialStore, ServerToNodeMetadata(Map<ServerStatus, NodeState> serverStateToNodeState, Map<String, Credentials> credentialStore,
@Memoized Supplier<Set<? extends Image>> images, Supplier<Location> location, @Memoized Supplier<Set<? extends Image>> images, Supplier<Location> location,
@Memoized Supplier<Set<? extends Hardware>> hardwares) { @Memoized Supplier<Set<? extends Hardware>> hardwares) {
this.serverToNodeState = checkNotNull(serverStateToNodeState, "serverStateToNodeState"); this.serverToNodeState = checkNotNull(serverStateToNodeState, "serverStateToNodeState");
this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.credentialStore = checkNotNull(credentialStore, "credentialStore");
this.images = checkNotNull(images, "images"); this.images = checkNotNull(images, "images");
this.location = checkNotNull(location, "location"); this.location = checkNotNull(location, "location");
this.hardwares = checkNotNull(hardwares, "hardwares"); this.hardwares = checkNotNull(hardwares, "hardwares");
} }
@Override @Override
public NodeMetadata apply(Server from) { public NodeMetadata apply(Server from) {
NodeMetadataBuilder builder = new NodeMetadataBuilder(); NodeMetadataBuilder builder = new NodeMetadataBuilder();
builder.ids(from.getId() + ""); builder.ids(from.getId() + "");
builder.name(from.getName()); builder.name(from.getName());
builder.location(new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId()).description( builder.location(new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId()).description(
from.getHostId()).parent(location.get()).build()); from.getHostId()).parent(location.get()).build());
builder.userMetadata(from.getMetadata()); builder.userMetadata(from.getMetadata());
builder.group(parseGroupFromName(from.getName())); builder.group(parseGroupFromName(from.getName()));
builder.imageId(from.getImageRef() + ""); builder.imageId(from.getImageRef() + "");
builder.operatingSystem(parseOperatingSystem(from)); builder.operatingSystem(parseOperatingSystem(from));
builder.hardware(parseHardware(from)); builder.hardware(parseHardware(from));
builder.state(serverToNodeState.get(from.getStatus())); builder.state(serverToNodeState.get(from.getStatus()));
builder.publicAddresses(from.getAddresses().getPublicAddresses()); builder.publicAddresses(Iterables.transform(from.getAddresses().getPublicAddresses(), Address.newAddress2StringFunction()));
builder.privateAddresses(from.getAddresses().getPrivateAddresses()); builder.privateAddresses(Iterables.transform(from.getAddresses().getPrivateAddresses(), Address.newAddress2StringFunction()));
builder.credentials(credentialStore.get("node#" + from.getId())); builder.credentials(credentialStore.get("node#" + from.getId()));
builder.uri(from.getURI()); builder.uri(from.getURI());
return builder.build(); return builder.build();
} }
protected Hardware parseHardware(Server from) { protected Hardware parseHardware(Server from) {
try { try {
return Iterables.find(hardwares.get(), new FindHardwareForServer(from)); return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
logger.warn("could not find a matching hardware for server %s", from); logger.warn("could not find a matching hardware for server %s", from);
} }
return null; return null;
} }
protected OperatingSystem parseOperatingSystem(Server from) { protected OperatingSystem parseOperatingSystem(Server from) {
try { try {
return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem(); return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
logger.warn("could not find a matching image for server %s in location %s", from, location); logger.warn("could not find a matching image for server %s in location %s", from, location);
} }
return null; return null;
} }
} }

View File

@ -0,0 +1,79 @@
package org.jclouds.openstack.nova.domain;
import com.google.common.base.Function;
import com.google.gson.annotations.SerializedName;
import javax.annotation.Nullable;
/**
* @author Dmitri Babaev
*/
public class Address {
@SerializedName("addr")
private String address;
private int version;
//for de-serialization
private Address() {
}
public Address(String address, int version) {
this.address = address;
this.version = version;
}
public String getAddress() {
return address;
}
public int getVersion() {
return version;
}
@Override
public String toString() {
return address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address1 = (Address) o;
if (version != address1.version) return false;
if (address != null ? !address.equals(address1.address) : address1.address != null) return false;
return true;
}
@Override
public int hashCode() {
int result = address != null ? address.hashCode() : 0;
result = 31 * result + version;
return result;
}
public static Function<Address, String> newAddress2StringFunction() {
return new Function<Address, String>() {
@Override
public String apply(@Nullable Address input) {
return input.getAddress();
}
};
}
public static Address valueOf(String address) {
return new Address(address, address.startsWith("::") ? 6 : 4);
}
public static Function<String, Address> newString2AddressFunction() {
return new Function<String, Address>() {
@Override
public Address apply(@Nullable String input) {
return valueOf(input);
}
};
}
}

View File

@ -18,113 +18,81 @@
*/ */
package org.jclouds.openstack.nova.domain; package org.jclouds.openstack.nova.domain;
import java.util.Set;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/** /**
*
* @author Adrian Cole * @author Adrian Cole
*/ */
public class Addresses { public class Addresses {
@SerializedName("public") @SerializedName("public")
private Set<Map<String, String>> publicAddresses = Sets.newHashSet(); private Set<Address> publicAddresses = Sets.newLinkedHashSet();
@SerializedName("private") @SerializedName("private")
private Set<Map<String, String>> privateAddresses = Sets.newHashSet(); private Set<Address> privateAddresses = Sets.newLinkedHashSet();
public Addresses() { public Addresses() {
} }
// public Addresses(Set<Map<String, String>> publicAddresses, Set<Map<String, String>> privateAddresses) { public Addresses(Set<Address> publicAddresses, Set<Address> privateAddresses) {
// this.publicAddresses = publicAddresses; this.publicAddresses = publicAddresses;
// this.privateAddresses = privateAddresses; this.privateAddresses = privateAddresses;
// } }
public Addresses(Set<String> publicAddresses, Set<String> privateAddresses) { public void setPublicAddresses(Set<Address> publicAddresses) {
this.publicAddresses.clear(); this.publicAddresses = publicAddresses;
this.privateAddresses.clear(); }
for (String address : publicAddresses) {
HashMap<String, String> addressMap = new HashMap<String, String>();
addressMap.put("version", "4");
addressMap.put("addr", "address");
this.publicAddresses.add(addressMap);
}
for (String address : privateAddresses) {
HashMap<String, String> addressMap = new HashMap<String, String>();
addressMap.put("version", "4");
addressMap.put("addr", "address");
this.privateAddresses.add(addressMap);
}
} public Set<Address> getPublicAddresses() {
return publicAddresses;
}
public void setPublicAddresses(Set<Map<String, String>> publicAddresses) { public void setPrivateAddresses(Set<Address> privateAddresses) {
this.publicAddresses = publicAddresses; this.privateAddresses = privateAddresses;
} }
public Set<String> getPublicAddresses() { public Set<Address> getPrivateAddresses() {
HashSet<String> addresses = new HashSet<String>(); return privateAddresses;
for (Map<String, String> address : publicAddresses) { }
if (address.containsKey("addr")) {
addresses.add(address.get("addr"));
}
}
return addresses;
}
public void setPrivateAddresses(Set<Map<String, String>> privateAddresses) { @Override
this.privateAddresses = privateAddresses; public String toString() {
} return "Addresses [privateAddresses=" + privateAddresses + ", publicAddresses="
+ publicAddresses + "]";
}
public Set<String> getPrivateAddresses() { @Override
HashSet<String> addresses = new HashSet<String>(); public int hashCode() {
for (Map<String, String> address : privateAddresses) { final int prime = 31;
if (address.containsKey("addr")) { int result = 1;
addresses.add(address.get("addr")); result = prime * result + ((privateAddresses == null) ? 0 : privateAddresses.hashCode());
} result = prime * result + ((publicAddresses == null) ? 0 : publicAddresses.hashCode());
} return result;
return addresses; }
}
@Override @Override
public String toString() { public boolean equals(Object obj) {
return "Addresses [privateAddresses=" + privateAddresses + ", publicAddresses=" if (this == obj)
+ publicAddresses + "]"; return true;
} if (obj == null)
return false;
@Override if (getClass() != obj.getClass())
public int hashCode() { return false;
final int prime = 31; Addresses other = (Addresses) obj;
int result = 1; if (privateAddresses == null) {
result = prime * result + ((privateAddresses == null) ? 0 : privateAddresses.hashCode()); if (other.privateAddresses != null)
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; return false;
if (getClass() != obj.getClass()) } else if (!privateAddresses.equals(other.privateAddresses))
return false;
if (publicAddresses == null) {
if (other.publicAddresses != null)
return false; return false;
Addresses other = (Addresses) obj; } else if (!publicAddresses.equals(other.publicAddresses))
if (privateAddresses == null) { return false;
if (other.privateAddresses != null) return true;
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;
}
} }

View File

@ -18,217 +18,196 @@
*/ */
package org.jclouds.openstack.nova.domain; package org.jclouds.openstack.nova.domain;
import com.google.common.collect.Maps;
import java.util.Map; 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 * A server is a virtual machine instance in the OpenStack Nova system. Flavor and image are
* requisite elements when creating a server. * requisite elements when creating a server.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class Server extends Resource { public class Server extends Resource {
private int id; private int id;
private String name; private String name;
// private Map<String, Map<String,String>> metadata = Maps.newHashMap(); private Map<String, String> metadata = Maps.newHashMap();
public Map<String, String> getMetadata() { private Addresses addresses;
return metadata; private String adminPass;
} private String flavorRef;
private String hostId;
private String imageRef;
public void setMetadata(Map<String, String> metadata) { private Integer progress;
this.metadata = metadata; private ServerStatus status;
}
private Map<String, String> metadata = Maps.newHashMap(); public Server() {
}
private Addresses addresses; public Server(int id, String name) {
this.id = id;
this.name = name;
}
private String adminPass; public void setMetadata(Map<String, String> metadata) {
private String flavorRef; this.metadata = metadata;
private String hostId; }
private String imageRef;
public String getAffinityId() { public Map<String, String> getMetadata() {
return affinityId; return metadata;
} }
public void setAffinityId(String affinityId) { public void setAddresses(Addresses addresses) {
this.affinityId = affinityId; this.addresses = addresses;
} }
private String affinityId; public Addresses getAddresses() {
return addresses;
}
private Integer progress; public void setAdminPass(String adminPass) {
private ServerStatus status; this.adminPass = adminPass;
}
public Server() { public String getAdminPass() {
} return adminPass;
}
public Server(int id, String name) { public void setFlavorRef(String flavorRef) {
this.id = id; this.flavorRef = flavorRef;
this.name = name; }
}
// public void setMetadata(Map<String, String> metadata) { public String getFlavorRef() {
// this.metadata.put("values", metadata); return flavorRef;
// } }
// public Map<String, String> getMetadata() { public void setHostId(String hostId) {
// return metadata.get("values"); this.hostId = hostId;
// } }
public void setAddresses(Addresses addresses) { /**
this.addresses = addresses; * 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.
* <p/>
* Note: hostId is unique PER ACCOUNT and is not globally unique.
*/
public String getHostId() {
return hostId;
}
public Addresses getAddresses() { public int getId() {
return addresses; return id;
} }
public void setAdminPass(String adminPass) { public void setImageRef(String imageRef) {
this.adminPass = adminPass; this.imageRef = imageRef;
} }
public String getAdminPass() { public String getImageRef() {
return adminPass; return imageRef;
} }
public void setFlavorRef(String flavorRef) { public String getName() {
this.flavorRef = flavorRef; return name;
} }
public String getFlavorRef() { public void setProgress(Integer progress) {
return flavorRef; this.progress = progress;
} }
public void setHostId(String hostId) { public Integer getProgress() {
this.hostId = hostId; return progress;
} }
/** public void setStatus(ServerStatus status) {
* The OpenStack Nova provisioning algorithm has an anti-affinity property that attempts to spread this.status = status;
* 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.
* <p/>
* Note: hostId is unique PER ACCOUNT and is not globally unique.
*/
public String getHostId() {
return hostId;
}
public int getId() { /**
return id; * 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;
}
public void getImageRef(String imageRef) { @Override
this.imageRef = imageRef; 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 + ((flavorRef == null) ? 0 : flavorRef.hashCode());
result = prime * result + ((hostId == null) ? 0 : hostId.hashCode());
result = prime * result + id;
result = prime * result + ((imageRef == null) ? 0 : imageRef.hashCode());
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public String getImageRef() { @Override
return imageRef; public boolean equals(Object obj) {
} if (this == obj)
return true;
public String getName() { if (obj == null)
return name; return false;
} if (getClass() != obj.getClass())
return false;
public void setProgress(Integer progress) { Server other = (Server) obj;
this.progress = progress; if (addresses == null) {
} if (other.addresses != null)
public Integer getProgress() {
return progress;
}
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 + ((flavorRef == null) ? 0 : flavorRef.hashCode());
result = prime * result + ((hostId == null) ? 0 : hostId.hashCode());
result = prime * result + id;
result = prime * result + ((imageRef == null) ? 0 : imageRef.hashCode());
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false; return false;
if (getClass() != obj.getClass()) } else if (!addresses.equals(other.addresses))
return false;
if (adminPass == null) {
if (other.adminPass != null)
return false; return false;
Server other = (Server) obj; } else if (!adminPass.equals(other.adminPass))
if (addresses == null) { return false;
if (other.addresses != null) if (flavorRef == null) {
return false; if (other.flavorRef != null)
} else if (!addresses.equals(other.addresses))
return false; return false;
if (adminPass == null) { } else if (!flavorRef.equals(other.flavorRef))
if (other.adminPass != null) return false;
return false; if (hostId == null) {
} else if (!adminPass.equals(other.adminPass)) if (other.hostId != null)
return false; return false;
if (flavorRef == null) { } else if (!hostId.equals(other.hostId))
if (other.flavorRef != null) return false;
return false; if (id != other.id)
} else if (!flavorRef.equals(other.flavorRef)) return false;
if (imageRef == null) {
if (other.imageRef != null)
return false; return false;
if (hostId == null) { } else if (!imageRef.equals(other.imageRef))
if (other.hostId != null) return false;
return false; if (metadata == null) {
} else if (!hostId.equals(other.hostId)) if (other.metadata != null)
return false; return false;
if (id != other.id) } else if (!metadata.equals(other.metadata))
return false;
if (name == null) {
if (other.name != null)
return false; return false;
if (imageRef == null) { } else if (!name.equals(other.name))
if (other.imageRef != null) return false;
return false; return true;
} else if (!imageRef.equals(other.imageRef)) }
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;
return true;
}
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
@Override @Override
public String toString() { public String toString() {
return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorRef=" return "Server [addresses=" + addresses + ", adminPass=" + adminPass + ", flavorRef="
+ flavorRef + ", hostId=" + hostId + ", id=" + id + ", imageRef=" + imageRef + flavorRef + ", hostId=" + hostId + ", id=" + id + ", imageRef=" + imageRef
+ ", metadata=" + metadata + ", name=" + name + "]"; + ", metadata=" + metadata + ", name=" + name + "]";
} }
} }

View File

@ -18,81 +18,88 @@
*/ */
package org.jclouds.openstack.nova.options; package org.jclouds.openstack.nova.options;
import com.google.common.collect.ImmutableMap; import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Lists; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Maps; import static com.google.common.base.Preconditions.checkState;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.encryption.internal.Base64; import org.jclouds.encryption.internal.Base64;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.nova.domain.Addresses; import org.jclouds.openstack.nova.domain.Addresses;
import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.binders.BindToJsonPayload;
import java.util.List; import com.google.common.collect.ImmutableMap;
import java.util.Map; import com.google.common.collect.Lists;
import java.util.Map.Entry; import com.google.common.collect.Maps;
import static com.google.common.base.Preconditions.*;
/** /**
*
* @author Adrian Cole * @author Adrian Cole
*
*/ */
public class CreateServerOptions extends BindToJsonPayload { public class CreateServerOptions extends BindToJsonPayload {
static class File { static class File {
private final String path; private final String path;
private final String contents; private final String contents;
public File(String path, byte[] contents) { public File(String path, byte[] contents) {
this.path = checkNotNull(path, "path"); this.path = checkNotNull(path, "path");
this.contents = Base64.encodeBytes(checkNotNull(contents, "contents")); this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
checkArgument(path.getBytes().length < 255, String.format( checkArgument(path.getBytes().length < 255, String.format(
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path "maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path
.getBytes().length)); .getBytes().length));
checkArgument(contents.length < 10 * 1024, String.format( checkArgument(contents.length < 10 * 1024, String.format(
"maximum size of the file is 10KB. Contents specified is %d bytes", "maximum size of the file is 10KB. Contents specified is %d bytes",
contents.length)); contents.length));
} }
public String getContents() { public String getContents() {
return contents; return contents;
} }
public String getPath() { public String getPath() {
return path; return path;
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private class ServerRequest { private class ServerRequest {
final String name; final String name;
final String imageRef; final String imageRef;
final String flavorRef; final String flavorRef;
Map<String, String> metadata; Map<String, String> metadata;
List<File> personality; List<File> personality;
Addresses addresses; Integer sharedIpGroupId;
Addresses addresses;
private ServerRequest(String name, String imageRef, String flavorRef) { private ServerRequest(String name, String imageRef, String flavorRef) {
this.name = name; this.name = name;
this.imageRef = imageRef; this.imageRef = imageRef;
this.flavorRef = flavorRef; this.flavorRef = flavorRef;
} }
} }
private Map<String, String> metadata = Maps.newHashMap(); private Map<String, String> metadata = Maps.newHashMap();
private List<File> files = Lists.newArrayList(); private List<File> files = Lists.newArrayList();
private String publicIp; private String publicIp;
@Override @Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) { public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"),
"name parameter not present"), checkNotNull(postParams "name parameter not present"), checkNotNull(postParams
.get("imageRef"), "imageRef parameter not present"), checkNotNull( .get("imageRef"), "imageRef parameter not present"), checkNotNull(
postParams.get("flavorRef"), "flavorRef parameter not present")); postParams.get("flavorRef"), "flavorRef parameter not present"));
if (metadata.size() > 0) if (metadata.size() > 0)
server.metadata = metadata; server.metadata = metadata;
if (files.size() > 0) if (files.size() > 0)
server.personality = files; server.personality = files;
if (publicIp != null) { if (publicIp != null) {
server.addresses = new Addresses(); server.addresses = new Addresses();
server.addresses.getPublicAddresses().add(publicIp); server.addresses.getPublicAddresses().add(publicIp);
@ -101,71 +108,69 @@ public class CreateServerOptions extends BindToJsonPayload {
return bindToRequest(request, ImmutableMap.of("server", server)); return bindToRequest(request, ImmutableMap.of("server", server));
} }
/** /**
* You may further customize a cloud server by injecting data into the file system of the cloud * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * /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 * the root group as owner and group owner, respectively and will allow user and group read
* access only (-r--r-----). * access only (-r--r-----).
*/ */
public CreateServerOptions withFile(String path, byte[] contents) { public CreateServerOptions withFile(String path, byte[] contents) {
checkState(files.size() < 5, "maximum number of files allowed is 5"); checkState(files.size() < 5, "maximum number of files allowed is 5");
files.add(new File(path, contents)); files.add(new File(path, contents));
return this; return this;
} }
/** /**
* Custom cloud server metadata can also be supplied at launch time. This metadata is stored in * 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 * 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 * 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. * can be supplied per server is 5.
*/ */
public CreateServerOptions withMetadata(Map<String, String> metadata) { public CreateServerOptions withMetadata(Map<String, String> metadata) {
checkNotNull(metadata, "metadata"); checkNotNull(metadata, "metadata");
checkArgument(metadata.size() <= 5, checkArgument(metadata.size() <= 5,
"you cannot have more then 5 metadata values. You specified: " + metadata.size()); "you cannot have more then 5 metadata values. You specified: " + metadata.size());
for (Entry<String, String> entry : metadata.entrySet()) { for (Entry<String, String> entry : metadata.entrySet()) {
checkArgument(entry.getKey().getBytes().length < 255, String.format( checkArgument(entry.getKey().getBytes().length < 255, String.format(
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes", "maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
entry.getKey(), entry.getKey().getBytes().length)); entry.getKey(), entry.getKey().getBytes().length));
checkArgument( checkArgument(
entry.getKey().getBytes().length < 255, entry.getKey().getBytes().length < 255,
String String
.format( .format(
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes", "maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
entry.getKey(), entry.getValue(), entry.getKey(), entry.getValue(),
entry.getValue().getBytes().length)); entry.getValue().getBytes().length));
} }
this.metadata = metadata; this.metadata = metadata;
return this; return this;
} }
public static class Builder {
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#withFile(String, byte[]) * @see CreateServerOptions#withMetadata(Map<String, String>)
*/ */
public static CreateServerOptions withFile(String path, byte[] contents) { public static CreateServerOptions withMetadata(Map<String, String> metadata) {
CreateServerOptions options = new CreateServerOptions(); CreateServerOptions options = new CreateServerOptions();
return options.withFile(path, contents); return options.withMetadata(metadata);
} }
}
/**
* @see CreateServerOptions#withMetadata(Map<String, String>)
*/
public static CreateServerOptions withMetadata(Map<String, String> metadata) {
CreateServerOptions options = new CreateServerOptions();
return options.withMetadata(metadata);
}
}
} }

View File

@ -39,9 +39,7 @@ import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientTest; import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory; import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextSpec; import org.jclouds.rest.RestContextSpec;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.*;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -54,8 +52,7 @@ import java.util.Properties;
import static org.jclouds.Constants.PROPERTY_API_VERSION; import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile; import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.*;
import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withMetadata;
import static org.jclouds.openstack.nova.options.ListOptions.Builder.changesSince; 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.ListOptions.Builder.withDetails;
import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage; import static org.jclouds.openstack.nova.options.RebuildServerOptions.Builder.withImage;
@ -81,7 +78,7 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageRef\":2,\"flavorRef\":1}}", assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1}}",
"application/json", false); "application/json", false);
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
@ -103,7 +100,7 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals( assertPayloadEquals(
request, request,
"{\"server\":{\"name\":\"ralphie\",\"imageRef\":2,\"flavorRef\":1,\"personality\":[{\"path\":\"/etc/jclouds\",\"contents\":\"Zm9v\"}]}}", "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"personality\":[{\"path\":\"/etc/jclouds\",\"contents\":\"Zm9v\"}]}}",
"application/json", false); "application/json", false);
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
@ -124,7 +121,7 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1"); assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/json\n"); assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
assertPayloadEquals(request, assertPayloadEquals(request,
"{\"server\":{\"name\":\"ralphie\",\"imageRef\":2,\"flavorRef\":1,\"metadata\":{\"foo\":\"bar\"}}}", "{\"server\":{\"name\":\"ralphie\",\"imageId\":2,\"flavorId\":1,\"metadata\":{\"foo\":\"bar\"}}}",
"application/json", false); "application/json", false);
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class); assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
@ -506,7 +503,7 @@ public class NovaAsyncClientTest extends RestClientTest<NovaAsyncClient> {
assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1"); assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1");
assertNonPayloadHeadersEqual(request, ""); assertNonPayloadHeadersEqual(request, "");
assertPayloadEquals(request, "{\"rebuild\":{\"imageRef\":2}}", MediaType.APPLICATION_JSON, false); assertPayloadEquals(request, "{\"rebuild\":{\"imageId\":2}}", MediaType.APPLICATION_JSON, false);
assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);

View File

@ -275,11 +275,11 @@ public class NovaClientLiveTest {
"rackspace".getBytes()).withMetadata(metadata)); "rackspace".getBytes()).withMetadata(metadata));
assertNotNull(server.getAdminPass()); assertNotNull(server.getAdminPass());
assertEquals(server.getStatus(), ServerStatus.BUILD);
serverId = server.getId(); serverId = server.getId();
adminPass = server.getAdminPass(); adminPass = server.getAdminPass();
ip = server.getAddresses().getPublicAddresses().iterator().next().getAddress();
assertEquals(server.getStatus(), ServerStatus.BUILD);
blockUntilServerActive(serverId); blockUntilServerActive(serverId);
ip = client.getServer(serverId).getAddresses().getPublicAddresses().iterator().next();
} }
private void blockUntilServerActive(int serverId) throws InterruptedException { private void blockUntilServerActive(int serverId) throws InterruptedException {
@ -316,8 +316,8 @@ public class NovaClientLiveTest {
assertNotNull(server.getHostId()); assertNotNull(server.getHostId());
assertEquals(server.getStatus(), ServerStatus.ACTIVE); assertEquals(server.getStatus(), ServerStatus.ACTIVE);
assert server.getProgress() >= 0 : "newDetails.getProgress()" + server.getProgress(); assert server.getProgress() >= 0 : "newDetails.getProgress()" + server.getProgress();
assertEquals("14362", server.getImageRef()); assertEquals(new Integer(14362), server.getImageRef());
assertEquals("1", server.getFlavorRef()); assertEquals(new Integer(1), server.getFlavorRef());
assertNotNull(server.getAddresses()); assertNotNull(server.getAddresses());
// listAddresses tests.. // listAddresses tests..
assertEquals(client.getAddresses(serverId), server.getAddresses()); assertEquals(client.getAddresses(serverId), server.getAddresses());
@ -328,12 +328,21 @@ public class NovaClientLiveTest {
// check metadata // check metadata
assertEquals(server.getMetadata(), metadata); assertEquals(server.getMetadata(), metadata);
assertPassword(server, adminPass);
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 {
doCheckPass(newDetails, pass);
} }
private void assertPassword(Server server, String pass) throws IOException { private void doCheckPass(Server newDetails, String pass) throws IOException {
IPSocket socket = new IPSocket(Iterables.get(server.getAddresses().getPublicAddresses(), 0), 22); IPSocket socket = new IPSocket(Iterables.get(newDetails.getAddresses().getPublicAddresses(), 0).getAddress(), 22);
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", pass)); SshClient client = sshFactory.create(socket, new Credentials("root", pass));
@ -349,7 +358,7 @@ public class NovaClientLiveTest {
} }
private ExecResponse exec(Server details, String pass, String command) throws IOException { private ExecResponse exec(Server details, String pass, String command) throws IOException {
IPSocket socket = new IPSocket(Iterables.get(details.getAddresses().getPublicAddresses(), 0), 22); IPSocket socket = new IPSocket(Iterables.get(details.getAddresses().getPublicAddresses(), 0).getAddress(), 22);
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", pass)); SshClient client = sshFactory.create(socket, new Credentials("root", pass));
try { try {
@ -374,7 +383,7 @@ public class NovaClientLiveTest {
public void testChangePassword() throws Exception { public void testChangePassword() throws Exception {
client.changeAdminPass(serverId, "elmo"); client.changeAdminPass(serverId, "elmo");
blockUntilServerActive(serverId); blockUntilServerActive(serverId);
assertPassword(client.getServer(serverId), "elmo"); checkPassOk(client.getServer(serverId), "elmo");
this.adminPass = "elmo"; this.adminPass = "elmo";
} }
@ -411,7 +420,7 @@ public class NovaClientLiveTest {
client.rebuildServer(serverId, new RebuildServerOptions().withImage(imageId)); client.rebuildServer(serverId, new RebuildServerOptions().withImage(imageId));
blockUntilServerActive(serverId); blockUntilServerActive(serverId);
// issue Web Hosting #119580 imageId comes back incorrect after rebuild // issue Web Hosting #119580 imageId comes back incorrect after rebuild
assertEquals(imageId, client.getServer(serverId).getImageRef()); assertEquals(new Integer(imageId), client.getServer(serverId).getImageRef());
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildServer") @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildServer")

View File

@ -39,15 +39,15 @@ public class _NovaClient {
System.out.println(cs.listAssignableLocations()); System.out.println(cs.listAssignableLocations());
System.out.println(cs.listNodes()); System.out.println(cs.listNodes());
TemplateOptions options = new TemplateOptions(); /*TemplateOptions options = new TemplateOptions();
Template template = cs.templateBuilder().imageId("13").options(options).build(); Template template = cs.templateBuilder().imageId("13").options(options).build();
try { try {
Set<? extends NodeMetadata> metedata = cs.runNodesWithTag("test", 1, template); Set<? extends NodeMetadata> metedata = cs.runNodesWithTag("test", 1, template);
System.out.println(metedata); System.out.println(metedata);
} catch (RunNodesException e) { } catch (RunNodesException e) {
e.printStackTrace(); e.printStackTrace();
} }*/
context.close(); context.close();
} }
} }

View File

@ -20,6 +20,7 @@ package org.jclouds.openstack.nova.functions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
@ -28,6 +29,7 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.nova.domain.Address;
import org.jclouds.openstack.nova.domain.Addresses; import org.jclouds.openstack.nova.domain.Addresses;
import org.jclouds.openstack.nova.domain.Server; import org.jclouds.openstack.nova.domain.Server;
import org.jclouds.openstack.nova.domain.ServerStatus; import org.jclouds.openstack.nova.domain.ServerStatus;
@ -49,7 +51,7 @@ import static org.testng.Assert.assertEquals;
public class ParseServerFromJsonResponseTest { public class ParseServerFromJsonResponseTest {
@Test @Test
public void testApplyInputStreamDetails() throws UnknownHostException, NoSuchMethodException, ClassNotFoundException { public void testApplyInputStreamDetails() throws UnknownHostException {
Server response = parseServer(); Server response = parseServer();
assertEquals(response.getId(), 1234); assertEquals(response.getId(), 1234);
@ -57,27 +59,25 @@ public class ParseServerFromJsonResponseTest {
assertEquals(response.getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234"); assertEquals(response.getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234");
assertEquals(response.getFlavorRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1"); assertEquals(response.getFlavorRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1");
assertEquals(response.getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0"); assertEquals(response.getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0");
assertEquals(response.getAffinityId(), "fc88bcf8394db9c8d0564e08ca6a9724188a84d1");
assertEquals(response.getStatus(), ServerStatus.BUILD); assertEquals(response.getStatus(), ServerStatus.BUILD);
assertEquals(response.getProgress(), new Integer(60)); assertEquals(response.getProgress(), new Integer(60));
List<String> publicAddresses = ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"); List<Address> publicAddresses = ImmutableList.copyOf(Iterables.transform(ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"), Address.newString2AddressFunction()));
List<String> privateAddresses = ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"); List<Address> privateAddresses = ImmutableList.copyOf(Iterables.transform(ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"), Address.newString2AddressFunction()));
Addresses addresses1 = new Addresses(new HashSet<String>(publicAddresses), new HashSet<String>(privateAddresses)); Addresses addresses1 = new Addresses(new HashSet<Address>(publicAddresses), new HashSet<Address>(privateAddresses));
assertEquals(response.getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1"));
assertEquals(response.getAddresses(), addresses1); assertEquals(response.getAddresses(), addresses1);
assertEquals(response.getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1"));
} }
public static Server parseServer() throws NoSuchMethodException, ClassNotFoundException { public static Server parseServer() {
Injector i = Guice.createInjector(new GsonModule()); Injector i = Guice.createInjector(new GsonModule());
InputStream is = ParseServerFromJsonResponseTest.class.getResourceAsStream("/test_get_server_detail.json"); InputStream is = ParseServerFromJsonResponseTest.class.getResourceAsStream("/test_get_server_detail.json");
UnwrapOnlyJsonValue<Server> parser = i.getInstance(Key.get(new TypeLiteral<UnwrapOnlyJsonValue<Server>>() { UnwrapOnlyJsonValue<Server> parser = i.getInstance(Key.get(new TypeLiteral<UnwrapOnlyJsonValue<Server>>() {
})); }));
Server response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
//Function<HttpResponse, ?> parser = i.getInstance(getParserOrThrowException(NovaClient.class.getMethod("getServer", int.class))); return response;
return (Server) parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
} }
} }

View File

@ -20,6 +20,7 @@ package org.jclouds.openstack.nova.functions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
@ -28,6 +29,7 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.nova.domain.Address;
import org.jclouds.openstack.nova.domain.Addresses; import org.jclouds.openstack.nova.domain.Addresses;
import org.jclouds.openstack.nova.domain.Server; import org.jclouds.openstack.nova.domain.Server;
import org.jclouds.openstack.nova.domain.ServerStatus; import org.jclouds.openstack.nova.domain.ServerStatus;
@ -77,15 +79,13 @@ public class ParseServerListFromJsonResponseTest {
assertEquals(response.get(0).getName(), "sample-server"); assertEquals(response.get(0).getName(), "sample-server");
assertEquals(response.get(0).getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234"); assertEquals(response.get(0).getImageRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234");
assertEquals(response.get(0).getFlavorRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1"); assertEquals(response.get(0).getFlavorRef(), "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1");
assertEquals(true, false, "Uncomment next line");
//assertEquals(response.getAffinityId(), "fc88bcf8394db9c8d0564e08ca6a9724188a84d1");
assertEquals(response.get(0).getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0"); assertEquals(response.get(0).getHostId(), "e4d909c290d0fb1ca068ffaddf22cbd0");
assertEquals(response.get(0).getStatus(), ServerStatus.BUILD); assertEquals(response.get(0).getStatus(), ServerStatus.BUILD);
assertEquals(response.get(0).getProgress(), new Integer(60)); assertEquals(response.get(0).getProgress(), new Integer(60));
List<String> publicAddresses = ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"); List<Address> publicAddresses = ImmutableList.copyOf(Iterables.transform(ImmutableList.of("67.23.10.132", "::babe:67.23.10.132", "67.23.10.131", "::babe:4317:0A83"), Address.newString2AddressFunction()));
List<String> privateAddresses = ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"); List<Address> privateAddresses = ImmutableList.copyOf(Iterables.transform(ImmutableList.of("10.176.42.16", "::babe:10.176.42.16"), Address.newString2AddressFunction()));
Addresses addresses1 = new Addresses(new HashSet<String>(publicAddresses), new HashSet<String>(privateAddresses)); Addresses addresses1 = new Addresses(new HashSet<Address>(publicAddresses), new HashSet<Address>(privateAddresses));
assertEquals(response.get(0).getAddresses(), addresses1); assertEquals(response.get(0).getAddresses(), addresses1);
assertEquals(response.get(0).getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1")); assertEquals(response.get(0).getMetadata(), ImmutableMap.of("Server Label", "Web Head 1", "Image Version", "2.1"));
@ -99,9 +99,9 @@ public class ParseServerListFromJsonResponseTest {
assertEquals(response.get(1).getStatus(), ServerStatus.ACTIVE); assertEquals(response.get(1).getStatus(), ServerStatus.ACTIVE);
assertEquals(response.get(1).getProgress(), null); assertEquals(response.get(1).getProgress(), null);
List<String> publicAddresses2 = ImmutableList.of("67.23.10.133", "::babe:67.23.10.133"); List<Address> publicAddresses2 = ImmutableList.of(new Address("67.23.10.133", 4), new Address("::babe:67.23.10.133", 4));
List<String> privateAddresses2 = ImmutableList.of("10.176.42.17", "::babe:10.176.42.17"); List<Address> privateAddresses2 = ImmutableList.of(new Address("10.176.42.17", 4), new Address("::babe:10.176.42.17", 4));
Addresses addresses2 = new Addresses(new HashSet<String>(publicAddresses2), new HashSet<String>(privateAddresses2)); Addresses addresses2 = new Addresses(new HashSet<Address>(publicAddresses2), new HashSet<Address>(privateAddresses2));
assertEquals(response.get(1).getAddresses(), addresses2); assertEquals(response.get(1).getAddresses(), addresses2);
assertEquals(response.get(1).getMetadata(), ImmutableMap.of("Server Label", "DB 1")); assertEquals(response.get(1).getMetadata(), ImmutableMap.of("Server Label", "DB 1"));

View File

@ -18,66 +18,67 @@
*/ */
package org.jclouds.openstack.nova.options; package org.jclouds.openstack.nova.options;
import com.google.common.collect.ImmutableMap; import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile;
import com.google.inject.Guice; import static org.testng.Assert.assertEquals;
import com.google.inject.Injector;
import java.net.URI;
import javax.ws.rs.HttpMethod;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.json.config.GsonModule; import org.jclouds.json.config.GsonModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import javax.ws.rs.HttpMethod; import com.google.common.collect.ImmutableMap;
import java.net.URI; import com.google.inject.Guice;
import com.google.inject.Injector;
import static org.jclouds.openstack.nova.options.CreateServerOptions.Builder.withFile;
import static org.testng.Assert.assertEquals;
/** /**
* Tests behavior of {@code ParseFlavorFromJsonResponse} * Tests behavior of {@code ParseFlavorFromJsonResponse}
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit")
public class CreateServerOptionsTest { public class CreateServerOptionsTest {
Injector injector = Guice.createInjector(new GsonModule()); Injector injector = Guice.createInjector(new GsonModule());
@Test @Test
public void testAddPayloadToRequestMapOfStringStringHttpRequest() { public void testAddPayloadToRequestMapOfStringStringHttpRequest() {
CreateServerOptions options = new CreateServerOptions(); CreateServerOptions options = new CreateServerOptions();
HttpRequest request = buildRequest(options); HttpRequest request = buildRequest(options);
assertEquals("{\"server\":{\"name\":\"foo\",\"imageRef\":1,\"flavorRef\":2}}", request.getPayload().getRawContent()); assertEquals("{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2}}", request.getPayload().getRawContent());
} }
private HttpRequest buildRequest(CreateServerOptions options) { private HttpRequest buildRequest(CreateServerOptions options) {
injector.injectMembers(options); injector.injectMembers(options);
HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost"));
options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageRef", "1", "flavorRef", "2")); options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageId", "1", "flavorId", "2"));
return request; return request;
} }
@Test @Test
public void testWithFile() { public void testWithFile() {
CreateServerOptions options = new CreateServerOptions(); CreateServerOptions options = new CreateServerOptions();
options.withFile("/tmp/rhubarb", "foo".getBytes()); options.withFile("/tmp/rhubarb", "foo".getBytes());
HttpRequest request = buildRequest(options); HttpRequest request = buildRequest(options);
assertFile(request); assertFile(request);
} }
@Test @Test
public void testWithFileStatic() { public void testWithFileStatic() {
CreateServerOptions options = withFile("/tmp/rhubarb", "foo".getBytes()); CreateServerOptions options = withFile("/tmp/rhubarb", "foo".getBytes());
HttpRequest request = buildRequest(options); HttpRequest request = buildRequest(options);
assertFile(request); assertFile(request);
} }
private void assertFile(HttpRequest request) {
assertEquals(request.getPayload().getRawContent(),
"{\"server\":{\"name\":\"foo\",\"imageRef\":1,\"flavorRef\":2,\"personality\":[{\"path\":\"/tmp/rhubarb\",\"contents\":\"Zm9v\"}]}}");
}
@Test
public void testWithMetadata() {
}
private void assertFile(HttpRequest request) {
assertEquals(
"{\"server\":{\"name\":\"foo\",\"imageId\":1,\"flavorId\":2,\"personality\":[{\"path\":\"/tmp/rhubarb\",\"contents\":\"Zm9v\"}]}}",
request.getPayload().getRawContent());
}
@Test
public void testWithMetadata() {
}
} }

View File

@ -1,56 +1,46 @@
{ {
"server" : { "server" : {
"id" : 1234, "id" : 1234,
"name" : "sample-server", "name" : "sample-server",
"imageRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234", "imageRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/images/1234",
"flavorRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1", "flavorRef" : "https://servers.api.rackspacecloud.com/v1.1/32278/flavors/1",
"updated" : "2010-10-10T12:00:00Z", "updated" : "2010-10-10T12:00:00Z",
"created" : "2010-08-10T12:00:00Z", "created" : "2010-08-10T12:00:00Z",
"hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0", "hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0",
"affinityId" : "fc88bcf8394db9c8d0564e08ca6a9724188a84d1", "affinityId" : "fc88bcf8394db9c8d0564e08ca6a9724188a84d1",
"status" : "BUILD", "status" : "BUILD",
"progress" : 60, "progress" : 60,
"addresses" : { "addresses" : {
"values" : [ "public": [
{ {"version" : 4, "addr" : "67.23.10.132"},
"id" : "public", {"version" : 6, "addr" : "::babe:67.23.10.132"},
"values" : [ {"version" : 4, "addr" : "67.23.10.131"},
{"version" : 4, "addr" : "67.23.10.132"}, {"version" : 6, "addr" : "::babe:4317:0A83"}
{"version" : 6, "addr" : "::babe:67.23.10.132"}, ],
{"version" : 4, "addr" : "67.23.10.131"}, "private" : [
{"version" : 6, "addr" : "::babe:4317:0A83"} {"version" : 4, "addr" : "10.176.42.16"},
] {"version" : 6, "addr" : "::babe:10.176.42.16"}
}, ]
{ },
"id" : "private", "metadata" : {
"values" : [ "Server Label" : "Web Head 1",
{"version" : 4, "addr" : "10.176.42.16"}, "Image Version" : "2.1"
{"version" : 6, "addr" : "::babe:10.176.42.16"} },
] "links": [
} {
] "rel" : "self",
}, "href" : "http://servers.api.openstack.org/v1.1/1234/servers/1234"
"metadata" : { },
"values" : { {
"Server Label" : "Web Head 1", "rel" : "bookmark",
"Image Version" : "2.1" "type" : "application/vnd.openstack.compute-v1.1+xml",
} "href" : "http://servers.api.openstack.org/1234/servers/1234"
}, },
"links": [ {
{ "rel" : "bookmark",
"rel" : "self", "type" : "application/vnd.openstack.compute-v1.1+json",
"href" : "http://servers.api.openstack.org/v1.1/1234/servers/1234" "href" : "http://servers.api.openstack.org/1234/servers/1234"
}, }
{ ]
"rel" : "bookmark", }
"type" : "application/vnd.openstack.compute-v1.1+xml", }
"href" : "http://servers.api.openstack.org/1234/servers/1234"
},
{
"rel" : "bookmark",
"type" : "application/vnd.openstack.compute-v1.1+json",
"href" : "http://servers.api.openstack.org/1234/servers/1234"
}
]
}
}