[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