Quitter VMware après Broadcom : guide de migration vérifié
Réponse courte (vérifié le 2026-11-03) : depuis le rachat de VMware, Broadcom a cessé de vendre des licences perpétuelles, regroupé son catalogue en deux offres sur abonnement, fermé en Europe son ancien programme de partenaires cloud et, cet automne, retiré le téléchargement public de VDDK, la bibliothèque de disques que la plupart des outils de migration utilisent pour lire les disques VMware. The Register annonce aujourd'hui un remplaçant open source en lecture seule pour cette bibliothèque. Toutes les voies de sortie n'ont toutefois pas besoin de VDDK : un export OVA suivi d'une conversion de disque n'utilise aucun code VMware. Ci-dessous : les changements datés, des cibles réalistes pour une équipe européenne, la migration étape par étape et un convertisseur que nous avons exécuté sur un vrai export au format VMware.
Source. "Architect creates open-source replacement for VM migration tool VMware has hidden" (Simon Sharwood, The Register, 2026-11-03). Les autres faits sont accompagnés d'un lien là où ils apparaissent. Votre propre contrat peut différer des conditions publiques : faites confirmer les détails de licence par votre revendeur.
Ce que Broadcom a changé, avec les dates
| Date | Changement | Source |
|---|---|---|
| 2023-12-11 | Fin de la vente des licences perpétuelles et des renouvellements de support ; offre réduite aux abonnements VMware Cloud Foundation (VCF) et vSphere Foundation (VVF) | Actualités Broadcom, Krish Prasad |
| 2025-03-28 | Le distributeur Arrow informe ses partenaires que la commande minimale passe de 16 à 72 cœurs par gamme de produits à partir du 2025-04-10 | The Register |
| 2026-01-26 | Contrats des fournisseurs cloud (VCSP) dans l'EEE non renouvelés ; affaires en cours à clôturer avant le 2026-03-31 ; programme sur invitation uniquement ensuite | The Register, 2026-01-31 |
| Août-sept. 2026 | VDDK retiré du téléchargement public : "The VDDK is not a customer entitlement" (VDDK n'est pas un droit client), accès via certains partenaires TAP | TechTarget, 2026-09-09 |
| 2026-11-03 | OpenVDDK d'Andrija Panić : remplaçant direct en lecture seule, sous licence Apache, de libvixDiskLib.so.8.0.3, qui "contains no VMware code" (ne contient aucun code VMware) | The Register, 2026-11-03 |
Deux réserves. Le minimum de 72 cœurs a été rapporté par un distributeur, pas sur une page Broadcom que nous aurions trouvée : l'article de Broadcom sur le décompte des cœurs (KB 313548, vérifié le 2026-11-03) indique "You must license a minimum of 16 physical cores for each CPU" (au moins 16 cœurs physiques par CPU), et certains revendeurs affirment que le minimum de commande de 72 cœurs a de nouveau été supprimé. Demandez par écrit le nombre de cœurs figurant sur votre devis. Et le volet européen reste ouvert : l'association professionnelle du cloud CISPE a demandé en juillet 2025 au Tribunal de l'Union européenne d'annuler l'autorisation de la fusion par la Commission (affaire T-503/25) ; nous n'avons pas encore trouvé de décision.
Pourquoi VDDK compte, et quelles voies s'en passent
La bibliothèque de VDDK, libvixDiskLib, permet à un outil de lire les disques des VM qui tournent encore sous vSphere. The Register cite virt-v2v, nbdkit, les importateurs Forklift / MTV, Apache CloudStack et des scripts maison parmi les outils qui la chargent. L'extension VM Conversion de Microsoft pour Windows Admin Center (de vCenter vers Hyper-V, avec synchronisation à chaud via Change Block Tracking) est toujours signalée en préversion dans sa documentation (mise à jour le 2026-02-20), et les articles de lancement mentionnent VDDK parmi ses prérequis. OpenVDDK est la nouvelle option pour tous ces outils, avec l'avertissement de son auteur lui-même : les retours des testeurs sont "not independently audited customer endorsements" (pas des recommandations clients auditées de manière indépendante).
Voies sans aucun VDDK, selon la documentation d'entrée VMware de virt-v2v (vérifiée le 2026-11-03) : un export OVA (-i ova), les fichiers de la VM sur NFS ou via SSH (-i vmx, -it ssh), NFC, et le simple chemin HTTPS de vCenter, que la documentation qualifie de "the slowest method" (la méthode la plus lente). L'assistant d'import ESXi de Proxmox VE, annoncé le 2024-03-27, lit les disques via l'API ESXi avec son propre système de fichiers en espace utilisateur écrit en Rust et a été testé avec ESXi 6.5 à 8.0 (forum Proxmox).
Où migrer : cinq cibles réalistes
| Cible | Adaptée quand | Points de vigilance |
|---|---|---|
| Proxmox VE (KVM) sur votre matériel | 3 hôtes ou plus, une équipe qui veut une interface graphique et du clustering | Les mises à jour, le stockage (ZFS, Ceph) et le support sont à votre charge |
| OpenStack | De nombreux locataires, tout passe par une API | Lourd à exploiter ; rentable seulement à grande échelle |
| Hyper-V (Windows Server) | Parc majoritairement Windows, licences Datacenter déjà payées | L'outil de Microsoft exige d'abord les pilotes Hyper-V dans les invités Linux |
| Serveurs cloud (KVM) | De quelques VM à quelques dizaines, sans envie de gérer un hyperviseur | Un import par VM, nouvelles adresses IP |
| Serveurs dédiés + votre propre hyperviseur | Charge stable, vous voulez la machine entière | Vous exploitez et mettez à jour l'hyperviseur vous-même |
Migrer une VM en cinq étapes
- Inventorier et faire le ménage. Notez le firmware (BIOS ou EFI), le contrôleur de disque, le type de carte réseau, les IP statiques et toute licence liée à une adresse MAC. Supprimez ou consolidez les snapshots afin d'exporter un seul disque à jour.
- Préparer l'invité tant qu'il tourne encore sous VMware. Windows : installez dès maintenant les pilotes virtio (virtio-win) ; un invité Windows déplacé sur un disque virtio sans eux ne démarre pas. Linux : les pilotes virtio sont dans le noyau, mais un initramfs en mode host-only peut les omettre ; vérifiez avec
lsinitramfsoulsinitrdet reconstruisez-le si nécessaire. - Exporter ou se connecter. Arrêtez la VM et exportez un OVF / OVA (vSphere Client "Export OVF Template", ou ovftool), ou laissez virt-v2v ou l'assistant Proxmox la récupérer.
- Convertir. Le disque seul avec qemu-img, l'invité complet avec virt-v2v (qui ajuste aussi les pilotes), ou directement dans Proxmox à partir de l'OVF.
- Démarrer sur la console, puis nettoyer. Vérifiez le nom de la carte réseau et l'IP, supprimez VMware Tools ou open-vm-tools, installez qemu-guest-agent.
qemu-img convert -p -f vmdk -O qcow2 disk1.vmdk disk1.qcow2
virt-v2v -i ova VM.ova -o local -os /var/tmp -of qcow2
qm importovf 120 VM.ovf local-lvm
Ce sont les formes documentées dans le manuel de chaque outil ; nous n'avons pas exécuté ces trois commandes ici. La conversion que nous avons réellement exécutée figure ci-dessous.
Testé : ce que fait réellement une conversion (ova2raw.js)
Pour montrer les rouages, nous avons écrit la conversion en une centaine de lignes de Bun, sans dépendances. Le script ouvre un OVA (une simple archive tar), vérifie le manifeste SHA-256, affiche le matériel demandé par l'OVF, décompresse le VMDK streamOptimized grain par grain dans une image raw creuse et vérifie la GPT. Nous lui avons donné l'OVA officiel Ubuntu 24.04, un vrai export au format VMware : noble-server-cloudimg-amd64.ova, build du 2026-10-17, dont le SHA-256 correspondait au SHA256SUMS d'Ubuntu. Enregistrez-le sous ova2raw.js :
// ova2raw.js - check a VMware OVA export and turn its disk into a raw image for KVM
// usage: bun ova2raw.js <export.ova> <out.raw>
import { openSync, readSync, writeSync, ftruncateSync, closeSync, fstatSync } from 'node:fs'
import { inflateSync } from 'node:zlib'
import { createHash } from 'node:crypto'
const [ova, out] = process.argv.slice(2)
const fd = openSync(ova, 'r')
const read = (pos, len) => {
const b = Buffer.alloc(len)
readSync(fd, b, 0, len, pos)
return b
}
// 1. an OVA is a plain tar: list its members
const files = {}
for (let o = 0; o + 512 <= fstatSync(fd).size; ) {
const h = read(o, 512)
if (h.every(x => x === 0)) break
const name = h.toString('utf8', 0, 100).replace(/\0.*$/s, '')
const size = parseInt(h.toString('ascii', 124, 136).replace(/\0.*$/s, '').trim(), 8)
files[name] = { pos: o + 512, size }
o += 512 + Math.ceil(size / 512) * 512
}
const find = ext => Object.keys(files).find(n => n.endsWith(ext))
const text = n => read(files[n].pos, files[n].size).toString('utf8')
console.log('OVA members:', Object.entries(files).map(([n, f]) => `${n} (${f.size} B)`).join(', '))
// 2. the .mf manifest: every member's SHA-256 must match
const mf = find('.mf')
for (const line of mf ? text(mf).split('\n') : []) {
const m = line.match(/^SHA(1|256)\((.+)\)\s*=\s*([0-9a-f]+)/i)
if (!m || !files[m[2]]) continue
const h = createHash('sha' + m[1])
const f = files[m[2]]
for (let p = 0; p < f.size; p += 1 << 24) h.update(read(f.pos + p, Math.min(1 << 24, f.size - p)))
console.log(` manifest ${m[2]}: ${h.digest('hex') === m[3].toLowerCase() ? 'OK' : 'MISMATCH'}`)
}
// 3. the .ovf descriptor: what the VM expects from its hardware
const ovf = text(find('.ovf'))
const items = [...ovf.matchAll(/<(?:ovf:)?Item\b[\s\S]*?<\/(?:ovf:)?Item>/g)].map(x => x[0])
const tag = (s, t) => s.match(new RegExp(`<(?:rasd:)?${t}>([^<]*)<`))?.[1]
const kind = { 3: 'CPU', 4: 'RAM', 5: 'IDE controller', 6: 'SCSI controller', 10: 'NIC', 17: 'disk', 20: 'SATA controller' }
console.log('OVF:', ovf.match(/vmw:osType="([^"]+)"/)?.[1] ?? '?', '| firmware',
ovf.match(/key="firmware" ovf:value="([^"]+)"/)?.[1] ?? 'bios (default)')
for (const it of items) {
const k = kind[tag(it, 'ResourceType')]
if (k) console.log(` ${k.padEnd(16)} ${tag(it, 'ElementName') ?? ''} ${tag(it, 'ResourceSubType') ?? ''} ${tag(it, 'VirtualQuantity') ?? ''}`.trimEnd())
}
// 4. the streamOptimized VMDK: a header, then compressed grains and markers
const vmdk = files[find('.vmdk')]
const hd = read(vmdk.pos, 512)
if (hd.readUInt32LE(0) !== 0x564d444b) throw new Error('not a sparse VMDK (no KDMV magic)')
const capacity = Number(hd.readBigUInt64LE(12)) * 512
const desc = read(vmdk.pos + Number(hd.readBigUInt64LE(28)) * 512, Number(hd.readBigUInt64LE(36)) * 512)
console.log(`VMDK ${desc.toString().match(/createType="([^"]+)"/)?.[1]}, virtual size ${capacity} B, grain ${Number(hd.readBigUInt64LE(20)) * 512} B`)
const ofd = openSync(out, 'w')
ftruncateSync(ofd, capacity) // sparse: unwritten grains stay holes = zeros
let pos = Number(hd.readBigUInt64LE(64)) * 512
let grains = 0
let written = 0
while (pos < vmdk.size) {
const m = read(vmdk.pos + pos, 512)
const val = Number(m.readBigUInt64LE(0))
const size = m.readUInt32LE(8)
if (size) { // compressed grain: lba, size, zlib data
const raw = inflateSync(read(vmdk.pos + pos + 12, size))
writeSync(ofd, raw, 0, raw.length, val * 512)
grains++
written += raw.length
pos += Math.ceil((12 + size) / 512) * 512
} else if (m.readUInt32LE(12) === 0) break // end-of-stream marker
else pos += 512 + val * 512 // grain table / directory / footer: metadata only
}
closeSync(ofd)
console.log(`Wrote ${out}: ${grains} grains, ${written} B of data in ${capacity} B`)
// 5. check the result: a valid GPT, and the file systems where the partitions say they are
const rfd = openSync(out, 'r')
const at = (p, l) => { const b = Buffer.alloc(l); readSync(rfd, b, 0, l, p); return b }
const gpt = at(512, 92)
const crc = Buffer.from(gpt)
crc.writeUInt32LE(0, 16)
console.log(`GPT "${gpt.toString('ascii', 0, 8)}", header CRC ${(Bun.hash.crc32(crc) >>> 0) === gpt.readUInt32LE(16) ? 'OK' : 'BAD'}`)
const n = gpt.readUInt32LE(80)
const es = gpt.readUInt32LE(84)
const ents = at(Number(gpt.readBigUInt64LE(72)) * 512, n * es)
console.log(` partition array CRC ${(Bun.hash.crc32(ents) >>> 0) === gpt.readUInt32LE(88) ? 'OK' : 'BAD'}`)
for (let i = 0; i < n; i++) {
const e = ents.subarray(i * es, (i + 1) * es)
if (e.subarray(0, 16).every(x => x === 0)) continue
const first = Number(e.readBigUInt64LE(32))
const last = Number(e.readBigUInt64LE(40))
const sb = at(first * 512 + 1024, 136)
const fs = sb.readUInt16LE(56) === 0xef53 ? `ext4 label "${sb.toString('ascii', 120, 136).replace(/\0.*$/s, '')}"`
: at(first * 512, 512).toString('ascii', 82, 87) === 'FAT32' ? 'FAT32 (EFI system partition)'
: e.toString('ascii', 0, 16) === 'Hah!IdontNeedEFI' ? 'BIOS boot (GRUB for legacy BIOS)' : 'unknown'
console.log(` p${i + 1} ${String((last - first + 1) * 512 / 2 ** 20 | 0).padStart(5)} MiB ${fs}`)
}
bun ova2raw.js noble-server-cloudimg-amd64.ova noble.raw
Sortie, exécuté sur un serveur DCXV Cloud avec Bun 1.4.4 le 2026-11-03 :
OVA members: ubuntu-noble-24.04-cloudimg.ovf (11186 B), ubuntu-noble-24.04-cloudimg.mf (213 B), ubuntu-noble-24.04-cloudimg.vmdk (590749696 B)
manifest ubuntu-noble-24.04-cloudimg.vmdk: OK
manifest ubuntu-noble-24.04-cloudimg.ovf: OK
OVF: ubuntu64Guest | firmware bios (default)
CPU 2 virtual CPU(s) 2
RAM 1024MB of memory 1024
SCSI controller SCSI Controller 0 VirtualSCSI
IDE controller VirtualIDEController 1
IDE controller VirtualIDEController 0
disk Hard Disk 1
NIC Ethernet 1 VmxNet3
VMDK streamOptimized, virtual size 10737418240 B, grain 65536 B
Wrote noble.raw: 28663 grains, 1878458368 B of data in 10737418240 B
GPT "EFI PART", header CRC OK
partition array CRC OK
p1 2558 MiB ext4 label "cloudimg-rootfs"
p14 4 MiB BIOS boot (GRUB for legacy BIOS)
p15 106 MiB FAT32 (EFI system partition)
p16 913 MiB ext4 label "BOOT"
Trois exécutions ont pris de 14.6 à 16.2 s en temps réel sur ce serveur. Le fichier raw fait 10 GiB et occupe 1.8 GB sur le disque.
Ce que la sortie vous apprend avant la bascule
- VmxNet3 et VirtualSCSI sont des périphériques VMware. Sous KVM, ils deviennent virtio-net et virtio-blk ou virtio-scsi. L'invité a besoin des pilotes pour les deux avant le premier démarrage, et l'interface réseau change généralement de nom : une IP statique liée à l'ancien nom ne montera pas. Utilisez la console pour le premier démarrage.
- Le firmware doit correspondre. Cet OVF ne définit rien, c'est donc du BIOS ; le disque contient à la fois une partition de démarrage BIOS et une partition système EFI, il démarre donc dans les deux cas. Une VM exportée avec
firmware=efinécessite l'UEFI (OVMF) sur la cible, sinon elle ne trouve aucun chargeur d'amorçage. - Un disque fin ne reste fin que si vous y veillez. 10 GiB virtuels, 1.8 GB de données. Copiez avec des outils qui gèrent les fichiers creux (
cp --sparse=always,rsync -S) ou utilisez qcow2, sinon vous transférez et stockez des zéros. - Le manifeste est votre contrôle de transfert. Vérifiez le SHA-256 après chaque copie entre sites : un envoi interrompu se convertit sans broncher en un disque qui échouera plus tard.
Planifier l'interruption de service
Une migration à froid coûte export + transfert + conversion + premier démarrage. Chronométrez chaque étape sur une VM de test, puis multipliez. Calcul du transfert (le nôtre, hors surcoût protocolaire) : 1.8 GB à 1 Gbit/s prennent environ 15 s, 1 TB environ 2.2 heures. Les outils à chaud copient pendant que la VM tourne et n'envoient à la fin que les blocs modifiés ; les testeurs cités par The Register rapportent des exécutions à froid et à chaud avec Forklift et MTV. Pour les bases de données et les applications chargées, la réplication suivie d'une bascule DNS vaut mieux que toute copie de disque : baissez d'abord le TTL (voir propagation DNS et TTL) et suivez les check-lists de comment migrer vers un serveur cloud.
Quand les serveurs cloud ou dédiés DCXV conviennent
- Une poignée de VM, pas d'équipe dédiée à l'hyperviseur : nos serveurs cloud tournent sous KVM avec des disques virtio. Dans le panneau, Réinstaller -> "Image personnalisée (URL)" accepte un fichier qcow2, raw ou vmdk via https ; une image plus grande que le disque de l'offre agrandit le disque et est facturée comme une extension de disque, et les paramètres réseau ne sont appliqués que si l'image contient cloud-init. Les offres commencent à EUR 15.00/mois (2 vCPU, 4 GB RAM, 40 GB ; vérifié le 2026-11-03, hors TVA).
- Des dizaines de VM ou votre propre cluster Proxmox : des serveurs dédiés à partir de EUR 210/mois, avec l'hyperviseur de votre choix.
- Invités Windows : nous revendons des licences Windows Server ; demandez avant la migration pour que la licence soit prête dès le premier jour.
Les deux fonctionnent dans nos centres de données Tier III de Prague et de Covilha.
Planifiez la migration avec nous
Exécutez ova2raw.js sur une VM exportée et envoyez les lignes matériel de sa sortie à sales@dcxv.com, ou lancez un serveur cloud et testez l'import vous-même.
