apache / apache/cloudstack

KVM: host.volume.encryption always false with qemu-img >= 10.1 (help header changed to "Supported image formats:")

Aperta Adatta ai principianti
#13,574 1 commento 0 reazioni 0 assegnatari Vedi su GitHub
component:kvm type:technical-debt
Lingua principale
Java
Stelle
3.1k
Fork
1.4k
Merge medio
6g 19h
PR unite (30g)
32

Descrizione

### problem

On KVM hosts running **qemu-img 10.1.0 or newer**, the agent reports
`host.volume.encryption = false` even though the host fully supports LUKS
volume encryption (`qemu-img` lists the `luks` format and `cryptsetup` is
installed). As a result, encrypted service/disk offerings cannot be deployed
on affected hosts and encrypted volumes fail placement.

**Root cause — a string mismatch after a QEMU change.**

`LibvirtComputingResource.hostSupportsVolumeEncryption()` first checks whether
qemu-img supports the LUKS format and returns early if not, so `cryptsetup` is
never reached. The result is stored as `host.volume.encryption`.

The LUKS check is in `QemuImg.helpSupportsImageFormat()`:

```java
Pattern pattern = Pattern.compile(
"Supported\\sformats:[a-zA-Z0-9-_\\s]*?\\b" + format + "\\b",
CASE_INSENSITIVE);
```

The anchor `Supported\sformats:` expects `Supported` + one whitespace +
`formats:`. QEMU changed this header in 10.1.0:

| qemu-img version | `--help` header |
|------------------|-----------------|
| <= 10.0.0 | `Supported formats:` |
| >= 10.1.0 | `Supported image formats:` |

(`qemu-img.c`: `printf("\nSupported image formats:\n");`)

The inserted word `image` breaks the match, so `supportsImageFormat(LUKS)`
returns false and `host.volume.encryption` is stored as false. The `luks`
format is still in the list — it is just never matched.

**Evidence** — regex against the real host output:
current regex -> NO match (the bug):
$ qemu-img --help 2>&1 | grep -iP 'Supported\sformats:[a-zA-Z0-9-_\s]*?\bluks\b'
(empty)
with "image" optional -> matches:
$ qemu-img --help 2>&1 | grep -izoP 'Supported\s(image\s)?formats:[a-zA-Z0-9-_\s]*?\bluks\b'
Supported image formats:
... io_uring luks

Verified end-to-end: after making `image` optional in the regex,
`host.volume.encryption` flipped from false to true on affected hosts, with no
other change.

### versions

- Apache CloudStack: 4.22.1.0 (affected code path is unchanged on `main`)
- Hypervisor: KVM on RHEL 9.8
- qemu-kvm 10.1.0 (`qemu-kvm-10.1.0-17.el9_8.3`), qemu-img 10.1.0
- libvirt 11.10.0
- cryptsetup: installed and functional
- Primary storage: SharedMountPoint

Actual `qemu-img --help` on an affected host:

qemu-img version 10.1.0 (qemu-kvm-10.1.0-17.el9_8.3)
...
Supported image formats:
blkdebug blklogwrites blkverify compress copy-before-write copy-on-read
file ftp ftps host_cdrom host_device http https io_uring luks nbd null-aio
null-co nvme nvme-io_uring preallocate qcow2 quorum raw rbd
snapshot-access throttle vdi vhdx virtio-blk-vfio-pci
virtio-blk-vhost-user virtio-blk-vhost-vdpa vmdk vpc

### The steps to reproduce the bug

1. Prepare a KVM host with qemu-img >= 10.1.0 (e.g. RHEL 9.8) and `cryptsetup` installed.
2. Add the host to CloudStack, or restart the agent so it re-reports host details.
3. Check the stored value:
```sql
SELECT h.name, hd.value
FROM host h
JOIN host_details hd ON hd.host_id = h.id
WHERE hd.name = 'host.volume.encryption';
```

Expected: `host.volume.encryption = true`
Actual: `host.volume.encryption = false` on every host with qemu-img >= 10.1.0

### What to do about it?

Make the `image` keyword optional in the detection regex in
`QemuImg.helpSupportsImageFormat()` — minimal and backward compatible:

```diff
- Pattern pattern = Pattern.compile("Supported\\sformats:[a-zA-Z0-9-_\\s]*?\\b" + format + "\\b", CASE_INSENSITIVE);
+ // QEMU >= 10.1.0 changed the qemu-img --help header from
+ // "Supported formats:" to "Supported image formats:".
+ Pattern pattern = Pattern.compile("Supported\\s(image\\s)?formats:[a-zA-Z0-9-_\\s]*?\\b" + format + "\\b", CASE_INSENSITIVE);
```

`Supported\s(image\s)?formats:` matches both the old and the new header; the
format list itself is untouched. Suggested unit tests (new header, legacy
header, negative case):

```java
@Test
public void testHelpSupportsImageFormatQemu101Header() {
String help =
"Supported image formats:\n" +
" file ftp ftps host_cdrom host_device http https io_uring luks nbd\n";
Assert.assertTrue(QemuImg.helpSupportsImageFormat(help, QemuImg.PhysicalDiskFormat.LUKS));
}

@Test
public void testHelpSupportsImageFormatLegacyHeader() {
String help = "Supported formats: blkdebug file luks nbd qcow2 raw rbd vmdk\n";
Assert.assertTrue(QemuImg.helpSupportsImageFormat(help, QemuImg.PhysicalDiskFormat.LUKS));
}

@Test
public void testHelpDoesNotSupportMissingFormat() {
String help = "Supported image formats:\n file qcow2 raw\n";
Assert.assertFalse(QemuImg.helpSupportsImageFormat(help, QemuImg.PhysicalDiskFormat.LUKS));
}
```

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Inizia da QemuImg.helpSupportsImageFormat(), quindi segui il relativo risultato attraverso LibvirtComputingResource.hostSupportsVolumeEncryption(). Verifica il rilevamento dei formati qemu-img esistente sia con l’header legacy sia con l’header di QEMU 10.1, aggiungi la copertura per il nuovo header e per i formati mancanti, quindi esegui i test QemuImg pertinenti; il lavoro è completato quando LUKS viene rilevato per entrambi gli header senza corrispondere a formati non supportati.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
java
Ambito
infrastructure
Tipo di issue
Bug
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
85/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.