Import…: an XVA file (typed, or chosen with the system's Open dialog: a desktop app, or the browser's, which hands the file * to the page), the server it will belong to and the storage of its disks. What the file holds (its ova.xml: the VM's name, its * disks) shows before it is sent. Closes with ['xva' => ['op' => 'import', ...]], src/Feature/Xva/XvaTransfers streams it. * * @property Label $where * @property TextBox $path * @property Button $browse * @property Label $info * @property ComboBox $home * @property ComboBox $sr * @property Label $error * @property Button $import * @property Button $cancel */ final class ImportForm extends Form { private array $params = []; /** @var array|null what ova.xml says of the file chosen */ private ?array $vm = null; public function __construct(private Session $session) { parent::__construct(); } /** XAPI's answers, events and the console go on while this dialog is open. */ protected function tick(float $dt): void { $this->session->tick($dt); } /** @return array */ public static function paramsFor(Connection $c, ?string $host = null): array { $cache = $c->cache; $hosts = [['ref' => '', 'name' => "Don't assign a home server"]]; foreach ($cache->hosts() as $ref => $h) { $hosts[] = ['ref' => (string)$ref, 'name' => (string)$h['name_label']]; } $default = (string)$cache->field((string)$cache->poolRef(), 'default_SR', ''); $srs = []; foreach ($cache->srs() as $ref => $sr) { if (($sr['content_type'] ?? '') !== 'iso') { $srs[] = ['ref' => (string)$ref, 'name' => $cache->name((string)$ref) . ' (' . Units::bytes((float)$sr['physical_size'] - (float)$sr['physical_utilisation']) . ' free)']; } } usort($srs, fn ($a, $b) => ($b['ref'] === $default) <=> ($a['ref'] === $default)); return ['server' => $c->name(), 'pool' => (string)($cache->poolRef() ?? ''), 'hosts' => $hosts, 'srs' => $srs, 'host' => $host]; } protected function loaded(array $params = []): void { $this->params = $params; $this->vm = null; $this->where->text = "Into '" . ($params['server'] ?? '') . "'"; $this->path->text = (string)($params['file'] ?? ''); $this->home->items = array_column($params['hosts'] ?? [], 'name'); $at = array_search($params['host'] ?? null, array_column($params['hosts'] ?? [], 'ref'), true); $this->home->selected = $at === false ? 0 : (int)$at; $this->sr->items = array_column($params['srs'] ?? [], 'name'); $this->sr->selected = 0; $this->browse->enabled = Native::supports('open'); $this->error->text = ''; $this->info->text = Native::supports('open') ? '' : 'Type the path of the file' . (Platform::isWasm() ? '.' : ' (no file chooser in this window).'); $this->onFileChange($this->path, $this->path->text); $this->setFocus($this->path); } public function onBrowseClick(Button $sender): void { if (!Native::supports('open')) { return; } FilePicker::open(['title' => 'Import an XVA file', 'filters' => ['XVA files' => ['xva'], 'All files' => ['*']]])->then(function (mixed $file): void { if (is_string($file)) { $this->path->text = $file; $this->onFileChange($this->path, $file); } }, fn (\Throwable $e) => $this->error->text = $e->getMessage()); } public function onFileChange(TextBox $sender, string $text): void { $this->vm = null; $file = trim($text); if ($file === '' || !is_file($file)) { if ($file !== '') { $this->info->text = 'No such file.'; } return; } $this->vm = Xva::readHeader(Xva::headOf($file)); $size = (float)@filesize($file); if ($this->vm === null) { $this->info->text = Units::bytes($size) . ' — this is not an XVA file (no ova.xml at its start).'; return; } $disks = array_map(fn (array $d) => $d[0] . ' (' . Units::bytes($d[1]) . ')', $this->vm['disks']); $this->info->text = "VM '" . $this->vm['name_label'] . "': " . (int)$this->vm['VCPUs_max'] . ' vCPUs, ' . Units::bytes((float)$this->vm['memory_static_max']) . ' of memory' . ($disks !== [] ? ', disks ' . implode(', ', $disks) : '') . ' · file ' . Units::bytes($size); } public function onImportClick(mixed $sender = null): void { $file = trim($this->path->text); $this->onFileChange($this->path, $file); if ($file === '' || !is_file($file)) { $this->error->text = 'Choose the XVA file to import.'; return; } if ($this->vm === null) { $this->error->text = 'This file is not an XVA export.'; return; } $sr = $this->params['srs'][(int)$this->sr->selected]['ref'] ?? null; if ($sr === null) { $this->error->text = 'There is no storage repository to put the disks on.'; return; } $host = $this->params['hosts'][(int)$this->home->selected]['ref'] ?? ''; $this->close(['xva' => ['op' => 'import', 'file' => $file, 'sr' => $sr, 'host' => $host, 'label' => (string)$this->vm['name_label']]]); } public function onCancelClick(Button $sender): void { $this->close(); } }