Accedi
IT
games/xencenter-xenadmin-xcp-jumphp / src / Form / CrossPoolMigrateForm.php · commit fb1b7c9
<?phpnamespace App\Form;use App\Feature\Storage\Menus;use App\Gui\Units;use App\Model\Connection;use App\Model\Session;use JumPHP\Gui\Button;use JumPHP\Gui\ComboBox;use JumPHP\Gui\Container;use JumPHP\Gui\Form;use JumPHP\Gui\FormLoader;use JumPHP\Gui\Label;/** * Cross-pool migration (XenCenter's Migrate wizard to another pool), on one page: the destination pool (another connected server), * its server, the SR of each disk and the network of each VIF. What XAPI is asked (Feature\Storage\Actions::crossPoolMigrate): * host.migrate_receive on the destination, then VM.assert_can_migrate and Async.VM.migrate_send(vm, dest, live, vdi_map, vif_map, {}) * on the source. Closes with ['crossPool' => [vm, target (index of the connection), host, vdiMap, vifMap]]. * * @property Label $heading * @property Label $where * @property ComboBox $pool * @property ComboBox $host * @property Container $disks * @property Container $vifs * @property Label $how * @property Label $error * @property Button $migrate * @property Button $cancel */final class CrossPoolMigrateForm extends Form{    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);    }    private array $params = [];    /** @return array<string, mixed> the VM's disks and VIFs, and what each other connected pool offers */    public static function paramsFor(Session $session, Connection $c, string $vm): array    {        $cache = $c->cache;        $disks = [];        foreach ($cache->disksOf($vm) as $d) {            if ($d['vdi'] !== null) {                $disks[] = ['vdi' => (string)$d['vbd']['VDI'], 'label' => (string)$d['vdi']['name_label'] . ' (' . Units::bytes((float)$d['vdi']['virtual_size']) . ')'];            }        }        $vifs = [];        foreach ($cache->vifsOf($vm) as $v) {            $vifs[] = ['vif' => $v['ref'], 'label' => 'Interface ' . $v['vif']['device'] . ' (' . $cache->name((string)$v['vif']['network']) . ')',                'network' => $cache->name((string)$v['vif']['network'])];        }        $targets = [];        foreach (Menus::otherPools($c) as $index => $other) {            $oc = $other->cache;            $hosts = $srs = $networks = [];            foreach ($oc->hosts() as $ref => $h) {                if ($h['enabled'] ?? true) {                    $m = $oc->hostMemory($ref);                    $hosts[] = ['ref' => $ref, 'name' => $h['name_label'] . ' (' . Units::bytes($m['free']) . ' free)'];                }            }            $default = (string)$oc->field((string)$oc->poolRef(), 'default_SR', '');            foreach ($oc->srs() as $ref => $sr) {                if (($sr['content_type'] ?? '') !== 'iso' && Menus::managed($oc, $ref) && Menus::attached($oc, $ref) > 0) {                    $srs[] = ['ref' => $ref, 'name' => $oc->name($ref) . ' (' . Units::bytes((float)$sr['physical_size'] - (float)$sr['physical_utilisation']) . ' free)'];                }            }            usort($srs, fn ($a, $b) => ($b['ref'] === $default) <=> ($a['ref'] === $default));            foreach ($oc->networks() as $ref => $n) {                $networks[] = ['ref' => $ref, 'name' => (string)$n['name_label']];            }            $targets[] = ['index' => $index, 'name' => $other->name(), 'hosts' => $hosts, 'srs' => $srs, 'networks' => $networks];        }        return ['vm' => $vm, 'name' => $cache->name($vm), 'from' => $c->name(), 'running' => $cache->field($vm, 'power_state') === 'Running', 'disks' => $disks, 'vifs' => $vifs,            'targets' => $targets];    }    protected function loaded(array $params = []): void    {        $this->params = $params;        $this->heading->text = "Migrate '" . ($params['name'] ?? '') . "' to another pool";        $this->where->text = "From '" . ($params['from'] ?? '') . "'";        $this->disks->clear();        foreach ($params['disks'] ?? [] as $i => $d) {            $this->disks->add(FormLoader::widget(['type' => 'row', 'gap' => 8, 'children' => [['type' => 'label', 'text' => $d['label'], 'width' => 240],                ['type' => 'combobox', 'id' => "disk:$i", 'items' => [], 'grow' => 1]]]));        }        $this->disks->visible = ($params['disks'] ?? []) !== [];        $this->vifs->clear();        foreach ($params['vifs'] ?? [] as $i => $v) {            $this->vifs->add(FormLoader::widget(['type' => 'row', 'gap' => 8, 'children' => [['type' => 'label', 'text' => $v['label'], 'width' => 240],                ['type' => 'combobox', 'id' => "vif:$i", 'items' => [], 'grow' => 1]]]));        }        $this->vifs->visible = ($params['vifs'] ?? []) !== [];        $this->pool->items = array_column($params['targets'] ?? [], 'name');        $this->pool->selected = ($params['targets'] ?? []) === [] ? -1 : 0;        $this->how->text = !empty($params['running'])            ? 'The VM keeps running: its memory and its disks are copied to the other pool while it works (storage motion), then it carries on there.'            : 'The VM is halted: its disks are copied to the other pool, then it is removed from this one.';        $this->error->text = ($params['targets'] ?? []) === [] ? 'Connect another pool first.' : '';        $this->onPoolChange($this->pool, (int)$this->pool->selected, '');    }    /** The servers, SRs and networks of the pool chosen: each disk goes to its default SR, each interface to the network of the same name. */    public function onPoolChange(ComboBox $sender, int $index, string $item): void    {        $t = $this->params['targets'][$index] ?? null;        $this->host->items = array_column($t['hosts'] ?? [], 'name');        $this->host->selected = ($t['hosts'] ?? []) === [] ? -1 : 0;        foreach ($this->params['disks'] ?? [] as $i => $d) {            $box = $this->widget("disk:$i");            if ($box instanceof ComboBox) {                $box->items = array_column($t['srs'] ?? [], 'name');                $box->selected = ($t['srs'] ?? []) === [] ? -1 : 0;            }        }        foreach ($this->params['vifs'] ?? [] as $i => $v) {            $box = $this->widget("vif:$i");            if ($box instanceof ComboBox) {                $names = array_column($t['networks'] ?? [], 'name');                $box->items = $names;                $same = array_search($v['network'], $names, true);                $box->selected = $same !== false ? (int)$same : ($names === [] ? -1 : 0);            }        }        $this->migrate->enabled = $t !== null;    }    public function onMigrateClick(mixed $sender = null): void    {        $t = $this->params['targets'][(int)$this->pool->selected] ?? null;        $host = $t['hosts'][(int)$this->host->selected]['ref'] ?? null;        if ($t === null || $host === null) {            $this->error->text = 'Choose the destination pool and server.';            return;        }        $vdiMap = [];        foreach ($this->params['disks'] ?? [] as $i => $d) {            $box = $this->widget("disk:$i");            $sr = $box instanceof ComboBox ? ($t['srs'][(int)$box->selected]['ref'] ?? null) : null;            if ($sr === null) {                $this->error->text = "Choose where the disk '{$d['label']}' goes.";                return;            }            $vdiMap[$d['vdi']] = $sr;        }        $vifMap = [];        foreach ($this->params['vifs'] ?? [] as $i => $v) {            $box = $this->widget("vif:$i");            $net = $box instanceof ComboBox ? ($t['networks'][(int)$box->selected]['ref'] ?? null) : null;            if ($net === null) {                $this->error->text = "Choose the network of '{$v['label']}'.";                return;            }            $vifMap[$v['vif']] = $net;        }        $this->close(['crossPool' => ['vm' => $this->params['vm'], 'target' => $t['index'], 'host' => $host, 'vdiMap' => $vdiMap, 'vifMap' => $vifMap]]);    }    public function onCancelClick(Button $sender): void    {        $this->close();    }}