예제 #1
0
						(floor($intDisplaySize) == $intDisplaySize) &&
						isset($arrSizePrefix[$intShifts])) {

					$arrResponse['display-size'] = $intDisplaySize . $arrSizePrefix[$intShifts];

					$intDisplaySize /= 1024;
					$intShifts++;
				}

				$arrResponse['virtual-size'] = $json_info['virtual-size'];
				$arrResponse['actual-size'] = $json_info['actual-size'];
				$arrResponse['format'] = $json_info['format'];
				$arrResponse['dirty-flag'] = $json_info['dirty-flag'];
				$arrResponse['resizable'] = true;
			}
		} else if (is_block($file)) {
			$strDevSize = trim(shell_exec("blockdev --getsize64 " . escapeshellarg($file)));
			if (!empty($strDevSize) && is_numeric($strDevSize)) {
				$arrResponse['actual-size'] = (int)$strDevSize;
				$arrResponse['format'] = 'raw';

				$intDisplaySize = (int)$strDevSize;
				$intShifts = 0;
				while (!empty($intDisplaySize) &&
						($intDisplaySize >= 2) &&
						isset($arrSizePrefix[$intShifts])) {

					$arrResponse['display-size'] = round($intDisplaySize, 0) . $arrSizePrefix[$intShifts];

					$intDisplaySize /= 1000; // 1000 looks better than 1024 for block devs
					$intShifts++;
예제 #2
0
		function config_to_xml($config) {
			$domain = $config['domain'];
			$media = $config['media'];
			$nics = $config['nic'];
			$disks = $config['disk'];
			$usb = $config['usb'];
			$shares = $config['shares'];
			$gpus = $config['gpu'];
			$audios = $config['audio'];
			$template = $config['template'];


			$type = $domain['type'];
			$name = $domain['name'];
			$mem = $domain['mem'];
			$maxmem = $mem;
			if (!empty($domain['maxmem'])) {
				$maxmem = $domain['maxmem'];
			}
			$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();
			$arch = $domain['arch'];
			$pae = '';
			if ($arch == 'i686'){
				$pae = '<pae/>';
			}

			$loader = '';
			if (!empty($domain['ovmf'])) {
				$loader = "<loader type='pflash'>/usr/share/qemu/ovmf-x64/OVMF-pure-efi.fd</loader>";
			}

			$metadata = '';
			if (!empty($template)) {
				$template_options = '';
				foreach ($template as $key => $value) {
					$template_options .= $key . "='" . htmlspecialchars($value, ENT_QUOTES | ENT_XML1) . "' ";
				}
				$metadata = "<metadata><vmtemplate " . $template_options . "/></metadata>";
			}

			$vcpus = 1;
			$vcpupinstr = '';

			if (!empty($domain['vcpu']) && is_array($domain['vcpu'])) {
				$vcpus = count($domain['vcpu']);
				foreach($domain['vcpu'] as $i => $vcpu) {
					$vcpupinstr .= "<vcpupin vcpu='$i' cpuset='$vcpu'/>";
				}
			} else if (!empty($domain['vcpus'])) {
				$vcpus = $domain['vcpus'];
				for ($i=0; $i < $vcpus; $i++) {
					$vcpupinstr .= "<vcpupin vcpu='$i' cpuset='$i'/>";
				}
			}

			$cpumode = '';
			if (!empty($domain['cpumode']) && $domain['cpumode'] == 'host-passthrough') {
				$cpumode .= "mode='host-passthrough'";
			}

			$cpustr = "<cpu $cpumode>
							<topology sockets='1' cores='{$vcpus}' threads='1'/>
						</cpu>
						<vcpu placement='static'>{$vcpus}</vcpu>
						<cputune>
							$vcpupinstr
						</cputune>";

			$bus = "ide";
			$ctrl = '';
			if ($machine_type == 'q35'){
				$bus = "sata";
				$ctrl = "<controller type='usb' index='0' model='ich9-ehci1'/>
						<controller type='usb' index='0' model='ich9-uhci1'/>";
			}

			// OVMF needs the bus set to ide for cdroms
			if (!empty($domain['ovmf'])) {
				$bus = "ide";
			}

			$clock = "<clock offset='" . $domain['clock'] . "'>
						<timer name='rtc' tickpolicy='catchup'/>
						<timer name='pit' tickpolicy='delay'/>
						<timer name='hpet' present='no'/>
					</clock>";

			$hyperv = '';
			if (!empty($domain['hyperv']) && $os_type == "windows") {
				$hyperv = "<hyperv>
							<relaxed state='on'/>
							<vapic state='on'/>
							<spinlocks state='on' retries='8191'/>
						</hyperv>";

				$clock = "<clock offset='" . $domain['clock'] . "'>
							<timer name='hypervclock' present='yes'/>
							<timer name='hpet' present='no'/>
						</clock>";
			}

			$usbstr = '';
			if (!empty($usb)) {
				foreach($usb as $i => $v){
					$usbx = explode(':', $v);
					$usbstr .= "<hostdev mode='subsystem' type='usb' managed='yes'>
									<source>
										<vendor id='0x".$usbx[0]."'/>
										<product id='0x".$usbx[1]."'/>
									</source>
								</hostdev>";
				}
			}

			$arrAvailableDevs = [];
			foreach (range('a', 'z') as $letter) {
				$arrAvailableDevs['hd' . $letter] = 'hd' . $letter;
			}
			$arrUsedBootOrders = [];

			//media settings
			$mediastr = '';
			if (!empty($media['cdrom'])) {
				unset($arrAvailableDevs['hda']);
				$arrUsedBootOrders[] = 2;
				$mediastr = "<disk type='file' device='cdrom'>
								<driver name='qemu'/>
								<source file='" . htmlspecialchars($media['cdrom'], ENT_QUOTES | ENT_XML1) . "'/>
								<target dev='hda' bus='$bus'/>
								<readonly/>
								<boot order='2'/>
							</disk>";
			}

			$driverstr = '';
			if (!empty($media['drivers']) && $os_type == "windows") {
				unset($arrAvailableDevs['hdb']);
				$driverstr = "<disk type='file' device='cdrom'>
								<driver name='qemu'/>
								<source file='" . htmlspecialchars($media['drivers'], ENT_QUOTES | ENT_XML1) . "'/>
								<target dev='hdb' bus='$bus'/>
								<readonly/>
							</disk>";
			}

			//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++;

						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 (is_file($disk['image'])) {
									$json_info = json_decode(shell_exec("qemu-img info --output json " . escapeshellarg($disk['image'])), true);
									$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 = json_decode(shell_exec("qemu-img info --output json " . escapeshellarg($strImgPath)), true);
								$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;
						}

						if (empty($disk['bus'])) {
							$disk['bus'] = 'virtio';
						}

						if (empty($disk['dev']) || !in_array($disk['dev'], $arrAvailableDevs)) {
							$disk['dev'] = array_shift($arrAvailableDevs);
						}
						unset($arrAvailableDevs[$disk['dev']]);

						$bootorder = '';
						if (!in_array(1, $arrUsedBootOrders)) {
							$bootorder = "<boot order='1'/>";
							$arrUsedBootOrders[] = 1;
						}

						$readonly = '';
						if (!empty($disk['readonly'])) {
							$readonly = '<readonly/>';
						}

						$strDevType = @filetype(realpath($disk['image']));

						if ($strDevType == 'file' || $strDevType == 'block') {
							$strSourceType = ($strDevType == 'file' ? 'file' : 'dev');

							$diskstr .= "<disk type='" . $strDevType . "' device='disk'>
											<driver name='qemu' type='" . $disk['driver'] . "' cache='writeback'/>
											<source " . $strSourceType . "='" . htmlspecialchars($disk['image'], ENT_QUOTES | ENT_XML1) . "'/>
											<target bus='" . $disk['bus'] . "' dev='" . $disk['dev'] . "'/>
											$bootorder
											$readonly
										</disk>";
						}
					}
				}
			}

			$netstr = '';
			if (!empty($nics)) {
				foreach ($nics as $i => $nic) {
					if (empty($nic['mac']) || empty($nic['network'])) {
						continue;
					}

					$netstr .= "<interface type='bridge'>
									<mac address='{$nic['mac']}'/>
									<source bridge='" . htmlspecialchars($nic['network'], ENT_QUOTES | ENT_XML1) . "'/>
									<model type='virtio'/>
								</interface>";
				}
			}

			$sharestr = '';
			if (!empty($shares) && $os_type != "windows") {
				foreach ($shares as $i => $share) {
					if (empty($share['source']) || empty($share['target'])) {
						continue;
					}

					$sharestr .= "<filesystem type='mount' accessmode='passthrough'>
										<source dir='" . htmlspecialchars($share['source'], ENT_QUOTES | ENT_XML1) . "'/>
										<target dir='" . htmlspecialchars($share['target'], ENT_QUOTES | ENT_XML1) . "'/>
									</filesystem>";
				}
			}

			$passwdstr = '';
			if (!empty($domain['password'])){
				$passwdstr = "passwd='" . htmlspecialchars($domain['password'], ENT_QUOTES | ENT_XML1) . "'";
			}

			$pcidevs='';
			$gpuargs='';
			$gpudevs=[];
			$gpuargsdevs=[];
			$gpuincr=0;
			$vnc='';
			if (!empty($gpus)) {
				foreach ($gpus as $i => $gpu) {
					if (empty($gpu['id'])) {
						continue;
					}

					// Skip duplicate video devices
					if (in_array($gpu['id'], $gpudevs)) {
						continue;
					}

					if ($gpu['id'] == 'vnc') {
						$strKeyMap = '';
						if (!empty($gpu['keymap'])) {
							$strKeyMap = "keymap='" . $gpu['keymap'] . "'";
						}

						$vnc = "<input type='tablet' bus='usb'/>
								<input type='mouse' bus='ps2'/>
								<input type='keyboard' bus='ps2'/>
								<graphics type='vnc' port='-1' autoport='yes' websocket='-1' listen='0.0.0.0' $passwdstr $strKeyMap>
									<listen type='address' address='0.0.0.0'/>
								</graphics>";

						if (!empty($domain['ovmf'])) {
							// OVMF doesn't work with vmvga
							$vnc .= "<video>
										<model type='cirrus'/>
									</video>";
						} else {
							// SeaBIOS is cool with vmvga
							$vnc .= "<video>
										<model type='vmvga'/>
									</video>";
						}
						continue;
					}

					list($gpu_bus, $gpu_slot, $gpu_function) = explode(":", str_replace('.', ':', $gpu['id']));

					if (!empty($domain['ovmf'])) {

						// OVMF passthrough uses the normal hostdev and libvirt can fully manage the device
						$pcidevs .= "<hostdev mode='subsystem' type='pci' managed='yes'>
										<driver name='vfio'/>
										<source>
											<address domain='0x0000' bus='0x" . $gpu_bus . "' slot='0x" . $gpu_slot . "' function='0x" . $gpu_function . "'/>
										</source>
									</hostdev>";

					} else {

						// VGA BIOS passthrough uses qemu args and we have to manage the device (libvirt wont attach/detach the device driver to vfio-pci)
						switch ($machine_type) {

							case 'q35':
								$gpuargs .= "<qemu:arg value='-device'/>
											<qemu:arg value='vfio-pci,host={$gpu_bus}:{$gpu_slot}.{$gpu_function},bus=pcie.0,multifunction=on" . ((empty($gpuargs) && empty($vnc)) ? ',x-vga=on' : '') . "'/>";
								$gpuargsdevs[$gpu['id']] = ""; // no address needed
								break;

							case 'pc':
								$gpuargs .= "<qemu:arg value='-device'/>
											<qemu:arg value='vfio-pci,host={$gpu_bus}:{$gpu_slot}.{$gpu_function},bus=root.1,addr=0{$gpuincr}.0,multifunction=on" . ((empty($gpuargs) && empty($vnc)) ? ',x-vga=on' : '') . "'/>";
								$gpuargsdevs[$gpu['id']] = "0{$gpuincr}.0";
								break;
						}

					}

					$gpudevs[] = $gpu['id'];
					$gpuincr++;
				}
			}

			$audioargs='';
			$audiodevs=[];
			if (!empty($audios)) {
				foreach ($audios as $i => $audio) {
					if (empty($audio['id'])) {
						continue;
					}

					// Skip duplicate audio devices
					if (in_array($audio['id'], $audiodevs)) {
						continue;
					}

					list($audio_bus, $audio_slot, $audio_function) = explode(":", str_replace('.', ':', $audio['id']));

					if (!empty($domain['ovmf']) || empty($gpuargsdevs)) {

						$pcidevs .= "<hostdev mode='subsystem' type='pci' managed='yes'>
										<driver name='vfio'/>
										<source>
											<address domain='0x0000' bus='0x" . $audio_bus . "' slot='0x" . $audio_slot . "' function='0x" . $audio_function . "'/>
										</source>
									</hostdev>";

					} else {

						// VGA BIOS passthrough uses qemu args and we have to manage the device (libvirt wont attach/detach the device driver to vfio-pci)
						switch ($machine_type) {

							case 'q35':
								$audioargs .= "<qemu:arg value='-device'/>
												<qemu:arg value='vfio-pci,host={$audio_bus}:{$audio_slot}.{$audio_function},bus=pcie.0'/>";
								break;

							case 'pc':
								// Look for video device and see if this sound device is a function of that video card
								$addr = '';
								if (isset($gpuargsdevs[$audio_bus . ':' . $audio_slot . '.0'])) {
									$addr = str_replace('.0', '.' . $audio_function, $gpuargsdevs[$audio_bus . ':' . $audio_slot . '.0']);
								} else {
									$addr = "0" . $gpuincr++ . ".0";
								}

								$audioargs .= "<qemu:arg value='-device'/>
												<qemu:arg value='vfio-pci,host={$audio_bus}:{$audio_slot}.{$audio_function},bus=root.1,addr=" . $addr . "'/>";
								break;
						}

					}

					$audiodevs[] = $audio['id'];
				}
			}

			$cmdargs='';
			if (!empty($gpuargs) || !empty($audioargs)) {
				switch ($machine_type) {

					case 'q35':
						$cmdargs .= "<qemu:commandline>
										<qemu:arg value='-device'/>
										<qemu:arg value='ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=2,chassis=1,id=root.1'/>
										$gpuargs
										$audioargs
									</qemu:commandline>";
						break;

					case 'pc':
						$cmdargs .= "<qemu:commandline>
										<qemu:arg value='-device'/>
										<qemu:arg value='ioh3420,bus=pci.0,addr=1c.0,multifunction=on,port=2,chassis=1,id=root.1'/>
										$gpuargs
										$audioargs
									</qemu:commandline>";
						break;

				}
			}


			return "<domain type='$type' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
						<uuid>$uuid</uuid>
						<name>$name</name>
						<description>" . htmlspecialchars($domain['desc'], ENT_QUOTES | ENT_XML1) . "</description>
						$metadata
						<currentMemory>$mem</currentMemory>
						<memory>$maxmem</memory>
						<memoryBacking>
							<nosharepages/>
							<locked/>
						</memoryBacking>
						$cpustr
						<os>
							$loader
							<type arch='$arch' machine='$machine'>hvm</type>
						</os>
						<features>
							<acpi/>
							<apic/>
							$hyperv
							$pae
						</features>
						$clock
						<on_poweroff>destroy</on_poweroff>
						<on_reboot>restart</on_reboot>
						<on_crash>restart</on_crash>
						<devices>
							<emulator>$emulator</emulator>
							$diskstr
							$mediastr
							$driverstr
							$ctrl
							$sharestr
							$netstr
							$vnc
							<console type='pty'/>
							$pcidevs
							$usbstr
							<channel type='unix'>
								<target type='virtio' name='org.qemu.guest_agent.0'/>
							</channel>
							<memballoon model='virtio'>
								<alias name='balloon0'/>
							</memballoon>
						</devices>
						$cmdargs
					</domain>";

		}