diff --git a/api/src/com/cloud/api/commands/UploadVolumeCmd.java b/api/src/com/cloud/api/commands/UploadVolumeCmd.java index 6df1fcd9fe8..e07ea4c6e1a 100755 --- a/api/src/com/cloud/api/commands/UploadVolumeCmd.java +++ b/api/src/com/cloud/api/commands/UploadVolumeCmd.java @@ -10,148 +10,129 @@ // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 -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; - } - -} +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +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.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.storage.Volume; +import com.cloud.user.UserContext; + +@Implementation(description="Uploads a data disk.", responseObject=VolumeResponse.class) +public class UploadVolumeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(UploadVolumeCmd.class.getName()); + private static final String s_name = "uploadvolumeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.FORMAT, type=CommandType.STRING, required=true, description="the format for the volume. Possible values include QCOW2, OVA, 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 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 execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + + Volume volume = _storageService.uploadVolume(this); + if (volume != null){ + VolumeResponse response = _responseGenerator.createVolumeResponse(volume); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to upload volume"); + } + } + + @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; + } + +} diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index 73e69d53026..9b5f9afe3f6 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -62,7 +62,9 @@ public interface Volume extends ControlledEntity, BasedOn, StateObject destroyedVolumeHostVOs = _volumeHostDao.listDestroyed(hostId); + s_logger.debug("Secondary storage garbage collector found " + destroyedVolumeHostVOs.size() + " templates to cleanup on secondary storage host: " + + secondaryStorageHost.getName()); + for (VolumeHostVO destroyedVolumeHostVO : destroyedVolumeHostVOs) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deleting volume host: " + destroyedVolumeHostVO); + } + String installPath = destroyedVolumeHostVO.getInstallPath(); + + if (installPath != null) { + Answer answer = _agentMgr.sendToSecStorage(secondaryStorageHost, new DeleteVolumeCommand(secondaryStorageHost.getStorageUrl(), destroyedVolumeHostVO.getInstallPath())); + + if (answer == null || !answer.getResult()) { + s_logger.debug("Failed to delete " + destroyedVolumeHostVO + " due to " + ((answer == null) ? "answer is null" : answer.getDetails())); + } else { + _volumeHostDao.remove(destroyedVolumeHostVO.getId()); + s_logger.debug("Deleted volume at: " + destroyedVolumeHostVO.getInstallPath()); + } + } else { + _volumeHostDao.remove(destroyedVolumeHostVO.getId()); + } + } + + }catch (Exception e2) { + s_logger.warn("problem cleaning up volumes in secondary storage " + secondaryStorageHost, e2); + } + } } catch (Exception e3) { s_logger.warn("problem cleaning up secondary storage ", e3); } @@ -3330,6 +3363,22 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (s_logger.isDebugEnabled()) { s_logger.debug("Expunging " + vol); } + + //Find out if the volume is present on secondary storage + VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(vol.getId()); + if(volumeHost != null){ + HostVO ssHost = _hostDao.findById(volumeHost.getHostId()); + DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), volumeHost.getInstallPath()); + Answer answer = _agentMgr.sendToSecStorage(ssHost, dtCommand); + if (answer == null || !answer.getResult()) { + s_logger.debug("Failed to delete " + volumeHost + " due to " + ((answer == null) ? "answer is null" : answer.getDetails())); + return; + } + _volumeHostDao.remove(volumeHost.getId()); + _volumeDao.remove(vol.getId()); + return; + } + String vmName = null; if (vol.getVolumeType() == Type.ROOT && vol.getInstanceId() != null) { VirtualMachine vm = _vmInstanceDao.findByIdIncludingRemoved(vol.getInstanceId()); diff --git a/server/src/com/cloud/storage/dao/VolumeHostDao.java b/server/src/com/cloud/storage/dao/VolumeHostDao.java index 96efd126595..f4d667ad021 100755 --- a/server/src/com/cloud/storage/dao/VolumeHostDao.java +++ b/server/src/com/cloud/storage/dao/VolumeHostDao.java @@ -14,4 +14,6 @@ public interface VolumeHostDao extends GenericDao { List listBySecStorage(long sserverId); + List listDestroyed(long hostId); + } diff --git a/server/src/com/cloud/storage/dao/VolumeHostDaoImpl.java b/server/src/com/cloud/storage/dao/VolumeHostDaoImpl.java index fb1ad0afbad..5c205fd0561 100755 --- a/server/src/com/cloud/storage/dao/VolumeHostDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VolumeHostDaoImpl.java @@ -6,6 +6,7 @@ import java.util.List; import javax.ejb.Local; import com.cloud.host.HostVO; +import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VolumeHostVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -16,6 +17,7 @@ public class VolumeHostDaoImpl extends GenericDaoBase implem protected final SearchBuilder HostVolumeSearch; protected final SearchBuilder VolumeSearch; protected final SearchBuilder HostSearch; + protected final SearchBuilder HostDestroyedSearch; VolumeHostDaoImpl(){ HostVolumeSearch = createSearchBuilder(); @@ -33,6 +35,11 @@ public class VolumeHostDaoImpl extends GenericDaoBase implem VolumeSearch.and("volume_id", VolumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); VolumeSearch.and("destroyed", VolumeSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); VolumeSearch.done(); + + HostDestroyedSearch = createSearchBuilder(); + HostDestroyedSearch.and("host_id", HostDestroyedSearch.entity().getHostId(), SearchCriteria.Op.EQ); + HostDestroyedSearch.and("destroyed", HostDestroyedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); + HostDestroyedSearch.done(); } @@ -63,5 +70,13 @@ public class VolumeHostDaoImpl extends GenericDaoBase implem sc.setParameters("destroyed", false); return listAll(); } + + @Override + public List listDestroyed(long hostId){ + SearchCriteria sc = HostDestroyedSearch.create(); + sc.setParameters("host_id", hostId); + sc.setParameters("destroyed", true); + return listIncludingRemovedBy(sc); + } } diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index c880f8f5392..2287179bf48 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -439,12 +439,7 @@ public class DownloadMonitorImpl implements DownloadMonitor { downloadJobExists = true; } - try { - _storageMgr.stateTransitTo(volume, Event.UploadRequested); - } catch (NoTransitionException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } + Long maxVolumeSizeInBytes = getMaxVolumeSizeInBytes(); String secUrl = sserver.getStorageUrl(); if(volumeHost != null) { @@ -478,7 +473,7 @@ public class DownloadMonitorImpl implements DownloadMonitor { try { send(ssvm.getId(), dcmd, dl); } catch (AgentUnavailableException e) { - s_logger.warn("Unable to start /resume download of template " + volume.getName() + " to " + sserver.getName(), e); + s_logger.warn("Unable to start /resume download of volume " + volume.getName() + " to " + sserver.getName(), e); dl.setDisconnected(); dl.scheduleStatusCheck(RequestType.GET_OR_RESTART); } @@ -551,7 +546,7 @@ public class DownloadMonitorImpl implements DownloadMonitor { else{ s_logger.warn("Failed to get size for volume" + volume.getName()); } - String eventType = EventTypes.EVENT_VOLUME_CREATE; + String eventType = EventTypes.EVENT_VOLUME_UPLOAD; if(volume.getAccountId() != Account.ACCOUNT_ID_SYSTEM){ UsageEventVO usageEvent = new UsageEventVO(eventType, volume.getAccountId(), host.getDataCenterId(), volume.getId(), volume.getName(), null, 0l , size); _usageEventDao.persist(usageEvent);