/**
* Copyright (C) 2009-2015 Dell, Inc.
* See annotations for authorship information
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.dasein.cloud.compute;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.CloudProvider;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.OperationNotSupportedException;
import org.dasein.cloud.Requirement;
import org.dasein.cloud.dc.DataCenter;
import org.dasein.util.uom.storage.Gigabyte;
import org.dasein.util.uom.storage.Storage;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Configuration options for creating volumes. When you create volumes, you provide an instance of this class to
* indicate the different configuration options for instantiating the volume. Not all clouds or volume types require
* all values to be specified. Use the meta-data in {@link VolumeSupport} to programmatically determine what is
* required.
* <p>Created by George Reese: 6/22/12 8:41 PM</p>
* @author George Reese (george.reese@imaginary.com)
* @since 2012-07
* @version 2012-07 introduced volume create options
* @version 2013.02 added javadoc
*/
public class VolumeCreateOptions {
/**
* Provides options for creating a block volume of a specific size. Useful only when {@link VolumeSupport#getVolumeProductRequirement()}
* is {@link Requirement#NONE} or {@link Requirement#OPTIONAL}.
* @param size the size of the volume to be created (or to be resized to)
* @param name the name of the new volume
* @param description a friendly description of the purpose of the new volume
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getInstance(@Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description) {
return new VolumeCreateOptions(null, null, size, name, description, 0);
}
/**
* Provides options for creating a block volume of a specific size based on a specific product. This method makes no sense
* when {@link VolumeSupport#getVolumeProductRequirement()} is {@link Requirement#NONE}.
* @param volumeProductId the ID of the volume product from {@link VolumeSupport#listVolumeProducts()} to use in creating the volume
* @param size the size of the volume to be created (or to be resized to)
* @param name the name of the new volume
* @param description a friendly description of the purpose of the new volume
* @param iops the minimum guaranteed iops or 0 if no guarantees are sought
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getInstance(@Nonnull String volumeProductId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description, @Nonnegative int iops) {
return new VolumeCreateOptions(volumeProductId, null, size, name, description, iops);
}
/**
* Provides options for creating a network volume of a specific size. Useful only when {@link VolumeSupport#getVolumeProductRequirement()}
* is {@link Requirement#NONE} or {@link Requirement#OPTIONAL}.
* @param inVlanId the ID of the VLAN in which the network volume will be created
* @param size the size of the volume to be created (or to be resized to)
* @param name the name of the new volume
* @param description a friendly description of the purpose of the new volume
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getNetworkInstance(@Nonnull String inVlanId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description) {
VolumeCreateOptions options = new VolumeCreateOptions(null, null, size, name, description, 0);
options.vlanId = inVlanId;
options.format = VolumeFormat.NFS;
return options;
}
/**
* Provides options for creating a network volume of a specific size based on a specific product. This method makes no sense
* when {@link VolumeSupport#getVolumeProductRequirement()} is {@link Requirement#NONE}.
* @param inVlanId the ID of the VLAN in which the network volume will be created
* @param volumeProductId the ID of the volume product from {@link VolumeSupport#listVolumeProducts()} to use in creating the volume
* @param size the size of the volume to be created (or to be resized to)
* @param name the name of the new volume
* @param description a friendly description of the purpose of the new volume
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getNetworkInstance(@Nonnull String volumeProductId, @Nonnull String inVlanId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description) {
VolumeCreateOptions options = new VolumeCreateOptions(volumeProductId, null, size, name, description, 0);
options.vlanId = inVlanId;
options.format = VolumeFormat.NFS;
return options;
}
/**
* Provides options for creating a network volume of a specific size based on a specific product. This method makes no sense
* when {@link VolumeSupport#getVolumeProductRequirement()} is {@link Requirement#NONE}.
* @param inVlanId the ID of the VLAN in which the network volume will be created
* @param volumeProductId the ID of the volume product from {@link VolumeSupport#listVolumeProducts()} to use in creating the volume
* @param size the size of the volume to be created (or to be resized to)
* @param name the name of the new volume
* @param description a friendly description of the purpose of the new volume
* @param iops the minimum guaranteed iops or 0 if no guarantees are sought
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getNetworkInstance(@Nonnull String volumeProductId, @Nonnull String inVlanId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description, @Nonnegative int iops) {
VolumeCreateOptions options = new VolumeCreateOptions(volumeProductId, null, size, name, description, iops);
options.vlanId = inVlanId;
options.format = VolumeFormat.NFS;
return options;
}
/**
* Provides options for creating a block volume from a snapshot. This method makes no sense when {@link ComputeServices#getSnapshotSupport()}
* is <code>null</code>. Useful only when {@link VolumeSupport#getVolumeProductRequirement()}
* is {@link Requirement#NONE} or {@link Requirement#OPTIONAL}.
* @param snapshotId the ID of the snapshot from which the volume will be created
* @param size the size of the volume to be created
* @param name the name of the volume to be created
* @param description a friendly description of the purpose of the new volume
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getInstanceForSnapshot(@Nonnull String snapshotId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description) {
return new VolumeCreateOptions(null, snapshotId, size, name, description, 0);
}
/**
* Provides options for creating a block volume from a snapshot. This method makes no sense when {@link ComputeServices#getSnapshotSupport()}
* is <code>null</code>. Useful only when {@link VolumeSupport#getVolumeProductRequirement()} is <em>NOT</em>
* {@link Requirement#NONE}.
* @param volumeProductId the ID of the volume product from which the new volume will be created
* @param snapshotId the ID of the snapshot from which the volume will be created
* @param size the size of the volume to be created
* @param name the name of the volume to be created
* @param description a friendly description of the purpose of the new volume
* @param iops the guaranteed iops for the new volume (or 0 for no guarantee)
* @return an object representing the options for creating the new volume
*/
static public VolumeCreateOptions getInstanceForSnapshot(@Nonnull String volumeProductId, @Nonnull String snapshotId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description, @Nonnegative int iops) {
return new VolumeCreateOptions(volumeProductId, snapshotId, size, name, description, iops);
}
// NOTE: ADDING/REMOVING/CHANGING AN ATTRIBUTE? MAKE SURE YOU REFLECT THE CHANGE IN THE copy() METHOD
private String dataCenterId;
private String providerVirtualMachineId;
private String description;
private String deviceId;
private VolumeFormat format;
private int iops;
private Map<String,Object> metaData;
private String name;
private String snapshotId;
private String virtualMachineId;
private String vlanId;
private String volumeProductId;
private Storage<Gigabyte> volumeSize;
// NOTE: SEE NOTE AT TOP OF ATTRIBUTE LIST WHEN ADDING/REMOVING/CHANGING AN ATTRIBUTE
public void setDataCenterId(String dataCenterId) {
this.dataCenterId = dataCenterId;
}
public void setProviderVirtualMachineId(String providerVirtualMachineId){
this.providerVirtualMachineId = providerVirtualMachineId;
}
public void setDescription(String description) {
this.description = description;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setFormat(VolumeFormat format) {
this.format = format;
}
public void setIops(int iops) {
this.iops = iops;
}
public void setMetaData(Map<String, Object> metaData) {
this.metaData = metaData;
}
public void setName(String name) {
this.name = name;
}
public void setSnapshotId(String snapshotId) {
this.snapshotId = snapshotId;
}
public void setVirtualMachineId(String virtualMachineId) {
this.virtualMachineId = virtualMachineId;
}
public void setVlanId(String vlanId) {
this.vlanId = vlanId;
}
public void setVolumeProductId(String volumeProductId) {
this.volumeProductId = volumeProductId;
}
public void setVolumeSize(Storage<Gigabyte> volumeSize) {
this.volumeSize = volumeSize;
}
@SuppressWarnings("UnusedDeclaration")
private VolumeCreateOptions() { }
private VolumeCreateOptions(@Nullable String volumeProductId, @Nullable String snapshotId, @Nonnull Storage<?> size, @Nonnull String name, @Nonnull String description, @Nonnegative int iops) {
this.volumeProductId = volumeProductId;
this.snapshotId = snapshotId;
volumeSize = (Storage<Gigabyte>)size.convertTo(Storage.GIGABYTE);
this.name = name;
this.description = description;
this.iops = iops;
this.format = VolumeFormat.BLOCK;
}
/**
* Provisions a volume in the specified cloud based on the options defined in this object.
* @param provider the cloud provider in which the volume should be provisioned
* @return the unique ID of the newly provisioned volume
* @throws CloudException an error occurred with the cloud provider while provisioning the volume
* @throws InternalException an error occurred within Dasein Cloud while preparing the API call
* @throws OperationNotSupportedException the cloud does not support volumes
*/
public @Nonnull String build(@Nonnull CloudProvider provider) throws CloudException, InternalException {
ComputeServices services = provider.getComputeServices();
if( services == null ) {
throw new OperationNotSupportedException(provider.getCloudName() + " does not have support for compute services");
}
VolumeSupport support = services.getVolumeSupport();
if( support == null ) {
throw new OperationNotSupportedException(provider.getCloudName() + " does not have volume support");
}
return support.createVolume(this);
}
/**
* Makes a copy of these creation options so that individual values may be altered as needed.
* @return a copy of these creation options
*/
public @Nonnull VolumeCreateOptions copy(@Nonnull String withName) {
VolumeCreateOptions options = new VolumeCreateOptions(volumeProductId, snapshotId, volumeSize, withName, description, iops);
options.dataCenterId = dataCenterId;
options.providerVirtualMachineId = providerVirtualMachineId;
options.deviceId = deviceId;
options.format = format;
options.virtualMachineId = virtualMachineId;
options.vlanId = vlanId;
if( metaData != null ) {
options.metaData = new HashMap<String, Object>();
options.metaData.putAll(metaData);
}
return options;
}
/**
* @return the data center into which the new volume will be provisioned
*/
public @Nullable String getDataCenterId() {
return dataCenterId;
}
/**
* @return the Virtual Machine instance on which the new volume will be attached at provision time
*/
public @Nullable String getProviderVirtualMachineId(){
return providerVirtualMachineId;
}
/**
* @return a friendly description of the function of the volume to be created
*/
public @Nonnull String getDescription() {
return description;
}
/**
* @return the device ID under which the volume will be attached
*/
public @Nullable String getDeviceId() {
return deviceId;
}
/**
* @return the format for the requested volume
*/
public @Nonnull VolumeFormat getFormat() {
return format;
}
/**
* Provides the requested minimum IOPS. This value will be ignored in clouds that cannot honor it.
* @return a minimum IOPS guarantee for this volume, or 0 for no guarantees
*/
public @Nonnegative int getIops() {
return iops;
}
/**
* @return any extra meta-data to assign to the volume
*/
public @Nonnull Map<String,Object> getMetaData() {
return (metaData == null ? new HashMap<String, Object>() : metaData);
}
/**
* @return a friendly (and ideally unique) name for this value for display in different contexts
*/
public @Nonnull String getName() {
return name;
}
/**
* @return the unique ID of the {@link Snapshot} from which this volume will be created
*/
public @Nullable String getSnapshotId() {
return snapshotId;
}
/**
* @return the virtual machine to which this volume will be automatically attached post-create.
*/
public @Nullable String getVirtualMachineId() {
return virtualMachineId;
}
/**
* @return the VLAN in which the volume will be provisioned (typically specified for network volumes)
*/
public @Nullable String getVlanId() {
return vlanId;
}
/**
* @return the unique ID of the {@link VolumeProduct} to use in creating a new volume
*/
public @Nullable String getVolumeProductId() {
return volumeProductId;
}
/**
* A cloud should honor the size <em>as best as possible</em> when a request is made to create a volume. The
* Dasein implementation will favor creating a volume over exactly matching the requested size.
* @return the expected size of the volume to be created
*/
public @Nonnull Storage<Gigabyte> getVolumeSize() {
return volumeSize;
}
/**
* Specifies the data center in which the volume will be provisioned
* @param dataCenterId the unique ID of the {@link DataCenter} in which the volume will be provisioned
* @return this
*/
public @Nonnull VolumeCreateOptions inDataCenter(@Nonnull String dataCenterId) {
this.dataCenterId = dataCenterId;
return this;
}
/**
* Indicates how the volume will be attached to a specific virtual machine on create.
* @param vmId the unique ID of the {@link VirtualMachine} to which the new volume will be automatically attached after create
* @param asDeviceId the device ID (see {@link VolumeSupport#listPossibleDeviceIds(Platform)} for valid device IDs) under which it will be attached
* @return this
*/
public @Nonnull VolumeCreateOptions withAttachment(@Nonnull String vmId, @Nonnull String asDeviceId) {
this.virtualMachineId = vmId;
this.deviceId = asDeviceId;
return this;
}
/**
* Specifies any meta-data to be associated with the volume at launch time. This method is
* accretive, meaning that it adds to any existing meta-data (or replaces an existing key). Though Dasein Cloud
* allows the ability to retain type in meta-data, the reality is that most clouds will convert values to strings.
* @param key the key of the meta-data entry
* @param value the value for the meta-data entry (this will probably become a {@link java.lang.String} in most clouds)
* @return this
*/
public @Nonnull VolumeCreateOptions withMetaData(@Nonnull String key, @Nonnull Object value) {
if( metaData == null ) {
metaData = new HashMap<String, Object>();
}
metaData.put(key, value);
return this;
}
/**
* Specifies meta-data to add onto any existing meta-data being associated with this volume at launch time.
* This method is accretive, meaning that it adds to any existing meta-data (or replaces an existing keys).
* Though Dasein Cloud allows the ability to retain type in meta-data, the reality is that most clouds will convert
* values to strings.
* @param metaData the meta-data to be set for the new volume
* @return this
*/
public @Nonnull VolumeCreateOptions withMetaData(@Nonnull Map<String,Object> metaData) {
if( this.metaData == null ) {
this.metaData = new HashMap<String, Object>();
}
this.metaData.putAll(metaData);
return this;
}
public @Nonnull VolumeCreateOptions withVirtualMachineId(@Nonnull String providerVirtualMachineId){
this.providerVirtualMachineId = providerVirtualMachineId;
return this;
}
}