package org.ovirt.engine.core.common.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.ovirt.engine.core.common.businessentities.BootSequence;
import org.ovirt.engine.core.common.businessentities.GraphicsDevice;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmBase;
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.network.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.Guid;
public class VmDeviceCommonUtils {
static final String NETWORK_CHAR = "N";
static final String CDROM_CHAR = "D";
static final String DRIVE_CHAR = "C";
public static final String SPEC_PARAM_SIZE = "size";
public static boolean isNetwork(VmDevice device) {
return device.getType() == VmDeviceGeneralType.INTERFACE;
}
public static boolean isDisk(VmDevice device) {
return device.getType() == VmDeviceGeneralType.DISK
&& device.getDevice().equals(VmDeviceType.DISK.getName());
}
public static boolean isCD(VmDevice device) {
return device.getType() == VmDeviceGeneralType.DISK
&& device.getDevice().equals(VmDeviceType.CDROM.getName());
}
public static boolean isSound(VmDevice device) {
return device.getType() == VmDeviceGeneralType.SOUND;
}
public static boolean isMemoryBalloon(VmDevice device) {
return device.getType() == VmDeviceGeneralType.BALLOON;
}
public static boolean isGraphics(VmDevice device) {
return device.getType() == VmDeviceGeneralType.GRAPHICS;
}
public static boolean isBridge(VmDevice device) {
return device.getType() == VmDeviceGeneralType.INTERFACE
&& device.getDevice().equals(VmDeviceType.BRIDGE.getName());
}
public static boolean isHostDevInterface(VmDevice device) {
return device.getType() == VmDeviceGeneralType.INTERFACE
&& device.getDevice().equals(VmDeviceType.HOST_DEVICE.getName());
}
public static VmDevice createVirtioSerialDeviceForVm(Guid vmId) {
return new VmDevice(new VmDeviceId(Guid.newGuid(), vmId),
VmDeviceGeneralType.CONTROLLER,
VmDeviceType.VIRTIOSERIAL.getName(),
"",
new HashMap<String, Object>(),
true,
true,
false,
"",
null,
null,
null);
}
/**
* Updates given devices boot order in accordance with bootSequence given.
*/
public static void updateVmDevicesBootOrder(
BootSequence bootSequence,
Collection<VmDevice> devices,
List<VmNetworkInterface> interfaces,
Map<VmDeviceId, DiskVmElement> deviceIdToDiskVmElement) {
int bootOrder = 0;
// reset current boot order of all relevant devices before recomputing it.
for (VmDevice device : devices) {
device.setBootOrder(0);
}
switch (bootSequence) {
case C:
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
break;
case CD:
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setCDBootOrder(devices, bootOrder);
break;
case CDN:
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
break;
case CN:
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
break;
case CND:
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setCDBootOrder(devices, bootOrder);
break;
case D:
bootOrder = setCDBootOrder(devices, bootOrder);
break;
case DC:
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
break;
case DCN:
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
break;
case DN:
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
break;
case DNC:
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
break;
case N:
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
break;
case NC:
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
break;
case NCD:
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
bootOrder = setCDBootOrder(devices, bootOrder);
break;
case ND:
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setCDBootOrder(devices, bootOrder);
break;
case NDC:
bootOrder = setNetworkBootOrder(devices, bootOrder, interfaces);
bootOrder = setCDBootOrder(devices, bootOrder);
bootOrder = setDiskBootOrder(devices, bootOrder, deviceIdToDiskVmElement);
break;
}
}
/**
* updates network devices boot order
*/
private static int setNetworkBootOrder(Collection<VmDevice> devices, int bootOrder, List<VmNetworkInterface> interfaces) {
for (VmDevice pluggedInterface : sortInterfacesByName(getPluggedManagedInterfaces(devices), interfaces)) {
pluggedInterface.setBootOrder(++bootOrder);
}
return bootOrder;
}
private static List<VmDevice> getPluggedManagedInterfaces(Collection<VmDevice> devices) {
List<VmDevice> result = new ArrayList<>();
for (VmDevice device : devices) {
if ((isHostDevInterface(device) || isBridge(device)) && device.isPlugged() && device.isManaged()) {
result.add(device);
}
}
return result;
}
private static List<VmDevice> sortInterfacesByName(List<VmDevice> pluggedInterfaces, List<VmNetworkInterface> interfaces) {
if (pluggedInterfaces.size() < 2) {
return pluggedInterfaces;
}
final Map<Guid, String> deviceIdToIfaceName = new HashMap<>();
for (VmNetworkInterface iface : interfaces) {
deviceIdToIfaceName.put(iface.getId(), iface.getName());
}
Collections.sort(pluggedInterfaces, Comparator.comparing(d -> deviceIdToIfaceName.get(d.getId().getDeviceId())));
return pluggedInterfaces;
}
/**
* updates CD boot order
*/
private static int setCDBootOrder(Collection<VmDevice> devices, int bootOrder) {
for (VmDevice device : devices) {
if (isCD(device) && device.isPlugged()) {
device.setBootOrder(++bootOrder);
}
}
return bootOrder;
}
/**
* updates disk boot order
* snapshot disk devices always will have lower priority than regular attached disks.
*/
private static int setDiskBootOrder(
Collection<VmDevice> devices,
int bootOrder,
Map<VmDeviceId, DiskVmElement> deviceIdTodiskVmElement) {
LinkedList<VmDevice> diskDevices = new LinkedList<>();
for (VmDevice device : devices) {
if (isDisk(device)) {
Guid id = device.getDeviceId();
if (id != null && !id.equals(Guid.Empty)) {
if (device.getSnapshotId() == null) {
diskDevices.addFirst(device);
} else {
diskDevices.addLast(device);
}
}
}
}
for (VmDevice device : diskDevices) {
DiskVmElement dve = deviceIdTodiskVmElement.get(device.getId());
if (dve != null && dve.isBoot()) {
device.setBootOrder(++bootOrder);
}
}
return bootOrder;
}
public static Map<VmDeviceId, DiskVmElement> extractDiskVmElements(VM vm) {
Map<VmDeviceId, DiskVmElement> result = new HashMap<>();
for(Disk disk : vm.getDiskMap().values()) {
DiskVmElement element = disk.getDiskVmElementForVm(vm.getId());
if (element != null) {
result.put(element.getId(), element);
}
}
return result;
}
public static boolean isInWhiteList(VmDeviceGeneralType type, String device) {
String expr = getDeviceTypeSearchExpr(type, device);
String whiteList = Config.getValue(ConfigValues.ManagedDevicesWhiteList);
return whiteList.indexOf(expr) >= 0;
}
private static String getDeviceTypeSearchExpr(VmDeviceGeneralType type, String device) {
StringBuilder sb = new StringBuilder();
sb.append("type=");
sb.append(type.getValue());
sb.append(" device=");
sb.append(device);
sb.append(" ");
return sb.toString();
}
/**
* is special device - device which is managed, but contains the general properties
*/
public static boolean isSpecialDevice(String device, VmDeviceGeneralType type) {
return (VmDeviceGeneralType.SOUND == type || VmDeviceType.USB.getName().equals(device)
|| (VmDeviceType.CONSOLE.getName().equals(device) && VmDeviceGeneralType.CONSOLE == type)
|| (VmDeviceType.SMARTCARD.getName().equals(device) && VmDeviceGeneralType.SMARTCARD == type)
|| (VmDeviceType.SPICEVMC.getName().equals(device) && VmDeviceGeneralType.REDIR == type)
|| (VmDeviceType.MEMBALLOON.getName().equals(device) && VmDeviceGeneralType.BALLOON == type))
|| (VmDeviceType.WATCHDOG.getName().equals(device) && VmDeviceGeneralType.WATCHDOG == type)
|| (VmDeviceType.VIRTIO.getName().equals(device) && VmDeviceGeneralType.RNG == type)
|| (VmDeviceType.VIRTIOSERIAL.getName().equals(device) && VmDeviceGeneralType.CONTROLLER == type)
|| (VmDeviceType.VIRTIOSCSI.getName().equals(device) && VmDeviceGeneralType.CONTROLLER == type);
}
/**
* Find a device in the map with the given type.
*/
public static VmDevice findVmDeviceByType(Map<?, VmDevice> vmManagedDeviceMap, VmDeviceType type) {
return findVmDeviceByType(vmManagedDeviceMap, type.getName());
}
/**
* Find a device in the map with the given type.
*/
public static VmDevice findVmDeviceByType(Map<?, VmDevice> vmManagedDeviceMap, String typeName) {
for (VmDevice vmDevice : vmManagedDeviceMap.values()) {
if (vmDevice.getDevice().equals(typeName)) {
return vmDevice;
}
}
return null;
}
/**
* Find a device in the map with the given general type.
*/
public static VmDevice findVmDeviceByGeneralType(Map<Guid, VmDevice> vmManagedDeviceMap,
VmDeviceGeneralType generalType) {
for (VmDevice vmDevice : vmManagedDeviceMap.values()) {
if (vmDevice.getType() == generalType) {
return vmDevice;
}
}
return null;
}
/**
* Check if device with the type given exists in the map.
*/
public static boolean isVmDeviceExists(Map<Guid, VmDevice> vmManagedDeviceMap, VmDeviceType type) {
return findVmDeviceByType(vmManagedDeviceMap, type) != null;
}
/**
* Check if device with the general type given exists in the map.
*/
public static boolean isVmDeviceExists(Map<Guid, VmDevice> vmManagedDeviceMap, VmDeviceGeneralType generalType) {
return findVmDeviceByGeneralType(vmManagedDeviceMap, generalType) != null;
}
/**
* Check if the given collection of devices contains balloon
*
* @param devices - collection of VM devices to look in
*/
public static boolean isBalloonDeviceExists(Collection<VmDevice> devices) {
for (VmDevice device : devices) {
if (isMemoryBalloon(device)) {
return true;
}
}
return false;
}
public static boolean isSoundDeviceExists(Collection<VmDevice> devices) {
for (VmDevice device : devices) {
if (isSound(device)) {
return true;
}
}
return false;
}
public static void addVideoDevice(VmBase vmBase) {
if (vmBase.getDefaultDisplayType().getDefaultVmDeviceType() == null) {
return;
}
VmDevice vmDevice = new VmDevice();
vmDevice.setId(new VmDeviceId(Guid.newGuid(), vmBase.getId()));
vmDevice.setType(VmDeviceGeneralType.VIDEO);
vmDevice.setDevice(vmBase.getDefaultDisplayType().getDefaultVmDeviceType().getName());
vmDevice.setManaged(true);
vmDevice.setPlugged(true);
vmDevice.setReadOnly(false);
vmDevice.setAddress("");
vmBase.getManagedDeviceMap().put(vmDevice.getDeviceId(), vmDevice);
}
public static void addGraphicsDevice(VmBase vmBase, VmDeviceType vmDeviceType) {
GraphicsDevice graphicsDevice = new GraphicsDevice(vmDeviceType);
graphicsDevice.setId(new VmDeviceId(Guid.newGuid(), vmBase.getId()));
vmBase.getManagedDeviceMap().put(graphicsDevice.getDeviceId(), graphicsDevice);
}
/**
* Get unit/slot number reserved by VDSM for CD-ROM.
*
* @param cdInterface name of the interface ("ide"/"scsi"/"sata")
* @return the index
*/
public static int getCdDeviceIndex(String cdInterface) {
switch (cdInterface) {
case "scsi":
case "ide":
case "sata":
return 2;
default:
return -1;
}
}
/**
* Get unit/slot number reserved by VDSM for payload.
*
* @param cdInterface name of the interface ("ide"/"scsi"/"sata")
* @return the index
*/
public static int getCdPayloadDeviceIndex(String cdInterface) {
switch (cdInterface) {
case "scsi":
return 1;
case "ide":
return 3;
case "sata":
return 1;
default:
return -1;
}
}
public static Integer getSizeOfMemoryDeviceMb(VmDevice memoryDevice) {
if (memoryDevice.getType() != VmDeviceGeneralType.MEMORY) {
throw new RuntimeException("Memory device expected but device "
+ memoryDevice
+ " passed of type "
+ memoryDevice.getType());
}
return (Integer) memoryDevice.getSpecParams().get(SPEC_PARAM_SIZE);
}
}