el8 fixes backport (#155)

* Extract the IO_URING configuration into the agent.properties (#6253)

When using advanced virtualization the IO Driver is not supported. The
admin will decide if want to enable/disable this configuration from
agent.properties file. The default value is true

* kvm: truncate vnc password to 8 chars (#6244)

This PR truncates the vnc password of kvm vms to 8 chars to support latest versions of libvirt.

* merge fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* [KVM] Enable IOURING only when it is available on the host (#6399)

* [KVM] Disable IOURING by default on agents

* Refactor

* Remove agent property for iouring

* Restore property

* Refactor suse check and enable on ubuntu by default

* Refactor irrespective of guest OS

* Improvement

* Logs and new path

* Refactor condition to enable iouring

* Improve condition

* Refactor property check

* Improvement

* Doc comment

* Extend comment

* Move method

* Add log

* [KVM] Fix VM migration error due to VNC password on libvirt limiting versions (#6404)

* [KVM] Fix VM migration error due to VNC password on libvirt limiting versions

* Fix passwd value

* Simplify implementation

Co-authored-by: slavkap <51903378+slavkap@users.noreply.github.com>
Co-authored-by: Wei Zhou <weizhou@apache.org>
Co-authored-by: Nicolas Vazquez <nicovazquez90@gmail.com>
diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 1f2328e..6c7dba4 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -288,3 +288,6 @@
 
 # Manually set the host CPU MHz, in cases where CPU scaling support detected value is wrong
 # host.cpu.manual.speed.mhz=0
+
+# Enable/disable IO driver for Qemu (in case it is not set CloudStack can also detect if its supported by qemu)
+# enable.io.uring=true
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index ef4f9bc..2e4bafd 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -313,6 +313,8 @@
 
     public final static String HOST_CACHE_PATH_PARAMETER = "host.cache.location";
     public final static String CONFIG_DIR = "config";
+    private boolean enableIoUring;
+    private final static String ENABLE_IO_URING_PROPERTY = "enable.io.uring";
 
     public static final String BASH_SCRIPT_PATH = "/bin/bash";
 
@@ -1138,6 +1140,12 @@
             s_logger.trace("Ignoring libvirt error.", e);
         }
 
+        // Enable/disable IO driver for Qemu (in case it is not set CloudStack can also detect if its supported by qemu)
+        // Do not remove - switching it to AgentProperties.Property may require accepting null values for the properties default value
+        String enableIoUringConfig = (String) params.get(ENABLE_IO_URING_PROPERTY);
+        enableIoUring = isIoUringEnabled(enableIoUringConfig);
+        s_logger.info("IO uring driver for Qemu: " + (enableIoUring ? "enabled" : "disabled"));
+
         final String cpuArchOverride = (String)params.get("guest.cpu.arch");
         if (!Strings.isNullOrEmpty(cpuArchOverride)) {
             _guestCpuArch = cpuArchOverride;
@@ -2973,17 +2981,67 @@
     }
 
     /**
+     * Check if IO_URING is supported by qemu
+     */
+    protected boolean isIoUringSupportedByQemu() {
+        s_logger.debug("Checking if iouring is supported");
+        String command = getIoUringCheckCommand();
+        if (org.apache.commons.lang3.StringUtils.isBlank(command)) {
+            s_logger.debug("Could not check iouring support, disabling it");
+            return false;
+        }
+        int exitValue = executeBashScriptAndRetrieveExitValue(command);
+        return exitValue == 0;
+    }
+
+    protected String getIoUringCheckCommand() {
+        String[] qemuPaths = { "/usr/bin/qemu-system-x86_64", "/usr/libexec/qemu-kvm", "/usr/bin/qemu-kvm" };
+        for (String qemuPath : qemuPaths) {
+            File file = new File(qemuPath);
+            if (file.exists()) {
+                String cmd = String.format("ldd %s | grep -Eqe '[[:space:]]liburing\\.so'", qemuPath);
+                s_logger.debug("Using the check command: " + cmd);
+                return cmd;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Set Disk IO Driver, if supported by the Libvirt/Qemu version.
      * IO Driver works for:
      * (i) Qemu >= 5.0;
      * (ii) Libvirt >= 6.3.0
      */
     protected void setDiskIoDriver(DiskDef disk) {
-        if (getHypervisorLibvirtVersion() >= HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IO_URING && getHypervisorQemuVersion() >= HYPERVISOR_QEMU_VERSION_SUPPORTS_IO_URING) {
+        if (enableIoUring) {
             disk.setIoDriver(DiskDef.IoDriver.IOURING);
         }
     }
 
+    /**
+     * IO_URING supported if the property 'enable.io.uring' is set to true OR it is supported by qemu
+     */
+    private boolean isIoUringEnabled(String enableIoUringConfig) {
+        boolean meetRequirements = getHypervisorLibvirtVersion() >= HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IO_URING
+                && getHypervisorQemuVersion() >= HYPERVISOR_QEMU_VERSION_SUPPORTS_IO_URING;
+        if (!meetRequirements) {
+            return false;
+        }
+        return enableIoUringConfig != null ?
+                Boolean.parseBoolean(enableIoUringConfig):
+                (isBaseOsUbuntu() || isIoUringSupportedByQemu());
+    }
+
+    private boolean isBaseOsUbuntu() {
+        Map<String, String> versionString = getVersionStrings();
+        String hostKey = "Host.OS";
+        if (MapUtils.isEmpty(versionString) || !versionString.containsKey(hostKey) || versionString.get(hostKey) == null) {
+            return false;
+        }
+        return versionString.get(hostKey).equalsIgnoreCase("ubuntu");
+    }
+
     private KVMPhysicalDisk getPhysicalDiskFromNfsStore(String dataStoreUrl, DataTO data) {
         final String volPath = dataStoreUrl + File.separator + data.getPath();
         final int index = volPath.lastIndexOf("/");
@@ -3847,10 +3905,20 @@
     }
 
     private String executeBashScript(final String script) {
+        return createScript(script).execute();
+    }
+
+    private Script createScript(final String script) {
         final Script command = new Script("/bin/bash", _timeout, s_logger);
         command.add("-c");
         command.add(script);
-        return command.execute();
+        return command;
+    }
+
+    private int executeBashScriptAndRetrieveExitValue(final String script) {
+        Script command = createScript(script);
+        command.execute();
+        return command.getExitValue();
     }
 
     public List<VmNetworkStatsEntry> getVmNetworkStat(Connect conn, String vmName) throws LibvirtException {
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index d8b1d4e..7c65f79 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -23,7 +23,7 @@
 import java.util.Map;
 
 import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 public class LibvirtVMDef {
@@ -1764,7 +1764,7 @@
                 graphicBuilder.append(" listen=''");
             }
             if (_passwd != null) {
-                graphicBuilder.append(" passwd='" + _passwd + "'");
+                graphicBuilder.append(" passwd='" + StringUtils.truncate(_passwd, 8) + "'");
             } else if (_keyMap != null) {
                 graphicBuilder.append(" _keymap='" + _keyMap + "'");
             }
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
index ab448df..ed3b048 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
@@ -149,7 +149,11 @@
 
             final String target = command.getDestinationIp();
             xmlDesc = dm.getXMLDesc(xmlFlag);
-            xmlDesc = replaceIpForVNCInDescFile(xmlDesc, target);
+
+            // Limit the VNC password in case the length is greater than 8 characters
+            // Since libvirt version 8 VNC passwords are limited to 8 characters
+            String vncPassword = org.apache.commons.lang3.StringUtils.truncate(to.getVncPassword(), 8);
+            xmlDesc = replaceIpForVNCInDescFileAndNormalizePassword(xmlDesc, target, vncPassword);
 
             String oldIsoVolumePath = getOldVolumePath(disks, vmName);
             String newIsoVolumePath = getNewVolumePathIfDatastoreHasChanged(libvirtComputingResource, conn, to);
@@ -450,9 +454,10 @@
      *     </graphics>
      * @param xmlDesc the qemu xml description
      * @param target the ip address to migrate to
+     * @param vncPassword if set, the VNC password truncated to 8 characters
      * @return the new xmlDesc
      */
-    String replaceIpForVNCInDescFile(String xmlDesc, final String target) {
+    String replaceIpForVNCInDescFileAndNormalizePassword(String xmlDesc, final String target, String vncPassword) {
         final int begin = xmlDesc.indexOf(GRAPHICS_ELEM_START);
         if (begin >= 0) {
             final int end = xmlDesc.lastIndexOf(GRAPHICS_ELEM_END) + GRAPHICS_ELEM_END.length();
@@ -460,6 +465,9 @@
                 String graphElem = xmlDesc.substring(begin, end);
                 graphElem = graphElem.replaceAll("listen='[a-zA-Z0-9\\.]*'", "listen='" + target + "'");
                 graphElem = graphElem.replaceAll("address='[a-zA-Z0-9\\.]*'", "address='" + target + "'");
+                if (org.apache.commons.lang3.StringUtils.isNotBlank(vncPassword)) {
+                    graphElem = graphElem.replaceAll("passwd='([^\\s]+)'", "passwd='" + vncPassword + "'");
+                }
                 xmlDesc = xmlDesc.replaceAll(GRAPHICS_ELEM_START + CONTENTS_WILDCARD + GRAPHICS_ELEM_END, graphElem);
             }
         }
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index 4106cc0..3632cf2 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -60,6 +60,7 @@
 import org.apache.cloudstack.utils.linux.MemStat;
 import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
 import org.apache.commons.lang.SystemUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.joda.time.Duration;
 import org.junit.Assert;
 import org.junit.Before;
@@ -773,7 +774,7 @@
         assertXpath(domainDoc, prefix + "/graphics/@type", "vnc");
         assertXpath(domainDoc, prefix + "/graphics/@listen", to.getVncAddr());
         assertXpath(domainDoc, prefix + "/graphics/@autoport", "yes");
-        assertXpath(domainDoc, prefix + "/graphics/@passwd", to.getVncPassword());
+        assertXpath(domainDoc, prefix + "/graphics/@passwd", StringUtils.truncate(to.getVncPassword(), 8));
     }
 
     private void verifySerialDevices(Document domainDoc, String prefix) {
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
index b5574bf..23f0ff9 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
@@ -571,16 +571,16 @@
     @Test
     public void testReplaceIpForVNCInDescFile() {
         final String targetIp = "192.168.22.21";
-        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(fullfile, targetIp);
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFileAndNormalizePassword(fullfile, targetIp, null);
         assertTrue("transformation does not live up to expectation:\n" + result, targetfile.equals(result));
     }
 
     @Test
-    public void testReplaceIpForVNCInDesc() {
+    public void testReplaceIpAndPasswordForVNCInDesc() {
         final String xmlDesc =
                 "<domain type='kvm' id='3'>" +
                 "  <devices>" +
-                "    <graphics type='vnc' port='5900' autoport='yes' listen='10.10.10.1'>" +
+                "    <graphics type='vnc' port='5900' autoport='yes' listen='10.10.10.1' passwd='123456789012345'>" +
                 "      <listen type='address' address='10.10.10.1'/>" +
                 "    </graphics>" +
                 "  </devices>" +
@@ -588,22 +588,23 @@
         final String expectedXmlDesc =
                 "<domain type='kvm' id='3'>" +
                 "  <devices>" +
-                "    <graphics type='vnc' port='5900' autoport='yes' listen='10.10.10.10'>" +
+                "    <graphics type='vnc' port='5900' autoport='yes' listen='10.10.10.10' passwd='12345678'>" +
                 "      <listen type='address' address='10.10.10.10'/>" +
                 "    </graphics>" +
                 "  </devices>" +
                 "</domain>";
         final String targetIp = "10.10.10.10";
-        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(xmlDesc, targetIp);
+        final String password = "12345678";
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFileAndNormalizePassword(xmlDesc, targetIp, password);
         assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result));
     }
 
     @Test
-    public void testReplaceFqdnForVNCInDesc() {
+    public void testReplaceFqdnAndPasswordForVNCInDesc() {
         final String xmlDesc =
                 "<domain type='kvm' id='3'>" +
                 "  <devices>" +
-                "    <graphics type='vnc' port='5900' autoport='yes' listen='localhost.local'>" +
+                "    <graphics type='vnc' port='5900' autoport='yes' listen='localhost.local' passwd='123456789012345'>" +
                 "      <listen type='address' address='localhost.local'/>" +
                 "    </graphics>" +
                 "  </devices>" +
@@ -611,13 +612,14 @@
         final String expectedXmlDesc =
                 "<domain type='kvm' id='3'>" +
                 "  <devices>" +
-                "    <graphics type='vnc' port='5900' autoport='yes' listen='localhost.localdomain'>" +
+                "    <graphics type='vnc' port='5900' autoport='yes' listen='localhost.localdomain' passwd='12345678'>" +
                 "      <listen type='address' address='localhost.localdomain'/>" +
                 "    </graphics>" +
                 "  </devices>" +
                 "</domain>";
         final String targetIp = "localhost.localdomain";
-        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(xmlDesc, targetIp);
+        final String password = "12345678";
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFileAndNormalizePassword(xmlDesc, targetIp, password);
         assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result));
     }
 
@@ -789,5 +791,4 @@
         Assert.assertTrue(replaced.contains("csdpdk-7"));
         Assert.assertFalse(replaced.contains("csdpdk-1"));
     }
-
 }