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.lampre...@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


_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to