Sair do VMware após a Broadcom: guia de migração verificado
Resposta curta (verificado em 2026-11-03): desde que comprou a VMware, a Broadcom deixou de vender licenças perpétuas, reduziu o catálogo a dois pacotes de subscrição, encerrou na Europa o seu antigo programa de parceiros cloud e, neste outono, retirou o download público do VDDK, a biblioteca de discos que a maioria das ferramentas de migração usa para ler discos VMware. Hoje o The Register noticia um substituto open source, só de leitura, para essa biblioteca. Mas nem todas as saídas precisam do VDDK: uma exportação OVA mais uma conversão de disco não usa qualquer código da VMware. A seguir: as alterações com datas, destinos realistas para uma equipa na UE, a migração passo a passo e um conversor que executámos sobre uma exportação real em formato VMware.
Fonte. "Architect creates open-source replacement for VM migration tool VMware has hidden" (Simon Sharwood, The Register, 2026-11-03). Os restantes factos têm ligação no ponto onde aparecem. O seu contrato pode diferir das condições públicas, por isso confirme os detalhes da licença com o seu revendedor.
O que a Broadcom mudou, com datas
| Data | Alteração | Fonte |
|---|---|---|
| 2023-12-11 | Fim da venda de licenças perpétuas e das renovações de suporte; oferta reduzida às subscrições VMware Cloud Foundation (VCF) e vSphere Foundation (VVF) | Notícias da Broadcom, Krish Prasad |
| 2025-03-28 | O distribuidor Arrow informa os parceiros de que a encomenda mínima sobe de 16 para 72 núcleos por linha de produto a partir de 2025-04-10 | The Register |
| 2026-01-26 | Contratos de fornecedores cloud (VCSP) no EEE não renovados; negócios em curso a fechar até 2026-03-31; depois disso, programa apenas por convite | The Register, 2026-01-31 |
| Aug-Sep 2026 | VDDK retirado do download público: "The VDDK is not a customer entitlement" (o VDDK não é um direito do cliente), acesso através de parceiros TAP selecionados | TechTarget, 2026-09-09 |
| 2026-11-03 | OpenVDDK de Andrija Panić: substituto direto, só de leitura e com licença Apache, da libvixDiskLib.so.8.0.3 que "contains no VMware code" (não contém código da VMware) | The Register, 2026-11-03 |
Duas ressalvas. O mínimo de 72 núcleos foi comunicado por um distribuidor, não numa página da Broadcom que tenhamos encontrado: o artigo da Broadcom sobre a contagem de núcleos (KB 313548, verificado em 2026-11-03) diz "You must license a minimum of 16 physical cores for each CPU" (é preciso licenciar um mínimo de 16 núcleos físicos por CPU), e alguns revendedores afirmam que a encomenda mínima de 72 núcleos voltou a ser abandonada. Peça por escrito o número de núcleos do seu orçamento. Além disso, a frente europeia continua em aberto: a associação de fornecedores cloud CISPE pediu em julho de 2025 ao Tribunal Geral da UE que anulasse a aprovação da fusão pela Comissão (processo T-503/25); ainda não encontrámos qualquer decisão.
Porque é que o VDDK importa e que caminhos o evitam
A biblioteca do VDDK, libvixDiskLib, permite a uma ferramenta ler os discos de VMs que ainda estão no vSphere. O The Register cita o virt-v2v, o nbdkit, os importadores Forklift / MTV, o Apache CloudStack e scripts internos entre as ferramentas que a carregam. A extensão VM Conversion da Microsoft para o Windows Admin Center (de vCenter para Hyper-V, com sincronização a quente via Change Block Tracking) continua marcada como pré-visualização na sua documentação (atualizada em 2026-02-20), e a cobertura do lançamento inclui o VDDK entre os pré-requisitos. O OpenVDDK é a nova opção para todas elas, com o aviso do próprio autor de que os relatórios dos testadores são "not independently audited customer endorsements" (não são recomendações de clientes auditadas de forma independente).
Caminhos sem VDDK, segundo a documentação de entrada VMware do virt-v2v (verificada em 2026-11-03): uma exportação OVA (-i ova), os ficheiros da VM por NFS ou por SSH (-i vmx, -it ssh), NFC e o caminho HTTPS simples do vCenter, a que a documentação chama "the slowest method" (o método mais lento). O assistente de importação ESXi do Proxmox VE, anunciado em 2024-03-27, lê os discos através da API do ESXi com o seu próprio sistema de ficheiros em espaço de utilizador escrito em Rust e foi testado com ESXi 6.5 a 8.0 (fórum do Proxmox).
Para onde migrar: cinco destinos realistas
| Destino | Adequado quando | Atenção a |
|---|---|---|
| Proxmox VE (KVM) no seu hardware | 3 ou mais hosts, uma equipa que quer interface gráfica e clustering | As atualizações, o armazenamento (ZFS, Ceph) e o suporte ficam a seu cargo |
| OpenStack | Muitos inquilinos, tudo através de uma API | Pesado de operar; só compensa em grande escala |
| Hyper-V (Windows Server) | Parque com muito Windows, licenças Datacenter já pagas | A ferramenta da Microsoft precisa primeiro dos controladores Hyper-V nos convidados Linux |
| Servidores cloud (KVM) | De algumas a algumas dezenas de VMs, sem vontade de operar um hipervisor | Uma importação por VM, novos endereços IP |
| Servidores dedicados + o seu próprio hipervisor | Carga estável, quer a máquina inteira | É você que opera e atualiza o hipervisor |
Migrar uma VM em cinco passos
- Inventário e limpeza. Registe o firmware (BIOS ou EFI), a controladora de disco, o tipo de NIC, os IPs estáticos e qualquer licença associada a um endereço MAC. Elimine ou consolide os snapshots para exportar um único disco atual.
- Prepare o convidado enquanto ainda corre no VMware. Windows: instale já os controladores virtio (virtio-win); um convidado Windows passado para um disco virtio sem eles não arranca. Linux: os controladores virtio estão no kernel, mas um initramfs host-only pode deixá-los de fora; verifique com
lsinitramfsoulsinitrde reconstrua-o se for preciso. - Exporte ou ligue. Desligue a VM e exporte um OVF / OVA (no vSphere Client, "Export OVF Template", ou com o ovftool), ou deixe o virt-v2v ou o assistente do Proxmox puxá-la.
- Converta. Só o disco com o qemu-img, o convidado completo com o virt-v2v (que também ajusta os controladores), ou diretamente para o Proxmox a partir do OVF.
- Arranque na consola e depois limpe. Verifique o nome da NIC e o IP, remova as VMware Tools ou o open-vm-tools e instale o 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
Estas são as formas documentadas no manual de cada ferramenta; não executámos estas três aqui. A conversão que de facto executámos está abaixo.
Testado: o que uma conversão faz realmente (ova2raw.js)
Para mostrar as peças em movimento, escrevemos a conversão em cerca de 100 linhas de Bun sem dependências. Abre uma OVA (um tar simples), verifica o manifesto SHA-256, mostra o hardware que o OVF pede, descompacta o VMDK streamOptimized grão a grão para uma imagem raw esparsa e verifica a GPT. Usámos a OVA oficial do Ubuntu 24.04, uma exportação real em formato VMware: noble-server-cloudimg-amd64.ova, build de 2026-10-17, cujo SHA-256 coincidia com o SHA256SUMS do Ubuntu. Guarde como 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
Saída, executado num servidor DCXV Cloud com Bun 1.4.4 em 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"
Três execuções demoraram de 14.6 a 16.2 s de tempo real nesse servidor. O ficheiro raw tem 10 GiB de tamanho e ocupa 1.8 GB em disco.
O que a saída lhe diz antes da migração definitiva
- VmxNet3 e VirtualSCSI são dispositivos VMware. No KVM passam a virtio-net e virtio-blk ou virtio-scsi. O convidado precisa de controladores para ambos antes do primeiro arranque, e a interface de rede costuma receber um nome novo, por isso um IP estático associado ao nome antigo não sobe. Use a consola no primeiro arranque.
- O firmware tem de coincidir. Este OVF não define nada, portanto é BIOS; o disco tem tanto uma partição de arranque BIOS como uma partição de sistema EFI, por isso arranca das duas formas. Uma VM exportada com
firmware=efiprecisa de UEFI (OVMF) no destino; caso contrário, não encontra o gestor de arranque. - O thin só continua thin se o mantiver assim. 10 GiB virtuais, 1.8 GB de dados. Copie com ferramentas que respeitem ficheiros esparsos (
cp --sparse=always,rsync -S) ou use qcow2; caso contrário, vai transferir e guardar zeros. - O manifesto é o seu controlo da transferência. Verifique o SHA-256 depois de cada cópia entre locais: um upload interrompido converte-se sem queixas num disco que falha mais tarde.
Planeie o tempo de inatividade
Uma migração a frio custa exportação + transferência + conversão + primeiro arranque. Meça cada etapa numa VM de teste e depois multiplique. Contas da transferência (nossas, sem a sobrecarga do protocolo): 1.8 GB a 1 Gbit/s são cerca de 15 s, 1 TB cerca de 2.2 horas. As ferramentas a quente copiam enquanto a VM está a correr e no fim só enviam os blocos alterados; os testadores do The Register relatam execuções a frio e a quente com o Forklift e o MTV. Para bases de dados e aplicações com muita atividade, replicação mais uma mudança de DNS é melhor do que qualquer cópia de disco: baixe primeiro o TTL (ver propagação de DNS e TTL) e siga as listas de verificação de como migrar para um servidor cloud.
Quando os servidores cloud ou dedicados da DCXV são adequados
- Algumas VMs, sem equipa de hipervisores: os nossos servidores cloud correm em KVM com discos virtio. No painel, Reinstalar -> "Imagem própria (URL)" aceita um ficheiro qcow2, raw ou vmdk por https; uma imagem maior do que o disco do plano aumenta o disco e é faturada como upgrade de disco, e as definições de rede só são aplicadas se a imagem tiver cloud-init. Os planos começam em EUR 15.00/mês (2 vCPU, 4 GB RAM, 40 GB; verificado em 2026-11-03, sem IVA).
- Dezenas de VMs ou o seu próprio cluster Proxmox: servidores dedicados desde EUR 210/mês, com o hipervisor que escolher.
- Convidados Windows: revendemos licenças Windows Server; fale connosco antes da migração para que a licença esteja pronta no primeiro dia.
Ambos funcionam nos nossos centros de dados Tier III em Praga e Covilha.
Planeie a migração connosco
Execute o ova2raw.js numa VM exportada e envie as linhas de hardware da saída para sales@dcxv.com, ou crie um servidor cloud e teste a importação por si.
