vm_exists_on_node in PVE::QemuConfig checks if a config file for a vmid exists
vm_running_locally in PVE::QemuServer::Helpers checks if a VM is running on the local machine by probing its pidfile and checking /proc/.../cmdline check_running is left in QemuServer for compatibility, but changed to simply call the two new helper functions. Both methods are also correctly mocked for testing snapshots. Signed-off-by: Stefan Reiter <s.rei...@proxmox.com> --- No users of check_running are changed for now, but this could be done in a future series. PVE/QemuConfig.pm | 16 ++++++++- PVE/QemuServer.pm | 67 ++------------------------------------ PVE/QemuServer/Helpers.pm | 68 +++++++++++++++++++++++++++++++++++++++ test/snapshot-test.pm | 16 +++++++-- 4 files changed, 100 insertions(+), 67 deletions(-) diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm index 499d5e3..bca2725 100644 --- a/PVE/QemuConfig.pm +++ b/PVE/QemuConfig.pm @@ -16,6 +16,20 @@ my $nodename = PVE::INotify::nodename(); my $MAX_UNUSED_DISKS = 256; +sub vm_exists_on_node { + my ($vmid, $node, $noerr) = @_; + + my $filename = __PACKAGE__->config_file($vmid, $node); + my $exists = -f $filename; + + if (!$noerr && !$exists) { + my $node_err = $node ? "on node '$node'" : "on local node"; + die "unable to find configuration file for VM $vmid $node_err - no such machine\n"; + } + + return $exists; +} + # BEGIN implemented abstract methods from PVE::AbstractConfig sub guest_type { @@ -161,7 +175,7 @@ sub __snapshot_save_vmstate { sub __snapshot_check_running { my ($class, $vmid) = @_; - return PVE::QemuServer::check_running($vmid); + return PVE::QemuServer::Helpers::vm_running_locally($vmid); } sub __snapshot_check_freeze_needed { diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index ac9abd0..cc9288f 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2952,73 +2952,12 @@ sub check_local_storage_availability { return $nodehash } -sub parse_cmdline { - my ($pid) = @_; - - my $fh = IO::File->new("/proc/$pid/cmdline", "r"); - if (defined($fh)) { - my $line = <$fh>; - $fh->close; - return undef if !$line; - my @param = split(/\0/, $line); - - my $cmd = $param[0]; - return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@); - - my $phash = {}; - my $pending_cmd; - for (my $i = 0; $i < scalar (@param); $i++) { - my $p = $param[$i]; - next if !$p; - - if ($p =~ m/^--?(.*)$/) { - if ($pending_cmd) { - $phash->{$pending_cmd} = 1; - } - $pending_cmd = $1; - } elsif ($pending_cmd) { - $phash->{$pending_cmd} = $p; - $pending_cmd = undef; - } - } - - return $phash; - } - return undef; -} - +# Compat only, use vm_exists_on_node and vm_running_locally where possible sub check_running { my ($vmid, $nocheck, $node) = @_; - my $filename = PVE::QemuConfig->config_file($vmid, $node); - - die "unable to find configuration file for VM $vmid - no such machine\n" - if !$nocheck && ! -f $filename; - - my $pidfile = PVE::QemuServer::Helpers::pidfile_name($vmid); - - if (my $fd = IO::File->new("<$pidfile")) { - my $st = stat($fd); - my $line = <$fd>; - close($fd); - - my $mtime = $st->mtime; - if ($mtime > time()) { - warn "file '$filename' modified in future\n"; - } - - if ($line =~ m/^(\d+)$/) { - my $pid = $1; - my $cmdline = parse_cmdline($pid); - if ($cmdline && $cmdline->{pidfile} eq $pidfile) { - if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) { - return $pid; - } - } - } - } - - return undef; + PVE::QemuConfig::vm_exists_on_node($vmid, $node, $nocheck); + return PVE::QemuServer::Helpers::vm_running_locally($vmid); } sub vzlist { diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm index 91e2c83..1f472a9 100644 --- a/PVE/QemuServer/Helpers.pm +++ b/PVE/QemuServer/Helpers.pm @@ -3,7 +3,10 @@ package PVE::QemuServer::Helpers; use strict; use warnings; +use File::stat; + use PVE::INotify; +use PVE::ProcFSTools; my $nodename = PVE::INotify::nodename(); @@ -34,4 +37,69 @@ sub vnc_socket { return "${var_run_tmpdir}/$vmid.vnc"; } +# Parse the cmdline of a running kvm/qemu process and return arguments as hash +sub parse_cmdline { + my ($pid) = @_; + + my $fh = IO::File->new("/proc/$pid/cmdline", "r"); + if (defined($fh)) { + my $line = <$fh>; + $fh->close; + return undef if !$line; + my @param = split(/\0/, $line); + + my $cmd = $param[0]; + return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@); + + my $phash = {}; + my $pending_cmd; + for (my $i = 0; $i < scalar (@param); $i++) { + my $p = $param[$i]; + next if !$p; + + if ($p =~ m/^--?(.*)$/) { + if ($pending_cmd) { + $phash->{$pending_cmd} = 1; + } + $pending_cmd = $1; + } elsif ($pending_cmd) { + $phash->{$pending_cmd} = $p; + $pending_cmd = undef; + } + } + + return $phash; + } + return undef; +} + +sub vm_running_locally { + my ($vmid) = @_; + + my $pidfile = pidfile_name($vmid); + + if (my $fd = IO::File->new("<$pidfile")) { + my $st = stat($fd); + my $line = <$fd>; + close($fd); + + my $mtime = $st->mtime; + if ($mtime > time()) { + warn "file '$pidfile' modified in future\n"; + } + + if ($line =~ m/^(\d+)$/) { + my $pid = $1; + my $cmdline = parse_cmdline($pid); + if ($cmdline && $cmdline->{pidfile} eq $pidfile) { + if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) { + return $pid; + } + } + } + } + + return undef; +} + 1; diff --git a/test/snapshot-test.pm b/test/snapshot-test.pm index c95e7f3..a76b4fd 100644 --- a/test/snapshot-test.pm +++ b/test/snapshot-test.pm @@ -297,14 +297,22 @@ sub __snapshot_save_vmstate { $snap->{vmstate} = "somestorage:state-volume"; $snap->{runningmachine} = "somemachine" } + +sub vm_exists_on_node { + my ($vmid, $node) = @_; + return -f cfs_config_path(undef, $vmid, $node); +} # END mocked PVE::QemuConfig methods -# BEGIN redefine PVE::QemuServer methods +# BEGIN mocked PVE::QemuServer::Helpers methods -sub check_running { +sub vm_running_locally { return $running; } +# END mocked PVE::QemuServer::Helpers methods + +# BEGIN redefine PVE::QemuServer methods sub do_snapshots_with_qemu { return 0; @@ -369,6 +377,9 @@ sub vm_stop { PVE::Tools::run_command("rm -rf snapshot-working"); PVE::Tools::run_command("cp -a snapshot-input snapshot-working"); +my $qemu_helpers_module = new Test::MockModule('PVE::QemuServer::Helpers'); +$qemu_helpers_module->mock('vm_running_locally', \&vm_running_locally); + my $qemu_config_module = new Test::MockModule('PVE::QemuConfig'); $qemu_config_module->mock('config_file_lock', \&config_file_lock); $qemu_config_module->mock('cfs_config_path', \&cfs_config_path); @@ -376,6 +387,7 @@ $qemu_config_module->mock('load_config', \&load_config); $qemu_config_module->mock('write_config', \&write_config); $qemu_config_module->mock('has_feature', \&has_feature); $qemu_config_module->mock('__snapshot_save_vmstate', \&__snapshot_save_vmstate); +$qemu_config_module->mock('vm_exists_on_node', \&vm_exists_on_node); # ignore existing replication config my $repl_config_module = new Test::MockModule('PVE::ReplicationConfig'); -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel