diff --git a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php index 288c1a75e..c28842002 100644 --- a/emhttp/plugins/dynamix.vm.manager/include/libvirt.php +++ b/emhttp/plugins/dynamix.vm.manager/include/libvirt.php @@ -1,5 +1,6 @@ set_logfile($debug); - if ($uri != false) { - $this->enabled = $this->connect($uri, $login, $pwd); - } + function Libvirt($uri = false, $login = false, $pwd = false, $debug=false) { + if ($debug) + $this->set_logfile($debug); + if ($uri != false) { + $this->enabled = $this->connect($uri, $login, $pwd); } + } - function __construct($uri = false, $login = false, $pwd = false, $debug=false) { - if ($debug) - $this->set_logfile($debug); - if ($uri != false) { - $this->enabled = $this->connect($uri, $login, $pwd); - } + function __construct($uri = false, $login = false, $pwd = false, $debug=false) { + if ($debug) + $this->set_logfile($debug); + if ($uri != false) { + $this->enabled = $this->connect($uri, $login, $pwd); } + } - function _set_last_error() { - $this->last_error = libvirt_get_last_error(); + function _set_last_error() { + $this->last_error = libvirt_get_last_error(); + return false; + } + + function enabled() { + return $this->enabled; + } + + function set_logfile($filename) { + if (!libvirt_logfile_set($filename,10000)) + return $this->_set_last_error(); + return true; + } + + function get_capabilities() { + $tmp = libvirt_connect_get_capabilities($this->conn); + return ($tmp) ? $tmp : $this->_set_last_error(); + } + + function get_domain_capabilities($emulatorbin, $arch, $machine, $virttype, $xpath) { + + #@conn [resource]: resource for connection + #@emulatorbin [string]: optional path to emulator + #@arch [string]: optional domain architecture + #@machine [string]: optional machine type + #@virttype [string]: optional virtualization type + #@flags [int] : extra flags; not used yet, so callers should always pass 0 + #@xpath [string]: optional xPath query to be applied on the result + #Returns: : domain capabilities XML from the connection or FALSE for error + + $tmp = libvirt_connect_get_domain_capabilities($this->conn, $emulatorbin, $arch, $machine, $virttype, 0, $xpath); + return ($tmp) ? $tmp : $this->_set_last_error(); + } + + function get_machine_types($arch = 'x86_64' /* or 'i686' */) { + $tmp = libvirt_connect_get_machine_types($this->conn); + if (!$tmp) + return $this->_set_last_error(); + + if (empty($tmp[$arch])) { + return []; + } + return $tmp[$arch]; + } + + function get_default_emulator() { + $tmp = libvirt_connect_get_capabilities($this->conn, '//capabilities/guest/arch/domain/emulator'); + return ($tmp) ? $tmp : $this->_set_last_error(); + } + + function set_folder_nodatacow($folder) { + if (!is_dir($folder)) { return false; } - function enabled() { - return $this->enabled; + $folder = transpose_user_path($folder); + + #@shell_exec("chattr +C -R ".escapeshellarg($folder)." &>/dev/null"); + return true; + } + + function create_disk_image($disk, $vmname = '', $diskid = 1) { + $arrReturn = []; + + if (!empty($disk['size'])) { + $disk['size'] = str_replace(["KB","MB","GB","TB","PB"], ["K","M","G","T","P"], strtoupper($disk['size'])); + } + if (empty($disk['driver'])) { + $disk['driver'] = 'raw'; } - function set_logfile($filename) { - if (!libvirt_logfile_set($filename,10000)) - return $this->_set_last_error(); + // if new is a folder then + // if existing then + // create folder 'new/vmname' + // create image file as new/vmname/vdisk[1-x].xxx + // if doesn't exist then + // create folder 'new' + // create image file as new/vdisk[1-x].xxx - return true; + // if new is a file then + // if existing then + // nothing to do + // if doesn't exist then + // create folder dirname('new') if needed + // create image file as new --> if size is specified + + if (!empty($disk['new'])) { + if (is_file($disk['new']) || is_block($disk['new'])) { + $disk['image'] = $disk['new']; + } } - function get_capabilities() { - $tmp = libvirt_connect_get_capabilities($this->conn); - return ($tmp) ? $tmp : $this->_set_last_error(); - } + if (!empty($disk['image'])) { + // Use existing disk image - function get_domain_capabilities($emulatorbin, $arch, $machine, $virttype, $xpath) { - - #@conn [resource]: resource for connection - #@emulatorbin [string]: optional path to emulator - #@arch [string]: optional domain architecture - #@machine [string]: optional machine type - #@virttype [string]: optional virtualization type - #@flags [int] : extra flags; not used yet, so callers should always pass 0 - #@xpath [string]: optional xPath query to be applied on the result - #Returns: : domain capabilities XML from the connection or FALSE for error - - $tmp = libvirt_connect_get_domain_capabilities($this->conn, $emulatorbin, $arch, $machine, $virttype, 0, $xpath); - return ($tmp) ? $tmp : $this->_set_last_error(); - } - - - function get_machine_types($arch = 'x86_64' /* or 'i686' */) { - $tmp = libvirt_connect_get_machine_types($this->conn); - - if (!$tmp) - return $this->_set_last_error(); - - if (empty($tmp[$arch])) - return []; - - return $tmp[$arch]; - } - - function get_default_emulator() { - $tmp = libvirt_connect_get_capabilities($this->conn, '//capabilities/guest/arch/domain/emulator'); - return ($tmp) ? $tmp : $this->_set_last_error(); - } - - function set_folder_nodatacow($folder) { - if (!is_dir($folder)) { - return false; + if (is_block($disk['image'])) { + // Valid block device, return as-is + return $disk; } - $folder = transpose_user_path($folder); + if (is_file($disk['image'])) { + $json_info = getDiskImageInfo($disk['image']); + $disk['driver'] = $json_info['format']; - #@shell_exec("chattr +C -R " . escapeshellarg($folder) . " &>/dev/null"); + if (!empty($disk['size'])) { + //TODO: expand disk image if size param is larger + } - return true; + return $disk; + } + + $disk['new'] = $disk['image']; } - function create_disk_image($disk, $vmname = '', $diskid = 1) { - $arrReturn = []; + if (!empty($disk['new'])) { + // Create new disk image + $strImgFolder = $disk['new']; + $strImgPath = ''; - if (!empty($disk['size'])) { - $disk['size'] = str_replace(["KB","MB","GB","TB","PB"], ["K","M","G","T","P"], strtoupper($disk['size'])); - } - if (empty($disk['driver'])) { - $disk['driver'] = 'raw'; + if (strpos($strImgFolder, '/dev/') === 0) { + // ERROR invalid block device + $arrReturn = [ + 'error' => "Not a valid block device location '".$strImgFolder."'" + ]; + return $arrReturn; } - // if new is a folder then - // if existing then - // create folder 'new/vmname' - // create image file as new/vmname/vdisk[1-x].xxx - // if doesn't exist then - // create folder 'new' - // create image file as new/vdisk[1-x].xxx - - // if new is a file then - // if existing then - // nothing to do - // if doesn't exist then - // create folder dirname('new') if needed - // create image file as new --> if size is specified - - if (!empty($disk['new'])) { - if (is_file($disk['new']) || is_block($disk['new'])) { - $disk['image'] = $disk['new']; - } + if (empty($disk['size'])) { + // ERROR invalid disk size + $arrReturn = [ + 'error' => "Please specify a disk size for '".$strImgFolder."'" + ]; + return $arrReturn; } - if (!empty($disk['image'])) { - // Use existing disk image + $path_parts = pathinfo($strImgFolder); + if (empty($path_parts['extension'])) { + // 'new' is a folder - if (is_block($disk['image'])) { - // Valid block device, return as-is - return $disk; + if (substr($strImgFolder, -1) != '/') { + $strImgFolder .= '/'; } - if (is_file($disk['image'])) { - $json_info = getDiskImageInfo($disk['image']); - $disk['driver'] = $json_info['format']; - - if (!empty($disk['size'])) { - //TODO: expand disk image if size param is larger - } - - return $disk; + if (is_dir($strImgFolder)) { + // 'new' is a folder and already exists, append vmname folder + $strImgFolder .= preg_replace('((^\.)|\/|(\.$))', '_', $vmname).'/'; } - $disk['new'] = $disk['image']; + // create folder if needed + if (!is_dir($strImgFolder)) { + #mkdir($strImgFolder, 0777, true); + my_mkdir($strImgFolder, 0777, true); + #chown($strImgFolder, 'nobody'); + #chgrp($strImgFolder, 'users'); + } + + $this->set_folder_nodatacow($strImgFolder); + + $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; + + $strImgPath = $strImgFolder.'vdisk'.$diskid.'.'.$strExt; + + } else { + // 'new' is a file + + // create parent folder if needed + if (!is_dir($path_parts['dirname'])) { + #mkdir($path_parts['dirname'], 0777, true); + my_mkdir($path_parts['dirname'], 0777, true); + #chown($path_parts['dirname'], 'nobody'); + #chgrp($path_parts['dirname'], 'users'); + } + + $this->set_folder_nodatacow($path_parts['dirname']); + + $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; + $strImgPath = $path_parts['dirname'].'/vdisk'.$diskid.'.'.$strExt; } - if (!empty($disk['new'])) { - // Create new disk image - $strImgFolder = $disk['new']; - $strImgPath = ''; - - if (strpos($strImgFolder, '/dev/') === 0) { - // ERROR invalid block device - $arrReturn = [ - 'error' => "Not a valid block device location '" . $strImgFolder . "'" - ]; - - return $arrReturn; - } - - if (empty($disk['size'])) { - // ERROR invalid disk size - $arrReturn = [ - 'error' => "Please specify a disk size for '" . $strImgFolder . "'" - ]; - - return $arrReturn; - } - - $path_parts = pathinfo($strImgFolder); - if (empty($path_parts['extension'])) { - // 'new' is a folder - - if (substr($strImgFolder, -1) != '/') { - $strImgFolder .= '/'; - } - - if (is_dir($strImgFolder)) { - // 'new' is a folder and already exists, append vmname folder - $strImgFolder .= preg_replace('((^\.)|\/|(\.$))', '_', $vmname) . '/'; - } + if (is_file($strImgPath)) { + $json_info = getDiskImageInfo($strImgPath); + $disk['driver'] = $json_info['format']; + $return_value = 0; + } else { + $strImgRawLocationPath = $strImgPath; + if (!empty($disk['storage']) && !empty($disk['select']) && $disk['select'] == 'auto' && $disk['storage'] != "default") $disk['select'] = $disk['storage']; + if (!empty($disk['select']) && (!in_array($disk['select'], ['auto', 'manual'])) && (is_dir('/mnt/'.$disk['select']))) { + // Force qemu disk creation to happen directly on either cache/disk1/disk2 ect based on dropdown selection + $strImgRawLocationPath = str_replace('/mnt/user/', '/mnt/'.$disk['select'].'/', $strImgPath); // create folder if needed - if (!is_dir($strImgFolder)) { - #mkdir($strImgFolder, 0777, true); - my_mkdir($strImgFolder, 0777, true); - #chown($strImgFolder, 'nobody'); - #chgrp($strImgFolder, 'users'); + $strImgRawLocationParent = dirname($strImgRawLocationPath); + if (!is_dir($strImgRawLocationParent)) { + #mkdir($strImgRawLocationParent, 0777, true); + my_mkdir($strImgRawLocationParent, 0777, true); + #chown($strImgRawLocationParent, 'nobody'); + #chgrp($strImgRawLocationParent, 'users'); } - $this->set_folder_nodatacow($strImgFolder); - - $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; - - $strImgPath = $strImgFolder . 'vdisk' . $diskid . '.' . $strExt; - - } else { - // 'new' is a file - - // create parent folder if needed - if (!is_dir($path_parts['dirname'])) { - #mkdir($path_parts['dirname'], 0777, true); - my_mkdir($path_parts['dirname'], 0777, true); - #chown($path_parts['dirname'], 'nobody'); - #chgrp($path_parts['dirname'], 'users'); - } - - $this->set_folder_nodatacow($path_parts['dirname']); - - $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; - $strImgPath = $path_parts['dirname'] . '/vdisk' . $diskid . '.' . $strExt; + $this->set_folder_nodatacow($strImgRawLocationParent); } + $strLastLine = exec("qemu-img create -q -f ".escapeshellarg($disk['driver'])." ".escapeshellarg($strImgRawLocationPath)." ".escapeshellarg($disk['size'])." 2>&1", $output, $return_value); if (is_file($strImgPath)) { - $json_info = getDiskImageInfo($strImgPath); - $disk['driver'] = $json_info['format']; - $return_value = 0; - } else { - $strImgRawLocationPath = $strImgPath; - if (!empty($disk['storage']) && !empty($disk['select']) && $disk['select'] == 'auto' && $disk['storage'] != "default") $disk['select'] = $disk['storage']; - if (!empty($disk['select']) && (!in_array($disk['select'], ['auto', 'manual'])) && (is_dir('/mnt/'.$disk['select']))) { - // Force qemu disk creation to happen directly on either cache/disk1/disk2 ect based on dropdown selection - $strImgRawLocationPath = str_replace('/mnt/user/', '/mnt/'.$disk['select'].'/', $strImgPath); - - // create folder if needed - $strImgRawLocationParent = dirname($strImgRawLocationPath); - if (!is_dir($strImgRawLocationParent)) { - #mkdir($strImgRawLocationParent, 0777, true); - my_mkdir($strImgRawLocationParent, 0777, true); - #chown($strImgRawLocationParent, 'nobody'); - #chgrp($strImgRawLocationParent, 'users'); - } - - $this->set_folder_nodatacow($strImgRawLocationParent); - } - - $strLastLine = exec("qemu-img create -q -f ".escapeshellarg($disk['driver'])." ".escapeshellarg($strImgRawLocationPath)." ".escapeshellarg($disk['size'])." 2>&1", $output, $return_value); - - if (is_file($strImgPath)) { - chmod($strImgPath, 0777); - chown($strImgPath, 'nobody'); - chgrp($strImgPath, 'users'); - } - } - - if ($return_value != 0) { - - // ERROR during image creation, return message to user - $arrReturn = [ - 'error' => "Error creating disk image '" . $strImgPath . "': " . $strLastLine, - 'error_output' => $output - ]; - - } else { - - // Success! - $arrReturn = [ - 'image' => $strImgPath, - 'driver' => $disk['driver'] - ]; - if (!empty($disk['dev'])) { - $arrReturn['dev'] = $disk['dev']; - } - if (!empty($disk['bus'])) { - $arrReturn['bus'] = $disk['bus']; - } - if (!empty($disk['boot'])) { - $arrReturn['boot'] = $disk['boot']; - } - if (!empty($disk['rotation'])) { - $arrReturn['rotation'] = $disk['rotation']; - } - if (!empty($disk['serial'])) { - $arrReturn['serial'] = $disk['serial']; - } - if (!empty($disk['discard'])) { - $arrReturn['discard'] = $disk['discard']; - } - + chmod($strImgPath, 0777); + chown($strImgPath, 'nobody'); + chgrp($strImgPath, 'users'); } } - return $arrReturn; + if ($return_value != 0) { + + // ERROR during image creation, return message to user + $arrReturn = [ + 'error' => "Error creating disk image '".$strImgPath."': ".$strLastLine, + 'error_output' => $output + ]; + + } else { + + // Success! + $arrReturn = [ + 'image' => $strImgPath, + 'driver' => $disk['driver'] + ]; + if (!empty($disk['dev'])) { + $arrReturn['dev'] = $disk['dev']; + } + if (!empty($disk['bus'])) { + $arrReturn['bus'] = $disk['bus']; + } + if (!empty($disk['boot'])) { + $arrReturn['boot'] = $disk['boot']; + } + if (!empty($disk['rotation'])) { + $arrReturn['rotation'] = $disk['rotation']; + } + if (!empty($disk['serial'])) { + $arrReturn['serial'] = $disk['serial']; + } + if (!empty($disk['discard'])) { + $arrReturn['discard'] = $disk['discard']; + } + + } } + return $arrReturn; + } + function config_to_xml($config,$vmclone = false) { + $domain = $config['domain']; + $media = $config['media']; + $nics = $config['nic']; + $disks = $config['disk']; + $usb = $config['usb']; + $usbopt = $config['usbopt']; + $usbboot = $config['usbboot']; + $shares = $config['shares']; + $gpus = $config['gpu']; + $pcis = $config['pci']; + $pciboot = $config['pciboot']; + $audios = $config['audio']; + $template = $config['template']; + $clocks = $config['clock']; + $evdevs = $config['evdev']; - function config_to_xml($config,$vmclone = false) { - $domain = $config['domain']; - $media = $config['media']; - $nics = $config['nic']; - $disks = $config['disk']; - $usb = $config['usb']; - $usbopt = $config['usbopt']; - $usbboot = $config['usbboot']; - $shares = $config['shares']; - $gpus = $config['gpu']; - $pcis = $config['pci']; - $pciboot = $config['pciboot']; - $audios = $config['audio']; - $template = $config['template']; - $clocks = $config['clock']; - $evdevs = $config['evdev']; + $type = $domain['type']; + $name = $domain['name']; + $mem = $domain['mem']; + $maxmem = (!empty($domain['maxmem'])) ? $domain['maxmem'] : $mem; + $uuid = (!empty($domain['uuid']) ? $domain['uuid'] : $this->domain_generate_uuid()); + $machine = $domain['machine']; + $machine_type = (stripos($machine, 'q35') !== false ? 'q35' : 'pc'); + $os_type = ((empty($template['os']) || stripos($template['os'], 'windows') === false) ? 'other' : 'windows'); + //$emulator = $this->get_default_emulator(); + $emulator = '/usr/local/sbin/qemu'; + $arch = $domain['arch']; + $pae = ($arch == 'i686') ? '' : ''; - $type = $domain['type']; - $name = $domain['name']; - $mem = $domain['mem']; - $maxmem = (!empty($domain['maxmem'])) ? $domain['maxmem'] : $mem; - $uuid = (!empty($domain['uuid']) ? $domain['uuid'] : $this->domain_generate_uuid()); - $machine = $domain['machine']; - $machine_type = (stripos($machine, 'q35') !== false ? 'q35' : 'pc'); - $os_type = ((empty($template['os']) || stripos($template['os'], 'windows') === false) ? 'other' : 'windows'); - //$emulator = $this->get_default_emulator(); - $emulator = '/usr/local/sbin/qemu'; - $arch = $domain['arch']; - $pae = ($arch == 'i686') ? '' : ''; - - $loader = ''; - $swtpm = ''; - $osbootdev = ''; - if (!empty($domain['ovmf'])) { - if ($domain['ovmf'] == 1) { - if (!is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd')) { - // Create a new copy of OVMF VARS for this VM - mkdir('/etc/libvirt/qemu/nvram/', 0777, true); - copy('/usr/share/qemu/ovmf-x64/OVMF_VARS-pure-efi.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd'); - } + $loader = ''; + $swtpm = ''; + $osbootdev = ''; + if (!empty($domain['ovmf'])) { + if ($domain['ovmf'] == 1) { + if (!is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd')) { + // Create a new copy of OVMF VARS for this VM + mkdir('/etc/libvirt/qemu/nvram/', 0777, true); + copy('/usr/share/qemu/ovmf-x64/OVMF_VARS-pure-efi.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd'); + } if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd')) { // Delete OVMF-TPM VARS for this VM if found unlink('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd'); - } + } - $loader = "/usr/share/qemu/ovmf-x64/OVMF_CODE-pure-efi.fd - /etc/libvirt/qemu/nvram/".$uuid."_VARS-pure-efi.fd"; - if ($domain['usbboot'] == 'Yes') $osbootdev = "" ; - } - if ($domain['ovmf'] == 2) { - if (!is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd')) { - // Create a new copy of OVMF VARS for this VM - mkdir('/etc/libvirt/qemu/nvram/', 0777, true); - copy('/usr/share/qemu/ovmf-x64/OVMF_VARS-pure-efi-tpm.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd'); - } + $loader = "/usr/share/qemu/ovmf-x64/OVMF_CODE-pure-efi.fd + /etc/libvirt/qemu/nvram/".$uuid."_VARS-pure-efi.fd"; + if ($domain['usbboot'] == 'Yes') $osbootdev = ""; + } + if ($domain['ovmf'] == 2) { + if (!is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd')) { + // Create a new copy of OVMF VARS for this VM + mkdir('/etc/libvirt/qemu/nvram/', 0777, true); + copy('/usr/share/qemu/ovmf-x64/OVMF_VARS-pure-efi-tpm.fd', '/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi-tpm.fd'); + } if (is_file('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd')) { // Delete OVMF VARS for this VM if found unlink('/etc/libvirt/qemu/nvram/'.$uuid.'_VARS-pure-efi.fd'); - } + } - $loader = "/usr/share/qemu/ovmf-x64/OVMF_CODE-pure-efi-tpm.fd - /etc/libvirt/qemu/nvram/".$uuid."_VARS-pure-efi-tpm.fd"; + $loader = "/usr/share/qemu/ovmf-x64/OVMF_CODE-pure-efi-tpm.fd + /etc/libvirt/qemu/nvram/".$uuid."_VARS-pure-efi-tpm.fd"; $swtpm = " - - "; - if ($domain['usbboot'] == 'Yes') $osbootdev = "" ; - } - } + + "; + if ($domain['usbboot'] == 'Yes') $osbootdev = ""; + } + } + $metadata = ''; + if (!empty($template)) { + $metadata .= ""; + $template_options = ''; + foreach ($template as $key => $value) { + $template_options .= $key."='".htmlspecialchars($value, ENT_QUOTES | ENT_XML1)."' "; + } + $metadata .= ""; + $metadata .= ""; + } - $metadata = ''; - if (!empty($template)) { - $metadata .= ""; - $template_options = ''; - foreach ($template as $key => $value) { - $template_options .= $key . "='" . htmlspecialchars($value, ENT_QUOTES | ENT_XML1) . "' "; + $vcpus = $domain['vcpus']; + $vcpupinstr = ''; + + if (!empty($domain['vcpu']) && is_array($domain['vcpu'])) { + $vcpus = count($domain['vcpu']); + foreach($domain['vcpu'] as $i => $vcpu) { + $vcpupinstr .= ""; + } + } + + $intCores = $vcpus; + $intThreads = 1; + $intCPUThreadsPerCore = 1; + + $cpumode = ''; + $cpucache = ''; + $cpufeatures = ''; + $cpumigrate = ''; + $cpucheck = ''; + $cpumatch = ''; + $cpucustom = ''; + $cpufallback = ''; + if (!empty($domain['cpumode']) && $domain['cpumode'] == 'host-passthrough') { + $cpumode .= "mode='host-passthrough'"; + $cpucache = ""; + + // detect if the processor is hyperthreaded: + $intCPUThreadsPerCore = max(intval(shell_exec('/usr/bin/lscpu | grep \'Thread(s) per core\' | awk \'{print $4}\'')), 1); + + // detect if the processor is AMD + multithreaded, and if so, enable topoext cpu feature + if ($intCPUThreadsPerCore > 1) { + $strCPUInfo = file_get_contents('/proc/cpuinfo'); + if (strpos($strCPUInfo, 'AuthenticAMD') !== false) { + $cpufeatures .= ""; } - $metadata .= ""; - $metadata .= ""; } - $vcpus = $domain['vcpus']; - $vcpupinstr = ''; - - if (!empty($domain['vcpu']) && is_array($domain['vcpu'])) { - $vcpus = count($domain['vcpu']); - foreach($domain['vcpu'] as $i => $vcpu) { - $vcpupinstr .= ""; - } - } - - $intCores = $vcpus; - $intThreads = 1; - $intCPUThreadsPerCore = 1; - - $cpumode = ''; - $cpucache = ''; - $cpufeatures = ''; - $cpumigrate = ''; - $cpucheck = ''; - $cpumatch = '' ; - $cpucustom = '' ; - $cpufallback = '' ; - if (!empty($domain['cpumode']) && $domain['cpumode'] == 'host-passthrough') { - $cpumode .= "mode='host-passthrough'"; - $cpucache = ""; - - // detect if the processor is hyperthreaded: - $intCPUThreadsPerCore = max(intval(shell_exec('/usr/bin/lscpu | grep \'Thread(s) per core\' | awk \'{print $4}\'')), 1); - - // detect if the processor is AMD + multithreaded, and if so, enable topoext cpu feature - if ($intCPUThreadsPerCore > 1) { - $strCPUInfo = file_get_contents('/proc/cpuinfo'); - if (strpos($strCPUInfo, 'AuthenticAMD') !== false) { - $cpufeatures .= ""; - } - } - - // even amount of cores assigned and cpu is hyperthreaded: pass that info along to the cpu section below - if ($intCPUThreadsPerCore > 1 && ($vcpus % $intCPUThreadsPerCore == 0)) { - $intCores = $vcpus / $intCPUThreadsPerCore; - $intThreads = $intCPUThreadsPerCore; - } - - if (!empty($domain['cpumigrate'])) $cpumigrate = " migratable='".$domain['cpumigrate']."'" ; - } - # - #Skylake-Client-noTSX-IBRS - - $cpustr = " - - $cpucache - $cpufeatures - - {$vcpus} - - $vcpupinstr - "; - - $usbmode = 'usb3'; - if (!empty($domain['usbmode'])) { - $usbmode = $domain['usbmode']; + // even amount of cores assigned and cpu is hyperthreaded: pass that info along to the cpu section below + if ($intCPUThreadsPerCore > 1 && ($vcpus % $intCPUThreadsPerCore == 0)) { + $intCores = $vcpus / $intCPUThreadsPerCore; + $intThreads = $intCPUThreadsPerCore; } - $ctrl = ''; - switch ($usbmode) { - case 'usb3': - $ctrl = " -
- "; - break; + if (!empty($domain['cpumigrate'])) $cpumigrate = " migratable='".$domain['cpumigrate']."'"; + } + # + #Skylake-Client-noTSX-IBRS - case 'usb3-qemu': - $ctrl = " -
- "; - break; + $cpustr = " + + $cpucache + $cpufeatures + + {$vcpus} + + $vcpupinstr + "; - case 'usb2': - $ctrl = " -
- - - -
- - - -
- - - -
- "; - break; - } + $usbmode = 'usb3'; + if (!empty($domain['usbmode'])) { + $usbmode = $domain['usbmode']; + } - /* - if ($os_type == "windows") $hypervclock = "" ; else $hypervclock = "" ; + $ctrl = ''; + switch ($usbmode) { + case 'usb3': + $ctrl = " +
+ "; + break; - $clock = " - - + case 'usb3-qemu': + $ctrl = " +
+ "; + break; + + case 'usb2': + $ctrl = " +
+ + + +
+ + + +
+ + + +
+ "; + break; + } + + /* + if ($os_type == "windows") $hypervclock = ""; else $hypervclock = ""; + + $clock = " + + + + $hypervclock + "; + + $hyperv = ''; + if ($domain['hyperv'] == 1 && $os_type == "windows") { + $hyperv = " + + + + + "; + + $clock = " + - $hypervclock "; + } + */ - $hyperv = ''; - if ($domain['hyperv'] == 1 && $os_type == "windows") { - $hyperv = " - - - - - "; - - $clock = " - - - "; + $clock = ""; + foreach ($clocks as $clockname => $clockvalues) { + switch ($clockname){ + case "rtc": + if ($clockvalues['present'] == "yes") $clock .= ""; + break; + case "pit": + if ($clockvalues['present'] == "yes") $clock .= ""; + break; + case "hpet": + $clock .= ""; + break; + case "hypervclock": + $clock .= ""; + break; } - */ + } + $hyperv = ""; + if ($domain['hyperv'] == 1 && $os_type == "windows") { + $hyperv = " + + + + "; - $clock = "" ; - foreach ($clocks as $clockname => $clockvalues) { - switch ($clockname){ - case "rtc": - if ($clockvalues['present'] == "yes") $clock .= ""; - break ; - case "pit": - if ($clockvalues['present'] == "yes") $clock .= ""; - break ; - case "hpet": - $clock .= "" ; - break ; - case "hypervclock": - $clock .= "" ; - break ; + if ($clocks['hypervclock']['present'] == "yes") { + $hyperv .= ""; + } + $hyperv .=""; + # $clock = " + # + # "; + } + $clock .= ""; + + $usbstr = ''; + if (!empty($usb)) { + foreach($usb as $i => $v){ + if ($vmclone) $usbx = explode(':', $v['id']); else $usbx = explode(':', $v); + $startupPolicy = ''; + if (isset($usbopt[$v]) && !$vmclone ) { + if (strpos($usbopt[$v], "#remove") == false) $startupPolicy = 'startupPolicy="optional"'; else $startupPolicy = ''; } - } - $hyperv = "" ; - if ($domain['hyperv'] == 1 && $os_type == "windows") { - $hyperv = " - - - - - "; - - if ($clocks['hypervclock']['present'] == "yes") - $hyperv .= "" ; - $hyperv .=""; - #$clock = " - # - # "; - } - $clock .= "" ; - - $usbstr = ''; - if (!empty($usb)) { - foreach($usb as $i => $v){ - if ($vmclone) $usbx = explode(':', $v['id']); else $usbx = explode(':', $v); - $startupPolicy = '' ; - if (isset($usbopt[$v]) && !$vmclone ) { - if (strpos($usbopt[$v], "#remove") == false) $startupPolicy = 'startupPolicy="optional"' ; else $startupPolicy = '' ; - } - if (isset($v["startupPolicy"]) && $vmclone ) { - if ($v["startupPolicy"] == "optional" ) $startupPolicy = 'startupPolicy="optional"' ; else $startupPolicy = '' ; - } - - $usbstr .= " - - - - " ; - if (!empty($usbboot[$v]) && !$vmclone ) { - $usbstr .= "" ; - } - if (isset($v["usbboot"]) && $vmclone ) { - if ($v["usbboot"] != NULL) $usbstr .= "" ; - } - $usbstr .= ""; + if (isset($v["startupPolicy"]) && $vmclone ) { + if ($v["startupPolicy"] == "optional" ) $startupPolicy = 'startupPolicy="optional"'; else $startupPolicy = ''; } - } - $arrAvailableDevs = []; - foreach (range('a', 'z') as $letter) { - $arrAvailableDevs['hd' . $letter] = 'hd' . $letter; - } + $usbstr .= " + + + + "; - $needSCSIController = false; - - //media settings - $bus = "ide"; - if ($machine_type == 'q35'){ - $bus = "sata"; - } - - $mediastr = ''; - if (!empty($media['cdrom'])) { - unset($arrAvailableDevs['hda']); - $cdromboot = $media['cdromboot'] ; - $media['cdrombus'] = $media['cdrombus'] ?: $bus; - if ($media['cdrombus'] == 'scsi') { - $needSCSIController = true; + if (!empty($usbboot[$v]) && !$vmclone ) { + $usbstr .= ""; } - if ($cdromboot > 0) { - $mediaboot = "" ; + if (isset($v["usbboot"]) && $vmclone ) { + if ($v["usbboot"] != NULL) $usbstr .= ""; } - $mediastr = " - - - - - $mediaboot - "; + $usbstr .= ""; } + } - $driverstr = ''; - if (!empty($media['drivers']) && $os_type == "windows") { - unset($arrAvailableDevs['hdb']); - $media['driversbus'] = $media['driversbus'] ?: $bus; - if ($media['driversbus'] == 'scsi') { - $needSCSIController = true; - } - $driverstr = " - - - - - "; + $arrAvailableDevs = []; + foreach (range('a', 'z') as $letter) { + $arrAvailableDevs['hd'.$letter] = 'hd'.$letter; + } + + $needSCSIController = false; + + //media settings + $bus = "ide"; + if ($machine_type == 'q35'){ + $bus = "sata"; + } + + $mediastr = ''; + if (!empty($media['cdrom'])) { + unset($arrAvailableDevs['hda']); + $cdromboot = $media['cdromboot']; + $media['cdrombus'] = $media['cdrombus'] ?: $bus; + if ($media['cdrombus'] == 'scsi') { + $needSCSIController = true; } + if ($cdromboot > 0) { + $mediaboot = ""; + } + $mediastr = " + + + + + $mediaboot + "; + } - //disk settings - $diskstr = ''; - $diskcount = 0; - if (!empty($disks)) { + $driverstr = ''; + if (!empty($media['drivers']) && $os_type == "windows") { + unset($arrAvailableDevs['hdb']); + $media['driversbus'] = $media['driversbus'] ?: $bus; + if ($media['driversbus'] == 'scsi') { + $needSCSIController = true; + } + $driverstr = " + + + + + "; + } - // force any hard drives to start with hdc, hdd, hde, etc - unset($arrAvailableDevs['hda']); - unset($arrAvailableDevs['hdb']); + //disk settings + $diskstr = ''; + $diskcount = 0; + if (!empty($disks)) { - foreach ($disks as $i => $disk) { - if (!empty($disk['image']) | !empty($disk['new']) ) { - //TODO: check if image/new is a block device - $diskcount++; + // force any hard drives to start with hdc, hdd, hde, etc + unset($arrAvailableDevs['hda']); + unset($arrAvailableDevs['hdb']); - if (!empty($disk['new'])) { - if (is_file($disk['new']) || is_block($disk['new'])) { - $disk['image'] = $disk['new']; - } + foreach ($disks as $i => $disk) { + if (!empty($disk['image']) | !empty($disk['new']) ) { + //TODO: check if image/new is a block device + $diskcount++; + + if (!empty($disk['new'])) { + if (is_file($disk['new']) || is_block($disk['new'])) { + $disk['image'] = $disk['new']; } + } - if (!empty($disk['image'])) { - if (empty($disk['driver'])) { - $disk['driver'] = 'raw'; + if (!empty($disk['image'])) { + if (empty($disk['driver'])) { + $disk['driver'] = 'raw'; - if (is_file($disk['image'])) { - $json_info = getDiskImageInfo($disk['image']); - $disk['driver'] = $json_info['format']; - } - } - } else { - if (empty($disk['driver'])) { - $disk['driver'] = 'raw'; - } - - $strImgFolder = $disk['new']; - $strImgPath = ''; - - $path_parts = pathinfo($strImgFolder); - if (empty($path_parts['extension'])) { - // 'new' is a folder - - if (substr($strImgFolder, -1) != '/') { - $strImgFolder .= '/'; - } - - if (is_dir($strImgFolder)) { - // 'new' is a folder and already exists, append domain name as child folder - $strImgFolder .= preg_replace('((^\.)|\/|(\.$))', '_', $domain['name']) . '/'; - } - - $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; - - $strImgPath = $strImgFolder . 'vdisk' . $diskcount . '.' . $strExt; - - } else { - // 'new' is a file - $strImgPath = $strImgFolder; - } - - if (is_file($strImgPath)) { - $json_info = getDiskImageInfo($strImgPath); + if (is_file($disk['image'])) { + $json_info = getDiskImageInfo($disk['image']); $disk['driver'] = $json_info['format']; } + } + } else { + if (empty($disk['driver'])) { + $disk['driver'] = 'raw'; + } - $arrReturn = [ - 'image' => $strImgPath, - 'driver' => $disk['driver'] - ]; - if (!empty($disk['dev'])) { - $arrReturn['dev'] = $disk['dev']; - } - if (!empty($disk['bus'])) { - $arrReturn['bus'] = $disk['bus']; + $strImgFolder = $disk['new']; + $strImgPath = ''; + + $path_parts = pathinfo($strImgFolder); + if (empty($path_parts['extension'])) { + // 'new' is a folder + + if (substr($strImgFolder, -1) != '/') { + $strImgFolder .= '/'; } - $disk = $arrReturn; - } + if (is_dir($strImgFolder)) { + // 'new' is a folder and already exists, append domain name as child folder + $strImgFolder .= preg_replace('((^\.)|\/|(\.$))', '_', $domain['name']).'/'; + } - $disk['bus'] = $disk['bus'] ?: 'virtio'; + $strExt = ($disk['driver'] == 'raw') ? 'img' : $disk['driver']; + $strImgPath = $strImgFolder.'vdisk'.$diskcount.'.'.$strExt; - if ($disk['bus'] == 'scsi') { - $needSCSIController = true; - } - - if (empty($disk['dev']) || !in_array($disk['dev'], $arrAvailableDevs)) { - $disk['dev'] = array_shift($arrAvailableDevs); - } - unset($arrAvailableDevs[$disk['dev']]); - $boot = $disk['boot'] ; - $bootorder = ''; - if ($boot > 0) { - $bootorder = "" ; - } - - $readonly = ''; - if (!empty($disk['readonly'])) { - $readonly = ''; - } - - $strDevType = @filetype(realpath($disk['image'])); - - if ($disk["serial"] != "") $serial = "".$disk["serial"]."" ; else $serial = "" ; - - $rotation_rate = ""; - if ($disk['bus'] == "scsi" || $disk['bus'] == "sata" || $disk['bus'] == "ide" ) { - if ($disk['rotation']) $rotation_rate = " rotation_rate='1' "; - } - - if ($strDevType == 'file' || $strDevType == 'block') { - $strSourceType = ($strDevType == 'file' ? 'file' : 'dev'); - - if (isset($disk['discard'])) $strDevUnmap = " discard=\"{$disk['discard']}\" "; else $strDevUnmap = " discard=\"ignore\" "; - - $diskstr .= " - - - - $bootorder - $readonly - $serial - "; - } - } - } - } - - $scsicontroller = ''; - if ($needSCSIController) { - $scsicontroller = ""; - } - - $netstr = ''; - if (!empty($nics)) { - foreach ($nics as $i => $nic) { - if (empty($nic['mac']) || empty($nic['network'])) { - continue; - } - $netmodel = $nic['model'] ?: 'virtio-net'; - - $net_res =$this->libvirt_get_net_res($this->conn, $nic['network']); - exec("ls --indicator-style=none /sys/class/net|grep -Po '^((vir)?br|vhost)[0-9]+(\.[0-9]+)?'",$br); - if ($nic["boot"] != NULL) $nicboot = "" ; else $nicboot = "" ; - if ($net_res) { - $netstr .= " - - - - $nicboot - "; - } elseif (in_array($nic['network'], $br)) { - if (preg_match('/^(vir)?br/',$nic['network'])) { - $netstr .= " - - - - $nicboot - "; } else { - $netstr .= " - - - - $nicboot - "; + // 'new' is a file + $strImgPath = $strImgFolder; } - } else { - continue; + + if (is_file($strImgPath)) { + $json_info = getDiskImageInfo($strImgPath); + $disk['driver'] = $json_info['format']; + } + + $arrReturn = [ + 'image' => $strImgPath, + 'driver' => $disk['driver'] + ]; + if (!empty($disk['dev'])) { + $arrReturn['dev'] = $disk['dev']; + } + if (!empty($disk['bus'])) { + $arrReturn['bus'] = $disk['bus']; + } + + $disk = $arrReturn; + } + + $disk['bus'] = $disk['bus'] ?: 'virtio'; + + if ($disk['bus'] == 'scsi') { + $needSCSIController = true; + } + + if (empty($disk['dev']) || !in_array($disk['dev'], $arrAvailableDevs)) { + $disk['dev'] = array_shift($arrAvailableDevs); + } + unset($arrAvailableDevs[$disk['dev']]); + $boot = $disk['boot']; + $bootorder = ''; + if ($boot > 0) { + $bootorder = ""; + } + + $readonly = ''; + if (!empty($disk['readonly'])) { + $readonly = ''; + } + + $strDevType = @filetype(realpath($disk['image'])); + + if ($disk["serial"] != "") $serial = "".$disk["serial"].""; else $serial = ""; + + $rotation_rate = ""; + if ($disk['bus'] == "scsi" || $disk['bus'] == "sata" || $disk['bus'] == "ide" ) { + if ($disk['rotation']) $rotation_rate = " rotation_rate='1' "; + } + + if ($strDevType == 'file' || $strDevType == 'block') { + $strSourceType = ($strDevType == 'file' ? 'file' : 'dev'); + + if (isset($disk['discard'])) $strDevUnmap = " discard=\"{$disk['discard']}\" "; else $strDevUnmap = " discard=\"ignore\" "; + + $diskstr .= " + + + + $bootorder + $readonly + $serial + "; } } } + } - $sharestr = ''; - $memorybacking = json_decode($domain['memoryBacking'],true); + $scsicontroller = ''; + if ($needSCSIController) { + $scsicontroller = ""; + } - if (!empty($shares)) { - foreach ($shares as $i => $share) { - if (empty($share['source']) || empty($share['target']) || ($os_type == "windows" && $share["mode"] == "9p")) { - continue; - } + $netstr = ''; + if (!empty($nics)) { + foreach ($nics as $i => $nic) { + if (empty($nic['mac']) || empty($nic['network'])) { + continue; + } + $netmodel = $nic['model'] ?: 'virtio-net'; - if ($share['mode'] == "virtiofs") { - if (!isset($memorybacking['source'])) $memorybacking['source']["@attributes"]["type"] = "memfd" ; - if (!isset($memorybacking['access'])) $memorybacking['access']["@attributes"]["mode"] = "shared" ; - - $sharestr .= " - - - - - - - - " ; + $net_res =$this->libvirt_get_net_res($this->conn, $nic['network']); + exec("ls --indicator-style=none /sys/class/net|grep -Po '^((vir)?br|vhost)[0-9]+(\.[0-9]+)?'",$br); + if ($nic["boot"] != NULL) $nicboot = ""; else $nicboot = ""; + if ($net_res) { + $netstr .= " + + + + $nicboot + "; + } elseif (in_array($nic['network'], $br)) { + if (preg_match('/^(vir)?br/',$nic['network'])) { + $netstr .= " + + + + $nicboot + "; } else { - $sharestr .= " - - - "; + $netstr .= " + + + + $nicboot + "; } + } else { + continue; } } + } - $pcidevs=''; - $gpudevs_used=[]; - $multidevices = [] ; #Load? - $vmrc=''; - $channelscopypaste = ''; - if (!empty($gpus)) { - foreach ($gpus as $i => $gpu) { - // Skip duplicate video devices - if (empty($gpu['id']) || in_array($gpu['id'], $gpudevs_used)) { - continue; - } - if ($gpu['id'] == 'nogpu') break; - if ($gpu['id'] == 'virtual') { - $strKeyMap = ''; - if (!empty($gpu['keymap'])) { - if ($gpu['keymap'] != "none") $strKeyMap = "keymap='" . $gpu['keymap'] . "'"; - } + $sharestr = ''; + $memorybacking = json_decode($domain['memoryBacking'],true); - $passwdstr = ''; - if (!empty($domain['password'])){ - $passwdstr = "passwd='" . htmlspecialchars($domain['password'], ENT_QUOTES | ENT_XML1) . "'"; - } + if (!empty($shares)) { + foreach ($shares as $i => $share) { + if (empty($share['source']) || empty($share['target']) || ($os_type == "windows" && $share["mode"] == "9p")) { + continue; + } - $strModelType = 'qxl'; - if (!empty($gpu['model'])) { - $strModelType = $gpu['model']; + if ($share['mode'] == "virtiofs") { + if (!isset($memorybacking['source'])) $memorybacking['source']["@attributes"]["type"] = "memfd"; + if (!isset($memorybacking['access'])) $memorybacking['access']["@attributes"]["mode"] = "shared"; - if (!empty($domain['ovmf']) && $strModelType == 'vmvga') { - // OVMF doesn't work with vmvga - $strModelType = 'qxl'; - } - } - if (!empty($gpu['autoport'])) { - $strAutoport = $gpu['autoport']; - } else $strAutoport = "yes" ; + $sharestr .= " + + + + + + + + "; + } else { + $sharestr .= " + + + "; + } + } + } - - - if (!empty($gpu['protocol'])) { - $strProtocol = $gpu['protocol']; - } else $strProtocol = "vnc" ; - - if (!empty($gpu['wsport'])) { - $strWSport = $gpu['wsport']; - } else $strWSport = "-1" ; - - if (!empty($gpu['port'])) { - $strPort = $gpu['port']; - } else $strPort = "-1" ; - - if ($strAutoport == "yes") $strPort = $strWSport = "-1" ; - if (($gpu['copypaste'] == "yes") && ($strProtocol == "spice")) $vmrcmousemode = "" ; else $vmrcmousemode = "" ; - if ($strProtocol == "spice") $virtualaudio = "spice" ; else $virtualaudio = "none" ; - - $strEGLHeadless = ""; - $strAccel3d =""; - if ($strModelType == "virtio3d") { - $strModelType = "virtio"; - if (!isset($gpu['render'])) $gpu['render'] = "auto"; - if ($gpu['render'] == "auto") { - $strEGLHeadless = ''; - $strAccel3d = ""; - } else { - $strEGLHeadless = ''; - $strAccel3d =""; - }} - - $strDisplayOptions = ""; - if ($strModelType == "qxl") { - if (empty($gpu['DisplayOptions'])) $gpu['DisplayOptions'] ="ram='65536' vram='16384' vgamem='16384' heads='1' primary='yes'"; - $strDisplayOptions = $gpu['DisplayOptions']; - } - - $vmrc = " - - - - - $vmrcmousemode - - $strEGLHeadless -