gestion multi subnet #33

Closed
opened 2026-05-23 20:52:37 +00:00 by nicolas.boufideline · 3 comments

il faut implementer une gestion multi subnet dans les vms, l'api la gere deja il faut le faire coter dispatcher et gestion qemu

-netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device virtio-net-pci,netdev=net0,mac=00:22:33:00:00:01
il faut implementer une gestion multi subnet dans les vms, l'api la gere deja il faut le faire coter dispatcher et gestion qemu ``` -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device virtio-net-pci,netdev=net0,mac=00:22:33:00:00:01 ```
Author
Owner

Constat

L'information est perdue dès le handler. internal/api/agent/vms.go parcourt req.Interfaces uniquement pour trouver celle marquée primary, puis ne transmet que primary.Subnet et primary.IP. Les autres interfaces sont jetées en silence, sans erreur ni log : une VM demandée avec trois interfaces en obtient une, et l'appelant reçoit 202.

Tout le reste de la chaîne est singulier : StartVMCommand{Subnet, IP}, les clés vm/<name>/{subnet,ip,tap_id}, vmData, un seul netns.Call pour le tap et la redirection metadata, -netdev tap,id=net0 avec addr=0x03 en dur côté QEMU, et vmFromDB qui reconstruit un tableau d'une seule entrée toujours marquée primary.

Contrainte posée : multi-interfaces dans un même VPC uniquement. Donc un seul netns, ce qui simplifie StartVM et rend le serveur de métadonnées joignable depuis toutes les interfaces de la VM.

Le point dur : une seule route par défaut

Une route par défaut est aujourd'hui toujours annoncée par chaque subnet. Une VM multi-NIC en recevrait autant que d'interfaces. Il faut donc que la route par défaut devienne une propriété du couple VM × interface, pas du subnet.

Faits mesurés sur lab3 — dnsmasq 2.90

Testés en netns isolés, pas supposés.

  1. Une option taggée l'emporte sur la même option non taggée, et le remplacement se fait par numéro d'option : un override de la 121 laisse l'option 3 non taggée intacte.
  2. Un override de l'option 121 remplace la précédente en entier, il ne s'y ajoute pas. Vérifié : une 121 taggée ne contenant que 0.0.0.0/0 fait disparaître la route /32 vers 169.254.169.254. Tout override doit donc réémettre la route metadata, sous peine de casser le provisionnement cloud-init.
  3. Les fichiers déposés dans dhcp-hostsdir / dhcp-optsdir sont lus à chaud, sans signal ni redémarrage (read /path/to/file dans le log). En revanche un fichier supprimé n'est pas pris en compte avant SIGHUP ou redémarrage.
  4. Une entrée pré-générée dhcp-host bloque celle du hostsdir pour la même adresse : duplicate dhcp-host IP address … at line 1 of hosts.d/vm1, le tag n'est jamais posé, et la VM reçoit la mauvaise route sans qu'aucune erreur ne remonte — dnsmasq démarre normalement. La pré-génération des entrées du range doit donc disparaître.
  5. Poser le tag par dhcp-mac dans le .conf fonctionne aussi, mais le .conf n'est pas rechargé à chaud : il faudrait redémarrer dnsmasq à chaque création de VM. Écarté.
  6. Bail 12 h, T1 à 6 h. Modifier les routes d'une VM déjà démarrée ne prend effet qu'au renouvellement, donc jusqu'à six heures plus tard, ou au redémarrage du guest. Sans effet sur la création d'une VM ; à retenir pour #40.

Schéma KV

Même motif que disk/<dev> (#32) et metadata/<document> (#34) :

vm/<name>/nic/<i>/subnet   → <subnet-name>
vm/<name>/nic/<i>/ip       → <ip>
vm/<name>/nic/<i>/tap_id   → <int>
vm/<name>/nic/<i>/primary  → "true"        (une seule)

L'index n'est pas décoratif : il détermine le slot PCI (0x03 + i, dans la plage 0x03–0x1d réservée au multi-NIC par #36), donc le nom de l'interface dans le guest. Le nommage reste stable si une interface est retirée.

Étapes

1 — dnsmasq passe aux répertoires. Le .conf garde dhcp-range, le DNS, la route /32 metadata non taggée, et pointe vers hostsdir/optsdir. Suppression de la pré-génération du range : la table IP→MAC reste en base sous subnet/<name>/dhcp/<ip>, où GetMACForIP va la chercher ; seules les entrées réellement utilisées descendent dans dnsmasq. Vérification : une VM mono-NIC se comporte exactement comme avant.

2 — Schéma KV et migration. Migration idempotente au démarrage : toute VM ayant /subnet sans /nic/ devient nic/0 avec primary=true, anciennes clés supprimées. Livrée dans le même changement que les lecteurs — la leçon de l'enum d'états (#37) : une étape intermédiaire qui lit le nouveau schéma sans la migration rendrait toutes les VM existantes illisibles.

3 — Dispatcher et API. StartVMCommand.NICs []VMNIC, refus si zéro ou plusieurs primaires, écriture des clés. vmFromDB reconstruit toutes les interfaces au lieu d'en fabriquer une. api/agent.yaml.

4 — StartVM / StopVM. Boucle sur les interfaces : un tap par NIC, une redirection DNAT par IP, les fichiers dnsmasq par interface. StopVM symétrique, et à la suppression retrait des fichiers puis Restart + Status sur l'unit dnsmasq — un retrait n'est pas relu à chaud (fait 3).

5 — QEMU. Un -netdev/-device par interface, id=net<i>, addr=0x03+i. Garde-fou au-delà de 27 interfaces, sur le modèle du garde-fou vdX de #36.

6 — Watchdog. checkVMTap ne vérifie qu'un tap ; il doit tous les vérifier.

7 — lab3. VM mono-NIC en non-régression, VM bi-NIC, et surtout ip route dans le guest : une seule route par défaut, celle de l'interface primaire, et la route metadata présente sur toutes.

Métadonnées

metadata_port reste au niveau de la VM. Le serveur écoute sur l'interface_ip du subnet primaire, et la redirection DNAT est installée pour chacune des IP de la VM vers cette même adresse. Tout étant dans le même VPC, donc le même netns, elle est joignable depuis n'importe quelle interface — les métadonnées fonctionnent quelle que soit la route empruntée, sans tag.

## Constat L'information est perdue dès le handler. `internal/api/agent/vms.go` parcourt `req.Interfaces` uniquement pour trouver celle marquée `primary`, puis ne transmet que `primary.Subnet` et `primary.IP`. **Les autres interfaces sont jetées en silence**, sans erreur ni log : une VM demandée avec trois interfaces en obtient une, et l'appelant reçoit 202. Tout le reste de la chaîne est singulier : `StartVMCommand{Subnet, IP}`, les clés `vm/<name>/{subnet,ip,tap_id}`, `vmData`, un seul `netns.Call` pour le tap et la redirection metadata, `-netdev tap,id=net0` avec `addr=0x03` en dur côté QEMU, et `vmFromDB` qui reconstruit un tableau d'une seule entrée toujours marquée `primary`. **Contrainte posée** : multi-interfaces **dans un même VPC uniquement**. Donc un seul netns, ce qui simplifie `StartVM` et rend le serveur de métadonnées joignable depuis toutes les interfaces de la VM. ## Le point dur : une seule route par défaut Une route par défaut est aujourd'hui toujours annoncée par chaque subnet. Une VM multi-NIC en recevrait autant que d'interfaces. Il faut donc que la route par défaut devienne **une propriété du couple VM × interface**, pas du subnet. ## Faits mesurés sur lab3 — dnsmasq 2.90 Testés en netns isolés, pas supposés. 1. **Une option taggée l'emporte sur la même option non taggée**, et le remplacement se fait **par numéro d'option** : un override de la 121 laisse l'option 3 non taggée intacte. 2. **Un override de l'option 121 remplace la précédente en entier**, il ne s'y ajoute pas. Vérifié : une 121 taggée ne contenant que `0.0.0.0/0` fait disparaître la route `/32` vers `169.254.169.254`. **Tout override doit donc réémettre la route metadata**, sous peine de casser le provisionnement cloud-init. 3. **Les fichiers déposés dans `dhcp-hostsdir` / `dhcp-optsdir` sont lus à chaud**, sans signal ni redémarrage (`read /path/to/file` dans le log). En revanche un fichier **supprimé** n'est pas pris en compte avant SIGHUP ou redémarrage. 4. **Une entrée pré-générée `dhcp-host` bloque celle du `hostsdir`** pour la même adresse : `duplicate dhcp-host IP address … at line 1 of hosts.d/vm1`, le tag n'est jamais posé, et la VM reçoit la mauvaise route **sans qu'aucune erreur ne remonte** — dnsmasq démarre normalement. La pré-génération des entrées du range doit donc disparaître. 5. Poser le tag par `dhcp-mac` dans le `.conf` fonctionne aussi, mais le `.conf` n'est pas rechargé à chaud : il faudrait redémarrer dnsmasq à chaque création de VM. Écarté. 6. **Bail 12 h, T1 à 6 h.** Modifier les routes d'une VM déjà démarrée ne prend effet qu'au renouvellement, donc jusqu'à six heures plus tard, ou au redémarrage du guest. Sans effet sur la création d'une VM ; à retenir pour #40. ## Schéma KV Même motif que `disk/<dev>` (#32) et `metadata/<document>` (#34) : ``` vm/<name>/nic/<i>/subnet → <subnet-name> vm/<name>/nic/<i>/ip → <ip> vm/<name>/nic/<i>/tap_id → <int> vm/<name>/nic/<i>/primary → "true" (une seule) ``` L'index n'est pas décoratif : il détermine le **slot PCI** (`0x03 + i`, dans la plage `0x03`–`0x1d` réservée au multi-NIC par #36), donc le nom de l'interface dans le guest. Le nommage reste stable si une interface est retirée. ## Étapes **1 — dnsmasq passe aux répertoires.** Le `.conf` garde `dhcp-range`, le DNS, la route `/32` metadata **non taggée**, et pointe vers `hostsdir`/`optsdir`. Suppression de la pré-génération du range : la table IP→MAC reste en base sous `subnet/<name>/dhcp/<ip>`, où `GetMACForIP` va la chercher ; seules les entrées réellement utilisées descendent dans dnsmasq. *Vérification* : une VM mono-NIC se comporte exactement comme avant. **2 — Schéma KV et migration.** Migration idempotente au démarrage : toute VM ayant `/subnet` sans `/nic/` devient `nic/0` avec `primary=true`, anciennes clés supprimées. **Livrée dans le même changement que les lecteurs** — la leçon de l'enum d'états (#37) : une étape intermédiaire qui lit le nouveau schéma sans la migration rendrait toutes les VM existantes illisibles. **3 — Dispatcher et API.** `StartVMCommand.NICs []VMNIC`, refus si zéro ou plusieurs primaires, écriture des clés. `vmFromDB` reconstruit toutes les interfaces au lieu d'en fabriquer une. `api/agent.yaml`. **4 — `StartVM` / `StopVM`.** Boucle sur les interfaces : un tap par NIC, une redirection DNAT par IP, les fichiers dnsmasq par interface. `StopVM` symétrique, et à la suppression **retrait des fichiers puis `Restart` + `Status`** sur l'unit dnsmasq — un retrait n'est pas relu à chaud (fait 3). **5 — QEMU.** Un `-netdev`/`-device` par interface, `id=net<i>`, `addr=0x03+i`. Garde-fou au-delà de 27 interfaces, sur le modèle du garde-fou `vdX` de #36. **6 — Watchdog.** `checkVMTap` ne vérifie qu'un tap ; il doit tous les vérifier. **7 — lab3.** VM mono-NIC en non-régression, VM bi-NIC, et surtout `ip route` dans le guest : **une seule** route par défaut, celle de l'interface primaire, et la route metadata présente sur toutes. ## Métadonnées `metadata_port` reste au niveau de la VM. Le serveur écoute sur l'`interface_ip` du subnet primaire, et la redirection DNAT est installée pour **chacune des IP** de la VM vers cette même adresse. Tout étant dans le même VPC, donc le même netns, elle est joignable depuis n'importe quelle interface — les métadonnées fonctionnent quelle que soit la route empruntée, sans tag.
Author
Owner

Correction du découpage

Le découpage en sept étapes que j'ai décrit plus haut n'est pas déployable tel quel : deux étapes laissent le dépôt incohérent entre elles. C'est la leçon de #37 — chaque étape doit laisser le dépôt fonctionnel — que je n'avais pas appliquée à mon propre plan.

Étape 1 annonçait la suppression de la pré-génération avec pour vérification « une VM mono-NIC se comporte comme avant ». Impossible : avec dhcp-range=…,static il n'y a pas de pool dynamique, donc sans réservation par VM aucune VM n'obtient d'adresse. L'écriture du fichier de réservation a donc été intégrée à l'étape 1.

Étapes 2 et 3 séparaient le schéma KV et ses lecteurs (2) de l'écrivain, le dispatcher (3). Entre les deux, loadVM lirait nic/<i> pendant que Prepare écrirait encore vm/<name>/subnet : toute VM créée dans cet intervalle serait illisible. Les deux sont fusionnées.

Découpage effectif

Étape Contenu Comportement observable
1 ✅ répertoires dhcp-hostsdir/dhcp-optsdir, fin de la pré-génération, un fichier de réservation par VM (sans tag) inchangé
2 schéma vm/<name>/nic/<i>/…, migration, dispatcher, loadVM, vmFromDB, API inchangé — une seule interface
3 boucle multi-NIC : taps, redirections DNAT, options DHCP taggées par interface multi-interfaces actif
4 QEMU : un -netdev/-device par interface, slot PCI 0x03 + i
5 watchdog : vérifier tous les taps
6 validation sur lab3

L'étape 2 change le schéma sans changer le comportement : une collection à un seul élément. C'est ce qui la rend vérifiable — une VM existante continue de tourner, une VM créée après est identique à ce qu'elle était.

Étape 1 — livrée

Le .conf d'un subnet passe de 517 à 7 lignes. Les options restent non taggées à ce stade.

no-resolv
dhcp-range=10.1.0.0,static,255.255.254.0,12h
dhcp-option=121,169.254.169.254/32,10.1.1.1,192.168.0.0/16,10.1.1.1,0.0.0.0/0,10.1.1.1
dhcp-option=3,10.1.1.1
dhcp-option=6,1.1.1.1,8.8.8.8
dhcp-hostsdir=/etc/dnsmasq.d/vp-admin_br-000001.hosts.d
dhcp-optsdir=/etc/dnsmasq.d/vp-admin_br-000001.opts.d

La table IP→MAC conserve ses 512 entrées, mais uniquement en base sous subnet/<name>/dhcp/<ip>, où GetMACForIP va la chercher. Seules les réservations utilisées descendent dans dnsmasq.

  • StartVM écrit hosts.d/<vm> ; StopVM retire le fichier puis redémarre dnsmasq et vérifie son statut — un ajout est relu à chaud, un retrait ne l'est pas. pkg/systemd a gagné Restart.
  • DeleteSubnet supprime les deux répertoires.
  • WriteReservations échoue si la liste est vide ou si une MAC ou une IP manque : écrire un fichier vide donnerait une VM sans adresse, sans aucun signal.
  • Pas de redémarrage si l'unit n'est pas active — sur un subnet à moitié démonté, cela relancerait un dnsmasq dont on veut se débarrasser.
  • TestGenerateConfig_NoPreGeneratedHosts verrouille la décision, avec sa raison en commentaire.
## Correction du découpage Le découpage en sept étapes que j'ai décrit plus haut n'est pas déployable tel quel : deux étapes laissent le dépôt incohérent entre elles. C'est la leçon de #37 — chaque étape doit laisser le dépôt fonctionnel — que je n'avais pas appliquée à mon propre plan. **Étape 1** annonçait la suppression de la pré-génération avec pour vérification « une VM mono-NIC se comporte comme avant ». Impossible : avec `dhcp-range=…,static` il n'y a pas de pool dynamique, donc sans réservation par VM **aucune VM n'obtient d'adresse**. L'écriture du fichier de réservation a donc été intégrée à l'étape 1. **Étapes 2 et 3** séparaient le schéma KV et ses lecteurs (2) de l'écrivain, le dispatcher (3). Entre les deux, `loadVM` lirait `nic/<i>` pendant que `Prepare` écrirait encore `vm/<name>/subnet` : **toute VM créée dans cet intervalle serait illisible**. Les deux sont fusionnées. ## Découpage effectif | Étape | Contenu | Comportement observable | |---|---|---| | **1** ✅ | répertoires `dhcp-hostsdir`/`dhcp-optsdir`, fin de la pré-génération, un fichier de réservation par VM (sans tag) | inchangé | | **2** | schéma `vm/<name>/nic/<i>/…`, migration, dispatcher, `loadVM`, `vmFromDB`, API | inchangé — **une seule interface** | | **3** | boucle multi-NIC : taps, redirections DNAT, options DHCP taggées par interface | multi-interfaces actif | | **4** | QEMU : un `-netdev`/`-device` par interface, slot PCI `0x03 + i` | | | **5** | watchdog : vérifier tous les taps | | | **6** | validation sur lab3 | | L'étape 2 change le schéma sans changer le comportement : une collection à un seul élément. C'est ce qui la rend vérifiable — une VM existante continue de tourner, une VM créée après est identique à ce qu'elle était. ## Étape 1 — livrée Le `.conf` d'un subnet passe de 517 à 7 lignes. Les options restent non taggées à ce stade. ``` no-resolv dhcp-range=10.1.0.0,static,255.255.254.0,12h dhcp-option=121,169.254.169.254/32,10.1.1.1,192.168.0.0/16,10.1.1.1,0.0.0.0/0,10.1.1.1 dhcp-option=3,10.1.1.1 dhcp-option=6,1.1.1.1,8.8.8.8 dhcp-hostsdir=/etc/dnsmasq.d/vp-admin_br-000001.hosts.d dhcp-optsdir=/etc/dnsmasq.d/vp-admin_br-000001.opts.d ``` La table IP→MAC conserve ses 512 entrées, mais **uniquement en base** sous `subnet/<name>/dhcp/<ip>`, où `GetMACForIP` va la chercher. Seules les réservations utilisées descendent dans dnsmasq. - `StartVM` écrit `hosts.d/<vm>` ; `StopVM` retire le fichier **puis redémarre dnsmasq et vérifie son statut** — un ajout est relu à chaud, un retrait ne l'est pas. `pkg/systemd` a gagné `Restart`. - `DeleteSubnet` supprime les deux répertoires. - `WriteReservations` échoue si la liste est vide ou si une MAC ou une IP manque : écrire un fichier vide donnerait une VM sans adresse, sans aucun signal. - Pas de redémarrage si l'unit n'est pas active — sur un subnet à moitié démonté, cela relancerait un dnsmasq dont on veut se débarrasser. - `TestGenerateConfig_NoPreGeneratedHosts` verrouille la décision, avec sa raison en commentaire.
Author
Owner

Validé sur lab3

Mono-interface — non-régression

Table de routage du guest identique à avant, une seule interface, métadonnées joignables. Surtout : opts.d/ vide. Une VM mono-interface ne génère aucun fichier d'options, son comportement est rigoureusement inchangé — c'est le résultat qui concerne toutes les VM existantes.

00:22:33:00:01:02,10.1.1.2,set:i-mono-0     ← hosts.d/i-mono

Deux interfaces

$ ip route | grep -c "^default"
1

C'est l'objet du ticket. Deux interfaces, deux adresses, une seule route par défaut, celle de la primaire.

default          via 10.1.1.1   dev ens3 metric 100
10.1.0.0/23      dev ens3 scope link     metric 100
10.1.2.0/24      dev ens4 scope link     metric 101
169.254.169.254  via 10.1.1.1   dev ens3 metric 100
169.254.169.254  via 10.1.2.254 dev ens4 metric 101
192.168.0.0/16   via 10.1.1.1   dev ens3 metric 100
192.168.0.0/16   via 10.1.2.254 dev ens4 metric 101

Fichiers générés — le subnet de la primaire ne reçoit aucune option, celui de la seconde reçoit l'override :

hosts.d/i-bi (br-000001) : 00:22:33:00:01:03,10.1.1.3,set:i-bi-0
hosts.d/i-bi (br-000002) : 00:22:33:00:00:05,10.1.2.5,set:i-bi-1
opts.d/i-bi  (br-000001) : (aucun)
opts.d/i-bi  (br-000002) : tag:i-bi-1,3
                           tag:i-bi-1,121,169.254.169.254/32,10.1.2.254,192.168.0.0/16,10.1.2.254

Les slots PCI sont confirmés par les noms d'interfaces : ens3 et ens4 dérivent des slots 0x03 et 0x04, dérivés eux-mêmes de l'index. C'est ce qui garantit un nommage stable dans le guest (cf. #36).

Refus et suppression

Zéro comme deux interfaces primaires sont refusées en 400, exactly one interface must be primary. À la suppression, la VM disparaît des deux hosts.d et ses deux taps sont retirés du netns.

Une prévision que je corrige

J'avais annoncé que le choix entre les deux routes vers le CIDR du VPC et vers le serveur de métadonnées serait non déterministe. C'est faux : le client DHCP attribue une métrique par interface, dans l'ordre. La primaire étant en index 0 porte la métrique la plus basse et gagne systématiquement.

Nuance sans conséquence fonctionnelle : si la primaire n'était pas l'index 0, les routes metadata et VPC passeraient par l'autre interface. La route par défaut resterait correcte, et les métadonnées fonctionneraient quand même — la redirection DNAT est posée pour chaque IP de la VM, et les deux passerelles sont dans le même VPC.

Non vérifié

systemctl is-active sur les deux dnsmasq après suppression. Le DELETE n'a pas remonté d'erreur, donc le redémarrage a eu lieu, mais le contrôle explicite n'a pas été fait.

Livré

  • Étape 1 — dhcp-hostsdir/dhcp-optsdir, fin de la pré-génération, un fichier de réservation par VM. Le .conf d'un subnet passe de 517 à 7 lignes ; la table IP→MAC reste en base pour GetMACForIP.
  • Étape 2 — schéma vm/<name>/nic/<i>/…, migration idempotente au démarrage, dispatcher, loadVM, vmFromDB, watchdog. loadVM et l'API refusent désormais autre chose qu'exactement une interface primaire.
  • Étapes 3 et 4 — boucle multi-interfaces : un tap par interface, une redirection DNAT par IP, fichiers dnsmasq par subnet touché, et une paire -netdev/-device par interface avec le slot PCI dérivé de l'index. Garde-fou au-delà de 27 interfaces, la capacité de la carte PCI.

Prérequis de déploiement : le format du .conf ayant changé, les subnets existants doivent être recréés. Un agent à jour n'écrit plus dans l'ancien format.

Corrections apportées au découpage en cours de route, faute d'étapes déployables isolément, dans le commentaire précédent.

## Validé sur lab3 ### Mono-interface — non-régression Table de routage du guest identique à avant, une seule interface, métadonnées joignables. Surtout : `opts.d/` **vide**. Une VM mono-interface ne génère aucun fichier d'options, son comportement est rigoureusement inchangé — c'est le résultat qui concerne toutes les VM existantes. ``` 00:22:33:00:01:02,10.1.1.2,set:i-mono-0 ← hosts.d/i-mono ``` ### Deux interfaces ``` $ ip route | grep -c "^default" 1 ``` C'est l'objet du ticket. Deux interfaces, deux adresses, **une seule route par défaut**, celle de la primaire. ``` default via 10.1.1.1 dev ens3 metric 100 10.1.0.0/23 dev ens3 scope link metric 100 10.1.2.0/24 dev ens4 scope link metric 101 169.254.169.254 via 10.1.1.1 dev ens3 metric 100 169.254.169.254 via 10.1.2.254 dev ens4 metric 101 192.168.0.0/16 via 10.1.1.1 dev ens3 metric 100 192.168.0.0/16 via 10.1.2.254 dev ens4 metric 101 ``` Fichiers générés — le subnet de la primaire ne reçoit aucune option, celui de la seconde reçoit l'override : ``` hosts.d/i-bi (br-000001) : 00:22:33:00:01:03,10.1.1.3,set:i-bi-0 hosts.d/i-bi (br-000002) : 00:22:33:00:00:05,10.1.2.5,set:i-bi-1 opts.d/i-bi (br-000001) : (aucun) opts.d/i-bi (br-000002) : tag:i-bi-1,3 tag:i-bi-1,121,169.254.169.254/32,10.1.2.254,192.168.0.0/16,10.1.2.254 ``` **Les slots PCI sont confirmés par les noms d'interfaces** : `ens3` et `ens4` dérivent des slots `0x03` et `0x04`, dérivés eux-mêmes de l'index. C'est ce qui garantit un nommage stable dans le guest (cf. #36). ### Refus et suppression Zéro comme deux interfaces primaires sont refusées en **400**, `exactly one interface must be primary`. À la suppression, la VM disparaît des deux `hosts.d` et ses **deux taps** sont retirés du netns. ## Une prévision que je corrige J'avais annoncé que le choix entre les deux routes vers le CIDR du VPC et vers le serveur de métadonnées serait non déterministe. C'est faux : le client DHCP attribue **une métrique par interface**, dans l'ordre. La primaire étant en index 0 porte la métrique la plus basse et gagne systématiquement. Nuance sans conséquence fonctionnelle : si la primaire n'était pas l'index 0, les routes metadata et VPC passeraient par l'autre interface. La route par défaut resterait correcte, et les métadonnées fonctionneraient quand même — la redirection DNAT est posée pour chaque IP de la VM, et les deux passerelles sont dans le même VPC. ## Non vérifié `systemctl is-active` sur les deux dnsmasq après suppression. Le `DELETE` n'a pas remonté d'erreur, donc le redémarrage a eu lieu, mais le contrôle explicite n'a pas été fait. ## Livré - **Étape 1** — `dhcp-hostsdir`/`dhcp-optsdir`, fin de la pré-génération, un fichier de réservation par VM. Le `.conf` d'un subnet passe de 517 à 7 lignes ; la table IP→MAC reste en base pour `GetMACForIP`. - **Étape 2** — schéma `vm/<name>/nic/<i>/…`, migration idempotente au démarrage, dispatcher, `loadVM`, `vmFromDB`, watchdog. `loadVM` et l'API refusent désormais autre chose qu'exactement une interface primaire. - **Étapes 3 et 4** — boucle multi-interfaces : un tap par interface, une redirection DNAT par IP, fichiers dnsmasq par subnet touché, et une paire `-netdev`/`-device` par interface avec le slot PCI dérivé de l'index. Garde-fou au-delà de 27 interfaces, la capacité de la carte PCI. **Prérequis de déploiement** : le format du `.conf` ayant changé, **les subnets existants doivent être recréés**. Un agent à jour n'écrit plus dans l'ancien format. Corrections apportées au découpage en cours de route, faute d'étapes déployables isolément, dans le commentaire précédent.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
syonad/two#33
No description provided.