[pve-devel] [PATCH cluster] add next_unused_vmid

Thomas Lamprecht t.lamprecht at proxmox.com
Mon Oct 17 17:44:32 CEST 2016


This can be used to get a unused VMID in a thread safe way, also the
new VMID can be reserved temporary (60s for now) so that multiple
calls to the API at the same time, which often first request a VMID
and then, in a later moment reserve it actually thorugh writing the
VMID.conf file, do not get in conflict with each other.

The implemented method is similar to the next_unused_port methods
from the PVE::Tools package, with the distinction that we have the
file for reserved VMIDs on the cluster FS as VMIDs are a cluster wide
resource.

This allows us to address bug #889

Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
---
 data/PVE/Cluster.pm | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 data/src/status.c   |  1 +
 2 files changed, 69 insertions(+)

diff --git a/data/PVE/Cluster.pm b/data/PVE/Cluster.pm
index fe741cf..be569ad 100644
--- a/data/PVE/Cluster.pm
+++ b/data/PVE/Cluster.pm
@@ -75,6 +75,7 @@ my $observed = {
     'ha/groups.cfg' => 1,
     'ha/fence.cfg' => 1,
     'status.cfg' => 1,
+    '.pve-reserved-vmids' => 1,
 };
 
 # only write output if something fails
@@ -996,6 +997,73 @@ sub check_vmid_unused {
     die "$vmtypestr $vmid already exists on node '$d->{node}'\n";
 }
 
+cfs_register_file(".pve-reserved-vmids",
+		  sub { my ($fn, $raw) = @_; return defined($raw) ? $raw : ''; },
+		  sub { my ($fn, $raw) = @_; return $raw; });
+
+sub next_unused_vmid {
+    my ($range_start, $range_end, $reserve) = @_;
+
+    # We use a file to register allocated VMIDs
+    # Those registrations expires after $expiretime.
+    # We use this to avoid race conditions between
+    # allocation and use of VMIDs.
+
+    my $file = ".pve-reserved-vmids";
+
+    my $code = sub {
+
+	my $expiretime = 60;
+	my $ctime = time();
+
+	my $reserved_vmids = {};
+
+	my $vmlist = get_vmlist() || {};
+	my $idlist = $vmlist->{ids} || {};
+
+	if (my $raw = cfs_read_file($file)) {
+	    while ($raw =~ /^\h*(.*?)\h*$/gm) {
+		my $line = $1;
+		if ($line =~ m/^(\d+)\s(\d+)$/) {
+		    my ($vmid, $timestamp) = ($1, $2);
+		    if (($timestamp + $expiretime) > $ctime) {
+			$reserved_vmids->{$vmid} = $timestamp; # not expired
+		    }
+		}
+	    }
+	}
+
+	my $new_vmid;
+	for (my $vmid = $range_start; $vmid < $range_end; $vmid++) {
+	    next if $reserved_vmids->{$vmid};
+
+	    if (!defined($idlist->{$vmid})) {
+		$new_vmid = $vmid;
+		$reserved_vmids->{$vmid} = $ctime;
+		last;
+	    }
+	}
+
+	if ($reserve) {
+	    my $data = "";
+	    foreach my $vmid (keys %$reserved_vmids) {
+		$data .= "$vmid $reserved_vmids->{$vmid}\n";
+	    }
+
+	    cfs_write_file($file, $data);
+	}
+
+	return $new_vmid;
+    };
+
+    my $vmid = cfs_lock_file($file, 10, $code);
+    die $@ if $@;
+
+    die "unable to get any free VMID\n" if !$vmid;
+
+    return $vmid;
+}
+
 sub check_node_exists {
     my ($nodename, $noerr) = @_;
 
diff --git a/data/src/status.c b/data/src/status.c
index 3896fcb..42ac5d4 100644
--- a/data/src/status.c
+++ b/data/src/status.c
@@ -89,6 +89,7 @@ static memdb_change_t memdb_change_array[] = {
 	{ .path = "ha/groups.cfg" },
 	{ .path = "ha/fence.cfg" },
 	{ .path = "status.cfg" },
+	{ .path = ".reserved-vmids" },
 };
 
 static GMutex mutex;
-- 
2.1.4





More information about the pve-devel mailing list