/** * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. * * This software is licensed under the GNU General Public License v3 or later. * * It is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ package com.cloud.vm; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachVolumeAnswer; import com.cloud.agent.api.AttachVolumeCommand; import com.cloud.agent.api.ComputeChecksumCommand; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.SnapshotCommand; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.UpgradeSnapshotCommand; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; import com.cloud.api.BaseCmd; import com.cloud.api.commands.AttachVolumeCmd; import com.cloud.api.commands.CreateTemplateCmd; import com.cloud.api.commands.CreateVMGroupCmd; import com.cloud.api.commands.DeleteVMGroupCmd; import com.cloud.api.commands.DeployVMCmd; import com.cloud.api.commands.DestroyVMCmd; import com.cloud.api.commands.DetachVolumeCmd; import com.cloud.api.commands.ListVMsCmd; import com.cloud.api.commands.MoveUserVMCmd; import com.cloud.api.commands.RebootVMCmd; import com.cloud.api.commands.RecoverVMCmd; import com.cloud.api.commands.ResetVMPasswordCmd; import com.cloud.api.commands.StartVMCmd; import com.cloud.api.commands.UpdateVMCmd; import com.cloud.api.commands.UpgradeVMCmd; 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; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.AccountVlanMapDao; 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; 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; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ManagementServerException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.network.IPAddressVO; import com.cloud.network.Network; import com.cloud.network.Network.GuestIpType; import com.cloud.network.Network.Provider; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkVO; import com.cloud.network.Networks.TrafficType; 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.lb.LoadBalancingRulesManager; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.RulesManager; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.dao.SecurityGroupDao; import com.cloud.network.vpn.PasswordResetElement; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Availability; import com.cloud.offering.ServiceOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.server.Criteria; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; 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; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountService; import com.cloud.user.AccountVO; import com.cloud.user.ResourceLimitService; import com.cloud.user.SSHKeyPair; import com.cloud.user.User; import com.cloud.user.UserContext; 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; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.crypt.RSAHelper; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.net.NetUtils; 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; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; @Local(value = { UserVmManager.class, UserVmService.class }) public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager { private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class); private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds @Inject protected HostDao _hostDao = null; @Inject protected HostDetailsDao _detailsDao = 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 VMTemplateHostDao _templateHostDao = null; @Inject protected VMTemplateZoneDao _templateZoneDao = null; @Inject protected DomainDao _domainDao = null; @Inject protected ResourceLimitDao _limitDao = null; @Inject protected UserVmDao _vmDao = null; @Inject protected VolumeDao _volsDao = null; @Inject protected DataCenterDao _dcDao = null; @Inject protected FirewallRulesDao _rulesDao = null; @Inject protected LoadBalancerVMMapDao _loadBalancerVMMapDao = null; @Inject protected LoadBalancerDao _loadBalancerDao = null; @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; @Inject protected SnapshotManager _snapshotMgr = null; @Inject protected AgentManager _agentMgr = null; @Inject protected ConfigurationManager _configMgr = null; @Inject protected AccountDao _accountDao = null; @Inject protected UserDao _userDao = null; @Inject protected SnapshotDao _snapshotDao = null; @Inject protected GuestOSDao _guestOSDao = null; @Inject protected GuestOSCategoryDao _guestOSCategoryDao = null; @Inject protected HighAvailabilityManager _haMgr = null; @Inject protected AlertManager _alertMgr = null; @Inject protected AccountManager _accountMgr; @Inject protected AccountService _accountService; @Inject protected AsyncJobManager _asyncMgr; @Inject protected VlanDao _vlanDao; @Inject protected ClusterDao _clusterDao; @Inject protected AccountVlanMapDao _accountVlanMapDao; @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; @Inject protected VirtualMachineManager _itMgr; @Inject protected NetworkDao _networkDao; @Inject protected VirtualNetworkApplianceManager _routerMgr; @Inject protected NicDao _nicDao; @Inject protected RulesManager _rulesMgr; @Inject protected LoadBalancingRulesManager _lbMgr; @Inject protected UsageEventDao _usageEventDao; @Inject protected SSHKeyPairDao _sshKeyPairDao; @Inject protected UserVmDetailsDao _vmDetailsDao; @Inject protected SecurityGroupDao _securityGroupDao; @Inject protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; @Inject protected VMInstanceDao _vmInstanceDao; @Inject protected ResourceLimitService _resourceLimitMgr; @Inject protected FirewallManager _firewallMgr; protected ScheduledExecutorService _executor = null; protected int _expungeInterval; protected int _expungeDelay; protected String _name; protected String _instance; protected String _zone; private ConfigurationDao _configDao; private int _createprivatetemplatefromvolumewait; private int _createprivatetemplatefromsnapshotwait; @Override public UserVmVO getVirtualMachine(long vmId) { return _vmDao.findById(vmId); } @Override public List getVirtualMachines(long hostId) { return _vmDao.listByHostId(hostId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true) public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException { Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); Long vmId = cmd.getId(); UserVmVO userVm = _vmDao.findById(cmd.getId()); // Do parameters input validation if (userVm == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + cmd.getId()); } VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId()); if (template == null || !template.getEnablePassword()) { throw new InvalidParameterValueException("Fail to reset password for the virtual machine, the template is not password enabled"); } if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) { s_logger.error("vm is not in the right state: " + vmId); throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state"); } userId = accountAndUserValidation(vmId, account, userId, userVm); boolean result = resetVMPasswordInternal(cmd, password); if (result) { userVm.setPassword(password); } return userVm; } private boolean resetVMPasswordInternal(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException { Long vmId = cmd.getId(); Long userId = UserContext.current().getCallerUserId(); VMInstanceVO vmInstance = _vmDao.findById(vmId); if (password == null || password.equals("")) { return false; } VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); if (template.getEnablePassword()) { Nic defaultNic = _networkMgr.getDefaultNic(vmId); if (defaultNic == null) { s_logger.error("Unable to reset password for vm " + vmInstance + " as the instance doesn't have default nic"); return false; } Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); List elements = _networkMgr.getPasswordResetElements(); boolean result = true; for (PasswordResetElement element : elements) { if (!element.savePassword(defaultNetwork, defaultNicProfile, vmProfile)) { result = false; } } // Need to reboot the virtual machine so that the password gets redownloaded from the DomR, and reset on the VM if (!result) { s_logger.debug("Failed to reset password for the virutal machine; no need to reboot the vm"); return false; } else { if (rebootVirtualMachine(userId, vmId) == null) { if (vmInstance.getState() == State.Stopped) { s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of password reset"); return true; } s_logger.warn("Failed to reboot the vm " + vmInstance); return false; } else { s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of password reset"); return true; } } } else { if (s_logger.isDebugEnabled()) { s_logger.debug("Reset password called for a vm that is not using a password enabled template"); } return false; } } @Override public boolean stopVirtualMachine(long userId, long vmId) { boolean status = false; if (s_logger.isDebugEnabled()) { s_logger.debug("Stopping vm=" + vmId); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM is either removed or deleted."); } return true; } User user = _userDao.findById(userId); Account account = _accountDao.findById(user.getAccountId()); try { status = _itMgr.stop(vm, user, account); } catch (ResourceUnavailableException e) { s_logger.debug("Unable to stop due to ", e); status = false; } if (status) { return status; } else { return status; } } @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) public Volume attachVolumeToVM(AttachVolumeCmd command) { Long vmId = command.getVirtualMachineId(); Long volumeId = command.getId(); Long deviceId = command.getDeviceId(); Account account = UserContext.current().getCaller(); // Check that the volume ID is valid VolumeVO volume = _volsDao.findById(volumeId); // Check that the volume is a data volume if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) { throw new InvalidParameterValueException("Please specify a valid data volume."); } // Check that the volume is stored on shared storage if (!(Volume.State.Allocated.equals(volume.getState())) && !_storageMgr.volumeOnSharedStoragePool(volume)) { throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); } if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()))) { throw new InvalidParameterValueException("Volume state must be in Allocated or Ready state"); } // Check that the volume is not currently attached to any VM if (volume.getInstanceId() != null) { throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); } // Check that the volume is not destroyed if (volume.getState() == Volume.State.Destroy) { throw new InvalidParameterValueException("Please specify a volume that is not destroyed."); } // Check that the virtual machine ID is valid and it's a user vm UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getType() != VirtualMachine.Type.User) { throw new InvalidParameterValueException("Please specify a valid User VM."); } // Check that the VM is in the correct state if (vm.getState() != State.Running && vm.getState() != State.Stopped) { throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); } // Check that the device ID is valid if (deviceId != null) { if (deviceId.longValue() == 0) { throw new InvalidParameterValueException("deviceId can't be 0, which is used by Root device"); } } // Check that the VM has less than 6 data volumes attached List existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK); if (existingDataVolumes.size() >= 6) { throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (6). Please specify another VM."); } // Check that the VM and the volume are in the same zone if (vm.getDataCenterIdToDeployIn() != volume.getDataCenterId()) { throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume."); } // Verify account information if (volume.getAccountId() != vm.getAccountId()) { throw new PermissionDeniedException("Virtual machine and volume belong to different accounts, can not attach. Permission denied."); } // If the account is not an admin, check that the volume and the virtual machine are owned by the account that was // passed in _accountMgr.checkAccess(account, null, volume); /* * if (account != null) { if (!isAdmin(account.getType())) { if (account.getId() != volume.getAccountId()) { throw new * PermissionDeniedException("Unable to find volume with ID: " + volumeId + " for account: " + account.getAccountName() * + ". Permission denied."); } * * if (account.getId() != vm.getAccountId()) { throw new PermissionDeniedException("Unable to find VM with ID: " + vmId * + " for account: " + account.getAccountName() + ". Permission denied"); } } else { if * (!_domainDao.isChildDomain(account.getDomainId(), volume.getDomainId()) || * !_domainDao.isChildDomain(account.getDomainId(), vm.getDomainId())) { throw new * PermissionDeniedException("Unable to attach volume " + volumeId + " to virtual machine instance " + vmId + * ". Permission denied."); } } } */ VolumeVO rootVolumeOfVm = null; List rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT); if (rootVolumesOfVm.size() != 1) { throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state. Please contact Cloud Support."); } else { 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 dataDiskHyperType = _volsDao.getHypervisorType(volume.getId()); if (rootDiskHyperType != dataDiskHyperType) { throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm"); } List vols = _volsDao.findByInstance(vmId); if (deviceId != null) { if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) { throw new RuntimeException("deviceId should be 1,2,4-15"); } for (VolumeVO vol : vols) { if (vol.getDeviceId().equals(deviceId)) { throw new RuntimeException("deviceId " + deviceId + " is used by VM " + vm.getHostName()); } } } else { // allocate deviceId here List devIds = new ArrayList(); for (int i = 1; i < 15; i++) { devIds.add(String.valueOf(i)); } devIds.remove("3"); for (VolumeVO vol : vols) { devIds.remove(vol.getDeviceId().toString().trim()); } 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; } } } if (moveVolumeNeeded) { // Move the volume to a storage pool in the VM's zone, pod, or cluster volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); } AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); if (asyncExecutor != null) { AsyncJobVO job = asyncExecutor.getJob(); if (s_logger.isInfoEnabled()) { s_logger.info("Trying to attaching volume " + volumeId + " to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status"); } _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); } String errorMsg = "Failed to attach volume: " + volume.getName() + " to VM: " + vm.getHostName(); boolean sendCommand = (vm.getState() == State.Running); AttachVolumeAnswer answer = null; Long hostId = vm.getHostId(); if (hostId == null) { hostId = vm.getLastHostId(); HostVO host = _hostDao.findById(hostId); if (host != null && host.getHypervisorType() == HypervisorType.VMware) { sendCommand = true; } } if (sendCommand) { StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); AttachVolumeCommand cmd = new AttachVolumeCommand(true, vm.getInstanceName(), volume.getPoolType(), volume.getFolder(), volume.getPath(), volume.getName(), deviceId, volume.getChainInfo()); cmd.setPoolUuid(volumePool.getUuid()); try { answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); } catch (Exception e) { throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); } } if (!sendCommand || (answer != null && answer.getResult())) { // Mark the volume as attached if (sendCommand) { _volsDao.attachVolume(volume.getId(), vmId, answer.getDeviceId()); } else { _volsDao.attachVolume(volume.getId(), vmId, deviceId); } return _volsDao.findById(volumeId); } else { if (answer != null) { String details = answer.getDetails(); if (details != null && !details.isEmpty()) { errorMsg += "; " + details; } } throw new CloudRuntimeException(errorMsg); } } @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { Account account = UserContext.current().getCaller(); if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null)) || (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null))) { throw new InvalidParameterValueException("Please provide either a volume id, or a tuple(device id, instance id)"); } Long volumeId = cmmd.getId(); VolumeVO volume = null; if (volumeId != null) { volume = _volsDao.findById(volumeId); } else { volume = _volsDao.findByInstanceAndDeviceId(cmmd.getVirtualMachineId(), cmmd.getDeviceId()).get(0); } Long vmId = null; if (cmmd.getVirtualMachineId() == null) { vmId = volume.getInstanceId(); } else { vmId = cmmd.getVirtualMachineId(); } // Check that the volume ID is valid if (volume == null) { throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId); } // If the account is not an admin, check that the volume is owned by the account that was passed in _accountMgr.checkAccess(account, null, volume); /* * if (!isAdmin) { if (account.getId() != volume.getAccountId()) { throw new * InvalidParameterValueException("Unable to find volume with ID: " + volumeId + " for account: " + * account.getAccountName()); } } else if (account != null) { if (!_domainDao.isChildDomain(account.getDomainId(), * volume.getDomainId())) { throw new PermissionDeniedException("Unable to detach volume with ID: " + volumeId + * ", permission denied."); } } */ // Check that the volume is a data volume 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 VM is in the correct state UserVmVO vm = _vmDao.findById(vmId); if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) { throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); } AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); if (asyncExecutor != null) { AsyncJobVO job = asyncExecutor.getJob(); if (s_logger.isInfoEnabled()) { s_logger.info("Trying to attaching volume " + volumeId + "to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status"); } _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); } String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName(); boolean sendCommand = (vm.getState() == State.Running); Answer answer = null; if (sendCommand) { AttachVolumeCommand cmd = new AttachVolumeCommand(false, vm.getInstanceName(), volume.getPoolType(), volume.getFolder(), volume.getPath(), volume.getName(), cmmd.getDeviceId() != null ? cmmd.getDeviceId() : volume.getDeviceId(), volume.getChainInfo()); StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); cmd.setPoolUuid(volumePool.getUuid()); try { answer = _agentMgr.send(vm.getHostId(), cmd); } catch (Exception e) { throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); } } if (!sendCommand || (answer != null && answer.getResult())) { // Mark the volume as detached _volsDao.detachVolume(volume.getId()); if (answer != null && answer instanceof AttachVolumeAnswer) { volume.setChainInfo(((AttachVolumeAnswer) answer).getChainInfo()); _volsDao.update(volume.getId(), volume); } return _volsDao.findById(volumeId); } else { if (answer != null) { String details = answer.getDetails(); if (details != null && !details.isEmpty()) { errorMsg += "; " + details; } } throw new CloudRuntimeException(errorMsg); } } @Override public boolean attachISOToVM(long vmId, long isoId, boolean attach) { UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { return false; } else if (vm.getState() != State.Running) { return true; } String isoPath; VMTemplateVO tmplt = _templateDao.findById(isoId); if (tmplt == null) { s_logger.warn("ISO: " + isoId + " does not exist"); return false; } // Get the path of the ISO Pair isoPathPair = null; if (tmplt.getTemplateType() == TemplateType.PERHOST) { isoPath = tmplt.getName(); } else { isoPathPair = _storageMgr.getAbsoluteIsoPath(isoId, vm.getDataCenterIdToDeployIn()); if (isoPathPair == null) { s_logger.warn("Couldn't get absolute iso path"); return false; } else { isoPath = isoPathPair.first(); } } String vmName = vm.getInstanceName(); HostVO host = _hostDao.findById(vm.getHostId()); if (host == null) { s_logger.warn("Host: " + vm.getHostId() + " does not exist"); return false; } AttachIsoCommand cmd = new AttachIsoCommand(vmName, isoPath, attach); if (isoPathPair != null) { cmd.setStoreUrl(isoPathPair.second()); } Answer a = _agentMgr.easySend(vm.getHostId(), cmd); return (a != null && a.getResult()); } private UserVm rebootVirtualMachine(long userId, long vmId) throws InsufficientCapacityException, ResourceUnavailableException { UserVmVO vm = _vmDao.findById(vmId); User caller = _accountMgr.getActiveUser(userId); Account owner = _accountMgr.getAccount(vm.getAccountId()); if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) { s_logger.warn("Vm id=" + vmId + " doesn't exist"); return null; } if (vm.getState() == State.Running && vm.getHostId() != null) { return _itMgr.reboot(vm, null, caller, owner); } else { s_logger.error("Vm id=" + vmId + " is not in Running state, failed to reboot"); return null; } } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "upgrading Vm") /* * TODO: cleanup eventually - Refactored API call */ public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) { Long virtualMachineId = cmd.getId(); Long serviceOfferingId = cmd.getServiceOfferingId(); Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); // Verify input parameters UserVmVO vmInstance = _vmDao.findById(virtualMachineId); if (vmInstance == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + virtualMachineId); } userId = accountAndUserValidation(virtualMachineId, account, userId, vmInstance); // Check that the specified service offering ID is valid ServiceOfferingVO newServiceOffering = _offeringDao.findById(serviceOfferingId); if (newServiceOffering == null) { throw new InvalidParameterValueException("Unable to find a service offering with id " + serviceOfferingId); } // Check that the VM is stopped if (!vmInstance.getState().equals(State.Stopped)) { s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState() + "; make sure the virtual machine is stopped and not in an error state before upgrading."); } // Check if the service offering being upgraded to is what the VM is already running with if (vmInstance.getServiceOfferingId() == newServiceOffering.getId()) { if (s_logger.isInfoEnabled()) { s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested service offering (" + newServiceOffering.getName() + ")"); } throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already has the requested service offering (" + newServiceOffering.getName() + ")"); } ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore. /* * if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { String errorMsg = * "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); errorMsg += * ". Please select a service offering with the same guest IP type as the VM's current service offering (" + * currentServiceOffering.getGuestIpType() + ")."; throw new InvalidParameterValueException(errorMsg); } */ // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service // offering if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) { throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + ", cannot switch between local storage and shared storage service offerings. Current offering useLocalStorage=" + currentServiceOffering.getUseLocalStorage() + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage()); } // Check that there are enough resources to upgrade the service offering if (!_itMgr.isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available for an offering of " + newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory"); } // Check that the service offering being upgraded to has all the tags of the current service offering List currentTags = _configMgr.csvTagsToList(currentServiceOffering.getTags()); List newTags = _configMgr.csvTagsToList(newServiceOffering.getTags()); if (!newTags.containsAll(currentTags)) { throw new InvalidParameterValueException("Unable to upgrade virtual machine; the new service offering does not have all the tags of the " + "current service offering. Current service offering tags: " + currentTags + "; " + "new service offering tags: " + newTags); } UserVmVO vmForUpdate = _vmDao.createForUpdate(); vmForUpdate.setServiceOfferingId(serviceOfferingId); vmForUpdate.setHaEnabled(_serviceOfferingDao.findById(serviceOfferingId).getOfferHA()); vmForUpdate.setLimitCpuUse(_serviceOfferingDao.findById(serviceOfferingId).getLimitCpuUse()); _vmDao.update(vmInstance.getId(), vmForUpdate); return _vmDao.findById(vmInstance.getId()); } private Long accountAndUserValidation(Long virtualMachineId, Account account, Long userId, UserVmVO vmInstance) { if (account != null) { if (!isAdmin(account.getType()) && (account.getId() != vmInstance.getAccountId())) { throw new InvalidParameterValueException("Unable to find a virtual machine with id " + virtualMachineId + " for this account"); } else if (!_domainDao.isChildDomain(account.getDomainId(), vmInstance.getDomainId())) { throw new InvalidParameterValueException("Invalid virtual machine id (" + virtualMachineId + ") given, unable to upgrade virtual machine."); } } // If command is executed via 8096 port, set userId to the id of System account (1) if (userId == null) { userId = Long.valueOf(User.UID_SYSTEM); } return userId; } @Override public HashMap getVirtualMachineStatistics(long hostId, String hostName, List vmIds) throws CloudRuntimeException { HashMap vmStatsById = new HashMap(); if (vmIds.isEmpty()) { return vmStatsById; } List vmNames = new ArrayList(); for (Long vmId : vmIds) { UserVmVO vm = _vmDao.findById(vmId); vmNames.add(vm.getInstanceName()); } Answer answer = _agentMgr.easySend(hostId, new GetVmStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName)); if (answer == null || !answer.getResult()) { s_logger.warn("Unable to obtain VM statistics."); return null; } else { HashMap vmStatsByName = ((GetVmStatsAnswer) answer).getVmStatsMap(); if (vmStatsByName == null) { s_logger.warn("Unable to obtain VM statistics."); return null; } for (String vmName : vmStatsByName.keySet()) { vmStatsById.put(vmIds.get(vmNames.indexOf(vmName)), vmStatsByName.get(vmName)); } } return vmStatsById; } @Override @DB public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException, CloudRuntimeException { Long vmId = cmd.getId(); Account accountHandle = UserContext.current().getCaller(); // if account is removed, return error if (accountHandle != null && accountHandle.getRemoved() != null) { throw new InvalidParameterValueException("The account " + accountHandle.getId() + " is removed"); } // Verify input parameters UserVmVO vm = _vmDao.findById(vmId.longValue()); if (vm == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } if ((accountHandle != null) && !_domainDao.isChildDomain(accountHandle.getDomainId(), vm.getDomainId())) { // the domain in which the VM lives is not in the admin's domain tree throw new InvalidParameterValueException("Unable to recover virtual machine with id " + vmId + ", invalid id given."); } if (vm.getRemoved() != null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Unable to find vm or vm is removed: " + vmId); } throw new InvalidParameterValueException("Unable to find vm by id " + vmId); } if (vm.getState() != State.Destroyed) { if (s_logger.isDebugEnabled()) { s_logger.debug("vm is not in the right state: " + vmId); } throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state"); } if (s_logger.isDebugEnabled()) { s_logger.debug("Recovering vm " + vmId); } Transaction txn = Transaction.currentTxn(); AccountVO account = null; txn.start(); account = _accountDao.lockRow(vm.getAccountId(), true); // if the account is deleted, throw error if (account.getRemoved() != null) { throw new CloudRuntimeException("Unable to recover VM as the account is deleted"); } // First check that the maximum number of UserVMs for the given accountId will not be exceeded _resourceLimitMgr.checkResourceLimit(account, ResourceType.user_vm); _haMgr.cancelDestroy(vm, vm.getHostId()); try { if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { s_logger.debug("Unable to recover the vm because it is not in the correct state: " + vmId); throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId); } } catch (NoTransitionException e) { throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId); } // Recover the VM's disks List volumes = _volsDao.findByInstance(vmId); for (VolumeVO volume : volumes) { if (volume.getVolumeType().equals(Volume.Type.ROOT)) { // Create an event Long templateId = volume.getTemplateId(); Long diskOfferingId = volume.getDiskOfferingId(); Long offeringId = null; if (diskOfferingId != null) { DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId); if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) { offeringId = offering.getId(); } } UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), offeringId, templateId, volume.getSize()); _usageEventDao.persist(usageEvent); } } _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.volume, new Long(volumes.size())); _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.user_vm); txn.commit(); return _vmDao.findById(vmId); } @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; ComponentLocator locator = ComponentLocator.getCurrentLocator(); _configDao = locator.getDao(ConfigurationDao.class); if (_configDao == null) { throw new ConfigurationException("Unable to get the configuration dao."); } Map configs = _configDao.getConfiguration("AgentManager", params); _instance = configs.get("instance.name"); if (_instance == null) { _instance = "DEFAULT"; } String value = _configDao.getValue(Config.CreatePrivateTemplateFromVolumeWait.toString()); _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait.getDefaultValue())); value = _configDao.getValue(Config.CreatePrivateTemplateFromSnapshotWait.toString()); _createprivatetemplatefromsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromSnapshotWait.getDefaultValue())); String workers = configs.get("expunge.workers"); int wrks = NumbersUtil.parseInt(workers, 10); String time = configs.get("expunge.interval"); _expungeInterval = NumbersUtil.parseInt(time, 86400); time = configs.get("expunge.delay"); _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); _itMgr.registerGuru(VirtualMachine.Type.User, this); VirtualMachine.State.getStateMachine().registerListener(new UserVmStateListener(_usageEventDao, _networkDao, _nicDao)); s_logger.info("User VM Manager is configured."); return true; } @Override public String getName() { return _name; } @Override public boolean start() { _executor.scheduleWithFixedDelay(new ExpungeTask(), _expungeInterval, _expungeInterval, TimeUnit.SECONDS); return true; } @Override public boolean stop() { _executor.shutdown(); return true; } protected UserVmManagerImpl() { } public String getRandomPrivateTemplateName() { return UUID.randomUUID().toString(); } @Override public Long convertToId(String vmName) { if (!VirtualMachineName.isValidVmName(vmName, _instance)) { return null; } return VirtualMachineName.getVmId(vmName); } @Override public boolean expunge(UserVmVO vm, long callerUserId, Account caller) { UserContext ctx = UserContext.current(); ctx.setAccountId(vm.getAccountId()); try { if (!_itMgr.advanceExpunge(vm, _accountMgr.getSystemUser(), caller)) { s_logger.info("Did not expunge " + vm); return false; } // Only if vm is not expunged already, cleanup it's resources if (vm != null && vm.getRemoved() == null) { // Cleanup vm resources - all the PF/LB/StaticNat rules associated with vm s_logger.debug("Starting cleaning up vm " + vm + " resources..."); if (cleanupVmResources(vm.getId())) { s_logger.debug("Successfully cleaned up vm " + vm + " resources as a part of expunge process"); } else { s_logger.warn("Failed to cleanup resources as a part of vm " + vm + " expunge"); return false; } _itMgr.remove(vm, _accountMgr.getSystemUser(), caller); } return true; } catch (ResourceUnavailableException e) { s_logger.warn("Unable to expunge " + vm, e); return false; } catch (OperationTimedoutException e) { s_logger.warn("Operation time out on expunging " + vm, e); return false; } catch (ConcurrentOperationException e) { s_logger.warn("Concurrent operations on expunging " + vm, e); return false; } } private boolean cleanupVmResources(long vmId) { boolean success = true; //Remove vm from security groups _securityGroupMgr.removeInstanceFromGroups(vmId); //Remove vm from instance group removeInstanceFromInstanceGroup(vmId); //cleanup firewall rules if (_firewallMgr.revokeFirewallRulesForVm(vmId)) { s_logger.debug("Firewall rules are removed successfully as a part of vm id=" + vmId + " expunge"); } else { success = false; s_logger.warn("Fail to remove firewall rules as a part of vm id=" + vmId + " expunge"); } //cleanup port forwarding rules if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) { s_logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + vmId + " expunge"); } else { success = false; s_logger.warn("Fail to remove port forwarding rules as a part of vm id=" + vmId + " expunge"); } // cleanup load balancer rules if (_lbMgr.removeVmFromLoadBalancers(vmId)) { s_logger.debug("Removed vm id=" + vmId + " from all load balancers as a part of expunge process"); } else { success = false; s_logger.warn("Fail to remove vm id=" + vmId + " from load balancers as a part of expunge process"); } // If vm is assigned to static nat, disable static nat for the ip address IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(vmId); try { if (ip != null) { if (_rulesMgr.disableStaticNat(ip.getId())) { s_logger.debug("Disabled 1-1 nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); } else { s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); success = false; } } } catch (ResourceUnavailableException e) { success = false; s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge because resource is unavailable", e); } return success; } @Override public void deletePrivateTemplateRecord(Long templateId) { if (templateId != null) { _templateDao.remove(templateId); } } @Override @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true) public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd) throws ResourceAllocationException { Long userId = UserContext.current().getCallerUserId(); if (userId == null) { userId = User.UID_SYSTEM; } Account account = UserContext.current().getCaller(); boolean isAdmin = ((account == null) || isAdmin(account.getType())); VMTemplateVO privateTemplate = null; UserVO user = _userDao.findById(userId); if (user == null) { throw new InvalidParameterValueException("User " + userId + " does not exist"); } 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(cmd.getTemplateTag() != null){ if(!_accountService.isRootAdmin(account.getType())){ throw new PermissionDeniedException("Parameter templatetag can only be specified by a Root Admin, permission denied"); } } // 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; } 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."); } Long volumeId = cmd.getVolumeId(); Long snapshotId = cmd.getSnapshotId(); if ((volumeId == null) && (snapshotId == null)) { throw new InvalidParameterValueException("Failed to create private template record, neither volume ID nor snapshot ID were specified."); } if ((volumeId != null) && (snapshotId != null)) { throw new InvalidParameterValueException("Failed to create private template record, please specify only one of volume ID (" + volumeId + ") and snapshot ID (" + snapshotId + ")"); } long domainId; long accountId; HypervisorType hyperType; VolumeVO volume = null; if (volumeId != null) { // create template from volume volume = _volsDao.findById(volumeId); if (volume == null) { throw new InvalidParameterValueException("Failed to create private template record, unable to find volume " + volumeId); } // If private template is created from Volume, check that the volume will not be active when the private template is // created if (!_storageMgr.volumeInactive(volume)) { String msg = "Unable to create private template for volume: " + volume.getName() + "; volume is attached to a non-stopped VM, please stop the VM first"; if (s_logger.isInfoEnabled()) { s_logger.info(msg); } throw new CloudRuntimeException(msg); } domainId = volume.getDomainId(); accountId = volume.getAccountId(); hyperType = _volsDao.getHypervisorType(volumeId); } else { // create template from snapshot SnapshotVO snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null) { throw new InvalidParameterValueException("Failed to create private template record, unable to find snapshot " + snapshotId); } if (snapshot.getStatus() != Snapshot.Status.BackedUp) { throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for template creation"); } domainId = snapshot.getDomainId(); accountId = snapshot.getAccountId(); hyperType = snapshot.getHypervisorType(); volume = _volsDao.findById(snapshot.getVolumeId()); } if (!isAdmin) { if (account.getId() != accountId) { throw new PermissionDeniedException("Unable to create a template permission denied."); } } else if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) { throw new PermissionDeniedException("Unable to create a template permission denied."); } VMTemplateVO existingTemplate = _templateDao.findByTemplateNameAccountId(name, accountId); if (existingTemplate != null) { throw new InvalidParameterValueException("Failed to create private template " + name + ", a template with that name already exists."); } AccountVO ownerAccount = _accountDao.findById(accountId); _resourceLimitMgr.checkResourceLimit(ownerAccount, ResourceType.template); if (!isAdmin || featured == null) { featured = Boolean.FALSE; } 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() + UUID.nameUUIDFromBytes(name.getBytes()).toString(); Long nextTemplateId = _templateDao.getNextInSequence(Long.class, "id"); String description = cmd.getDisplayText(); boolean isExtractable = false; Long sourceTemplateId = null; if (volume != null) { VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId()); isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM; if (template != null){ sourceTemplateId = template.getId(); }else if (volume.getVolumeType() == Type.ROOT){ //vm created out of blank template UserVm userVm = ApiDBUtils.findUserVmById(volume.getInstanceId()); sourceTemplateId = userVm.getIsoId(); } } String templateTag = cmd.getTemplateTag(); if(templateTag != null){ if(s_logger.isDebugEnabled()){ s_logger.debug("Adding template tag: "+templateTag); } } privateTemplate = new VMTemplateVO(nextTemplateId, uniqueName, name, ImageFormat.RAW, isPublic, featured, isExtractable, TemplateType.USER, null, null, requiresHvmValue, bitsValue, accountId, null, description, passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag); if(sourceTemplateId != null){ if(s_logger.isDebugEnabled()){ s_logger.debug("This template is getting created from other template, setting source template Id to: "+sourceTemplateId); } } privateTemplate.setSourceTemplateId(sourceTemplateId); VMTemplateVO template = _templateDao.persist(privateTemplate); // Increment the number of templates if (template != null) { _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.template); } return template; } @Override @DB @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", async = true) public VMTemplateVO createPrivateTemplate(CreateTemplateCmd command) throws CloudRuntimeException { Long userId = UserContext.current().getCallerUserId(); if (userId == null) { userId = User.UID_SYSTEM; } long templateId = command.getEntityId(); Long volumeId = command.getVolumeId(); Long snapshotId = command.getSnapshotId(); SnapshotCommand cmd = null; VMTemplateVO privateTemplate = null; String uniqueName = getRandomPrivateTemplateName(); StoragePoolVO pool = null; HostVO secondaryStorageHost = null; long zoneId; Long accountId = null; try { if (snapshotId != null) { // create template from snapshot SnapshotVO snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null) { throw new CloudRuntimeException("Unable to find Snapshot for Id " + snapshotId); } zoneId = snapshot.getDataCenterId(); secondaryStorageHost = _hostDao.findById(snapshot.getSecHostId()); if (secondaryStorageHost == null) { throw new CloudRuntimeException("Secondary storage " + snapshot.getSecHostId() + " doesn't exist"); } String secondaryStorageURL = secondaryStorageHost.getStorageUrl(); String name = command.getTemplateName(); String backupSnapshotUUID = snapshot.getBackupSnapshotId(); if (backupSnapshotUUID == null) { throw new CloudRuntimeException("Unable to create private template from snapshot " + snapshotId + " due to there is no backupSnapshotUUID for this snapshot"); } Long dcId = snapshot.getDataCenterId(); accountId = snapshot.getAccountId(); volumeId = snapshot.getVolumeId(); String origTemplateInstallPath = null; List pools = _storageMgr.ListByDataCenterHypervisor(zoneId, snapshot.getHypervisorType()); if (pools == null || pools.size() == 0 ) { throw new CloudRuntimeException("Unable to find storage pools in zone " + zoneId); } pool = pools.get(0); if (snapshot.getVersion() != null && snapshot.getVersion().equalsIgnoreCase("2.1")) { VolumeVO volume = _volsDao.findByIdIncludingRemoved(volumeId); if (volume == null) { throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to unable to find orignal volume:" + volumeId + ", try it later "); } if ( volume.getTemplateId() == null ) { _snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); } else { VMTemplateVO template = _templateDao.findByIdIncludingRemoved(volume.getTemplateId()); if (template == null) { throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to unalbe to find orignal template :" + volume.getTemplateId() + ", try it later "); } Long origTemplateId = template.getId(); Long origTmpltAccountId = template.getAccountId(); if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to volume:" + volumeId + " is being used, try it later "); } cmd = new UpgradeSnapshotCommand(null, secondaryStorageURL, dcId, accountId, volumeId, origTemplateId, origTmpltAccountId, null, snapshot.getBackupSnapshotId(), snapshot.getName(), "2.1"); if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { throw new CloudRuntimeException("Creating template failed due to volume:" + volumeId + " is being used, try it later "); } Answer answer = null; try { answer = _storageMgr.sendToPool(pool, cmd); cmd = null; } catch (StorageUnavailableException e) { } finally { _volsDao.unlockFromLockTable(volumeId.toString()); } if ((answer != null) && answer.getResult()) { _snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); } else { throw new CloudRuntimeException("Unable to upgrade snapshot"); } } } if( snapshot.getSwiftName() != null ) { _snapshotMgr.downloadSnapshotsFromSwift(snapshot); } cmd = new CreatePrivateTemplateFromSnapshotCommand(pool.getUuid(), secondaryStorageURL, dcId, accountId, snapshot.getVolumeId(), backupSnapshotUUID, snapshot.getName(), origTemplateInstallPath, templateId, name, _createprivatetemplatefromsnapshotwait); } else if (volumeId != null) { VolumeVO volume = _volsDao.findById(volumeId); if (volume == null) { throw new CloudRuntimeException("Unable to find volume for Id " + volumeId); } accountId = volume.getAccountId(); if (volume.getPoolId() == null) { _templateDao.remove(templateId); throw new CloudRuntimeException("Volume " + volumeId + " is empty, can't create template on it"); } String vmName = _storageMgr.getVmNameOnVolume(volume); zoneId = volume.getDataCenterId(); secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId); if (secondaryStorageHost == null) { throw new CloudRuntimeException("Can not find the secondary storage for zoneId " + zoneId); } String secondaryStorageURL = secondaryStorageHost.getStorageUrl(); pool = _storagePoolDao.findById(volume.getPoolId()); cmd = new CreatePrivateTemplateFromVolumeCommand(secondaryStorageURL, templateId, accountId, command.getTemplateName(), uniqueName, volume.getPath(), vmName, _createprivatetemplatefromvolumewait); } else { throw new CloudRuntimeException("Creating private Template need to specify snapshotId or volumeId"); } // FIXME: before sending the command, check if there's enough capacity // on the storage server to create the template // This can be sent to a KVM host too. CreatePrivateTemplateAnswer answer = null; if (snapshotId != null) { if (!_snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { throw new CloudRuntimeException("Creating template from snapshot failed due to snapshot:" + snapshotId + " is being used, try it later "); } } else { if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { throw new CloudRuntimeException("Creating template from volume failed due to volume:" + volumeId + " is being used, try it later "); } } try { answer = (CreatePrivateTemplateAnswer) _storageMgr.sendToPool(pool, cmd); } catch (StorageUnavailableException e) { } finally { if (snapshotId != null) { _snapshotDao.unlockFromLockTable(snapshotId.toString()); } else { _volsDao.unlockFromLockTable(volumeId.toString()); } } if ((answer != null) && answer.getResult()) { privateTemplate = _templateDao.findById(templateId); String answerUniqueName = answer.getUniqueName(); if (answerUniqueName != null) { privateTemplate.setUniqueName(answerUniqueName); } else { privateTemplate.setUniqueName(uniqueName); } ImageFormat format = answer.getImageFormat(); if (format != null) { privateTemplate.setFormat(format); } else { // This never occurs. // Specify RAW format makes it unusable for snapshots. privateTemplate.setFormat(ImageFormat.RAW); } String checkSum = getChecksum(secondaryStorageHost.getId(), answer.getPath()); Transaction txn = Transaction.currentTxn(); txn.start(); privateTemplate.setChecksum(checkSum); _templateDao.update(templateId, privateTemplate); // add template zone ref for this template _templateDao.addTemplateToZone(privateTemplate, zoneId); VMTemplateHostVO templateHostVO = new VMTemplateHostVO(secondaryStorageHost.getId(), templateId); templateHostVO.setDownloadPercent(100); templateHostVO.setDownloadState(Status.DOWNLOADED); templateHostVO.setInstallPath(answer.getPath()); templateHostVO.setLastUpdated(new Date()); templateHostVO.setSize(answer.getVirtualSize()); templateHostVO.setPhysicalSize(answer.getphysicalSize()); _templateHostDao.persist(templateHostVO); UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, privateTemplate.getAccountId(), secondaryStorageHost.getDataCenterId(), privateTemplate.getId(), privateTemplate.getName(), null, privateTemplate.getSourceTemplateId(), templateHostVO.getSize()); _usageEventDao.persist(usageEvent); txn.commit(); } } finally { if (privateTemplate == null) { Transaction txn = Transaction.currentTxn(); txn.start(); // Remove the template record _templateDao.remove(templateId); // decrement resource count _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); txn.commit(); } } return privateTemplate; } @Override public String getChecksum(Long hostId, String templatePath){ HostVO ssHost = _hostDao.findById(hostId); Host.Type type = ssHost.getType(); if( type != Host.Type.SecondaryStorage && type != Host.Type.LocalSecondaryStorage ) { return null; } String secUrl = ssHost.getStorageUrl(); Answer answer; answer = _agentMgr.sendToSecStorage(ssHost, new ComputeChecksumCommand(secUrl, templatePath)); if(answer != null && answer.getResult()) { return answer.getDetails(); } return null; } // 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)) { try { _itMgr.stateTransitTo(vm, VirtualMachine.Event.OperationFailedToError, null); } catch (NoTransitionException e1) { s_logger.warn(e1.getMessage()); } // destroy associated volumes for vm in error state // get all volumes in non destroyed state List volumesForThisVm = _volsDao.findUsableVolumesForInstance(vm.getId()); for (VolumeVO volume : volumesForThisVm) { try { if (volume.getState() != Volume.State.Destroy) { _storageMgr.destroyVolume(volume); } } catch (ConcurrentOperationException e) { s_logger.warn("Unable to delete volume:" + volume.getId() + " for vm:" + vmId + " whilst transitioning to error state"); } } String msg = "Failed to deploy Vm with Id: " + vmId; _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), msg, msg); _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.user_vm); } } } protected class ExpungeTask implements Runnable { public ExpungeTask() { } @Override public void run() { GlobalLock scanLock = GlobalLock.getInternLock("UserVMExpunge"); try { if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { try { List vms = _vmDao.findDestroyedVms(new Date(System.currentTimeMillis() - ((long) _expungeDelay << 10))); if (s_logger.isInfoEnabled()) { if (vms.size() == 0) { s_logger.trace("Found " + vms.size() + " vms to expunge."); } else { s_logger.info("Found " + vms.size() + " vms to expunge."); } } for (UserVmVO vm : vms) { try { expunge(vm, _accountMgr.getSystemUser().getId(), _accountMgr.getSystemAccount()); } catch (Exception e) { s_logger.warn("Unable to expunge " + vm, e); } } } catch (Exception e) { s_logger.error("Caught the following Exception", e); } finally { scanLock.unlock(); } } } finally { scanLock.releaseRef(); } } } private static boolean isAdmin(short accountType) { return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm") public UserVm updateVirtualMachine(UpdateVMCmd cmd) { String displayName = cmd.getDisplayName(); String group = cmd.getGroup(); Boolean ha = cmd.getHaEnable(); Long id = cmd.getId(); Long osTypeId = cmd.getOsTypeId(); Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); String userData = cmd.getUserData(); // Input validation UserVmVO vmInstance = null; // Verify input parameters vmInstance = _vmDao.findById(id.longValue()); if (vmInstance == null) { throw new InvalidParameterValueException("unable to find virtual machine with id " + id); } ServiceOffering offering = _serviceOfferingDao.findById(vmInstance.getServiceOfferingId()); if (!offering.getOfferHA() && ha != null && ha) { throw new InvalidParameterValueException("Can't enable ha for the vm as it's created from the Service offering having HA disabled"); } userId = accountAndUserValidation(id, account, userId, vmInstance); if (displayName == null) { displayName = vmInstance.getDisplayName(); } if (ha == null) { ha = vmInstance.isHaEnabled(); } UserVmVO vm = _vmDao.findById(id); if (vm == null) { throw new CloudRuntimeException("Unable to find virual machine with id " + id); } if (vm.getState() == State.Error || vm.getState() == State.Expunging) { s_logger.error("vm is not in the right state: " + id); throw new InvalidParameterValueException("Vm with id " + id + " is not in the right state"); } if (userData != null) { validateUserData(userData); // update userData on domain router. } else { userData = vmInstance.getUserData(); } String description = ""; if (displayName != vmInstance.getDisplayName()) { description += "New display name: " + displayName + ". "; } if (ha != vmInstance.isHaEnabled()) { if (ha) { description += "Enabled HA. "; } else { description += "Disabled HA. "; } } if (osTypeId == null) { osTypeId = vmInstance.getGuestOSId(); } else { description += "Changed Guest OS Type to " + osTypeId + ". "; } if (group != null) { if (addInstanceToGroup(id, group)) { description += "Added to group: " + group + "."; } } _vmDao.updateVM(id, displayName, ha, osTypeId, userData); return _vmDao.findById(id); } @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()); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true) public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException { Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); Long vmId = cmd.getId(); // Verify input parameters UserVmVO vmInstance = _vmDao.findById(vmId.longValue()); if (vmInstance == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } userId = accountAndUserValidation(vmId, account, userId, vmInstance); return rebootVirtualMachine(userId, vmId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_DESTROY, eventDescription = "destroying Vm", async = true) public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException { return destroyVm(cmd.getId()); } @Override @DB public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { Account account = UserContext.current().getCaller(); Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); Long accountId = null; String groupName = cmd.getGroupName(); if (account == null) { account = _accountDao.findById(1L); } if (account != null) { if (isAdmin(account.getType())) { if ((domainId != null) && (accountName != null)) { if (!_domainDao.isChildDomain(account.getDomainId(), domainId)) { throw new PermissionDeniedException("Unable to create vm group in domain " + domainId + ", permission denied."); } Account userAccount = _accountDao.findActiveAccount(accountName, domainId); if (userAccount != null) { accountId = userAccount.getId(); } else { throw new InvalidParameterValueException("Failed to create vm group " + groupName + ", unable to find account " + accountName + " in domain " + domainId); } } else { // the admin must be creating the vm group accountId = account.getId(); } } else { accountId = account.getId(); } } if (accountId == null) { throw new InvalidParameterValueException("Failed to create vm group " + groupName + ", unable to find account for which to create a group."); } // Check if name is already in use by this account boolean isNameInUse = _vmGroupDao.isNameInUse(accountId, groupName); if (isNameInUse) { throw new InvalidParameterValueException("Unable to create vm group, a group with name " + groupName + " already exisits for account " + accountId); } return createVmGroup(groupName, accountId); } @DB private InstanceGroupVO createVmGroup(String groupName, long accountId) { Account account = null; final Transaction txn = Transaction.currentTxn(); txn.start(); try { account = _accountDao.acquireInLockTable(accountId); // to ensure duplicate vm group names are not created. if (account == null) { s_logger.warn("Failed to acquire lock on account"); return null; } InstanceGroupVO group = _vmGroupDao.findByAccountAndName(accountId, groupName); if (group == null) { group = new InstanceGroupVO(groupName, accountId); group = _vmGroupDao.persist(group); } return group; } finally { if (account != null) { _accountDao.releaseFromLockTable(accountId); } txn.commit(); } } @Override public boolean deleteVmGroup(DeleteVMGroupCmd cmd) { Account account = UserContext.current().getCaller(); Long groupId = cmd.getId(); // Verify input parameters InstanceGroupVO group = _vmGroupDao.findById(groupId); if ((group == null) || (group.getRemoved() != null)) { throw new InvalidParameterValueException("unable to find a vm group with id " + groupId); } if (account != null) { Account tempAccount = _accountDao.findById(group.getAccountId()); if (!isAdmin(account.getType()) && (account.getId() != group.getAccountId())) { throw new PermissionDeniedException("unable to find a group with id " + groupId); } else if (!_domainDao.isChildDomain(account.getDomainId(), tempAccount.getDomainId())) { throw new PermissionDeniedException("Invalid group id (" + groupId + ") given, unable to update the group."); } } return deleteVmGroup(groupId); } @Override public boolean deleteVmGroup(long groupId) { // delete all the mappings from group_vm_map table List groupVmMaps = _groupVMMapDao.listByGroupId(groupId); for (InstanceGroupVMMapVO groupMap : groupVmMaps) { SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); _groupVMMapDao.expunge(sc); } if (_vmGroupDao.remove(groupId)) { return true; } else { return false; } } @Override @DB public boolean addInstanceToGroup(long userVmId, String groupName) { UserVmVO vm = _vmDao.findById(userVmId); InstanceGroupVO group = _vmGroupDao.findByAccountAndName(vm.getAccountId(), groupName); // Create vm group if the group doesn't exist for this account if (group == null) { group = createVmGroup(groupName, vm.getAccountId()); } if (group != null) { final Transaction txn = Transaction.currentTxn(); txn.start(); UserVm userVm = _vmDao.acquireInLockTable(userVmId); if (userVm == null) { s_logger.warn("Failed to acquire lock on user vm id=" + userVmId); } try { // don't let the group be deleted when we are assigning vm to it. InstanceGroupVO ngrpLock = _vmGroupDao.lockRow(group.getId(), false); if (ngrpLock == null) { s_logger.warn("Failed to acquire lock on vm group id=" + group.getId() + " name=" + group.getName()); txn.rollback(); return false; } // Currently don't allow to assign a vm to more than one group if (_groupVMMapDao.listByInstanceId(userVmId) != null) { // Delete all mappings from group_vm_map table List groupVmMaps = _groupVMMapDao.listByInstanceId(userVmId); for (InstanceGroupVMMapVO groupMap : groupVmMaps) { SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); _groupVMMapDao.expunge(sc); } } InstanceGroupVMMapVO groupVmMapVO = new InstanceGroupVMMapVO(group.getId(), userVmId); _groupVMMapDao.persist(groupVmMapVO); txn.commit(); return true; } finally { if (userVm != null) { _vmDao.releaseFromLockTable(userVmId); } } } return false; } @Override public InstanceGroupVO getGroupForVm(long vmId) { // TODO - in future releases vm can be assigned to multiple groups; but currently return just one group per vm try { List groupsToVmMap = _groupVMMapDao.listByInstanceId(vmId); if (groupsToVmMap != null && groupsToVmMap.size() != 0) { InstanceGroupVO group = _vmGroupDao.findById(groupsToVmMap.get(0).getGroupId()); return group; } else { return null; } } catch (Exception e) { s_logger.warn("Error trying to get group for a vm: " + e); return null; } } @Override public void removeInstanceFromInstanceGroup(long vmId) { try { List groupVmMaps = _groupVMMapDao.listByInstanceId(vmId); for (InstanceGroupVMMapVO groupMap : groupVmMaps) { SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); _groupVMMapDao.expunge(sc); } } catch (Exception e) { s_logger.warn("Error trying to remove vm from group: " + e); } } protected boolean validPassword(String password) { if (password == null || password.length() == 0) { return false; } for (int i = 0; i < password.length(); i++) { if (password.charAt(i) == ' ') { return false; } } return true; } @Override public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); List networkList = new ArrayList(); // Verify that caller can perform actions in behalf of vm owner _accountMgr.checkAccess(caller, null, owner); // Get default guest network in Basic zone Network defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(zone.getId(), TrafficType.Guest); if (defaultNetwork == null) { throw new InvalidParameterValueException("Unable to find a default network to start a vm"); } else { networkList.add(_networkDao.findById(defaultNetwork.getId())); } boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); if (securityGroupIdList != null && isVmWare) { throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor"); } else if (!isVmWare) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList(); } SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); if (defaultGroup != null) { //check if security group id list already contains Default security group, and if not - add it boolean defaultGroupPresent = false; for (Long securityGroupId : securityGroupIdList) { if (securityGroupId.longValue() == defaultGroup.getId()) { defaultGroupPresent = true; break; } } if (!defaultGroupPresent) { securityGroupIdList.add(defaultGroup.getId()); } } else { //create default security group for the account if (s_logger.isDebugEnabled()) { s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); } defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } } return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); } @Override public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); List networkList = new ArrayList(); boolean isSecurityGroupEnabledNetworkUsed = false; boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); //Verify that caller can perform actions in behalf of vm owner _accountMgr.checkAccess(caller, null, owner); // If no network is specified, find system security group enabled network if (networkIdList == null || networkIdList.isEmpty()) { NetworkVO networkWithSecurityGroup = _networkMgr.getNetworkWithSecurityGroupEnabled(zone.getId()); if (networkWithSecurityGroup == null) { throw new InvalidParameterValueException("No network with security enabled is found in zone id=" + zone.getId()); } networkList.add(networkWithSecurityGroup); } else if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { if (isVmWare) { throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor"); } // Only one network can be specified, and it should be security group enabled if (networkIdList.size() > 1) { throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); } NetworkVO network = _networkDao.findById(networkIdList.get(0).longValue()); if (network == null) { throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); } if (!network.isSecurityGroupEnabled()) { throw new InvalidParameterValueException("Network is not security group enabled: " + network.getId()); } networkList.add(network); isSecurityGroupEnabledNetworkUsed = true; } else { // Verify that all the networks are Direct/Guest/AccountSpecific; can't create combination of SG enabled network and // regular networks for (Long networkId : networkIdList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); } if (network.isSecurityGroupEnabled() && networkIdList.size() > 1) { throw new InvalidParameterValueException("Can't create a vm with multiple networks one of which is Security Group enabled"); } if (network.getTrafficType() != TrafficType.Guest || network.getGuestType() != GuestIpType.Direct || (network.getIsShared() && !network.isSecurityGroupEnabled())) { throw new InvalidParameterValueException("Can specify only Direct Guest Account specific networks when deploy vm in Security Group enabled zone"); } // Perform account permission check if (!network.getIsShared()) { // Check account permissions List networkMap = _networkDao.listBy(owner.getId(), network.getId()); if (networkMap == null || networkMap.isEmpty()) { throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied"); } } networkList.add(network); } } // if network is security group enabled, and default security group is not present in the list of groups specified, add it automatically if (isSecurityGroupEnabledNetworkUsed && !isVmWare) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList(); } SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); if (defaultGroup != null) { //check if security group id list already contains Default security group, and if not - add it boolean defaultGroupPresent = false; for (Long securityGroupId : securityGroupIdList) { if (securityGroupId.longValue() == defaultGroup.getId()) { defaultGroupPresent = true; break; } } if (!defaultGroupPresent) { securityGroupIdList.add(defaultGroup.getId()); } } else { //create default security group for the account if (s_logger.isDebugEnabled()) { s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); } defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } } return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); } @Override public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); List networkList = new ArrayList(); // Verify that caller can perform actions in behalf of vm owner _accountMgr.checkAccess(caller, null, owner); if (networkIdList == null || networkIdList.isEmpty()) { NetworkVO defaultNetwork = null; // if no network is passed in // 1) Check if default virtual network offering has Availability=Required. If it's true, search for corresponding // network // * if network is found, use it. If more than 1 virtual network is found, throw an error // * if network is not found, create a new one and use it // 2) If Availability=Optional, search for default networks for the account. If it's more than 1, throw an error. // If it's 0, and there are no default direct networks, create default Guest Virtual network List defaultVirtualOffering = _networkOfferingDao.listByTrafficTypeAndGuestType(false, TrafficType.Guest, GuestIpType.Virtual); if (defaultVirtualOffering.get(0).getAvailability() == Availability.Required) { // get Virtual netowrks List virtualNetworks = _networkMgr.listNetworksForAccount(owner.getId(), zone.getId(), GuestIpType.Virtual, true); if (virtualNetworks.isEmpty()) { s_logger.debug("Creating default Virtual network for account " + owner + " as a part of deployVM process"); Network newNetwork = _networkMgr.createNetwork(defaultVirtualOffering.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", false, null, zone.getId(), null, null, null, null, owner, false, null, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { throw new InvalidParameterValueException("More than 1 default Virtaul networks are found for account " + owner + "; please specify networkIds"); } else { defaultNetwork = virtualNetworks.get(0); } } else { List defaultNetworks = _networkMgr.listNetworksForAccount(owner.getId(), zone.getId(), null, true); if (defaultNetworks.isEmpty()) { if (defaultVirtualOffering.get(0).getAvailability() == Availability.Optional) { s_logger.debug("Creating default Virtual network for account " + owner + " as a part of deployVM process"); Network newNetwork = _networkMgr.createNetwork(defaultVirtualOffering.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", false, null, zone.getId(), null, null, null, null, owner, false, null, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else { throw new InvalidParameterValueException("Unable to find default networks for account " + owner); } } else if (defaultNetworks.size() > 1) { throw new InvalidParameterValueException("More than 1 default network is found for account " + owner); } else { defaultNetwork = defaultNetworks.get(0); } } // Check that network offering doesn't have Availability=Unavailable NetworkOffering networkOffering = _configMgr.getNetworkOffering(defaultNetwork.getNetworkOfferingId()); if (networkOffering.getAvailability() == Availability.Unavailable) { throw new InvalidParameterValueException("Unable to find default network; please specify networkOfferingIds"); } networkList.add(defaultNetwork); } else { boolean requiredNetworkOfferingIsPresent = false; List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); Long requiredOfferingId = null; if (!requiredOfferings.isEmpty()) { // in 2.2.x there can be only one required offering - default Virtual requiredOfferingId = requiredOfferings.get(0).getId(); } for (Long networkId : networkIdList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); } // Perform account permission check if (!network.getIsShared()) { List networkMap = _networkDao.listBy(owner.getId(), network.getId()); if (networkMap == null || networkMap.isEmpty()) { throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied"); } } else { if (!_networkMgr.isNetworkAvailableInDomain(networkId, owner.getDomainId())) { throw new PermissionDeniedException("Shared network id=" + networkId + " is not available in domain id=" + owner.getDomainId()); } } // check that corresponding offering is available NetworkOffering networkOffering = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); if (networkOffering.getAvailability() == Availability.Unavailable) { throw new InvalidParameterValueException("Network id=" + network.getId() + " can't be used; corresponding network offering is " + Availability.Unavailable); } //don't allow to use system networks if (networkOffering.isSystemOnly()) { throw new InvalidParameterValueException("Network id=" + networkId + " is system only and can't be used for vm deployment"); } if (requiredOfferingId != null && network.getNetworkOfferingId() == requiredOfferingId.longValue()) { requiredNetworkOfferingIsPresent = true; } networkList.add(network); } // If default Virtual network offering is Required, it has to be specified in the network list if (requiredOfferingId != null && !requiredNetworkOfferingIsPresent) { throw new InvalidParameterValueException("Network created from the network offering id=" + requiredOfferingId + " is required; change network offering availability to be Optional to relax this requirement"); } } return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); } @DB @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, String hostName, String displayName, Account owner, Long diskOfferingId, Long diskSize, List networkList, List securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, String defaultNetworkIp, String keyboard) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, owner); long accountId = owner.getId(); assert !(requestedIps != null && defaultNetworkIp != null) : "requestedIp list and defaultNetworkIp should never be specified together"; if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getId()); } if (zone.getDomainId() != null) { DomainVO domain = _domainDao.findById(zone.getDomainId()); if (domain == null) { throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); } // check that caller can operate with domain _accountMgr.checkAccess(caller, domain); // check that vm owner can create vm in the domain _accountMgr.checkAccess(owner, domain); } // check if account/domain is with in resource limits to create a new vm _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); //verify security group ids if (securityGroupIdList != null) { for (Long securityGroupId : securityGroupIdList) { if (_securityGroupDao.findById(securityGroupId) == null) { throw new InvalidParameterValueException("Unable to find security group by id " + securityGroupId); } } } // check if we have available pools for vm deployment long availablePools = _storagePoolDao.countPoolsByStatus(StoragePoolStatus.Up); if (availablePools < 1) { throw new StorageUnavailableException("There are no available pools in the UP state for vm deployment", -1); } ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); if (template.getTemplateType().equals(TemplateType.SYSTEM)) { throw new InvalidParameterValueException("Unable to use system template " + template.getId() + " to deploy a user vm"); } List listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId()); if (listZoneTemplate == null || listZoneTemplate.isEmpty()) { throw new InvalidParameterValueException("The template " + template.getId() + " is not available for use"); } boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); if (isIso && !template.isBootable()) { throw new InvalidParameterValueException("Installing from ISO requires an ISO that is bootable: " + template.getId()); } // Check templates permissions if (!template.isPublicTemplate()) { Account templateOwner = _accountMgr.getAccount(template.getAccountId()); _accountMgr.checkAccess(owner, null, templateOwner); } // If the template represents an ISO, a disk offering must be passed in, and will be used to create the root disk // Else, a disk offering is optional, and if present will be used to create the data disk Pair rootDiskOffering = new Pair(null, null); List> dataDiskOfferings = new ArrayList>(); if (isIso) { if (diskOfferingId == null) { throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); } DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } Long size = null; if (diskOffering.getDiskSize() == 0) { size = diskSize; if (size == null) { throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); } } rootDiskOffering.first(diskOffering); rootDiskOffering.second(size); } else { rootDiskOffering.first(offering); if (diskOfferingId != null) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } Long size = null; if (diskOffering.getDiskSize() == 0) { size = diskSize; if (size == null) { throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); } } dataDiskOfferings.add(new Pair(diskOffering, size)); } } validateUserData(userData); // Find an SSH public key corresponding to the key pair name, if one is given String sshPublicKey = null; if (sshKeyPair != null && !sshKeyPair.equals("")) { SSHKeyPair pair = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), sshKeyPair); if (pair == null) { throw new InvalidParameterValueException("A key pair with name '" + sshKeyPair + "' was not found."); } sshPublicKey = pair.getPublicKey(); } DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); s_logger.debug("Allocating in the DB for vm"); List> networks = new ArrayList>(); short defaultNetworkNumber = 0; for (NetworkVO network : networkList) { if (network.getDataCenterId() != zone.getId()) { throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId()); } NicProfile profile = null; //Add requested ips if (requestedIps != null && requestedIps.get(network.getId()) != null) { profile = new NicProfile(requestedIps.get(network.getId())); } if (network.isDefault()) { defaultNetworkNumber++; // if user requested specific ip for default network, add it if (defaultNetworkIp != null) { profile = new NicProfile(defaultNetworkIp); } } networks.add(new Pair(network, profile)); } // Verify network information - network default network has to be set; and vm can't have more than one default network // This is a part of business logic because default network is required by Agent Manager in order to configure default // gateway for the vm if (defaultNetworkNumber == 0) { throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); } else if (defaultNetworkNumber > 1) { throw new InvalidParameterValueException("Only 1 default network per vm is supported"); } long id = _vmDao.getNextInSequence(Long.class, "id"); String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); if (hostName == null) { hostName = instanceName; } else { // verify hostName (hostname doesn't have to be unique) if (!NetUtils.verifyDomainNameLabel(hostName, true)) { throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); } } HypervisorType hypervisorType = null; if (template == null || template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { hypervisorType = hypervisor; } else { hypervisorType = template.getHypervisorType(); } Transaction txn = Transaction.currentTxn(); txn.start(); UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(), offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), offering.getId(), userData, hostName); if (sshPublicKey != null) { vm.setDetail("SSH.PublicKey", sshPublicKey); } if(keyboard != null && !keyboard.isEmpty()) vm.setDetail(VmDetailConstants.KEYBOARD, keyboard); if (isIso) { vm.setIsoId(template.getId()); } s_logger.debug("Allocating in the DB for vm"); if (_itMgr.allocate(vm, _templateDao.findById(template.getId()), offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, hypervisorType, owner) == null) { return null; } _vmDao.saveDetails(vm); if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully allocated DB entry for " + vm); } UserContext.current().setEventDetails("Vm Id: " + vm.getId()); UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString()); _usageEventDao.persist(usageEvent); _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); txn.commit(); // Assign instance to the group try { if (group != null) { boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); if (!addToGroup) { throw new CloudRuntimeException("Unable to assign Vm to the group " + group); } } } catch (Exception ex) { throw new CloudRuntimeException("Unable to assign Vm to the group " + group); } _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); return vm; } private void validateUserData(String userData) { byte[] decodedUserData = null; if (userData != null) { if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { throw new InvalidParameterValueException("User data is too long"); } decodedUserData = org.apache.commons.codec.binary.Base64.decodeBase64(userData.getBytes()); if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES) { throw new InvalidParameterValueException("User data is too long"); } if (decodedUserData.length < 1) { throw new InvalidParameterValueException("User data is too short"); } } } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "starting Vm", async = true) public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { return startVirtualMachine(cmd, null); } 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(); 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()); 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); } vm = _itMgr.start(vm, params, caller, owner, plan); } finally { updateVmStateForFailedVmCreation(vm.getId()); } if (template.getEnablePassword()) { // this value is not being sent to the backend; need only for api dispaly purposes vm.setPassword(password); } return vm; } @Override public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { UserVmVO vm = profile.getVirtualMachine(); Map details = _vmDetailsDao.findDetails(vm.getId()); vm.setDetails(details); Account owner = _accountDao.findById(vm.getAccountId()); if (owner == null) { throw new PermissionDeniedException("The owner of " + vm + " does not exist: " + vm.getAccountId()); } if (owner.getState() == Account.State.disabled) { throw new PermissionDeniedException("The owner of " + vm + " is disabled: " + vm.getAccountId()); } if (vm.getIsoId() != null) { String isoPath = null; VirtualMachineTemplate template = _templateDao.findById(vm.getIsoId()); if (template == null || template.getFormat() != ImageFormat.ISO) { throw new CloudRuntimeException("Can not find ISO in vm_template table for id " + vm.getIsoId()); } Pair isoPathPair = _storageMgr.getAbsoluteIsoPath(template.getId(), vm.getDataCenterIdToDeployIn()); if (isoPathPair == null) { s_logger.warn("Couldn't get absolute iso path"); return false; } else { isoPath = isoPathPair.first(); } if (template.isBootable()) { profile.setBootLoaderType(BootloaderType.CD); } GuestOSVO guestOS = _guestOSDao.findById(template.getGuestOSId()); String displayName = null; if (guestOS != null) { displayName = guestOS.getDisplayName(); } VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, StoragePoolType.ISO, null, template.getName(), null, isoPath, 0, null, displayName); iso.setDeviceId(3); profile.addDisk(iso); } else { VirtualMachineTemplate template = profile.getTemplate(); /* create a iso placeholder */ VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, StoragePoolType.ISO, null, template.getName(), null, null, 0, null); iso.setDeviceId(3); profile.addDisk(iso); } return true; } @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { UserVmVO userVm = profile.getVirtualMachine(); List nics = _nicDao.listByVmId(userVm.getId()); for (NicVO nic : nics) { NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network.getTrafficType() == TrafficType.Guest || network.getTrafficType() == TrafficType.Public) { userVm.setPrivateIpAddress(nic.getIp4Address()); userVm.setPrivateMacAddress(nic.getMacAddress()); } } _vmDao.update(userVm.getId(), userVm); return true; } @Override public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { return true; } @Override public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { UserVmVO vm = profile.getVirtualMachine(); Answer[] answersToCmds = cmds.getAnswers(); if(answersToCmds == null){ if(s_logger.isDebugEnabled()){ s_logger.debug("Returning from finalizeStart() since there are no answers to read"); } return true; } Answer startAnswer = cmds.getAnswer(StartAnswer.class); String returnedIp = null; String originalIp = null; if (startAnswer != null) { StartAnswer startAns = (StartAnswer) startAnswer; VirtualMachineTO vmTO = startAns.getVirtualMachine(); for (NicTO nicTO: vmTO.getNics()) { if (nicTO.getType() == TrafficType.Guest) { returnedIp = nicTO.getIp(); } } } List nics = _nicDao.listByVmId(vm.getId()); NicVO guestNic = null; for (NicVO nic : nics) { NetworkVO network = _networkDao.findById(nic.getNetworkId()); long isDefault = (nic.isDefaultNic()) ? 1 : 0; UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), network.getNetworkOfferingId(), null, isDefault); _usageEventDao.persist(usageEvent); if (network.getTrafficType() == TrafficType.Guest) { originalIp = nic.getIp4Address(); guestNic = nic; } } boolean ipChanged = false; if (originalIp != null && !originalIp.equalsIgnoreCase(returnedIp)) { if (returnedIp != null && guestNic != null) { guestNic.setIp4Address(returnedIp); ipChanged = true; } } if (returnedIp != null && !returnedIp.equalsIgnoreCase(originalIp)) { if (guestNic != null) { guestNic.setIp4Address(returnedIp); ipChanged = true; } } if (ipChanged) { DataCenterVO dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); UserVmVO userVm = profile.getVirtualMachine(); if (dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName())){ _nicDao.update(guestNic.getId(), guestNic); userVm.setPrivateIpAddress(guestNic.getIp4Address()); _vmDao.update(userVm.getId(), userVm); s_logger.info("Detected that ip changed in the answer, updated nic in the db with new ip " + returnedIp); } } return true; } @Override public void finalizeExpunge(UserVmVO vm) { } @Override public UserVmVO persist(UserVmVO vm) { return _vmDao.persist(vm); } @Override public UserVmVO findById(long id) { return _vmDao.findById(id); } @Override public UserVmVO findByName(String name) { if (!VirtualMachineName.isValidVmName(name)) { return null; } return findById(VirtualMachineName.getVmId(name)); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = "stopping Vm", async = true) public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOperationException { // Input validation Account caller = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); // if account is removed, return error if (caller != null && caller.getRemoved() != null) { throw new PermissionDeniedException("The account " + caller.getId() + " is removed"); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } userId = accountAndUserValidation(vmId, caller, userId, vm); UserVO user = _userDao.findById(userId); try { _itMgr.advanceStop(vm, forced, user, caller); } catch (ResourceUnavailableException e) { throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); } catch (OperationTimedoutException e) { throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); } return _vmDao.findById(vmId); } @Override public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { } public String generateRandomPassword() { return PasswordGenerator.generateRandomPassword(6); } @Override public UserVm startVirtualMachine(long vmId) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // Input validation Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); // if account is removed, return error if (account != null && account.getRemoved() != null) { throw new PermissionDeniedException("The account " + account.getId() + " is removed"); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } userId = accountAndUserValidation(vmId, account, userId, vm); UserVO user = _userDao.findById(userId); //check if vm is security group enabled if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId)) { //if vm is not mapped to security group, create a mapping if (s_logger.isDebugEnabled()) { s_logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); } SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); if (defaultSecurityGroup != null) { List groupList = new ArrayList(); groupList.add(defaultSecurityGroup.getId()); _securityGroupMgr.addInstanceToGroups(vmId, groupList); } } return _itMgr.start(vm, null, user, account); } @Override public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { Account account = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); // Verify input parameters UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId); } if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { s_logger.trace("Vm id=" + vmId + " is already destroyed"); return vm; } userId = accountAndUserValidation(vmId, account, userId, vm); User caller = _userDao.findById(userId); boolean status; State vmState = vm.getState(); try { status = _itMgr.destroy(vm, caller, account); } catch (OperationTimedoutException e) { throw new CloudRuntimeException("Unable to destroy " + vm, e); } if (status) { // Mark the account's volumes as destroyed List volumes = _volsDao.findByInstance(vmId); for (VolumeVO volume : volumes) { if (volume.getVolumeType().equals(Volume.Type.ROOT)) { UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName()); _usageEventDao.persist(usageEvent); } } if (vmState != State.Error) { _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.user_vm); } return _vmDao.findById(vmId); } else { throw new CloudRuntimeException("Failed to destroy vm with id " + vmId); } } @Override public List searchForUserVMs(ListVMsCmd cmd) { Account caller = UserContext.current().getCaller(); Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); Boolean isRecursive = cmd.isRecursive(); String hypervisor = cmd.getHypervisor(); Long accountId = null; String path = null; if (isRecursive != null && isRecursive && domainId == null) { throw new InvalidParameterValueException("Please enter a parent domain id for listing vms recursively"); } if (domainId != null) { // Verify if user is authorized to see instances belonging to the domain DomainVO domain = _domainDao.findById(domainId); if (domain == null) { throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); } _accountMgr.checkAccess(caller, domain); } boolean isAdmin = false; if (_accountMgr.isAdmin(caller.getType())) { isAdmin = true; if (accountName != null && domainId != null) { caller = _accountDao.findActiveAccount(accountName, domainId); if (caller == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); } accountId = caller.getId(); } if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) { if (isRecursive == null) { DomainVO domain = _domainDao.findById(caller.getDomainId()); path = domain.getPath(); } } } else { //regular user can't specify any other domain rather than his own if (domainId != null && domainId.longValue() != caller.getDomainId()) { throw new PermissionDeniedException("Caller is not authorised to see domain id=" + domainId + " entries"); } accountId = caller.getId(); } if (isRecursive != null && isRecursive && isAdmin) { if (isRecursive) { DomainVO domain = _domainDao.findById(domainId); path = domain.getPath(); domainId = null; } } Criteria c = new Criteria("id", Boolean.TRUE, cmd.getStartIndex(), cmd.getPageSizeVal()); c.addCriteria(Criteria.KEYWORD, cmd.getKeyword()); c.addCriteria(Criteria.ID, cmd.getId()); c.addCriteria(Criteria.NAME, cmd.getInstanceName()); c.addCriteria(Criteria.STATE, cmd.getState()); c.addCriteria(Criteria.DATACENTERID, cmd.getZoneId()); c.addCriteria(Criteria.GROUPID, cmd.getGroupId()); c.addCriteria(Criteria.FOR_VIRTUAL_NETWORK, cmd.getForVirtualNetwork()); c.addCriteria(Criteria.NETWORKID, cmd.getNetworkId()); if (domainId != null) { c.addCriteria(Criteria.DOMAINID, domainId); } if (path != null) { c.addCriteria(Criteria.PATH, path); } if (HypervisorType.getType(hypervisor) != HypervisorType.None) { c.addCriteria(Criteria.HYPERVISOR, hypervisor); } else if (hypervisor != null) { throw new InvalidParameterValueException("Invalid HypervisorType " + hypervisor); } // ignore these search requests if it's not an admin if (isAdmin) { c.addCriteria(Criteria.PODID, cmd.getPodId()); c.addCriteria(Criteria.HOSTID, cmd.getHostId()); c.addCriteria(Criteria.STORAGE_ID, cmd.getStorageId()); } if (accountId != null) { c.addCriteria(Criteria.ACCOUNTID, new Object[] { accountId }); } c.addCriteria(Criteria.ISADMIN, isAdmin); return searchForUserVMs(c); } @Override public List searchForUserVMs(Criteria c) { Filter searchFilter = new Filter(UserVmVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit()); SearchBuilder sb = _vmDao.createSearchBuilder(); Object[] accountIds = (Object[]) c.getCriteria(Criteria.ACCOUNTID); Object domainId = c.getCriteria(Criteria.DOMAINID); Object id = c.getCriteria(Criteria.ID); Object name = c.getCriteria(Criteria.NAME); Object state = c.getCriteria(Criteria.STATE); Object notState = c.getCriteria(Criteria.NOTSTATE); Object zone = c.getCriteria(Criteria.DATACENTERID); Object pod = c.getCriteria(Criteria.PODID); Object hostId = c.getCriteria(Criteria.HOSTID); Object hostName = c.getCriteria(Criteria.HOSTNAME); Object keyword = c.getCriteria(Criteria.KEYWORD); Object isAdmin = c.getCriteria(Criteria.ISADMIN); assert c.getCriteria(Criteria.IPADDRESS) == null : "We don't support search by ip address on VM any more. If you see this assert, it means we have to find a different way to search by the nic table."; Object groupId = c.getCriteria(Criteria.GROUPID); Object path = c.getCriteria(Criteria.PATH); Object networkId = c.getCriteria(Criteria.NETWORKID); Object hypervisor = c.getCriteria(Criteria.HYPERVISOR); Object storageId = c.getCriteria(Criteria.STORAGE_ID); sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("accountIdEQ", sb.entity().getAccountId(), SearchCriteria.Op.EQ); sb.and("accountIdIN", sb.entity().getAccountId(), SearchCriteria.Op.IN); sb.and("name", sb.entity().getHostName(), SearchCriteria.Op.LIKE); sb.and("stateEQ", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); sb.and("stateNIN", sb.entity().getState(), SearchCriteria.Op.NIN); sb.and("dataCenterId", sb.entity().getDataCenterIdToDeployIn(), SearchCriteria.Op.EQ); sb.and("podId", sb.entity().getPodIdToDeployIn(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ); sb.and("hostIdIN", sb.entity().getHostId(), SearchCriteria.Op.IN); sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ); if (path != null) { SearchBuilder domainSearch = _domainDao.createSearchBuilder(); domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); } if (groupId != null && (Long) groupId == -1) { SearchBuilder vmSearch = _groupVMMapDao.createSearchBuilder(); vmSearch.and("instanceId", vmSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); sb.join("vmSearch", vmSearch, sb.entity().getId(), vmSearch.entity().getInstanceId(), JoinBuilder.JoinType.LEFTOUTER); } else if (groupId != null) { SearchBuilder groupSearch = _groupVMMapDao.createSearchBuilder(); groupSearch.and("groupId", groupSearch.entity().getGroupId(), SearchCriteria.Op.EQ); sb.join("groupSearch", groupSearch, sb.entity().getId(), groupSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); } if (networkId != null) { SearchBuilder nicSearch = _nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); SearchBuilder networkSearch = _networkDao.createSearchBuilder(); networkSearch.and("networkId", networkSearch.entity().getId(), SearchCriteria.Op.EQ); nicSearch.join("networkSearch", networkSearch, nicSearch.entity().getNetworkId(), networkSearch.entity().getId(), JoinBuilder.JoinType.INNER); sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); } if (storageId != null) { SearchBuilder volumeSearch = _volsDao.createSearchBuilder(); volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), SearchCriteria.Op.EQ); sb.join("volumeSearch", volumeSearch, sb.entity().getId(), volumeSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); } // populate the search criteria with the values passed in SearchCriteria sc = sb.create(); if (groupId != null && (Long) groupId == -1) { sc.setJoinParameters("vmSearch", "instanceId", (Object) null); } else if (groupId != null) { sc.setJoinParameters("groupSearch", "groupId", groupId); } if (keyword != null) { SearchCriteria ssc = _vmDao.createSearchCriteria(); ssc.addOr("displayName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("hostName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("instanceName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("state", SearchCriteria.Op.EQ, keyword); sc.addAnd("displayName", SearchCriteria.Op.SC, ssc); } if (id != null) { sc.setParameters("id", id); } if (accountIds != null) { if (accountIds.length == 1) { if (accountIds[0] != null) { sc.setParameters("accountIdEQ", accountIds[0]); } } else { sc.setParameters("accountIdIN", accountIds); } } if (domainId != null) { sc.setParameters("domainId", domainId); } if (path != null) { sc.setJoinParameters("domainSearch", "path", path + "%"); } if (networkId != null) { sc.setJoinParameters("nicSearch", "networkId", networkId); } if (name != null) { sc.setParameters("name", "%" + name + "%"); } if (state != null) { if (notState != null && (Boolean) notState == true) { sc.setParameters("stateNEQ", state); } else { sc.setParameters("stateEQ", state); } } if (hypervisor != null) { sc.setParameters("hypervisorType", hypervisor); } // Don't show Destroyed and Expunging vms to the end user if ((isAdmin != null) && ((Boolean) isAdmin != true)) { sc.setParameters("stateNIN", "Destroyed", "Expunging"); } if (zone != null) { sc.setParameters("dataCenterId", zone); if (state == null) { sc.setParameters("stateNEQ", "Destroyed"); } } if (pod != null) { sc.setParameters("podId", pod); if (state == null) { sc.setParameters("stateNEQ", "Destroyed"); } } if (hostId != null) { sc.setParameters("hostIdEQ", hostId); } else { if (hostName != null) { List hosts = _hostDao.findHostsLike((String) hostName); if (hosts != null & !hosts.isEmpty()) { Long[] hostIds = new Long[hosts.size()]; for (int i = 0; i < hosts.size(); i++) { HostVO host = hosts.get(i); hostIds[i] = host.getId(); } sc.setParameters("hostIdIN", (Object[]) hostIds); } else { return new ArrayList(); } } } if (storageId != null) { sc.setJoinParameters("volumeSearch", "poolId", storageId); } return _vmDao.search(sc, searchFilter); } @Override public HypervisorType getHypervisorTypeOfUserVM(long vmid) { UserVmVO userVm = _vmDao.findById(vmid); if (userVm == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmid); } return userVm.getHypervisorType(); } @Override public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { // TODO Auto-generated method stub return null; } @Override public UserVm getUserVm(long vmId) { return _vmDao.findById(vmId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) public UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { // access check - only root admin can migrate VM Account caller = UserContext.current().getCaller(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { if (s_logger.isDebugEnabled()) { s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); } throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); } // business logic if (vm.getState() != State.Running) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM is not Running, unable to migrate the vm " + vm); } throw new InvalidParameterValueException("VM is not Running, unable to migrate the vm " + vm); } if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware)) { if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is not XenServer/VMware, cannot migrate this VM."); } throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer only"); } ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); if (svcOffering.getUseLocalStorage()) { if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is using Local Storage, cannot migrate this VM."); } throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate"); } // call to core process DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); HostPodVO pod = _podDao.findById(destinationHost.getPodId()); long srcHostId = vm.getHostId(); Cluster cluster = _clusterDao.findById(destinationHost.getClusterId()); DeployDestination dest = new DeployDestination(dcVO, pod, cluster, destinationHost); //check max guest vm limit for the destinationHost HypervisorType hypervisorType = destinationHost.getHypervisorType(); String hypervisorVersion = destinationHost.getHypervisorVersion(); Long maxGuestLimit = _hypervisorCapabilitiesDao.getMaxGuestsLimit(hypervisorType, hypervisorVersion); Long vmCount = _vmInstanceDao.countRunningByHostId(destinationHost.getId()); if (vmCount.longValue() == maxGuestLimit.longValue()){ if (s_logger.isDebugEnabled()) { s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: "+ destinationHost.getId() +" already has max Running VMs(count includes system VMs), limit is: " + maxGuestLimit + " , cannot migrate to this host"); } throw new VirtualMachineMigrationException("Destination host, hostId: "+ destinationHost.getId() +" already has max Running VMs(count includes system VMs), limit is: " + maxGuestLimit + " , cannot migrate to this host"); } UserVmVO migratedVm = _itMgr.migrate((UserVmVO) vm, srcHostId, dest); return migratedVm; } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false) public UserVm moveVMToUser(MoveUserVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // VERIFICATIONS and VALIDATIONS //VV 1: verify the two users Account oldAccount = UserContext.current().getCaller(); Account newAccount = _accountService.getAccount(cmd.getAccountId()); if (newAccount == null) { throw new InvalidParameterValueException("Unable to find account " + newAccount + " in domain " + oldAccount.getDomainId()); } //VV 2: check if account/domain is with in resource limits to create a new vm _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.user_vm); //get the VM UserVmVO vm = _vmDao.findById(cmd.getVmId()); // VV 3: check if vm is running if (vm.getState() == State.Running) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM is Running, unable to move the vm " + vm); } throw new InvalidParameterValueException("VM is Running, unable to move the vm " + vm); } // VV 4: Check if new owner can use the vm template VirtualMachineTemplate template = _templateDao.findById(vm.getTemplateId()); if (!template.isPublicTemplate()) { Account templateOwner = _accountMgr.getAccount(template.getAccountId()); _accountMgr.checkAccess(newAccount, null, templateOwner); } // VV 5: check that vm owner can create vm in the domain DomainVO domain = _domainDao.findById(oldAccount.getDomainId()); _accountMgr.checkAccess(newAccount, domain); DataCenterVO zone = _dcDao.findById(vm.getDataCenterIdToDeployIn()); VMInstanceVO vmoi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); VirtualMachineProfileImpl vmOldProfile = new VirtualMachineProfileImpl(vmoi); // OWNERSHIP STEP 1: update the vm owner vm.setAccountId(newAccount.getAccountId()); vm.setAccountId(newAccount.getId()); _vmDao.persist(vm); // OS 2: update volume List volumes = _volsDao.findByInstance(cmd.getVmId()); for (VolumeVO volume : volumes) { volume.setAccountId(cmd.getAccountId()); _volsDao.persist(volume); } // OS 3: update the network if (zone.getNetworkType() == NetworkType.Advanced) { //cleanup the network for the oldOwner _networkMgr.cleanupNics(vmOldProfile); _networkMgr.expungeNics(vmOldProfile); // add the new nics List networkList = new ArrayList(); NetworkVO defaultNetwork = null; List oldNetworks = new ArrayList(); List zoneNetworks = _networkDao.listByZone(zone.getId()); for (NetworkVO network : zoneNetworks) { // get the default networks for the account NetworkOfferingVO no = _networkOfferingDao.findById(network.getNetworkOfferingId()); if (!no.isSystemOnly()) { if (network.getIsShared() || !_networkDao.listBy(oldAccount.getId(), network.getId()).isEmpty()) { if (network.isDefault()) { oldNetworks.add(network); } } } } for (NetworkVO oldNet: oldNetworks){ long networkOffering = oldNet.getNetworkOfferingId(); List virtualNetworks = _networkMgr.listNetworksForAccount(newAccount.getId(), zone.getId(), GuestIpType.Virtual, true); if (virtualNetworks.isEmpty()) { Network newNetwork = _networkMgr.createNetwork(networkOffering, newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", false, null, vm.getDataCenterIdToDeployIn(), null, null, null, null, newAccount, false, null, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { throw new InvalidParameterValueException("More than 1 default Virtaul networks are found for account " + newAccount + "; please specify networkIds"); } else { defaultNetwork = virtualNetworks.get(0); } networkList.add(defaultNetwork); List> networks = new ArrayList>(); short defaultNetworkNumber = 0; for (NetworkVO network : networkList) { if (network.isDefault()) { defaultNetworkNumber++; } networks.add(new Pair(network, null)); } VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi); _networkMgr.allocate(vmProfile, networks); } } return vm; } }