diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java index f4f54499aa4..f0466b765f5 100755 --- a/api/src/com/cloud/api/ApiConstants.java +++ b/api/src/com/cloud/api/ApiConstants.java @@ -328,6 +328,8 @@ public class ApiConstants { public static final String PURPOSE = "purpose"; public static final String IS_TAGGED = "istagged"; public static final String INSTANCE_NAME = "instancename"; + public static final String START_VM = "startvm"; + public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java index 86a2f850aab..5c684f86186 100644 --- a/api/src/com/cloud/api/commands/DeployVMCmd.java +++ b/api/src/com/cloud/api/commands/DeployVMCmd.java @@ -132,6 +132,9 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="Deploy vm for the project") private Long projectId; + @Parameter(name=ApiConstants.START_VM, type=CommandType.BOOLEAN, description="true if network offering supports specifying ip ranges; defaulted to true if not specified") + private Boolean startVm; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -223,7 +226,6 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { return networks; } } - return networkIds; } @@ -239,6 +241,10 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { return hostId; } + public boolean getStartVm() { + return startVm == null ? true : startVm; + } + private Map getIpToNetworkMap() { if ((networkIds != null || ipAddress != null) && ipToNetworkList != null) { throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); @@ -311,32 +317,37 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @Override public void execute(){ UserVm result; - try { - UserContext.current().setEventDetails("Vm Id: "+getEntityId()); - if (getHypervisor() == HypervisorType.BareMetal) { - result = _bareMetalVmService.startVirtualMachine(this); - } else { - result = _userVmService.startVirtualMachine(this); + + if (getStartVm()) { + try { + UserContext.current().setEventDetails("Vm Id: "+getEntityId()); + if (getHypervisor() == HypervisorType.BareMetal) { + result = _bareMetalVmService.startVirtualMachine(this); + } else { + result = _userVmService.startVirtualMachine(this); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientCapacityException ex) { + s_logger.info(ex); + s_logger.trace(ex); + throw new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); } - - if (result != null) { - UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", result).get(0); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to deploy vm"); - } - } catch (ResourceUnavailableException ex) { - s_logger.warn("Exception: ", ex); - throw new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); - } catch (ConcurrentOperationException ex) { - s_logger.warn("Exception: ", ex); - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, ex.getMessage()); - } catch (InsufficientCapacityException ex) { - s_logger.info(ex); - s_logger.trace(ex); - throw new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); - } + } else { + result = _userVmService.getUserVm(getEntityId()); + } + + if (result != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to deploy vm"); + } } @Override diff --git a/api/src/com/cloud/api/commands/StartVMCmd.java b/api/src/com/cloud/api/commands/StartVMCmd.java index caf3e9445ed..6fb3e509870 100644 --- a/api/src/com/cloud/api/commands/StartVMCmd.java +++ b/api/src/com/cloud/api/commands/StartVMCmd.java @@ -86,7 +86,7 @@ public class StartVMCmd extends BaseAsyncCmd { } return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are -// tracked + // tracked } @Override @@ -111,7 +111,8 @@ public class StartVMCmd extends BaseAsyncCmd { public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { try { UserContext.current().setEventDetails("Vm Id: " + getId()); - UserVm result; + + UserVm result ; if (_userVmService.getHypervisorTypeOfUserVM(getId()) == HypervisorType.BareMetal) { result = _bareMetalVmService.startVirtualMachine(this); } else { diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index af5872fc3bc..8b8eb205b36 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -50,6 +50,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.uservm.UserVm; import com.cloud.utils.exception.ExecutionException; +import com.cloud.vm.VirtualMachineProfile.Param; public interface UserVmService { /** @@ -364,8 +365,6 @@ public interface UserVmService { UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOperationException; - UserVm startVirtualMachine(long vmId, Long hostId) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; - void deletePrivateTemplateRecord(Long templateId); /** diff --git a/core/src/com/cloud/vm/UserVmVO.java b/core/src/com/cloud/vm/UserVmVO.java index 7c65065c924..bc5026b37dc 100755 --- a/core/src/com/cloud/vm/UserVmVO.java +++ b/core/src/com/cloud/vm/UserVmVO.java @@ -38,6 +38,9 @@ public class UserVmVO extends VMInstanceVO implements UserVm { @Column(name="display_name", updatable=true, nullable=true) private String displayName; + @Column(name="update_parameters", updatable=true) + protected boolean updateParameters = true; + transient String password; @Override @@ -119,4 +122,12 @@ public class UserVmVO extends VMInstanceVO implements UserVm { public void setDomainId(long domainId){ this.domainId = domainId; } + + public void setUpdateParameters(boolean updateParameters) { + this.updateParameters = updateParameters; + } + + public boolean isUpdateParameters() { + return updateParameters; + } } diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java index a03cf9eec87..2677e5ce384 100755 --- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -32,6 +32,7 @@ import com.cloud.api.commands.AttachVolumeCmd; import com.cloud.api.commands.CreateTemplateCmd; import com.cloud.api.commands.DeployVMCmd; import com.cloud.api.commands.DetachVolumeCmd; +import com.cloud.api.commands.StartVMCmd; import com.cloud.api.commands.UpgradeVMCmd; import com.cloud.baremetal.PxeServerManager.PxeServerType; import com.cloud.configuration.Resource.ResourceType; @@ -383,8 +384,7 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet } public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { - long vmId = cmd.getEntityId(); - UserVmVO vm = _vmDao.findById(vmId); + UserVmVO vm = _vmDao.findById(cmd.getInstanceId()); List servers = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); if (servers.size() == 0) { @@ -402,6 +402,30 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet return startVirtualMachine(cmd, params); } + + + public UserVm startVirtualMachine(StartVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { + UserVmVO vm = _vmDao.findById(cmd.getInstanceId()); + + VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); + if (template == null || template.getFormat() != Storage.ImageFormat.BAREMETAL) { + throw new InvalidParameterValueException("Invalid template with id = " + vm.getTemplateId()); + } + + Map params = null; + if (vm.isUpdateParameters()) { + List servers = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); + if (servers.size() == 0) { + throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); + } + HostVO pxeServer = servers.get(0); + params = new HashMap(); + params.put(Param.PxeSeverType, _pxeMgr.getPxeServerType(pxeServer)); + } + + Pair> vmDetailsPair = super.startVirtualMachine(vm.getId(), cmd.getHostId(), params); + return vmDetailsPair.first(); + } @Override public boolean configure(String name, Map params) throws ConfigurationException { diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index f9dfd6a8a23..35254cb8a3b 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -3046,11 +3046,17 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag } recreateVols.add(vol); } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Volume " + vol + " is not recreatable! Cannot recreate on storagepool: " + assignedPool); + if (assignedPool.getId() != vol.getPoolId()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Volume " + vol + " is not recreatable! Cannot recreate on storagepool: " + assignedPool); + } + throw new StorageUnavailableException("Volume is not recreatable, Unable to create " + vol, Volume.class, vol.getId()); + // copy volume usecase - not yet developed. + } else { + StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); + vm.addDisk(new VolumeTO(vol, pool)); } - throw new StorageUnavailableException("Volume is not recreatable, Unable to create " + vol, Volume.class, vol.getId()); - // copy volume usecase - not yet developed. + } } } else { diff --git a/server/src/com/cloud/vm/ItWorkDao.java b/server/src/com/cloud/vm/ItWorkDao.java index 469597366f6..2eafb14ca21 100644 --- a/server/src/com/cloud/vm/ItWorkDao.java +++ b/server/src/com/cloud/vm/ItWorkDao.java @@ -37,4 +37,5 @@ public interface ItWorkDao extends GenericDao { boolean updateStep(ItWorkVO work, Step step); List listWorkInProgressFor(long nodeId); + } diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 619a62f2473..864c589df41 100755 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -14,12 +14,17 @@ package com.cloud.vm; import java.util.HashMap; import java.util.List; +import java.util.Map; import com.cloud.agent.api.VmStatsEntry; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.server.Criteria; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; /** * @@ -91,4 +96,7 @@ public interface UserVmManager extends VirtualMachineGuru, UserVmServi List searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, List permittedAccounts, boolean listAll, ListProjectResourcesCriteria listProjectResourcesCriteria); String getChecksum(Long hostId, String templatePath); + + Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; + } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a515dd98457..77ab07fb8a2 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -73,12 +73,10 @@ import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; import com.cloud.async.BaseAsyncJobExecutor; -import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.configuration.dao.ResourceLimitDao; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; @@ -86,7 +84,6 @@ import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; -import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.domain.DomainVO; @@ -94,7 +91,6 @@ import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventVO; -import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -123,13 +119,11 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; -import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.lb.LoadBalancingRulesManager; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.PortForwardingRuleVO; @@ -173,7 +167,6 @@ import com.cloud.storage.Volume; import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; -import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.StoragePoolDao; @@ -197,7 +190,6 @@ import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; -import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -221,7 +213,6 @@ import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.AnnotationHelper; import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.InstanceGroupVMMapDao; import com.cloud.vm.dao.NicDao; @@ -238,14 +229,10 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected HostDao _hostDao = null; @Inject - protected DomainRouterDao _routerDao = null; - @Inject protected ServiceOfferingDao _offeringDao = null; @Inject protected DiskOfferingDao _diskOfferingDao = null; @Inject - protected UserStatisticsDao _userStatsDao = null; - @Inject protected VMTemplateDao _templateDao = null; @Inject protected VMTemplateDetailsDao _templateDetailsDao = null; @@ -256,8 +243,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected DomainDao _domainDao = null; @Inject - protected ResourceLimitDao _limitDao = null; - @Inject protected UserVmDao _vmDao = null; @Inject protected VolumeDao _volsDao = null; @@ -268,16 +253,12 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected LoadBalancerVMMapDao _loadBalancerVMMapDao = null; @Inject - protected LoadBalancerDao _loadBalancerDao = null; - @Inject protected PortForwardingRulesDao _portForwardingDao; @Inject protected IPAddressDao _ipAddressDao = null; @Inject protected HostPodDao _podDao = null; @Inject - protected CapacityDao _capacityDao = null; - @Inject protected NetworkManager _networkMgr = null; @Inject protected StorageManager _storageMgr = null; @@ -296,8 +277,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected GuestOSDao _guestOSDao = null; @Inject - protected GuestOSCategoryDao _guestOSCategoryDao = null; - @Inject protected HighAvailabilityManager _haMgr = null; @Inject protected AlertManager _alertMgr = null; @@ -308,22 +287,16 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected AsyncJobManager _asyncMgr; @Inject - protected VlanDao _vlanDao; - @Inject protected ClusterDao _clusterDao; @Inject protected StoragePoolDao _storagePoolDao; @Inject - protected VMTemplateHostDao _vmTemplateHostDao; - @Inject protected SecurityGroupManager _securityGroupMgr; @Inject protected ServiceOfferingDao _serviceOfferingDao; @Inject protected NetworkOfferingDao _networkOfferingDao; @Inject - protected EventDao _eventDao = null; - @Inject protected InstanceGroupDao _vmGroupDao; @Inject protected InstanceGroupVMMapDao _groupVMMapDao; @@ -332,8 +305,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject protected NetworkDao _networkDao; @Inject - protected VirtualNetworkApplianceManager _routerMgr; - @Inject protected NicDao _nicDao; @Inject protected RulesManager _rulesMgr; @@ -363,6 +334,8 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager protected NetworkServiceMapDao _ntwkSrvcDao; @Inject SecurityGroupVMMapDao _securityGroupVMMapDao; + @Inject + protected ItWorkDao _workDao; protected ScheduledExecutorService _executor = null; protected int _expungeInterval; @@ -593,29 +566,14 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager rootVolumeOfVm = rootVolumesOfVm.get(0); } - HypervisorType rootDiskHyperType = _volsDao.getHypervisorType(rootVolumeOfVm.getId()); - - if (volume.getState().equals(Volume.State.Allocated)) { - /* Need to create the volume */ - VMTemplateVO rootDiskTmplt = _templateDao.findById(vm.getTemplateId()); - DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); - HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); - StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); - ServiceOfferingVO svo = _serviceOfferingDao.findById(vm.getServiceOfferingId()); - DiskOfferingVO diskVO = _diskOfferingDao.findById(volume.getDiskOfferingId()); - - volume = _storageMgr.createVolume(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); - - if (volume == null) { - throw new CloudRuntimeException("Failed to create volume when attaching it to VM: " + vm.getHostName()); - } - } - + HypervisorType rootDiskHyperType = vm.getHypervisorType(); + HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume.getId()); - if (rootDiskHyperType != dataDiskHyperType) { + if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) { throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm"); } - + + //allocate deviceId List vols = _volsDao.findByInstance(vmId); if (deviceId != null) { if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) { @@ -638,46 +596,71 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } deviceId = Long.parseLong(devIds.iterator().next()); } - - StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); - DiskOfferingVO volumeDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); - String[] volumeTags = volumeDiskOffering.getTagsArray(); - - StoragePoolVO sourcePool = _storagePoolDao.findById(volume.getPoolId()); - List sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, true); - boolean moveVolumeNeeded = true; - if (sharedVMPools.size() == 0) { - String poolType; - if (vmRootVolumePool.getClusterId() != null) { - poolType = "cluster"; - } else if (vmRootVolumePool.getPodId() != null) { - poolType = "pod"; - } else { - poolType = "zone"; - } - throw new CloudRuntimeException("There are no storage pools in the VM's " + poolType + " with all of the volume's tags (" + volumeDiskOffering.getTags() + ")."); - } else { - Long sourcePoolDcId = sourcePool.getDataCenterId(); - Long sourcePoolPodId = sourcePool.getPodId(); - Long sourcePoolClusterId = sourcePool.getClusterId(); - for (StoragePoolVO vmPool : sharedVMPools) { - Long vmPoolDcId = vmPool.getDataCenterId(); - Long vmPoolPodId = vmPool.getPodId(); - Long vmPoolClusterId = vmPool.getClusterId(); - - if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId) { - moveVolumeNeeded = false; - break; - } - } + + boolean createVolumeOnBackend = true; + if (rootVolumeOfVm.getState() == Volume.State.Allocated) { + createVolumeOnBackend = false; } - if (moveVolumeNeeded) { - // Move the volume to a storage pool in the VM's zone, pod, or cluster - try { - volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException(e.toString()); + //create volume on the backend only when vm's root volume is allocated + if (createVolumeOnBackend) { + if (volume.getState().equals(Volume.State.Allocated)) { + /* Need to create the volume */ + VMTemplateVO rootDiskTmplt = _templateDao.findById(vm.getTemplateId()); + DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); + HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); + StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); + ServiceOfferingVO svo = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + DiskOfferingVO diskVO = _diskOfferingDao.findById(volume.getDiskOfferingId()); + Long clusterId = (rootDiskPool == null ? null : rootDiskPool.getClusterId()); + + volume = _storageMgr.createVolume(volume, vm, rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); + + if (volume == null) { + throw new CloudRuntimeException("Failed to create volume when attaching it to VM: " + vm.getHostName()); + } + } + + StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); + DiskOfferingVO volumeDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + String[] volumeTags = volumeDiskOffering.getTagsArray(); + + StoragePoolVO sourcePool = _storagePoolDao.findById(volume.getPoolId()); + List sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, true); + boolean moveVolumeNeeded = true; + if (sharedVMPools.size() == 0) { + String poolType; + if (vmRootVolumePool.getClusterId() != null) { + poolType = "cluster"; + } else if (vmRootVolumePool.getPodId() != null) { + poolType = "pod"; + } else { + poolType = "zone"; + } + throw new CloudRuntimeException("There are no storage pools in the VM's " + poolType + " with all of the volume's tags (" + volumeDiskOffering.getTags() + ")."); + } else { + Long sourcePoolDcId = sourcePool.getDataCenterId(); + Long sourcePoolPodId = sourcePool.getPodId(); + Long sourcePoolClusterId = sourcePool.getClusterId(); + for (StoragePoolVO vmPool : sharedVMPools) { + Long vmPoolDcId = vmPool.getDataCenterId(); + Long vmPoolPodId = vmPool.getPodId(); + Long vmPoolClusterId = vmPool.getClusterId(); + + if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId) { + moveVolumeNeeded = false; + break; + } + } + } + + if (moveVolumeNeeded) { + // Move the volume to a storage pool in the VM's zone, pod, or cluster + try { + volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException(e.toString()); + } } } @@ -774,17 +757,17 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (volume.getVolumeType() != Volume.Type.DATADISK) { throw new InvalidParameterValueException("Please specify a data volume."); } - - // Check that the volume is stored on shared storage - if (!_storageMgr.volumeOnSharedStoragePool(volume)) { - throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); - } - + // Check that the volume is currently attached to a VM if (vmId == null) { throw new InvalidParameterValueException("The specified volume is not attached to a VM."); } + // Check that the volume is stored on shared storage + if (volume.getState() != Volume.State.Allocated && !_storageMgr.volumeOnSharedStoragePool(volume)) { + throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); + } + // Check that the VM is in the correct state UserVmVO vm = _vmDao.findById(vmId); if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) { @@ -1661,9 +1644,13 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager // used for vm transitioning to error state private void updateVmStateForFailedVmCreation(Long vmId) { + UserVmVO vm = _vmDao.findById(vmId); + + if (vm != null) { if (vm.getState().equals(State.Stopped)) { + s_logger.debug("Destroying vm " + vm + " as it failed to create"); try { _itMgr.stateTransitTo(vm, VirtualMachine.Event.OperationFailedToError, null); } catch (NoTransitionException e1) { @@ -1815,7 +1802,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Override @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - return startVirtualMachine(cmd.getId(), cmd.getHostId()); + return startVirtualMachine(cmd.getId(), cmd.getHostId(), null).first(); } @Override @@ -2522,71 +2509,23 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager protected UserVm startVirtualMachine(DeployVMCmd cmd, Map additonalParams) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { - Host destinationHost = null; - if(cmd.getHostId() != null){ - Account account = UserContext.current().getCaller(); - if(!_accountService.isRootAdmin(account.getType())){ - throw new PermissionDeniedException("Parameter hostid can only be specified by a Root Admin, permission denied"); - } - destinationHost = _hostDao.findById(cmd.getHostId()); - if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + cmd.getHostId()); - } - } long vmId = cmd.getEntityId(); + Long hostId = cmd.getHostId(); UserVmVO vm = _vmDao.findById(vmId); - _vmDao.loadDetails(vm); - - // Check that the password was passed in and is valid - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - - String password = "saved_password"; - if (template.getEnablePassword()) { - password = generateRandomPassword(); - } - - if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); - } - - // Check if an SSH key pair was selected for the instance and if so use it to encrypt & save the vm password - String sshPublicKey = vm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - - vm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(vm); - } - - long userId = UserContext.current().getCallerUserId(); - UserVO caller = _userDao.findById(userId); - - AccountVO owner = _accountDao.findById(vm.getAccountId()); - + + Pair> vmParamPair = null; try { - Map params = new HashMap(); - if (additonalParams != null) { - params.putAll(additonalParams); - } - params.put(VirtualMachineProfile.Param.VmPassword, password); - - DataCenterDeployment plan = null; - if (destinationHost != null) { - s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); - } - - vm = _itMgr.start(vm, params, caller, owner, plan); + vmParamPair = startVirtualMachine(vmId, hostId, additonalParams); + vm = vmParamPair.first();; } finally { updateVmStateForFailedVmCreation(vm.getId()); } + // Check that the password was passed in and is valid + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); if (template.getEnablePassword()) { // this value is not being sent to the backend; need only for api display purposes - vm.setPassword(password); + vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword)); } return vm; @@ -2804,14 +2743,14 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } @Override - public UserVm startVirtualMachine(long vmId, Long hostId) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + public Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // Input validation - Account caller = UserContext.current().getCaller(); - Long userId = UserContext.current().getCallerUserId(); + Account callerAccount = UserContext.current().getCaller(); + UserVO callerUser = _userDao.findById(UserContext.current().getCallerUserId()); // if account is removed, return error - if (caller != null && caller.getRemoved() != null) { - throw new InvalidParameterValueException("The account " + caller.getId() + " is removed"); + if (callerAccount != null && callerAccount.getRemoved() != null) { + throw new InvalidParameterValueException("The account " + callerAccount.getId() + " is removed"); } UserVmVO vm = _vmDao.findById(vmId); @@ -2819,7 +2758,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } - _accountMgr.checkAccess(caller, null, true, vm); + _accountMgr.checkAccess(callerAccount, null, true, vm); Account owner = _accountDao.findById(vm.getAccountId()); @@ -2842,9 +2781,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId); } } - - UserVO user = _userDao.findById(userId); - + //check if vm is security group enabled if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkMgr.canAddDefaultSecurityGroup()) { //if vm is not mapped to security group, create a mapping @@ -2865,8 +2802,49 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); } + + //Set parameters + Map params = null; + if (vm.isUpdateParameters()) { + _vmDao.loadDetails(vm); + // Check that the password was passed in and is valid + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - return _itMgr.start(vm, null, user, caller, plan); + String password = "saved_password"; + if (template.getEnablePassword()) { + password = generateRandomPassword(); + } + + if (!validPassword(password)) { + throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); + } + + // Check if an SSH key pair was selected for the instance and if so use it to encrypt & save the vm password + String sshPublicKey = vm.getDetail("SSH.PublicKey"); + if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); + if (encryptedPasswd == null) { + throw new CloudRuntimeException("Error encrypting password"); + } + + vm.setDetail("Encrypted.Password", encryptedPasswd); + _vmDao.saveDetails(vm); + } + + params = new HashMap(); + if (additionalParams != null) { + params.putAll(additionalParams); + } + params.put(VirtualMachineProfile.Param.VmPassword, password); + } + + vm = _itMgr.start(vm, params, callerUser, callerAccount, plan); + if (vm != null && vm.isUpdateParameters()) { + vm.setUpdateParameters(false); + _vmDao.update(vm.getId(), vm); + } + + return new Pair(vm, params); } @Override diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index b0afd18344f..7bab3dc5d99 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -276,6 +276,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if (s_logger.isDebugEnabled()) { s_logger.debug("Allocating nics for " + vm); } + try { _networkMgr.allocate(vmProfile, networks); } catch (ConcurrentOperationException e) { @@ -736,25 +737,29 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene try { if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is being started in podId: " + vm.getPodIdToDeployIn()); + s_logger.debug("VM is being created in podId: " + vm.getPodIdToDeployIn()); } - _networkMgr.prepare(vmProfile, dest, ctx); + _networkMgr.prepare(vmProfile, dest, ctx); if (vm.getHypervisorType() != HypervisorType.BareMetal) { _storageMgr.prepare(vmProfile, dest); } - //since StorageMgr succeeded in volume creation, resue Volume for further tries until current cluster has capacity + //since StorageMgr succeeded in volume creation, reuse Volume for further tries until current cluster has capacity if(!reuseVolume){ reuseVolume = true; } + + VirtualMachineTO vmTO = null; + Commands cmds = null; vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); - VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + vmTO = hvGuru.implement(vmProfile); - Commands cmds = new Commands(OnError.Stop); + cmds = new Commands(OnError.Stop); cmds.addCommand(new StartCommand(vmTO)); vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); + work = _workDao.findById(work.getId()); if (work == null || work.getStep() != Step.Prepare) { throw new ConcurrentOperationException("Work steps have been changed: " + work); @@ -762,7 +767,10 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene _workDao.updateStep(work, Step.Starting); _agentMgr.send(destHostId, cmds); + _workDao.updateStep(work, Step.Started); + + StartAnswer startAnswer = cmds.getAnswer(StartAnswer.class); if (startAnswer != null && startAnswer.getResult()) { String host_guid = startAnswer.getHost_guid(); @@ -797,6 +805,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + (startAnswer == null ? " no start answer" : startAnswer.getDetails())); + } catch (OperationTimedoutException e) { s_logger.debug("Unable to send the start command to host " + dest.getHost()); if (e.isActive()) { diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java index e1b244e9139..1ba56480256 100644 --- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java +++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java @@ -57,6 +57,7 @@ import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.exception.ExecutionException; @@ -401,7 +402,7 @@ public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Mana } @Override - public UserVm startVirtualMachine(long vmId, Long hostId) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + public Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // TODO Auto-generated method stub return null; } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index cb39be56568..e7353507e33 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1046,6 +1046,7 @@ CREATE TABLE `cloud`.`user_vm` ( `iso_id` bigint unsigned, `display_name` varchar(255), `user_data` varchar(2048), + `update_parameters` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Defines if the parameters have been updated for the vm', PRIMARY KEY (`id`), CONSTRAINT `fk_user_vm__id` FOREIGN KEY `fk_user_vm__id` (`id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/setup/db/db/schema-301to302.sql b/setup/db/db/schema-301to302.sql index 7f2419f25f7..e179518d3a0 100755 --- a/setup/db/db/schema-301to302.sql +++ b/setup/db/db/schema-301to302.sql @@ -25,3 +25,6 @@ DELETE FROM `cloud`.`configuration` WHERE name='secstorage.vm.ram.size'; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'consoleproxy.service.offering', NULL, 'Service offering used by console proxy; if NULL - system offering will be used'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'secstorage.service.offering', NULL, 'Service offering used by secondary storage; if NULL - system offering will be used'); + +ALTER TABLE `cloud`.`user_vm` ADD COLUMN `update_parameters` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Defines if the parameters need to be set for the vm'; +UPDATE `cloud`.`user_vm` SET update_parameters=0 where id>0;