session->tick($dt); } private array $params = []; /** @return array the lists of a connection */ public static function paramsFor(Connection $c, ?string $template = null, ?string $host = null): array { $cache = $c->cache; $templates = []; foreach ($cache->templates() as $ref => $t) { $templates[] = ['ref' => $ref, 'name' => (string)$t['name_label'], 'memory' => (int)round((float)$t['memory_static_max'] / 1048576), 'vcpus' => (int)$t['VCPUs_at_startup']]; } $hosts = [['ref' => '', 'name' => "Don't assign a home server"]]; foreach ($cache->hosts() as $ref => $h) { $hosts[] = ['ref' => $ref, 'name' => (string)$h['name_label']]; } $srs = []; $default = (string)$cache->field((string)$cache->poolRef(), 'default_SR', ''); foreach ($cache->srs() as $ref => $sr) { if (($sr['content_type'] ?? '') !== 'iso') { $srs[] = ['ref' => $ref, 'uuid' => (string)$sr['uuid'], 'name' => $cache->name($ref) . ' (' . \App\Gui\Units::bytes((float)$sr['physical_size'] - (float)$sr['physical_utilisation']) . ' free)']; } } usort($srs, fn ($a, $b) => ($b['ref'] === $default) <=> ($a['ref'] === $default)); $networks = []; foreach ($cache->networks() as $ref => $n) { $networks[] = ['ref' => $ref, 'name' => (string)$n['name_label']]; } return ['server' => $c->name(), 'templates' => $templates, 'hosts' => $hosts, 'srs' => $srs, 'networks' => $networks, 'template' => $template, 'host' => $host]; } protected function loaded(array $params = []): void { $this->params = $params; $this->where->text = "In '" . ($params['server'] ?? '') . "'"; $this->template->items = array_column($params['templates'] ?? [], 'name'); $at = array_search($params['template'] ?? null, array_column($params['templates'] ?? [], 'ref'), true); $this->template->selected = $at === false ? 0 : (int)$at; $this->home->items = array_column($params['hosts'] ?? [], 'name'); $hostAt = array_search($params['host'] ?? null, array_column($params['hosts'] ?? [], 'ref'), true); $this->home->selected = $hostAt === false ? 0 : (int)$hostAt; $this->sr->items = array_column($params['srs'] ?? [], 'name'); $this->sr->selected = 0; $this->network->items = array_column($params['networks'] ?? [], 'name'); $this->network->selected = 0; $this->error->text = ''; $this->onTemplateChange($this->template, (int)$this->template->selected, ''); $this->setFocus($this->name); } public function onTemplateChange(ComboBox $sender, int $index, string $item): void { $t = $this->params['templates'][$index] ?? null; if ($t === null) { return; } $this->name->text = 'New VM (' . $t['name'] . ')'; $this->vcpus->text = (string)max(1, $t['vcpus']); $this->memory->text = (string)max(256, $t['memory']); } public function onCreateClick(Button $sender): void { $t = $this->params['templates'][(int)$this->template->selected] ?? null; $vcpus = (int)$this->vcpus->text; $memory = (int)$this->memory->text; if ($t === null) { $this->error->text = 'There is no template on this server.'; return; } if (trim($this->name->text) === '') { $this->error->text = 'Enter a name for the VM.'; return; } if ($vcpus < 1 || $vcpus > 64 || $memory < 256) { $this->error->text = 'From 1 to 64 vCPUs, and at least 256 MB of memory.'; return; } $this->close(['newVm' => ['template' => $t['ref'], 'name' => trim($this->name->text), 'description' => $this->description->text, 'home' => $this->params['hosts'][(int)$this->home->selected]['ref'] ?? '', 'vcpus' => $vcpus, 'memory' => $memory, 'sr' => $this->params['srs'][(int)$this->sr->selected] ?? null, 'network' => $this->params['networks'][(int)$this->network->selected]['ref'] ?? null, 'start' => $this->start->checked]]); } public function onCancelClick(Button $sender): void { $this->close(); } /** The XAPI calls of the wizard. @param array $p */ public static function create(Session $session, array $p): void { $c = $session->connectionOf((string)$p['template']); if ($c === null) { return; } $name = (string)$p['name']; $bytes = (float)$p['memory'] * 1048576; $vcpus = (int)$p['vcpus']; $template = $c->cache->get((string)$p['template']) ?? []; $vif = ['device' => '0', 'MAC' => '', 'MTU' => 1500, 'other_config' => [], 'qos_algorithm_type' => '', 'qos_algorithm_params' => [], 'locking_mode' => 'network_default', 'ipv4_allowed' => [], 'ipv6_allowed' => []]; Chain::run([ fn () => $c->run("Creating VM '$name'", 'VM.clone', [(string)$p['template'], $name], (string)$p['template']), function (string $vm) use ($c, $p, $template) { // the disks of the template go to the chosen SR $disks = (string)($template['other_config']['disks'] ?? ''); if ($disks === '' || !is_array($p['sr'] ?? null)) { return $vm; } $xml = (string)preg_replace('/sr="[^"]*"/', 'sr="' . $p['sr']['uuid'] . '"', $disks); return Chain::keep(Chain::run([fn () => $c->call('VM.remove_from_other_config', $vm, 'disks'), fn () => $c->call('VM.add_to_other_config', $vm, 'disks', $xml)]), $vm); }, fn (string $vm) => Chain::keep($c->run("Provisioning '$name'", 'VM.provision', [$vm], $vm), $vm), fn (string $vm) => Chain::keep($c->call('VM.set_is_a_template', $vm, false), $vm), fn (string $vm) => Chain::keep($c->call('VM.set_name_description', $vm, (string)$p['description']), $vm), fn (string $vm) => Chain::keep($c->run("Setting the memory of '$name'", 'VM.set_memory_limits', [$vm, min($bytes, 268435456.0), $bytes, $bytes, $bytes], $vm), $vm), // XAPI wants VCPUs_at_startup <= VCPUs_max at every step: the maximum first when it grows, the startup count first when it shrinks fn (string $vm) => Chain::keep($c->call($vcpus >= (int)($template['VCPUs_at_startup'] ?? 1) ? 'VM.set_VCPUs_max' : 'VM.set_VCPUs_at_startup', $vm, $vcpus), $vm), fn (string $vm) => Chain::keep($c->call($vcpus >= (int)($template['VCPUs_at_startup'] ?? 1) ? 'VM.set_VCPUs_at_startup' : 'VM.set_VCPUs_max', $vm, $vcpus), $vm), fn (string $vm) => (string)$p['home'] !== '' ? Chain::keep($c->call('VM.set_affinity', $vm, (string)$p['home']), $vm) : $vm, fn (string $vm) => $p['network'] !== null ? Chain::keep($c->run("Adding a network interface to '$name'", 'VIF.create', [['VM' => $vm, 'network' => (string)$p['network']] + $vif], $vm), $vm) : $vm, fn (string $vm) => !empty($p['start']) ? Chain::keep($c->run("Starting VM '$name'", 'VM.start', [$vm, false, false], $vm), $vm) : $vm, ])->then(fn () => null, fn () => null); } }