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_PLUGGED;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_SNAPABLE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.storage.disk.image.DisksFilter;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AttachDetachVmDiskParameters;
import org.ovirt.engine.core.common.action.CloneVmParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.GraphicsDevice;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
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.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmWatchdog;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.utils.VmDeviceType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.dao.VmDeviceDao;
@DisableInPrepareMode
@NonTransactiveCommandAttribute(forceCompensation = true)
public class CloneVmCommand<T extends CloneVmParameters> extends AddVmAndCloneImageCommand<T> {
@Inject
private VmDeviceDao vmDeviceDao;
@Inject
private VmDao vmDao;
private Collection<DiskImage> diskImagesFromConfiguration;
private Guid oldVmId;
private VM vm;
private VM sourceVm;
protected CloneVmCommand(T params, CommandContext commandContext) {
super(params, commandContext);
}
public CloneVmCommand(Guid commandId) {
super(commandId);
}
@Override
protected void init() {
super.init();
oldVmId = getParameters().getVmId();
setVmName(getParameters().getNewName());
// init the parameters only at first instantiation (not subsequent for end action)
if (Guid.isNullOrEmpty(getParameters().getNewVmGuid())) {
setupParameters();
} else {
// the VM id has to be the new VM id - same as the getVm is always the new VM
setVmId(getParameters().getNewVmGuid());
}
getParameters().setUseCinderCommandCallback(!getAdjustedDiskImagesFromConfiguration().isEmpty());
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Command);
}
@Override
protected void logErrorOneOrMoreActiveDomainsAreMissing() {
log.error("Can not found any default active domain for one of the disks of snapshot with id '{}'",
oldVmId);
}
@Override
protected Guid getStoragePoolIdFromSourceImageContainer() {
return getVm().getStoragePoolId();
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
Map<String, Pair<String, String>> locks = new HashMap<>();
for (DiskImage image: getImagesToCheckDestinationStorageDomains()) {
locks.put(image.getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, getDiskSharedLockMessage()));
}
locks.put(getSourceVmFromDb().getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_VM_IS_BEING_CLONED));
return locks;
}
@Override
protected Collection<DiskImage> getAdjustedDiskImagesFromConfiguration() {
if (diskImagesFromConfiguration == null) {
VdcQueryReturnValue vdcReturnValue = runInternalQuery(
VdcQueryType.GetAllDisksByVmId,
new IdQueryParameters(oldVmId));
List<Disk> loadedImages = vdcReturnValue.getReturnValue() != null ? (List<Disk>) vdcReturnValue.getReturnValue() : new ArrayList<>();
diskImagesFromConfiguration = DisksFilter.filterImageDisks(loadedImages, ONLY_SNAPABLE, ONLY_ACTIVE);
diskImagesFromConfiguration.addAll(DisksFilter.filterCinderDisks(loadedImages, ONLY_PLUGGED));
}
return diskImagesFromConfiguration;
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put(VdcObjectType.VM.name().toLowerCase(),
StringUtils.defaultString(getParameters().getNewName()));
jobProperties.put("sourcevm",
StringUtils.defaultString(getSourceVmFromDb().getName()));
}
return jobProperties;
}
@Override
protected Guid getSourceVmId() {
return oldVmId;
}
@Override
protected VM getVmFromConfiguration() {
return getVm();
}
@Override
protected VM getSourceVmFromDb() {
if (sourceVm == null) {
sourceVm = vmDao.get(oldVmId);
}
return sourceVm;
}
@Override
public VM getVm() {
if (vm == null) {
vm = vmDao.get(oldVmId);
getVmDeviceUtils().setVmDevices(vm.getStaticData());
vmHandler.updateDisksFromDb(vm);
vmHandler.updateVmGuestAgentVersion(vm);
vmHandler.updateNetworkInterfacesFromDb(vm);
vmHandler.updateVmInitFromDB(vm.getStaticData(), true);
vm.setName(getParameters().getNewName());
vm.setId(getVmId());
}
return vm;
}
private void fillDisksToParameters() {
for (Disk image : getAdjustedDiskImagesFromConfiguration()) {
diskInfoDestinationMap.put(image.getId(), (DiskImage) image);
}
getParameters().setDiskInfoDestinationMap(diskInfoDestinationMap);
}
@Override
protected void removeVmRelatedEntitiesFromDb() {
detachDisks();
super.removeVmRelatedEntitiesFromDb();
}
@Override
protected boolean addVmImages() {
if (super.addVmImages()) {
attachDisks();
return true;
}
return false;
}
private void detachDisks() {
attachDetachDisks(VdcActionType.DetachDiskFromVm);
}
private void attachDisks() {
attachDetachDisks(VdcActionType.AttachDiskToVm);
}
private void attachDetachDisks(VdcActionType actionType) {
VdcQueryReturnValue vdcReturnValue = runInternalQuery(
VdcQueryType.GetAllDisksByVmId,
new IdQueryParameters(oldVmId));
List<Disk> loadedImages = vdcReturnValue.getReturnValue() != null ? (List<Disk>) vdcReturnValue.getReturnValue() : new ArrayList<>();
for (Disk disk : loadedImages) {
if (disk.getDiskStorageType() == DiskStorageType.LUN || disk.isShareable()) {
attachDetachDisk(disk, actionType);
}
}
}
private void attachDetachDisk(Disk disk, VdcActionType actionType) {
DiskVmElement oldDve = disk.getDiskVmElementForVm(oldVmId);
runInternalAction(
actionType,
new AttachDetachVmDiskParameters(
new DiskVmElement(disk.getId(), getParameters().getNewVmGuid()),
oldDve.isPlugged(),
oldDve.isReadOnly()
)
);
}
private void setupParameters() {
setVmId(Guid.newGuid());
VM vmToClone = getVm();
getParameters().setNewVmGuid(getVmId());
getParameters().setVm(vmToClone);
List<VmDevice> devices = vmDeviceDao.getVmDeviceByVmId(oldVmId);
getParameters().setSoundDeviceEnabled(containsDeviceWithType(devices, VmDeviceGeneralType.SOUND));
getParameters().setConsoleEnabled(containsDeviceWithType(devices, VmDeviceGeneralType.CONSOLE));
getParameters().setVirtioScsiEnabled(containsDeviceWithType(devices, VmDeviceGeneralType.CONTROLLER, VmDeviceType.VIRTIOSCSI));
getParameters().setBalloonEnabled(containsDeviceWithType(devices, VmDeviceGeneralType.BALLOON));
setGraphicsDevices(devices);
VdcQueryReturnValue watchdogs = runInternalQuery(VdcQueryType.GetWatchdog, new IdQueryParameters(oldVmId));
if (!((List<VmWatchdog>) watchdogs.getReturnValue()).isEmpty()) {
VmWatchdog watchdog = ((List<VmWatchdog>) watchdogs.getReturnValue()).iterator().next();
getParameters().setUpdateWatchdog(true);
getParameters().setWatchdog(watchdog);
}
fillDisksToParameters();
}
private void setGraphicsDevices(List<VmDevice> devices) {
for (GraphicsType graphicsType : GraphicsType.values()) {
getParameters().getGraphicsDevices().put(graphicsType, null); // prevent copying from the template
}
for (VmDevice device : devices) {
if (device.getType() == VmDeviceGeneralType.GRAPHICS) {
GraphicsDevice graphicsDevice = new GraphicsDevice(device);
getParameters().getGraphicsDevices().put(graphicsDevice.getGraphicsType(), graphicsDevice);
}
}
}
private boolean containsDeviceWithType(List<VmDevice> devices, VmDeviceGeneralType generalType, VmDeviceType deviceType) {
for (VmDevice device : devices) {
if (device.getType() == generalType) {
if (deviceType == null || (deviceType.getName() != null && deviceType.getName().equals(device.getDevice()))) {
return true;
}
}
}
return false;
}
private boolean containsDeviceWithType(List<VmDevice> devices, VmDeviceGeneralType type) {
return containsDeviceWithType(devices, type, null);
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionList = new ArrayList<>();
permissionList.add(new PermissionSubject(getParameters().getVmId(),
VdcObjectType.VM,
getActionType().getActionGroup()));
return permissionList;
}
@Override
protected boolean validate() {
if (!(getSourceVmFromDb().getStatus() == VMStatus.Suspended || getSourceVmFromDb().isDown())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_IS_NOT_DOWN);
}
return super.validate();
}
@Override
protected void updateOriginalTemplate(VmStatic vmStatic) {
vmStatic.setOriginalTemplateGuid(getVm().getOriginalTemplateGuid());
vmStatic.setOriginalTemplateName(getVm().getOriginalTemplateName());
}
}