package org.ovirt.engine.core.common.businessentities; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.compat.StringHelper; import org.ovirt.engine.core.compat.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents paravirtualized rng device. * The device is determined by 3 parameters * - bytes - determines how many bytes are permitted to be consumed per period, * - period - duration of a period in milliseconds, * - source - determines the backend for the device. * For more information about RNG device see libvirt documentation. */ public class VmRngDevice extends VmDevice implements Serializable { private static Logger log = LoggerFactory.getLogger(VmRngDevice.class); /** Enum representing source for RNG device backend. */ public enum Source { /** /dev/random device is used as a backend; used before {@link #FIRST_URANDOM_VERSION} */ RANDOM, /** /dev/urandom used since {@link #FIRST_URANDOM_VERSION} as replacement of {@link #RANDOM} */ URANDOM, /** /dev/hwrng device (usually specialized HW generator) is used as a backend. */ HWRNG; public static final Version FIRST_URANDOM_VERSION = Version.v4_1; public static Source getUrandomOrRandomFor(Version version) { if (version.greaterOrEquals(FIRST_URANDOM_VERSION)) { return URANDOM; } return RANDOM; } /** * @param oldVersion old compatibility version * @param newVersion new compatibility version * @return whether or not update of random/urandom rng device is required; * false if some of the arguments is null */ public static boolean urandomRandomUpdateRequired(Version oldVersion, Version newVersion) { if (Objects.equals(oldVersion, newVersion)) { return false; } if (oldVersion == null || newVersion == null) { return false; } return (newVersion.greaterOrEquals(VmRngDevice.Source.FIRST_URANDOM_VERSION) && oldVersion.less(VmRngDevice.Source.FIRST_URANDOM_VERSION)) || (oldVersion.greaterOrEquals(VmRngDevice.Source.FIRST_URANDOM_VERSION) && newVersion.less(VmRngDevice.Source.FIRST_URANDOM_VERSION)); } } public static Set<Source> csvToSourcesSet(String csvSources) { Set<Source> result = new HashSet<>(); if (csvSources != null) { for (String chunk : csvSources.split(",")) { if (!StringHelper.isNullOrEmpty(chunk)) { try { Source src = Source.valueOf(chunk); if (src != null) { result.add(src); } } catch (IllegalArgumentException ex) { log.warn("Unknown RNG source '{}'", chunk); } } } } return result; } public static String sourcesToCsv(Collection<Source> sources) { if (sources == null) { return ""; } StringBuilder resultBuilder = new StringBuilder(""); for (Source source : sources) { resultBuilder.append(source.name()); resultBuilder.append(","); } if (resultBuilder.length() > 0) { resultBuilder.deleteCharAt(resultBuilder.length() - 1); } return resultBuilder.toString(); } public static final String BYTES_STRING = "bytes"; public static final String PERIOD_STRING = "period"; public static final String SOURCE_STRING = "source"; public VmRngDevice() { this(new VmDeviceId(null, null), createSpecPars(null, null, Source.URANDOM)); } public VmRngDevice(VmDevice dev) { this(dev.getId(), dev.getSpecParams()); } public VmRngDevice(VmDeviceId id, Map<String, Object> specPars) { super(); setId(id); setDevice(VmDeviceType.VIRTIO.getName()); setType(VmDeviceGeneralType.RNG); setAddress(""); setPlugged(true); setManaged(true); setSpecParams(specPars); } public void updateSourceByVersion(Version clusterVersion) { final Source source = getSource(); if (source == Source.URANDOM || source == Source.RANDOM) { setSource(Source.getUrandomOrRandomFor(clusterVersion)); } } private static Map<String, Object> createSpecPars(Integer bytes, Integer period, Source source) { Map<String, Object> result = new HashMap<>(); if (bytes != null) { result.put(BYTES_STRING, bytes.toString()); } if (period != null) { result.put(PERIOD_STRING, period.toString()); } result.put(SOURCE_STRING, source.name().toLowerCase()); return result; } public Integer getBytes() { return integerOrNull(getSpecParams().get(BYTES_STRING)); } public void setBytes(Integer bytes) { if (bytes == null) { getSpecParams().remove(BYTES_STRING); } else { getSpecParams().put(BYTES_STRING, bytes.toString()); } } public Integer getPeriod() { return integerOrNull(getSpecParams().get(PERIOD_STRING)); } public void setPeriod(Integer period) { if (period == null) { getSpecParams().remove(PERIOD_STRING); } else { getSpecParams().put(PERIOD_STRING, period.toString()); } } public Source getSource() { try { return Source.valueOf(((String) getSpecParams().get(SOURCE_STRING)).toUpperCase()); } catch (Exception e) { return Source.URANDOM; } } public void setSource(Source source) { if (source == null) { getSpecParams().put(SOURCE_STRING, Source.URANDOM.name().toLowerCase()); } else { getSpecParams().put(SOURCE_STRING, source.name().toLowerCase()); } } private Integer integerOrNull(Object o) { if (o instanceof Integer) { return (Integer) o; } else if (o instanceof String) { try { return Integer.parseInt((String) o); } catch (Exception ignore) { } } return null; } }