mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Support for local data disk. Currently enable/disable config is at zone level, in subsequent checkins it can be made more granular.
Following changes are made:
- Create disk offering API now takes an extra parameter to denote storage type (local or shared). This is similar to storage type in service offering.
- Create/delete of data volume on local storage
- Attach/detach for local data volumes. Re-attach is allowed as long as vm host and data volume storage pool host is same.
- Migration of VM instance is not supported if it uses local root or data volumes.
- Migrate is not supported for local volumes.
- Zone level config to enable/disable local storage usage for service and disk offerings.
- Local storage gets discovered when a host is added/reconnected if zone level config is enabled. When disabled existing local storages are not removed but any new local storage is not added.
- Deploy VM command validates service and disk offerings based on local storage config.
- Upgrade uses the global config 'use.local.storage' to set the zone level config for local storage.
(cherry picked from commit 62710aed37606168012a0ed255a876c8e7954010)
3789 lines
176 KiB
Java
Executable File
3789 lines
176 KiB
Java
Executable File
// 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.naming.ConfigurationException;
|
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.cloud.acl.ControlledEntity.ACLType;
|
|
import com.cloud.agent.AgentManager;
|
|
import com.cloud.agent.api.Answer;
|
|
import com.cloud.agent.api.AttachIsoCommand;
|
|
import com.cloud.agent.api.AttachVolumeAnswer;
|
|
import com.cloud.agent.api.AttachVolumeCommand;
|
|
import com.cloud.agent.api.ComputeChecksumCommand;
|
|
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
|
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
|
|
import com.cloud.agent.api.GetVmStatsAnswer;
|
|
import com.cloud.agent.api.GetVmStatsCommand;
|
|
import com.cloud.agent.api.SnapshotCommand;
|
|
import com.cloud.agent.api.StartAnswer;
|
|
import com.cloud.agent.api.StopAnswer;
|
|
import com.cloud.agent.api.UpgradeSnapshotCommand;
|
|
import com.cloud.agent.api.VmStatsEntry;
|
|
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
|
import com.cloud.agent.api.to.NicTO;
|
|
import com.cloud.agent.api.to.VirtualMachineTO;
|
|
import com.cloud.agent.api.to.VolumeTO;
|
|
import com.cloud.agent.manager.Commands;
|
|
import com.cloud.alert.AlertManager;
|
|
import com.cloud.api.ApiDBUtils;
|
|
import com.cloud.api.BaseCmd;
|
|
import com.cloud.api.commands.AssignVMCmd;
|
|
import com.cloud.api.commands.AttachVolumeCmd;
|
|
import com.cloud.api.commands.CreateTemplateCmd;
|
|
import com.cloud.api.commands.CreateVMGroupCmd;
|
|
import com.cloud.api.commands.DeleteVMGroupCmd;
|
|
import com.cloud.api.commands.DeployVMCmd;
|
|
import com.cloud.api.commands.DestroyVMCmd;
|
|
import com.cloud.api.commands.DetachVolumeCmd;
|
|
import com.cloud.api.commands.ListVMsCmd;
|
|
import com.cloud.api.commands.RebootVMCmd;
|
|
import com.cloud.api.commands.RecoverVMCmd;
|
|
import com.cloud.api.commands.ResetVMPasswordCmd;
|
|
import com.cloud.api.commands.RestoreVMCmd;
|
|
import com.cloud.api.commands.StartVMCmd;
|
|
import com.cloud.api.commands.UpdateVMCmd;
|
|
import com.cloud.api.commands.UpgradeVMCmd;
|
|
import com.cloud.async.AsyncJobExecutor;
|
|
import com.cloud.async.AsyncJobManager;
|
|
import com.cloud.async.AsyncJobVO;
|
|
import com.cloud.async.BaseAsyncJobExecutor;
|
|
import com.cloud.capacity.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.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.server.ResourceTag.TaggedResourceType;
|
|
import com.cloud.service.ServiceOfferingVO;
|
|
import com.cloud.service.dao.ServiceOfferingDao;
|
|
import com.cloud.storage.DiskOfferingVO;
|
|
import com.cloud.storage.GuestOSVO;
|
|
import com.cloud.storage.Snapshot;
|
|
import com.cloud.storage.SnapshotVO;
|
|
import com.cloud.storage.Storage;
|
|
import com.cloud.storage.Storage.ImageFormat;
|
|
import com.cloud.storage.Storage.StoragePoolType;
|
|
import com.cloud.storage.Storage.TemplateType;
|
|
import com.cloud.storage.StorageManager;
|
|
import com.cloud.storage.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.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.ResourceTagVO;
|
|
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.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;
|
|
import com.cloud.utils.db.DB;
|
|
import com.cloud.utils.db.Filter;
|
|
import com.cloud.utils.db.GlobalLock;
|
|
import com.cloud.utils.db.JoinBuilder;
|
|
import com.cloud.utils.db.SearchBuilder;
|
|
import com.cloud.utils.db.SearchCriteria;
|
|
import com.cloud.utils.db.Transaction;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
import com.cloud.utils.exception.ExecutionException;
|
|
import com.cloud.utils.fsm.NoTransitionException;
|
|
import com.cloud.utils.net.NetUtils;
|
|
import com.cloud.vm.VirtualMachine.State;
|
|
import com.cloud.vm.dao.InstanceGroupDao;
|
|
import com.cloud.vm.dao.InstanceGroupVMMapDao;
|
|
import com.cloud.vm.dao.NicDao;
|
|
import com.cloud.vm.dao.UserVmDao;
|
|
import com.cloud.vm.dao.UserVmDetailsDao;
|
|
import com.cloud.vm.dao.VMInstanceDao;
|
|
|
|
@Local(value = { UserVmManager.class, UserVmService.class })
|
|
public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager {
|
|
private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class);
|
|
|
|
private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds
|
|
|
|
@Inject
|
|
protected HostDao _hostDao = null;
|
|
@Inject
|
|
protected 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 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 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;
|
|
|
|
private ConfigurationDao _configDao;
|
|
private int _createprivatetemplatefromvolumewait;
|
|
private int _createprivatetemplatefromsnapshotwait;
|
|
@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);
|
|
} 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);
|
|
|
|
} 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) {
|
|
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");
|
|
}
|
|
|
|
if (userData != null) {
|
|
validateUserData(userData);
|
|
// update userData on domain router.
|
|
} else {
|
|
userData = vmInstance.getUserData();
|
|
}
|
|
|
|
String description = "";
|
|
|
|
if (displayName != vmInstance.getDisplayName()) {
|
|
description += "New display name: " + displayName + ". ";
|
|
}
|
|
|
|
if (ha != vmInstance.isHaEnabled()) {
|
|
if (ha) {
|
|
description += "Enabled HA. ";
|
|
} else {
|
|
description += "Disabled HA. ";
|
|
}
|
|
}
|
|
if (osTypeId == null) {
|
|
osTypeId = vmInstance.getGuestOSId();
|
|
} else {
|
|
description += "Changed Guest OS Type to " + osTypeId + ". ";
|
|
}
|
|
|
|
if (group != null) {
|
|
if (addInstanceToGroup(id, group)) {
|
|
description += "Added to group: " + group + ".";
|
|
}
|
|
}
|
|
|
|
_vmDao.updateVM(id, displayName, ha, osTypeId, userData);
|
|
|
|
return _vmDao.findById(id);
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true)
|
|
public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
|
|
return startVirtualMachine(cmd.getId(), 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);
|
|
|
|
// 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<Long>();
|
|
}
|
|
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<Long> networkIdList,
|
|
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>();
|
|
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 Direct/Guest/AccountSpecific; can't create combination of SG enabled network and
|
|
// regular networks
|
|
for (Long networkId : networkIdList) {
|
|
NetworkVO network = _networkDao.findById(networkId);
|
|
|
|
if (network == null) {
|
|
throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue());
|
|
}
|
|
|
|
boolean isSecurityGroupEnabled = _networkMgr.isSecurityGroupSupportedInNetwork(network);
|
|
if (isSecurityGroupEnabled && networkIdList.size() > 1) {
|
|
throw new InvalidParameterValueException("Can't create a vm with multiple networks one of which is Security Group enabled");
|
|
}
|
|
|
|
if (network.getTrafficType() != TrafficType.Guest || network.getGuestType() != Network.GuestType.Shared || (network.getGuestType() == Network.GuestType.Shared && !isSecurityGroupEnabled)) {
|
|
throw new InvalidParameterValueException("Can specify only Direct Guest Account specific networks when deploy vm in Security Group enabled zone");
|
|
}
|
|
|
|
// Perform account permission check
|
|
if (network.getGuestType() != Network.GuestType.Shared) {
|
|
// Check account permissions
|
|
List<NetworkVO> networkMap = _networkDao.listBy(owner.getId(), network.getId());
|
|
if (networkMap == null || networkMap.isEmpty()) {
|
|
throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied");
|
|
}
|
|
}
|
|
|
|
networkList.add(network);
|
|
}
|
|
}
|
|
|
|
// if network is security group enabled, and 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<Long>();
|
|
}
|
|
|
|
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<Long> networkIdList, 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);
|
|
|
|
List<HypervisorType> 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<NetworkOfferingVO> 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<NetworkVO> 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 = virtualNetworks.get(0);
|
|
}
|
|
} 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 XenServer, KVM, and VmWare hypervisors are supported for vpc networks
|
|
if (!vpcSupportedHTypes.contains(template.getHypervisorType())) {
|
|
throw new InvalidParameterValueException("Can't create vm from template with hypervisor "
|
|
+ template.getHypervisorType() + " in vpc network " + 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<NetworkVO> networkList, List<Long> securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, String> 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<VMTemplateZoneVO> 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);
|
|
}
|
|
|
|
// If the template represents an ISO, a disk offering must be passed in, and will be used to create the root disk
|
|
// Else, a disk offering is optional, and if present will be used to create the data disk
|
|
Pair<DiskOfferingVO, Long> rootDiskOffering = new Pair<DiskOfferingVO, Long>(null, null);
|
|
List<Pair<DiskOfferingVO, Long>> dataDiskOfferings = new ArrayList<Pair<DiskOfferingVO, Long>>();
|
|
|
|
if (isIso) {
|
|
if (diskOfferingId == null) {
|
|
throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk.");
|
|
}
|
|
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
|
|
if (diskOffering == null) {
|
|
throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId);
|
|
}
|
|
Long size = null;
|
|
if (diskOffering.getDiskSize() == 0) {
|
|
size = diskSize;
|
|
if (size == null) {
|
|
throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter.");
|
|
}
|
|
}
|
|
rootDiskOffering.first(diskOffering);
|
|
rootDiskOffering.second(size);
|
|
} else {
|
|
rootDiskOffering.first(offering);
|
|
if (diskOfferingId != null) {
|
|
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
|
|
if (diskOffering == null) {
|
|
throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId);
|
|
}
|
|
Long size = null;
|
|
if (diskOffering.getDiskSize() == 0) {
|
|
size = diskSize;
|
|
if (size == null) {
|
|
throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter.");
|
|
}
|
|
}
|
|
dataDiskOfferings.add(new Pair<DiskOfferingVO, Long>(diskOffering, size));
|
|
}
|
|
}
|
|
|
|
//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<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>();
|
|
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<NetworkVO, NicProfile>(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;
|
|
}
|
|
}
|
|
|
|
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<String, List<Long>> ntwkDomains = new HashMap<String, List<Long>>();
|
|
for (NetworkVO network : networkList) {
|
|
String ntwkDomain = network.getNetworkDomain();
|
|
if (!ntwkDomains.containsKey(ntwkDomain)) {
|
|
List<Long> ntwkIds = new ArrayList<Long>();
|
|
ntwkIds.add(network.getId());
|
|
ntwkDomains.put(ntwkDomain, ntwkIds);
|
|
} else {
|
|
List<Long> 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<String> 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());
|
|
|
|
if (_itMgr.allocate(vm, _templateDao.findById(template.getId()), offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, hypervisorType, owner) == null) {
|
|
return null;
|
|
}
|
|
|
|
_vmDao.saveDetails(vm);
|
|
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Successfully allocated DB entry for " + vm);
|
|
}
|
|
UserContext.current().setEventDetails("Vm Id: " + vm.getId());
|
|
|
|
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString());
|
|
_usageEventDao.persist(usageEvent);
|
|
|
|
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm);
|
|
txn.commit();
|
|
// Assign instance to the group
|
|
try {
|
|
if (group != null) {
|
|
boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group);
|
|
if (!addToGroup) {
|
|
throw new CloudRuntimeException("Unable to assign Vm to the group " + group);
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
throw new CloudRuntimeException("Unable to assign Vm to the group " + group);
|
|
}
|
|
|
|
_securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList);
|
|
|
|
return vm;
|
|
}
|
|
|
|
private void validateUserData(String userData) {
|
|
byte[] decodedUserData = null;
|
|
if (userData != null) {
|
|
if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) {
|
|
throw new InvalidParameterValueException("User data is too long");
|
|
}
|
|
decodedUserData = 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<VirtualMachineProfile.Param, Object> additonalParams) throws ResourceUnavailableException, InsufficientCapacityException,
|
|
ConcurrentOperationException {
|
|
|
|
long vmId = cmd.getEntityId();
|
|
Long hostId = cmd.getHostId();
|
|
UserVmVO vm = _vmDao.findById(vmId);
|
|
|
|
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> 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<UserVmVO> profile, DeployDestination dest, ReservationContext context) {
|
|
UserVmVO vm = profile.getVirtualMachine();
|
|
Map<String, String> 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<String, String> 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<UserVmVO> profile, DeployDestination dest, ReservationContext context) {
|
|
UserVmVO userVm = profile.getVirtualMachine();
|
|
List<NicVO> 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<UserVmVO> profile) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean finalizeStart(VirtualMachineProfile<UserVmVO> 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<NicVO> 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);
|
|
}
|
|
|
|
_accountMgr.checkAccess(caller, null, true, vm);
|
|
UserVO user = _userDao.findById(userId);
|
|
|
|
try {
|
|
_itMgr.advanceStop(vm, forced, user, caller);
|
|
} catch (ResourceUnavailableException e) {
|
|
throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e);
|
|
} catch (OperationTimedoutException e) {
|
|
throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e);
|
|
}
|
|
|
|
return _vmDao.findById(vmId);
|
|
}
|
|
|
|
@Override
|
|
public void finalizeStop(VirtualMachineProfile<UserVmVO> profile, StopAnswer answer) {
|
|
//release elastic IP here
|
|
IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(profile.getId());
|
|
if (ip != null && ip.getSystem()) {
|
|
UserContext ctx = UserContext.current();
|
|
try {
|
|
_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<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> 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<Long> groupList = new ArrayList<Long>();
|
|
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<VirtualMachineProfile.Param, Object> 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<VirtualMachineProfile.Param, Object>();
|
|
if (additionalParams != null) {
|
|
params.putAll(additionalParams);
|
|
}
|
|
params.put(VirtualMachineProfile.Param.VmPassword, password);
|
|
}
|
|
|
|
vm = _itMgr.start(vm, params, callerUser, callerAccount, plan);
|
|
|
|
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> 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<VolumeVO> 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 List<UserVmVO> searchForUserVMs(ListVMsCmd cmd) {
|
|
Account caller = UserContext.current().getCaller();
|
|
List<Long> permittedAccounts = new ArrayList<Long>();
|
|
String hypervisor = cmd.getHypervisor();
|
|
boolean listAll = cmd.listAll();
|
|
Long id = cmd.getId();
|
|
Map<String, String> tags = cmd.getTags();
|
|
|
|
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
|
|
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
|
Long domainId = domainIdRecursiveListProject.first();
|
|
Boolean isRecursive = domainIdRecursiveListProject.second();
|
|
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
|
|
|
Criteria c = new Criteria("id", Boolean.TRUE, cmd.getStartIndex(), cmd.getPageSizeVal());
|
|
c.addCriteria(Criteria.KEYWORD, cmd.getKeyword());
|
|
c.addCriteria(Criteria.ID, cmd.getId());
|
|
c.addCriteria(Criteria.NAME, cmd.getInstanceName());
|
|
c.addCriteria(Criteria.STATE, cmd.getState());
|
|
c.addCriteria(Criteria.DATACENTERID, cmd.getZoneId());
|
|
c.addCriteria(Criteria.GROUPID, cmd.getGroupId());
|
|
c.addCriteria(Criteria.FOR_VIRTUAL_NETWORK, cmd.getForVirtualNetwork());
|
|
c.addCriteria(Criteria.NETWORKID, cmd.getNetworkId());
|
|
c.addCriteria(Criteria.TEMPLATE_ID, cmd.getTemplateId());
|
|
c.addCriteria(Criteria.ISO_ID, cmd.getIsoId());
|
|
|
|
if (domainId != null) {
|
|
c.addCriteria(Criteria.DOMAINID, domainId);
|
|
}
|
|
|
|
if (HypervisorType.getType(hypervisor) != HypervisorType.None) {
|
|
c.addCriteria(Criteria.HYPERVISOR, hypervisor);
|
|
} else if (hypervisor != null) {
|
|
throw new InvalidParameterValueException("Invalid HypervisorType " + hypervisor);
|
|
}
|
|
|
|
// ignore these search requests if it's not an admin
|
|
if (_accountMgr.isAdmin(caller.getType())) {
|
|
c.addCriteria(Criteria.PODID, cmd.getPodId());
|
|
c.addCriteria(Criteria.HOSTID, cmd.getHostId());
|
|
c.addCriteria(Criteria.STORAGE_ID, cmd.getStorageId());
|
|
}
|
|
|
|
if (!permittedAccounts.isEmpty()) {
|
|
c.addCriteria(Criteria.ACCOUNTID, permittedAccounts.toArray());
|
|
}
|
|
c.addCriteria(Criteria.ISADMIN, _accountMgr.isAdmin(caller.getType()));
|
|
|
|
return searchForUserVMs(c, caller, domainId, isRecursive, permittedAccounts, listAll, listProjectResourcesCriteria, tags);
|
|
}
|
|
|
|
@Override
|
|
public List<UserVmVO> searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive,
|
|
List<Long> permittedAccounts, boolean listAll, ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags) {
|
|
Filter searchFilter = new Filter(UserVmVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit());
|
|
|
|
SearchBuilder<UserVmVO> sb = _vmDao.createSearchBuilder();
|
|
_accountMgr.buildACLSearchBuilder(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);
|
|
|
|
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().getDataCenterIdToDeployIn(), SearchCriteria.Op.EQ);
|
|
sb.and("podId", sb.entity().getPodIdToDeployIn(), SearchCriteria.Op.EQ);
|
|
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
|
|
sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ);
|
|
sb.and("hostIdIN", sb.entity().getHostId(), SearchCriteria.Op.IN);
|
|
sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ);
|
|
sb.and("isoId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ);
|
|
|
|
if (groupId != null && (Long) groupId == -1) {
|
|
SearchBuilder<InstanceGroupVMMapVO> vmSearch = _groupVMMapDao.createSearchBuilder();
|
|
vmSearch.and("instanceId", vmSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
|
|
sb.join("vmSearch", vmSearch, sb.entity().getId(), vmSearch.entity().getInstanceId(), JoinBuilder.JoinType.LEFTOUTER);
|
|
} else if (groupId != null) {
|
|
SearchBuilder<InstanceGroupVMMapVO> groupSearch = _groupVMMapDao.createSearchBuilder();
|
|
groupSearch.and("groupId", groupSearch.entity().getGroupId(), SearchCriteria.Op.EQ);
|
|
sb.join("groupSearch", groupSearch, sb.entity().getId(), groupSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
if (tags != null && !tags.isEmpty()) {
|
|
SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
|
|
for (int count=0; count < tags.size(); count++) {
|
|
tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
|
|
tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
|
|
tagSearch.cp();
|
|
}
|
|
tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
|
sb.groupBy(sb.entity().getId());
|
|
sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
if (networkId != null) {
|
|
SearchBuilder<NicVO> nicSearch = _nicDao.createSearchBuilder();
|
|
nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
|
|
|
SearchBuilder<NetworkVO> networkSearch = _networkDao.createSearchBuilder();
|
|
networkSearch.and("networkId", networkSearch.entity().getId(), SearchCriteria.Op.EQ);
|
|
nicSearch.join("networkSearch", networkSearch, nicSearch.entity().getNetworkId(), networkSearch.entity().getId(), JoinBuilder.JoinType.INNER);
|
|
|
|
sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
if (storageId != null) {
|
|
SearchBuilder<VolumeVO> volumeSearch = _volsDao.createSearchBuilder();
|
|
volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), SearchCriteria.Op.EQ);
|
|
sb.join("volumeSearch", volumeSearch, sb.entity().getId(), volumeSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
// populate the search criteria with the values passed in
|
|
SearchCriteria<UserVmVO> sc = sb.create();
|
|
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
|
|
|
if (tags != null && !tags.isEmpty()) {
|
|
int count = 0;
|
|
sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.UserVm.toString());
|
|
for (String key : tags.keySet()) {
|
|
sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
|
|
sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (groupId != null && (Long) groupId == -1) {
|
|
sc.setJoinParameters("vmSearch", "instanceId", (Object) null);
|
|
} else if (groupId != null) {
|
|
sc.setJoinParameters("groupSearch", "groupId", groupId);
|
|
}
|
|
|
|
if (keyword != null) {
|
|
SearchCriteria<UserVmVO> ssc = _vmDao.createSearchCriteria();
|
|
ssc.addOr("displayName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
|
ssc.addOr("hostName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
|
ssc.addOr("instanceName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
|
ssc.addOr("state", SearchCriteria.Op.EQ, keyword);
|
|
|
|
sc.addAnd("displayName", SearchCriteria.Op.SC, ssc);
|
|
}
|
|
|
|
if (id != null) {
|
|
sc.setParameters("id", id);
|
|
}
|
|
|
|
if (templateId != null) {
|
|
sc.setParameters("templateId", templateId);
|
|
}
|
|
|
|
if (isoId != null) {
|
|
sc.setParameters("isoId", isoId);
|
|
}
|
|
|
|
if (networkId != null) {
|
|
sc.setJoinParameters("nicSearch", "networkId", networkId);
|
|
}
|
|
|
|
if (name != null) {
|
|
sc.setParameters("name", "%" + name + "%");
|
|
}
|
|
|
|
if (state != null) {
|
|
if (notState != null && (Boolean) notState == true) {
|
|
sc.setParameters("stateNEQ", state);
|
|
} else {
|
|
sc.setParameters("stateEQ", state);
|
|
}
|
|
}
|
|
|
|
if (hypervisor != null) {
|
|
sc.setParameters("hypervisorType", hypervisor);
|
|
}
|
|
|
|
// Don't show Destroyed and Expunging vms to the end user
|
|
if ((isAdmin != null) && ((Boolean) isAdmin != true)) {
|
|
sc.setParameters("stateNIN", "Destroyed", "Expunging");
|
|
}
|
|
|
|
if (zone != null) {
|
|
sc.setParameters("dataCenterId", zone);
|
|
|
|
if (state == null) {
|
|
sc.setParameters("stateNEQ", "Destroyed");
|
|
}
|
|
}
|
|
if (pod != null) {
|
|
sc.setParameters("podId", pod);
|
|
|
|
if (state == null) {
|
|
sc.setParameters("stateNEQ", "Destroyed");
|
|
}
|
|
}
|
|
|
|
if (hostId != null) {
|
|
sc.setParameters("hostIdEQ", hostId);
|
|
} else {
|
|
if (hostName != null) {
|
|
List<HostVO> hosts = _resourceMgr.listHostsByNameLike((String) hostName);
|
|
if (hosts != null & !hosts.isEmpty()) {
|
|
Long[] hostIds = new Long[hosts.size()];
|
|
for (int i = 0; i < hosts.size(); i++) {
|
|
HostVO host = hosts.get(i);
|
|
hostIds[i] = host.getId();
|
|
}
|
|
sc.setParameters("hostIdIN", (Object[]) hostIds);
|
|
} else {
|
|
return new ArrayList<UserVmVO>();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (storageId != null) {
|
|
sc.setJoinParameters("volumeSearch", "poolId", storageId);
|
|
}
|
|
s_logger.debug("THE WHERE CLAUSE IS:" + sc.getWhereClause());
|
|
return _vmDao.search(sc, searchFilter);
|
|
}
|
|
|
|
@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<VolumeVO> 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<VolumeVO> 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<PortForwardingRuleVO> 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<FirewallRuleVO> 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<LoadBalancerVMMapVO> 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<VolumeVO> 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<SnapshotVO> 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<VMInstanceVO> vmOldProfile = new VirtualMachineProfileImpl<VMInstanceVO>(vmoi);
|
|
|
|
// OS 3: update the network
|
|
List<Long> networkIdList = cmd.getNetworkIds();
|
|
List<Long> 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<NetworkVO> networkList = new ArrayList<NetworkVO>();
|
|
|
|
// 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<Long>();
|
|
}
|
|
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<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>();
|
|
NicProfile profile = new NicProfile();
|
|
profile.setDefaultNic(true);
|
|
networks.add(new Pair<NetworkVO, NicProfile>(networkList.get(0), profile));
|
|
|
|
VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId());
|
|
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>(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<NetworkVO> applicableNetworks = new HashSet<NetworkVO>();
|
|
|
|
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<NetworkOfferingVO> 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<NetworkVO> 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 = virtualNetworks.get(0);
|
|
}
|
|
} 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<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>();
|
|
int toggle=0;
|
|
for (NetworkVO appNet: applicableNetworks){
|
|
NicProfile defaultNic = new NicProfile();
|
|
if (toggle==0){
|
|
defaultNic.setDefaultNic(true);
|
|
toggle++;
|
|
}
|
|
networks.add(new Pair<NetworkVO, NicProfile>(appNet, defaultNic));
|
|
}
|
|
VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId());
|
|
VirtualMachineProfileImpl<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>(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<VolumeVO> 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<UserVmVO> profile) {
|
|
}
|
|
|
|
}
|