로그인
KO
games/xencenter-xenadmin-xcp-jumphp / src / Xapi / Fake / NetworkOps.php · commit fb1b7c9
<?phpnamespace App\Xapi\Fake;use App\Xapi\XapiError;/** * The network calls of the demo pool (used by FakeXapi): network.create / destroy / set_MTU, pool.create_VLAN (a VLAN PIF on the * same NIC of each host), PIF.destroy (a VLAN interface), Bond.create / destroy. */trait NetworkOps{    /** @return array{0: float, 1: \Closure, 2: string, 3: string}|null */    private function networkOperation(string $class, string $method, array $p): ?array    {        $ref = is_scalar($p[0] ?? null) ? (string)$p[0] : '';        switch ("$class.$method") {            case 'network.create':                $r = (array)$p[0];                return [0.5, fn (): string => $this->networkCreate($r), 'Creating a network', ''];            case 'network.destroy':                $this->record('network', $ref);                return [0.5, function () use ($ref): string {                    $n = $this->db['network'][$ref];                    if ((array)$n['VIFs'] !== []) {                        throw new XapiError(['NETWORK_CONTAINS_VIF', ...array_map('strval', (array)$n['VIFs'])]);                    }                    if ((array)$n['PIFs'] !== []) {                        throw new XapiError(['NETWORK_CONTAINS_PIF', ...array_map('strval', (array)$n['PIFs'])]);                    }                    $this->delete('network', $ref);                    return '';                }, 'Removing a network', $ref];            case 'network.set_MTU':                $this->record('network', $ref);                $mtu = (int)($p[1] ?? 0);                return [0.3, function () use ($ref, $mtu): string {                    if ($mtu < 68 || $mtu > 9216) {                        throw new XapiError(['INVALID_VALUE', 'MTU', (string)$mtu]);                    }                    $this->set('network', $ref, ['MTU' => $mtu]);                    foreach ((array)$this->db['network'][$ref]['PIFs'] as $pif) {                        $this->set('pif', (string)$pif, ['MTU' => $mtu]);                    }                    return '';                }, 'Changing the MTU', $ref];            case 'pool.create_VLAN':                $device = $ref;                $network = (string)($p[1] ?? '');                $vlan = (int)($p[2] ?? -1);                $this->record('network', $network);                return [2.0, function () use ($device, $network, $vlan): array {                    if ($vlan < 0 || $vlan > 4094) {                        throw new XapiError(['VLAN_TAG_INVALID', (string)$vlan]);                    }                    $made = [];                    foreach ($this->db['host'] as $host => $h) {                        $nic = null;                        foreach ((array)$h['PIFs'] as $pif) {                            $r = $this->db['pif'][$pif];                            if ($r['device'] === $device && (int)$r['VLAN'] === $vlan) {                                throw new XapiError(['PIF_VLAN_EXISTS', (string)$pif]);                            }                            if ($r['device'] === $device && !empty($r['physical'])) {                                $nic = $r;                            }                        }                        if ($nic === null) {                            throw new XapiError(['PIF_DEVICE_NOT_FOUND', $device, $host]);                        }                        $made[] = $this->addPif($host, $network, $device, (string)$nic['MAC'], (string)$nic['metrics'], $vlan, false);                    }                    return $made;                }, 'Creating a VLAN', $network];            case 'pif.destroy':                $pif = $this->record('pif', $ref);                return [0.5, function () use ($ref, $pif): string {                    if (!empty($pif['physical']) || (int)$pif['VLAN'] < 0) {                        throw new XapiError(['PIF_IS_PHYSICAL', $ref]);                    }                    $this->removePif($ref);                    return '';                }, 'Removing a VLAN interface', $ref];            case 'bond.create':                $network = $ref;                $this->record('network', $network);                $members = array_map('strval', (array)($p[1] ?? []));                return [3.0, fn (): string => $this->bondCreate($network, $members, (string)($p[2] ?? ''), (string)($p[3] ?? 'balance-slb'), (array)($p[4] ?? [])),                    'Creating a bond', $network];            case 'bond.destroy':                $bond = $this->record('bond', $ref);                return [2.0, function () use ($ref, $bond): string {                    foreach ((array)$bond['slaves'] as $pif) {                        $this->set('pif', (string)$pif, ['bond_slave_of' => 'OpaqueRef:NULL']);                    }                    $this->removePif((string)$bond['master']);                    $this->delete('bond', $ref);                    return '';                }, 'Removing a bond', $ref];        }        return null;    }    private function networkCreate(array $r): string    {        $bridges = array_column($this->db['network'], 'bridge');        for ($n = 1; in_array('xapi' . $n, $bridges, true); $n++) {        }        $mtu = (int)($r['MTU'] ?? 1500) ?: 1500;        if ($mtu < 68 || $mtu > 9216) {            throw new XapiError(['INVALID_VALUE', 'MTU', (string)$mtu]);        }        return $this->add('network', ['name_label' => (string)($r['name_label'] ?? ''), 'name_description' => (string)($r['name_description'] ?? ''), 'bridge' => 'xapi' . $n,            'MTU' => $mtu, 'PIFs' => [], 'VIFs' => [], 'other_config' => (array)($r['other_config'] ?? []), 'tags' => (array)($r['tags'] ?? [])]);    }    private function addPif(string $host, string $network, string $device, string $mac, string $metrics, int $vlan, bool $physical, string $speedName = ''): string    {        $pif = $this->add('pif', ['device' => $device, 'host' => $host, 'network' => $network, 'MAC' => $mac, 'MTU' => (int)($this->db['network'][$network]['MTU'] ?? 1500),            'VLAN' => $vlan, 'IP' => '', 'netmask' => '', 'gateway' => '', 'DNS' => '', 'ip_configuration_mode' => 'None', 'management' => false, 'currently_attached' => true,            'physical' => $physical, 'metrics' => $metrics, 'bond_master_of' => [], 'bond_slave_of' => 'OpaqueRef:NULL']);        $this->append('host', $host, 'PIFs', $pif);        $this->append('network', $network, 'PIFs', $pif);        return $pif;    }    private function removePif(string $pif): void    {        $r = $this->db['pif'][$pif] ?? null;        if ($r === null) {            return;        }        $this->removeFrom('host', (string)$r['host'], 'PIFs', $pif);        $this->removeFrom('network', (string)$r['network'], 'PIFs', $pif);        $this->delete('pif', $pif);    }    /** @param list<string> $members */    private function bondCreate(string $network, array $members, string $mac, string $mode, array $properties): string    {        if (count($members) < 2) {            throw new XapiError(['PIF_BOND_NEEDS_MORE_MEMBERS']);        }        if (!in_array($mode, ['balance-slb', 'active-backup', 'lacp'], true)) {            throw new XapiError(['INVALID_VALUE', 'mode', $mode]);        }        $host = null;        foreach ($members as $pif) {            $r = $this->record('pif', $pif);            if ($host !== null && $r['host'] !== $host) {                throw new XapiError(['PIF_CANNOT_BOND_CROSS_HOST', $pif]);            }            $host = (string)$r['host'];            if (empty($r['physical']) || (int)$r['VLAN'] >= 0) {                throw new XapiError(['PIF_IS_VLAN', $pif]);            }            if (($r['bond_slave_of'] ?? 'OpaqueRef:NULL') !== 'OpaqueRef:NULL') {                throw new XapiError(['PIF_ALREADY_BONDED', $pif]);            }        }        $first = $this->db['pif'][$members[0]];        $n = 0;        foreach ((array)$this->db['host'][$host]['PIFs'] as $pif) {            $n += str_starts_with((string)$this->db['pif'][$pif]['device'], 'bond') ? 1 : 0;        }        $master = $this->addPif((string)$host, $network, 'bond' . $n, $mac !== '' ? $mac : (string)$first['MAC'], (string)$first['metrics'], -1, false);        $bond = $this->add('bond', ['master' => $master, 'slaves' => $members, 'mode' => $mode, 'properties' => $properties, 'primary_slave' => $members[0],            'links_up' => count($members), 'other_config' => []]);        $this->set('pif', $master, ['bond_master_of' => [$bond]]);        foreach ($members as $pif) {            $this->set('pif', $pif, ['bond_slave_of' => $bond]);        }        return $bond;    }}