ES
games/xencenter-xenadmin-xcp-jumphp / src / Form / NewVmForm.php · commit fb1b7c9
<?phpnamespace App\Form;use App\Model\Chain;use App\Model\Connection;use App\Model\Session;use JumPHP\Gui\Button;use JumPHP\Gui\CheckBox;use JumPHP\Gui\ComboBox;use JumPHP\Gui\Form;use JumPHP\Gui\Label;use JumPHP\Gui\TextBox;/** * The New VM wizard of XenCenter, on one page: what it asks, then the XAPI calls it makes (create()): VM.clone of the template, * the disks on the chosen SR (its other_config "disks"), VM.provision, not a template any more, memory, vCPUs, home server, * a VIF on the network, VM.start. * * @property Label $where * @property ComboBox $template * @property TextBox $name * @property TextBox $description * @property ComboBox $home * @property TextBox $vcpus * @property TextBox $memory * @property ComboBox $sr * @property ComboBox $network * @property CheckBox $start * @property Label $error * @property Button $create * @property Button $cancel */final class NewVmForm extends Form{    public function __construct(private \App\Model\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);    }    private array $params = [];    /** @return array<string, mixed> 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<string, mixed> $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);    }}