<?phpnamespace App\Form;use App\Model\Connection;use JumPHP\Gui\Button;use JumPHP\Gui\CheckBox;use JumPHP\Gui\ComboBox;use JumPHP\Gui\Container;use JumPHP\Gui\Form;use JumPHP\Gui\FormLoader;use JumPHP\Gui\Label;use JumPHP\Gui\TextBox;/** * The New Network wizard of XenCenter, on one page. What XAPI is asked (Feature\Storage\Actions::createNetwork): network.create * {name_label, name_description, MTU, other_config {automatic}}, then * - External: pool.create_VLAN(device, network, VLAN): a VLAN interface on that NIC of every server (VLAN 0: untagged); * - Single-server private: nothing more; * - Bonded: Bond.create(network, the PIFs of the NICs on each server, MAC of the first, mode, {}). * Closes with ['newNetwork' => [...]]. * * @property Label $where * @property ComboBox $kind * @property TextBox $name * @property TextBox $description * @property TextBox $mtu * @property Container $externalPanel * @property ComboBox $nic * @property TextBox $vlan * @property Container $privatePanel * @property Container $bondPanel * @property Container $bondNics * @property ComboBox $bondMode * @property CheckBox $auto * @property Label $error * @property Button $create * @property Button $cancel */final class NewNetworkForm extends Form{ public const TYPES = ['external', 'private', 'bond']; public const MODES = ['balance-slb', 'active-backup', 'lacp']; 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 NICs of the pool master (the same devices are used on every server) */ public static function paramsFor(Connection $c): array { $nics = []; foreach ($c->cache->pifsOf((string)$c->cache->master()) as $pif) { if (empty($pif['physical']) || (int)$pif['VLAN'] !== -1) { continue; } $speed = (int)($c->cache->get((string)($pif['metrics'] ?? ''))['speed'] ?? 0); $bonded = ($pif['bond_slave_of'] ?? 'OpaqueRef:NULL') !== 'OpaqueRef:NULL'; $nics[] = ['device' => (string)$pif['device'], 'label' => $pif['device'] . ($speed > 0 ? ' (' . ($speed >= 1000 ? ($speed / 1000) . ' Gbit/s' : "$speed Mbit/s") . ')' : '') . (!empty($pif['management']) ? ', management' : '') . ($bonded ? ', bonded' : ''), 'bonded' => $bonded, 'management' => !empty($pif['management'])]; } return ['server' => $c->name(), 'pool' => (string)$c->cache->poolRef(), 'nics' => $nics]; } protected function loaded(array $params = []): void { $this->params = $params; $this->where->text = "In '" . ($params['server'] ?? '') . "'"; $this->kind->selected = 0; $this->name->text = ''; $this->description->text = ''; $this->mtu->text = '1500'; $this->vlan->text = ''; $this->nic->items = array_column($params['nics'] ?? [], 'label'); $this->nic->selected = 0; $this->bondMode->selected = 0; $this->auto->checked = true; $this->bondNics->clear(); foreach ($params['nics'] ?? [] as $nic) { // a NIC already in a bond, or the management one, is not offered $this->bondNics->add(FormLoader::widget(['type' => 'checkbox', 'id' => 'bond:' . $nic['device'], 'text' => (string)$nic['device'], 'enabled' => !$nic['bonded'] && !$nic['management']])); } $this->error->text = ''; $this->onKindChange($this->kind, 0, ''); $this->setFocus($this->name); } public function networkType(): string { return self::TYPES[(int)$this->kind->selected] ?? 'external'; } public function onKindChange(ComboBox $sender, int $index, string $item): void { $type = $this->networkType(); $this->externalPanel->visible = $type === 'external'; $this->privatePanel->visible = $type === 'private'; $this->bondPanel->visible = $type === 'bond'; $this->error->text = ''; } /** @return list<string> the devices ticked for the bond */ public function bondMembers(): array { $out = []; foreach ($this->params['nics'] ?? [] as $nic) { $box = $this->widget('bond:' . $nic['device']); if ($box instanceof CheckBox && $box->checked) { $out[] = (string)$nic['device']; } } return $out; } public function onCreateClick(mixed $sender = null): void { $type = $this->networkType(); $name = trim($this->name->text); $mtu = trim($this->mtu->text); if ($name === '') { $this->error->text = 'Enter a name for the network.'; return; } if (!ctype_digit($mtu) || (int)$mtu < 68 || (int)$mtu > 9216) { $this->error->text = 'The MTU is a number from 68 to 9216 (1500 usually, 9000 for jumbo frames).'; return; } $spec = ['pool' => $this->params['pool'] ?? '', 'type' => $type, 'name' => $name, 'description' => $this->description->text, 'mtu' => (int)$mtu, 'auto' => $this->auto->checked]; if ($type === 'external') { $vlan = trim($this->vlan->text); $nic = $this->params['nics'][(int)$this->nic->selected] ?? null; if ($nic === null) { $this->error->text = 'This server has no network card to put the VLAN on.'; return; } if ($vlan !== '' && (!ctype_digit($vlan) || (int)$vlan > 4094)) { $this->error->text = 'The VLAN is a number from 0 to 4094.'; return; } $spec += ['device' => $nic['device'], 'vlan' => (int)$vlan]; } elseif ($type === 'bond') { $members = $this->bondMembers(); if (count($members) < 2) { $this->error->text = 'Choose two NICs or more to bond.'; return; } $spec += ['members' => $members, 'mode' => self::MODES[(int)$this->bondMode->selected] ?? 'balance-slb']; } $this->close(['newNetwork' => $spec]); } public function onCancelClick(Button $sender): void { $this->close(); }}