// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.vm; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity; import org.apache.cloudstack.engine.service.api.OrchestrationService; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; 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.query.dao.UserVmJoinDao; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; import com.cloud.async.BaseAsyncJobExecutor; import com.cloud.capacity.CapacityManager; 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.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.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.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.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.network.IPAddressVO; import com.cloud.network.LoadBalancerVMMapVO; import com.cloud.network.Network; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkVO; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.PortForwardingRuleVO; import com.cloud.network.rules.RulesManager; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.dao.SecurityGroupDao; import com.cloud.network.security.dao.SecurityGroupVMMapDao; import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.dao.VpcDao; 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.projects.Project.ListProjectResourcesCriteria; import com.cloud.projects.ProjectManager; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; 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.GuestOSCategoryVO; 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.StoragePool; 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.VolumeHostVO; 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.VMTemplateDetailsDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeHostDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.tags.dao.ResourceTagDao; 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.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.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.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; 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.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; @Component @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 ServiceOfferingDao _offeringDao = null; @Inject protected DiskOfferingDao _diskOfferingDao = null; @Inject protected VMTemplateDao _templateDao = null; @Inject protected VMTemplateDetailsDao _templateDetailsDao = null; @Inject protected VMTemplateHostDao _templateHostDao = null; @Inject protected VMTemplateZoneDao _templateZoneDao = null; @Inject protected DomainDao _domainDao = null; @Inject protected UserVmDao _vmDao = null; @Inject protected UserVmJoinDao _vmJoinDao = null; @Inject protected VolumeDao _volsDao = null; @Inject protected DataCenterDao _dcDao = null; @Inject protected FirewallRulesDao _rulesDao = null; @Inject protected LoadBalancerVMMapDao _loadBalancerVMMapDao = null; @Inject protected PortForwardingRulesDao _portForwardingDao; @Inject protected IPAddressDao _ipAddressDao = null; @Inject protected HostPodDao _podDao = 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 HighAvailabilityManager _haMgr = null; @Inject protected AlertManager _alertMgr = null; @Inject protected AccountManager _accountMgr; @Inject protected AccountService _accountService; @Inject protected AsyncJobManager _asyncMgr; @Inject protected ClusterDao _clusterDao; @Inject protected StoragePoolDao _storagePoolDao; @Inject protected SecurityGroupManager _securityGroupMgr; @Inject protected ServiceOfferingDao _serviceOfferingDao; @Inject protected NetworkOfferingDao _networkOfferingDao; @Inject protected InstanceGroupDao _vmGroupDao; @Inject protected InstanceGroupVMMapDao _groupVMMapDao; @Inject protected VirtualMachineManager _itMgr; @Inject protected NetworkDao _networkDao; @Inject protected NicDao _nicDao; @Inject protected VpcDao _vpcDao; @Inject protected RulesManager _rulesMgr; @Inject protected LoadBalancingRulesManager _lbMgr; @Inject protected UsageEventDao _usageEventDao; @Inject protected SSHKeyPairDao _sshKeyPairDao; @Inject protected UserVmDetailsDao _vmDetailsDao; @Inject protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; @Inject protected SecurityGroupDao _securityGroupDao; @Inject protected CapacityManager _capacityMgr;; @Inject protected VMInstanceDao _vmInstanceDao; @Inject protected ResourceLimitService _resourceLimitMgr; @Inject protected FirewallManager _firewallMgr; @Inject protected ProjectManager _projectMgr; @Inject protected ResourceManager _resourceMgr; @Inject protected NetworkServiceMapDao _ntwkSrvcDao; @Inject SecurityGroupVMMapDao _securityGroupVMMapDao; @Inject protected ItWorkDao _workDao; @Inject protected VolumeHostDao _volumeHostDao; @Inject ResourceTagDao _resourceTagDao; @Inject PhysicalNetworkDao _physicalNetworkDao; @Inject VpcManager _vpcMgr; @Inject protected GuestOSCategoryDao _guestOSCategoryDao; 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; @Inject protected OrchestrationService _orchSrvc; @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 caller = UserContext.current().getCaller(); Long vmId = cmd.getId(); UserVmVO userVm = _vmDao.findById(cmd.getId()); _vmDao.loadDetails(userVm); // 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"); } _accountMgr.checkAccess(caller, null, true, userVm); boolean result = resetVMPasswordInternal(cmd, password); if (result) { userVm.setPassword(password); // update the password in vm_details table too // Check if an SSH key pair was selected for the instance and if so // use it to encrypt & save the vm password String sshPublicKey = userVm.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"); } userVm.setDetail("Encrypted.Password", encryptedPasswd); _vmDao.saveDetails(userVm); } } else { throw new CloudRuntimeException( "Failed to reset password for the virtual machine "); } 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, _networkMgr .isSecurityGroupSupportedInNetwork(defaultNetwork), _networkMgr.getNetworkTag(template.getHypervisorType(), defaultNetwork)); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl( vmInstance); vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); UserDataServiceProvider element = _networkMgr .getPasswordResetProvider(defaultNetwork); if (element == null) { throw new CloudRuntimeException( "Can't find network element for " + Service.UserData.getName() + " provider needed for password reset"); } boolean result = element.savePassword(defaultNetwork, defaultNicProfile, vmProfile); // 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 (vmInstance.getState() == State.Stopped) { s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of password reset"); return true; } if (rebootVirtualMachine(userId, vmId) == null) { 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; } } private int getMaxDataVolumesSupported(UserVmVO vm) { Long hostId = vm.getHostId(); if (hostId == null) { hostId = vm.getLastHostId(); } HostVO host = _hostDao.findById(hostId); Integer maxDataVolumesSupported = null; if (host != null) { _hostDao.loadDetails(host); maxDataVolumesSupported = _hypervisorCapabilitiesDao .getMaxDataVolumesLimit(host.getHypervisorType(), host.getDetail("product_version")); } if (maxDataVolumesSupported == null) { maxDataVolumesSupported = 6; // 6 data disks by default if nothing // is specified in // 'hypervisor_capabilities' table } return maxDataVolumesSupported.intValue(); } @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 caller = 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 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 number of data volumes attached to VM is less than // that supported by hypervisor List existingDataVolumes = _volsDao.findByInstanceAndType( vmId, Volume.Type.DATADISK); int maxDataVolumesSupported = getMaxDataVolumesSupported(vm); if (existingDataVolumes.size() >= maxDataVolumesSupported) { throw new InvalidParameterValueException( "The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). 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."); } // If local storage is disabled then attaching a volume with local disk // offering not allowed DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId()); if (!dataCenter.isLocalStorageEnabled()) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume .getDiskOfferingId()); if (diskOffering.getUseLocalStorage()) { throw new InvalidParameterValueException( "Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it"); } } // permission check _accountMgr.checkAccess(caller, null, true, volume, vm); // Check if volume is stored on secondary Storage. boolean isVolumeOnSec = false; VolumeHostVO volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); if (volHostVO != null) { isVolumeOnSec = true; if (!(volHostVO.getDownloadState() == Status.DOWNLOADED)) { throw new InvalidParameterValueException( "Volume is not uploaded yet. Please try this operation once the volume is uploaded"); } } if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp .equals(volume.getState()))) { throw new InvalidParameterValueException( "Volume state must be in Allocated, Ready or in Uploaded state"); } 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."); } else { rootVolumeOfVm = rootVolumesOfVm.get(0); } HypervisorType rootDiskHyperType = vm.getHypervisorType(); HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume .getId()); if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) { throw new InvalidParameterValueException( "Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm"); } // allocate deviceId List vols = _volsDao.findByInstance(vmId); if (deviceId != null) { if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) { 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()); } boolean createVolumeOnBackend = true; if (rootVolumeOfVm.getState() == Volume.State.Allocated) { createVolumeOnBackend = false; if (isVolumeOnSec) { throw new CloudRuntimeException( "Cant attach uploaded volume to the vm which is not created. Please start it and then retry"); } } // create volume on the backend only when vm's root volume is allocated if (createVolumeOnBackend) { if (volume.getState().equals(Volume.State.Allocated) || isVolumeOnSec) { /* Need to create the volume */ VMTemplateVO rootDiskTmplt = _templateDao.findById(vm .getTemplateId()); DataCenterVO dcVO = _dcDao.findById(vm .getDataCenterIdToDeployIn()); HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); StoragePoolVO rootDiskPool = _storagePoolDao .findById(rootVolumeOfVm.getPoolId()); ServiceOfferingVO svo = _serviceOfferingDao.findById(vm .getServiceOfferingId()); DiskOfferingVO diskVO = _diskOfferingDao.findById(volume .getDiskOfferingId()); Long clusterId = (rootDiskPool == null ? null : rootDiskPool .getClusterId()); if (!isVolumeOnSec) { volume = _storageMgr.createVolume(volume, vm, rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); } else { try { // Format of data disk should be the same as root disk if (!volHostVO .getFormat() .getFileExtension() .equals(_storageMgr .getSupportedImageFormatForCluster(rootDiskPool .getClusterId()))) { throw new InvalidParameterValueException( "Failed to attach volume to VM since volumes format " + volHostVO.getFormat() .getFileExtension() + " is not compatible with the vm hypervisor type"); } // Check that there is some shared storage. StoragePoolVO vmRootVolumePool = _storagePoolDao .findById(rootVolumeOfVm.getPoolId()); List sharedVMPools = _storagePoolDao .findPoolsByTags( vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), null, true); if (sharedVMPools.size() == 0) { throw new CloudRuntimeException( "Cannot attach volume since there are no shared storage pools in the VM's cluster to copy the uploaded volume to."); } volume = _storageMgr.copyVolumeFromSecToPrimary(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); } catch (NoTransitionException e) { throw new CloudRuntimeException( "Unable to transition the volume ", e); } } if (volume == null) { throw new CloudRuntimeException( "Failed to create volume when attaching it to VM: " + vm.getHostName()); } } StoragePoolVO vmRootVolumePool = _storagePoolDao .findById(rootVolumeOfVm.getPoolId()); DiskOfferingVO volumeDiskOffering = _diskOfferingDao .findById(volume.getDiskOfferingId()); String[] volumeTags = volumeDiskOffering.getTagsArray(); boolean isVolumeOnSharedPool = !volumeDiskOffering .getUseLocalStorage(); StoragePoolVO sourcePool = _storagePoolDao.findById(volume .getPoolId()); List matchingVMPools = _storagePoolDao .findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, isVolumeOnSharedPool); boolean moveVolumeNeeded = true; if (matchingVMPools.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 sourcePoolId = sourcePool.getId(); Long sourcePoolDcId = sourcePool.getDataCenterId(); Long sourcePoolPodId = sourcePool.getPodId(); Long sourcePoolClusterId = sourcePool.getClusterId(); for (StoragePoolVO vmPool : matchingVMPools) { long vmPoolId = vmPool.getId(); Long vmPoolDcId = vmPool.getDataCenterId(); Long vmPoolPodId = vmPool.getPodId(); Long vmPoolClusterId = vmPool.getClusterId(); // Moving a volume is not required if storage pools belongs // to same cluster in case of shared volume or // identical storage pool in case of local if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId && (isVolumeOnSharedPool || sourcePoolId == vmPoolId)) { moveVolumeNeeded = false; break; } } } if (moveVolumeNeeded) { if (isVolumeOnSharedPool) { // Move the volume to a storage pool in the VM's zone, pod, // or cluster try { volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); } catch (ConcurrentOperationException e) { throw new CloudRuntimeException(e.toString()); } } else { throw new CloudRuntimeException( "Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() + " as migration of local data volume is not allowed"); } } } 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 caller = 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); } // Permissions check _accountMgr.checkAccess(caller, null, true, volume); // 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 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 vmId = cmd.getId(); Long svcOffId = cmd.getServiceOfferingId(); Account caller = UserContext.current().getCaller(); // Verify input parameters UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance == null) { throw new InvalidParameterValueException( "unable to find a virtual machine with id " + vmId); } _accountMgr.checkAccess(caller, null, true, vmInstance); // Check that the specified service offering ID is valid _itMgr.checkIfCanUpgrade(vmInstance, svcOffId); _itMgr.upgradeVmDb(vmId, svcOffId); return _vmDao.findById(vmInstance.getId()); } @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 caller = UserContext.current().getCaller(); // Verify input parameters UserVmVO vm = _vmDao.findById(vmId.longValue()); if (vm == null) { throw new InvalidParameterValueException( "unable to find a virtual machine with id " + vmId); } // check permissions _accountMgr.checkAccess(caller, null, true, vm); 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); if (_expungeInterval < 600) { _expungeInterval = 600; } time = configs.get("expunge.delay"); _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); if (_expungeDelay < 600) { _expungeDelay = 600; } _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 { // expunge the vm 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 and disassociate ip if elasticIP is enabled IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(vmId); try { if (ip != null) { if (_rulesMgr.disableStaticNat(ip.getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM, true)) { 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, Account templateOwner) throws ResourceAllocationException { Long userId = UserContext.current().getCallerUserId(); Account caller = UserContext.current().getCaller(); boolean isAdmin = (isAdmin(caller.getType())); _accountMgr.checkAccess(caller, null, true, templateOwner); 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(caller.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 + ")"); } HypervisorType hyperType; VolumeVO volume = null; VMTemplateVO privateTemplate = 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); } // check permissions _accountMgr.checkAccess(caller, null, true, volume); // 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); } 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); } volume = _volsDao.findById(snapshot.getVolumeId()); VolumeVO snapshotVolume = _volsDao .findByIdIncludingRemoved(snapshot.getVolumeId()); // check permissions _accountMgr.checkAccess(caller, null, true, snapshot); 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"); } /* * // bug #11428. Operation not supported if vmware and snapshots * parent volume = ROOT if(snapshot.getHypervisorType() == * HypervisorType.VMware && snapshotVolume.getVolumeType() == * Type.DATADISK){ throw new UnsupportedServiceException( * "operation not supported, snapshot with id " + snapshotId + * " is created from Data Disk"); } */ hyperType = snapshot.getHypervisorType(); } _resourceLimitMgr.checkResourceLimit(templateOwner, 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, templateOwner.getId(), null, description, passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails()); 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) { if (cmd.getDetails() != null) { _templateDetailsDao.persist(template.getId(), cmd.getDetails()); } _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); } if (template != null) { return template; } else { throw new CloudRuntimeException("Failed to create a 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 = null; Long accountId = null; SnapshotVO snapshot = null; String secondaryStorageURL = null; try { if (snapshotId != null) { // create template from snapshot snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null) { throw new CloudRuntimeException( "Unable to find Snapshot for Id " + snapshotId); } zoneId = snapshot.getDataCenterId(); secondaryStorageHost = _snapshotMgr .getSecondaryStorageHost(snapshot); secondaryStorageURL = _snapshotMgr .getSecondaryStorageURL(snapshot); 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.getSwiftId() != null && snapshot.getSwiftId() != 0) { _snapshotMgr.downloadSnapshotsFromSwift(snapshot); } cmd = new CreatePrivateTemplateFromSnapshotCommand(pool, 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); } secondaryStorageURL = secondaryStorageHost.getStorageUrl(); pool = _storagePoolDao.findById(volume.getPoolId()); cmd = new CreatePrivateTemplateFromVolumeCommand(pool, 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 (snapshot != null && snapshot.getSwiftId() != null && secondaryStorageURL != null && zoneId != null && accountId != null && volumeId != null) { _snapshotMgr.deleteSnapshotsForVolume(secondaryStorageURL, zoneId, accountId, volumeId); } if (privateTemplate == null) { Transaction txn = Transaction.currentTxn(); txn.start(); // Remove the template record _templateDao.expunge(templateId); // decrement resource count if (accountId != null) { _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); } txn.commit(); } } if (privateTemplate != null) { return privateTemplate; } else { throw new CloudRuntimeException("Failed to create a template"); } } @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)) { s_logger.debug("Destroying vm " + vm + " as it failed to create"); 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) throws ResourceUnavailableException, InsufficientCapacityException { String displayName = cmd.getDisplayName(); String group = cmd.getGroup(); Boolean ha = cmd.getHaEnable(); Long id = cmd.getId(); Long osTypeId = cmd.getOsTypeId(); 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"); } _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, 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"); } boolean updateUserdata = false; if (userData != null) { // check and replace newlines userData = userData.replace("\\n", ""); validateUserData(userData); // update userData on domain router. updateUserdata = true; } 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); if (updateUserdata) { boolean result = updateUserDataInternal(_vmDao.findById(id)); if (result) { s_logger.debug("User data successfully updated for vm id="+id); } else { throw new CloudRuntimeException("Failed to reset userdata for the virtual machine "); } } return _vmDao.findById(id); } private boolean updateUserDataInternal(UserVm vm) throws ResourceUnavailableException, InsufficientCapacityException { VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); Nic defaultNic = _networkMgr.getDefaultNic(vm.getId()); if (defaultNic == null) { s_logger.error("Unable to update userdata for vm id=" + vm.getId() + " 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, _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork), _networkMgr.getNetworkTag(template.getHypervisorType(), defaultNetwork)); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl((VMInstanceVO)vm); UserDataServiceProvider element = _networkMgr.getUserDataUpdateProvider(defaultNetwork); if (element == null) { throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); } boolean result = element.saveUserData(defaultNetwork, defaultNicProfile, vmProfile); return true; } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { return startVirtualMachine(cmd.getId(), cmd.getHostId(), null).first(); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true) public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException { Account caller = UserContext.current().getCaller(); 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); } _accountMgr.checkAccess(caller, null, true, vmInstance); return rebootVirtualMachine(UserContext.current().getCallerUserId(), 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 caller = UserContext.current().getCaller(); Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); String groupName = cmd.getGroupName(); Long projectId = cmd.getProjectId(); Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); long accountId = owner.getId(); // 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 protected 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 caller = 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); } _accountMgr.checkAccess(caller, null, true, 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, true, owner); // Get default guest network in Basic zone Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone .getId()); 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 && _networkMgr .isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkMgr.canAddDefaultSecurityGroup()) { // add the default securityGroup only if no security group is // specified if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList(); } SecurityGroup defaultGroup = _securityGroupMgr .getDefaultSecurityGroup(owner.getId()); if (defaultGroup != null) { 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, true, 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); isSecurityGroupEnabledNetworkUsed = true; } 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 (!_networkMgr.isSecurityGroupSupportedInNetwork(network)) { throw new InvalidParameterValueException( "Network is not security group enabled: " + network.getId()); } networkList.add(network); isSecurityGroupEnabledNetworkUsed = true; } else { // Verify that all the networks are Shared/Guest; can't create combination of SG enabled and disabled 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()); } boolean isSecurityGroupEnabled = _networkMgr.isSecurityGroupSupportedInNetwork(network); if (isSecurityGroupEnabled) { if (networkIdList.size() > 1) { throw new InvalidParameterValueException("Can't create a vm with multiple networks one of" + " which is Security Group enabled"); } isSecurityGroupEnabledNetworkUsed = true; } if (!(network.getTrafficType() == TrafficType.Guest && network.getGuestType() == Network.GuestType.Shared)) { throw new InvalidParameterValueException("Can specify only Shared Guest networks when" + " deploy vm in Advance Security Group enabled zone"); } // Perform account permission check if (network.getAclType() == ACLType.Account) { _accountMgr.checkAccess(caller, AccessType.UseNetwork, false, network); } networkList.add(network); } } // if network is security group enabled, and no security group is // specified, then add the default security group automatically if (isSecurityGroupEnabledNetworkUsed && !isVmWare && _networkMgr.canAddDefaultSecurityGroup()) { // add the default securityGroup only if no security group is // specified if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList(); } SecurityGroup defaultGroup = _securityGroupMgr .getDefaultSecurityGroup(owner.getId()); if (defaultGroup != null) { 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, true, owner); List vpcSupportedHTypes = _vpcMgr .getSupportedVpcHypervisors(); if (networkIdList == null || networkIdList.isEmpty()) { NetworkVO defaultNetwork = null; // if no network is passed in // 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 List requiredOfferings = _networkOfferingDao .listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { throw new InvalidParameterValueException( "Unable to find network offering with availability=" + Availability.Required + " to automatically create the network as a part of vm creation"); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = _networkMgr.listNetworksForAccount(owner.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks.isEmpty()) { long physicalNetworkId = _networkMgr.findPhysicalNetworkId( zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao .findById(physicalNetworkId); if (physicalNetwork == null) { throw new InvalidParameterValueException( "Unable to find physical network with id: " + physicalNetworkId + " and tag: " + requiredOfferings.get(0).getTags()); } s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = _networkMgr.createGuestNetwork( requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { throw new InvalidParameterValueException( "More than 1 default Isolated networks are found for account " + owner + "; please specify networkIds"); } else { defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } } else { throw new InvalidParameterValueException( "Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); } networkList.add(defaultNetwork); } else { 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.getVpcId() != null) { // Only ISOs, XenServer, KVM, and VmWare template types are // supported for vpc networks if (template.getFormat() != ImageFormat.ISO && !vpcSupportedHTypes.contains(template .getHypervisorType())) { throw new InvalidParameterValueException( "Can't create vm from template with hypervisor " + template.getHypervisorType() + " in vpc network " + network); } // Only XenServer, KVM, and VMware hypervisors are supported // for vpc networks if (!vpcSupportedHTypes.contains(hypervisor)) { throw new InvalidParameterValueException( "Can't create vm of hypervisor type " + hypervisor + " in vpc network"); } } _networkMgr.checkNetworkPermissions(owner, network); // don't allow to use system networks NetworkOffering networkOffering = _configMgr .getNetworkOffering(network.getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { throw new InvalidParameterValueException( "Network id=" + networkId + " is system only and can't be used for vm deployment"); } networkList.add(network); } } 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, true, owner); if (owner.getState() == Account.State.disabled) { throw new PermissionDeniedException( "The owner of vm to deploy is disabled: " + 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 _configMgr.checkZoneAccess(caller, zone); // check that vm owner can create vm in the domain _configMgr.checkZoneAccess(owner, zone); } // check if account/domain is with in resource limits to create a new vm boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2)); // verify security group ids if (securityGroupIdList != null) { for (Long securityGroupId : securityGroupIdList) { SecurityGroup sg = _securityGroupDao.findById(securityGroupId); if (sg == null) { throw new InvalidParameterValueException( "Unable to find security group by id " + securityGroupId); } else { // verify permissions _accountMgr.checkAccess(caller, null, true, owner, sg); } } } // 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"); } 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, true, templateOwner); } // check if the user data is correct 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(); } List> networks = new ArrayList>(); List networkUuidList = new ArrayList(); short defaultNetworkNumber = 0; boolean securityGroupEnabled = false; boolean vpcNetwork = false; for (NetworkVO network : networkList) { if (network.getDataCenterId() != zone.getId()) { throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId()); } String requestedIp = null; if (requestedIps != null && !requestedIps.isEmpty()) { requestedIp = requestedIps.get(network.getId()); } NicProfile profile = new NicProfile(requestedIp); if (defaultNetworkNumber == 0) { defaultNetworkNumber++; // if user requested specific ip for default network, add it if (defaultNetworkIp != null) { profile = new NicProfile(defaultNetworkIp); } profile.setDefaultNic(true); } networks.add(new Pair(network, profile)); if (_networkMgr.isSecurityGroupSupportedInNetwork(network)) { securityGroupEnabled = true; } // vm can't be a part of more than 1 VPC network if (network.getVpcId() != null) { if (vpcNetwork) { throw new InvalidParameterValueException( "Vm can't be a part of more than 1 VPC network"); } vpcNetwork = true; } networkUuidList.add(network.getUuid()); } if (securityGroupIdList != null && !securityGroupIdList.isEmpty() && !securityGroupEnabled) { throw new InvalidParameterValueException( "Unable to deploy vm with security groups as SecurityGroup service is not enabled for the vm's network"); } // 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); String uuidName = UUID.randomUUID().toString(); // verify hostname information if (hostName == null) { hostName = uuidName; } else { // 1) check is hostName is RFC complient 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"); } // 2) hostName has to be unique in the network domain Map> ntwkDomains = new HashMap>(); for (NetworkVO network : networkList) { String ntwkDomain = network.getNetworkDomain(); if (!ntwkDomains.containsKey(ntwkDomain)) { List ntwkIds = new ArrayList(); ntwkIds.add(network.getId()); ntwkDomains.put(ntwkDomain, ntwkIds); } else { List ntwkIds = ntwkDomains.get(ntwkDomain); ntwkIds.add(network.getId()); ntwkDomains.put(ntwkDomain, ntwkIds); } } for (String ntwkDomain : ntwkDomains.keySet()) { for (Long ntwkId : ntwkDomains.get(ntwkDomain)) { // * get all vms hostNames in the network List hostNames = _vmInstanceDao .listDistinctHostNames(ntwkId); // * verify that there are no duplicates if (hostNames.contains(hostName)) { throw new InvalidParameterValueException( "The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain + "; network=" + _networkMgr.getNetwork(ntwkId)); } } } } 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); vm.setUuid(uuidName); 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"); DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); _vmDao.persist(vm); _vmDao.saveDetails(vm); long guestOSId = template.getGuestOSId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); long guestOSCategoryId = guestOS.getCategoryId(); GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); List computeTags = new ArrayList(); computeTags.add(offering.getHostTag()); List rootDiskTags = new ArrayList(); rootDiskTags.add(offering.getTags()); if(isIso){ VirtualMachineEntity vmEntity = _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), owner.getAccountName(), vm.getIsoId().toString(), hostName, displayName, hypervisor.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkUuidList, plan); }else { VirtualMachineEntity vmEntity = _orchSrvc.createVirtualMachine(vm.getUuid(), owner.getAccountName(), new Long(template.getId()).toString(), hostName, displayName, hypervisor.name(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkUuidList, plan); } 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 (!Base64.isBase64(userData)) { throw new InvalidParameterValueException( "User data is not base64 encoded"); } if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { throw new InvalidParameterValueException( "User data is too long"); } decodedUserData = 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 { long vmId = cmd.getEntityId(); Long hostId = cmd.getHostId(); UserVmVO vm = _vmDao.findById(vmId); Pair> vmParamPair = null; try { vmParamPair = startVirtualMachine(vmId, hostId, additonalParams); vm = vmParamPair.first(); ; } finally { updateVmStateForFailedVmCreation(vm.getId()); } // Check that the password was passed in and is valid VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm .getTemplateId()); if (template.getEnablePassword()) { // this value is not being sent to the backend; need only for api // display purposes vm.setPassword((String) vmParamPair.second().get( VirtualMachineProfile.Param.VmPassword)); } 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); 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 (template.getTemplateType() == TemplateType.PERHOST) { isoPath = template.getName(); } else { 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()); } } 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; NetworkVO guestNetwork = 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; guestNetwork = network; } } 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(); // dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName()) if (_ntwkSrvcDao.canProviderSupportServiceInNetwork( guestNetwork.getId(), Service.Dhcp, Provider.ExternalDhcpServer)) { _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); } } // get system ip and create static nat rule for the vm try { _rulesMgr.getSystemIpAndEnableStaticNatForVm( profile.getVirtualMachine(), false); } catch (Exception ex) { s_logger.warn( "Failed to get system ip and enable static nat for the vm " + profile.getVirtualMachine() + " due to exception ", ex); return false; } 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); } 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) { // release elastic IP here IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(profile.getId()); if (ip != null && ip.getSystem()) { UserContext ctx = UserContext.current(); try { long networkId = ip.getAssociatedWithNetworkId(); Network guestNetwork = _networkMgr.getNetwork(networkId); NetworkOffering offering = _configMgr.getNetworkOffering(guestNetwork.getNetworkOfferingId()); assert (offering.getAssociatePublicIP() == true) : "User VM should not have system owned public IP associated with it when offering configured not to associate public IP."; _rulesMgr.disableStaticNat(ip.getId(), ctx.getCaller(), ctx.getCallerUserId(), true); } catch (Exception ex) { s_logger.warn( "Failed to disable static nat and release system ip " + ip + " as a part of vm " + profile.getVirtualMachine() + " stop due to exception ", ex); } } } public String generateRandomPassword() { return PasswordGenerator.generateRandomPassword(6); } @Override public Pair> startVirtualMachine( long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // Input validation Account callerAccount = UserContext.current().getCaller(); UserVO callerUser = _userDao.findById(UserContext.current() .getCallerUserId()); // if account is removed, return error if (callerAccount != null && callerAccount.getRemoved() != null) { throw new InvalidParameterValueException("The account " + callerAccount.getId() + " is removed"); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException( "unable to find a virtual machine with id " + vmId); } _accountMgr.checkAccess(callerAccount, null, true, vm); Account owner = _accountDao.findById(vm.getAccountId()); if (owner == null) { throw new InvalidParameterValueException("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()); } Host destinationHost = null; if (hostId != 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(hostId); if (destinationHost == null) { throw new InvalidParameterValueException( "Unable to find the host to deploy the VM, host id=" + hostId); } } // check if vm is security group enabled if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkMgr.canAddDefaultSecurityGroup()) { // 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); } } DataCenterDeployment plan = null; if (destinationHost != null) { s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); } // Set parameters Map params = null; VMTemplateVO template = null; if (vm.isUpdateParameters()) { _vmDao.loadDetails(vm); // Check that the password was passed in and is valid 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); } params = new HashMap(); if (additionalParams != null) { params.putAll(additionalParams); } params.put(VirtualMachineProfile.Param.VmPassword, password); } vm = _itMgr.start(vm, params, callerUser, callerAccount, plan); Pair> vmParamPair = new Pair( vm, params); if (vm != null && vm.isUpdateParameters()) { // this value is not being sent to the backend; need only for api // display purposes if (template.getEnablePassword()) { vm.setPassword((String) vmParamPair.second().get( VirtualMachineProfile.Param.VmPassword)); vm.setUpdateParameters(false); _vmDao.update(vm.getId(), vm); } } return vmParamPair; } @Override public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { Account caller = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); // Verify input parameters UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to find a virtual machine with specified vmId"); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { s_logger.trace("Vm id=" + vmId + " is already destroyed"); return vm; } _accountMgr.checkAccess(caller, null, true, vm); User userCaller = _userDao.findById(userId); boolean status; State vmState = vm.getState(); try { status = _itMgr.destroy(vm, userCaller, caller); } catch (OperationTimedoutException e) { CloudRuntimeException ex = new CloudRuntimeException( "Unable to destroy with specified vmId", e); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } 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 { CloudRuntimeException ex = new CloudRuntimeException( "Failed to destroy vm with specified vmId"); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } } @Override public Pair, Integer> searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, List permittedAccounts, boolean listAll, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags) { Filter searchFilter = new Filter(UserVmJoinVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit()); //first search distinct vm id by using query criteria and pagination SearchBuilder sb = _vmJoinDao.createSearchBuilder(); sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct ids _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); 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 networkId = c.getCriteria(Criteria.NETWORKID); Object hypervisor = c.getCriteria(Criteria.HYPERVISOR); Object storageId = c.getCriteria(Criteria.STORAGE_ID); Object templateId = c.getCriteria(Criteria.TEMPLATE_ID); Object isoId = c.getCriteria(Criteria.ISO_ID); Object vpcId = c.getCriteria(Criteria.VPC_ID); sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); 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().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ); sb.and("hostName", sb.entity().getHostName(), SearchCriteria.Op.LIKE); sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); sb.and("isoId", sb.entity().getIsoId(), SearchCriteria.Op.EQ); sb.and("instanceGroupId", sb.entity().getInstanceGroupId(), SearchCriteria.Op.EQ); if (groupId != null && (Long) groupId != -1) { sb.and("instanceGroupId", sb.entity().getInstanceGroupId(), SearchCriteria.Op.EQ); } if (tags != null && !tags.isEmpty()) { for (int count=0; count < tags.size(); count++) { sb.or().op("key" + String.valueOf(count), sb.entity().getTagKey(), SearchCriteria.Op.EQ); sb.and("value" + String.valueOf(count), sb.entity().getTagValue(), SearchCriteria.Op.EQ); sb.cp(); } } if (networkId != null) { sb.and("networkId", sb.entity().getNetworkId(), SearchCriteria.Op.EQ); } if(vpcId != null && networkId == null){ sb.and("vpcId", sb.entity().getVpcId(), SearchCriteria.Op.EQ); } if (storageId != null) { sb.and("poolId", sb.entity().getPoolId(), SearchCriteria.Op.EQ); } // populate the search criteria with the values passed in SearchCriteria sc = sb.create(); // building ACL condition _accountMgr.buildACLViewSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (tags != null && !tags.isEmpty()) { int count = 0; for (String key : tags.keySet()) { sc.setParameters("key" + String.valueOf(count), key); sc.setParameters("value" + String.valueOf(count), tags.get(key)); count++; } } if (groupId != null && (Long)groupId != -1) { sc.setParameters("instanceGroupId", groupId); } if (keyword != null) { SearchCriteria ssc = _vmJoinDao.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 (templateId != null) { sc.setParameters("templateId", templateId); } if (isoId != null) { sc.setParameters("isoId", isoId); } if (networkId != null) { sc.setParameters("networkId", networkId); } if(vpcId != null && networkId == null){ sc.setParameters("vpcId", vpcId); } 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 (pod != null) { sc.setParameters("podId", pod); if (state == null) { sc.setParameters("stateNEQ", "Destroyed"); } } if (hostId != null) { sc.setParameters("hostIdEQ", hostId); } else { if (hostName != null) { sc.setParameters("hostName", hostName); } } if (storageId != null) { sc.setParameters("poolId", storageId); } // search vm details by ids Pair, Integer> uniqueVmPair = _vmJoinDao.searchAndCount(sc, searchFilter); Integer count = uniqueVmPair.second(); if ( count.intValue() == 0 ){ // handle empty result cases return uniqueVmPair; } List uniqueVms = uniqueVmPair.first(); Long[] vmIds = new Long[uniqueVms.size()]; int i = 0; for (UserVmJoinVO v : uniqueVms ){ vmIds[i++] = v.getId(); } List vms = _vmJoinDao.searchByIds(vmIds); return new Pair, Integer>(vms, count); } @Override public HypervisorType getHypervisorTypeOfUserVM(long vmId) { UserVmVO userVm = _vmDao.findById(vmId); if (userVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "unable to find a virtual machine with specified id"); ex.addProxyObject(userVm, vmId, "vmId"); throw ex; } 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 public VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool) { // 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!"); } VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException( "Unable to find the VM by id=" + vmId); } if (vm.getState() != State.Stopped) { InvalidParameterValueException ex = new InvalidParameterValueException( "VM is not Stopped, unable to migrate the vm having the specified id"); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } if (vm.getType() != VirtualMachine.Type.User) { throw new InvalidParameterValueException( "can only do storage migration on user vm"); } List vols = _volsDao.findByInstance(vm.getId()); if (vols.size() > 1) { throw new InvalidParameterValueException( "Data disks attached to the vm, can not migrate. Need to dettach data disks at first"); } HypervisorType destHypervisorType = _clusterDao.findById( destPool.getClusterId()).getHypervisorType(); if (vm.getHypervisorType() != destHypervisorType) { throw new InvalidParameterValueException( "hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + vm.getHypervisorType().toString()); } VMInstanceVO migratedVm = _itMgr.storageMigration(vm, destPool); return migratedVm; } private boolean isVMUsingLocalStorage(VMInstanceVO vm) { boolean usesLocalStorage = false; ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm .getServiceOfferingId()); if (svcOffering.getUseLocalStorage()) { usesLocalStorage = true; } else { List volumes = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK); for (VolumeVO vol : volumes) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol .getDiskOfferingId()); if (diskOffering.getUseLocalStorage()) { usesLocalStorage = true; break; } } } return usesLocalStorage; } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) public VirtualMachine migrateVirtualMachine(Long vmId, 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!"); } VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException( "Unable to find the VM by id=" + vmId); } // business logic if (vm.getState() != State.Running) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM is not Running, unable to migrate the vm " + vm); } InvalidParameterValueException ex = new InvalidParameterValueException( "VM is not Running, unable to migrate the vm with specified id"); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm)) { if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm, cannot migrate this VM."); } throw new InvalidParameterValueException( "Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM only"); } if (isVMUsingLocalStorage(vm)) { 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"); } // check if migrating to same host long srcHostId = vm.getHostId(); if (destinationHost.getId() == srcHostId) { throw new InvalidParameterValueException( "Cannot migrate VM, VM is already presnt on this host, please specify valid destination host to migrate the VM"); } // check if host is UP if (destinationHost.getStatus() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { throw new InvalidParameterValueException( "Cannot migrate VM, destination host is not in correct state, has status: " + destinationHost.getStatus() + ", state: " + destinationHost.getResourceState()); } // call to core process DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); HostPodVO pod = _podDao.findById(destinationHost.getPodId()); Cluster cluster = _clusterDao.findById(destinationHost.getClusterId()); DeployDestination dest = new DeployDestination(dcVO, pod, cluster, destinationHost); // check max guest vm limit for the destinationHost HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); } throw new VirtualMachineMigrationException( "Destination host, hostId: " + destinationHost.getId() + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); } VMInstanceVO migratedVm = _itMgr.migrate(vm, srcHostId, dest); return migratedVm; } @DB @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false) public UserVm moveVMToUser(AssignVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // VERIFICATIONS and VALIDATIONS // VV 1: verify the two users Account caller = UserContext.current().getCaller(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN && caller.getType() != Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { // only // root // admin // can // assign // VMs throw new InvalidParameterValueException( "Only domain admins are allowed to assign VMs and not " + caller.getType()); } // get and check the valid VM UserVmVO vm = _vmDao.findById(cmd.getVmId()); if (vm == null) { throw new InvalidParameterValueException( "There is no vm by that id " + cmd.getVmId()); } else if (vm.getState() == State.Running) { // VV 3: check if vm is // running if (s_logger.isDebugEnabled()) { s_logger.debug("VM is Running, unable to move the vm " + vm); } InvalidParameterValueException ex = new InvalidParameterValueException( "VM is Running, unable to move the vm with specified vmId"); ex.addProxyObject(vm, cmd.getVmId(), "vmId"); throw ex; } Account oldAccount = _accountService.getActiveAccountById(vm .getAccountId()); if (oldAccount == null) { throw new InvalidParameterValueException("Invalid account for VM " + vm.getAccountId() + " in domain."); } // don't allow to move the vm from the project if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { InvalidParameterValueException ex = new InvalidParameterValueException( "Specified Vm id belongs to the project and can't be moved"); ex.addProxyObject(vm, cmd.getVmId(), "vmId"); throw ex; } Account newAccount = _accountService.getActiveAccountByName( cmd.getAccountName(), cmd.getDomainId()); if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { throw new InvalidParameterValueException("Invalid accountid=" + cmd.getAccountName() + " in domain " + cmd.getDomainId()); } if (newAccount.getState() == Account.State.disabled) { throw new InvalidParameterValueException("The new account owner " + cmd.getAccountName() + " is disabled."); } // make sure the accounts are under same domain if (oldAccount.getDomainId() != newAccount.getDomainId()) { throw new InvalidParameterValueException( "The account should be under same domain for moving VM between two accounts. Old owner domain =" + oldAccount.getDomainId() + " New owner domain=" + newAccount.getDomainId()); } // make sure the accounts are not same if (oldAccount.getAccountId() == newAccount.getAccountId()) { throw new InvalidParameterValueException( "The account should be same domain for moving VM between two accounts. Account id =" + oldAccount.getAccountId()); } // don't allow to move the vm if there are existing PF/LB/Static Nat // rules, or vm is assigned to static Nat ip List pfrules = _portForwardingDao.listByVm(cmd .getVmId()); if (pfrules != null && pfrules.size() > 0) { throw new InvalidParameterValueException( "Remove the Port forwarding rules for this VM before assigning to another user."); } List snrules = _rulesDao .listStaticNatByVmId(vm.getId()); if (snrules != null && snrules.size() > 0) { throw new InvalidParameterValueException( "Remove the StaticNat rules for this VM before assigning to another user."); } List maps = _loadBalancerVMMapDao .listByInstanceId(vm.getId()); if (maps != null && maps.size() > 0) { throw new InvalidParameterValueException( "Remove the load balancing rules for this VM before assigning to another user."); } // check for one on one nat IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(cmd.getVmId()); if (ip != null) { if (ip.isOneToOneNat()) { throw new InvalidParameterValueException( "Remove the one to one nat rule for this VM for ip " + ip.toString()); } } DataCenterVO zone = _dcDao.findById(vm.getDataCenterIdToDeployIn()); // Remove vm from instance group removeInstanceFromInstanceGroup(cmd.getVmId()); // VV 2: check if account/domain is with in resource limits to create a // new vm _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.user_vm); // VV 3: check if volumes are with in resource limits _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, _volsDao.findByInstance(cmd.getVmId()).size()); // 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, true, templateOwner); } // VV 5: check the new account can create vm in the domain DomainVO domain = _domainDao.findById(cmd.getDomainId()); _accountMgr.checkAccess(newAccount, domain); Transaction txn = Transaction.currentTxn(); txn.start(); // generate destroy vm event for usage _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_DESTROY, vm .getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm .getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString())); // update resource counts _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.user_vm); // OWNERSHIP STEP 1: update the vm owner vm.setAccountId(newAccount.getAccountId()); vm.setDomainId(cmd.getDomainId()); _vmDao.persist(vm); // OS 2: update volume List volumes = _volsDao.findByInstance(cmd.getVmId()); for (VolumeVO volume : volumes) { _usageEventDao .persist(new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName())); _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.volume); volume.setAccountId(newAccount.getAccountId()); _volsDao.persist(volume); _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.volume); _usageEventDao.persist(new UsageEventVO( EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), volume.getTemplateId(), volume .getSize())); // snapshots: mark these removed in db List snapshots = _snapshotDao .listByVolumeIdIncludingRemoved(volume.getId()); for (SnapshotVO snapshot : snapshots) { _snapshotDao.remove(snapshot.getId()); } } _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.user_vm); // generate usage events to account for this change _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_CREATE, vm .getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm .getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString())); txn.commit(); VMInstanceVO vmoi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); VirtualMachineProfileImpl vmOldProfile = new VirtualMachineProfileImpl( vmoi); // OS 3: update the network List networkIdList = cmd.getNetworkIds(); List securityGroupIdList = cmd.getSecurityGroupIdList(); if (zone.getNetworkType() == NetworkType.Basic) { if (networkIdList != null && !networkIdList.isEmpty()) { throw new InvalidParameterValueException( "Can't move vm with network Ids; this is a basic zone VM"); } // cleanup the old security groups _securityGroupMgr.removeInstanceFromGroups(cmd.getVmId()); // cleanup the network for the oldOwner _networkMgr.cleanupNics(vmOldProfile); _networkMgr.expungeNics(vmOldProfile); // security groups will be recreated for the new account, when the // VM is started List networkList = new ArrayList(); // Get default guest network in Basic zone Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone .getId()); 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); if (securityGroupIdList != null && isVmWare) { throw new InvalidParameterValueException( "Security group feature is not supported for vmWare hypervisor"); } else if (!isVmWare && _networkMgr .isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkMgr.canAddDefaultSecurityGroup()) { if (securityGroupIdList == null) { securityGroupIdList = new ArrayList(); } SecurityGroup defaultGroup = _securityGroupMgr .getDefaultSecurityGroup(newAccount.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 " + newAccount + " so creating a new one"); } defaultGroup = _securityGroupMgr.createSecurityGroup( SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, newAccount.getDomainId(), newAccount.getId(), newAccount.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } } List> networks = new ArrayList>(); NicProfile profile = new NicProfile(); profile.setDefaultNic(true); networks.add(new Pair(networkList.get(0), profile)); VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl( vmi); _networkMgr.allocate(vmProfile, networks); _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); s_logger.debug("AssignVM: Basic zone, adding security groups no " + securityGroupIdList.size() + " to " + vm.getInstanceName()); } else { if (zone.isSecurityGroupEnabled()) { throw new InvalidParameterValueException( "Not yet implemented for SecurityGroupEnabled advanced networks."); } else { if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { throw new InvalidParameterValueException( "Can't move vm with security groups; security group feature is not enabled in this zone"); } // cleanup the network for the oldOwner _networkMgr.cleanupNics(vmOldProfile); _networkMgr.expungeNics(vmOldProfile); Set applicableNetworks = new HashSet(); if (networkIdList != null && !networkIdList.isEmpty()) { // add any additional networks for (Long networkId : networkIdList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to find specified network id"); ex.addProxyObject(network, networkId, "networkId"); throw ex; } _networkMgr .checkNetworkPermissions(newAccount, network); // don't allow to use system networks NetworkOffering networkOffering = _configMgr .getNetworkOffering(network .getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Specified Network id is system only and can't be used for vm deployment"); ex.addProxyObject(network, networkId, "networkId"); throw ex; } applicableNetworks.add(network); } } else { NetworkVO defaultNetwork = null; List requiredOfferings = _networkOfferingDao .listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { throw new InvalidParameterValueException( "Unable to find network offering with availability=" + Availability.Required + " to automatically create the network as a part of vm creation"); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = _networkMgr.listNetworksForAccount(newAccount.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks.isEmpty()) { long physicalNetworkId = _networkMgr .findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0) .getTrafficType()); // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao .findById(physicalNetworkId); if (physicalNetwork == null) { throw new InvalidParameterValueException("Unable to find physical network with id: "+physicalNetworkId + " and tag: " +requiredOfferings.get(0).getTags()); } s_logger.debug("Creating network for account " + newAccount + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", null, null, null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { throw new InvalidParameterValueException( "More than 1 default Isolated networks are found " + "for account " + newAccount + "; please specify networkIds"); } else { defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } } else { throw new InvalidParameterValueException( "Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); } applicableNetworks.add(defaultNetwork); } // add the new nics List> networks = new ArrayList>(); int toggle = 0; for (NetworkVO appNet : applicableNetworks) { NicProfile defaultNic = new NicProfile(); if (toggle == 0) { defaultNic.setDefaultNic(true); toggle++; } networks.add(new Pair(appNet, defaultNic)); } VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl( vmi); _networkMgr.allocate(vmProfile, networks); s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName()); } // END IF NON SEC GRP ENABLED } // END IF ADVANCED s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + cmd.getAccountName()); return vm; } @Override public UserVm restoreVM(RestoreVMCmd cmd) { // Input validation Account caller = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); UserVO user = _userDao.findById(userId); boolean needRestart = false; long vmId = cmd.getVmId(); UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Cann not find VM with ID " + vmId); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } Account owner = _accountDao.findById(vm.getAccountId()); if (owner == null) { throw new InvalidParameterValueException("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.getState() != VirtualMachine.State.Running && vm.getState() != VirtualMachine.State.Stopped) { throw new CloudRuntimeException( "Vm " + vmId + " currently in " + vm.getState() + " state, restore vm can only execute when VM in Running or Stopped"); } if (vm.getState() == VirtualMachine.State.Running) { needRestart = true; } List rootVols = _volsDao.findByInstance(vmId); if (rootVols.isEmpty()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Can not find root volume for VM " + vmId); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } VolumeVO root = rootVols.get(0); long templateId = root.getTemplateId(); VMTemplateVO template = _templateDao.findById(templateId); if (template == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Cannot find template for specified volumeid and vmId"); ex.addProxyObject(vm, vmId, "vmId"); ex.addProxyObject(root, root.getId(), "volumeId"); throw ex; } if (needRestart) { try { _itMgr.stop(vm, user, caller); } catch (ResourceUnavailableException e) { s_logger.debug("Stop vm " + vmId + " failed", e); CloudRuntimeException ex = new CloudRuntimeException( "Stop vm failed for specified vmId"); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } } /* allocate a new volume from original template */ VolumeVO newVol = _storageMgr.allocateDuplicateVolume(root, null); _volsDao.attachVolume(newVol.getId(), vmId, newVol.getDeviceId()); /* Detach and destory the old root volume */ try { _volsDao.detachVolume(root.getId()); _storageMgr.destroyVolume(root); } catch (ConcurrentOperationException e) { s_logger.debug("Unable to delete old root volume " + root.getId() + ", user may manually delete it", e); } if (needRestart) { try { _itMgr.start(vm, null, user, caller); } catch (Exception e) { s_logger.debug("Unable to start VM " + vmId, e); CloudRuntimeException ex = new CloudRuntimeException( "Unable to start VM with specified id" + e.getMessage()); ex.addProxyObject(vm, vmId, "vmId"); throw ex; } } s_logger.debug("Restore VM " + vmId + " with template " + root.getTemplateId() + " successfully"); return vm; } @Override public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // not supported throw new UnsupportedOperationException( "Plug nic is not supported for vm of type " + vm.getType()); } @Override public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { // not supported throw new UnsupportedOperationException( "Unplug nic is not supported for vm of type " + vm.getType()); } @Override public void prepareStop(VirtualMachineProfile profile) { } }