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