mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-06-14 15:54:08 +00:00
Merge patch series "more robust handling of fleecing images"
Fiona Ebner <f.ebner@proxmox.com> says: Record the created fleecing images in the VM configuration to be able to remove left-overs after hard failures. Adds a new special configuration section 'fleecing', making special section handling more generic as preparation, as well as fixing some corner cases in configuration parsing and adding tests. Fiona Ebner (16): migration: remove unused variable test: avoid duplicate mock module in restore config test test: add parse config tests parse config: be precise about section type checks test: add test case exposing issue with unknown sections parse config: skip unknown sections and warn about their presence vzdump: anchor matches for pending and special sections vzdump: skip all special sections config: make special section handling generic test: parse config: test config with duplicate sections parse config: warn about duplicate sections check type: require schema as an argument config: add fleecing section fix #5440: vzdump: better cleanup fleecing images after hard errors migration: attempt to clean up potential left-over fleecing images destroy vm: clean up potential left-over fleecing images Link: https://lore.proxmox.com/20250127112923.31703-1-f.ebner@proxmox.com Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
commit
5f8a64ae59
@ -1717,7 +1717,7 @@ __PACKAGE__->register_method({
|
||||
my $vmid = $param->{vmid};
|
||||
my $conf = PVE::QemuConfig->load_config($vmid);
|
||||
|
||||
my $ci = $conf->{cloudinit};
|
||||
my $ci = $conf->{'special-sections'}->{cloudinit};
|
||||
|
||||
$conf->{cipassword} = '**********' if exists $conf->{cipassword};
|
||||
$ci->{cipassword} = '**********' if exists $ci->{cipassword};
|
||||
@ -6042,9 +6042,25 @@ __PACKAGE__->register_method({
|
||||
# not handled by update_vm_api
|
||||
my $vmgenid = delete $new_conf->{vmgenid};
|
||||
my $meta = delete $new_conf->{meta};
|
||||
my $cloudinit = delete $new_conf->{cloudinit}; # this is informational only
|
||||
|
||||
my $special_sections = delete $new_conf->{'special-sections'} // {};
|
||||
|
||||
# fleecing state is specific to source side
|
||||
delete $special_sections->{fleecing};
|
||||
|
||||
$new_conf->{skip_cloud_init} = 1; # re-use image from source side
|
||||
|
||||
# TODO PVE 10 - remove backwards-compat handling?
|
||||
my $cloudinit = delete $new_conf->{cloudinit};
|
||||
if ($cloudinit) {
|
||||
if ($special_sections->{cloudinit}) {
|
||||
warn "config has duplicate special 'cloudinit' sections - skipping"
|
||||
." legacy variant\n";
|
||||
} else {
|
||||
$special_sections->{cloudinit} = $cloudinit;
|
||||
}
|
||||
}
|
||||
|
||||
$new_conf->{vmid} = $state->{vmid};
|
||||
$new_conf->{node} = $node;
|
||||
|
||||
@ -6066,7 +6082,7 @@ __PACKAGE__->register_method({
|
||||
$conf->{lock} = 'migrate';
|
||||
$conf->{vmgenid} = $vmgenid if defined($vmgenid);
|
||||
$conf->{meta} = $meta if defined($meta);
|
||||
$conf->{cloudinit} = $cloudinit if defined($cloudinit);
|
||||
$conf->{'special-sections'} = $special_sections;
|
||||
PVE::QemuConfig->write_config($state->{vmid}, $conf);
|
||||
|
||||
$state->{lock} = 'migrate';
|
||||
|
@ -15,6 +15,7 @@ use PVE::QemuServer::Monitor qw(mon_cmd);
|
||||
use PVE::QemuServer;
|
||||
use PVE::QemuServer::Machine;
|
||||
use PVE::QemuServer::Memory qw(get_current_memory);
|
||||
use PVE::RESTEnvironment qw(log_warn);
|
||||
use PVE::Storage;
|
||||
use PVE::Tools;
|
||||
use PVE::Format qw(render_bytes render_duration);
|
||||
@ -543,7 +544,7 @@ sub load_current_config {
|
||||
my ($class, $vmid, $current) = @_;
|
||||
|
||||
my $conf = $class->SUPER::load_current_config($vmid, $current);
|
||||
delete $conf->{cloudinit};
|
||||
delete $conf->{'special-sections'};
|
||||
return $conf;
|
||||
}
|
||||
|
||||
@ -593,4 +594,85 @@ sub has_cloudinit {
|
||||
return $found;
|
||||
}
|
||||
|
||||
# Caller is expected to deal with volumes from an already existing 'fleecing' special section in the
|
||||
# configuration first.
|
||||
sub record_fleecing_images {
|
||||
my ($vmid, $volids) = @_;
|
||||
|
||||
return if scalar($volids->@*) == 0;
|
||||
|
||||
PVE::QemuConfig->lock_config($vmid, sub {
|
||||
my $conf = PVE::QemuConfig->load_config($vmid);
|
||||
$conf->{'special-sections'}->{fleecing}->{'fleecing-images'} = join(',', $volids->@*);
|
||||
PVE::QemuConfig->write_config($vmid, $conf);
|
||||
});
|
||||
}
|
||||
|
||||
# Will also cancel a running backup job inside QEMU. Not doing so can lead to a deadlock when
|
||||
# attempting to detach the fleecing image.
|
||||
sub cleanup_fleecing_images {
|
||||
my ($vmid, $storecfg, $log_func) = @_;
|
||||
|
||||
if (!$log_func) {
|
||||
$log_func = sub {
|
||||
my ($level, $line) = @_;
|
||||
chomp($line);
|
||||
if ($level eq 'info') {
|
||||
print "$line\n";
|
||||
} else {
|
||||
log_warn($line);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my $volids = [];
|
||||
my $failed = [];
|
||||
|
||||
# cancel left-over backup job and detach any left-over images from a running VM
|
||||
if (PVE::QemuServer::Helpers::vm_running_locally($vmid)) {
|
||||
eval {
|
||||
if (my $status = mon_cmd($vmid, 'query-backup')) {
|
||||
if ($status->{status} && $status->{status} eq 'active') {
|
||||
$log_func->('warn', "left-over backup job still running inside QEMU - canceling now");
|
||||
mon_cmd($vmid, 'backup-cancel');
|
||||
}
|
||||
}
|
||||
};
|
||||
$log_func->('warn', "checking/canceling old backup job failed - $@") if $@;
|
||||
|
||||
my $block_info = mon_cmd($vmid, "query-block");
|
||||
for my $info ($block_info->@*) {
|
||||
my $device_id = $info->{device};
|
||||
next if $device_id !~ m/-fleecing$/;
|
||||
|
||||
$log_func->('info', "detaching (old) fleecing image for '$device_id'");
|
||||
$device_id =~ s/^drive-//; # re-added by qemu_drivedel()
|
||||
eval { PVE::QemuServer::qemu_drivedel($vmid, $device_id) };
|
||||
$log_func->('warn', "error detaching (old) fleecing image '$device_id' - $@") if $@;
|
||||
}
|
||||
}
|
||||
|
||||
PVE::QemuConfig->lock_config($vmid, sub {
|
||||
my $conf = PVE::QemuConfig->load_config($vmid);
|
||||
my $special = $conf->{'special-sections'};
|
||||
if (my $fleecing = $special->{fleecing}) {
|
||||
$volids = [PVE::Tools::split_list($fleecing->{'fleecing-images'})];
|
||||
delete $fleecing->{'fleecing-images'};
|
||||
delete $special->{fleecing} if !scalar(keys $fleecing->%*);
|
||||
PVE::QemuConfig->write_config($vmid, $conf);
|
||||
}
|
||||
});
|
||||
|
||||
for my $volid ($volids->@*) {
|
||||
$log_func->('info', "removing (old) fleecing image '$volid'");
|
||||
eval { PVE::Storage::vdisk_free($storecfg, $volid); };
|
||||
if (my $err = $@) {
|
||||
$log_func->('warn', "error removing fleecing image '$volid' - $err");
|
||||
push $failed->@*, $volid;
|
||||
}
|
||||
}
|
||||
|
||||
record_fleecing_images($vmid, $failed);
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -178,11 +178,17 @@ sub prepare {
|
||||
|
||||
my $storecfg = $self->{storecfg} = PVE::Storage::config();
|
||||
|
||||
# updates the configuration, so ordered before saving the configuration in $self
|
||||
eval {
|
||||
PVE::QemuConfig::cleanup_fleecing_images(
|
||||
$vmid, $storecfg, sub { $self->log($_[0], $_[1]); });
|
||||
};
|
||||
$self->log('warn', "attempt to clean up left-over fleecing images failed - $@") if $@;
|
||||
|
||||
# test if VM exists
|
||||
my $conf = $self->{vmconf} = PVE::QemuConfig->load_config($vmid);
|
||||
|
||||
my $version = PVE::QemuServer::Helpers::get_node_pvecfg_version($self->{node});
|
||||
my $cloudinit_config = $conf->{cloudinit};
|
||||
|
||||
my $repl_conf = PVE::ReplicationConfig->new();
|
||||
$self->{replication_jobcfg} = $repl_conf->find_local_replication_job($vmid, $self->{node});
|
||||
|
@ -1829,7 +1829,7 @@ sub vmconfig_register_unused_drive {
|
||||
if (drive_is_cloudinit($drive)) {
|
||||
eval { PVE::Storage::vdisk_free($storecfg, $drive->{file}) };
|
||||
warn $@ if $@;
|
||||
delete $conf->{cloudinit};
|
||||
delete $conf->{'special-sections'}->{cloudinit};
|
||||
} elsif (!drive_is_cdrom($drive)) {
|
||||
my $volid = $drive->{file};
|
||||
if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
|
||||
@ -2051,11 +2051,13 @@ sub cloudinit_pending_properties {
|
||||
}
|
||||
|
||||
sub check_type {
|
||||
my ($key, $value) = @_;
|
||||
my ($key, $value, $schema) = @_;
|
||||
|
||||
die "unknown setting '$key'\n" if !$confdesc->{$key};
|
||||
die "check_type: no schema defined\n" if !$schema;
|
||||
|
||||
my $type = $confdesc->{$key}->{type};
|
||||
die "unknown setting '$key'\n" if !$schema->{$key};
|
||||
|
||||
my $type = $schema->{$key}->{type};
|
||||
|
||||
if (!defined($value)) {
|
||||
die "got undefined value\n";
|
||||
@ -2076,7 +2078,7 @@ sub check_type {
|
||||
return $value if $value =~ m/^(\d+)(\.\d+)?$/;
|
||||
die "type check ('number') failed - got '$value'\n";
|
||||
} elsif ($type eq 'string') {
|
||||
if (my $fmt = $confdesc->{$key}->{format}) {
|
||||
if (my $fmt = $schema->{$key}->{format}) {
|
||||
PVE::JSONSchema::check_format($fmt, $value);
|
||||
return $value;
|
||||
}
|
||||
@ -2090,6 +2092,9 @@ sub check_type {
|
||||
sub destroy_vm {
|
||||
my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
|
||||
|
||||
eval { PVE::QemuConfig::cleanup_fleecing_images($vmid, $storecfg) };
|
||||
log_warn("attempt to clean up left-over fleecing images failed - $@") if $@;
|
||||
|
||||
my $conf = PVE::QemuConfig->load_config($vmid);
|
||||
|
||||
if (!$skiplock && !PVE::QemuConfig->has_lock($conf, 'suspended')) {
|
||||
@ -2163,16 +2168,27 @@ sub destroy_vm {
|
||||
}
|
||||
}
|
||||
|
||||
my $fleecing_section_schema = {
|
||||
'fleecing-images' => {
|
||||
type => 'string',
|
||||
format => 'pve-volume-id-list',
|
||||
description => "For internal use only. List of fleecing images allocated during backup."
|
||||
." If no backup is running, these are left-overs that failed to be removed.",
|
||||
optional => 1,
|
||||
},
|
||||
};
|
||||
|
||||
sub parse_vm_config {
|
||||
my ($filename, $raw, $strict) = @_;
|
||||
|
||||
return if !defined($raw);
|
||||
|
||||
# note that pending, snapshot and special sections are currently skipped when a backup is taken
|
||||
my $res = {
|
||||
digest => Digest::SHA::sha1_hex($raw),
|
||||
snapshots => {},
|
||||
pending => {},
|
||||
cloudinit => {},
|
||||
pending => undef,
|
||||
'special-sections' => {},
|
||||
};
|
||||
|
||||
my $handle_error = sub {
|
||||
@ -2199,30 +2215,52 @@ sub parse_vm_config {
|
||||
}
|
||||
$descr = undef;
|
||||
};
|
||||
my $section = '';
|
||||
|
||||
my $special_schemas = {
|
||||
cloudinit => $confdesc, # not actually used right now, see below
|
||||
fleecing => $fleecing_section_schema,
|
||||
};
|
||||
my $special_sections_re_string = join('|', keys $special_schemas->%*);
|
||||
my $special_sections_re_1 = qr/($special_sections_re_string)/;
|
||||
|
||||
my $section = { name => '', type => 'main', schema => $confdesc };
|
||||
|
||||
my @lines = split(/\n/, $raw);
|
||||
foreach my $line (@lines) {
|
||||
next if $line =~ m/^\s*$/;
|
||||
|
||||
if ($line =~ m/^\[PENDING\]\s*$/i) {
|
||||
$section = 'pending';
|
||||
$section = { name => 'pending', type => 'pending', schema => $confdesc };
|
||||
$finish_description->();
|
||||
$conf = $res->{$section} = {};
|
||||
$handle_error->("vm $vmid - duplicate section: $section->{name}\n")
|
||||
if defined($res->{$section->{name}});
|
||||
$conf = $res->{$section->{name}} = {};
|
||||
next;
|
||||
} elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
|
||||
$section = 'cloudinit';
|
||||
} elsif ($line =~ m/^\[special:$special_sections_re_1\]\s*$/i) {
|
||||
$section = { name => $1, type => 'special', schema => $special_schemas->{$1} };
|
||||
$finish_description->();
|
||||
$conf = $res->{$section} = {};
|
||||
$handle_error->("vm $vmid - duplicate special section: $section->{name}\n")
|
||||
if defined($res->{'special-sections'}->{$section->{name}});
|
||||
$conf = $res->{'special-sections'}->{$section->{name}} = {};
|
||||
next;
|
||||
|
||||
} elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
|
||||
$section = $1;
|
||||
$section = { name => $1, type => 'snapshot', schema => $confdesc };
|
||||
$finish_description->();
|
||||
$conf = $res->{snapshots}->{$section} = {};
|
||||
$handle_error->("vm $vmid - duplicate snapshot section: $section->{name}\n")
|
||||
if defined($res->{snapshots}->{$section->{name}});
|
||||
$conf = $res->{snapshots}->{$section->{name}} = {};
|
||||
next;
|
||||
} elsif ($line =~ m/^\[([^\]]*)\]\s*$/i) {
|
||||
my $unknown_section = $1;
|
||||
$section = undef;
|
||||
$finish_description->();
|
||||
$handle_error->("vm $vmid - skipping unknown section: '$unknown_section'\n");
|
||||
next;
|
||||
}
|
||||
|
||||
next if !defined($section);
|
||||
|
||||
if ($line =~ m/^\#(.*)$/) {
|
||||
$descr = '' if !defined($descr);
|
||||
$descr .= PVE::Tools::decode_text($1) . "\n";
|
||||
@ -2240,7 +2278,7 @@ sub parse_vm_config {
|
||||
$conf->{$key} = $value;
|
||||
} elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
|
||||
my $value = $1;
|
||||
if ($section eq 'pending') {
|
||||
if ($section->{name} eq 'pending' && $section->{type} eq 'pending') {
|
||||
$conf->{delete} = $value; # we parse this later
|
||||
} else {
|
||||
$handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
|
||||
@ -2248,17 +2286,17 @@ sub parse_vm_config {
|
||||
} elsif ($line =~ m/^([a-z][a-z_\-]*\d*):\s*(.+?)\s*$/) {
|
||||
my $key = $1;
|
||||
my $value = $2;
|
||||
if ($section eq 'cloudinit') {
|
||||
if ($section->{name} eq 'cloudinit' && $section->{type} eq 'special') {
|
||||
# ignore validation only used for informative purpose
|
||||
$conf->{$key} = $value;
|
||||
next;
|
||||
}
|
||||
eval { $value = check_type($key, $value); };
|
||||
eval { $value = check_type($key, $value, $section->{schema}); };
|
||||
if ($@) {
|
||||
$handle_error->("vm $vmid - unable to parse value of '$key' - $@");
|
||||
} else {
|
||||
$key = 'ide2' if $key eq 'cdrom';
|
||||
my $fmt = $confdesc->{$key}->{format};
|
||||
my $fmt = $section->{schema}->{$key}->{format};
|
||||
if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
|
||||
my $v = parse_drive($key, $value);
|
||||
if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
|
||||
@ -2280,6 +2318,8 @@ sub parse_vm_config {
|
||||
$finish_description->();
|
||||
delete $res->{snapstate}; # just to be sure
|
||||
|
||||
$res->{pending} = {} if !defined($res->{pending});
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@ -2310,7 +2350,7 @@ sub write_vm_config {
|
||||
|
||||
foreach my $key (keys %$cref) {
|
||||
next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
|
||||
$key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
|
||||
$key eq 'snapstate' || $key eq 'pending' || $key eq 'special-sections';
|
||||
my $value = $cref->{$key};
|
||||
if ($key eq 'delete') {
|
||||
die "propertry 'delete' is only allowed in [PENDING]\n"
|
||||
@ -2318,7 +2358,7 @@ sub write_vm_config {
|
||||
# fixme: check syntax?
|
||||
next;
|
||||
}
|
||||
eval { $value = check_type($key, $value); };
|
||||
eval { $value = check_type($key, $value, $confdesc); };
|
||||
die "unable to parse value of '$key' - $@" if $@;
|
||||
|
||||
$cref->{$key} = $value;
|
||||
@ -2364,7 +2404,7 @@ sub write_vm_config {
|
||||
}
|
||||
|
||||
foreach my $key (sort keys %$conf) {
|
||||
next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
|
||||
next if $key =~ /^(digest|description|pending|snapshots|special-sections)$/;
|
||||
$raw .= "$key: $conf->{$key}\n";
|
||||
}
|
||||
return $raw;
|
||||
@ -2377,9 +2417,10 @@ sub write_vm_config {
|
||||
$raw .= &$generate_raw_config($conf->{pending}, 1);
|
||||
}
|
||||
|
||||
if (scalar(keys %{$conf->{cloudinit}}) && PVE::QemuConfig->has_cloudinit($conf)){
|
||||
$raw .= "\n[special:cloudinit]\n";
|
||||
$raw .= &$generate_raw_config($conf->{cloudinit});
|
||||
for my $special (sort keys $conf->{'special-sections'}->%*) {
|
||||
next if $special eq 'cloudinit' && !PVE::QemuConfig->has_cloudinit($conf);
|
||||
$raw .= "\n[special:$special]\n";
|
||||
$raw .= &$generate_raw_config($conf->{'special-sections'}->{$special});
|
||||
}
|
||||
|
||||
foreach my $snapname (sort keys %{$conf->{snapshots}}) {
|
||||
@ -4740,7 +4781,7 @@ sub vmconfig_hotplug_pending {
|
||||
my ($conf, $opt, $old, $new) = @_;
|
||||
return if !$cloudinit_pending_properties->{$opt};
|
||||
|
||||
my $ci = ($conf->{cloudinit} //= {});
|
||||
my $ci = ($conf->{'special-sections'}->{cloudinit} //= {});
|
||||
|
||||
my $recorded = $ci->{$opt};
|
||||
my %added = map { $_ => 1 } PVE::Tools::split_list(delete($ci->{added}) // '');
|
||||
@ -5131,7 +5172,7 @@ sub vmconfig_apply_pending {
|
||||
if ($generate_cloudinit) {
|
||||
if (PVE::QemuServer::Cloudinit::apply_cloudinit_config($conf, $vmid)) {
|
||||
# After successful generation and if there were changes to be applied, update the
|
||||
# config to drop the {cloudinit} entry.
|
||||
# config to drop the 'cloudinit' special section.
|
||||
PVE::QemuConfig->write_config($vmid, $conf);
|
||||
}
|
||||
}
|
||||
@ -5562,7 +5603,7 @@ sub vm_start_nolock {
|
||||
if (!$migratedfrom) {
|
||||
if (PVE::QemuServer::Cloudinit::apply_cloudinit_config($conf, $vmid)) {
|
||||
# FIXME: apply_cloudinit_config updates $conf in this case, and it would only drop
|
||||
# $conf->{cloudinit}, so we could just not do this?
|
||||
# $conf->{'special-sections'}->{cloudinit}, so we could just not do this?
|
||||
# But we do it above, so for now let's be consistent.
|
||||
$conf = PVE::QemuConfig->load_config($vmid); # update/reload
|
||||
}
|
||||
|
@ -657,7 +657,11 @@ my $cloudinit_methods = {
|
||||
sub has_changes {
|
||||
my ($conf) = @_;
|
||||
|
||||
return !!$conf->{cloudinit}->%*;
|
||||
if (my $cloudinit = $conf->{'special-sections'}->{cloudinit}) {
|
||||
return !!$cloudinit->%*;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub generate_cloudinit_config {
|
||||
@ -689,7 +693,7 @@ sub apply_cloudinit_config {
|
||||
my $has_changes = generate_cloudinit_config($conf, $vmid);
|
||||
|
||||
if ($has_changes) {
|
||||
delete $conf->{cloudinit};
|
||||
delete $conf->{'special-sections'}->{cloudinit};
|
||||
PVE::QemuConfig->write_config($vmid, $conf);
|
||||
return 1;
|
||||
}
|
||||
|
@ -236,20 +236,21 @@ sub assemble {
|
||||
|
||||
my $found_snapshot;
|
||||
my $found_pending;
|
||||
my $found_cloudinit;
|
||||
my $found_special;
|
||||
while (defined (my $line = <$conffd>)) {
|
||||
next if $line =~ m/^\#vzdump\#/; # just to be sure
|
||||
next if $line =~ m/^\#qmdump\#/; # just to be sure
|
||||
if ($line =~ m/^\[(.*)\]\s*$/) {
|
||||
if ($1 =~ m/PENDING/i) {
|
||||
if ($1 =~ m/^PENDING$/i) {
|
||||
$found_pending = 1;
|
||||
} elsif ($1 =~ m/special:cloudinit/) {
|
||||
$found_cloudinit = 1;
|
||||
} elsif ($1 =~ m/^special:.*$/) {
|
||||
$found_special = 1;
|
||||
} else {
|
||||
$found_snapshot = 1;
|
||||
}
|
||||
}
|
||||
next if $found_snapshot || $found_pending || $found_cloudinit; # skip all snapshots,pending changes and cloudinit config data
|
||||
# skip all snapshots, pending changes and special sections
|
||||
next if $found_snapshot || $found_pending || $found_special;
|
||||
|
||||
if ($line =~ m/^unused\d+:\s*(\S+)\s*/) {
|
||||
$self->loginfo("skip unused drive '$1' (not included into backup)");
|
||||
@ -270,6 +271,9 @@ sub assemble {
|
||||
}
|
||||
}
|
||||
|
||||
if ($found_special) {
|
||||
$self->loginfo("special config section found (not included into backup)");
|
||||
}
|
||||
if ($found_snapshot) {
|
||||
$self->loginfo("snapshots found (not included into backup)");
|
||||
}
|
||||
@ -539,15 +543,25 @@ sub get_and_check_pbs_encryption_config {
|
||||
die "internal error - unhandled case for getting & checking PBS encryption ($keyfile, $master_keyfile)!";
|
||||
}
|
||||
|
||||
# Helper is intended to be called from allocate_fleecing_images() only. Otherwise, fleecing volids
|
||||
# have already been recorded in the configuration and PVE::QemuConfig::cleanup_fleecing_images()
|
||||
# should be used instead.
|
||||
my sub cleanup_fleecing_images {
|
||||
my ($self, $disks) = @_;
|
||||
my ($self, $vmid, $disks) = @_;
|
||||
|
||||
my $failed = [];
|
||||
|
||||
for my $di ($disks->@*) {
|
||||
if (my $volid = $di->{'fleece-volid'}) {
|
||||
eval { PVE::Storage::vdisk_free($self->{storecfg}, $volid); };
|
||||
$self->log('warn', "error removing fleecing image '$volid' - $@") if $@;
|
||||
if (my $err = $@) {
|
||||
$self->log('warn', "error removing fleecing image '$volid' - $err");
|
||||
push $failed->@*, $volid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PVE::QemuConfig::record_fleecing_images($vmid, $failed);
|
||||
}
|
||||
|
||||
my sub allocate_fleecing_images {
|
||||
@ -555,8 +569,7 @@ my sub allocate_fleecing_images {
|
||||
|
||||
die "internal error - no fleecing storage specified\n" if !$fleecing_storeid;
|
||||
|
||||
# TODO what about potential left-over images from a failed attempt? Just
|
||||
# auto-remove? While unlikely, could conflict with manually created image from user...
|
||||
my $fleece_volids = [];
|
||||
|
||||
eval {
|
||||
my $n = 0; # counter for fleecing image names
|
||||
@ -582,6 +595,8 @@ my sub allocate_fleecing_images {
|
||||
$di->{'fleece-volid'} = PVE::Storage::vdisk_alloc(
|
||||
$self->{storecfg}, $fleecing_storeid, $vmid, $format, $name, $size);
|
||||
|
||||
push $fleece_volids->@*, $di->{'fleece-volid'};
|
||||
|
||||
$n++;
|
||||
} else {
|
||||
die "implement me (type '$di->{type}')";
|
||||
@ -589,9 +604,11 @@ my sub allocate_fleecing_images {
|
||||
}
|
||||
};
|
||||
if (my $err = $@) {
|
||||
cleanup_fleecing_images($self, $disks);
|
||||
cleanup_fleecing_images($self, $vmid, $disks);
|
||||
die $err;
|
||||
}
|
||||
|
||||
PVE::QemuConfig::record_fleecing_images($vmid, $fleece_volids);
|
||||
}
|
||||
|
||||
my sub detach_fleecing_images {
|
||||
@ -651,6 +668,13 @@ my sub check_and_prepare_fleecing {
|
||||
$use_fleecing = 0;
|
||||
}
|
||||
|
||||
# clean up potential left-overs from a previous attempt
|
||||
eval {
|
||||
PVE::QemuConfig::cleanup_fleecing_images(
|
||||
$vmid, $self->{storecfg}, sub { $self->log($_[0], $_[1]); });
|
||||
};
|
||||
$self->log('warn', "attempt to clean up left-over fleecing images failed - $@") if $@;
|
||||
|
||||
if ($use_fleecing) {
|
||||
$self->query_block_node_sizes($vmid, $disks);
|
||||
|
||||
@ -1243,7 +1267,11 @@ sub cleanup {
|
||||
}
|
||||
|
||||
$detach_tpmstate_drive->($task, $vmid);
|
||||
detach_fleecing_images($task->{disks}, $vmid) if $task->{'use-fleecing'};
|
||||
if ($task->{'use-fleecing'}) {
|
||||
detach_fleecing_images($task->{disks}, $vmid);
|
||||
PVE::QemuConfig::cleanup_fleecing_images(
|
||||
$vmid, $self->{storecfg}, sub { $self->log($_[0], $_[1]); });
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_fleecing_images($self, $task->{disks}) if $task->{'use-fleecing'};
|
||||
|
@ -1,6 +1,6 @@
|
||||
all: test
|
||||
|
||||
test: test_snapshot test_cfg_to_cmd test_pci_addr_conflicts test_qemu_img_convert test_migration test_restore_config
|
||||
test: test_snapshot test_cfg_to_cmd test_pci_addr_conflicts test_qemu_img_convert test_migration test_restore_config test_parse_config
|
||||
|
||||
test_snapshot: run_snapshot_tests.pl
|
||||
./run_snapshot_tests.pl
|
||||
@ -25,6 +25,9 @@ $(MIGRATION_TEST_TARGETS):
|
||||
test_restore_config: run_qemu_restore_config_tests.pl
|
||||
./run_qemu_restore_config_tests.pl
|
||||
|
||||
test_parse_config: run_parse_config_tests.pl
|
||||
./run_parse_config_tests.pl
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf MigrationTest/run
|
||||
rm -rf MigrationTest/run parse-config-output
|
||||
|
40
test/parse-config-expected/cloudinit-snapshot.conf
Normal file
40
test/parse-config-expected/cloudinit-snapshot.conf
Normal file
@ -0,0 +1,40 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: cloudinit
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[special:cloudinit]
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
name: deb122
|
||||
|
||||
[cloudinit]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737549549
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
@ -0,0 +1 @@
|
||||
vm 8006 - unable to parse value of 'numa' - type check ('boolean') failed - got 'verify meee~ :)'
|
43
test/parse-config-expected/duplicate-sections.conf
Normal file
43
test/parse-config-expected/duplicate-sections.conf
Normal file
@ -0,0 +1,43 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb122
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: foo
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[PENDING]
|
||||
vga: qxl
|
||||
|
||||
[special:cloudinit]
|
||||
name: deb123
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 4
|
||||
cpu: host
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
@ -0,0 +1 @@
|
||||
vm 8006 - duplicate section: pending
|
44
test/parse-config-expected/unknown-sections.conf
Normal file
44
test/parse-config-expected/unknown-sections.conf
Normal file
@ -0,0 +1,44 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: foo
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[PENDING]
|
||||
bios: ovmf
|
||||
|
||||
[special:cloudinit]
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
name: deb122
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
@ -0,0 +1 @@
|
||||
vm 8006 - skipping unknown section: 'special:unknown123'
|
36
test/parse-config-expected/verify-snapshot.conf
Normal file
36
test/parse-config-expected/verify-snapshot.conf
Normal file
@ -0,0 +1,36 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: snap
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[snap]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737549549
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
@ -0,0 +1 @@
|
||||
vm 8006 - unable to parse value of 'numa' - type check ('boolean') failed - got 'verify meee~ :)'
|
41
test/parse-config-input/cloudinit-snapshot.conf
Normal file
41
test/parse-config-input/cloudinit-snapshot.conf
Normal file
@ -0,0 +1,41 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: cloudinit
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[special:cloudinit]
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
name: deb122
|
||||
|
||||
[cloudinit]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: verify meee~ :)
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737549549
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
68
test/parse-config-input/duplicate-sections.conf
Normal file
68
test/parse-config-input/duplicate-sections.conf
Normal file
@ -0,0 +1,68 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb122
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: foo
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[PENDING]
|
||||
bios: ovmf
|
||||
|
||||
[PENDING]
|
||||
vga: qxl
|
||||
|
||||
[special:cloudinit]
|
||||
name: deb12
|
||||
|
||||
[special:cloudinit]
|
||||
name: deb123
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 4
|
||||
cpu: host
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
20
test/parse-config-input/fleecing-section.conf
Normal file
20
test/parse-config-input/fleecing-section.conf
Normal file
@ -0,0 +1,20 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[special:fleecing]
|
||||
fleecing-images: zfs:vm-120-fleece-0
|
16
test/parse-config-input/locked.conf
Normal file
16
test/parse-config-input/locked.conf
Normal file
@ -0,0 +1,16 @@
|
||||
# locked
|
||||
bootdisk: scsi0
|
||||
cores: 1
|
||||
ide2: none,media=cdrom
|
||||
lock: backup
|
||||
memory: 512
|
||||
name: apache
|
||||
net0: virtio=92:38:11:FD:ED:87,bridge=vmbr0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: mydir:1422/vm-1422-disk-0.qcow2,size=4G
|
||||
scsihw: virtio-scsi-pci
|
||||
smbios1: uuid=ddf91b3f-a597-42be-9a7e-fb6421dcd5cd
|
||||
sockets: 1
|
||||
unused7: mydir:1422/vm-1422-disk-8.qcow2
|
||||
vmgenid: 0
|
15
test/parse-config-input/plain.conf
Normal file
15
test/parse-config-input/plain.conf
Normal file
@ -0,0 +1,15 @@
|
||||
# plain VM
|
||||
bootdisk: scsi0
|
||||
cores: 1
|
||||
ide2: none,media=cdrom
|
||||
memory: 512
|
||||
name: apache
|
||||
net0: virtio=92:38:11:FD:ED:87,bridge=vmbr0,firewall=1
|
||||
numa: 0
|
||||
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: foo bar
|
||||
vmgenid: 0
|
16
test/parse-config-input/regular-vm-efi.conf
Normal file
16
test/parse-config-input/regular-vm-efi.conf
Normal file
@ -0,0 +1,16 @@
|
||||
# regular VM with an EFI disk
|
||||
bios: ovmf
|
||||
boot: order=scsi0;ide2;net0
|
||||
cores: 1
|
||||
efidisk0: mydir:139/vm-139-disk-0.qcow2,size=128K
|
||||
ide2: local:iso/debian-10.6.0-amd64-netinst.iso,media=cdrom
|
||||
memory: 2048
|
||||
name: eficloneclone
|
||||
net0: virtio=7A:6C:A5:8B:11:93,bridge=vmbr0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: rbdkvm:vm-139-disk-1,size=4G
|
||||
scsihw: virtio-scsi-pci
|
||||
smbios1: uuid=21a7e7bc-3cd2-4232-a009-a41f4ee992ae
|
||||
sockets: 1
|
||||
vmgenid: 0
|
44
test/parse-config-input/sections.conf
Normal file
44
test/parse-config-input/sections.conf
Normal file
@ -0,0 +1,44 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: foo
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[PENDING]
|
||||
bios: ovmf
|
||||
|
||||
[special:cloudinit]
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
name: deb122
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
189
test/parse-config-input/snapshots.conf
Normal file
189
test/parse-config-input/snapshots.conf
Normal file
@ -0,0 +1,189 @@
|
||||
boot: order=scsi1;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
ide0: dir:111/vm-111-disk-2.qcow2,size=1G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2019-evaluation.iso,media=cdrom,size=4985424K
|
||||
machine: pc-i440fx-9.1
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
net1: e1000=BC:24:11:79:D5:65,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: win19_5_2_plus_stuff
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsi1: lvmthinbig:vm-111-disk-0,iothread=1,size=32G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
sockets: 1
|
||||
unused0: rbd:vm-111-disk-0
|
||||
vga: qxl
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 713da648-38a6-489e-b0b2-dd9cef419f33
|
||||
|
||||
[machine_version_5_1]
|
||||
boot: order=ide0;ide2;net0
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
ide0: lvmthinbig:vm-111-disk-0,size=32G
|
||||
ide2: sani:iso/Win2016-1616-evaluation.ISO,media=cdrom,size=5198078K
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: e1000=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736939109
|
||||
sockets: 1
|
||||
vmgenid: 1f314a76-50a3-4b92-9307-c8c6e313d3ca
|
||||
|
||||
[machine_version_5_1_with_virtio]
|
||||
boot: order=ide0;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
ide0: lvmthinbig:vm-111-disk-0,size=32G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2016-1616-evaluation.ISO,media=cdrom,size=5198078K
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: machine_version_5_1
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736940462
|
||||
sockets: 1
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 4f602356-cb9c-45ad-a554-d76d95c7c0f8
|
||||
|
||||
[ovmf_machine_version_5_1]
|
||||
bios: ovmf
|
||||
boot: order=ide0;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
efidisk0: rbd:vm-111-disk-0,efitype=4m,pre-enrolled-keys=1,size=1M
|
||||
ide0: lvmthinbig:vm-111-disk-0,size=32G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2016-1616-evaluation.ISO,media=cdrom,size=5198078K
|
||||
machine: pc-q35-5.1
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: e1000=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: machine_version_5_1_with_virtio
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736943308
|
||||
sockets: 1
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 4f602356-cb9c-45ad-a554-d76d95c7c0f8
|
||||
|
||||
[ovmf_machine_version_5_1_virtio]
|
||||
bios: ovmf
|
||||
boot: order=ide0;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
efidisk0: rbd:vm-111-disk-0,efitype=4m,pre-enrolled-keys=1,size=1M
|
||||
ide0: lvmthinbig:vm-111-disk-0,size=32G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2016-1616-evaluation.ISO,media=cdrom,size=5198078K
|
||||
machine: pc-q35-5.1
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: ovmf_machine_version_5_1
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736944525
|
||||
sockets: 1
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 00b95468-4f34-4faa-b0af-b214ff5bbcdf
|
||||
|
||||
[static-network]
|
||||
bios: ovmf
|
||||
boot: order=ide0;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
efidisk0: rbd:vm-111-disk-0,efitype=4m,pre-enrolled-keys=1,size=1M
|
||||
ide0: lvmthinbig:vm-111-disk-0,size=32G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2016-1616-evaluation.ISO,media=cdrom,size=5198078K
|
||||
machine: pc-q35-5.1
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: ovmf_machine_version_5_1_virtio
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736945713
|
||||
sockets: 1
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 5d65fc62-2cb1-4945-9641-631b37c265a5
|
||||
|
||||
[win19_5_2]
|
||||
boot: order=scsi1;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2019-evaluation.iso,media=cdrom,size=4985424K
|
||||
machine: pc-i440fx-5.2
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
net1: e1000=BC:24:11:79:D5:65,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: machine_version_5_1_with_virtio
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsi1: lvmthinbig:vm-111-disk-0,iothread=1,size=32G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736950690
|
||||
sockets: 1
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: f259de06-fa08-4ff7-8ba9-b1233a726ac4
|
||||
|
||||
[win19_5_2_plus_stuff]
|
||||
boot: order=scsi1;ide2;net0;ide1
|
||||
cores: 4
|
||||
cpu: x86-64-v2-AES
|
||||
ide0: dir:111/vm-111-disk-2.qcow2,size=1G
|
||||
ide1: sani:iso/virtio-win-0.1.266.iso,media=cdrom,size=707456K
|
||||
ide2: sani:iso/Win2019-evaluation.iso,media=cdrom,size=4985424K
|
||||
machine: pc-i440fx-5.2
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.1.2,ctime=1736349024
|
||||
name: win-machine-ver
|
||||
net0: virtio=BC:24:11:A3:DA:B1,bridge=vnet0,firewall=1
|
||||
net1: e1000=BC:24:11:79:D5:65,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: win10
|
||||
parent: win19_5_2
|
||||
scsi0: dir:111/vm-111-disk-1.qcow2,iothread=1,size=1G
|
||||
scsi1: lvmthinbig:vm-111-disk-0,iothread=1,size=32G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=2c4a2cda-712b-44ab-8728-51f5e734b658
|
||||
snaptime: 1736951300
|
||||
sockets: 1
|
||||
vga: qxl
|
||||
virtio0: dir:111/vm-111-disk-0.qcow2,iothread=1,size=1G
|
||||
vmgenid: 713da648-38a6-489e-b0b2-dd9cef419f33
|
57
test/parse-config-input/unknown-sections.conf
Normal file
57
test/parse-config-input/unknown-sections.conf
Normal file
@ -0,0 +1,57 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: foo
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[special:unknown123]
|
||||
name: foo
|
||||
|
||||
[PENDING]
|
||||
bios: ovmf
|
||||
|
||||
[special:unknown124]
|
||||
bios: seabios
|
||||
|
||||
[special:cloudinit]
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
name: deb122
|
||||
|
||||
[special:unknown125]
|
||||
name: bar
|
||||
|
||||
[foo]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip=dhcp,ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737548747
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[:3]
|
||||
name: baz
|
||||
cat: nya~
|
37
test/parse-config-input/verify-snapshot.conf
Normal file
37
test/parse-config-input/verify-snapshot.conf
Normal file
@ -0,0 +1,37 @@
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: 0
|
||||
ostype: l26
|
||||
parent: snap
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
sockets: 1
|
||||
unused0: rbd:vm-120-disk-0
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
||||
|
||||
[snap]
|
||||
boot: order=scsi0
|
||||
cores: 2
|
||||
cpu: x86-64-v2-AES
|
||||
ide2: lvm:vm-120-cloudinit,media=cdrom
|
||||
ipconfig0: ip6=dhcp
|
||||
memory: 4096
|
||||
meta: creation-qemu=9.0.2,ctime=1725975013
|
||||
name: deb1223
|
||||
net0: vmxnet3=BC:24:11:2C:69:EC,bridge=vnet0,firewall=1
|
||||
numa: verify meee~ :)
|
||||
ostype: l26
|
||||
scsi0: nfs:120/vm-120-disk-0.qcow2,iothread=1,size=4G
|
||||
scsihw: virtio-scsi-single
|
||||
smbios1: uuid=b3247ab1-1fe6-428e-965b-08a1b64a8746
|
||||
snaptime: 1737549549
|
||||
sockets: 1
|
||||
vmgenid: 7079e97c-50e3-4079-afe7-23e67566b946
|
92
test/run_parse_config_tests.pl
Executable file
92
test/run_parse_config_tests.pl
Executable file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Tests parsing and writing VM configuration files.
|
||||
# The parsing part is already covered by the config2command test too, but that only focuses on the
|
||||
# main section, not other section types and does not also test parsing in strict mode.
|
||||
#
|
||||
# If no expected file exists, the input is assumed to be equal to the expected output.
|
||||
# If $file.strict.error (respectively $file.non-strict.error) exists, it is assumed to be the
|
||||
# expected error when parsing the config in strict (respectively non-strict) mode.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib qw(..);
|
||||
|
||||
use File::Path qw(make_path remove_tree);
|
||||
|
||||
use Test::MockModule;
|
||||
use Test::More;
|
||||
|
||||
use PVE::QemuServer;
|
||||
use PVE::Tools;
|
||||
|
||||
my $INPUT_DIR = './parse-config-input';
|
||||
my $OUTPUT_DIR = './parse-config-output';
|
||||
my $EXPECTED_DIR = './parse-config-expected';
|
||||
|
||||
# NOTE update when you add/remove tests
|
||||
plan tests => 2 * 10;
|
||||
|
||||
sub run_tests {
|
||||
my ($strict) = @_;
|
||||
|
||||
PVE::Tools::dir_glob_foreach('./parse-config-input', '.*\.conf', sub {
|
||||
my ($file) = @_;
|
||||
|
||||
my $strict_mode = $strict ? 'strict' : 'non-strict';
|
||||
|
||||
my $expected_err_file = "${EXPECTED_DIR}/${file}.${strict_mode}.error";
|
||||
my $expected_err;
|
||||
$expected_err = PVE::Tools::file_get_contents($expected_err_file) if -f $expected_err_file;
|
||||
|
||||
my $fake_config_fn ="$file/qemu-server/8006.conf";
|
||||
my $input_file = "${INPUT_DIR}/${file}";
|
||||
my $input = PVE::Tools::file_get_contents($input_file);
|
||||
my $conf = eval {
|
||||
PVE::QemuServer::parse_vm_config($fake_config_fn, $input, $strict);
|
||||
};
|
||||
if (my $err = $@) {
|
||||
if ($expected_err) {
|
||||
is($err, $expected_err, $file);
|
||||
} else {
|
||||
note("got unexpected error '$err'");
|
||||
fail($file);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($expected_err) {
|
||||
note("expected error for strict mode did not occur: '$expected_err'");
|
||||
fail($file);
|
||||
return;
|
||||
}
|
||||
|
||||
my $output = eval { PVE::QemuServer::write_vm_config($fake_config_fn, $conf); };
|
||||
if (my $err = $@) {
|
||||
note("got unexpected error '$err'");
|
||||
fail($file);
|
||||
return;
|
||||
}
|
||||
|
||||
my $output_file = "${OUTPUT_DIR}/${file}";
|
||||
PVE::Tools::file_set_contents($output_file, $output);
|
||||
|
||||
my $expected_file = "${EXPECTED_DIR}/${file}";
|
||||
$expected_file = $input_file if !-f $expected_file;
|
||||
|
||||
my $cmd = ['diff', '-u', $expected_file, $output_file];
|
||||
if (system(@$cmd) == 0) {
|
||||
pass($file);
|
||||
} else {
|
||||
fail($file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
make_path(${OUTPUT_DIR});
|
||||
run_tests(0);
|
||||
run_tests(1);
|
||||
remove_tree(${OUTPUT_DIR}) or die "failed to remove output directory\n";
|
||||
|
||||
done_testing();
|
@ -7,7 +7,6 @@ use lib qw(..);
|
||||
|
||||
use Test::MockModule;
|
||||
use Test::More;
|
||||
use Test::MockModule;
|
||||
|
||||
use File::Basename;
|
||||
|
||||
@ -17,18 +16,11 @@ use PVE::Tools qw(dir_glob_foreach file_get_contents);
|
||||
my $INPUT_DIR = './restore-config-input';
|
||||
my $EXPECTED_DIR = './restore-config-expected';
|
||||
|
||||
my $pve_cluster_module = Test::MockModule->new('PVE::Cluster');
|
||||
$pve_cluster_module->mock(
|
||||
cfs_read_file => sub {
|
||||
return {};
|
||||
},
|
||||
);
|
||||
|
||||
# NOTE update when you add/remove tests
|
||||
plan tests => 4;
|
||||
|
||||
my $cfs_mock = Test::MockModule->new("PVE::Cluster");
|
||||
$cfs_mock->mock(
|
||||
my $pve_cluster_module = Test::MockModule->new("PVE::Cluster");
|
||||
$pve_cluster_module->mock(
|
||||
cfs_read_file => sub {
|
||||
my ($file) = @_;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user