package org.ovirt.engine.core.bll.hostdev;
import static org.ovirt.engine.core.utils.collections.MultiValueMapUtils.addToMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.VmHostDevicesParameters;
import org.ovirt.engine.core.common.businessentities.HostDevice;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmHostDevice;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.dao.VmDeviceDao;
public class RemoveVmHostDevicesCommand extends AbstractVmHostDevicesCommand<VmHostDevicesParameters> {
@Inject
private VmDeviceDao vmDeviceDao;
public RemoveVmHostDevicesCommand(VmHostDevicesParameters parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
}
@Override
protected void executeCommand() {
Set<HostDevice> affectedHostDevices = getAffectedHostDevices();
Map<String, VmHostDevice> existingDevices = getExistingVmHostDevicesByName();
Map<Integer, List<VmHostDevice>> existingDevicesByIommuGroup = new HashMap<>();
for (HostDevice hostDevice : affectedHostDevices) {
boolean shouldRemoveDevice = getPrimaryDeviceNames().contains(hostDevice.getDeviceName());
boolean deviceExists = existingDevices.containsKey(hostDevice.getDeviceName());
if (deviceExists) {
VmHostDevice device = existingDevices.get(hostDevice.getDeviceName());
addToMap(getIommuGroupKey(hostDevice.getIommuGroup()), device, existingDevicesByIommuGroup);
if (shouldRemoveDevice) {
// first just set the flag that this device is not required
device.setIommuPlaceholder(true);
}
}
}
List<VmDevice> devicesToRemove = new ArrayList<>();
List<VmDevice> devicesToUpdate = new ArrayList<>();
// go through all the affected IOMMU groups and remove those who only contain
// no longer needed (placeholder) devices
for (Map.Entry<Integer, List<VmHostDevice>> group : existingDevicesByIommuGroup.entrySet()) {
List<VmHostDevice> devices = group.getValue();
// devices without IOMMU group can be safely removed
boolean noIommuDeviceGroup = group.getKey() == getIommuGroupKey(null);
if (noIommuDeviceGroup || allPlaceholder(devices)) {
// all devices in this group became unnecessary, so remove them
devicesToRemove.addAll(devices);
} else {
// some devices in this group are still required so just update the placeholder flag
devicesToUpdate.addAll(devices);
}
}
vmDeviceDao.removeAllInBatch(devicesToRemove);
vmDeviceDao.updateAllInBatch(devicesToUpdate);
setSucceeded(true);
}
/**
* Returns whether all passed in devices are just IOMMU placeholders - i.e. not required by the user
*/
private static boolean allPlaceholder(Collection<VmHostDevice> devices) {
for (VmHostDevice device : devices) {
if (!device.isIommuPlaceholder()) {
return false;
}
}
return true;
}
private static int getIommuGroupKey(Integer iommuGroup) {
return iommuGroup == null ? -1 : iommuGroup;
}
@Override
public AuditLogType getAuditLogTypeValue() {
return AuditLogType.VM_REMOVE_HOST_DEVICES;
}
public List<String> getNamesRemoved() {
return new ArrayList<>(getPrimaryDeviceNames());
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__REMOVE);
addValidationMessage(EngineMessage.VAR__TYPE__HOST_DEVICES);
}
}