mirror of https://github.com/apache/jclouds.git
Merge pull request #965 from rackspace/openstack-nova-volume-attachment-api
OpenStack Nova Volume Attachment API
This commit is contained in:
commit
d557b581e3
|
@ -25,19 +25,20 @@ import org.jclouds.concurrent.Timeout;
|
|||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.location.Zone;
|
||||
import org.jclouds.location.functions.ZoneToEndpoint;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.FlavorApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.ImageApi;
|
||||
|
@ -131,13 +132,6 @@ public interface NovaApi {
|
|||
Optional<? extends SimpleTenantUsageApi> getSimpleTenantUsageExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Volume features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeApi> getVolumeExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Virtual Interface features.
|
||||
*/
|
||||
|
@ -187,6 +181,20 @@ public interface NovaApi {
|
|||
Optional<? extends QuotaClassApi> getQuotaClassExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Volume features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeApi> getVolumeExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Volume Attachment features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeAttachmentApi> getVolumeAttachmentExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Volume Type features.
|
||||
*/
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.Set;
|
|||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.location.Zone;
|
||||
import org.jclouds.location.functions.ZoneToEndpoint;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationAsyncApi;
|
||||
|
@ -32,10 +31,12 @@ import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi;
|
|||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi;
|
||||
|
@ -129,13 +130,6 @@ public interface NovaAsyncApi {
|
|||
Optional<? extends SimpleTenantUsageAsyncApi> getSimpleTenantUsageExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Volume features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeAsyncApi> getVolumeExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Virtual Interface features.
|
||||
*/
|
||||
|
@ -186,6 +180,20 @@ public interface NovaAsyncApi {
|
|||
Optional<? extends QuotaClassAsyncApi> getQuotaClassExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Volume features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeAsyncApi> getVolumeExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Volume features.
|
||||
*/
|
||||
@Delegate
|
||||
Optional<? extends VolumeAttachmentAsyncApi> getVolumeAttachmentExtensionForZone(
|
||||
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Volume Type features.
|
||||
*/
|
||||
|
|
|
@ -32,8 +32,6 @@ import org.jclouds.http.annotation.Redirection;
|
|||
import org.jclouds.http.annotation.ServerError;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi;
|
||||
|
@ -51,6 +49,8 @@ import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi;
|
|||
import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerAdminAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageApi;
|
||||
|
@ -59,6 +59,8 @@ import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi;
|
|||
import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.FlavorApi;
|
||||
|
@ -103,7 +105,6 @@ public class NovaRestClientModule<S extends NovaApi, A extends NovaAsyncApi> ext
|
|||
.put(KeyPairApi.class, KeyPairAsyncApi.class)
|
||||
.put(HostAdministrationApi.class, HostAdministrationAsyncApi.class)
|
||||
.put(SimpleTenantUsageApi.class, SimpleTenantUsageAsyncApi.class)
|
||||
.put(VolumeApi.class, VolumeAsyncApi.class)
|
||||
.put(VirtualInterfaceApi.class, VirtualInterfaceAsyncApi.class)
|
||||
.put(ServerWithSecurityGroupsApi.class, ServerWithSecurityGroupsAsyncApi.class)
|
||||
.put(ServerAdminApi.class, ServerAdminAsyncApi.class)
|
||||
|
@ -111,6 +112,8 @@ public class NovaRestClientModule<S extends NovaApi, A extends NovaAsyncApi> ext
|
|||
.put(FlavorExtraSpecsApi.class, FlavorExtraSpecsAsyncApi.class)
|
||||
.put(QuotaApi.class, QuotaAsyncApi.class)
|
||||
.put(QuotaClassApi.class, QuotaClassAsyncApi.class)
|
||||
.put(VolumeApi.class, VolumeAsyncApi.class)
|
||||
.put(VolumeAttachmentApi.class, VolumeAttachmentAsyncApi.class)
|
||||
.put(VolumeTypeApi.class, VolumeTypeAsyncApi.class)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -83,29 +83,37 @@ public interface VolumeApi {
|
|||
* List volume attachments for a given instance.
|
||||
*
|
||||
* @return all Floating IPs
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#listAttachmentsOnServer(String)
|
||||
*/
|
||||
FluentIterable<? extends VolumeAttachment> listAttachmentsOnServer(String serverId);
|
||||
@Deprecated FluentIterable<? extends VolumeAttachment> listAttachmentsOnServer(String serverId);
|
||||
|
||||
/**
|
||||
* Get a specific attached volume.
|
||||
*
|
||||
* @return data about the given volume attachment.
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#getAttachmentForVolumeOnServer(String, String)
|
||||
*/
|
||||
VolumeAttachment getAttachmentForVolumeOnServer(String volumeId, String serverId);
|
||||
@Deprecated VolumeAttachment getAttachmentForVolumeOnServer(String volumeId, String serverId);
|
||||
|
||||
/**
|
||||
* Attach a volume to an instance
|
||||
*
|
||||
* @return data about the new volume attachment
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#attachVolumeToServerAsDevice(String, String, String)
|
||||
*/
|
||||
VolumeAttachment attachVolumeToServerAsDevice(String volumeId, String serverId, String device);
|
||||
@Deprecated VolumeAttachment attachVolumeToServerAsDevice(String volumeId, String serverId, String device);
|
||||
|
||||
/**
|
||||
* Detach a Volume from an instance.
|
||||
*
|
||||
* @return true if successful
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#detachVolumeFromServer(String, String)
|
||||
*/
|
||||
Boolean detachVolumeFromServer(String server_id, String volumeId);
|
||||
@Deprecated Boolean detachVolumeFromServer(String server_id, String volumeId);
|
||||
|
||||
/**
|
||||
* Returns a summary list of snapshots.
|
||||
|
|
|
@ -126,31 +126,37 @@ public interface VolumeAsyncApi {
|
|||
* List volume attachments for a given instance.
|
||||
*
|
||||
* @return all Floating IPs
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#listAttachmentsOnServer(String)
|
||||
*/
|
||||
@GET
|
||||
@Path("/servers/{server_id}/os-volume_attachments")
|
||||
@SelectJson("volumeAttachments")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends FluentIterable<? extends VolumeAttachment>> listAttachmentsOnServer(@PathParam("server_id") String serverId);
|
||||
@Deprecated ListenableFuture<? extends FluentIterable<? extends VolumeAttachment>> listAttachmentsOnServer(@PathParam("server_id") String serverId);
|
||||
|
||||
/**
|
||||
* Get a specific attached volume.
|
||||
*
|
||||
* @return data about the given volume attachment.
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#getAttachmentForVolumeOnServer(String, String)
|
||||
*/
|
||||
@GET
|
||||
@Path("/servers/{server_id}/os-volume_attachments/{id}")
|
||||
@SelectJson("volumeAttachment")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends VolumeAttachment> getAttachmentForVolumeOnServer(@PathParam("id") String volumeId,
|
||||
@Deprecated ListenableFuture<? extends VolumeAttachment> getAttachmentForVolumeOnServer(@PathParam("id") String volumeId,
|
||||
@PathParam("server_id") String serverId);
|
||||
|
||||
/**
|
||||
* Attach a volume to an instance
|
||||
*
|
||||
* @return the new Attachment
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#attachVolumeToServerAsDevice(String, String, String)
|
||||
*/
|
||||
@POST
|
||||
@Path("/servers/{server_id}/os-volume_attachments")
|
||||
|
@ -158,19 +164,21 @@ public interface VolumeAsyncApi {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@WrapWith("volumeAttachment")
|
||||
ListenableFuture<? extends VolumeAttachment> attachVolumeToServerAsDevice(@PayloadParam("volumeId") String volumeId,
|
||||
@Deprecated ListenableFuture<? extends VolumeAttachment> attachVolumeToServerAsDevice(@PayloadParam("volumeId") String volumeId,
|
||||
@PathParam("server_id") String serverId, @PayloadParam("device") String device);
|
||||
|
||||
/**
|
||||
* Detach a Volume from an instance.
|
||||
*
|
||||
* @return true if successful
|
||||
* @deprecated To be removed in jclouds 1.7
|
||||
* @see VolumeAttachmentApi#detachVolumeFromServer(String, String)
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/servers/{server_id}/os-volume_attachments/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
|
||||
ListenableFuture<Boolean> detachVolumeFromServer(@PathParam("id") String volumeId, @PathParam("server_id") String serverId);
|
||||
@Deprecated ListenableFuture<Boolean> detachVolumeFromServer(@PathParam("id") String volumeId, @PathParam("server_id") String serverId);
|
||||
|
||||
/**
|
||||
* Returns a summary list of snapshots.
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* 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.v2_0.extensions;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jclouds.concurrent.Timeout;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
|
||||
import org.jclouds.openstack.v2_0.ServiceType;
|
||||
import org.jclouds.openstack.v2_0.services.Extension;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Volume Attachments.
|
||||
*
|
||||
* This API strictly handles attaching Volumes to Servers. To create and manage Volumes you need to use one of the
|
||||
* following APIs:
|
||||
*
|
||||
* 1. The Cinder API
|
||||
* If your OpenStack deployment is Folsom or later and it supports the Cinder block storage service, use this API.
|
||||
* @see org.jclouds.openstack.cinder.v1.features.VolumeApi
|
||||
*
|
||||
* 2. The nova-volume API
|
||||
* If your OpenStack deployment is Essex or earlier and it supports the nova-volume extension, use this API.
|
||||
* @see org.jclouds.openstack.nova.v2_0.extensions.VolumeApi
|
||||
*
|
||||
* @see VolumeAttachmentAsyncApi
|
||||
* @author Everett Toews
|
||||
*/
|
||||
@Beta
|
||||
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUMES)
|
||||
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
|
||||
public interface VolumeAttachmentApi {
|
||||
/**
|
||||
* List Volume Attachments for a given Server.
|
||||
*
|
||||
* @param serverId The ID of the Server
|
||||
* @return All VolumeAttachments for the Server
|
||||
*/
|
||||
FluentIterable<? extends VolumeAttachment> listAttachmentsOnServer(String serverId);
|
||||
|
||||
/**
|
||||
* Get a specific Volume Attachment for a Volume and Server.
|
||||
*
|
||||
* @param volumeId The ID of the Volume
|
||||
* @param serverId The ID of the Server
|
||||
* @return The Volume Attachment.
|
||||
*/
|
||||
VolumeAttachment getAttachmentForVolumeOnServer(String volumeId, String serverId);
|
||||
|
||||
/**
|
||||
* Attach a Volume to a Server.
|
||||
*
|
||||
* Note: If you are using KVM as your hypervisor then the actual device name in the Server will be different than
|
||||
* the one specified. When the Server sees a new device, it picks the next available name (which in most cases is
|
||||
* /dev/vdc) and the disk shows up there on the Server.
|
||||
*
|
||||
* @param serverId The ID of the Server
|
||||
* @param volumeId The ID of the Volume
|
||||
* @param device The name of the device this Volume will be identified as in the Server (e.g. /dev/vdc)
|
||||
* @return The Volume Attachment.
|
||||
*/
|
||||
VolumeAttachment attachVolumeToServerAsDevice(String volumeId, String serverId, String device);
|
||||
|
||||
/**
|
||||
* Detach a Volume from a server.
|
||||
*
|
||||
* @param serverId The ID of the Server
|
||||
* @param volumeId The ID of the Volume
|
||||
* @return true if successful
|
||||
*/
|
||||
boolean detachVolumeFromServer(String serverId, String volumeId);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* 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.v2_0.extensions;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
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.keystone.v2_0.filters.AuthenticateRequest;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
|
||||
import org.jclouds.openstack.v2_0.ServiceType;
|
||||
import org.jclouds.openstack.v2_0.services.Extension;
|
||||
import org.jclouds.rest.annotations.ExceptionParser;
|
||||
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.WrapWith;
|
||||
import org.jclouds.rest.functions.ReturnEmptyFluentIterableOnNotFoundOr404;
|
||||
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
|
||||
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to Volume Attachments .
|
||||
*
|
||||
* @see VolumeAttachmentApi
|
||||
* @author Everett Toews
|
||||
*/
|
||||
@Beta
|
||||
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUMES)
|
||||
@SkipEncoding({'/', '='})
|
||||
@RequestFilters(AuthenticateRequest.class)
|
||||
public interface VolumeAttachmentAsyncApi {
|
||||
/**
|
||||
* @see VolumeAttachmentApi#listAttachmentsOnServer(String)
|
||||
*/
|
||||
@GET
|
||||
@Path("/servers/{server_id}/os-volume_attachments")
|
||||
@SelectJson("volumeAttachments")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends FluentIterable<? extends VolumeAttachment>> listAttachmentsOnServer(
|
||||
@PathParam("server_id") String serverId);
|
||||
|
||||
/**
|
||||
* @see VolumeAttachmentApi#getAttachmentForVolumeOnServer(String, String)
|
||||
*/
|
||||
@GET
|
||||
@Path("/servers/{server_id}/os-volume_attachments/{id}")
|
||||
@SelectJson("volumeAttachment")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends VolumeAttachment> getAttachmentForVolumeOnServer(
|
||||
@PathParam("id") String volumeId,
|
||||
@PathParam("server_id") String serverId);
|
||||
|
||||
/**
|
||||
* @see VolumeAttachmentApi#attachVolumeToServerAsDevice(String, String, String)
|
||||
*/
|
||||
@POST
|
||||
@Path("/servers/{server_id}/os-volume_attachments")
|
||||
@SelectJson("volumeAttachment")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@WrapWith("volumeAttachment")
|
||||
ListenableFuture<? extends VolumeAttachment> attachVolumeToServerAsDevice(
|
||||
@PayloadParam("volumeId") String volumeId,
|
||||
@PathParam("server_id") String serverId,
|
||||
@PayloadParam("device") String device);
|
||||
|
||||
/**
|
||||
* @see VolumeAttachmentApi#detachVolumeFromServer(String, String)
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/servers/{server_id}/os-volume_attachments/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
|
||||
ListenableFuture<Boolean> detachVolumeFromServer(
|
||||
@PathParam("id") String volumeId,
|
||||
@PathParam("server_id") String serverId);
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* 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.v2_0.extensions;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Volume;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
|
||||
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Tests VolumeAttachmentApi Guice wiring and parsing
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
@Test(groups = "unit", testName = "VolumeAttachmentApiExpectTest")
|
||||
public class VolumeAttachmentApiExpectTest extends BaseNovaApiExpectTest {
|
||||
private DateService dateService = new SimpleDateFormatDateService();
|
||||
|
||||
public void testListAttachments() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/attachment_list.json")).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
Set<? extends VolumeAttachment> attachments = api.listAttachmentsOnServer("instance-1").toImmutableSet();
|
||||
assertEquals(attachments, ImmutableSet.of(testAttachment()));
|
||||
// double-check individual fields
|
||||
VolumeAttachment attachment = Iterables.getOnlyElement(attachments);
|
||||
assertEquals(attachment.getDevice(), "/dev/vdc");
|
||||
assertEquals(attachment.getServerId(), "b4785058-cb80-491b-baa3-e4ee6546450e");
|
||||
assertEquals(attachment.getId(), "1");
|
||||
assertEquals(attachment.getVolumeId(), "1");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = AuthorizationException.class)
|
||||
public void testListAttachmentsFail() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-2/os-volume_attachments");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(401).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
api.listAttachmentsOnServer("instance-2");
|
||||
}
|
||||
|
||||
public void testGetAttachment() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/attachment_details.json")).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
VolumeAttachment attachment = api.getAttachmentForVolumeOnServer("1", "instance-1");
|
||||
assertEquals(attachment, testAttachment());
|
||||
}
|
||||
|
||||
public void testGetAttachmentFail() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(404).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
assertNull(api.getAttachmentForVolumeOnServer("1", "instance-1"));
|
||||
}
|
||||
|
||||
public void testAttachVolume() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).method("POST")
|
||||
.payload(payloadFromStringWithContentType("{\"volumeAttachment\":{\"volumeId\":\"1\",\"device\":\"/dev/vdc\"}}", MediaType.APPLICATION_JSON)).endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/attachment_details.json")).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
VolumeAttachment result = api.attachVolumeToServerAsDevice("1", "instance-1", "/dev/vdc");
|
||||
assertEquals(result, testAttachment());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ResourceNotFoundException.class)
|
||||
public void testAttachVolumeFail() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).method("POST")
|
||||
.payload(payloadFromStringWithContentType("{\"volumeAttachment\":{\"volumeId\":\"1\",\"device\":\"/dev/vdc\"}}", MediaType.APPLICATION_JSON)).endpoint(endpoint).build(),
|
||||
HttpResponse.builder().statusCode(404).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
api.attachVolumeToServerAsDevice("1", "instance-1","/dev/vdc");
|
||||
}
|
||||
|
||||
public void testDetachVolume() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
|
||||
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/attachment_details.json")).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
assertTrue(api.detachVolumeFromServer("1", "instance-1"));
|
||||
}
|
||||
|
||||
public void testDetachVolumeFail() {
|
||||
URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1");
|
||||
VolumeAttachmentApi api = requestsSendResponses(
|
||||
keystoneAuthWithUsernameAndPasswordAndTenantName,
|
||||
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
|
||||
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
|
||||
HttpResponse.builder().statusCode(404).build()
|
||||
).getVolumeAttachmentExtensionForZone("az-1.region-a.geo-1").get();
|
||||
|
||||
assertFalse(api.detachVolumeFromServer("1", "instance-1"));
|
||||
}
|
||||
|
||||
protected Volume testVolume() {
|
||||
return Volume.builder().status(Volume.Status.IN_USE).description("This is a test volume").zone("nova").name("test")
|
||||
.attachments(ImmutableSet.of(testAttachment())).size(1).id("1").created(dateService.iso8601SecondsDateParse("2012-04-23 12:16:45")).build();
|
||||
}
|
||||
|
||||
protected VolumeAttachment testAttachment() {
|
||||
return VolumeAttachment.builder().device("/dev/vdc").serverId("b4785058-cb80-491b-baa3-e4ee6546450e").id("1").volumeId("1").build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* 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.v2_0.extensions;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Volume;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
|
||||
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest;
|
||||
import org.jclouds.openstack.nova.v2_0.options.CreateVolumeOptions;
|
||||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Tests behavior of Volume Attachment API
|
||||
*
|
||||
* @author Everett Toews
|
||||
*/
|
||||
@Test(groups = "live", testName = "VolumeAttachmentApiLiveTest", singleThreaded = true)
|
||||
public class VolumeAttachmentApiLiveTest extends BaseNovaApiLiveTest {
|
||||
|
||||
private Optional<? extends VolumeApi> volumeApi;
|
||||
private Optional<? extends VolumeAttachmentApi> volumeAttachmentApi;
|
||||
|
||||
private String zone;
|
||||
private Volume testVolume;
|
||||
|
||||
@BeforeClass(groups = {"integration", "live"})
|
||||
@Override
|
||||
public void setupContext() {
|
||||
super.setupContext();
|
||||
zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova");
|
||||
volumeApi = novaContext.getApi().getVolumeExtensionForZone(zone);
|
||||
volumeAttachmentApi = novaContext.getApi().getVolumeAttachmentExtensionForZone(zone);
|
||||
}
|
||||
|
||||
@AfterClass(groups = { "integration", "live" })
|
||||
@Override
|
||||
protected void tearDownContext() {
|
||||
if (volumeApi.isPresent()) {
|
||||
if (testVolume != null) {
|
||||
final String volumeId = testVolume.getId();
|
||||
assertTrue(volumeApi.get().delete(volumeId));
|
||||
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() {
|
||||
@Override
|
||||
public boolean apply(VolumeApi volumeApi) {
|
||||
return volumeApi.get(volumeId) == null;
|
||||
}
|
||||
}, 180 * 1000L).apply(volumeApi.get()));
|
||||
}
|
||||
}
|
||||
|
||||
super.tearDownContext();
|
||||
}
|
||||
|
||||
public void testCreateVolume() {
|
||||
if (volumeApi.isPresent()) {
|
||||
CreateVolumeOptions options = CreateVolumeOptions.Builder
|
||||
.name("jclouds-test-volume")
|
||||
.description("description of test volume")
|
||||
.availabilityZone(zone);
|
||||
|
||||
testVolume = volumeApi.get().create(1, options);
|
||||
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() {
|
||||
@Override
|
||||
public boolean apply(VolumeApi volumeApi) {
|
||||
return volumeApi.get(testVolume.getId()).getStatus() == Volume.Status.AVAILABLE;
|
||||
}
|
||||
}, 180 * 1000L).apply(volumeApi.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "testCreateVolume")
|
||||
public void testAttachments() {
|
||||
if (volumeApi.isPresent()) {
|
||||
String server_id = null;
|
||||
try {
|
||||
final String serverId = server_id = createServerInZone(zone).getId();
|
||||
|
||||
Set<? extends VolumeAttachment> attachments =
|
||||
volumeAttachmentApi.get().listAttachmentsOnServer(serverId).toImmutableSet();
|
||||
assertNotNull(attachments);
|
||||
final int before = attachments.size();
|
||||
|
||||
VolumeAttachment testAttachment = volumeAttachmentApi.get().attachVolumeToServerAsDevice(
|
||||
testVolume.getId(), serverId, "/dev/vdf");
|
||||
assertNotNull(testAttachment.getId());
|
||||
assertEquals(testAttachment.getVolumeId(), testVolume.getId());
|
||||
|
||||
assertTrue(new RetryablePredicate<VolumeAttachmentApi>(new Predicate<VolumeAttachmentApi>() {
|
||||
@Override
|
||||
public boolean apply(VolumeAttachmentApi volumeAttachmentApi) {
|
||||
return volumeAttachmentApi.listAttachmentsOnServer(serverId).size() > before;
|
||||
}
|
||||
}, 60 * 1000L).apply(volumeAttachmentApi.get()));
|
||||
|
||||
attachments = volumeAttachmentApi.get().listAttachmentsOnServer(serverId).toImmutableSet();
|
||||
assertNotNull(attachments);
|
||||
assertEquals(attachments.size(), before + 1);
|
||||
|
||||
assertEquals(volumeApi.get().get(testVolume.getId()).getStatus(), Volume.Status.IN_USE);
|
||||
|
||||
boolean foundIt = false;
|
||||
for (VolumeAttachment att : attachments) {
|
||||
VolumeAttachment details = volumeAttachmentApi.get()
|
||||
.getAttachmentForVolumeOnServer(att.getVolumeId(), serverId);
|
||||
assertNotNull(details);
|
||||
assertNotNull(details.getId());
|
||||
assertNotNull(details.getServerId());
|
||||
assertNotNull(details.getVolumeId());
|
||||
if (Objects.equal(details.getVolumeId(), testVolume.getId())) {
|
||||
foundIt = true;
|
||||
assertEquals(details.getDevice(), "/dev/vdf");
|
||||
assertEquals(details.getServerId(), serverId);
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundIt, "Failed to find the attachment we created in listAttachments() response");
|
||||
|
||||
volumeAttachmentApi.get().detachVolumeFromServer(testVolume.getId(), serverId);
|
||||
assertTrue(new RetryablePredicate<VolumeAttachmentApi>(new Predicate<VolumeAttachmentApi>() {
|
||||
@Override
|
||||
public boolean apply(VolumeAttachmentApi volumeAttachmentApi) {
|
||||
return volumeAttachmentApi.listAttachmentsOnServer(serverId).size() == before;
|
||||
}
|
||||
}, 60 * 1000L).apply(volumeAttachmentApi.get()));
|
||||
|
||||
} finally {
|
||||
if (server_id != null)
|
||||
novaContext.getApi().getServerApiForZone(zone).delete(server_id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue