content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.hypervisor.kvm.resource;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
  import org.apache.cloudstack.utils.qemu.QemuObject;
  import org.apache.commons.lang.StringEscapeUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import com.cloud.agent.properties.AgentProperties;
  import com.cloud.agent.properties.AgentPropertiesFileHandler;
  
  public class LibvirtVMDef {
      protected static Logger LOGGER = LogManager.getLogger(LibvirtVMDef.class);
  
      private String _hvsType;
      private static long s_libvirtVersion;
      private static long s_qemuVersion;
      private String _domName;
      private String _domUUID;
      private String _desc;
      private String _platformEmulator;
      private final Map<String, Object> components = new HashMap<String, Object>();
      private static final int NUMBER_OF_IOTHREADS = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.IOTHREADS);
  
      public static class GuestDef {
          enum GuestType {
              KVM, XEN, EXE, LXC
          }
  
          enum BootOrder {
              HARDISK("hd"), CDROM("cdrom"), FLOPPY("fd"), NETWORK("network");
              String _order;
  
              BootOrder(String order) {
                  _order = order;
              }
  
              @Override
              public String toString() {
                  return _order;
              }
          }
  
          public enum BootType {
              UEFI("UEFI"), BIOS("BIOS");
  
              String _type;
  
              BootType(String type) {
                  _type = type;
              }
  
              @Override
              public String toString() {
                  return _type;
              }
          }
  
          public enum BootMode {
              LEGACY("LEGACY"), SECURE("SECURE");
  
              String _mode;
  
              BootMode(String mode) {
                  _mode = mode;
              }
  
              @Override
              public String toString() {
                  return _mode;
              }
          }
  
          private GuestType _type;
          private String manufacturer;
          private String product;
          private BootType _boottype;
          private BootMode _bootmode;
          private String _arch;
          private String _loader;
          private String _kernel;
          private String _initrd;
          private String _root;
          private String _cmdline;
          private String _uuid;
          private final List<BootOrder> _bootdevs = new ArrayList<BootOrder>();
          private String _machine;
          private String _nvram;
          private String _nvramTemplate;
          private boolean iothreads;
  
          public static final String GUEST_LOADER_SECURE = "guest.loader.secure";
          public static final String GUEST_LOADER_LEGACY = "guest.loader.legacy";
          public static final String GUEST_NVRAM_PATH = "guest.nvram.path";
          public static final String GUEST_NVRAM_TEMPLATE_SECURE = "guest.nvram.template.secure";
          public static final String GUEST_NVRAM_TEMPLATE_LEGACY = "guest.nvram.template.legacy";
  
          public void setGuestType(GuestType type) {
              _type = type;
          }
  
          public GuestType getGuestType() {
              return _type;
          }
  
          public String getManufacturer() {
              if (StringUtils.isEmpty(manufacturer)) {
                  return "Apache Software Foundation";
              }
              return manufacturer;
          }
  
          public void setManufacturer(String manufacturer) {
              this.manufacturer = manufacturer;
          }
  
          public String getProduct() {
              if (StringUtils.isEmpty(product)) {
                  return "CloudStack KVM Hypervisor";
              }
              return product;
          }
  
          public void setProduct(String product) {
              this.product = product;
          }
  
          public void setNvram(String nvram) { _nvram = nvram; }
  
          public void setNvramTemplate(String nvramTemplate) { _nvramTemplate = nvramTemplate; }
  
          public void setGuestArch(String arch) {
              _arch = arch;
          }
  
          public void setMachineType(String machine) {
              _machine = machine;
          }
  
          public void setLoader(String loader) {
              _loader = loader;
          }
  
          public void setBootKernel(String kernel, String initrd, String rootdev, String cmdline) {
              _kernel = kernel;
              _initrd = initrd;
              _root = rootdev;
              _cmdline = cmdline;
          }
  
          public void setBootOrder(BootOrder order) {
              _bootdevs.add(order);
          }
  
          public void setUuid(String uuid) {
              _uuid = uuid;
          }
  
          public BootType getBootType() {
              return _boottype;
          }
  
          public void setBootType(BootType boottype) {
              this._boottype = boottype;
          }
  
          public BootMode getBootMode() {
              return _bootmode;
          }
  
          public void setBootMode(BootMode bootmode) {
              this._bootmode = bootmode;
          }
  
          public void setIothreads(boolean iothreads) {
              this.iothreads = iothreads;
          }
  
          @Override
          public String toString() {
              if (_type == GuestType.KVM) {
                  StringBuilder guestDef = new StringBuilder();
  
                  guestDef.append("<sysinfo type='smbios'>\n");
                  guestDef.append("<system>\n");
                  guestDef.append("<entry name='manufacturer'>" + getManufacturer() +"</entry>\n");
                  guestDef.append("<entry name='product'>" + getProduct() + "</entry>\n");
                  guestDef.append("<entry name='uuid'>" + _uuid + "</entry>\n");
                  guestDef.append("<entry name='serial'>" + _uuid + "</entry>\n");
                  guestDef.append("</system>\n");
                  guestDef.append("</sysinfo>\n");
  
                  guestDef.append("<os>\n");
                  guestDef.append("<type ");
                  if (_arch != null) {
                      guestDef.append(" arch='" + _arch + "'");
                  }
                  if (_machine != null) {
                      guestDef.append(" machine='" + _machine + "'");
                  }
                  guestDef.append(">hvm</type>\n");
                  if (_arch != null && _arch.equals("aarch64")) {
                      guestDef.append("<loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader>\n");
                  }
                  if (_loader != null) {
                      if (_bootmode == BootMode.LEGACY) {
                          guestDef.append("<loader readonly='yes' secure='no' type='pflash'>" + _loader + "</loader>\n");
                      } else if (_bootmode == BootMode.SECURE) {
                          guestDef.append("<loader readonly='yes' secure='yes' type='pflash'>" + _loader + "</loader>\n");
                      }
                  }
                  if (_nvram != null) {
                      guestDef.append("<nvram ");
                      if (_nvramTemplate != null) {
                          guestDef.append("template='" + _nvramTemplate + "'>");
                      } else {
                          guestDef.append(">");
                      }
  
                      guestDef.append(_nvram);
                      guestDef.append(_uuid + ".fd</nvram>");
                  }
                  if (!_bootdevs.isEmpty()) {
                      for (BootOrder bo : _bootdevs) {
                          guestDef.append("<boot dev='" + bo + "'/>\n");
                      }
                  }
                  guestDef.append("<smbios mode='sysinfo'/>\n");
                  guestDef.append("</os>\n");
                  if (iothreads) {
                      guestDef.append(String.format("<iothreads>%s</iothreads>", NUMBER_OF_IOTHREADS));
                  }
                  return guestDef.toString();
              } else if (_type == GuestType.LXC) {
                  StringBuilder guestDef = new StringBuilder();
                  guestDef.append("<os>\n");
                  guestDef.append("<type>exe</type>\n");
                  guestDef.append("<init>/sbin/init</init>\n");
                  guestDef.append("</os>\n");
                  return guestDef.toString();
              } else {
                  return null;
              }
          }
      }
  
      public static class GuestResourceDef {
          private long memory;
          private long currentMemory = -1;
          private int vcpu = -1;
          private int maxVcpu = -1;
          private boolean memoryBalloning = false;
          private int memoryBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
  
          public void setMemorySize(long mem) {
              this.memory = mem;
          }
  
          public void setCurrentMem(long currMem) {
              this.currentMemory = currMem;
          }
  
          public void setVcpuNum(int vcpu) {
              this.vcpu = vcpu;
          }
  
          public void setMaxVcpuNum(int maxVcpu) {
              this.maxVcpu = maxVcpu;
          }
  
          public int getVcpu() {
              return vcpu;
          }
  
          public int getMaxVcpu() {
              return maxVcpu;
          }
  
          public void setMemBalloning(boolean memoryBalloning) {
              this.memoryBalloning = memoryBalloning;
          }
  
          @Override
          public String toString() {
              StringBuilder response = new StringBuilder();
              response.append(String.format("<memory>%s</memory>\n", this.currentMemory));
              response.append(String.format("<currentMemory>%s</currentMemory>\n", this.currentMemory));
  
              if (this.memory > this.currentMemory) {
                  response.append(String.format("<maxMemory slots='16' unit='KiB'>%s</maxMemory>\n", this.memory));
                  response.append(String.format("<cpu> <numa> <cell id='0' cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, this.currentMemory));
              }
  
              MemBalloonDef memBalloonDef = new MemBalloonDef();
              if (this.memoryBalloning) {
                  memBalloonDef.defVirtioMemBalloon(String.valueOf(memoryBalloonStatsPeriod));
              } else {
                  memBalloonDef.defNoneMemBalloon();
              }
              response.append(String.format("<devices>%n%s%n</devices>%n", memBalloonDef.toString()));
  
              response.append(String.format("<vcpu current=\"%s\">%s</vcpu>\n", this.vcpu, this.maxVcpu));
              return response.toString();
          }
      }
  
      public static class HyperVEnlightenmentFeatureDef {
          enum Enlight {
              RELAX("relaxed"),
              VAPIC("vapic"),
              SPIN("spinlocks");
  
              private final String featureName;
              Enlight(String featureName) { this.featureName = featureName; }
              String getFeatureName() { return featureName; }
  
              static boolean isValidFeature(String featureName) {
                  Enlight[] enlights = Enlight.values();
                  for(Enlight e : enlights) {
                      if(e.getFeatureName().equals(featureName))
                          return true;
                  }
                  return false;
              }
          }
  
          private final Map<String, String> features = new HashMap<String, String>();
          private int retries = 4096; // set to sane default
  
          public void setFeature(String feature, boolean on) {
              if(on && Enlight.isValidFeature(feature))
                  setFeature(feature);
          }
  
          private void setFeature(String feature) {
              features.put(feature, "on");
          }
  
          public void setRetries(int retry) {
              if(retry>=retries)
                  retries=retry;
          }
  
          public int getRetries() {
              return retries;
          }
  
          @Override
          public String toString() {
              StringBuilder feaBuilder = new StringBuilder();
              feaBuilder.append("<hyperv>\n");
              for (Map.Entry<String, String> e : features.entrySet()) {
                  feaBuilder.append("<");
                  feaBuilder.append(e.getKey());
  
                  if(e.getKey().equals("spinlocks"))  feaBuilder.append(" state='" + e.getValue() + "' retries='" + getRetries() + "'");
                  else                                feaBuilder.append(" state='" + e.getValue() + "'");
  
                  feaBuilder.append("/>\n");
              }
              feaBuilder.append("</hyperv>\n");
              return feaBuilder.toString();
          }
      }
  
      public static class FeaturesDef {
          private final List<String> _features = new ArrayList<String>();
  
          private HyperVEnlightenmentFeatureDef hyperVEnlightenmentFeatureDef = null;
          public void addFeatures(String feature) {
              _features.add(feature);
          }
  
          public void addHyperVFeature(HyperVEnlightenmentFeatureDef hyperVEnlightenmentFeatureDef) {
              this.hyperVEnlightenmentFeatureDef = hyperVEnlightenmentFeatureDef;
          }
  
          @Override
          public String toString() {
              StringBuilder feaBuilder = new StringBuilder();
              feaBuilder.append("<features>\n");
              for (String feature : _features) {
                  if (feature.equalsIgnoreCase("smm")) {
                      feaBuilder.append("<" + feature + " state=\'on\' " + "/>\n");
                  } else {
                      feaBuilder.append("<" + feature + "/>\n");
                  }
              }
              if (hyperVEnlightenmentFeatureDef != null) {
                  String hpervF = hyperVEnlightenmentFeatureDef.toString();
                  if (!hpervF.isEmpty()) {
                      feaBuilder.append(hpervF);
                  }
              }
              feaBuilder.append("</features>\n");
              return feaBuilder.toString();
          }
      }
  
      public static class TermPolicy {
          private String _reboot;
          private String _powerOff;
          private String _crash;
  
          public TermPolicy() {
              _reboot = _powerOff = _crash = "destroy";
          }
  
          public void setRebootPolicy(String rbPolicy) {
              _reboot = rbPolicy;
          }
  
          public void setPowerOffPolicy(String poPolicy) {
              _powerOff = poPolicy;
          }
  
          public void setCrashPolicy(String crashPolicy) {
              _crash = crashPolicy;
          }
  
          @Override
          public String toString() {
              StringBuilder term = new StringBuilder();
              term.append("<on_reboot>" + _reboot + "</on_reboot>\n");
              term.append("<on_poweroff>" + _powerOff + "</on_poweroff>\n");
              term.append("<on_crash>" + _powerOff + "</on_crash>\n");
              return term.toString();
          }
      }
  
      public static class ClockDef {
          public enum ClockOffset {
              UTC("utc"), LOCALTIME("localtime"), TIMEZONE("timezone"), VARIABLE("variable");
  
              private String _offset;
  
              private ClockOffset(String offset) {
                  _offset = offset;
              }
  
              @Override
              public String toString() {
                  return _offset;
              }
          }
  
          private ClockOffset _offset;
          private String _timerName;
          private String _tickPolicy;
          private String _track;
          private boolean _noKvmClock = false;
  
          public ClockDef() {
              _offset = ClockOffset.UTC;
          }
  
          public void setClockOffset(ClockOffset offset) {
              _offset = offset;
          }
  
          public void setTimer(String timerName, String tickPolicy, String track) {
              _timerName = timerName;
              _tickPolicy = tickPolicy;
              _track = track;
          }
  
          public void setTimer(String timerName, String tickPolicy, String track, boolean noKvmClock) {
              _noKvmClock = noKvmClock;
              setTimer(timerName, tickPolicy, track);
          }
  
          @Override
          public String toString() {
              StringBuilder clockBuilder = new StringBuilder();
              clockBuilder.append("<clock offset='");
              clockBuilder.append(_offset.toString());
              clockBuilder.append("'>\n");
              if (_timerName != null) {
                  clockBuilder.append("<timer name='");
                  clockBuilder.append(_timerName);
                  clockBuilder.append("' ");
  
                  if (_timerName.equals("kvmclock") && _noKvmClock) {
                      clockBuilder.append("present='no' />");
                  } else if (_timerName.equals("hypervclock")) {
                      clockBuilder.append("present='yes' />");
                  } else {
                      if (_tickPolicy != null) {
                          clockBuilder.append("tickpolicy='");
                          clockBuilder.append(_tickPolicy);
                          clockBuilder.append("' ");
                      }
  
                      if (_track != null) {
                          clockBuilder.append("track='");
                          clockBuilder.append(_track);
                          clockBuilder.append("' ");
                      }
  
                      clockBuilder.append(">\n");
                      clockBuilder.append("</timer>\n");
                  }
              }
              clockBuilder.append("</clock>\n");
              return clockBuilder.toString();
          }
      }
  
      public static class DevicesDef {
          private String _emulator;
          private GuestDef.GuestType _guestType;
          private final Map<String, List<?>> devices = new HashMap<String, List<?>>();
  
          public boolean addDevice(Object device) {
              Object dev = devices.get(device.getClass().toString());
              if (dev == null) {
                  List<Object> devs = new ArrayList<Object>();
                  devs.add(device);
                  devices.put(device.getClass().toString(), devs);
              } else {
                  List<Object> devs = (List<Object>)dev;
                  devs.add(device);
              }
              return true;
          }
  
          public void setEmulatorPath(String emulator) {
              _emulator = emulator;
          }
  
          public void setGuestType(GuestDef.GuestType guestType) {
              _guestType = guestType;
          }
  
          @Override
          public String toString() {
              StringBuilder devicesBuilder = new StringBuilder();
              devicesBuilder.append("<devices>\n");
              if (_emulator != null) {
                  devicesBuilder.append("<emulator>" + _emulator + "</emulator>\n");
              }
  
              for (List<?> devs : devices.values()) {
                  for (Object dev : devs) {
                      if (_guestType == GuestDef.GuestType.LXC) {
                          if (dev instanceof GraphicDef || dev instanceof InputDef) {
                              continue;
                          }
                          if(dev instanceof DiskDef){
                              DiskDef disk = (DiskDef)dev;
                              if(!disk.getDiskType().toString().equals("block")){
                                  continue;
                              }
                          }
                      }
                      devicesBuilder.append(dev.toString());
                  }
              }
  
              if (_emulator != null && (_emulator.endsWith("aarch64") || _emulator.endsWith("s390x"))) {
                  devicesBuilder.append("<controller type='pci' model='pcie-root'/>\n");
                  for (int i = 0; i < 32; i++) {
                    devicesBuilder.append("<controller type='pci' model='pcie-root-port'/>\n");
                  }
                  devicesBuilder.append("<controller type='pci' model='pcie-to-pci-bridge'/>\n");
              }
  
              devicesBuilder.append("</devices>\n");
              return devicesBuilder.toString();
          }
  
          @SuppressWarnings("unchecked")
          public List<DiskDef> getDisks() {
              return (List<DiskDef>)devices.get(DiskDef.class.toString());
          }
  
          @SuppressWarnings("unchecked")
          public List<InterfaceDef> getInterfaces() {
              return (List<InterfaceDef>)devices.get(InterfaceDef.class.toString());
          }
  
      }
  
      public static class DiskDef {
  
          public static class LibvirtDiskEncryptDetails {
              String passphraseUuid;
              QemuObject.EncryptFormat encryptFormat;
  
              public LibvirtDiskEncryptDetails(String passphraseUuid, QemuObject.EncryptFormat encryptFormat) {
                  this.passphraseUuid = passphraseUuid;
                  this.encryptFormat = encryptFormat;
              }
  
              public String getPassphraseUuid() { return this.passphraseUuid; }
              public QemuObject.EncryptFormat getEncryptFormat() { return this.encryptFormat; }
          }
  
          public static class DiskGeometry {
              int cylinders;
              int heads;
              int sectors;
  
              public DiskGeometry(int cylinders, int heads, int sectors) {
                  this.cylinders = cylinders;
                  this.heads = heads;
                  this.sectors = sectors;
              }
  
              public String toXml() {
                  return String.format("<geometry cyls='%d' heads='%d' secs='%d'/>\n", this.cylinders, this.heads, this.sectors);
              }
          }
  
          public enum DeviceType {
              FLOPPY("floppy"), DISK("disk"), CDROM("cdrom"), LUN("lun");
              String _type;
  
              DeviceType(String type) {
                  _type = type;
              }
  
              @Override
              public String toString() {
                  return _type;
              }
          }
  
          public enum DiskType {
              FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network");
              String _diskType;
  
              DiskType(String type) {
                  _diskType = type;
              }
  
              @Override
              public String toString() {
                  return _diskType;
              }
          }
  
          public enum DiskProtocol {
              RBD("rbd"), SHEEPDOG("sheepdog"), GLUSTER("gluster");
              String _diskProtocol;
  
              DiskProtocol(String protocol) {
                  _diskProtocol = protocol;
              }
  
              @Override
              public String toString() {
                  return _diskProtocol;
              }
          }
  
          public enum DiskBus {
              IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML("uml"), FDC("fdc"), SATA("sata"), VIRTIOBLK("virtio-blk");
              String _bus;
  
              DiskBus(String bus) {
                  _bus = bus;
              }
  
              @Override
              public String toString() {
                  return _bus;
              }
          }
  
          public enum DiskFmtType {
              RAW("raw"), QCOW2("qcow2");
              String _fmtType;
  
              DiskFmtType(String fmt) {
                  _fmtType = fmt;
              }
  
              @Override
              public String toString() {
                  return _fmtType;
              }
          }
  
          public enum DiskCacheMode {
              NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough"), HYPERVISOR_DEFAULT("default");
              String _diskCacheMode;
  
              DiskCacheMode(String cacheMode) {
                  _diskCacheMode = cacheMode;
              }
  
              @Override
              public String toString() {
                  if (_diskCacheMode == null) {
                      return "NONE";
                  }
                  return _diskCacheMode;
              }
          }
  
          public enum DiscardType {
              IGNORE("ignore"), UNMAP("unmap");
              String _discardType;
              DiscardType(String discardType) {
                  _discardType = discardType;
              }
  
              @Override
              public String toString() {
                  if (_discardType == null) {
                      return "ignore";
                  }
                  return _discardType;
              }
  
          }
  
          public enum BlockIOSize {
              SIZE_512("512"), SIZE_4K("4096");
              final String blockSize;
  
              BlockIOSize(String size) { this.blockSize = size; }
  
              @Override
              public String toString() {
                  return blockSize;
              }
          }
  
          private DeviceType _deviceType; /* floppy, disk, cdrom */
          private DiskType _diskType;
          private DiskProtocol _diskProtocol;
          private String _sourcePath;
          private String _sourceHost;
          private int _sourcePort;
          private String _authUserName;
          private String _authSecretUUID;
          private String _diskLabel;
          private DiskBus _bus;
          private DiskFmtType _diskFmtType; /* qcow2, raw etc. */
          private boolean _readonly = false;
          private boolean _shareable = false;
          private boolean _deferAttach = false;
          private Long _bytesReadRate;
          private Long _bytesReadRateMax;
          private Long _bytesReadRateMaxLength;
          private Long _bytesWriteRate;
          private Long _bytesWriteRateMax;
          private Long _bytesWriteRateMaxLength;
          private Long _iopsReadRate;
          private Long _iopsReadRateMax;
          private Long _iopsReadRateMaxLength;
          private Long _iopsWriteRate;
          private Long _iopsWriteRateMax;
          private Long _iopsWriteRateMaxLength;
          private DiskCacheMode _diskCacheMode;
          private String _serial;
          private boolean qemuDriver = true;
          private DiscardType _discard = DiscardType.IGNORE;
          private IoDriverPolicy ioDriver;
          private LibvirtDiskEncryptDetails encryptDetails;
          private boolean isIothreadsEnabled;
          private BlockIOSize logicalBlockIOSize = null;
          private BlockIOSize physicalBlockIOSize = null;
          private DiskGeometry geometry = null;
  
          public DiscardType getDiscard() {
              return _discard;
          }
  
          public void setDiscard(DiscardType discard) {
              this._discard = discard;
          }
  
          public IoDriverPolicy getIoDriver() {
              return ioDriver;
          }
  
          public void setIoDriver(IoDriverPolicy ioDriver) {
              this.ioDriver = ioDriver;
          }
  
          public void setDeviceType(DeviceType deviceType) {
              _deviceType = deviceType;
          }
  
          public void isIothreadsEnabled(boolean isIothreadsEnabled) {
              this.isIothreadsEnabled = isIothreadsEnabled;
          }
  
          public void setPhysicalBlockIOSize(BlockIOSize size) { this.physicalBlockIOSize = size; }
  
          public void setLogicalBlockIOSize(BlockIOSize size) { this.logicalBlockIOSize = size; }
  
          public void defFileBasedDisk(String filePath, String diskLabel, DiskBus bus, DiskFmtType diskFmtType) {
              _diskType = DiskType.FILE;
              _deviceType = DeviceType.DISK;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = filePath;
              _diskLabel = diskLabel;
              _diskFmtType = diskFmtType;
              _bus = bus;
  
          }
  
          /* skip iso labels */
          private String getDevLabel(int devId, DiskBus bus, boolean forIso) {
              if (devId < 0) {
                  return "";
              }
  
              if (bus == DiskBus.SCSI) {
                  return "sd" + getDevLabelSuffix(devId);
              } else if (bus == DiskBus.VIRTIO) {
                  return "vd" + getDevLabelSuffix(devId);
              } else if (bus == DiskBus.SATA){
                  return "sd" + getDevLabelSuffix(devId);
              }
              if (forIso) {
                  devId --;
              } else if(devId >= 2) {
                  devId += 2;
              }
              return "hd" + getDevLabelSuffix(devId);
  
          }
  
          private String getDevLabelSuffix(int deviceIndex) {
              if (deviceIndex < 0) {
                  return "";
              }
  
              int base = 'z' - 'a' + 1;
              String labelSuffix = "";
              do {
                  char suffix = (char)('a' + (deviceIndex % base));
                  labelSuffix = suffix + labelSuffix;
                  deviceIndex = (deviceIndex / base) - 1;
              } while (deviceIndex >= 0);
  
              return labelSuffix;
          }
  
          public void defFileBasedDisk(String filePath, int devId, DiskBus bus, DiskFmtType diskFmtType) {
  
              _diskType = DiskType.FILE;
              _deviceType = DeviceType.DISK;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = filePath;
              _diskLabel = getDevLabel(devId, bus, false);
              _diskFmtType = diskFmtType;
              _bus = bus;
  
          }
  
          public void defFileBasedDisk(String filePath, int devId, DiskFmtType diskFmtType,boolean isWindowsOS) {
  
              _diskType = DiskType.FILE;
              _deviceType = DeviceType.DISK;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = filePath;
              _diskFmtType = diskFmtType;
  
              if (isWindowsOS) {
                  _diskLabel = getDevLabel(devId, DiskBus.SATA, false); // Windows Secure VM
                  _bus = DiskBus.SATA;
              } else {
                  _diskLabel = getDevLabel(devId, DiskBus.VIRTIO, false); // Linux Secure VM
                  _bus = DiskBus.VIRTIO;
              }
          }
  
          public void defISODisk(String volPath, DiskType diskType) {
              _diskType = diskType;
              _deviceType = DeviceType.CDROM;
              _sourcePath = volPath;
              _diskLabel = getDevLabel(3, DiskBus.IDE, true);
              _diskFmtType = DiskFmtType.RAW;
              _diskCacheMode = DiskCacheMode.NONE;
              _bus = DiskBus.IDE;
          }
  
          public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) {
              _diskType = diskType;
              _deviceType = DeviceType.CDROM;
              _sourcePath = volPath;
              _bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
              _diskLabel = getDevLabel(3, _bus, true);
              _diskFmtType = DiskFmtType.RAW;
              _diskCacheMode = DiskCacheMode.NONE;
          }
  
          public void defISODisk(String volPath, Integer devId, DiskType diskType) {
              defISODisk(volPath, devId, null, diskType);
          }
  
          public void defISODisk(String volPath, Integer devId, String diskLabel, DiskType diskType) {
              if (devId == null && StringUtils.isBlank(diskLabel)) {
                  LOGGER.debug(String.format("No ID or label informed for volume [%s].", volPath));
                  defISODisk(volPath, diskType);
                  return;
              }
  
              _diskType = diskType;
              _deviceType = DeviceType.CDROM;
              _sourcePath = volPath;
  
              if (StringUtils.isNotBlank(diskLabel)) {
                  LOGGER.debug(String.format("Using informed label [%s] for volume [%s].", diskLabel, volPath));
                  _diskLabel = diskLabel;
              } else {
                  _diskLabel = getDevLabel(devId, DiskBus.IDE, true);
                  LOGGER.debug(String.format("Using device ID [%s] to define the label [%s] for volume [%s].", devId, _diskLabel, volPath));
              }
  
              _diskFmtType = DiskFmtType.RAW;
              _diskCacheMode = DiskCacheMode.NONE;
              _bus = DiskBus.IDE;
          }
  
          public void defISODisk(String volPath, Integer devId, boolean isSecure, DiskType diskType) {
              if (!isSecure) {
                  defISODisk(volPath, devId, diskType);
              } else {
                  _diskType = diskType;
                  _deviceType = DeviceType.CDROM;
                  _sourcePath = volPath;
                  _diskLabel = getDevLabel(devId, DiskBus.SATA, true);
                  _bus = DiskBus.SATA;
                  _diskFmtType = DiskFmtType.RAW;
                  _diskCacheMode = DiskCacheMode.NONE;
  
              }
          }
  
          public void defBlockBasedDisk(String diskName, int devId, DiskBus bus) {
              _diskType = DiskType.BLOCK;
              _deviceType = DeviceType.DISK;
              _diskFmtType = DiskFmtType.RAW;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = diskName;
              _diskLabel = getDevLabel(devId, bus, false);
              _bus = bus;
          }
  
          public void defBlockBasedDisk(String diskName, String diskLabel, DiskBus bus) {
              _diskType = DiskType.BLOCK;
              _deviceType = DeviceType.DISK;
              _diskFmtType = DiskFmtType.RAW;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = diskName;
              _diskLabel = diskLabel;
              _bus = bus;
          }
  
          public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort, String authUserName, String authSecretUUID, int devId, DiskBus bus,
                  DiskProtocol protocol, DiskFmtType diskFmtType) {
              _diskType = DiskType.NETWORK;
              _deviceType = DeviceType.DISK;
              _diskFmtType = diskFmtType;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = diskName;
              _sourceHost = sourceHost;
              _sourcePort = sourcePort;
              _authUserName = authUserName;
              _authSecretUUID = authSecretUUID;
              _diskLabel = getDevLabel(devId, bus, false);
              _bus = bus;
              _diskProtocol = protocol;
          }
  
          public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort, String authUserName, String authSecretUUID, String diskLabel, DiskBus bus,
                  DiskProtocol protocol, DiskFmtType diskFmtType) {
              _diskType = DiskType.NETWORK;
              _deviceType = DeviceType.DISK;
              _diskFmtType = diskFmtType;
              _diskCacheMode = DiskCacheMode.NONE;
              _sourcePath = diskName;
              _sourceHost = sourceHost;
              _sourcePort = sourcePort;
              _authUserName = authUserName;
              _authSecretUUID = authSecretUUID;
              _diskLabel = diskLabel;
              _bus = bus;
              _diskProtocol = protocol;
          }
  
          public void setReadonly() {
              _readonly = true;
          }
  
          public void setSharable() {
              _shareable = true;
          }
  
          public void setAttachDeferred(boolean deferAttach) {
              _deferAttach = deferAttach;
          }
  
          public boolean isAttachDeferred() {
              return _deferAttach;
          }
  
          public String getDiskPath() {
              return _sourcePath;
          }
  
          public String getDiskLabel() {
              return _diskLabel;
          }
  
          public void setDiskLabel(String label) {
              _diskLabel = label;
          }
  
          public DiskType getDiskType() {
              return _diskType;
          }
  
          public DeviceType getDeviceType() {
              return _deviceType;
          }
  
          public void setDiskPath(String volPath) {
              _sourcePath = volPath;
          }
  
          public DiskProtocol getDiskProtocol() {
              return _diskProtocol;
          }
  
          public DiskBus getBusType() {
              return _bus;
          }
  
          public void setBusType(DiskBus busType) {
              _bus = busType;
          }
  
          public DiskFmtType getDiskFormatType() {
              return _diskFmtType;
          }
  
          public void setDiskFormatType(DiskFmtType type) { _diskFmtType = type; }
  
          public void setBytesReadRate(Long bytesReadRate) {
              _bytesReadRate = bytesReadRate;
          }
  
          public void setBytesReadRateMax(Long bytesReadRateMax) {
              _bytesReadRateMax = bytesReadRateMax;
          }
  
          public void  setBytesReadRateMaxLength(Long bytesReadRateLength) {
              _bytesReadRateMaxLength = bytesReadRateLength;
          }
  
          public void setBytesWriteRate(Long bytesWriteRate) {
              _bytesWriteRate = bytesWriteRate;
          }
  
          public void setBytesWriteRateMax(Long bytesWriteRateMax) {
              _bytesWriteRateMax = bytesWriteRateMax;
          }
  
          public void setBytesWriteRateMaxLength(Long bytesWriteRateMaxLength) {
              _bytesWriteRateMaxLength = bytesWriteRateMaxLength;
          }
  
          public void setIopsReadRate(Long iopsReadRate) {
              _iopsReadRate = iopsReadRate;
          }
  
          public void setIopsReadRateMax(Long iopsReadRateMax) {
              _iopsReadRateMax = iopsReadRateMax;
          }
  
          public void setIopsReadRateMaxLength(Long iopsReadRateMaxLength) {
              _iopsReadRateMaxLength = iopsReadRateMaxLength;
          }
  
          public void setIopsWriteRate(Long iopsWriteRate) {
              _iopsWriteRate = iopsWriteRate;
          }
  
          public void setIopsWriteRateMax(Long iopsWriteRateMax) {
              _iopsWriteRateMax = iopsWriteRateMax;
          }
  
          public void setIopsWriteRateMaxLength(Long iopsWriteRateMaxLength) {
              _iopsWriteRateMaxLength = iopsWriteRateMaxLength;
          }
  
          public void setCacheMode(DiskCacheMode cacheMode) {
              _diskCacheMode = cacheMode;
          }
  
          public DiskCacheMode getCacheMode() {
              return _diskCacheMode;
          }
  
          public void setQemuDriver(boolean qemuDriver){
              this.qemuDriver = qemuDriver;
          }
  
          public void setSerial(String serial) {
              this._serial = serial;
          }
  
          public void setLibvirtDiskEncryptDetails(LibvirtDiskEncryptDetails details)
          {
              this.encryptDetails = details;
          }
  
          public LibvirtDiskEncryptDetails getLibvirtDiskEncryptDetails()
          {
              return this.encryptDetails;
          }
  
          public void setGeometry(DiskGeometry geometry)
          {
              this.geometry = geometry;
          }
  
          public String getSourceHost() {
              return _sourceHost;
          }
  
          public int getSourceHostPort() {
              return _sourcePort;
          }
  
          public String getSourcePath() {
              return _sourcePath;
          }
  
          @Override
          public String toString() {
              StringBuilder diskBuilder = new StringBuilder();
              diskBuilder.append("<disk ");
              if (_deviceType != null) {
                  diskBuilder.append(" device='" + _deviceType + "'");
              }
              diskBuilder.append(" type='" + _diskType + "'");
              diskBuilder.append(">\n");
              if(qemuDriver) {
                  diskBuilder.append("<driver name='qemu'" + " type='" + _diskFmtType + "' ");
  
                  if (_deviceType != DeviceType.CDROM) {
                      diskBuilder.append("cache='" + _diskCacheMode + "' ");
                  }
  
                  if(_discard != null && _discard != DiscardType.IGNORE) {
                      diskBuilder.append("discard='" + _discard.toString() + "' ");
                  }
  
                  if(ioDriver != null) {
                      diskBuilder.append(String.format("io='%s' ", ioDriver));
                  }
  
                  if (isIothreadsEnabled && _bus == DiskBus.VIRTIO) {
                      diskBuilder.append(String.format("iothread='%s' ", NUMBER_OF_IOTHREADS));
                  }
                  diskBuilder.append("/>\n");
              }
  
              if (_diskType == DiskType.FILE) {
                  diskBuilder.append("<source ");
                  if (_sourcePath != null) {
                      diskBuilder.append("file='" + _sourcePath + "'");
                  } else if (_deviceType == DeviceType.CDROM) {
                      diskBuilder.append("file=''");
                  }
                  diskBuilder.append("/>\n");
              } else if (_diskType == DiskType.BLOCK) {
                  diskBuilder.append("<source");
                  if (_sourcePath != null) {
                      diskBuilder.append(" dev='" + _sourcePath + "'");
                  }
                  diskBuilder.append("/>\n");
              } else if (_diskType == DiskType.NETWORK) {
                  diskBuilder.append("<source ");
                  diskBuilder.append(" protocol='" + _diskProtocol + "'");
                  diskBuilder.append(" name='" + _sourcePath + "'");
                  diskBuilder.append(">\n");
                  for (String sourceHost : _sourceHost.split(",")) {
                      diskBuilder.append("<host name='");
                      diskBuilder.append(sourceHost.replace("[", "").replace("]", ""));
                      if (_sourcePort != 0) {
                          diskBuilder.append("' port='");
                          diskBuilder.append(_sourcePort);
                      }
                      diskBuilder.append("'/>\n");
                  }
                  diskBuilder.append("</source>\n");
                  if (_authUserName != null) {
                      diskBuilder.append("<auth username='" + _authUserName + "'>\n");
                      diskBuilder.append("<secret type='ceph' uuid='" + _authSecretUUID + "'/>\n");
                      diskBuilder.append("</auth>\n");
                  }
              }
              diskBuilder.append("<target dev='" + _diskLabel + "'");
              if (_bus != null) {
                  diskBuilder.append(" bus='" + _bus + "'");
              }
              diskBuilder.append("/>\n");
  
              if (geometry != null) {
                  diskBuilder.append(geometry.toXml());
              }
  
              if (logicalBlockIOSize != null || physicalBlockIOSize != null) {
                  diskBuilder.append("<blockio ");
                  if (logicalBlockIOSize != null) {
                      diskBuilder.append(String.format("logical_block_size='%s' ", logicalBlockIOSize));
                  }
                  if (physicalBlockIOSize != null) {
                      diskBuilder.append(String.format("physical_block_size='%s' ", physicalBlockIOSize));
                  }
                  diskBuilder.append("/>\n");
              }
  
              if (_serial != null && !_serial.isEmpty() && _deviceType != DeviceType.LUN) {
                  diskBuilder.append("<serial>" + _serial + "</serial>\n");
              }
  
              if (encryptDetails != null) {
                  diskBuilder.append("<encryption format='" + encryptDetails.encryptFormat + "'>\n");
                  diskBuilder.append("<secret type='passphrase' uuid='" + encryptDetails.passphraseUuid + "' />\n");
                  diskBuilder.append("</encryption>\n");
              }
  
              if ((_deviceType != DeviceType.CDROM) &&
                      (s_libvirtVersion >= 9008) &&
                      (s_qemuVersion >= 1001000) &&
                      (((_bytesReadRate != null) && (_bytesReadRate > 0)) || ((_bytesWriteRate != null) && (_bytesWriteRate > 0)) ||
                              ((_iopsReadRate != null) && (_iopsReadRate > 0)) || ((_iopsWriteRate != null) && (_iopsWriteRate > 0)))) { // not CDROM, from libvirt 0.9.8 and QEMU 1.1.0
                  diskBuilder.append("<iotune>\n");
                  if ((_bytesReadRate != null) && (_bytesReadRate > 0))
                      diskBuilder.append("<read_bytes_sec>" + _bytesReadRate + "</read_bytes_sec>\n");
                  if ((_bytesWriteRate != null) && (_bytesWriteRate > 0))
                      diskBuilder.append("<write_bytes_sec>" + _bytesWriteRate + "</write_bytes_sec>\n");
                  if ((_iopsReadRate != null) && (_iopsReadRate > 0))
                      diskBuilder.append("<read_iops_sec>" + _iopsReadRate + "</read_iops_sec>\n");
                  if ((_iopsWriteRate != null) && (_iopsWriteRate > 0))
                      diskBuilder.append("<write_iops_sec>" + _iopsWriteRate + "</write_iops_sec>\n");
                  if (s_qemuVersion >= 2004000) {
                      if (_bytesReadRateMax != null && _bytesReadRateMax > 0 ) {
                          diskBuilder.append("<read_bytes_sec_max>" + _bytesReadRateMax + "</read_bytes_sec_max>\n");
                      }
                      if (_bytesWriteRateMax != null && _bytesWriteRateMax > 0) {
                          diskBuilder.append("<write_bytes_sec_max>" + _bytesWriteRateMax + "</write_bytes_sec_max>\n");
                      }
                      if (_iopsReadRateMax != null && _iopsReadRateMax > 0)
                          diskBuilder.append("<read_iops_sec_max>" + _iopsReadRateMax + "</read_iops_sec_max>\n");
                      if (_iopsWriteRateMax != null && _iopsWriteRateMax > 0)
                          diskBuilder.append("<write_iops_sec_max>" + _iopsWriteRateMax + "</write_iops_sec_max>\n");
                  }
                  if (s_qemuVersion >= 2006000) {
                      if (_bytesReadRateMaxLength != null && _bytesReadRateMaxLength > 0) {
                          diskBuilder.append("<read_bytes_sec_max_length>" + _bytesReadRateMaxLength + "</read_bytes_sec_max_length>\n");
                      }
                      if (_bytesWriteRateMaxLength != null && _bytesWriteRateMaxLength > 0) {
                          diskBuilder.append("<write_bytes_sec_max_length>" + _bytesWriteRateMaxLength + "</write_bytes_sec_max_length>\n");
                      }
                      if (_iopsReadRateMaxLength != null && _iopsReadRateMaxLength > 0)
                          diskBuilder.append("<read_iops_sec_max_length>" + _iopsReadRateMaxLength + "</read_iops_sec_max_length>\n");
                      if (_iopsWriteRateMaxLength != null && _iopsWriteRateMaxLength > 0)
                          diskBuilder.append("<write_iops_sec_max_length>" + _iopsWriteRateMaxLength + "</write_iops_sec_max_length>\n");
                  }
  
                  diskBuilder.append("</iotune>\n");
              }
  
              diskBuilder.append("</disk>\n");
              return diskBuilder.toString();
          }
      }
  
      public static class MemBalloonDef {
          private MemBalloonModel memBalloonModel;
          private String memBalloonStatsPeriod;
  
          public enum MemBalloonModel {
              NONE("none"), VIRTIO("virtio");
              String model;
  
              MemBalloonModel(String model) {
                  this.model = model;
              }
  
              @Override
              public String toString() {
                  return model;
              }
          }
  
          public void defNoneMemBalloon() {
              memBalloonModel = MemBalloonModel.NONE;
          }
  
          public void defVirtioMemBalloon(String period) {
              memBalloonModel = MemBalloonModel.VIRTIO;
              memBalloonStatsPeriod = period;
          }
  
          public MemBalloonModel getMemBalloonModel() {
              return memBalloonModel;
          }
  
          public String getMemBalloonStatsPeriod() {
              return memBalloonStatsPeriod;
          }
  
          @Override
          public String toString() {
              StringBuilder memBalloonBuilder = new StringBuilder();
              memBalloonBuilder.append("<memballoon model='" + memBalloonModel + "'>\n");
              if (StringUtils.isNotBlank(memBalloonStatsPeriod)) {
                  memBalloonBuilder.append("<stats period='" + memBalloonStatsPeriod +"'/>\n");
              }
              memBalloonBuilder.append("</memballoon>");
              return memBalloonBuilder.toString();
          }
      }
  
      public static class InterfaceDef {
          public enum GuestNetType {
              BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"), USER("user"), ETHERNET("ethernet"), INTERNAL("internal"), VHOSTUSER("vhostuser");
              String _type;
  
              GuestNetType(String type) {
                  _type = type;
              }
  
              @Override
              public String toString() {
                  return _type;
              }
          }
  
          public enum NicModel {
              E1000("e1000"), VIRTIO("virtio"), RTL8139("rtl8139"), NE2KPCI("ne2k_pci"), VMXNET3("vmxnet3");
              String _model;
  
              NicModel(String model) {
                  _model = model;
              }
  
              @Override
              public String toString() {
                  return _model;
              }
          }
  
          enum HostNicType {
              DIRECT_ATTACHED_WITHOUT_DHCP, DIRECT_ATTACHED_WITH_DHCP, VNET, VLAN;
          }
  
          public static final int MULTI_QUEUE_NUMBER_MEANS_CPU_CORES = -1;
  
          private GuestNetType _netType; /*
           * bridge, ethernet, network, user,
           * internal, vhostuser
           */
          private HostNicType _hostNetType; /* Only used by agent java code */
          private String _netSourceMode;
          private String _sourceName;
          private String _networkName;
          private String _macAddr;
          private String _ipAddr;
          private String _scriptPath;
          private NicModel _model;
          private int _networkRateKBps;
          private String _virtualPortType;
          private String _virtualPortInterfaceId;
          private int _vlanTag = -1;
          private boolean _pxeDisable = false;
          private boolean _linkStateUp = true;
          private Integer _slot;
          private String _dpdkSourcePath;
          private String _dpdkSourcePort;
          private String _dpdkExtraLines;
          private String _interfaceMode;
          private String _userIp4Network;
          private Integer _userIp4Prefix;
          private Integer _multiQueueNumber;
          private Boolean _packedVirtQueues;
  
          public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model) {
              defBridgeNet(brName, targetBrName, macAddr, model, 0);
          }
  
          public void defUserNet(NicModel model, String macAddr, String ip4Network, Integer ip4Prefix) {
              _netType = GuestNetType.USER;
              _macAddr = macAddr;
              _userIp4Network = ip4Network;
              _userIp4Prefix = ip4Prefix;
              _model = model;
          }
  
          public void defUserNet(NicModel model, String macAddr) {
              _netType = GuestNetType.USER;
              _macAddr = macAddr;
              _model = model;
          }
  
          public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model, Integer networkRateKBps) {
              _netType = GuestNetType.BRIDGE;
              _sourceName = brName;
              _networkName = targetBrName;
              _macAddr = macAddr;
              _model = model;
              _networkRateKBps = networkRateKBps;
          }
  
          public void defDpdkNet(String dpdkSourcePath, String dpdkPort, String macAddress, NicModel model,
                                 Integer networkRateKBps, String extra, String interfaceMode) {
              _netType = GuestNetType.VHOSTUSER;
              _dpdkSourcePath = dpdkSourcePath;
              _dpdkSourcePort = dpdkPort;
              _macAddr = macAddress;
              _model = model;
              _networkRateKBps = networkRateKBps;
              _dpdkExtraLines = extra;
              _interfaceMode = interfaceMode;
          }
  
          public void defDirectNet(String sourceName, String targetName, String macAddr, NicModel model, String sourceMode) {
              defDirectNet(sourceName, targetName, macAddr, model, sourceMode, 0);
          }
  
          public void defDirectNet(String sourceName, String targetName, String macAddr, NicModel model, String sourceMode, Integer networkRateKBps) {
              _netType = GuestNetType.DIRECT;
              _netSourceMode = sourceMode;
              _sourceName = sourceName;
              _networkName = targetName;
              _macAddr = macAddr;
              _model = model;
              _networkRateKBps = networkRateKBps;
          }
  
          public void defPrivateNet(String networkName, String targetName, String macAddr, NicModel model) {
              defPrivateNet(networkName, targetName, macAddr, model, 0);
          }
  
          public void defPrivateNet(String networkName, String targetName, String macAddr, NicModel model, Integer networkRateKBps) {
              _netType = GuestNetType.NETWORK;
              _sourceName = networkName;
              _networkName = targetName;
              _macAddr = macAddr;
              _model = model;
              _networkRateKBps = networkRateKBps;
          }
  
          public void defEthernet(String targetName, String macAddr, NicModel model, String scriptPath) {
              defEthernet(targetName, macAddr, model, scriptPath, 0);
          }
  
          public void defEthernet(String targetName, String macAddr, NicModel model, String scriptPath, Integer networkRateKBps) {
              _netType = GuestNetType.ETHERNET;
              _networkName = targetName;
              _sourceName = targetName;
              _macAddr = macAddr;
              _model = model;
              _scriptPath = scriptPath;
              _networkRateKBps = networkRateKBps;
          }
  
          public void defEthernet(String targetName, String macAddr, NicModel model) {
              defEthernet(targetName, macAddr, model, null);
          }
  
          public void setHostNetType(HostNicType hostNetType) {
              _hostNetType = hostNetType;
          }
  
          public HostNicType getHostNetType() {
              return _hostNetType;
          }
  
          public void setPxeDisable(boolean pxeDisable) {
              _pxeDisable = pxeDisable;
          }
  
          public String getBrName() {
              return _sourceName;
          }
  
          public GuestNetType getNetType() {
              return _netType;
          }
  
          public String getNetSourceMode() {
              return _netSourceMode;
          }
  
          public String getDevName() {
              return _networkName;
          }
  
          public void setDevName(String networkName) {
              _networkName = networkName;
          }
  
          public String getMacAddress() {
              return _macAddr;
          }
  
          public NicModel getModel() {
              return _model;
          }
  
          public void setVirtualPortType(String virtualPortType) {
              _virtualPortType = virtualPortType;
          }
  
          public String getVirtualPortType() {
              return _virtualPortType;
          }
  
          public void setVirtualPortInterfaceId(String virtualPortInterfaceId) {
              _virtualPortInterfaceId = virtualPortInterfaceId;
          }
  
          public String getVirtualPortInterfaceId() {
              return _virtualPortInterfaceId;
          }
  
          public void setVlanTag(int vlanTag) {
              _vlanTag = vlanTag;
          }
  
          public int getVlanTag() {
              return _vlanTag;
          }
  
          public void setSlot(Integer slot) {
              _slot = slot;
          }
  
          public Integer getSlot() {
              return _slot;
          }
  
          public void setLinkStateUp(boolean linkStateUp) {
              _linkStateUp = linkStateUp;
          }
  
          public boolean isLinkStateUp() {
              return _linkStateUp;
          }
  
          public String getDpdkSourcePort() {
              return _dpdkSourcePort;
          }
          public void setDpdkSourcePort(String port) {
              _dpdkSourcePort = port;
          }
  
          public String getDpdkOvsPath() {
              return _dpdkSourcePath;
          }
  
          public void setDpdkOvsPath(String path) {
              _dpdkSourcePath = path;
          }
  
          public String getInterfaceMode() {
              return _interfaceMode;
          }
  
          public void setInterfaceMode(String mode) {
              _interfaceMode = mode;
          }
  
          public void setMultiQueueNumber(Integer multiQueueNumber) {
              this._multiQueueNumber = multiQueueNumber;
          }
  
          public void setPackedVirtQueues(Boolean packedVirtQueues) {
              this._packedVirtQueues = packedVirtQueues;
          }
  
          public String getContent() {
              StringBuilder netBuilder = new StringBuilder();
              if (_netType == GuestNetType.BRIDGE) {
                  netBuilder.append("<source bridge='" + _sourceName + "'/>\n");
              } else if (_netType == GuestNetType.NETWORK) {
                  netBuilder.append("<source network='" + _sourceName + "'/>\n");
              } else if (_netType == GuestNetType.DIRECT) {
                  netBuilder.append("<source dev='" + _sourceName + "' mode='" + _netSourceMode + "'/>\n");
              } else if (_netType == GuestNetType.VHOSTUSER) {
                  netBuilder.append("<source type='unix' path='"+ _dpdkSourcePath + _dpdkSourcePort +
                          "' mode='" + _interfaceMode + "'/>\n");
              }
  
              if (_networkName != null) {
                  netBuilder.append("<target dev='" + _networkName + "'/>\n");
              }
              if (_macAddr != null) {
                  netBuilder.append("<mac address='" + _macAddr + "'/>\n");
              }
              if (_model != null) {
                  netBuilder.append("<model type='" + _model + "'/>\n");
              }
              if (NicModel.VIRTIO.equals(_model)) {
                  boolean isMultiQueueNumberSpecified = _multiQueueNumber != null;
                  boolean isPackedVirtQueuesEnabled = _packedVirtQueues != null && _packedVirtQueues
                          && s_qemuVersion >= 4200000 && s_libvirtVersion >= 6300000;
                  if (isMultiQueueNumberSpecified || isPackedVirtQueuesEnabled) {
                      netBuilder.append("<driver");
                      if (isMultiQueueNumberSpecified) {
                          netBuilder.append(" queues='" + _multiQueueNumber + "'");
                      }
                      if (isPackedVirtQueuesEnabled) {
                          netBuilder.append(" packed='on'");
                      }
                      netBuilder.append("/>\n");
                  }
              }
              if ((s_libvirtVersion >= 9004) && (_networkRateKBps > 0)) { // supported from libvirt 0.9.4
                  netBuilder.append("<bandwidth>\n");
                  netBuilder.append("<inbound average='" + _networkRateKBps + "' peak='" + _networkRateKBps + "'/>\n");
                  netBuilder.append("<outbound average='" + _networkRateKBps + "' peak='" + _networkRateKBps + "'/>\n");
                  netBuilder.append("</bandwidth>\n");
              }
              if (_scriptPath != null) {
                  netBuilder.append("<script path='" + _scriptPath + "'/>\n");
              }
              if (_pxeDisable && !"s390x".equals(System.getProperty("os.arch"))) {
                  netBuilder.append("<rom bar='off' file=''/>");
              }
              if (_virtualPortType != null) {
                  netBuilder.append("<virtualport type='" + _virtualPortType + "'>\n");
                  if (_virtualPortInterfaceId != null) {
                      netBuilder.append("<parameters interfaceid='" + _virtualPortInterfaceId + "'/>\n");
                  }
                  netBuilder.append("</virtualport>\n");
              }
              if (_vlanTag > 0 && _vlanTag < 4095) {
                  netBuilder.append("<vlan trunk='no'>\n<tag id='" + _vlanTag + "'/>\n</vlan>");
              }
  
              if (StringUtils.isNotBlank(_dpdkExtraLines)) {
                  netBuilder.append(_dpdkExtraLines);
              }
  
              if (_netType != GuestNetType.VHOSTUSER && _netType != GuestNetType.USER) {
                  netBuilder.append("<link state='" + (_linkStateUp ? "up" : "down") +"'/>\n");
              }
  
              if (_slot  != null) {
                  netBuilder.append(String.format("<address type='pci' domain='0x0000' bus='0x00' slot='0x%02x' function='0x0'/>\n", _slot));
              }
  
              if (StringUtils.isNotBlank(_userIp4Network) && _userIp4Prefix != null) {
                  netBuilder.append(String.format("<ip family='ipv4' address='%s' prefix='%s'/>\n", _userIp4Network, _userIp4Prefix));
              }
  
              return netBuilder.toString();
          }
  
          @Override
          public String toString() {
              StringBuilder netBuilder = new StringBuilder();
              netBuilder.append("<interface type='" + _netType + "'>\n");
              netBuilder.append(getContent());
              netBuilder.append("</interface>\n");
              return netBuilder.toString();
          }
      }
  
      public static class ConsoleDef {
          private final String _ttyPath;
          private final String _type;
          private final String _source;
          private short _port = -1;
  
          public ConsoleDef(String type, String path, String source, short port) {
              _type = type;
              _ttyPath = path;
              _source = source;
              _port = port;
          }
  
          @Override
          public String toString() {
              StringBuilder consoleBuilder = new StringBuilder();
              consoleBuilder.append("<console ");
              consoleBuilder.append("type='" + _type + "'");
              if (_ttyPath != null) {
                  consoleBuilder.append("tty='" + _ttyPath + "'");
              }
              consoleBuilder.append(">\n");
              if (_source != null) {
                  consoleBuilder.append("<source path='" + _source + "'/>\n");
              }
              if (_port != -1) {
                  consoleBuilder.append("<target port='" + _port + "'/>\n");
              }
              consoleBuilder.append("</console>\n");
              return consoleBuilder.toString();
          }
      }
  
      public static class CpuTuneDef {
          private int _shares = 0;
          private int quota = 0;
          private int period = 0;
          static final int DEFAULT_PERIOD = 10000;
          static final int MIN_QUOTA = 1000;
          static final int MAX_PERIOD = 1000000;
  
          public void setShares(int shares) {
              _shares = shares;
          }
  
          public int getShares() {
              return _shares;
          }
  
          public int getQuota() {
              return quota;
          }
  
          public void setQuota(int quota) {
              this.quota = quota;
          }
  
          public int getPeriod() {
              return period;
          }
  
          public void setPeriod(int period) {
              this.period = period;
          }
  
          @Override
          public String toString() {
              StringBuilder cpuTuneBuilder = new StringBuilder();
              cpuTuneBuilder.append("<cputune>\n");
              if (_shares > 0) {
                  cpuTuneBuilder.append("<shares>" + _shares + "</shares>\n");
              }
              if (quota > 0) {
                  cpuTuneBuilder.append("<quota>" + quota + "</quota>\n");
              }
              if (period > 0) {
                  cpuTuneBuilder.append("<period>" + period + "</period>\n");
              }
              cpuTuneBuilder.append("</cputune>\n");
              return cpuTuneBuilder.toString();
          }
      }
  
      public static class CpuModeDef {
          private String _mode;
          private String _model;
          private List<String> _features;
          private int _coresPerSocket = -1;
          private int _threadsPerCore = -1;
          private int _sockets = -1;
  
          public void setMode(String mode) {
              _mode = mode;
          }
  
          public void setFeatures(List<String> features) {
              if (features != null) {
                  _features = features;
              }
          }
  
          public void setModel(String model) {
              _model = model;
          }
  
          public void setTopology(int coresPerSocket, int threadsPerCore, int sockets) {
              _coresPerSocket = coresPerSocket;
              _threadsPerCore = threadsPerCore;
              _sockets = sockets;
          }
  
          @Override
          public String toString() {
              StringBuilder modeBuilder = new StringBuilder();
  
              // start cpu def, adding mode, model
              if ("custom".equalsIgnoreCase(_mode) && _model != null){
                  modeBuilder.append("<cpu mode='custom' match='exact'><model fallback='allow'>" + _model + "</model>");
              } else if ("host-model".equals(_mode)) {
                  modeBuilder.append("<cpu mode='host-model'><model fallback='allow'></model>");
              } else if ("host-passthrough".equals(_mode)) {
                  modeBuilder.append("<cpu mode='host-passthrough'>");
              } else {
                  modeBuilder.append("<cpu>");
              }
  
              if (_features != null) {
                  for (String feature : _features) {
                      if (feature.startsWith("-")) {
                          modeBuilder.append("<feature policy='disable' name='" + feature.substring(1) + "'/>");
                      } else {
                          modeBuilder.append("<feature policy='require' name='" + feature + "'/>");
                      }
                  }
              }
  
              // add topology. Note we require sockets, cores, and threads defined
              if (_sockets > 0 && _coresPerSocket > 0 && _threadsPerCore > 0) {
                  modeBuilder.append("<topology sockets='" + _sockets + "' cores='" + _coresPerSocket + "' threads='"  + _threadsPerCore + "' />");
              }
  
              // close cpu def
              modeBuilder.append("</cpu>");
              return modeBuilder.toString();
          }
  
          public int getCoresPerSocket() {
              return _coresPerSocket;
          }
          public int getThreadsPerCore() { return _threadsPerCore; }
          public int getSockets() { return _sockets; }
      }
  
      public static class SerialDef {
          private final String _type;
          private final String _source;
          private short _port = -1;
  
          public SerialDef(String type, String source, short port) {
              _type = type;
              _source = source;
              _port = port;
          }
  
          @Override
          public String toString() {
              StringBuilder serialBuidler = new StringBuilder();
              serialBuidler.append("<serial type='" + _type + "'>\n");
              if (_source != null) {
                  serialBuidler.append("<source path='" + _source + "'/>\n");
              }
              if (_port != -1) {
                  serialBuidler.append("<target port='" + _port + "'/>\n");
              }
              serialBuidler.append("</serial>\n");
              return serialBuidler.toString();
          }
      }
  
      public static class VideoDef {
          private String _videoModel;
          private int _videoRam;
  
          public VideoDef(String videoModel, int videoRam) {
              _videoModel = videoModel;
              _videoRam = videoRam;
          }
  
          @Override
          public String toString() {
              StringBuilder videoBuilder = new StringBuilder();
              if (_videoModel != null && !_videoModel.isEmpty()){
                  videoBuilder.append("<video>\n");
                  if (_videoRam != 0) {
                      videoBuilder.append("<model type='" + _videoModel + "' vram='" + _videoRam + "'/>\n");
                  } else {
                      videoBuilder.append("<model type='" + _videoModel + "'/>\n");
                  }
                  videoBuilder.append("</video>\n");
                  return videoBuilder.toString();
              }
              return "";
          }
      }
  
      public final static class ChannelDef {
          enum ChannelType {
              UNIX("unix"), SERIAL("serial"), SPICEVMC("spicevmc");
              String type;
  
              ChannelType(String type) {
                  this.type = type;
              }
  
              @Override
              public String toString() {
                  return this.type;
              }
          }
  
          enum ChannelState {
              DISCONNECTED("disconnected"), CONNECTED("connected");
              String type;
  
              ChannelState(String type) {
                  this.type = type;
              }
  
              @Override
              public String toString() {
                  return type;
              }
          }
  
          private final String name;
          private File path = new File("");
          private final ChannelType type;
          private ChannelState state;
  
          public ChannelDef(String name, ChannelType type) {
              this.name = name;
              this.type = type;
          }
  
          public ChannelDef(String name, ChannelType type, File path) {
              this.name = name;
              this.path = path;
              this.type = type;
          }
  
          public ChannelDef(String name, ChannelType type, ChannelState state) {
              this.name = name;
              this.state = state;
              this.type = type;
          }
  
          public ChannelDef(String name, ChannelType type, ChannelState state, File path) {
              this.name = name;
              this.path = path;
              this.state = state;
              this.type = type;
          }
  
          public ChannelType getChannelType() {
              return type;
          }
  
          public ChannelState getChannelState() {
              return state;
          }
  
          public String getName() {
              return name;
          }
  
          public File getPath() {
              return path;
          }
  
          @Override
          public String toString() {
              StringBuilder virtioSerialBuilder = new StringBuilder();
              virtioSerialBuilder.append("<channel type='" + type.toString() + "'>\n");
              if (path == null) {
                  virtioSerialBuilder.append("<source mode='bind'/>\n");
              } else {
                  virtioSerialBuilder.append("<source mode='bind' path='" + path.toString() + "'/>\n");
              }
              virtioSerialBuilder.append("<address type='virtio-serial'/>\n");
              if (state == null) {
                  virtioSerialBuilder.append("<target type='virtio' name='" + name + "'/>\n");
              } else {
                  virtioSerialBuilder.append("<target type='virtio' name='" + name + "' state='" + state.toString() + "'/>\n");
              }
              virtioSerialBuilder.append("</channel>\n");
              return virtioSerialBuilder.toString();
          }
      }
  
      public static class GraphicDef {
          private final String _type;
          private short _port = -2;
          private boolean _autoPort = false;
          private final String _listenAddr;
          private final String _passwd;
          private final String _keyMap;
  
          public GraphicDef(String type, short port, boolean autoPort, String listenAddr, String passwd, String keyMap) {
              _type = type;
              _port = port;
              _autoPort = autoPort;
              _listenAddr = listenAddr;
              _passwd = StringEscapeUtils.escapeXml(passwd);
              _keyMap = keyMap;
          }
  
          @Override
          public String toString() {
              StringBuilder graphicBuilder = new StringBuilder();
              graphicBuilder.append("<graphics type='" + _type + "'");
              if (_autoPort) {
                  graphicBuilder.append(" autoport='yes'");
              } else if (_port != -2) {
                  graphicBuilder.append(" port='" + _port + "'");
              }
              if (_listenAddr != null) {
                  graphicBuilder.append(" listen='" + _listenAddr + "'");
              } else {
                  graphicBuilder.append(" listen=''");
              }
              if (_passwd != null) {
                  graphicBuilder.append(" passwd='" + StringUtils.truncate(_passwd, 8) + "'");
              } else if (_keyMap != null) {
                  graphicBuilder.append(" _keymap='" + _keyMap + "'");
              }
              graphicBuilder.append("/>\n");
              return graphicBuilder.toString();
          }
      }
  
      public static class SCSIDef {
          private short index = 0;
          private int domain = 0;
          private int bus = 0;
          private int slot = 9;
          private int function = 0;
          private int queues = 0;
          private boolean isIoThreadsEnabled;
  
          public SCSIDef(short index, int domain, int bus, int slot, int function, int queues) {
              this.index = index;
              this.domain = domain;
              this.bus = bus;
              this.slot = slot;
              this.function = function;
              this.queues = queues;
          }
  
          public SCSIDef(short index, int domain, int bus, int slot, int function, int queues, boolean ioThreadsEnabled) {
              this.index = index;
              this.domain = domain;
              this.bus = bus;
              this.slot = slot;
              this.function = function;
              this.queues = queues;
              this.isIoThreadsEnabled = ioThreadsEnabled;
          }
  
          public SCSIDef() {
  
          }
  
          @Override
          public String toString() {
              StringBuilder scsiBuilder = new StringBuilder();
  
              scsiBuilder.append(String.format("<controller type='scsi' index='%d' model='virtio-scsi'>\n", this.index));
              scsiBuilder.append(String.format("<address type='pci' domain='0x%04X' bus='0x%02X' slot='0x%02X' function='0x%01X'/>\n",
                      this.domain, this.bus, this.slot, this.function ) );
              if (this.queues > 0 || isIoThreadsEnabled) {
                  scsiBuilder.append("<driver");
                  if (queues > 0) {
                      scsiBuilder.append(String.format(" queues='%s'", queues));
                  }
                  if (isIoThreadsEnabled) {
                      scsiBuilder.append(String.format(" iothread='%s'", NUMBER_OF_IOTHREADS));
                  }
                  scsiBuilder.append("/>\n");
              }
  
              scsiBuilder.append("</controller>\n");
              return scsiBuilder.toString();
          }
      }
  
      public static class USBDef {
          private short index = 0;
          private int domain = 0;
          private int bus = 0;
          private int slot = 9;
          private int function = 0;
  
          public USBDef(short index, int domain, int bus, int slot, int function) {
              this.index = index;
              this.domain = domain;
              this.bus = bus;
              this.slot = slot;
              this.function = function;
          }
  
          public USBDef() {
          }
  
          @Override
          public String toString() {
              StringBuilder scsiBuilder = new StringBuilder();
  
              scsiBuilder.append(String.format("<controller type='usb' index='%d' model='qemu-xhci'>\n", this.index));
              scsiBuilder.append("<alias name='usb'/>");
              scsiBuilder.append(String.format("<address type='pci' domain='0x%04X' bus='0x%02X' slot='0x%02X' function='0x%01X'/>\n",
                      this.domain, this.bus, this.slot, this.function ) );
              scsiBuilder.append("</controller>\n");
              return scsiBuilder.toString();
          }
      }
  
      public static class InputDef {
          private final String _type; /* tablet, mouse */
          private final String _bus; /* ps2, usb, xen */
  
          public InputDef(String type, String bus) {
              _type = type;
              _bus = bus;
          }
  
          @Override
          public String toString() {
              StringBuilder inputBuilder = new StringBuilder();
              inputBuilder.append("<input type='" + _type + "'");
              if (_bus != null) {
                  inputBuilder.append(" bus='" + _bus + "'");
              }
              inputBuilder.append("/>\n");
              return inputBuilder.toString();
          }
      }
  
      public static class FilesystemDef {
          private final String _sourcePath;
          private final String _targetPath;
  
          public FilesystemDef(String sourcePath, String targetPath) {
              _sourcePath = sourcePath;
              _targetPath = targetPath;
          }
  
          @Override
          public String toString() {
              StringBuilder fsBuilder = new StringBuilder();
              fsBuilder.append("<filesystem type='mount'>\n");
              fsBuilder.append("  <source dir='" + _sourcePath + "'/>\n");
              fsBuilder.append("  <target dir='" + _targetPath + "'/>\n");
              fsBuilder.append("</filesystem>\n");
              return fsBuilder.toString();
          }
      }
  
      public class MetadataDef {
          Map<String, Object> customNodes = new HashMap<>();
  
          public <T> T getMetadataNode(Class<T> fieldClass) {
              T field = (T) customNodes.get(fieldClass.getName());
              if (field == null) {
                  try {
                      field = fieldClass.newInstance();
                      customNodes.put(field.getClass().getName(), field);
                  } catch (InstantiationException | IllegalAccessException e) {
                      LOGGER.debug("No default constructor available in class " + fieldClass.getName() + ", ignoring exception", e);
                  }
              }
              return field;
          }
  
          @Override
          public String toString() {
              StringBuilder fsBuilder = new StringBuilder();
              fsBuilder.append("<metadata>\n");
              for (Object field : customNodes.values()) {
                  fsBuilder.append(field.toString());
              }
              fsBuilder.append("</metadata>\n");
              return fsBuilder.toString();
          }
      }
  
      public static class RngDef {
          enum RngModel {
              VIRTIO("virtio");
              String model;
  
              RngModel(String model) {
                  this.model = model;
              }
  
              @Override
              public String toString() {
                  return model;
              }
          }
  
          enum RngBackendModel {
              RANDOM("random"), EGD("egd");
              String model;
  
              RngBackendModel(String model) {
                  this.model = model;
              }
  
              @Override
              public String toString() {
                  return model;
              }
          }
  
          private String path = "/dev/random";
          private RngModel rngModel = RngModel.VIRTIO;
          private RngBackendModel rngBackendModel = RngBackendModel.RANDOM;
          private Integer rngRateBytes = 2048;
          private Integer rngRatePeriod = 1000;
  
          public RngDef(String path) {
              this.path = path;
          }
  
          public RngDef(String path, Integer rngRateBytes, Integer rngRatePeriod) {
              this.path = path;
              this.rngRateBytes = rngRateBytes;
              this.rngRatePeriod = rngRatePeriod;
          }
  
          public RngDef(RngModel rngModel) {
              this.rngModel = rngModel;
          }
  
          public RngDef(RngBackendModel rngBackendModel) {
              this.rngBackendModel = rngBackendModel;
          }
  
          public RngDef(String path, RngBackendModel rngBackendModel) {
              this.path = path;
              this.rngBackendModel = rngBackendModel;
          }
  
          public RngDef(String path, RngBackendModel rngBackendModel, Integer rngRateBytes, Integer rngRatePeriod) {
              this.path = path;
              this.rngBackendModel = rngBackendModel;
              this.rngRateBytes = rngRateBytes;
              this.rngRatePeriod = rngRatePeriod;
          }
  
          public RngDef(String path, RngModel rngModel) {
              this.path = path;
              this.rngModel = rngModel;
          }
  
          public String getPath() {
             return path;
          }
  
          public RngBackendModel getRngBackendModel() {
              return rngBackendModel;
          }
  
          public RngModel getRngModel() {
              return rngModel;
          }
  
          public int getRngRateBytes() {
              return rngRateBytes != null ? rngRateBytes : 0;
          }
  
          public int getRngRatePeriod() {
              return rngRatePeriod != null ? rngRatePeriod : 0;
          }
  
          @Override
          public String toString() {
              StringBuilder rngBuilder = new StringBuilder();
              rngBuilder.append("<rng model='" + rngModel + "'>\n");
              rngBuilder.append("<rate period='" + rngRatePeriod + "' bytes='" + rngRateBytes + "' />\n");
              rngBuilder.append("<backend model='" + rngBackendModel + "'>" + path + "</backend>");
              rngBuilder.append("</rng>\n");
              return rngBuilder.toString();
          }
      }
  
      public static class WatchDogDef {
          enum WatchDogModel {
              I6300ESB("i6300esb"), IB700("ib700"), DIAG288("diag288"), ITCO("itco"), NONE("none");
              String model;
  
              WatchDogModel(String model) {
                  this.model = model;
              }
  
              @Override
              public String toString() {
                  return model;
              }
          }
  
          enum WatchDogAction {
              RESET("reset"), SHUTDOWN("shutdown"), POWEROFF("poweroff"), PAUSE("pause"), NONE("none"), DUMP("dump"), INJECT_NMI("inject-nmi");
              String action;
  
              WatchDogAction(String action) {
                  this.action = action;
              }
  
              @Override
              public String toString() {
                  return action;
              }
          }
  
          WatchDogModel model = WatchDogModel.I6300ESB;
          WatchDogAction action = WatchDogAction.NONE;
  
          public WatchDogDef(WatchDogAction action) {
              this.action = action;
          }
  
          public WatchDogDef(WatchDogModel model) {
              this.model = model;
          }
  
          public WatchDogDef(WatchDogAction action, WatchDogModel model) {
              this.action = action;
              this.model = model;
          }
  
          public WatchDogAction getAction() {
              return action;
          }
  
          public WatchDogModel getModel() {
              return model;
          }
  
          @Override
          public String toString() {
              if (WatchDogModel.NONE == model) {
                  // Do not add watchodogs when the model is set to none
                  return "";
              }
              StringBuilder wacthDogBuilder = new StringBuilder();
              wacthDogBuilder.append("<watchdog model='" + model + "' action='" + action + "'/>\n");
              return wacthDogBuilder.toString();
          }
      }
  
      public static class TpmDef {
          enum TpmModel {
              TIS("tpm-tis"), // TPM Interface Specification (TIS)
              CRB("tpm-crb"); // Command-Response Buffer (CRB)
  
              final String model;
  
              TpmModel(String model) {
                  this.model = model;
              }
  
              @Override
              public String toString() {
                  return model;
              }
          }
  
          enum TpmVersion {
              V1_2("1.2"),    // 1.2
              V2_0("2.0");    // 2.0. Default version. The CRB model is only supported with version 2.0.
  
              final String version;
  
              TpmVersion(String version) {
                  this.version = version;
              }
  
              @Override
              public String toString() {
                  return version;
              }
          }
  
          private TpmModel model;
          private TpmVersion version = TpmVersion.V2_0;
  
          public TpmDef(TpmModel model, TpmVersion version) {
              this.model = model;
              if (version != null) {
                  this.version = version;
              }
          }
  
          public TpmDef(String model, String version) {
              this.model = Arrays.stream(TpmModel.values())
                      .filter(tpmModel -> tpmModel.toString().equals(model))
                      .findFirst()
                      .orElse(null);
              if (version != null) {
                  this.version = Arrays.stream(TpmVersion.values())
                          .filter(tpmVersion -> tpmVersion.toString().equals(version))
                          .findFirst()
                          .orElse(this.version);;
              }
          }
  
          public TpmModel getModel() {
              return model;
          }
  
          public TpmVersion getVersion() {
              return version;
          }
  
          @Override
          public String toString() {
              StringBuilder tpmBuidler = new StringBuilder();
              if (model != null) {
                  tpmBuidler.append("<tpm model='").append(model).append("'>\n");
                  tpmBuidler.append("<backend type='emulator' version='").append(version).append("'/>\n");
                  tpmBuidler.append("</tpm>\n");
              }
              return tpmBuidler.toString();
          }
      }
  
      public void setHvsType(String hvs) {
          _hvsType = hvs;
      }
  
      public String getHvsType() {
          return _hvsType;
      }
  
      public static void setGlobalLibvirtVersion(long libvirtVersion) {
          s_libvirtVersion = libvirtVersion;
      }
  
      public void setLibvirtVersion(long libvirtVersion) {
          setGlobalLibvirtVersion(libvirtVersion);
      }
  
      public static void setGlobalQemuVersion(long qemuVersion) {
          s_qemuVersion = qemuVersion;
      }
  
      public void setQemuVersion(long qemuVersion) {
          setGlobalQemuVersion(qemuVersion);
      }
  
      public void setDomainName(String domainName) {
          _domName = domainName;
      }
  
      public void setDomUUID(String uuid) {
          _domUUID = uuid;
      }
  
      public void setDomDescription(String desc) {
          _desc = desc;
      }
  
      public String getGuestOSType() {
          return _desc;
      }
  
      public void setPlatformEmulator(String platformEmulator) {
          _platformEmulator = platformEmulator;
      }
  
      public String getPlatformEmulator() {
          return _platformEmulator;
      }
  
      public void addComp(Object comp) {
          components.put(comp.getClass().toString(), comp);
      }
  
      public DevicesDef getDevices() {
          Object o = components.get(DevicesDef.class.toString());
          if (o != null) {
              return (DevicesDef)o;
          }
          return null;
      }
  
      public MetadataDef getMetaData() {
          MetadataDef o = (MetadataDef) components.get(MetadataDef.class.toString());
          if (o == null) {
              o = new MetadataDef();
              addComp(o);
          }
          return o;
      }
  
      @Override
      public String toString() {
          StringBuilder vmBuilder = new StringBuilder();
          vmBuilder.append("<domain type='" + _hvsType + "'>\n");
          vmBuilder.append("<name>" + _domName + "</name>\n");
          if (_domUUID != null) {
              vmBuilder.append("<uuid>" + _domUUID + "</uuid>\n");
          }
          if (_desc != null) {
              vmBuilder.append("<description>" + _desc + "</description>\n");
          }
          for (Object o : components.values()) {
              vmBuilder.append(o.toString());
          }
          vmBuilder.append("</domain>\n");
          return vmBuilder.toString();
      }
  }
  