Nova client changes for delegate APIs & misc cleanup

This commit is contained in:
Jeremy Daggett 2012-02-28 09:31:35 -08:00
parent 8296d47e96
commit ec990e6672
14 changed files with 1045 additions and 67 deletions

View File

@ -26,6 +26,8 @@ import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
@ -77,4 +79,19 @@ public interface NovaAsyncClient {
@Delegate
FloatingIPClient getFloatingIPClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Security Group features.
*/
@Delegate
SecurityGroupClient getSecurityGroupClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Key Pair features.
*/
@Delegate
KeyPairClient getKeyPairClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -28,6 +28,8 @@ import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.features.ServerClient;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
@ -79,4 +81,19 @@ public interface NovaClient {
@Delegate
FloatingIPClient getFloatingIPClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides synchronous access to Security Group features.
*/
@Delegate
SecurityGroupClient getSecurityGroupClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides synchronous access to Key Pair features.
*/
@Delegate
KeyPairClient getKeyPairClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -34,6 +34,10 @@ import org.jclouds.openstack.nova.v1_1.features.FloatingIPAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.KeyPairAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.features.SecurityGroupAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ServerClient;
import org.jclouds.openstack.nova.v1_1.handlers.NovaErrorHandler;
@ -56,6 +60,8 @@ public class NovaRestClientModule extends RestClientModule<NovaClient, NovaAsync
.put(FlavorClient.class, FlavorAsyncClient.class)
.put(ImageClient.class, ImageAsyncClient.class)
.put(FloatingIPClient.class, FloatingIPAsyncClient.class)
.put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)
.put(KeyPairClient.class, KeyPairAsyncClient.class)
.build();
public NovaRestClientModule() {

View File

@ -26,9 +26,9 @@ import java.util.Set;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.ImageStatus;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
/**
* An image is a collection of files you use to create or rebuild a server.
@ -51,18 +51,17 @@ public class Image extends Resource {
public static class Builder extends Resource.Builder {
private ImageStatus status;
private Date updated;
private Date created;
private String tenantId;
private String userId;
private ImageStatus status;
private int progress;
private String serverRef;
private int minDisk;
private int minRam;
private Resource server;
private Map<String, String> metadata = Maps.newHashMap();
public Builder status(ImageStatus status) {
this.status = status;
return this;
}
public Builder updated(Date updated) {
this.updated = updated;
return this;
@ -73,13 +72,38 @@ public class Image extends Resource {
return this;
}
public Builder tenantId(String tenantId) {
this.tenantId = tenantId;
return this;
}
public Builder userId(String userId) {
this.userId = userId;
return this;
}
public Builder status(ImageStatus status) {
this.status = status;
return this;
}
public Builder progress(int progress) {
this.progress = progress;
return this;
}
public Builder serverRef(String serverRef) {
this.serverRef = serverRef;
public Builder minDisk(int minDisk) {
this.minDisk = minDisk;
return this;
}
public Builder minRam(int minRam) {
this.minRam = minRam;
return this;
}
public Builder server(Resource server) {
this.server = server;
return this;
}
@ -89,14 +113,14 @@ public class Image extends Resource {
}
public Image build() {
return new Image(id, name, links, status, updated, created, progress,
serverRef, metadata);
return new Image(id, name, links, updated, created, tenantId, userId,
status, progress, minDisk, minRam, server, metadata);
}
public Builder fromImage(Image in) {
return fromResource(in).status(in.getStatus())
.updated(in.getUpdated()).created(in.getCreated())
.progress(in.getProgress()).serverRef(in.getServerRef())
.progress(in.getProgress()).server(in.getServer())
.metadata(in.getMetadata());
}
@ -133,29 +157,36 @@ public class Image extends Resource {
}
}
private ImageStatus status;
private Date updated;
private Date created;
@SerializedName("tenant_id")
private String tenantId;
@SerializedName("user_id")
private String userId;
private ImageStatus status;
private int progress;
private String serverRef;
private int minDisk;
private int minRam;
private Resource server;
private Map<String, String> metadata = Maps.newHashMap();
protected Image(String id, String name, Set<Link> links, ImageStatus status,
Date updated, Date created, int progress, String serverRef,
protected Image(String id, String name, Set<Link> links, Date updated,
Date created, String tenantId, String userId, ImageStatus status,
int progress, int minDisk, int minRam, Resource server,
Map<String, String> metadata) {
super(id, name, links);
this.status = status;
this.updated = updated;
this.created = created;
this.tenantId = tenantId;
this.userId = userId;
this.status = status;
this.progress = progress;
this.serverRef = serverRef;
this.minDisk = minDisk;
this.minRam = minRam;
this.server = server;
this.metadata = metadata;
}
public ImageStatus getStatus() {
return this.status;
}
public Date getUpdated() {
return this.updated;
}
@ -163,25 +194,47 @@ public class Image extends Resource {
public Date getCreated() {
return this.created;
}
public String getTenantId() {
return this.tenantId;
}
public String getUserId() {
return this.userId;
}
public ImageStatus getStatus() {
return this.status;
}
public int getProgress() {
return this.progress;
}
public String getServerRef() {
return this.serverRef;
public int getMinDisk() {
return this.minDisk;
}
public int getMinRam() {
return this.minRam;
}
public Resource getServer() {
return this.server;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
@Override
public String toString() {
return toStringHelper("").add("id", id).add("name", name)
.add("links", links).add("status", status).add("updated", updated)
.add("created", created).add("progress", progress).add("serverRef", serverRef)
.add("links", links).add("updated", updated)
.add("created", created).add("tenantId", tenantId)
.add("userId", userId).add("status", status)
.add("progress", progress).add("minDisk", minDisk)
.add("minRam", minRam).add("server", server)
.add("metadata", metadata).toString();
}

View File

@ -0,0 +1,37 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.domain;
/**
*
* @author Adrian Cole
*/
public enum RebootType {
HARD, SOFT;
public String value() {
return name();
}
public static RebootType fromValue(String v) {
return valueOf(v);
}
}

View File

@ -21,6 +21,7 @@ package org.jclouds.openstack.nova.v1_1.domain;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import java.util.Map;
import java.util.Set;
@ -29,14 +30,17 @@ import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.Address.Type;
import org.jclouds.util.Multimaps2;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.gson.annotations.SerializedName;
/**
* A server is a virtual machine instance in the compute system. Flavor and image are requisite
* elements when creating a server.
* A server is a virtual machine instance in the compute system. Flavor and
* image are requisite elements when creating a server.
*
* @author Adrian Cole
* @see <a href=
@ -53,13 +57,124 @@ public class Server extends Resource {
}
public static class Builder extends Resource.Builder {
private Multimap<Address.Type, Address> addresses = LinkedHashMultimap.create();
private String tenantId;
private String userId;
private Date updated;
private Date created;
private String hostId;
private String accessIPv4;
private String accessIPv6;
private ServerStatus status;
private int progress;
private Resource image;
private Resource flavor;
private Map<String, String> metadata = Maps.newHashMap();
// TODO: get gson multimap ad
private Multimap<Address.Type, Address> addresses = LinkedHashMultimap
.create();
/**
* @see Server#getTenantId()
*/
public Builder tenantId(String tenantId) {
this.tenantId = tenantId;
return this;
}
/**
* @see Server#getUserId()
*/
public Builder userId(String userId) {
this.userId = userId;
return this;
}
/**
* @see Server#getUpdated()
*/
public Builder updated(Date updated) {
this.updated = updated;
return this;
}
/**
* @see Server#getCreated()
*/
public Builder created(Date created) {
this.created = created;
return this;
}
/**
* @see Server#getHostId()
*/
public Builder hostId(String hostId) {
this.hostId = hostId;
return this;
}
/**
* @see Server#getAccessIPv4()
*/
public Builder accessIPv4(String accessIPv4) {
this.accessIPv4 = accessIPv4;
return this;
}
/**
* @see Server#getAccessIPv6()
*/
public Builder accessIPv6(String accessIPv6) {
this.accessIPv6 = accessIPv6;
return this;
}
/**
* @see Server#getStatus()
*/
public Builder status(ServerStatus status) {
this.status = status;
return this;
}
/**
* @see Server#getProgress()
*/
public Builder progress(int progress) {
this.progress = progress;
return this;
}
/**
* @see Server#getImage()
*/
public Builder image(Resource image) {
this.image = image;
return this;
}
/**
* @see Server#getImage()
*/
public Builder flavor(Resource flavor) {
this.flavor = flavor;
return this;
}
/**
* @see Server#getMetadata()
*/
public Builder metadata(Map<String, String> metadata) {
this.metadata = ImmutableMap.copyOf(metadata);
return this;
}
/**
* @see Server#getAddresses()
*/
public Builder addresses(Multimap<Address.Type, Address> addresses) {
this.addresses = ImmutableMultimap.copyOf(checkNotNull(addresses, "addresses"));
this.addresses = ImmutableMultimap.copyOf(checkNotNull(addresses,
"addresses"));
return this;
}
@ -67,15 +182,16 @@ public class Server extends Resource {
* @see Server#getPrivateAddresses()
*/
public Builder privateAddresses(Address... privateAddresses) {
return privateAddresses(ImmutableSet.copyOf(checkNotNull(privateAddresses, "privateAddresses")));
return privateAddresses(ImmutableSet.copyOf(checkNotNull(
privateAddresses, "privateAddresses")));
}
/**
* @see Server#getPrivateAddresses()
*/
public Builder privateAddresses(Set<Address> privateAddresses) {
this.addresses.replaceValues(Address.Type.PRIVATE, ImmutableSet.copyOf(checkNotNull(privateAddresses,
"privateAddresses")));
this.addresses.replaceValues(Address.Type.PRIVATE, ImmutableSet
.copyOf(checkNotNull(privateAddresses, "privateAddresses")));
return this;
}
@ -83,20 +199,24 @@ public class Server extends Resource {
* @see Server#getPublicAddresses()
*/
public Builder publicAddresses(Address... publicAddresses) {
return publicAddresses(ImmutableSet.copyOf(checkNotNull(publicAddresses, "publicAddresses")));
return publicAddresses(ImmutableSet.copyOf(checkNotNull(
publicAddresses, "publicAddresses")));
}
/**
* @see Server#getPublicAddresses()
*/
public Builder publicAddresses(Set<Address> publicAddresses) {
this.addresses.replaceValues(Address.Type.PUBLIC, ImmutableSet.copyOf(checkNotNull(publicAddresses,
"publicAddresses")));
this.addresses.replaceValues(Address.Type.PUBLIC, ImmutableSet
.copyOf(checkNotNull(publicAddresses, "publicAddresses")));
return this;
}
public Server build() {
return new Server(id, name, links, addresses);
// return new Server(id, name, links, addresses);
return new Server(id, name, links, tenantId, userId, updated,
created, hostId, accessIPv4, accessIPv6, status, progress,
image, flavor, addresses, metadata);
}
public Builder fromServer(Server in) {
@ -135,15 +255,101 @@ public class Server extends Resource {
return Builder.class.cast(super.fromResource(in));
}
}
@SerializedName("tenant_id")
protected String tenantId;
@SerializedName("user_id")
protected String userId;
protected Date updated;
protected Date created;
protected String hostId;
protected String accessIPv4;
protected String accessIPv6;
protected ServerStatus status;
protected int progress;
protected Resource image;
protected Resource flavor;
// TODO: get gson multimap adapter!
protected final Map<Address.Type, Set<Address>> addresses;
protected Map<String, String> metadata;
protected String adminPass;
protected Server(String id, String name, Set<Link> links, Multimap<Address.Type, Address> addresses) {
protected Server(String id, String name, Set<Link> links, String tenantId,
String userId, Date updated, Date created, String hostId,
String accessIPv4, String accessIPv6, ServerStatus status,
int progress, Resource image, Resource flavor,
Multimap<Address.Type, Address> addresses, Map<String, String> metadata) {
super(id, name, links);
this.addresses = Multimaps2.toOldSchool(ImmutableMultimap.copyOf(checkNotNull(addresses, "addresses")));
this.tenantId = tenantId;
this.userId = userId;
this.updated = updated;
this.created = created;
this.hostId = hostId;
this.accessIPv4 = accessIPv4;
this.accessIPv6 = accessIPv6;
this.status = status;
this.progress = progress;
this.image = image;
this.flavor = flavor;
this.metadata = Maps.newHashMap(metadata);
this.addresses = Multimaps2.toOldSchool(ImmutableMultimap
.copyOf(checkNotNull(addresses, "addresses")));
}
public String getTenantId() {
return this.tenantId;
}
public String getUserId() {
return this.userId;
}
public Date getUpdated() {
return this.updated;
}
public Date getCreated() {
return this.created;
}
public String getHostId() {
return this.hostId;
}
public String getAccessIPv4() {
return this.accessIPv4;
}
public String getAccessIPv6() {
return this.accessIPv6;
}
public ServerStatus getStatus() {
return this.status;
}
public int getProgress() {
return this.progress;
}
public Resource getImage() {
return this.image;
}
public Resource getFlavor() {
return this.flavor;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
public String getAdminPass() {
return this.adminPass;
}
/**
* @return the private ip addresses assigned to the server
*/
@ -167,8 +373,14 @@ public class Server extends Resource {
@Override
public String toString() {
return toStringHelper("").add("id", id).add("name", name).add("links", links).add("addresses", addresses)
.toString();
return toStringHelper("").add("id", id).add("name", name)
.add("tenantId", tenantId).add("userId", userId).add("hostId", hostId)
.add("updated", updated).add("created", created)
.add("accessIPv4", accessIPv4).add("accessIPv6", accessIPv6)
.add("status", status).add("progress", progress)
.add("image", image).add("flavor", flavor)
.add("metadata", metadata)
.add("links", links).add("addresses", addresses).toString();
}
}

View File

@ -0,0 +1,46 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.domain;
/**
* Servers contain a status attribute that can be used as an indication of the
* current server state. Servers with an ACTIVE status are available for use.
*
* Other possible values for the status attribute include: BUILD, REBUILD,
* SUSPENDED, RESIZE, VERIFY_RESIZE, REVERT_RESIZE, PASSWORD, REBOOT,
* HARD_REBOOT, DELETED, UNKNOWN, and ERROR.
*
* @author Adrian Cole
*/
public enum ServerStatus {
ACTIVE, BUILD, REBUILD, SUSPENDED, RESIZE, VERIFY_RESIZE, REVERT_RESIZE, PASSWORD, REBOOT, HARD_REBOOT, DELETED, UNKNOWN, ERROR, UNRECOGNIZED;
public String value() {
return name();
}
public static ServerStatus fromValue(String v) {
try {
return valueOf(v);
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -21,19 +21,32 @@ package org.jclouds.openstack.nova.v1_1.features;
import java.util.Set;
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.domain.Resource;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
import org.jclouds.openstack.nova.v1_1.options.RebuildServerOptions;
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.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
@ -81,4 +94,94 @@ public interface ServerAsyncClient {
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<Server> getServer(@PathParam("id") String id);
/**
* @see NovaClient#deleteServer
*/
@DELETE
@Consumes
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
@Path("/servers/{id}")
ListenableFuture<Boolean> deleteServer(@PathParam("id") String id);
/**
* @see NovaClient#rebootServer
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"reboot\":%7B\"type\":\"{type}\"%7D%7D")
ListenableFuture<Void> rebootServer(@PathParam("id") String id, @PayloadParam("type") RebootType rebootType);
/**
* @see NovaClient#resizeServer
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"resize\":%7B\"flavorId\":{flavorId}%7D%7D")
ListenableFuture<Void> resizeServer(@PathParam("id") String id, @PayloadParam("flavorId") String flavorId);
/**
* @see NovaClient#confirmResizeServer
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"confirmResize\":null}")
ListenableFuture<Void> confirmResizeServer(@PathParam("id") String id);
/**
* @see NovaClient#revertResizeServer
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("{\"revertResize\":null}")
ListenableFuture<Void> revertResizeServer(@PathParam("id") String id);
/**
* @see NovaClient#createServer
*/
@POST
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@Path("/servers")
@MapBinder(CreateServerOptions.class)
ListenableFuture<Server> createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef,
@PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options);
/**
* @see NovaClient#rebuildServer
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@MapBinder(RebuildServerOptions.class)
ListenableFuture<Void> rebuildServer(@PathParam("id") String id, RebuildServerOptions... options);
/**
* @see NovaClient#changeAdminPass
*/
@POST
@Path("/servers/{id}/action")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"changePassword\":%7B\"adminPass\":\"{adminPass}\"%7D%7D")
ListenableFuture<Void> changeAdminPass(@PathParam("id") String id, @PayloadParam("adminPass") String adminPass);
/**
* @see NovaClient#renameServer
*/
@PUT
@Path("/servers/{id}")
@Consumes
@Produces(MediaType.APPLICATION_JSON)
@Payload("%7B\"server\":%7B\"name\":\"{name}\"%7D%7D")
ListenableFuture<Void> renameServer(@PathParam("id") String id, @PayloadParam("name") String newName);
}

View File

@ -21,9 +21,30 @@ package org.jclouds.openstack.nova.v1_1.features;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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.concurrent.Timeout;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
import org.jclouds.openstack.nova.v1_1.options.RebuildServerOptions;
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.Unwrap;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides synchronous access to Server.
@ -60,4 +81,50 @@ public interface ServerClient {
*/
Server getServer(String id);
/**
* @see NovaClient#createServer
*/
Server createServer(String name, String imageRef,
String flavorRef, CreateServerOptions... options);
/**
* @see NovaClient#deleteServer
*/
Boolean deleteServer(String id);
/**
* @see NovaClient#rebootServer
*/
void rebootServer(String id, RebootType rebootType);
/**
* @see NovaClient#resizeServer
*/
void resizeServer(String id, String flavorId);
/**
* @see NovaClient#confirmResizeServer
*/
void confirmResizeServer(String id);
/**
* @see NovaClient#revertResizeServer
*/
void revertResizeServer(String id);
/**
* @see NovaClient#rebuildServer
*/
void rebuildServer(String id, RebuildServerOptions... options);
/**
* @see NovaClient#changeAdminPass
*/
void changeAdminPass(String id, String adminPass);
/**
* @see NovaClient#renameServer
*/
void renameServer(String id, String newName);
}

View File

@ -0,0 +1,245 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.encryption.internal.Base64;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
/**
*
* @author Adrian Cole
*
*/
public class CreateServerOptions implements MapBinder {
@Inject
private BindToJsonPayload jsonBinder;
static class File {
private final String path;
private final String contents;
public File(String path, byte[] contents) {
this.path = checkNotNull(path, "path");
this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
checkArgument(path.getBytes().length < 255, String.format(
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path.getBytes().length));
checkArgument(contents.length < 10 * 1024, String.format(
"maximum size of the file is 10KB. Contents specified is %d bytes", contents.length));
}
public String getContents() {
return contents;
}
public String getPath() {
return path;
}
}
@SuppressWarnings("unused")
private class ServerRequest {
final String name;
final String imageRef;
final String flavorRef;
String adminPass;
Map<String, String> metadata;
List<File> personality;
String key_name;
@SerializedName(value="security_groups")
Set<SecurityGroup> securityGroups;
private ServerRequest(String name, String imageRef, String flavorRef) {
this.name = name;
this.imageRef = imageRef;
this.flavorRef = flavorRef;
}
}
private Map<String, String> metadata = Maps.newHashMap();
private List<File> files = Lists.newArrayList();
private Set<String> securityGroups = Sets.newHashSet();
private String keyName;
private String adminPass;
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"),
checkNotNull(postParams.get("imageRef"), "imageRef parameter not present"), checkNotNull(postParams
.get("flavorRef"), "flavorRef parameter not present"));
if (metadata.size() > 0)
server.metadata = metadata;
if (files.size() > 0)
server.personality = files;
if (keyName != null)
server.key_name = keyName;
if (securityGroups.size() > 0) {
server.securityGroups = Sets.newHashSet();
for (String groupName : securityGroups) {
SecurityGroup group = SecurityGroup.builder().name(groupName).build();
server.securityGroups.add(group);
}
}
if (adminPass != null) {
server.adminPass = adminPass;
}
return bindToRequest(request, ImmutableMap.of("server", server));
}
/**
* You may further customize a cloud server by injecting data into the file system of the cloud
* server itself. This is useful, for example, for inserting ssh keys, setting configuration
* files, or storing data that you want to retrieve from within the instance itself. It is
* intended to provide a minimal amount of launch-time personalization. If significant
* customization is required, a custom image should be created. The max size of the file path
* data is 255 bytes while the max size of the file contents is 10KB. Note that the file contents
* should be encoded as a Base64 string and the 10KB limit refers to the number of bytes in the
* decoded data not the number of characters in the encoded data. The maximum number of file
* path/content pairs that can be supplied is 5. Any existing files that match the specified file
* will be renamed to include the extension bak followed by a time stamp. For example, the file
* /etc/passwd will be backed up as /etc/passwd.bak.1246036261.5785. All files will have root and
* the root group as owner and group owner, respectively and will allow user and group read
* access only (-r--r-----).
*/
public CreateServerOptions withFile(String path, byte[] contents) {
checkState(files.size() < 5, "maximum number of files allowed is 5");
files.add(new File(path, contents));
return this;
}
public CreateServerOptions withAdminPass(String adminPass) {
checkNotNull(adminPass, "adminPass");
this.adminPass = adminPass;
return this;
}
/**
* Custom cloud server metadata can also be supplied at launch time. This metadata is stored in
* the API system where it is retrievable by querying the API for server status. The maximum size
* of the metadata key and value is each 255 bytes and the maximum number of key-value pairs that
* can be supplied per server is 5.
*/
public CreateServerOptions withMetadata(Map<String, String> metadata) {
checkNotNull(metadata, "metadata");
checkArgument(metadata.size() <= 5, "you cannot have more then 5 metadata values. You specified: "
+ metadata.size());
for (Entry<String, String> entry : metadata.entrySet()) {
checkArgument(entry.getKey().getBytes().length < 255, String.format(
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes", entry.getKey(), entry
.getKey().getBytes().length));
checkArgument(entry.getKey().getBytes().length < 255, String.format(
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes", entry
.getKey(), entry.getValue(), entry.getValue().getBytes().length));
}
this.metadata = metadata;
return this;
}
/**
* A keypair name can be defined when creating a server. This key will be
* linked to the server and used to SSH connect to the machine
*
* @param keyName
* @return
*/
public CreateServerOptions withKeyName(String keyName) {
checkNotNull(keyName, "keyName");
this.keyName = keyName;
return this;
}
/**
* Defines the security group name to be used when creating a server.
*
* @param groupName
* @return
*/
public CreateServerOptions withSecurityGroup(String groupName) {
checkNotNull(groupName, "groupName");
this.securityGroups.add(groupName);
return this;
}
public static class Builder {
/**
* @see CreateServerOptions#withFile(String,byte [])
*/
public static CreateServerOptions withFile(String path, byte[] contents) {
CreateServerOptions options = new CreateServerOptions();
return options.withFile(path, contents);
}
public static CreateServerOptions withAdminPass(String adminPass) {
CreateServerOptions options = new CreateServerOptions();
return options.withAdminPass(adminPass);
}
/**
* @see CreateServerOptions#withMetadata(Map<String, String>)
*/
public static CreateServerOptions withMetadata(Map<String, String> metadata) {
CreateServerOptions options = new CreateServerOptions();
return options.withMetadata(metadata);
}
/**
* @see CreateServerOptions#withKeyName(String)
*/
public static CreateServerOptions withKeyName(String keyName) {
CreateServerOptions options = new CreateServerOptions();
return options.withKeyName(keyName);
}
/**
* @see CreateServerOptions#withGroupName(String)
*/
public static CreateServerOptions withSecurityGroup(String name) {
CreateServerOptions options = new CreateServerOptions();
return options.withSecurityGroup(name);
}
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
return jsonBinder.bindToRequest(request, input);
}
}

View File

@ -0,0 +1,109 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.options;
import java.util.Date;
import org.jclouds.openstack.options.BaseListOptions;
/**
* Options used to control the amount of detail in the request.
*
* @see BaseListOptions
* @see <a href="http://wiki.openstack.org/OpenStackAPI_1-1" />
* @author Adrian Cole
*/
public class ListOptions extends BaseListOptions {
public static final ListOptions NONE = new ListOptions();
/**
* unless used, only the name and id will be returned per row.
*
* @return
*/
public ListOptions withDetails() {
this.pathSuffix = "/detail";
return this;
}
/**
* {@inheritDoc}
*/
@Override
public ListOptions changesSince(Date ifModifiedSince) {
super.changesSince(ifModifiedSince);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public ListOptions maxResults(int limit) {
super.maxResults(limit);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public ListOptions startAt(long offset) {
super.startAt(offset);
return this;
}
public static class Builder {
/**
* @see ListOptions#withDetails()
*/
public static ListOptions withDetails() {
ListOptions options = new ListOptions();
return options.withDetails();
}
/**
* @see BaseListOptions#startAt(long)
*/
public static ListOptions startAt(long prefix) {
ListOptions options = new ListOptions();
return options.startAt(prefix);
}
/**
* @see BaseListOptions#maxResults(long)
*/
public static ListOptions maxResults(int maxKeys) {
ListOptions options = new ListOptions();
return options.maxResults(maxKeys);
}
/**
* @see BaseListOptions#changesSince(Date)
*/
public static ListOptions changesSince(Date since) {
ListOptions options = new ListOptions();
return options.changesSince(since);
}
}
}

View File

@ -0,0 +1,80 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
/**
*
*
* @author Adrian Cole
*
*/
public class RebuildServerOptions implements MapBinder {
@Inject
private BindToJsonPayload jsonBinder;
String imageRef;
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
Map<String, String> image = Maps.newHashMap();
if (imageRef != null)
image.put("imageRef", imageRef);
return jsonBinder.bindToRequest(request, ImmutableMap.of("rebuild", image));
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
throw new IllegalStateException("RebuildServer is a POST operation");
}
/**
* @param ref
* - reference of the image to rebuild the server with.
*/
public RebuildServerOptions withImage(String ref) {
checkNotNull(ref, "image reference should not be null");
checkArgument(!ref.isEmpty(), "image reference should not be empty");
this.imageRef = ref;
return this;
}
public static class Builder {
/**
* @see RebuildServerOptions#withImage(String)
*/
public static RebuildServerOptions withImage(String ref) {
RebuildServerOptions options = new RebuildServerOptions();
return options.withImage(ref);
}
}
}

View File

@ -73,22 +73,4 @@ public class ServerClientExpectTest extends BaseNovaRestClientExpectTest {
assertTrue(clientWhenNoServersExist.getServerClientForRegion("North").listServers().isEmpty());
}
// TODO: gson deserializer for Multimap
public void testGetServerWhenResponseIs2xx() throws Exception {
HttpRequest listServers = HttpRequest.builder().method("GET").endpoint(
URI.create("https://compute.north.host/v1.1/3456/servers/foo")).headers(
ImmutableMultimap.<String, String> builder().put("Accept", "application/json").put("X-Auth-Token",
authToken).build()).build();
HttpResponse listServersResponse = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/server_details.json")).build();
NovaClient clientWhenServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey,
responseWithKeystoneAccess, listServers, listServersResponse);
assertEquals(clientWhenServersExist.getServerClientForRegion("North").getServer("foo").toString(),
new ParseServerTest().expected().toString());
}
}

View File

@ -10,6 +10,10 @@
"progress" : 80,
"minDisk" : 5,
"minRam" : 256,
"metadata" : {
"ImageType" : "Gold",
"ImageVersion" : "1.5"
},
"server" : {
"id": "52415800-8b69-11e0-9b19-734f335aa7b3",
"links": [