JCLOUDS-281: Support Nova Block Device Mapping v2 Boot

This commit is contained in:
Jeremy Daggett 2014-10-07 20:54:16 -07:00 committed by Jeremy Daggett
parent c9d5d2a200
commit 00b2de6205
10 changed files with 438 additions and 344 deletions

View File

@ -16,77 +16,71 @@
*/
package org.jclouds.openstack.nova.v2_0.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import javax.inject.Named;
import java.beans.ConstructorProperties;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.MoreObjects.ToStringHelper;
/**
* A representation of a block device that should be attached to the Nova instance to be launched
*
* A representation of a block device that can be used to boot a Nova instance.
*/
public class BlockDeviceMapping {
@Named("delete_on_termination")
String deleteOnTermination = "0";
@Named("device_name")
String deviceName = null;
@Named("volume_id")
String volumeId = null;
@Named("volume_size")
String volumeSize = "";
private String uuid;
@Named("device_name")
private String deviceName;
@Named("device_type")
private String deviceType;
@Named("volume_size")
private Integer volumeSize;
@Named("source_type")
private String sourceType;
@Named("destination_type")
private String destinationType;
@Named("disk_bus")
private String diskBus;
@Named("no_device")
private Boolean noDevice;
@Named("guest_format")
private String guestFormat;
@Named("boot_index")
private Integer bootIndex;
@Named("delete_on_termination")
private Boolean deleteOnTermination;
@ConstructorProperties({"volume_id", "volume_size", "device_name", "delete_on_termination"})
private BlockDeviceMapping(String volumeId, String volumeSize, String deviceName, String deleteOnTermination) {
checkNotNull(volumeId);
checkNotNull(deviceName);
this.volumeId = volumeId;
this.volumeSize = volumeSize;
@ConstructorProperties({"uuid", "device_name", "device_type", "volume_size", "source_type", "destination_type",
"disk_bus", "no_device", "guest_format", "boot_index", "delete_on_termination"})
protected BlockDeviceMapping(String uuid, String deviceName, String deviceType, Integer volumeSize,
String sourceType, String destinationType, String diskBus, Boolean noDevice, String guestFormat,
Integer bootIndex, Boolean deleteOnTermination) {
this.uuid = uuid;
this.deviceName = deviceName;
if (deleteOnTermination != null) {
this.deleteOnTermination = deleteOnTermination;
}
this.deviceType = deviceType;
this.volumeSize = volumeSize;
this.sourceType = sourceType;
this.destinationType = destinationType;
this.diskBus = diskBus;
this.noDevice = noDevice;
this.guestFormat = guestFormat;
this.bootIndex = bootIndex;
this.deleteOnTermination = deleteOnTermination;
}
/**
* Default constructor.
*/
private BlockDeviceMapping() {}
/**
* Copy constructor
* @param blockDeviceMapping
*/
private BlockDeviceMapping(BlockDeviceMapping blockDeviceMapping) {
this(blockDeviceMapping.volumeId,
blockDeviceMapping.volumeSize,
blockDeviceMapping.deviceName,
blockDeviceMapping.deleteOnTermination);
}
/**
* @return the volume id of the block device
* @return the uuid of the volume
*/
@Nullable
public String getVolumeId() {
return volumeId;
public String getUuid() {
return uuid;
}
/**
* @return the size of the block device
*/
@Nullable
public String getVolumeSize() {
return volumeSize;
}
/**
* @return the device name to which the volume is attached
* @return the device name
*/
@Nullable
public String getDeviceName() {
@ -94,15 +88,81 @@ public class BlockDeviceMapping {
}
/**
* @return whether the volume should be deleted on terminating the instance
* @return the device type
*/
public String getDeleteOnTermination() {
return deviceName;
@Nullable
public String getDeviceType() {
return deviceType;
}
/**
* @return the size of the volume
*/
@Nullable
public Integer getVolumeSize() {
return volumeSize;
}
/**
* @return the source type of the block device
*/
@Nullable
public String getSourceType() {
return sourceType;
}
/**
* @return the destination type of the block device
*/
@Nullable
public String getDestinationType() {
return destinationType;
}
/**
* @return the disk bus of the block device
*/
@Nullable
public String getDiskBus() {
return diskBus;
}
/**
* @return true if there is no block device
*/
@Nullable
public Boolean getNoDevice() {
return noDevice;
}
/**
* @return the guest format of the block device
*/
@Nullable
public String getGuestFormat() {
return guestFormat;
}
/**
* @return the boot index of the block device
*/
@Nullable
public Integer getBootIndex() {
return bootIndex;
}
/**
* @return true if the block device should terminate on deletion
*/
@Nullable
public Boolean getDeleteOnTermination() {
return deleteOnTermination;
}
@Override
public int hashCode() {
return Objects.hashCode(volumeId, volumeSize, deviceName, deleteOnTermination);
return Objects.hashCode(uuid, deviceName, deviceType, volumeSize, sourceType, destinationType, diskBus,
noDevice, guestFormat, bootIndex, deleteOnTermination);
}
@Override
@ -112,168 +172,134 @@ public class BlockDeviceMapping {
if (obj == null || getClass() != obj.getClass())
return false;
BlockDeviceMapping that = BlockDeviceMapping.class.cast(obj);
return Objects.equal(this.volumeId, that.volumeId)
&& Objects.equal(this.volumeSize, that.volumeSize)
return Objects.equal(this.uuid, that.uuid)
&& Objects.equal(this.deviceName, that.deviceName)
&& Objects.equal(this.deviceType, that.deviceType)
&& Objects.equal(this.volumeSize, that.volumeSize)
&& Objects.equal(this.sourceType, that.sourceType)
&& Objects.equal(this.destinationType, that.destinationType)
&& Objects.equal(this.diskBus, that.diskBus)
&& Objects.equal(this.noDevice, that.noDevice)
&& Objects.equal(this.guestFormat, that.guestFormat)
&& Objects.equal(this.bootIndex, that.bootIndex)
&& Objects.equal(this.deleteOnTermination, that.deleteOnTermination);
}
protected ToStringHelper string() {
return MoreObjects.toStringHelper(this)
.add("uuid", uuid)
.add("deviceName", deviceName)
.add("deviceType", deviceType)
.add("volumeSize", volumeSize)
.add("sourceType", sourceType)
.add("destinationType", destinationType)
.add("diskBus", diskBus)
.add("noDevice", noDevice)
.add("guestFormat", guestFormat)
.add("bootIndex", bootIndex)
.add("deleteOnTermination", deleteOnTermination);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("volumeId", volumeId)
.add("volumeSize", volumeSize)
.add("deviceName", deviceName)
.add("deleteOnTermination", deleteOnTermination)
.toString();
return string().toString();
}
/*
* Methods to get the Create and Update builders follow
*/
/**
* @return the Builder for creating a new block device mapping
*/
public static CreateBuilder createOptions(String volumeId, String deviceName) {
return new CreateBuilder(volumeId, deviceName);
public static Builder builder() {
return new Builder();
}
/**
* @return the Builder for updating a block device mapping
*/
public static UpdateBuilder updateOptions() {
return new UpdateBuilder();
public Builder toBuilder() {
return builder().fromBlockDeviceMapping(this);
}
private abstract static class Builder<ParameterizedBuilderType> {
protected BlockDeviceMapping blockDeviceMapping;
public static class Builder {
protected String uuid;
protected String deviceName;
protected String deviceType;
protected Integer volumeSize;
protected String sourceType;
protected String destinationType;
protected String diskBus;
protected Boolean noDevice;
protected String guestFormat;
protected Integer bootIndex;
protected Boolean deleteOnTermination;
/**
* No-parameters constructor used when updating.
* */
private Builder() {
blockDeviceMapping = new BlockDeviceMapping();
}
protected abstract ParameterizedBuilderType self();
/**
* Provide the volume id to the BlockDeviceMapping's Builder.
*
* @return the Builder.
* @see BlockDeviceMapping#getVolumeId()
*/
public ParameterizedBuilderType volumeId(String volumeId) {
blockDeviceMapping.volumeId = volumeId;
return self();
}
/**
* Provide the volume size in GB to the BlockDeviceMapping's Builder.
*
* @return the Builder.
* @see BlockDeviceMapping#getVolumeSize()
*/
public ParameterizedBuilderType volumeSize(int volumeSize) {
blockDeviceMapping.volumeSize = Integer.toString(volumeSize);
return self();
}
/**
* Provide the deviceName to the BlockDeviceMapping's Builder.
*
* @return the Builder.
* @see BlockDeviceMapping#getDeviceName()
*/
public ParameterizedBuilderType deviceName(String deviceName) {
blockDeviceMapping.deviceName = deviceName;
return self();
}
/**
* Provide an option indicated to delete the volume on instance deletion to BlockDeviceMapping's Builder.
*
* @return the Builder.
* @see BlockDeviceMapping#getVolumeSize()
*/
public ParameterizedBuilderType deleteOnTermination(boolean deleteOnTermination) {
blockDeviceMapping.deleteOnTermination = deleteOnTermination ? "1" : "0";
return self();
}
}
/**
* Create and Update builders (inheriting from Builder)
*/
public static class CreateBuilder extends Builder<CreateBuilder> {
/**
* Supply required properties for creating a Builder
*/
private CreateBuilder(String volumeId, String deviceName) {
blockDeviceMapping.volumeId = volumeId;
blockDeviceMapping.deviceName = deviceName;
}
/**
* @return a CreateOptions constructed with this Builder.
*/
public CreateOptions build() {
return new CreateOptions(blockDeviceMapping);
}
protected CreateBuilder self() {
public Builder uuid(String uuid) {
this.uuid = uuid;
return this;
}
}
/**
* Create and Update builders (inheriting from Builder)
*/
public static class UpdateBuilder extends Builder<UpdateBuilder> {
/**
* Supply required properties for updating a Builder
*/
private UpdateBuilder() {
}
/**
* @return a UpdateOptions constructed with this Builder.
*/
public UpdateOptions build() {
return new UpdateOptions(blockDeviceMapping);
}
protected UpdateBuilder self() {
public Builder deviceName(String deviceName) {
this.deviceName = deviceName;
return this;
}
}
/**
* Create and Update options - extend the domain class, passed to API update and create calls.
* Essentially the same as the domain class. Ensure validation and safe typing.
*/
public static class CreateOptions extends BlockDeviceMapping {
/**
* Copy constructor
*/
private CreateOptions(BlockDeviceMapping blockDeviceMapping) {
super(blockDeviceMapping);
checkNotNull(blockDeviceMapping.volumeId, "volume id should not be null");
checkNotNull(blockDeviceMapping.deviceName, "device name should not be null");
public Builder deviceType(String deviceType) {
this.deviceType = deviceType;
return this;
}
public Builder volumeSize(Integer volumeSize) {
this.volumeSize = volumeSize;
return this;
}
public Builder sourceType(String sourceType) {
this.sourceType = sourceType;
return this;
}
public Builder destinationType(String destinationType) {
this.destinationType = destinationType;
return this;
}
public Builder diskBus(String diskBus) {
this.diskBus = diskBus;
return this;
}
public Builder noDevice(Boolean noDevice) {
this.noDevice = noDevice;
return this;
}
public Builder guestFormat(String guestFormat) {
this.guestFormat = guestFormat;
return this;
}
public Builder bootIndex(Integer bootIndex) {
this.bootIndex = bootIndex;
return this;
}
public Builder deleteOnTermination(Boolean deleteOnTermination) {
this.deleteOnTermination = deleteOnTermination;
return this;
}
public BlockDeviceMapping build() {
return new BlockDeviceMapping(uuid, deviceName, deviceType, volumeSize, sourceType, destinationType, diskBus,
noDevice, guestFormat, bootIndex, deleteOnTermination);
}
public Builder fromBlockDeviceMapping(BlockDeviceMapping in) {
return this
.uuid(in.getUuid())
.deviceName(in.getDeviceName())
.deviceType(in.getDeviceType())
.volumeSize(in.getVolumeSize())
.sourceType(in.getSourceType())
.destinationType(in.getDestinationType())
.diskBus(in.getDiskBus())
.noDevice(in.getNoDevice())
.bootIndex(in.getBootIndex())
.deleteOnTermination(in.getDeleteOnTermination())
.guestFormat(in.getGuestFormat());
}
}
/**
* Create and Update options - extend the domain class, passed to API update and create calls.
* Essentially the same as the domain class. Ensure validation and safe typing.
*/
public static class UpdateOptions extends BlockDeviceMapping {
/**
* Copy constructor
*/
private UpdateOptions(BlockDeviceMapping blockDeviceMapping) {
super(blockDeviceMapping);
}
}
}

View File

@ -17,9 +17,7 @@
package org.jclouds.openstack.nova.v2_0.extensions;
/**
* Extension namespaces
*
* @see <a href= "http://nova.openstack.org/api_ext/" />
* OpenStack Nova Extension Namespaces
*/
public final class ExtensionNamespaces {
/**
@ -34,7 +32,6 @@ public final class ExtensionNamespaces {
* Volume attachment support
*/
public static final String VOLUME_ATTACHMENTS = "http://docs.openstack.org/compute/ext/os-volume-attachment-update/api/v2";
/**
* Volume types support
*/
@ -95,26 +92,27 @@ public final class ExtensionNamespaces {
* Admin Action extension
*/
public static final String ADMIN_ACTIONS = "http://docs.openstack.org/ext/admin-actions/api/v1.1";
/**
* Extended Server Status extension
*/
public static final String EXTENDED_STATUS = "http://docs.openstack.org/compute/ext/extended_status/api/v1.1";
/**
* Disk Config extension
*/
public static final String DISK_CONFIG = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1";
/**
* Aggregates extension
*/
public static final String AGGREGATES = "http://docs.openstack.org/ext/aggregates/api/v1.1";
/**
* Consoles extension
*/
public static final String CONSOLES = "http://docs.openstack.org/compute/ext/os-consoles/api/v2";
/**
* Block Device Mapping v2 Boot Extension
*/
public static final String BLOCK_DEVICE_MAPPING_V2_BOOT =
"http://docs.openstack.org/compute/ext/block_device_mapping_v2_boot/api/v2";
private ExtensionNamespaces() {
throw new AssertionError("intentionally unimplemented");

View File

@ -110,7 +110,7 @@ public class CreateServerOptions implements MapBinder {
private Set<Network> novaNetworks = ImmutableSet.of();
private String availabilityZone;
private boolean configDrive;
private Set<BlockDeviceMapping> blockDeviceMapping = ImmutableSet.of();
private Set<BlockDeviceMapping> blockDeviceMappings = ImmutableSet.of();
@Override
public boolean equals(Object object) {
@ -119,12 +119,14 @@ public class CreateServerOptions implements MapBinder {
}
if (object instanceof CreateServerOptions) {
final CreateServerOptions other = CreateServerOptions.class.cast(object);
return equal(keyName, other.keyName) && equal(securityGroupNames, other.securityGroupNames)
&& equal(metadata, other.metadata) && equal(personality, other.personality)
&& equal(adminPass, other.adminPass) && equal(diskConfig, other.diskConfig)
&& equal(adminPass, other.adminPass) && equal(networks, other.networks)
return equal(keyName, other.keyName) && equal(adminPass, other.adminPass)
&& equal(securityGroupNames, other.securityGroupNames) && equal(metadata, other.metadata)
&& equal(personality, other.personality)
&& equal(diskConfig, other.diskConfig)
&& equal(networks, other.networks)
&& equal(availabilityZone, other.availabilityZone)
&& equal(configDrive, other.configDrive);
&& equal(configDrive, other.configDrive)
&& equal(blockDeviceMappings, other.blockDeviceMappings);
} else {
return false;
}
@ -132,11 +134,12 @@ public class CreateServerOptions implements MapBinder {
@Override
public int hashCode() {
return Objects.hashCode(keyName, securityGroupNames, metadata, personality, adminPass, networks, availabilityZone, configDrive);
return Objects.hashCode(keyName, adminPass, securityGroupNames, metadata, personality, networks, availabilityZone,
configDrive, blockDeviceMappings);
}
protected ToStringHelper string() {
ToStringHelper toString = MoreObjects.toStringHelper("").omitNullValues();
ToStringHelper toString = MoreObjects.toStringHelper(this);
toString.add("keyName", keyName);
if (!securityGroupNames.isEmpty())
toString.add("securityGroupNames", securityGroupNames);
@ -151,10 +154,10 @@ public class CreateServerOptions implements MapBinder {
toString.add("userData", userData == null ? null : new String(userData));
if (!networks.isEmpty())
toString.add("networks", networks);
toString.add("availability_zone", availabilityZone == null ? null : availabilityZone);
toString.add("availabilityZone", availabilityZone == null ? null : availabilityZone);
toString.add("configDrive", configDrive);
if (!blockDeviceMapping.isEmpty())
toString.add("blockDeviceMapping", blockDeviceMapping);
if (!blockDeviceMappings.isEmpty())
toString.add("blockDeviceMappings", blockDeviceMappings);
return toString;
}
@ -181,8 +184,8 @@ public class CreateServerOptions implements MapBinder {
Set<Map<String, String>> networks;
@Named("config_drive")
String configDrive;
@Named("block_device_mapping")
Set<BlockDeviceMapping> blockDeviceMapping;
@Named("block_device_mapping_v2")
Set<BlockDeviceMapping> blockDeviceMappings;
private ServerRequest(String name, String imageRef, String flavorRef) {
this.name = name;
@ -218,11 +221,9 @@ public class CreateServerOptions implements MapBinder {
if (adminPass != null) {
server.adminPass = adminPass;
}
if (diskConfig != null) {
server.diskConfig = diskConfig;
}
if (!networks.isEmpty() || !novaNetworks.isEmpty()) {
server.networks = Sets.newLinkedHashSet(); // ensures ordering is preserved - helps testing and more intuitive for users.
for (Network network : novaNetworks) {
@ -243,9 +244,8 @@ public class CreateServerOptions implements MapBinder {
server.networks.add(ImmutableMap.of("uuid", network));
}
}
if (!blockDeviceMapping.isEmpty()) {
server.blockDeviceMapping = blockDeviceMapping;
if (!blockDeviceMappings.isEmpty()) {
server.blockDeviceMappings = blockDeviceMappings;
}
return bindToRequest(request, ImmutableMap.of("server", server));
@ -319,7 +319,7 @@ public class CreateServerOptions implements MapBinder {
* Custom user-data can be also be supplied at launch time.
* It is retrievable by the instance and is often used for launch-time configuration
* by instance scripts.
* Pass userData unencdoed, as the value will be base64 encoded automatically.
* Pass userData unencoded, as the value will be base64 encoded automatically.
*/
public CreateServerOptions userData(byte[] userData) {
this.userData = userData;
@ -340,21 +340,6 @@ public class CreateServerOptions implements MapBinder {
/**
* 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
*/
public String getKeyPairName() {
return keyName;
}
/**
* The availability zone in which to launch the server.
*
* @return the availability zone to be used
*/
public String getAvailabilityZone() {
return availabilityZone;
}
/**
* @see #getKeyPairName()
*/
public CreateServerOptions keyPairName(String keyName) {
@ -370,6 +355,75 @@ public class CreateServerOptions implements MapBinder {
return this;
}
/**
* @see #getSecurityGroupNames()
*/
public CreateServerOptions securityGroupNames(String... securityGroupNames) {
return securityGroupNames(ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")));
}
/**
* @see #getSecurityGroupNames()
*/
public CreateServerOptions securityGroupNames(Iterable<String> securityGroupNames) {
for (String groupName : checkNotNull(securityGroupNames, "securityGroupNames"))
checkNotNull(emptyToNull(groupName), "all security groups must be non-empty");
this.securityGroupNames = ImmutableSet.copyOf(securityGroupNames);
return this;
}
/**
* @see #getDiskConfig()
*/
public CreateServerOptions diskConfig(String diskConfig) {
this.diskConfig = diskConfig;
return this;
}
/**
* @see #getNetworks()
*/
public CreateServerOptions networks(Iterable<String> networks) {
this.networks = ImmutableSet.copyOf(networks);
return this;
}
/**
* @see #getNetworks()
* Overwrites networks supplied by {@link #networks(Iterable)}
*/
public CreateServerOptions novaNetworks(Iterable<Network> networks) {
this.novaNetworks = ImmutableSet.copyOf(networks);
return this;
}
/**
* @see #getNetworks()
*/
public CreateServerOptions networks(String... networks) {
return networks(ImmutableSet.copyOf(networks));
}
/**
* @see #getBlockDeviceMappings()
*/
public CreateServerOptions blockDeviceMappings(Set<BlockDeviceMapping> blockDeviceMappings) {
this.blockDeviceMappings = ImmutableSet.copyOf(blockDeviceMappings);
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
*/
public String getKeyPairName() {
return keyName;
}
public String getAvailabilityZone() {
return availabilityZone;
}
/**
* Security groups the user specified to run servers with.
* <p/>
@ -402,23 +456,6 @@ public class CreateServerOptions implements MapBinder {
return novaNetworks;
}
/**
* @see #getSecurityGroupNames
*/
public CreateServerOptions securityGroupNames(String... securityGroupNames) {
return securityGroupNames(ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")));
}
/**
* @see #getSecurityGroupNames
*/
public CreateServerOptions securityGroupNames(Iterable<String> securityGroupNames) {
for (String groupName : checkNotNull(securityGroupNames, "securityGroupNames"))
checkNotNull(emptyToNull(groupName), "all security groups must be non-empty");
this.securityGroupNames = ImmutableSet.copyOf(securityGroupNames);
return this;
}
/**
* When you create a server from an image with the diskConfig value set to
* {@link Server#DISK_CONFIG_AUTO}, the server is built with a single partition that is expanded to
@ -434,14 +471,6 @@ public class CreateServerOptions implements MapBinder {
return diskConfig;
}
/**
* @see #getDiskConfig
*/
public CreateServerOptions diskConfig(String diskConfig) {
this.diskConfig = diskConfig;
return this;
}
/**
* Determines if a configuration drive will be attached to the server or not.
* This can be used for cloud-init or other configuration purposes.
@ -451,56 +480,24 @@ public class CreateServerOptions implements MapBinder {
}
/**
* @see #getNetworks
* Block devices that should be attached to the instance at boot time.
*/
public CreateServerOptions networks(Iterable<String> networks) {
this.networks = ImmutableSet.copyOf(networks);
return this;
}
/**
* @see #getNetworks
* Overwrites networks supplied by {@link #networks(Iterable)}
*/
public CreateServerOptions novaNetworks(Iterable<Network> networks) {
this.novaNetworks = ImmutableSet.copyOf(networks);
return this;
}
/**
* @see #getNetworks
*/
public CreateServerOptions networks(String... networks) {
return networks(ImmutableSet.copyOf(networks));
}
/**
* @see #getBlockDeviceMapping
*/
public CreateServerOptions blockDeviceMapping(Set<BlockDeviceMapping> blockDeviceMapping) {
this.blockDeviceMapping = ImmutableSet.copyOf(blockDeviceMapping);
return this;
}
/**
* Block volumes that should be attached to the instance at boot time.
*
* @see <a href="http://docs.openstack.org/trunk/openstack-ops/content/attach_block_storage.html">Attach Block Storage<a/>
*/
public Set<BlockDeviceMapping> getBlockDeviceMapping() {
return blockDeviceMapping;
public Set<BlockDeviceMapping> getBlockDeviceMappings() {
return blockDeviceMappings;
}
public static class Builder {
/**
* @see CreateServerOptions#writeFileToPath
* @see CreateServerOptions#writeFileToPath(byte[], String)
*/
public static CreateServerOptions writeFileToPath(byte[] contents, String path) {
CreateServerOptions options = new CreateServerOptions();
return options.writeFileToPath(contents, path);
}
/**
* @see CreateServerOptions#adminPass(String)
*/
public static CreateServerOptions adminPass(String adminPass) {
CreateServerOptions options = new CreateServerOptions();
return options.adminPass(adminPass);
@ -515,7 +512,7 @@ public class CreateServerOptions implements MapBinder {
}
/**
* @see #getKeyPairName()
* @see CreateServerOptions#keyPairName(String)
*/
public static CreateServerOptions keyPairName(String keyName) {
CreateServerOptions options = new CreateServerOptions();
@ -523,67 +520,62 @@ public class CreateServerOptions implements MapBinder {
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* @see CreateServerOptions#securityGroupNames(String...)
*/
public static CreateServerOptions securityGroupNames(String... groupNames) {
CreateServerOptions options = new CreateServerOptions();
if (new CreateServerOptions().securityGroupNames(groupNames) == CreateServerOptions.class.cast(options.securityGroupNames(groupNames)))
System.out.println("They are fucking equal, dump the cast!!!");
return CreateServerOptions.class.cast(options.securityGroupNames(groupNames));
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* @see CreateServerOptions#securityGroupNames(Iterable)
*/
public static CreateServerOptions securityGroupNames(Iterable<String> groupNames) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.securityGroupNames(groupNames));
return CreateServerOptions.class.cast(new CreateServerOptions().securityGroupNames(groupNames));
}
/**
* @see CreateServerOptions#getDiskConfig
* @see CreateServerOptions#diskConfig(String)
*/
public static CreateServerOptions diskConfig(String diskConfig) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.diskConfig(diskConfig));
return CreateServerOptions.class.cast(new CreateServerOptions().diskConfig(diskConfig));
}
/**
* @see CreateServerOptions#getNetworks
* @see CreateServerOptions#networks(String...)
*/
public static CreateServerOptions networks(String... networks) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.networks(networks));
return CreateServerOptions.class.cast(new CreateServerOptions().networks(networks));
}
/**
* @see CreateServerOptions#getNetworks
* @see CreateServerOptions#networks(Iterable)
*/
public static CreateServerOptions networks(Iterable<String> networks) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.networks(networks));
return CreateServerOptions.class.cast(new CreateServerOptions().networks(networks));
}
/**
* @see CreateServerOptions#getNetworks
* @see CreateServerOptions#novaNetworks(Iterable)
*/
public static CreateServerOptions novaNetworks(Iterable<Network> networks) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.novaNetworks(networks));
return CreateServerOptions.class.cast(new CreateServerOptions().novaNetworks(networks));
}
/**
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getAvailabilityZone()
* @see CreateServerOptions#availabilityZone(String)
*/
public static CreateServerOptions availabilityZone(String availabilityZone) {
CreateServerOptions options = new CreateServerOptions();
return options.availabilityZone(availabilityZone);
return new CreateServerOptions().availabilityZone(availabilityZone);
}
/**
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getBlockDeviceMapping()
* @see CreateServerOptions#blockDeviceMappings(Set)
*/
public static CreateServerOptions blockDeviceMapping (Set<BlockDeviceMapping> blockDeviceMapping) {
CreateServerOptions options = new CreateServerOptions();
return options.blockDeviceMapping(blockDeviceMapping);
public static CreateServerOptions blockDeviceMappings(Set<BlockDeviceMapping> blockDeviceMappings) {
return new CreateServerOptions().blockDeviceMappings(blockDeviceMappings);
}
}

View File

@ -23,11 +23,9 @@ import static org.testng.Assert.assertTrue;
import java.util.Set;
import org.jclouds.openstack.nova.v2_0.domain.BlockDeviceMapping;
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.CreateServerOptions;
import org.jclouds.openstack.nova.v2_0.options.CreateVolumeOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@ -36,7 +34,6 @@ 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.ImmutableSet;
import com.google.common.collect.Iterables;
/**
@ -154,6 +151,7 @@ public class VolumeAttachmentApiLiveTest extends BaseNovaApiLiveTest {
}
}
/*
@Test(dependsOnMethods = "testCreateVolume")
public void testAttachmentAtBoot() {
if (volumeApi.isPresent()) {
@ -188,5 +186,5 @@ public class VolumeAttachmentApiLiveTest extends BaseNovaApiLiveTest {
}
}
}
}
}*/
}

View File

@ -200,8 +200,7 @@ public class ServerApiExpectTest extends BaseNovaApiExpectTest {
new ParseCreatedServerTest().expected().toString());
}
public void testCreateServerWithAttachedDiskWhenResponseIs202() throws Exception {
public void testCreateServerWithBootVolumeWhenResponseIs202() throws Exception {
HttpRequest createServer = HttpRequest
.builder()
.method("POST")
@ -209,10 +208,9 @@ public class ServerApiExpectTest extends BaseNovaApiExpectTest {
.addHeader("Accept", "application/json")
.addHeader("X-Auth-Token", authToken)
.payload(payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"block_device_mapping\":[{\"volume_size\":\"\",\"volume_id\":\"f0c907a5-a26b-48ba-b803-83f6b7450ba5\",\"delete_on_termination\":\"1\",\"device_name\":\"vdb\"}]}}", "application/json"))
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"\",\"flavorRef\":\"12345\",\"block_device_mapping_v2\":[{\"volume_size\":100,\"uuid\":\"f0c907a5-a26b-48ba-b803-83f6b7450ba5\",\"destination_type\":\"volume\",\"source_type\":\"image\"}]}}", "application/json"))
.build();
HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
.payload(payloadFromResourceWithContentType("/new_server.json", "application/json; charset=UTF-8")).build();
@ -220,12 +218,43 @@ public class ServerApiExpectTest extends BaseNovaApiExpectTest {
NovaApi apiWithNewServer = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, createServer, createServerResponse);
BlockDeviceMapping blockDeviceMapping = BlockDeviceMapping.createOptions("f0c907a5-a26b-48ba-b803-83f6b7450ba5", "vdb").deleteOnTermination(true).build();
assertEquals(apiWithNewServer.getServerApi("az-1.region-a.geo-1").create("test-e92", "1241",
"100", new CreateServerOptions().blockDeviceMapping(ImmutableSet.of(blockDeviceMapping))).toString(),
BlockDeviceMapping blockDeviceMapping = BlockDeviceMapping.builder()
.uuid("f0c907a5-a26b-48ba-b803-83f6b7450ba5").sourceType("image").destinationType("volume")
.volumeSize(100).build();
assertEquals(apiWithNewServer.getServerApi("az-1.region-a.geo-1").create("test-e92", "",
"12345", new CreateServerOptions().blockDeviceMappings(ImmutableSet.of(blockDeviceMapping))).toString(),
new ParseCreatedServerTest().expected().toString());
}
public void testCreateServerWithBootVolumeWhenResponseIs404() throws Exception {
HttpRequest createServer = HttpRequest
.builder()
.method("POST")
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/servers")
.addHeader("Accept", "application/json")
.addHeader("X-Auth-Token", authToken)
.payload(payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"\",\"flavorRef\":\"12345\",\"block_device_mapping_v2\":[{\"volume_size\":100,\"uuid\":\"f0c907a5-a26b-48ba-b803-83f6b7450ba5\",\"destination_type\":\"volume\",\"source_type\":\"image\"}]}}", "application/json"))
.build();
HttpResponse createServerResponse = HttpResponse.builder().statusCode(404).build();
NovaApi apiWithNewServer = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, createServer, createServerResponse);
BlockDeviceMapping blockDeviceMapping = BlockDeviceMapping.builder()
.uuid("f0c907a5-a26b-48ba-b803-83f6b7450ba5").sourceType("image")
.destinationType("volume").volumeSize(100).build();
try {
apiWithNewServer.getServerApi("az-1.region-a.geo-1").create("test-e92", "", "12345", new CreateServerOptions().blockDeviceMappings(ImmutableSet.of(blockDeviceMapping)));
fail("Expected an exception.");
} catch (Exception e) {
// expected
}
}
public void testCreateServerWithDiskConfigAuto() throws Exception {
HttpRequest createServer = HttpRequest.builder()
.method("POST")

View File

@ -19,12 +19,11 @@ package org.jclouds.openstack.nova.v2_0.features;
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitActive;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import com.google.common.base.Optional;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.nova.v2_0.domain.BlockDeviceMapping;
import org.jclouds.openstack.nova.v2_0.domain.Network;
import org.jclouds.openstack.nova.v2_0.domain.Server;
import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
@ -34,9 +33,11 @@ import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
import org.jclouds.openstack.nova.v2_0.options.RebuildServerOptions;
import org.jclouds.openstack.v2_0.domain.Link.Relation;
import org.jclouds.openstack.v2_0.domain.Resource;
import org.jclouds.openstack.v2_0.features.ExtensionApi;
import org.jclouds.openstack.v2_0.predicates.LinkPredicates;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@ -132,6 +133,50 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
}
}
/**
* This test creates a new server with a boot device from an image.
*
* This needs to be supported by the provider, and is usually not supported.
*
* TODO: Configurable system properties for flavor/image refs.
*/
@Test
public void testCreateWithBlockDeviceMapping() {
String serverId = null;
// Rackspace Performance Flavor
String flavorRef = "performance1-2";
// Rackspace CentOS 6.5 image
String imageRef = "3ab30cc6-c503-41d3-8a37-106fda7848a7";
for (String regionId : regions) {
ServerApi serverApi = api.getServerApi(regionId);
ExtensionApi extensionApi = api.getExtensionApi(regionId);
// check for the existence of the block mapping v2 boot extension
if (extensionApi.get("os-block-device-mapping-v2-boot") != null) {
try {
BlockDeviceMapping blockDeviceMappings = BlockDeviceMapping.builder()
.uuid(imageRef).sourceType("image").destinationType("volume")
.volumeSize(100).bootIndex(0).build();
CreateServerOptions options = CreateServerOptions.Builder
.blockDeviceMappings(ImmutableSet.of(blockDeviceMappings));
ServerCreated server = serverApi.create(hostName, "", flavorRef, options);
serverId = server.getId();
awaitActive(serverApi).apply(server.getId());
Server serverCheck = serverApi.get(serverId);
assertEquals(serverCheck.getStatus(), ACTIVE);
} finally {
if (serverId != null) {
serverApi.delete(serverId);
}
}
}
}
}
@Test
public void testCreateInWrongAvailabilityZone() {
String serverId = null;
@ -154,9 +199,7 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
@Test
public void testRebuildServer() {
String serverId = null;
for (String regionId : regions) {
ServerApi serverApi = api.getServerApi(regionId);
try {
@ -213,6 +256,6 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
private void checkServer(Server server) {
checkResource(server);
assertFalse(server.getAddresses().isEmpty());
assertNotNull(server.getFlavor());
}
}

View File

@ -280,6 +280,14 @@
"alias": "os-availability-zone",
"description": "1. Add availability_zone to the Create Server v1.1 API.\n 2. Add availability zones describing.\n "
},
{
"updated":"2013-07-08T00:00:00+00:00",
"name":"BlockDeviceMappingV2Boot",
"links":[],
"namespace":"http://docs.openstack.org/compute/ext/block_device_mapping_v2_boot/api/v2",
"alias":"os-block-device-mapping-v2-boot",
"description":"Allow boot with the new BDM data format."
},
{
"alias": "os-volume-attachment-update",
"description": "Support for updating a volume attachment.",

View File

@ -39,4 +39,4 @@
"metadata": {},
"OS-DCF:diskConfig": "AUTO"
}
}
}

View File

@ -37,4 +37,4 @@
"id": 71752,
"metadata": {}
}
}
}