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(),