You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ah...@apache.org on 2013/01/09 14:26:48 UTC
[9/50] [abbrv] Merge branch 'api_refactoring' into javelin
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/30f2565d/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --cc server/src/com/cloud/vm/UserVmManagerImpl.java
index e0454c3,f3aca68..0317643
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@@ -29,31 -29,21 +29,23 @@@ import java.util.concurrent.ScheduledEx
import java.util.concurrent.TimeUnit;
import javax.ejb.Local;
+import javax.inject.Inject;
import javax.naming.ConfigurationException;
- import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
- import org.apache.cloudstack.engine.service.api.OrchestrationService;
+ import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd;
+ import org.apache.cloudstack.api.command.user.vm.*;
+ 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.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
- import com.cloud.acl.ControlledEntity.ACLType;
+ import org.apache.cloudstack.acl.ControlledEntity.ACLType;
+ import org.apache.cloudstack.acl.SecurityChecker.AccessType;
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.*;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
@@@ -215,8 -146,8 +148,7 @@@ import com.cloud.uservm.UserVm
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.PasswordGenerator;
- import com.cloud.utils.Ternary;
import com.cloud.utils.component.ComponentLocator;
-import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.crypt.RSAHelper;
@@@ -232,4447 -163,3552 +164,4317 @@@ import com.cloud.utils.exception.Execut
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;
+ import com.cloud.vm.dao.*;
+@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
+ 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;
-
- protected ScheduledExecutorService _executor = null;
- protected int _expungeInterval;
- protected int _expungeDelay;
-
- protected String _name;
- protected String _instance;
- protected String _zone;
+ 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<? extends UserVm> 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<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>(
+ 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<VolumeVO> 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<VolumeVO> 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<VolumeVO> 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<String> devIds = new ArrayList<String>();
+ 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<StoragePoolVO>(), 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<StoragePoolVO> 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<StoragePoolVO>(),
+ 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<StoragePoolVO> 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<String, String> 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<Long, VmStatsEntry> getVirtualMachineStatistics(long hostId,
+ String hostName, List<Long> vmIds) throws CloudRuntimeException {
+ HashMap<Long, VmStatsEntry> vmStatsById = new HashMap<Long, VmStatsEntry>();
+
+ if (vmIds.isEmpty()) {
+ return vmStatsById;
+ }
+
+ List<String> vmNames = new ArrayList<String>();
+
+ 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<String, VmStatsEntry> 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<VolumeVO> 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<String, Object> 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<String, String> 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<StoragePoolVO> 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.getUuid(), secondaryStorageURL, dcId, accountId,
- snapshot.getVolumeId(), backupSnapshotUUID,
- snapshot.getName(), origTemplateInstallPath,
- templateId, name,
- _createprivatetemplatefromsnapshotwait);
++ 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.getUuid(), secondaryStorageURL, templateId,
- accountId, command.getTemplateName(), uniqueName,
- volume.getPath(), vmName,
- _createprivatetemplatefromvolumewait);
++ cmd = new CreatePrivateTemplateFromVolumeCommand(pool, secondaryStorageURL, templateId, accountId, command.getTemplateName(), uniqueName, volume.getPath(), vmName, _createprivatetemplatefromvolumewait);
- private ConfigurationDao _configDao;
- private int _createprivatetemplatefromvolumewait;
- private int _createprivatetemplatefromsnapshotwait;
- @Override
- public UserVmVO getVirtualMachine(long vmId) {
- return _vmDao.findById(vmId);
- }
+ } 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<VolumeVO> 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<UserVmVO> 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) {
++ 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");
+ }
- @Override
- public List<? extends UserVm> getVirtualMachines(long hostId) {
- return _vmDao.listByHostId(hostId);
- }
++ 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);
- @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());
++ 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 ");
++ }
+ }
+
- 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");
- }
+ return _vmDao.findById(id);
+ }
- 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");
++ 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;
+ }
+
- _accountMgr.checkAccess(caller, null, true, userVm);
-
- boolean result = resetVMPasswordInternal(cmd, password);
++ Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
++ NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null,
++ _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork),
++ _networkMgr.getNetworkTag(template.getHypervisorType(), defaultNetwork));
+
- 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");
- }
++ VirtualMachineProfile<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>((VMInstanceVO)vm);
+
- userVm.setDetail("Encrypted.Password", encryptedPasswd);
- _vmDao.saveDetails(userVm);
- }
- } else {
- throw new CloudRuntimeException("Failed to reset password for the virtual machine ");
++ 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 userVm;
++ return true;
+ }
+
- 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;
- }
+ @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<InstanceGroupVMMapVO> groupVmMaps = _groupVMMapDao
+ .listByGroupId(groupId);
+ for (InstanceGroupVMMapVO groupMap : groupVmMaps) {
+ SearchCriteria<InstanceGroupVMMapVO> 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<InstanceGroupVMMapVO> groupVmMaps = _groupVMMapDao
+ .listByInstanceId(userVmId);
+ for (InstanceGroupVMMapVO groupMap : groupVmMaps) {
+ SearchCriteria<InstanceGroupVMMapVO> 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<InstanceGroupVMMapVO> 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<InstanceGroupVMMapVO> groupVmMaps = _groupVMMapDao
+ .listByInstanceId(vmId);
+ for (InstanceGroupVMMapVO groupMap : groupVmMaps) {
+ SearchCriteria<InstanceGroupVMMapVO> 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<Long> securityGroupIdList, Account owner, String hostName,
+ String displayName, Long diskOfferingId, Long diskSize,
+ String group, HypervisorType hypervisor, String userData,
+ String sshKeyPair, Map<Long, String> requestedIps,
+ String defaultIp, String keyboard)
+ throws InsufficientCapacityException, ConcurrentOperationException,
+ ResourceUnavailableException, StorageUnavailableException,
+ ResourceAllocationException {
+
+ Account caller = UserContext.current().getCaller();
+ List<NetworkVO> networkList = new ArrayList<NetworkVO>();
+
+ // Verify that caller can perform actions in behalf of vm owner
+ _accountMgr.checkAccess(caller, null, true, owner);
+
+ //
<TRUNCATED>
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/30f2565d/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------