From 97f3068687145e975e7521d263b26d2904d95bc2 Mon Sep 17 00:00:00 2001 From: Kris McQueen Date: Thu, 19 Aug 2010 17:40:39 -0700 Subject: [PATCH] Work in progress refactoring createTemplate command to new API. The logic had been partially moved to the manager, still need to finish up moving the logic, then delete the unnecessary code. --- .../impl/UserConcentratedAllocator.java | 3 +- .../cloud/api/commands/CreateTemplateCmd.java | 14 +- .../cloud/server/ManagementServerImpl.java | 10 +- server/src/com/cloud/vm/UserVmManager.java | 18 ++- .../src/com/cloud/vm/UserVmManagerImpl.java | 122 +++++++++++++++--- 5 files changed, 135 insertions(+), 32 deletions(-) diff --git a/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java index 9d5d50442f5..8c628dee712 100755 --- a/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java @@ -170,14 +170,13 @@ public class UserConcentratedAllocator implements PodAllocator { // if ((capacity.getTotalCapacity() - calcHostAllocatedCpuMemoryCapacity(capacity.getHostOrPoolId(), capacityType)) >= capacityNeeded) { - + hostCandidate[0] = capacity.getHostOrPoolId(); enoughCapacity = true; break; } } else { if ((capacity.getTotalCapacity() - capacity.getUsedCapacity()) >= capacityNeeded) { - hostCandidate[0] = capacity.getHostOrPoolId(); enoughCapacity = true; break; diff --git a/server/src/com/cloud/api/commands/CreateTemplateCmd.java b/server/src/com/cloud/api/commands/CreateTemplateCmd.java index ec09f0c8f09..8cfa65323d0 100644 --- a/server/src/com/cloud/api/commands/CreateTemplateCmd.java +++ b/server/src/com/cloud/api/commands/CreateTemplateCmd.java @@ -25,6 +25,8 @@ import java.util.Map; import org.apache.log4j.Logger; import com.cloud.api.BaseCmd; +import com.cloud.api.BaseCmd.Manager; +import com.cloud.api.BaseAsyncCreateCmd; import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.async.executor.CreatePrivateTemplateResultObject; @@ -35,8 +37,9 @@ import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; import com.cloud.user.Account; import com.cloud.utils.Pair; - -public class CreateTemplateCmd extends BaseCmd { + +@Implementation(method="createPrivateTemplate", createMethod="createPrivateTemplateRecord", manager=Manager.UserVmManager) +public class CreateTemplateCmd extends BaseAsyncCreateCmd { public static final Logger s_logger = Logger.getLogger(CreateTemplateCmd.class.getName()); private static final String s_name = "createtemplateresponse"; private static final List> s_properties = new ArrayList>(); @@ -141,6 +144,7 @@ public class CreateTemplateCmd extends BaseCmd { /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + @Override public String getName() { return s_name; } @@ -148,9 +152,9 @@ public class CreateTemplateCmd extends BaseCmd { public static String getResultObjectName() { return "template"; } - - public List> getProperties() { - return s_properties; + + @Override + public String getResponse() { } @Override diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 86d5c9d1382..86b1800261f 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -6664,19 +6664,17 @@ public class ManagementServerImpl implements ManagementServer { @Override public long createPrivateTemplateAsync(Long userId, long volumeId, String name, String description, long guestOSId, Boolean requiresHvm, Integer bits, Boolean passwordEnabled, boolean isPublic, boolean featured, Long snapshotId) throws InvalidParameterValueException, ResourceAllocationException, InternalErrorException { - if (name.length() > 32) - { + if (name.length() > 32) { throw new InvalidParameterValueException("Template name should be less than 32 characters"); } - - if(!name.matches("^[\\p{Alnum} ._-]+")) - { + + if (!name.matches("^[\\p{Alnum} ._-]+")) { throw new InvalidParameterValueException("Only alphanumeric, space, dot, dashes and underscore characters allowed"); } // The volume below could be destroyed or removed. VolumeVO volume = _volumeDao.findById(volumeId); - + // If private template is created from Volume, check that the volume will not be active when the private template is created if (snapshotId == null && !_storageMgr.volumeInactive(volume)) { String msg = "Unable to create private template for volume: " + volume.getName() + "; volume is attached to a non-stopped VM."; diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 0ae15dfdadd..d8b21be53e5 100644 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -22,6 +22,7 @@ import java.util.List; import com.cloud.agent.api.VmStatsEntry; import com.cloud.api.ServerApiException; +import com.cloud.api.commands.CreateTemplateCmd; import com.cloud.api.commands.DetachVolumeCmd; import com.cloud.api.commands.RebootVMCmd; import com.cloud.api.commands.RecoverVMCmd; @@ -42,6 +43,7 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientStorageCapacityException; import com.cloud.exception.InternalErrorException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.StorageUnavailableException; import com.cloud.network.security.NetworkGroupVO; @@ -199,18 +201,26 @@ public interface UserVmManager extends Manager, VirtualMachineManager boolean recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException, InternalErrorException; - VMTemplateVO createPrivateTemplateRecord(Long userId, long vmId, String name, String description, long guestOsId, Boolean requiresHvm, Integer bits, Boolean passwordEnabled, boolean isPublic, boolean featured) - throws InvalidParameterValueException; + /** + * Create a template database record in preparation for creating a private template. + * @param cmd the command object that defines the name, display text, snapshot/volume, bits, public/private, etc. + * for the private template + * @return the vm template object if successful, null otherwise + * @throws InvalidParameterValueException + */ + VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd) throws InvalidParameterValueException, PermissionDeniedException; /** * Creates a private template from a snapshot of a VM - * @param template the template record that is used to store data (we need instance be created first) + * @param cmd the command object that defines + * + * template the template record that is used to store data (we need instance be created first) * @param snapshotId the id of the snaphot to use for the template * @param name the user given name of the private template * @param description the user give description (aka display text) for the template * @return a template if successfully created, null otherwise */ - VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description); + VMTemplateVO createPrivateTemplate(CreateTemplateCmd cmd); /** * @param userId The Id of the user who invoked this operation. diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 54d853972e8..68b39470d16 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -66,6 +66,7 @@ import com.cloud.agent.manager.AgentManager; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; import com.cloud.api.ServerApiException; +import com.cloud.api.commands.CreateTemplateCmd; import com.cloud.api.commands.DetachVolumeCmd; import com.cloud.api.commands.RebootVMCmd; import com.cloud.api.commands.RecoverVMCmd; @@ -114,6 +115,7 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InternalErrorException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.StorageUnavailableException; import com.cloud.ha.HighAvailabilityManager; @@ -1844,7 +1846,7 @@ public class UserVmManagerImpl implements UserVmManager { s_logger.debug("Execute asynchronize destroy VM command:" + resultDescription); return response; } - long childEventId = executor.getAsyncJobMgr().getExecutorContext().getManagementServer().saveStartedEvent(param.getUserId(), param.getAccountId(), + long childEventId = EventUtils.saveStartedEvent(param.getUserId(), param.getAccountId(), EventTypes.EVENT_VM_STOP, "stopping vm " + vm.getName(), 0); param.setChildEventId(childEventId); StopCommand cmd = new StopCommand(vm, vm.getInstanceName(), vm.getVnet()); @@ -2585,10 +2587,17 @@ public class UserVmManagerImpl implements UserVmManager { } } } - - public VMTemplateVO createPrivateTemplateRecord(Long userId, long volumeId, String name, String description, long guestOSId, Boolean requiresHvm, Integer bits, Boolean passwordEnabled, boolean isPublic, boolean featured) - throws InvalidParameterValueException { + @Override + public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd) throws InvalidParameterValueException, PermissionDeniedException { + Long userId = UserContext.current().getUserId(); + if (userId == null) { + userId = User.UID_SYSTEM; + } + + Account account = (Account)UserContext.current().getAccountObject(); + boolean isAdmin = ((account == null) || isAdmin(account.getType())); + VMTemplateVO privateTemplate = null; UserVO user = _userDao.findById(userId); @@ -2597,16 +2606,70 @@ public class UserVmManagerImpl implements UserVmManager { throw new InvalidParameterValueException("User " + userId + " does not exist"); } + Long volumeId = cmd.getVolumeId(); + Long snapshotId = cmd.getSnapshotId(); + if (volumeId == null) { + if (snapshotId == null) { + throw new InvalidParameterValueException("Failed to create private template record, neither volume ID nor snapshot ID were specified."); + } + SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + if (snapshot == null) { + throw new InvalidParameterValueException("Failed to create private template record, unable to find snapshot " + snapshotId); + } + volumeId = snapshot.getVolumeId(); + } else { + if (snapshotId != null) { + throw new InvalidParameterValueException("Failed to create private template record, please specify only one of volume ID (" + volumeId + ") and snapshot ID (" + snapshotId + ")"); + } + } + VolumeVO volume = _volsDao.findById(volumeId); if (volume == null) { throw new InvalidParameterValueException("Volume with ID: " + volumeId + " does not exist"); } + if (!isAdmin) { + if (account.getId().longValue() != volume.getAccountId()) { + throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, "unable to find a volume with id " + volumeId + " for this account"); + } + } else if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), volume.getDomainId())) { + throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, "Unable to create a template from volume with id " + volumeId + ", permission denied."); + } + + String name = cmd.getTemplateName(); + if ((name == null) || (name.length() > 32)) { + throw new InvalidParameterValueException("Template name cannot be null and should be less than 32 characters"); + } + + if (!name.matches("^[\\p{Alnum} ._-]+")) { + throw new InvalidParameterValueException("Only alphanumeric, space, dot, dashes and underscore characters allowed"); + } + String uniqueName = Long.valueOf((userId == null)?1:userId).toString() + Long.valueOf(volumeId).toString() + UUID.nameUUIDFromBytes(name.getBytes()).toString(); + + // do some parameter defaulting + Integer bits = cmd.getBits(); + Boolean requiresHvm = cmd.getRequiresHvm(); + Boolean passwordEnabled = cmd.isPasswordEnabled(); + Boolean isPublic = cmd.isPublic(); + Boolean featured = cmd.isFeatured(); + int bitsValue = ((bits == null) ? 64 : bits.intValue()); boolean requiresHvmValue = ((requiresHvm == null) ? true : requiresHvm.booleanValue()); boolean passwordEnabledValue = ((passwordEnabled == null) ? false : passwordEnabled.booleanValue()); + if (isPublic == null) { + isPublic = Boolean.FALSE; + } + + if (!isAdmin || featured == null) { + featured = Boolean.FALSE; + } - // if the volume is a root disk, try to find out requiresHvm and bits if possible + boolean allowPublicUserTemplates = Boolean.parseBoolean(_configDao.getValue("allow.public.user.templates")); + if (!isAdmin && !allowPublicUserTemplates && isPublic) { + throw new PermissionDeniedException("Failed to create template " + name + ", only private templates can be created."); + } + + // if the volume is a root disk, try to find out requiresHvm and bits if possible if (Volume.VolumeType.ROOT.equals(volume.getVolumeType())) { Long instanceId = volume.getInstanceId(); if (instanceId != null) { @@ -2621,13 +2684,14 @@ public class UserVmManagerImpl implements UserVmManager { } } + Long guestOSId = cmd.getOsTypeId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); if (guestOS == null) { throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist."); } - String uniqueName = Long.valueOf((userId == null)?1:userId).toString() + Long.valueOf(volumeId).toString() + UUID.nameUUIDFromBytes(name.getBytes()).toString(); Long nextTemplateId = _templateDao.getNextInSequence(Long.class, "id"); + String description = cmd.getDisplayText(); privateTemplate = new VMTemplateVO(nextTemplateId, uniqueName, @@ -2647,17 +2711,46 @@ public class UserVmManagerImpl implements UserVmManager { guestOS.getId(), true); + // FIXME: scheduled events should get saved when the command is actually scheduled, not when it starts executing, need another callback + // for when the command is scheduled? Could this fit into the setup / execute / response lifecycle? Right after setup you would + // know your job is being scheduled, so you could save this kind of event in setup after verifying params. + long eventId = EventUtils.saveScheduledEvent(userId, volume.getAccountId(), EventTypes.EVENT_TEMPLATE_CREATE, "creating template" +name); + return _templateDao.persist(privateTemplate); } @Override @DB - public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description) { - VMTemplateVO privateTemplate = null; - long templateId = template.getId(); - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); - if (snapshot != null) { - Long volumeId = snapshot.getVolumeId(); - VolumeVO volume = _volsDao.findById(volumeId); + public VMTemplateVO createPrivateTemplate(CreateTemplateCmd command) { + Long volumeId = command.getVolumeId(); + Long snapshotId = command.getSnapshotId(); + + // Verify input parameters + if (snapshotId != null) { + Snapshot snapshot = _snapshotDao.findById(snapshotId); + + // Set the volumeId to that of the snapshot. All further input parameter checks will be done w.r.t the volume. + volumeId = snapshot.getVolumeId(); + } + + // The volume below could be destroyed or removed. + VolumeVO volume = _volsDao.findById(volumeId); + + // If private template is created from Volume, check that the volume will not be active when the private template is created + if (snapshotId == null && !_storageMgr.volumeInactive(volume)) { + String msg = "Unable to create private template for volume: " + volume.getName() + "; volume is attached to a non-stopped VM."; + + if (s_logger.isInfoEnabled()) { + s_logger.info(msg); + } + + throw new InternalErrorException(msg); + } + + VMTemplateVO privateTemplate = null; + long templateId = command.getId(); + if (snapshotId != null) { + volumeId = snapshot.getVolumeId(); + volume = _volsDao.findById(volumeId); StringBuilder userFolder = new StringBuilder(); Formatter userFolderFormat = new Formatter(userFolder); userFolderFormat.format("u%06d", snapshot.getAccountId()); @@ -2698,8 +2791,7 @@ public class UserVmManagerImpl implements UserVmManager { origTemplateInstallPath, templateId, name); - } - else { + } else { cmd = new CreatePrivateTemplateCommand(secondaryStorageURL, templateId, volume.getAccountId(),