From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 0BE261FF09B for ; Mon, 09 Nov 2026 14:56:34 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 612D921482; Mon, 09 Nov 2026 14:56:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1794232580; x=1794837380; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=YQdJ0M+HCWYvCu37MoeB2PazKe0aW645W9qN7+hqrNE=; b=hNn8uVkYLHe+1DLsF0W+v6N4jcSyQyGHrXOKzVHo4yl3Bf2VMR14vGtBbZqHugmftI wvfVUPagg4nDMMNzVnS7tmXXotKb81fQTMIxq3ZGFwNe7ko4TYfyAnqDM4cYhzpNI2kr 9GFNtbeAs4GvR0fCojXjjF2pk0iY9zMTBYJHnzBjOs8G6KKkoijcLk2MFZrolPx6l8Ef Twz3Wn5DyyPm2w8CK6KJKzBpwI8fCPskAx8ZL46WDUVIhJE2fec9C8foFsQrgTWg2chB g33XoV5I2j8hs5hCXH8RI5CGfphDG5In5mPxWb+En+1wYDPeIwbhoitYi+TlHPwvBO7z cEvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794232580; x=1794837380; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=YQdJ0M+HCWYvCu37MoeB2PazKe0aW645W9qN7+hqrNE=; b=CYJ5acK7lvB6gHs/V0gaH6sP2SK11etY7pwVSkfS6FxUQj3S2xz653PUMS9KmREiIv FIG4T4Z1dhWlwCArzB2b1btNNWp+pSnNBUDxu6v5Ma3gUZ8kqet49kVl7P/mofXp+O8A cF9Q4WTU6fv+ShRibmxFluw45X8yew0d4Cv/81UdL18G+GAgkIySgmgQk2UawugTMkO8 k7Se4rbDNu5VAw6opcQa9mn772NJhe+jkHhj5rTgU4OvI2WRvby7YwIRQ2wRzJDTxcXl oj34OiO+6FHEfWZ3OlWmfh7nbTKxiEvqN1crkRXDASbTmm9frxOWZsrkhHAKVFkWxY+W ZfSA== X-Gm-Message-State: APuU4Z/fP8fwOfznuwTWHzG+3ohXMsyL8AhXhwdoVCO6Em2PpPaco3X/ YdLb55KQhx9vMI+mtACnWYQSqAkLg/tDW+zhppg/ntaKw93jPICy78oCM6/+cjZk X-Gm-Gg: AYBFou1XaFR4dlfJJ7gfGvt81aeBk7RmoeR2R5Rsd6Z5YurTfgh4LiuNfzWtTN9w37o LSP4V84rbvCuZrupuZNkSP9wIrl3FQF4wJ8JyuREvbIKnP6FEZ0qCq1yVMlqucfjIjqFrQ8lLje zacx/KhzhUm4syPlorHCmuZoam+0yxDpubHyaxdMOKQUb6f5XD1Coto+jIvCUReiCdmieMtIrnZ BQTEktv1rmicjRUFBmrZpsVPSHpfXWoM52iTMAE+QKoGO+LyrjKjJtwd/xBjaaFW+a7v2+aPpks q/o3hxKa/poyNNyWIHxFhZ94jxxdswyq5HEd0NCX8BVtFO6KMooGjiW7cEA1eBJHFtnklpfACKZ CgDnQWe4oeZPfNU0/kbSVCnD+vjRs2l+acQWRojSbyOW9ZMM39ZmC9tUeWhDc659PSPsmwbUDn8 QzTz94IdbemiZjuhS9hOPbluiIoZjXQX+NIqBQo5nEu6jRE/XXS7x3KfKKKat3ZHS4BIqNlEmdO rTYTNirCcWFQKYL8B0PGiw80oQVWSSeMrLf2N6pAygMgWyZUhvGgMEclQsT3cVqFyv8+A/VgDvo vMhNeI87dRhoUTW546oyOrG2w5kTr9cjXqhqK0AWZJI6Db6x8ZFUJq62DyCoMm3Jr35bL9344jU tcDjm5wfoFFUj905j6CJdStzaI1fiJLlA0EzcBCG3nY/F/Z1/ETSuO2J9fgucIX3IyfQ= X-Received: by 2002:a05:6102:4457:b0:7a5:673f:9a37 with SMTP id ada2fe7eead31-7d2db1ace8fmr596115137.15.1794232580142; Mon, 09 Nov 2026 05:56:20 -0800 (PST) From: Lucas Barros To: pve-devel@lists.proxmox.com Subject: [PATCH qemu-server] fix #7173: config: do not write out an empty tag list Date: Mon, 9 Nov 2026 10:56:13 -0300 Message-ID: <20261109135613.94692-1-lucasbarros.kam@gmail.com> X-Mailer: git-send-email 2.50.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.001 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 5S2IEI2ZARHJN7X5ICC73TWH7G5WLQ37 X-Message-ID-Hash: 5S2IEI2ZARHJN7X5ICC73TWH7G5WLQ37 X-MailFrom: lucasbarros.kam@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Removing the last tag of a VM, for example in the web UI, sets 'tags' to an empty string, which then ends up as an empty 'tags:' line in the VM config and in the output of 'qm config'. This carries no information, is inconsistent with how unset options are handled and confuses tools that consume the config, as they need to special case an empty tag list. Drop the key when writing the config if the tag list is empty, which also cleans up configs that already contain such a line on the next write. The pending section is left alone, as an empty value there is still needed to apply the change. Signed-off-by: Lucas Barros --- A matching patch for pve-container is sent separately. Only the main section is touched. In [PENDING] the empty value is still needed so that applying the pending change removes the tags, and it is dropped from the main section on the write that follows. The new parse-config test input contains the 'tags: ' line with the trailing space that the writer produces today, so 'git am' warns about trailing whitespace there. Without the trailing space the line would not match the config parser at all. An alternative would be to turn an empty 'tags' value into a deletion in the update API, but that would not cover other paths that end up with an empty value (create, clone, restore, 'qm set --tags ""'). Tested on a PVE 9 node (qemu-server 9.2.15, pve-container 6.1.18), adding and then removing the only tag of a VM and of a container: without the patches: $ qm set 9901 --tags foo; pvesh set /nodes//qemu/9901/config --tags '' $ grep -n tags /etc/pve/qemu-server/9901.conf | cat -A 6:tags: $ $ pct set 9902 --tags foo; pct set 9902 --tags '' $ grep -n tags /etc/pve/lxc/9902.conf | cat -A 7:tags: $ $ pvesh get /nodes//qemu/9901/config --output-format json | jq -c '{tags}' {"tags":" "} with the patches: $ grep -c tags /etc/pve/qemu-server/9901.conf /etc/pve/lxc/9902.conf /etc/pve/qemu-server/9901.conf:0 /etc/pve/lxc/9902.conf:0 $ pvesh get /nodes//qemu/9901/config --output-format json | jq -c '{tags}' {"tags":null} An existing empty 'tags:' line goes away on the next write of the config, removing the tag of a running VM leaves no line and no [PENDING] entry, and 'tags: a;b' is still written as before. 'make test' (run with 16 CPUs in /proc/cpuinfo, the cfg2cmd tests need more vCPUs than the test machine has) passes before and after; the parse-config tests go from 20 to 22, and the new empty-tags case fails without the change to QemuServer.pm. proxmox-perltidy reports no changes. This patch was prepared with the help of an LLM based coding assistant; I reviewed the change and ran the tests mentioned above myself. src/PVE/QemuServer.pm | 3 +++ src/test/parse-config-expected/empty-tags.conf | 11 +++++++++++ src/test/parse-config-input/empty-tags.conf | 12 ++++++++++++ src/test/run_parse_config_tests.pl | 2 +- 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/test/parse-config-expected/empty-tags.conf create mode 100644 src/test/parse-config-input/empty-tags.conf diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm index 8045c07e..22621e63 100644 --- a/src/PVE/QemuServer.pm +++ b/src/PVE/QemuServer.pm @@ -2067,6 +2067,9 @@ sub write_vm_config { delete $conf->{smp}; } + # an empty tag list carries no information, so do not write out the key + delete $conf->{tags} if defined($conf->{tags}) && $conf->{tags} =~ m/^[\s,;]*$/; + my $used_volids = {}; my $cleanup_config = sub { diff --git a/src/test/parse-config-expected/empty-tags.conf b/src/test/parse-config-expected/empty-tags.conf new file mode 100644 index 00000000..a5b02f2c --- /dev/null +++ b/src/test/parse-config-expected/empty-tags.conf @@ -0,0 +1,11 @@ +# VM with an empty tag list left behind after removing the last tag +bootdisk: scsi0 +cores: 1 +memory: 512 +name: apache +ostype: l26 +scsi0: mydir:142/vm-142-disk-0.qcow2,size=4G +scsihw: virtio-scsi-pci +smbios1: uuid=ddf91b3f-a597-42be-9a7e-fb6421dcd5cd +sockets: 1 +vmgenid: 0 diff --git a/src/test/parse-config-input/empty-tags.conf b/src/test/parse-config-input/empty-tags.conf new file mode 100644 index 00000000..a37a9c2d --- /dev/null +++ b/src/test/parse-config-input/empty-tags.conf @@ -0,0 +1,12 @@ +# VM with an empty tag list left behind after removing the last tag +bootdisk: scsi0 +cores: 1 +memory: 512 +name: apache +ostype: l26 +scsi0: mydir:142/vm-142-disk-0.qcow2,size=4G +scsihw: virtio-scsi-pci +smbios1: uuid=ddf91b3f-a597-42be-9a7e-fb6421dcd5cd +sockets: 1 +tags: +vmgenid: 0 diff --git a/src/test/run_parse_config_tests.pl b/src/test/run_parse_config_tests.pl index f68abc06..c30aad99 100755 --- a/src/test/run_parse_config_tests.pl +++ b/src/test/run_parse_config_tests.pl @@ -25,7 +25,7 @@ my $INPUT_DIR = './parse-config-input'; my $EXPECTED_DIR = './parse-config-expected'; # NOTE update when you add/remove tests -plan tests => 2 * 10; +plan tests => 2 * 11; sub run_tests { my ($strict) = @_; -- 2.47.3