package org.ovirt.engine.core.bll;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_ACTIVE;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_NOT_SHAREABLE;
import static org.ovirt.engine.core.bll.validator.CpuPinningValidator.isCpuPinningValid;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.network.VmInterfaceManager;
import org.ovirt.engine.core.bll.network.cluster.NetworkHelper;
import org.ovirt.engine.core.bll.profiles.DiskProfileHelper;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaSanityParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
import org.ovirt.engine.core.bll.quota.QuotaVdsDependent;
import org.ovirt.engine.core.bll.storage.disk.image.DisksFilter;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.storage.utils.BlockStorageDiscardFunctionalityHelper;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.bll.utils.IconUtils;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.validator.IconValidator;
import org.ovirt.engine.core.bll.validator.InClusterUpgradeValidator;
import org.ovirt.engine.core.bll.validator.VmValidationUtils;
import org.ovirt.engine.core.bll.validator.VmValidator;
import org.ovirt.engine.core.bll.validator.VmWatchdogValidator;
import org.ovirt.engine.core.bll.validator.storage.CinderDisksValidator;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.FeatureSupported;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AddVmParameters;
import org.ovirt.engine.core.common.action.AddVmToPoolParameters;
import org.ovirt.engine.core.common.action.CreateSnapshotFromTemplateParameters;
import org.ovirt.engine.core.common.action.GraphicsParameters;
import org.ovirt.engine.core.common.action.ImagesContainterParametersBase;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.RngDeviceParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.VmNumaNodeOperationParameters;
import org.ovirt.engine.core.common.action.WatchdogParameters;
import org.ovirt.engine.core.common.asynctasks.EntityInfo;
import org.ovirt.engine.core.common.businessentities.ActionGroup;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.DisplayType;
import org.ovirt.engine.core.common.businessentities.GraphicsDevice;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.ImageType;
import org.ovirt.engine.core.common.businessentities.MigrationSupport;
import org.ovirt.engine.core.common.businessentities.OriginType;
import org.ovirt.engine.core.common.businessentities.Permission;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StoragePoolStatus;
import org.ovirt.engine.core.common.businessentities.UsbPolicy;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType;
import org.ovirt.engine.core.common.businessentities.VmDeviceId;
import org.ovirt.engine.core.common.businessentities.VmDynamic;
import org.ovirt.engine.core.common.businessentities.VmNumaNode;
import org.ovirt.engine.core.common.businessentities.VmPayload;
import org.ovirt.engine.core.common.businessentities.VmRngDevice;
import org.ovirt.engine.core.common.businessentities.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmStatistics;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.VmWatchdog;
import org.ovirt.engine.core.common.businessentities.network.Network;
import org.ovirt.engine.core.common.businessentities.network.VmInterfaceType;
import org.ovirt.engine.core.common.businessentities.network.VmNic;
import org.ovirt.engine.core.common.businessentities.storage.CinderDisk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskInterface;
import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.osinfo.OsRepository;
import org.ovirt.engine.core.common.queries.VmIconIdSizePair;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.utils.VmCpuCountHelper;
import org.ovirt.engine.core.common.utils.VmDeviceType;
import org.ovirt.engine.core.common.utils.customprop.VmPropertiesUtils;
import org.ovirt.engine.core.common.validation.group.CreateVm;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.dao.ClusterDao;
import org.ovirt.engine.core.dao.DiskVmElementDao;
import org.ovirt.engine.core.dao.PermissionDao;
import org.ovirt.engine.core.dao.StorageDomainDao;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.VmDeviceDao;
import org.ovirt.engine.core.dao.VmDynamicDao;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.dao.VmStatisticsDao;
import org.ovirt.engine.core.dao.network.VmNetworkStatisticsDao;
import org.ovirt.engine.core.dao.network.VmNicDao;
import org.ovirt.engine.core.dao.profiles.DiskProfileDao;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
/**
* This class adds a thinly provisioned VM over a template
*/
@DisableInPrepareMode
@NonTransactiveCommandAttribute(forceCompensation = true)
public class AddVmCommand<T extends AddVmParameters> extends VmManagementCommandBase<T>
implements QuotaStorageDependent, QuotaVdsDependent {
private static final Base64 BASE_64 = new Base64(0, null);
@Inject
private DiskProfileHelper diskProfileHelper;
@Inject
private BlockStorageDiscardFunctionalityHelper discardHelper;
protected HashMap<Guid, DiskImage> diskInfoDestinationMap;
protected Map<Guid, StorageDomain> destStorages = new HashMap<>();
protected Map<Guid, List<DiskImage>> storageToDisksMap;
private String cachedDiskSharedLockMessage;
private Guid imageTypeId;
private ImageType imageType;
private Guid vmInterfacesSourceId;
private VmTemplate vmDisksSource;
private Guid vmDevicesSourceId;
private List<StorageDomain> poolDomains;
private Map<Guid, Guid> srcDiskIdToTargetDiskIdMapping = new HashMap<>();
private Map<Guid, Guid> srcVmNicIdToTargetVmNicIdMapping = new HashMap<>();
@Inject
private InClusterUpgradeValidator clusterUpgradeValidator;
@Inject
private VmNicDao vmNicDao;
@Inject
private VmDeviceDao vmDeviceDao;
@Inject
private DiskVmElementDao diskVmElementDao;
@Inject
private VdsDao vdsDao;
@Inject
private VmStaticDao vmStaticDao;
@Inject
private StorageDomainDao storageDomainDao;
@Inject
private ClusterDao clusterDao;
@Inject
private VmNetworkStatisticsDao vmNetworkStatisticsDao;
@Inject
private VmDynamicDao vmDynamicDao;
@Inject
private VmStatisticsDao vmStatisticsDao;
@Inject
private PermissionDao permissionDao;
@Inject
private DiskProfileDao diskProfileDao;
protected AddVmCommand(Guid commandId) {
super(commandId);
}
public AddVmCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
// if we came from endAction the VmId is not null
setVmId(parameters.getVmId().equals(Guid.Empty) ? Guid.newGuid() : parameters.getVmId());
setVmName(parameters.getVm().getName());
parameters.setVmId(getVmId());
setStorageDomainId(getParameters().getStorageDomainId());
}
@Override
protected void init() {
super.init();
T parameters = getParameters();
if (parameters.getVmStaticData() != null) {
Guid templateIdToUse = getParameters().getVmStaticData().getVmtGuid();
if (parameters.getVmStaticData().isUseLatestVersion()) {
VmTemplate latest = vmTemplateDao.getTemplateWithLatestVersionInChain(templateIdToUse);
if (latest != null) {
// if not using original template, need to override storage mappings
// as it may have different set of disks
if (!templateIdToUse.equals(latest.getId())) {
getParameters().setDiskInfoDestinationMap(new HashMap<>());
}
setVmTemplate(latest);
templateIdToUse = latest.getId();
getParameters().getVmStaticData().setVmtGuid(templateIdToUse);
}
}
setVmTemplateId(templateIdToUse);
// API backward compatibility
if (getVmDeviceUtils().shouldOverrideSoundDevice(
getParameters().getVmStaticData(),
getEffectiveCompatibilityVersion(),
getParameters().isSoundDeviceEnabled())) {
parameters.setSoundDeviceEnabled(true);
}
if (parameters.isConsoleEnabled() == null) {
parameters.setConsoleEnabled(false);
}
vmDevicesSourceId = (getInstanceTypeId() != null) ?
getInstanceTypeId() : parameters.getVmStaticData().getVmtGuid();
imageTypeId = parameters.getVmStaticData().getImageTypeId();
vmInterfacesSourceId = parameters.getVmStaticData().getVmtGuid();
vmDisksSource = getVmTemplate();
}
parameters.setEntityInfo(new EntityInfo(VdcObjectType.VM, getVmId()));
// override values here for validate to run with correct values, has to come before init-disks
if (isCompatibilityVersionSupportedByCluster(getEffectiveCompatibilityVersion())) {
updateVmObject();
}
if (getParameters().getVmStaticData().getDefaultDisplayType() == DisplayType.none && !parameters.isConsoleEnabled()) {
parameters.getVmStaticData().setUsbPolicy(UsbPolicy.DISABLED);
}
initTemplateDisks();
initStoragePoolId();
diskInfoDestinationMap = getParameters().getDiskInfoDestinationMap();
if (diskInfoDestinationMap == null) {
diskInfoDestinationMap = new HashMap<>();
}
vmHandler.updateDefaultTimeZone(parameters.getVmStaticData());
// Fill the migration policy if it was omitted
if (getParameters().getVmStaticData() != null &&
getParameters().getVmStaticData().getMigrationSupport() == null) {
setDefaultMigrationPolicy();
}
if (vmDisksSource != null) {
parameters.setUseCinderCommandCallback(!vmDisksSource.getDiskTemplateMap().isEmpty());
}
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Command);
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
Map<String, Pair<String, String>> locks = new HashMap<>();
locks.put(getVmTemplateId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.TEMPLATE,
new LockMessage(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_IS_USED_FOR_CREATE_VM)
.with("VmName", getVmName())));
for (DiskImage image: getImagesToCheckDestinationStorageDomains()) {
locks.put(image.getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, getDiskSharedLockMessage()));
}
if (getParameters().getPoolId() != null) {
locks.put(getParameters().getPoolId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_POOL,
new LockMessage(EngineMessage.ACTION_TYPE_FAILED_VM_POOL_IS_USED_FOR_CREATE_VM)
.with("VmName", getVmName())));
}
return locks;
}
protected String getDiskSharedLockMessage() {
if (cachedDiskSharedLockMessage == null) {
cachedDiskSharedLockMessage = new LockMessage(EngineMessage.ACTION_TYPE_FAILED_DISK_IS_USED_FOR_CREATE_VM)
.with("VmName", getVmName())
.toString();
}
return cachedDiskSharedLockMessage;
}
protected ImageType getImageType() {
if (imageType == null && imageTypeId != null) {
imageType = vmTemplateDao.getImageType(imageTypeId);
}
return imageType;
}
protected void initStoragePoolId() {
if (getCluster() != null) {
setStoragePoolId(getCluster().getStoragePoolId() != null ? getCluster().getStoragePoolId()
: Guid.Empty);
}
}
protected void initTemplateDisks() {
if (vmDisksSource != null) {
vmTemplateHandler.updateDisksFromDb(vmDisksSource);
}
}
private Guid _vmSnapshotId = Guid.Empty;
protected Guid getVmSnapshotId() {
return _vmSnapshotId;
}
protected List<? extends VmNic> _vmInterfaces;
protected List<? extends VmNic> getVmInterfaces() {
if (_vmInterfaces == null) {
List<VmNic> vmNetworkInterfaces = vmNicDao.getAllForTemplate(vmInterfacesSourceId);
_vmInterfaces = vmNetworkInterfaces == null ? new ArrayList<>() : vmNetworkInterfaces;
}
return _vmInterfaces;
}
protected Map<Guid, VmDevice> getVmInterfaceDevices() {
List<VmDevice> vmInterfaceDevicesList = vmDeviceDao.getVmDeviceByVmIdAndType(vmInterfacesSourceId, VmDeviceGeneralType.INTERFACE);
Map<Guid, VmDevice> vmInterfaceDevices = new HashMap<>();
for (VmDevice device : vmInterfaceDevicesList) {
vmInterfaceDevices.put(device.getDeviceId(), device);
}
return vmInterfaceDevices;
}
private List<DiskVmElement> diskVmElements;
protected List<DiskVmElement> getDiskVmElements() {
if (diskVmElements == null) {
diskVmElements = diskVmElementDao.getAllForVm(vmDisksSource.getId());
}
return diskVmElements;
}
protected boolean canAddVm(List<String> reasons, Collection<StorageDomain> destStorages) {
VmStatic vmStaticFromParams = getParameters().getVmStaticData();
if (!canAddVm(reasons, vmStaticFromParams.getName(), getStoragePoolId(), vmStaticFromParams.getPriority())) {
return false;
}
if (!validateCustomProperties(vmStaticFromParams, reasons)) {
return false;
}
// check that template image and vm are on the same storage pool
if (shouldCheckSpaceInStorageDomains()) {
if (!getStoragePoolId().equals(getStoragePoolIdFromSourceImageContainer())) {
reasons.add(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH.toString());
return false;
}
for (StorageDomain domain : destStorages) {
StorageDomainValidator storageDomainValidator = new StorageDomainValidator(domain);
if (!validate(storageDomainValidator.isDomainExistAndActive())) {
return false;
}
}
if (!validateSpaceRequirements()) {
return false;
}
}
return vmHandler.validateDedicatedVdsExistOnSameCluster(vmStaticFromParams,
getReturnValue().getValidationMessages());
}
protected boolean shouldCheckSpaceInStorageDomains() {
return !getImagesToCheckDestinationStorageDomains().stream().map(DiskImage::getImageId)
.findFirst().orElse(VmTemplateHandler.BLANK_VM_TEMPLATE_ID).equals(VmTemplateHandler.BLANK_VM_TEMPLATE_ID);
}
protected void setDefaultMigrationPolicy() {
if (getCluster() != null) {
boolean isMigrationSupported =
FeatureSupported.isMigrationSupported(getCluster().getArchitecture(),
getEffectiveCompatibilityVersion());
MigrationSupport migrationSupport =
isMigrationSupported ? MigrationSupport.MIGRATABLE : MigrationSupport.PINNED_TO_HOST;
getParameters().getVmStaticData().setMigrationSupport(migrationSupport);
}
}
protected Guid getStoragePoolIdFromSourceImageContainer() {
return vmDisksSource.getStoragePoolId();
}
protected boolean validateAddVmCommand() {
return areParametersLegal(getReturnValue().getValidationMessages())
&& checkNumberOfMonitors() && checkSingleQxlDisplay()
&& validate(VmValidator.checkPciAndIdeLimit(getParameters().getVm().getOs(),
getEffectiveCompatibilityVersion(),
getParameters().getVmStaticData().getNumOfMonitors(),
getVmInterfaces(),
getDiskVmElements(),
isVirtioScsiEnabled(),
hasWatchdog(),
isBalloonEnabled(),
isSoundDeviceEnabled()))
&& canAddVm(getReturnValue().getValidationMessages(), destStorages.values())
&& hostToRunExist();
}
/**
* Check if destination storage has enough space
*/
protected boolean validateSpaceRequirements() {
for (Map.Entry<Guid, List<DiskImage>> sdImageEntry : storageToDisksMap.entrySet()) {
StorageDomain destStorageDomain = destStorages.get(sdImageEntry.getKey());
List<DiskImage> disksList = sdImageEntry.getValue();
StorageDomainValidator storageDomainValidator = createStorageDomainValidator(destStorageDomain);
if (!validateDomainsThreshold(storageDomainValidator) ||
!validateFreeSpace(storageDomainValidator, disksList)) {
return false;
}
}
return true;
}
protected StorageDomainValidator createStorageDomainValidator(StorageDomain storageDomain) {
return new StorageDomainValidator(storageDomain);
}
private boolean validateDomainsThreshold(StorageDomainValidator storageDomainValidator) {
return validate(storageDomainValidator.isDomainWithinThresholds());
}
/**
* This validation is for thin provisioning, when done differently on other commands, this method should be overridden.
*/
protected boolean validateFreeSpace(StorageDomainValidator storageDomainValidator, List<DiskImage> disksList) {
Collection<DiskImage> disks = ImagesHandler.getDisksDummiesForStorageAllocations(disksList);
return validate(storageDomainValidator.hasSpaceForNewDisks(disks));
}
protected boolean checkSingleQxlDisplay() {
if (!getParameters().getVmStaticData().getSingleQxlPci() || getParameters().getVmStaticData().getDefaultDisplayType() == DisplayType.none) {
return true;
}
return vmHandler.isSingleQxlDeviceLegal(getParameters().getVm().getDefaultDisplayType(),
getParameters().getVm().getOs(),
getReturnValue().getValidationMessages());
}
protected boolean hostToRunExist() {
List<Guid> dedicatedHostsList = getParameters().getVmStaticData().getDedicatedVmForVdsList();
if (dedicatedHostsList.isEmpty()){
return true;
}
for (Guid candidateHostGuid : dedicatedHostsList) {
if (vdsDao.get(candidateHostGuid) == null) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_HOST_NOT_EXIST);
return false;
}
}
return true;
}
@Override
protected List<Class<?>> getValidationGroups() {
addValidationGroup(CreateVm.class);
return super.getValidationGroups();
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__ADD);
addValidationMessage(EngineMessage.VAR__TYPE__VM);
}
@Override
protected boolean validate() {
if (getCluster() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_CAN_NOT_BE_EMPTY);
}
if (getVmTemplate() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST);
}
if (getVmTemplate().isDisabled()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_IS_DISABLED);
}
if (getStoragePool() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_EXIST);
}
if (!isExternalVM() && getStoragePool().getStatus() != StoragePoolStatus.Up) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMAGE_REPOSITORY_NOT_FOUND);
}
if (!isTemplateInValidDc()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_NOT_EXISTS_IN_CURRENT_DC);
}
if (!isDisksVolumeFormatValid()) {
return false;
}
Version customCompatibilityVersionFromParams = getParameters().getVmStaticData().getCustomCompatibilityVersion();
if (customCompatibilityVersionFromParams != null && !isCompatibilityVersionSupportedByCluster(customCompatibilityVersionFromParams)) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CUSTOM_COMPATIBILITY_VERSION_NOT_SUPPORTED,
String.format("$Ccv %s", customCompatibilityVersionFromParams));
}
// A VM cannot be added in a cluster without a defined architecture
if (getCluster().getArchitecture() == ArchitectureType.undefined) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_UNDEFINED_ARCHITECTURE);
}
if (verifySourceDomains() && buildAndCheckDestStorageDomains()) {
chooseDisksSourceDomains();
} else {
return false;
}
if (isBalloonEnabled() && !osRepository.isBalloonEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion())) {
addValidationMessageVariable("clusterArch", getCluster().getArchitecture());
return failValidation(EngineMessage.BALLOON_REQUESTED_ON_NOT_SUPPORTED_ARCH);
}
if (isSoundDeviceEnabled() && !osRepository.isSoundDeviceEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion())) {
addValidationMessageVariable("clusterArch", getCluster().getArchitecture());
return failValidation(EngineMessage.SOUND_DEVICE_REQUESTED_ON_NOT_SUPPORTED_ARCH);
}
if (!validateQuota(getParameters().getVmStaticData().getQuotaId())) {
return false;
}
// otherwise..
storageToDisksMap =
ImagesHandler.buildStorageToDiskMap(getImagesToCheckDestinationStorageDomains(),
diskInfoDestinationMap);
if (!validateAddVmCommand()) {
return false;
}
VM vmFromParams = getParameters().getVm();
// check if the selected template is compatible with Cluster architecture.
if (!getVmTemplate().getId().equals(VmTemplateHandler.BLANK_VM_TEMPLATE_ID)
&& getCluster().getArchitecture() != getVmTemplate().getClusterArch()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_IS_INCOMPATIBLE);
}
if (StringUtils.isEmpty(vmFromParams.getName())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_MAY_NOT_BE_EMPTY);
}
// check that VM name is not too long
if (!isVmNameValidLength(vmFromParams)) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_LENGTH_IS_TOO_LONG);
}
// check for Vm Payload
if (getParameters().getVmPayload() != null) {
if (!checkPayload(getParameters().getVmPayload(),
getParameters().getVmStaticData().getIsoPath())) {
return false;
}
// otherwise, we save the content in base64 string
for (Map.Entry<String, String> entry : getParameters().getVmPayload().getFiles().entrySet()) {
entry.setValue(new String(BASE_64.encode(entry.getValue().getBytes()), Charset.forName(CharEncoding.UTF_8)));
}
}
// check for Vm Watchdog Model
if (getParameters().getWatchdog() != null) {
if (!validate(new VmWatchdogValidator(vmFromParams.getOs(),
getParameters().getWatchdog(),
getEffectiveCompatibilityVersion()).isValid())) {
return false;
}
}
// check if the OS type is supported
if (!vmHandler.isOsTypeSupported(vmFromParams.getOs(), getCluster().getArchitecture(),
getReturnValue().getValidationMessages())) {
return false;
}
if (!vmHandler.isCpuSupported(
vmFromParams.getVmOsId(),
getEffectiveCompatibilityVersion(),
getCluster().getCpuName(),
getReturnValue().getValidationMessages())) {
return false;
}
// Check if the graphics and display from parameters are supported
if (!vmHandler.isGraphicsAndDisplaySupported(getParameters().getVmStaticData().getOsId(),
vmHandler.getResultingVmGraphics(
getVmDeviceUtils().getGraphicsTypesOfEntity(getVmTemplateId()),
getParameters().getGraphicsDevices()),
vmFromParams.getDefaultDisplayType(),
getReturnValue().getValidationMessages(),
getEffectiveCompatibilityVersion())) {
return false;
}
if (!FeatureSupported.isMigrationSupported(getCluster().getArchitecture(), getEffectiveCompatibilityVersion())
&& vmFromParams.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST) {
return failValidation(EngineMessage.VM_MIGRATION_IS_NOT_SUPPORTED);
}
// check cpuPinning if the check haven't failed yet
if (!validate(isCpuPinningValid(vmFromParams.getCpuPinning(), vmFromParams.getStaticData()))) {
return false;
}
if (vmFromParams.isUseHostCpuFlags()
&& vmFromParams.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST) {
return failValidation(EngineMessage.VM_HOSTCPU_MUST_BE_PINNED_TO_HOST);
}
if (vmFromParams.isUseHostCpuFlags() && (ArchitectureType.ppc == getCluster().getArchitecture().getFamily())) {
return failValidation(EngineMessage.USE_HOST_CPU_REQUESTED_ON_UNSUPPORTED_ARCH);
}
if (!validateMemoryAlignment(getParameters().getVmStaticData())) {
return false;
}
if (getInstanceTypeId() != null && getInstanceType() == null) {
// invalid instance type
return failValidation(EngineMessage.ACTION_TYPE_FAILED_INSTANCE_TYPE_DOES_NOT_EXIST);
}
if (imageTypeId != null && getImageType() == null) {
// invalid image type
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMAGE_TYPE_DOES_NOT_EXIST);
}
if (!validate(VmValidator.validateCpuSockets(getParameters().getVmStaticData(),
getEffectiveCompatibilityVersion()))) {
return false;
}
if (!isCpuSharesValid(vmFromParams)) {
return failValidation(EngineMessage.QOS_CPU_SHARES_OUT_OF_RANGE);
}
if (!VmCpuCountHelper.validateCpuCounts(vmFromParams)) {
return failValidation(EngineMessage.TOO_MANY_CPU_COMPONENTS);
}
if (Boolean.TRUE.equals(getParameters().isVirtioScsiEnabled())) {
// Verify OS compatibility
if (!VmHandler.isOsTypeSupportedForVirtioScsi(vmFromParams.getOs(), getEffectiveCompatibilityVersion(),
getReturnValue().getValidationMessages())) {
return false;
}
}
if (vmFromParams.getMinAllocatedMem() > vmFromParams.getMemSizeMb()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_MIN_MEMORY_CANNOT_EXCEED_MEMORY_SIZE);
}
if (!setAndValidateDiskProfiles()) {
return false;
}
if (!setAndValidateCpuProfile()) {
return false;
}
if (getVmId() != null && vmStaticDao.get(getVmId()) != null) {
return failValidation(EngineMessage.VM_ID_EXISTS);
}
List<CinderDisk> cinderDisks = DisksFilter.filterCinderDisks(diskInfoDestinationMap.values());
CinderDisksValidator cinderDisksValidator = new CinderDisksValidator(cinderDisks);
if (!validate(cinderDisksValidator.validateCinderDiskLimits())) {
return false;
}
if (getParameters().getVmLargeIcon() != null && !validate(IconValidator.validate(
IconValidator.DimensionsType.LARGE_CUSTOM_ICON,
getParameters().getVmLargeIcon()))) {
return false;
}
if (getSmallIconId() != null
&& getParameters().getVmLargeIcon() == null // icon id is ignored if large icon is sent
&& !validate(IconValidator.validateIconId(getSmallIconId(), "Small"))) {
return false;
}
if (getLargeIconId() != null
&& getParameters().getVmLargeIcon() == null // icon id is ignored if large icon is sent
&& !validate(IconValidator.validateIconId(getLargeIconId(), "Large"))) {
return false;
}
if (!validate(getNumaValidator().checkVmNumaNodesIntegrity(
getParameters().getVm(),
getParameters().getVm().getvNumaNodeList()))) {
return false;
}
if (getCluster().isInUpgradeMode()) {
getParameters().getVm().setClusterCompatibilityVersion(getCluster().getCompatibilityVersion());
if (!validate(getClusterUpgradeValidator().isVmReadyForUpgrade(getParameters().getVm()))) {
return false;
}
}
if (!validate(VmHandler.validateMaxMemorySize(
getParameters().getVmStaticData(),
getEffectiveCompatibilityVersion()))) {
return false;
}
if (shouldAddLease(getParameters().getVmStaticData())) {
if (!getParameters().getVmStaticData().isAutoStartup()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_LEASES_ARE_NOT_SUPPORTED_WITH_HA_OFF);
}
if (!FeatureSupported.isVmLeasesSupported(getEffectiveCompatibilityVersion())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_LEASES_ARE_NOT_SUPPORTED);
}
if (!validateLeaseStorageDomain(getParameters().getVmStaticData().getLeaseStorageDomainId())) {
return false;
}
}
return true;
}
protected boolean isDisksVolumeFormatValid() {
if (diskInfoDestinationMap.values().stream()
.anyMatch(d -> d.getDiskStorageType() != DiskStorageType.CINDER &&
d.getVolumeFormat() != VolumeFormat.COW)) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_THIN_TEMPLATE_DISKS_SHOULD_ONLY_BE_COW);
}
return true;
}
private boolean isExternalVM() {
return getParameters().getVmStaticData().getOrigin() == OriginType.EXTERNAL;
}
private Guid getSmallIconId() {
if (getParameters().getVmStaticData() != null) {
return getParameters().getVmStaticData().getSmallIconId();
}
return null;
}
private Guid getLargeIconId() {
if (getParameters().getVmStaticData() != null) {
return getParameters().getVmStaticData().getLargeIconId();
}
return null;
}
private boolean setAndValidateDiskProfiles() {
if (diskInfoDestinationMap != null && !diskInfoDestinationMap.isEmpty()) {
Map<DiskImage, Guid> map = new HashMap<>();
List<DiskImage> diskImages = DisksFilter.filterImageDisks(diskInfoDestinationMap.values(),
ONLY_NOT_SHAREABLE,
ONLY_ACTIVE);
for (DiskImage diskImage : diskImages) {
map.put(diskImage, diskImage.getStorageIds().get(0));
}
return validate(diskProfileHelper.setAndValidateDiskProfiles(map, getCurrentUser()));
}
return true;
}
protected boolean checkTemplateImages(List<String> reasons) {
if (getParameters().getParentCommand() == VdcActionType.AddVmPool) {
return true;
}
for (StorageDomain storage : destStorages.values()) {
if (!validate(vmTemplateHandler.isVmTemplateImagesReady(vmDisksSource, storage.getId(),
false, false, true, true,
storageToDisksMap.get(storage.getId())))) {
return false;
}
}
return true;
}
protected boolean buildAndCheckDestStorageDomains() {
boolean retValue;
if (diskInfoDestinationMap.isEmpty()) {
retValue = fillDestMap();
} else {
retValue = validateProvidedDestinations();
}
if (retValue && getImagesToCheckDestinationStorageDomains().size() != diskInfoDestinationMap.size()) {
log.error("Can not find any default active domain for one of the disks of template with id '{}'",
vmDisksSource.getId());
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_MISSED_STORAGES_FOR_SOME_DISKS);
retValue = false;
}
return retValue && validateIsImagesOnDomains();
}
protected boolean verifySourceDomains() {
return true;
}
protected void chooseDisksSourceDomains() {}
protected Collection<DiskImage> getImagesToCheckDestinationStorageDomains() {
return vmDisksSource.getDiskTemplateMap().values();
}
private boolean validateProvidedDestinations() {
for (DiskImage diskImage : diskInfoDestinationMap.values()) {
if (diskImage.getStorageIds() == null || diskImage.getStorageIds().isEmpty()) {
diskImage.setStorageIds(new ArrayList<>());
diskImage.getStorageIds().add(getParameters().getStorageDomainId());
}
Guid storageDomainId = diskImage.getStorageIds().get(0);
if (destStorages.get(storageDomainId) == null) {
StorageDomain storage = storageDomainDao.getForStoragePool(storageDomainId, getStoragePoolId());
StorageDomainValidator validator =
new StorageDomainValidator(storage);
if (!validate(validator.isDomainExistAndActive()) || !validate(validator.domainIsValidDestination())) {
return false;
}
destStorages.put(storage.getId(), storage);
}
}
return true;
}
private boolean fillDestMap() {
if (getParameters().getStorageDomainId() != null
&& !Guid.Empty.equals(getParameters().getStorageDomainId())) {
Guid storageId = getParameters().getStorageDomainId();
for (DiskImage image : getImagesToCheckDestinationStorageDomains()) {
diskInfoDestinationMap.put(image.getId(), makeNewImage(storageId, image));
}
return validateProvidedDestinations();
}
fillImagesMapBasedOnTemplate();
return true;
}
protected List<StorageDomain> getPoolDomains() {
if (poolDomains == null) {
poolDomains = storageDomainDao.getAllForStoragePool(vmDisksSource.getStoragePoolId());
}
return poolDomains;
}
private void fillImagesMapBasedOnTemplate() {
ImagesHandler.fillImagesMapBasedOnTemplate(vmDisksSource,
getPoolDomains(),
diskInfoDestinationMap,
destStorages);
}
protected boolean validateIsImagesOnDomains() {
for (DiskImage image : getImagesToCheckDestinationStorageDomains()) {
if (!image.getStorageIds().containsAll(diskInfoDestinationMap.get(image.getId()).getStorageIds())) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_NOT_FOUND_ON_DESTINATION_DOMAIN);
return false;
}
}
return true;
}
private DiskImage makeNewImage(Guid storageId, DiskImage image) {
DiskImage newImage = new DiskImage();
newImage.setImageId(image.getImageId());
newImage.setDiskAlias(image.getDiskAlias());
newImage.setVolumeFormat(image.getVolumeFormat());
newImage.setVolumeType(image.getVolumeType());
ArrayList<Guid> storageIds = new ArrayList<>();
storageIds.add(storageId);
newImage.setStorageIds(storageIds);
newImage.setQuotaId(image.getQuotaId());
// Find out the correct disk profile for storage domain
newImage.setDiskProfileId(diskProfileDao.getAllForStorageDomain(storageId).stream()
.filter(p -> image.getDiskProfileIds().contains(p.getId()))
.findFirst()
.map(p -> p.getId())
.orElse(null));
return newImage;
}
protected boolean canAddVm(List<String> reasons, String name, Guid storagePoolId,
int vmPriority) {
// Checking if a desktop with same name already exists
if (isVmWithSameNameExists(name, storagePoolId)) {
reasons.add(EngineMessage.ACTION_TYPE_FAILED_NAME_ALREADY_USED.name());
return false;
}
if (!verifyAddVM(reasons, vmPriority)) {
return false;
}
if (!checkTemplateImages(reasons)) {
return false;
}
return true;
}
protected boolean verifyAddVM(List<String> reasons, int vmPriority) {
return VmHandler.verifyAddVm(reasons, getVmInterfaces().size(), vmPriority, getMacPool());
}
@Override
protected void executeVmCommand() {
vmHandler.warnMemorySizeLegal(getParameters().getVm().getStaticData(), getEffectiveCompatibilityVersion());
List<String> errorMessages = new ArrayList<>();
if (!canAddVm(errorMessages, destStorages.values())) {
log.error("Failed to add VM. The reasons are: {}", String.join(",", errorMessages));
return;
}
if (!addVmLease(getParameters().getVm().getLeaseStorageDomainId(), getVmId())) {
return;
}
TransactionSupport.executeInNewTransaction(() -> {
addVmStatic();
addVmDynamic();
addVmNetwork();
addVmNumaNodes();
addVmStatistics();
addActiveSnapshot();
addVmPermission();
addVmInit();
addVmRngDevice();
getCompensationContext().stateChanged();
return null;
});
if (addVmImages()) {
TransactionSupport.executeInNewTransaction(() -> {
copyDiskVmElements();
copyVmDevices();
addDiskPermissions();
addVmPayload();
updateSmartCardDevices();
addVmWatchdog();
addGraphicsDevice();
getVmDeviceUtils().updateVirtioScsiController(getVm().getStaticData(), getParameters().isVirtioScsiEnabled());
setActionReturnValue(getVm().getId());
setSucceeded(true);
return null;
});
}
if (getParameters().getPoolId() != null) {
addVmToPool();
}
discardHelper.logIfDisksWithIllegalPassDiscardExist(getVmId());
}
/**
* After the copy of the images, copy the properties of the disk VM elements of the source disks to new
* disk VM elements for the created destination disks and save them
*/
private void copyDiskVmElements() {
for (Map.Entry<Guid, Guid> srcToDst : getSrcDiskIdToTargetDiskIdMapping().entrySet()) {
DiskVmElement srcDve = getImagesToCheckDestinationStorageDomains().
stream().
filter(d -> d.getId().equals(srcToDst.getKey())).
findFirst().
get().
getDiskVmElementForVm(getSourceVmId());
createAndSaveNewDiskVmElement(srcToDst.getValue(), getVmId(), srcDve);
}
}
protected Guid getSourceVmId() {
return getVmTemplateId();
}
private void addGraphicsDevice() {
for (GraphicsDevice graphicsDevice : getParameters().getGraphicsDevices().values()) {
if (graphicsDevice == null) {
continue;
}
graphicsDevice.setVmId(getVmId());
getBackend().runInternalAction(VdcActionType.AddGraphicsDevice, new GraphicsParameters(graphicsDevice));
}
}
private void updateSmartCardDevices() {
// if vm smartcard settings is different from device source's
// add or remove the smartcard according to user request
boolean smartcardOnDeviceSource = getInstanceTypeId() != null ? getInstanceType().isSmartcardEnabled() : getVmTemplate().isSmartcardEnabled();
if (getVm().isSmartcardEnabled() != smartcardOnDeviceSource) {
getVmDeviceUtils().updateSmartcardDevice(getVm().getId(), getVm().isSmartcardEnabled());
}
}
private void addVmWatchdog() {
VmWatchdog vmWatchdog = getParameters().getWatchdog();
if (vmWatchdog != null) {
VdcActionType actionType = getVmDeviceUtils().hasWatchdog(getVmTemplateId()) ?
VdcActionType.UpdateWatchdog : VdcActionType.AddWatchdog;
runInternalAction(
actionType,
buildWatchdogParameters(vmWatchdog),
cloneContextAndDetachFromParent());
}
}
private WatchdogParameters buildWatchdogParameters(VmWatchdog vmWatchdog) {
WatchdogParameters parameters = new WatchdogParameters();
parameters.setId(getParameters().getVmId());
parameters.setAction(vmWatchdog.getAction());
parameters.setModel(vmWatchdog.getModel());
return parameters;
}
private void addVmRngDevice() {
VmRngDevice rngDev = getParameters().getRngDevice();
if (rngDev != null) {
rngDev.setVmId(getVmId());
RngDeviceParameters params = new RngDeviceParameters(rngDev, true);
VdcReturnValueBase result = runInternalAction(VdcActionType.AddRngDevice, params, cloneContextAndDetachFromParent());
if (!result.getSucceeded()) {
log.error("Couldn't add RNG device for new VM.");
throw new IllegalArgumentException("Couldn't add RNG device for new VM.");
}
}
}
private void addVmPayload() {
VmPayload payload = getParameters().getVmPayload();
if (payload != null) {
getVmDeviceUtils().addManagedDevice(new VmDeviceId(Guid.newGuid(), getParameters().getVmId()),
VmDeviceGeneralType.DISK,
payload.getDeviceType(),
payload.getSpecParams(),
true,
true);
}
}
protected void copyVmDevices() {
getVmDeviceUtils().copyVmDevices(vmDevicesSourceId,
getVmId(),
getSrcDeviceIdToTargetDeviceIdMapping(),
isSoundDeviceEnabled(),
getParameters().isConsoleEnabled(),
isVirtioScsiEnabled(),
isBalloonEnabled(),
getParameters().getGraphicsDevices().keySet(),
false,
getEffectiveCompatibilityVersion());
if (getInstanceTypeId() != null) {
copyDiskDevicesFromTemplate();
}
}
/**
* If both the instance type and the template is set, than all the devices has to be copied from instance type except the
* disk devices which has to be copied from the template (since the instance type has no disks but the template does have).
*/
private void copyDiskDevicesFromTemplate() {
List<VmDevice> disks =
vmDeviceDao.getVmDeviceByVmIdTypeAndDevice(vmDisksSource.getId(),
VmDeviceGeneralType.DISK,
VmDeviceType.DISK.getName());
getVmDeviceUtils().copyDiskDevices(
getVmId(),
disks,
getSrcDeviceIdToTargetDeviceIdMapping()
);
}
private boolean isLegalClusterId(Guid clusterId, List<String> reasons) {
// check given cluster id
Cluster cluster = clusterDao.get(clusterId);
boolean legalClusterId = cluster != null;
if (!legalClusterId) {
reasons.add(EngineError.VM_INVALID_SERVER_CLUSTER_ID.toString());
}
return legalClusterId;
}
protected boolean areParametersLegal(List<String> reasons) {
boolean returnValue = false;
final VmStatic vmStaticData = getParameters().getVmStaticData();
if (vmStaticData != null) {
returnValue = isLegalClusterId(vmStaticData.getClusterId(), reasons);
if (!validatePinningAndMigration(reasons, vmStaticData, getParameters().getVm().getCpuPinning())) {
returnValue = false;
}
}
return returnValue;
}
protected void addVmNetwork() {
List<? extends VmNic> nics = getVmInterfaces();
VmInterfaceManager vmInterfaceManager = new VmInterfaceManager(getMacPool());
vmInterfaceManager.sortVmNics(nics, getVmInterfaceDevices());
List<String> macAddresses = getMacPool().allocateMacAddresses(nics.size());
// Add interfaces from template
for (int i = 0; i < nics.size(); ++i) {
VmNic iface = nics.get(i);
Guid id = Guid.newGuid();
srcVmNicIdToTargetVmNicIdMapping.put(iface.getId(), id);
iface.setId(id);
iface.setMacAddress(macAddresses.get(i));
iface.setSpeed(VmInterfaceType.forValue(iface.getType()).getSpeed());
iface.setVmTemplateId(null);
iface.setVmId(getParameters().getVmStaticData().getId());
updateProfileOnNic(iface);
vmNicDao.save(iface);
getCompensationContext().snapshotNewEntity(iface);
vmNetworkStatisticsDao.save(iface.getStatistics());
getCompensationContext().snapshotNewEntity(iface.getStatistics());
}
}
private void addVmNumaNodes() {
List<VmNumaNode> numaNodes = getParameters().getVm().getvNumaNodeList();
if (numaNodes.isEmpty()) {
return;
}
VmNumaNodeOperationParameters params = new VmNumaNodeOperationParameters(getParameters().getVm(), numaNodes);
VdcReturnValueBase returnValueBase = getBackend().runInternalAction(VdcActionType.AddVmNumaNodes, params);
if (!returnValueBase.getSucceeded()) {
auditLogDirector.log(this, AuditLogType.NUMA_ADD_VM_NUMA_NODE_FAILED);
}
}
private void addVmInit() {
vmHandler.addVmInitToDB(getParameters().getVmStaticData());
}
private void addVmStatic() {
VmStatic vmStatic = getParameters().getVmStaticData();
if (vmStatic.getOrigin() == null) {
vmStatic.setOrigin(OriginType.valueOf(Config.getValue(ConfigValues.OriginType)));
}
vmStatic.setId(getVmId());
vmStatic.setQuotaId(getQuotaId());
vmStatic.setCreationDate(new Date());
vmStatic.setCreatedByUserId(getUserId());
setIconIds(vmStatic);
// Parses the custom properties field that was filled by frontend to
// predefined and user defined fields
VmPropertiesUtils.getInstance().separateCustomPropertiesToUserAndPredefined(
getEffectiveCompatibilityVersion(), vmStatic);
updateOriginalTemplate(vmStatic);
vmStaticDao.save(vmStatic);
getCompensationContext().snapshotNewEntity(vmStatic);
}
protected void updateOriginalTemplate(VmStatic vmStatic) {
vmStatic.setOriginalTemplateGuid(vmStatic.getVmtGuid());
vmStatic.setOriginalTemplateName(getVmTemplate().getName());
}
private void addVmDynamic() {
VmDynamic vmDynamic = new VmDynamic();
vmDynamic.setId(getVmId());
vmDynamic.setStatus(VMStatus.Down);
vmDynamic.setVmHost("");
vmDynamic.setIp("");
vmDynamic.setFqdn("");
vmDynamic.setLastStopTime(new Date());
vmDynamicDao.save(vmDynamic);
getCompensationContext().snapshotNewEntity(vmDynamic);
}
private void addVmStatistics() {
VmStatistics stats = new VmStatistics(getVmId());
vmStatisticsDao.save(stats);
getCompensationContext().snapshotNewEntity(stats);
}
protected boolean addVmImages() {
if (!vmDisksSource.getDiskTemplateMap().isEmpty()) {
if (getVm().getStatus() != VMStatus.Down) {
log.error("Cannot add images. VM is not Down");
throw new EngineException(EngineError.IRS_IMAGE_STATUS_ILLEGAL);
}
lockVM();
Collection<DiskImage> templateDisks = getImagesToCheckDestinationStorageDomains();
List<DiskImage> diskImages = DisksFilter.filterImageDisks(templateDisks, ONLY_NOT_SHAREABLE,
ONLY_ACTIVE);
for (DiskImage image : diskImages) {
VdcReturnValueBase result = runInternalActionWithTasksContext(
getDiskCreationCommandType(),
buildDiskCreationParameters(image));
// if couldn't create snapshot then stop the transaction and the command
if (!result.getSucceeded()) {
throw new EngineException(result.getFault().getError());
} else {
getTaskIdList().addAll(result.getInternalVdsmTaskIdList());
DiskImage newImage = result.getActionReturnValue();
srcDiskIdToTargetDiskIdMapping.put(image.getId(), newImage.getId());
}
}
// Clone volumes for Cinder disk templates
addVmCinderDisks(templateDisks);
}
return true;
}
protected VdcActionType getDiskCreationCommandType() {
return VdcActionType.CreateSnapshotFromTemplate;
}
protected void lockVM() {
vmHandler.lockVm(getVmId());
}
protected CreateSnapshotFromTemplateParameters buildDiskCreationParameters(DiskImage image) {
CreateSnapshotFromTemplateParameters tempVar = new CreateSnapshotFromTemplateParameters(
image.getImageId(), getParameters().getVmStaticData().getId());
tempVar.setDestStorageDomainId(diskInfoDestinationMap.get(image.getId()).getStorageIds().get(0));
tempVar.setDiskAlias(diskInfoDestinationMap.get(image.getId()).getDiskAlias());
tempVar.setStorageDomainId(image.getStorageIds().get(0));
tempVar.setVmSnapshotId(getVmSnapshotId());
tempVar.setParentCommand(VdcActionType.AddVm);
tempVar.setEntityInfo(getParameters().getEntityInfo());
tempVar.setParentParameters(getParameters());
tempVar.setQuotaId(diskInfoDestinationMap.get(image.getId()).getQuotaId());
tempVar.setDiskProfileId(diskInfoDestinationMap.get(image.getId()).getDiskProfileId());
return tempVar;
}
private void createAndSaveNewDiskVmElement(Guid newDiskImageId, Guid newVmId, DiskVmElement oldDve) {
DiskVmElement newDve = DiskVmElement.copyOf(oldDve, newDiskImageId, newVmId);
diskVmElementDao.save(newDve);
}
protected void addVmCinderDisks(Collection<DiskImage> templateDisks) {
List<CinderDisk> cinderDisks = DisksFilter.filterCinderDisks(templateDisks);
if (cinderDisks.isEmpty()) {
return;
}
Map<Guid, Guid> diskImageMap = new HashMap<>();
for (CinderDisk cinderDisk : cinderDisks) {
ImagesContainterParametersBase params = buildCloneCinderDiskCommandParameters(cinderDisk);
VdcReturnValueBase vdcReturnValueBase = runInternalAction(
VdcActionType.CloneSingleCinderDisk,
params,
cloneContext().withoutExecutionContext().withoutLock());
if (!vdcReturnValueBase.getSucceeded()) {
log.error("Error cloning Cinder disk '{}': {}", cinderDisk.getDiskAlias());
getReturnValue().setFault(vdcReturnValueBase.getFault());
return;
}
Guid imageId = vdcReturnValueBase.getActionReturnValue();
diskImageMap.put(cinderDisk.getId(), imageId);
}
srcDiskIdToTargetDiskIdMapping.putAll(diskImageMap);
}
private ImagesContainterParametersBase buildCloneCinderDiskCommandParameters(CinderDisk cinderDisk) {
ImagesContainterParametersBase createParams = new ImagesContainterParametersBase(cinderDisk.getImageId());
DiskImage templateDisk = diskInfoDestinationMap.get(cinderDisk.getId());
createParams.setDiskAlias(templateDisk.getDiskAlias());
createParams.setStorageDomainId(templateDisk.getStorageIds().get(0));
createParams.setEntityInfo(getParameters().getEntityInfo());
createParams.setParentCommand(getActionType());
createParams.setParentParameters(getParameters());
createParams.setVmSnapshotId(getVmSnapshotId());
return createParams;
}
private void addVmToPool() {
AddVmToPoolParameters parameters = new AddVmToPoolParameters(getParameters().getPoolId(), getVmId());
parameters.setShouldBeLogged(false);
VdcReturnValueBase result = runInternalActionWithTasksContext(
VdcActionType.AddVmToPool,
parameters);
setSucceeded(result.getSucceeded());
if (!result.getSucceeded()) {
log.error("Error adding VM {} to Pool {}", getVmId(), getParameters().getPoolId());
getReturnValue().setFault(result.getFault());
return;
}
addVmPermission();
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? !getReturnValue().getVdsmTaskIdList().isEmpty() ? AuditLogType.USER_ADD_VM_STARTED
: AuditLogType.USER_ADD_VM : AuditLogType.USER_FAILED_ADD_VM;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.USER_ADD_VM_FINISHED_SUCCESS
: AuditLogType.USER_ADD_VM_FINISHED_FAILURE;
default:
return AuditLogType.USER_ADD_VM_FINISHED_FAILURE;
}
}
@Override
protected VdcActionType getChildActionType() {
return VdcActionType.CreateSnapshotFromTemplate;
}
@Override
protected void endWithFailure() {
super.endActionOnDisks();
removeVmRelatedEntitiesFromDb();
setSucceeded(true);
}
protected void removeVmRelatedEntitiesFromDb() {
removeVmUsers();
removeVmNetwork();
// Note that currently newly added vm never have memory state
// In case it will be changed (clone vm from snapshot will clone the memory state),
// we'll need to remove the memory state images here as well.
removeVmSnapshots();
removeVmStatic();
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionList = new ArrayList<>();
permissionList.add(new PermissionSubject(getClusterId(),
VdcObjectType.Cluster,
getActionType().getActionGroup()));
permissionList.add(new PermissionSubject(getVmTemplateId(),
VdcObjectType.VmTemplate,
getActionType().getActionGroup()));
if (getVmTemplate() != null && !getVmTemplate().getDiskList().isEmpty()) {
permissionList.addAll(getParameters().getDiskInfoDestinationMap()
.values()
.stream()
.filter(disk -> disk.getStorageIds() != null && !disk.getStorageIds().isEmpty())
.map(disk -> new PermissionSubject(disk.getStorageIds().get(0),
VdcObjectType.Storage,
ActionGroup.CREATE_DISK))
.collect(Collectors.toList()));
}
addPermissionSubjectForAdminLevelProperties(permissionList);
return permissionList;
}
/**
* user need permission on each object used: template, instance type, image type.
*/
@Override
protected boolean checkPermissions(final List<PermissionSubject> permSubjects) {
if (getInstanceTypeId() != null && !checkInstanceTypeImagePermissions(getInstanceTypeId())) {
return false;
}
if (imageTypeId != null && !checkInstanceTypeImagePermissions(imageTypeId)) {
return false;
}
for (PermissionSubject permSubject : permSubjects) {
// if user is using instance type, then create_instance may be sufficient
if (getInstanceTypeId() != null && checkCreateInstancePermission(permSubject)) {
continue;
}
// create_vm is overriding in case no create_instance, try again with it
if (!checkSinglePermission(permSubject, getReturnValue().getValidationMessages())) {
logMissingPermission(permSubject);
return false;
}
}
return true;
}
/**
* To create a vm either {@link ActionGroup#CREATE_VM} or {@link ActionGroup#CREATE_INSTANCE} permissions is
* required for selected {@link VdcObjectType}s. However {@link #getPermissionCheckSubjects()} returns only
* {@link ActionGroup#CREATE_VM} based permissions subjects. This method helps to mitigate this problem.
* @param permSubject permission subject
* @return true if {@link ActionGroup#CREATE_INSTANCE} based permission is sufficient, false otherwise
*/
private boolean checkCreateInstancePermission(PermissionSubject permSubject) {
final List<VdcObjectType> overriddenPermissionObjectTypes = Arrays.asList(
VdcObjectType.Cluster,
VdcObjectType.VmTemplate);
final boolean instanceCreateObjectType = overriddenPermissionObjectTypes.contains(permSubject.getObjectType());
if (!instanceCreateObjectType) {
return false;
}
final PermissionSubject alteredPermissionSubject = new PermissionSubject(permSubject.getObjectId(),
permSubject.getObjectType(),
ActionGroup.CREATE_INSTANCE,
permSubject.getMessage());
return checkSinglePermission(alteredPermissionSubject, getReturnValue().getValidationMessages());
}
/**
* If using an instance type/image the user needs to have either CREATE_INSTANCE or the specific
* getActionType().getActionGroup() on the instance type/image
*/
private boolean checkInstanceTypeImagePermissions(Guid id) {
Collection<String> createInstanceMessages = new ArrayList<>();
Collection<String> actionGroupMessages = new ArrayList<>();
PermissionSubject createInstanceSubject = new PermissionSubject(id, VdcObjectType.VmTemplate, ActionGroup.CREATE_INSTANCE);
PermissionSubject actionGroupSubject = new PermissionSubject(id, VdcObjectType.VmTemplate, getActionType().getActionGroup());
// it is enough if at least one of this two permissions are there
if (!checkSinglePermission(createInstanceSubject, createInstanceMessages) &&
!checkSinglePermission(actionGroupSubject, actionGroupMessages)) {
getReturnValue().getValidationMessages().addAll(actionGroupMessages);
return false;
}
return true;
}
protected void addPermissionSubjectForAdminLevelProperties(List<PermissionSubject> permissionList) {
VmStatic vmFromParams = getParameters().getVmStaticData();
VmTemplate vmTemplate = getVmTemplate();
if (vmFromParams != null && vmTemplate != null) {
// user needs specific permission to change custom properties
if (!Objects.equals(vmFromParams.getCustomProperties(), vmTemplate.getCustomProperties())) {
permissionList.add(new PermissionSubject(getClusterId(),
VdcObjectType.Cluster, ActionGroup.CHANGE_VM_CUSTOM_PROPERTIES));
}
//if the template is blank we ignore his pinned hosts
if(vmTemplate.isBlank()){
return;
}
Set<Guid> dedicatedVmForVdsFromUser = new HashSet<>(vmFromParams.getDedicatedVmForVdsList());
Set<Guid> dedicatedVmForVdsFromTemplate = new HashSet<>(vmTemplate.getDedicatedVmForVdsList());
// host-specific parameters can be changed by administration role only
if (!dedicatedVmForVdsFromUser.equals(dedicatedVmForVdsFromTemplate)
|| !StringUtils.isEmpty(vmFromParams.getCpuPinning())) {
permissionList.add(new PermissionSubject(getClusterId(),
VdcObjectType.Cluster, ActionGroup.EDIT_ADMIN_VM_PROPERTIES));
}
}
}
private void addVmPermission() {
UniquePermissionsSet permissionsToAdd = new UniquePermissionsSet();
if (isMakeCreatorExplicitOwner()) {
permissionsToAdd.addPermission(getCurrentUser().getId(), PredefinedRoles.VM_OPERATOR.getId(),
getVmId(), VdcObjectType.VM);
}
if (getParameters().isCopyTemplatePermissions() && !getVmTemplateId().equals(VmTemplateHandler.BLANK_VM_TEMPLATE_ID)) {
copyTemplatePermissions(permissionsToAdd);
}
if (!permissionsToAdd.isEmpty()) {
List<Permission> permissionsList = permissionsToAdd.asPermissionList();
MultiLevelAdministrationHandler.addPermission(permissionsList.toArray(new Permission[permissionsList.size()]));
getCompensationContext().snapshotNewEntities(permissionsList);
}
}
private boolean isMakeCreatorExplicitOwner() {
return getParameters().isMakeCreatorExplicitOwner()
|| (getCurrentUser() != null && getParameters().getPoolId() == null
&& !checkUserAuthorization(getCurrentUser().getId(),
ActionGroup.MANIPULATE_PERMISSIONS,
getVmId(),
VdcObjectType.VM));
}
private void copyTemplatePermissions(UniquePermissionsSet permissionsToAdd) {
List<Permission> templatePermissions =
permissionDao.getAllForEntity(getVmTemplateId(), getEngineSessionSeqId(), false);
for (Permission templatePermission : templatePermissions) {
boolean templateOwnerRole = templatePermission.getRoleId().equals(PredefinedRoles.TEMPLATE_OWNER.getId());
boolean templateUserRole = templatePermission.getRoleId().equals(PredefinedRoles.TEMPLATE_USER.getId());
if (templateOwnerRole || templateUserRole) {
continue;
}
permissionsToAdd.addPermission(templatePermission.getAdElementId(), templatePermission.getRoleId(),
getVmId(), VdcObjectType.VM);
}
}
private void addDiskPermissions() {
List<Guid> newDiskImageIds = new ArrayList<>(srcDiskIdToTargetDiskIdMapping.values());
Permission[] permsArray = new Permission[newDiskImageIds.size()];
for (int i = 0; i < newDiskImageIds.size(); i++) {
permsArray[i] =
new Permission(getUserIdOfDiskOperator(),
PredefinedRoles.DISK_OPERATOR.getId(),
newDiskImageIds.get(i),
VdcObjectType.Disk);
}
MultiLevelAdministrationHandler.addPermission(permsArray);
}
private Guid getUserIdOfDiskOperator() {
Guid diskOperatorIdFromParams = getParameters().getDiskOperatorAuthzPrincipalDbId();
return diskOperatorIdFromParams != null ? diskOperatorIdFromParams : getCurrentUser().getId();
}
private void addActiveSnapshot() {
_vmSnapshotId = Guid.newGuid();
getSnapshotsManager().addActiveSnapshot(_vmSnapshotId, getVm(), getCompensationContext());
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
if (!StringUtils.isBlank(getParameters().getVm().getName())) {
return Collections.singletonMap(getParameters().getVm().getName(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_NAME, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
}
return null;
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put(VdcObjectType.VM.name().toLowerCase(),
(getVmName() == null) ? "" : getVmName());
}
return jobProperties;
}
private Guid getQuotaId() {
return getQuotaManager().getDefaultQuotaIfNull(
getParameters().getVmStaticData().getQuotaId(),
getStoragePoolId());
}
@Override
public List<QuotaConsumptionParameter> getQuotaStorageConsumptionParameters() {
return diskInfoDestinationMap.values()
.stream()
.map(disk -> new QuotaStorageConsumptionParameter(
disk.getQuotaId(),
null,
QuotaStorageConsumptionParameter.QuotaAction.CONSUME,
disk.getStorageIds().get(0),
(double) disk.getSizeInGigabytes()))
.collect(Collectors.toList());
}
@Override
public List<QuotaConsumptionParameter> getQuotaVdsConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
list.add(new QuotaSanityParameter(getQuotaId(), null));
return list;
}
public Map<Guid, Guid> getSrcDiskIdToTargetDiskIdMapping() {
return srcDiskIdToTargetDiskIdMapping;
}
public Map<Guid, Guid> getSrcDeviceIdToTargetDeviceIdMapping() {
Map<Guid, Guid> srcDeviceIdToTargetDeviceIdMapping = new HashMap<>();
srcDeviceIdToTargetDeviceIdMapping.putAll(srcVmNicIdToTargetVmNicIdMapping);
srcDeviceIdToTargetDeviceIdMapping.putAll(srcDiskIdToTargetDiskIdMapping);
return srcDeviceIdToTargetDeviceIdMapping;
}
protected boolean isVirtioScsiEnabled() {
Boolean virtioScsiEnabled = getParameters().isVirtioScsiEnabled();
boolean isOsSupportedForVirtIoScsi = VmValidationUtils.isDiskInterfaceSupportedByOs(
getParameters().getVm().getOs(), getEffectiveCompatibilityVersion(), DiskInterface.VirtIO_SCSI);
return virtioScsiEnabled != null ? virtioScsiEnabled : isOsSupportedForVirtIoScsi;
}
protected boolean isBalloonEnabled() {
Boolean balloonEnabled = getParameters().isBalloonEnabled();
return balloonEnabled != null ? balloonEnabled :
osRepository.isBalloonEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion());
}
protected boolean isSoundDeviceEnabled() {
Boolean soundDeviceEnabled = getParameters().isSoundDeviceEnabled();
return soundDeviceEnabled != null ? soundDeviceEnabled :
osRepository.isSoundDeviceEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion());
}
private boolean hasWatchdog() {
return getParameters().getWatchdog() != null;
}
protected boolean isVirtioScsiControllerAttached(Guid vmId) {
return getVmDeviceUtils().hasVirtioScsiController(vmId);
}
/**
* This method override vm values with the instance type values
* in case instance type is selected for this vm
*/
private void updateVmObject() {
updateParametersVmFromInstanceType();
// set vm interface source id to be the instance type, vm interface are taken from it
if (getInstanceType() != null) {
vmInterfacesSourceId = getInstanceTypeId();
}
VmStatic vmStatic = getParameters().getVmStaticData();
ImageType imageType = getImageType();
if (imageType != null) {
vmStatic.setOsId(imageType.getOsId());
vmStatic.setIsoPath(imageType.getIsoPath());
vmStatic.setInitrdUrl(imageType.getInitrdUrl());
vmStatic.setKernelUrl(imageType.getKernelUrl());
vmStatic.setKernelParams(imageType.getKernelParams());
// set vm disks source to be the image type, vm disks are taken from it
vmDisksSource = (VmTemplate)imageType;
}
// Choose a proper default OS according to the cluster architecture
if (getParameters().getVmStaticData().getOsId() == OsRepository.AUTO_SELECT_OS) {
if (getCluster().getArchitecture() != ArchitectureType.undefined) {
Integer defaultOs = osRepository.getDefaultOSes().get(getCluster().getArchitecture());
getParameters().getVmStaticData().setOsId(defaultOs);
}
}
vmHandler.autoSelectUsbPolicy(getParameters().getVmStaticData());
// Choose a proper default display type according to the cluster architecture
vmHandler.autoSelectDefaultDisplayType(vmDevicesSourceId,
getParameters().getVmStaticData(),
getCluster(),
getParameters().getGraphicsDevices());
// If not set by user, choose proper graphics device according to the cluster architecture
vmHandler.autoSelectGraphicsDevice(vmDevicesSourceId,
getParameters().getVmStaticData(),
getCluster(),
getParameters().getGraphicsDevices(),
getEffectiveCompatibilityVersion());
}
private boolean isTemplateInValidDc() {
return VmTemplateHandler.BLANK_VM_TEMPLATE_ID.equals(getVmTemplateId())
|| getVmTemplate().getStoragePoolId().equals(getStoragePoolId());
}
private void updateProfileOnNic(VmNic iface) {
Network network = NetworkHelper.getNetworkByVnicProfileId(iface.getVnicProfileId());
if (network != null && !NetworkHelper.isNetworkInCluster(network, getClusterId())) {
iface.setVnicProfileId(null);
}
}
protected boolean checkNumberOfMonitors() {
if (getParameters().getVmStaticData().getDefaultDisplayType() == DisplayType.none) {
return true;
}
Collection<GraphicsType> graphicsTypes = vmHandler.getResultingVmGraphics(
getVmDeviceUtils().getGraphicsTypesOfEntity(getVmTemplateId()),
getParameters().getGraphicsDevices());
int numOfMonitors = getParameters().getVmStaticData().getNumOfMonitors();
return vmHandler.isNumOfMonitorsLegal(graphicsTypes,
numOfMonitors,
getReturnValue().getValidationMessages());
}
/**
* Icon processing policy:
* <ul>
* <li>If there is an attached icon, it is used as large icon as base for computation of small icon.
* Predefined icons should not be sent in parameters.</li>
* <li>If there are no icon in parameters && both (small and large) icon ids are set then those ids are used.
* </li>
* <li>Otherwise (at least one icon id is null) both icon ids are copied from template.</li>
* </ul>
*/
private void setIconIds(VmStatic vmStatic) {
if (getParameters().getVmLargeIcon() != null){
final VmIconIdSizePair iconIds =
IconUtils.ensureIconPairInDatabase(getParameters().getVmLargeIcon());
vmStatic.setLargeIconId(iconIds.getLarge());
vmStatic.setSmallIconId(iconIds.getSmall());
} else {
if (vmStatic.getLargeIconId() == null
|| vmStatic.getSmallIconId() == null) {
vmStatic.setSmallIconId(getVmTemplate().getSmallIconId());
vmStatic.setLargeIconId(getVmTemplate().getLargeIconId());
}
}
}
@Override
public CommandCallback getCallback() {
return getParameters().isUseCinderCommandCallback() ? new ConcurrentChildCommandsExecutionCallback() : null;
}
private InClusterUpgradeValidator getClusterUpgradeValidator() {
return clusterUpgradeValidator;
}
}