Bug 10808: Add Upload Volume api and do validation.

This commit is contained in:
Nitin Mehta 2012-03-24 13:59:59 +05:30
parent 47d01dbd3d
commit a5a7be4c7e
3 changed files with 259 additions and 0 deletions

View File

@ -0,0 +1,145 @@
package com.cloud.api.commands;
import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseAsyncCreateCmd;
import com.cloud.api.IdentityMapper;
import com.cloud.api.Implementation;
import com.cloud.api.Parameter;
import com.cloud.api.ServerApiException;
import com.cloud.api.response.VolumeResponse;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.UserContext;
@Implementation(description="Uploads a data disk.", responseObject=VolumeResponse.class)
public class UploadVolumeCmd extends BaseAsyncCreateCmd {
public static final Logger s_logger = Logger.getLogger(UploadVolumeCmd.class.getName());
private static final String s_name = "uploadvolumeresponse";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, required=true, description="the display text of the volume. This is usually used for display purposes.", length=4096)
private String displayText;
@Parameter(name=ApiConstants.FORMAT, type=CommandType.STRING, required=true, description="the format for the volume. Possible values include QCOW2, RAW, and VHD.")
private String format;
@Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, required=true, description="the target hypervisor for the volume")
private String hypervisor;
@Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="the name of the volume")
private String volumeName;
@Parameter(name=ApiConstants.URL, type=CommandType.STRING, required=true, description="the URL of where the volume is hosted. Possible URL include http:// and https://")
private String url;
@IdentityMapper(entityTableName="data_center")
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="the ID of the zone the volume is to be hosted on")
private Long zoneId;
@IdentityMapper(entityTableName="domain")
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId. If the account parameter is used, domainId must also be used.")
private Long domainId;
@Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="an optional accountName. Must be used with domainId.")
private String accountName;
@Parameter(name=ApiConstants.CHECKSUM, type=CommandType.STRING, description="the MD5 checksum value of this volume")
private String checksum;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getDisplayText() {
return displayText;
}
public String getFormat() {
return format;
}
public String getHypervisor() {
return hypervisor;
}
public String getVolumeName() {
return volumeName;
}
public String getUrl() {
return url;
}
public Long getZoneId() {
return zoneId;
}
public Long getDomainId() {
return domainId;
}
public String getAccountName() {
return accountName;
}
public String getChecksum() {
return checksum;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void create() throws ResourceAllocationException {
}
@Override
public String getEntityTable() {
return "volumes";
}
@Override
public String getEventDescription() {
return "creating volume: " + getVolumeName();
}
@Override
public String getEventType() {
return EventTypes.EVENT_VOLUME_CREATE;
}
@Override
public void execute() throws ResourceUnavailableException,
InsufficientCapacityException, ServerApiException,
ConcurrentOperationException, ResourceAllocationException,
NetworkRuleConflictException {
// TODO Auto-generated method stub
}
@Override
public String getCommandName() {
return s_name;
}
@Override
public long getEntityOwnerId() {
Long accountId = finalyzeAccountId(accountName, domainId, null, true);
if (accountId == null) {
return UserContext.current().getCaller().getId();
}
return accountId;
}
}

View File

@ -200,6 +200,7 @@ updateHostPassword=com.cloud.api.commands.UpdateHostPasswordCmd;1
#### volume commands
attachVolume=com.cloud.api.commands.AttachVolumeCmd;15
uploadVolume=com.cloud.api.commands.UploadVolumeCmd;15
detachVolume=com.cloud.api.commands.DetachVolumeCmd;15
createVolume=com.cloud.api.commands.CreateVolumeCmd;15
deleteVolume=com.cloud.api.commands.DeleteVolumeCmd;15

View File

@ -18,6 +18,8 @@
package com.cloud.storage;
import java.math.BigDecimal;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
@ -75,6 +77,7 @@ import com.cloud.api.commands.CreateVolumeCmd;
import com.cloud.api.commands.DeletePoolCmd;
import com.cloud.api.commands.ListVolumesCmd;
import com.cloud.api.commands.UpdateStoragePoolCmd;
import com.cloud.api.commands.UploadVolumeCmd;
import com.cloud.async.AsyncJobManager;
import com.cloud.capacity.Capacity;
import com.cloud.capacity.CapacityVO;
@ -1613,6 +1616,115 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
return _volsDao.findById(volume.getId());
}
/*
* Just allocate a volume in the database, don't send the createvolume cmd to hypervisor. The volume will be finally
* created
* only when it's attached to a VM.
*/
// @Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
public VolumeVO createVolumeEntry(UploadVolumeCmd cmd) throws ResourceAllocationException{
Account caller = UserContext.current().getCaller();
long ownerId = cmd.getEntityOwnerId();
Long zoneId = cmd.getZoneId();
String volumeName = cmd.getVolumeName();
String url = cmd.getUrl();
validateVolume(caller, ownerId, zoneId, volumeName, url, cmd.getFormat());
persistVolume();
return null;
}
private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) throws ResourceAllocationException{
// permission check
_accountMgr.checkAccess(caller, null, true, _accountMgr.getActiveAccountById(ownerId));
// Check that the resource limit for volumes won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume);
// Verify that zone exists
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
}
// Check if zone is disabled
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
}
if (url.toLowerCase().contains("file://")) {
throw new InvalidParameterValueException("File:// type urls are currently unsupported");
}
String userSpecifiedName = volumeName;
if (userSpecifiedName == null) {
userSpecifiedName = getRandomVolumeName();
}
if((!url.toLowerCase().endsWith("vhd"))&&(!url.toLowerCase().endsWith("vhd.zip"))
&&(!url.toLowerCase().endsWith("vhd.bz2"))&&(!url.toLowerCase().endsWith("vhd.gz"))
&&(!url.toLowerCase().endsWith("qcow2"))&&(!url.toLowerCase().endsWith("qcow2.zip"))
&&(!url.toLowerCase().endsWith("qcow2.bz2"))&&(!url.toLowerCase().endsWith("qcow2.gz"))
&&(!url.toLowerCase().endsWith("ova"))&&(!url.toLowerCase().endsWith("ova.zip"))
&&(!url.toLowerCase().endsWith("ova.bz2"))&&(!url.toLowerCase().endsWith("ova.gz"))
&&(!url.toLowerCase().endsWith("img"))&&(!url.toLowerCase().endsWith("raw"))){
throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase());
}
if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith("vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase().endsWith("vhd.gz") ))
|| (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith("qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz") ))
|| (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith("ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase().endsWith("ova.gz")))
|| (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith("img") && !url.toLowerCase().endsWith("raw")))) {
throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase());
}
validateUrl(url);
return false;
}
private String validateUrl(String url){
try {
URI uri = new URI(url);
if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http")
&& !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) {
throw new IllegalArgumentException("Unsupported scheme for url: " + url);
}
int port = uri.getPort();
if (!(port == 80 || port == 443 || port == -1)) {
throw new IllegalArgumentException("Only ports 80 and 443 are allowed");
}
String host = uri.getHost();
try {
InetAddress hostAddr = InetAddress.getByName(host);
if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) {
throw new IllegalArgumentException("Illegal host specified in url");
}
if (hostAddr instanceof Inet6Address) {
throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")");
}
} catch (UnknownHostException uhe) {
throw new IllegalArgumentException("Unable to resolve " + host);
}
return uri.toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid URL " + url);
}
}
private boolean persistVolume() {
return false;
}
/*
* Just allocate a volume in the database, don't send the createvolume cmd to hypervisor. The volume will be finally
* created
@ -1714,6 +1826,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
* throw new UnsupportedServiceException("operation not supported, snapshot with id " + snapshotId +
* " is created from ROOT volume");
* }
*
*/
}