add Storage::get_bandwidth_limit helper

Takes an operation, an optional requested bandwidth
limit override, and a list of storages involved in the
operation and lowers the requested bandwidth against global
and storage-specific limits unless the user has permissions
to change those.
This means:
 * Global limits apply to all users without Sys.Modify on /
   (as they can change datacenter.cfg options via the API).
 * Storage specific limits apply to users without
   Datastore.Allocate access on /storage/X for any involved
   storage X.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller
2018-01-30 11:46:19 +01:00
parent d2bf0b4945
commit 9edb99a5a7
15 changed files with 271 additions and 1 deletions

View File

@ -1534,4 +1534,76 @@ sub complete_volume {
return $res;
}
# Various io-heavy operations require io/bandwidth limits which can be
# configured on multiple levels: The global defaults in datacenter.cfg, and
# per-storage overrides. When we want to do a restore from storage A to storage
# B, we should take the smaller limit defined for storages A and B, and if no
# such limit was specified, use the one from datacenter.cfg.
sub get_bandwidth_limit {
my ($operation, $storage_list, $override) = @_;
# called for each limit (global, per-storage) with the 'default' and the
# $operation limit and should udpate $override for every limit affecting
# us.
my $use_global_limits = 0;
my $apply_limit = sub {
my ($bwlimit) = @_;
if (defined($bwlimit)) {
my $limits = PVE::JSONSchema::parse_property_string('bwlimit', $bwlimit);
my $limit = $limits->{$operation} // $limits->{default};
if (defined($limit)) {
if (!$override || $limit < $override) {
$override = $limit;
}
return;
}
}
# If there was no applicable limit, try to apply the global ones.
$use_global_limits = 1;
};
my $rpcenv = PVE::RPCEnvironment->get();
my $authuser = $rpcenv->get_user();
# Apply per-storage limits - if there are storages involved.
if (@$storage_list) {
my $config = config();
# The Datastore.Allocate permission allows us to modify the per-storage
# limits, therefore it also allows us to override them.
# Since we have most likely multiple storages to check, do a quick check on
# the general '/storage' path to see if we can skip the checks entirely:
return $override if $rpcenv->check($authuser, '/storage', ['Datastore.Allocate'], 1);
my %done;
foreach my $storage (@$storage_list) {
# Avoid duplicate checks:
next if $done{$storage};
$done{$storage} = 1;
# Otherwise we may still have individual /storage/$ID permissions:
if (!$rpcenv->check($authuser, "/storage/$storage", ['Datastore.Allocate'], 1)) {
# And if not: apply the limits.
my $storecfg = storage_config($config, $storage);
$apply_limit->($storecfg->{bwlimit});
}
}
# Storage limits take precedence over the datacenter defaults, so if
# a limit was applied:
return $override if !$use_global_limits;
}
# Sys.Modify on '/' means we can change datacenter.cfg which contains the
# global default limits.
if (!$rpcenv->check($authuser, '/', ['Sys.Modify'], 1)) {
# So if we cannot modify global limits, apply them to our currently
# requested override.
my $dc = cfs_read_file('datacenter.cfg');
$apply_limit->($dc->{bwlimit});
}
return $override;
}
1;

View File

@ -45,6 +45,7 @@ sub options {
content => { optional => 1 },
nodes => { optional => 1 },
disable => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -42,6 +42,7 @@ sub properties {
type => 'string',
default => 'no',
},
bwlimit => get_standard_option('bwlimit'),
};
}
@ -56,6 +57,7 @@ sub options {
format => { optional => 1 },
mkdir => { optional => 1 },
is_mountpoint => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -136,6 +136,7 @@ sub options {
content => { optional => 1 },
format => { optional => 1 },
mkdir => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -68,6 +68,7 @@ sub options {
nodes => { optional => 1},
disable => { optional => 1},
content => { optional => 1},
bwlimit => { optional => 1 },
};
}

View File

@ -261,6 +261,7 @@ sub options {
nodes => { optional => 1},
disable => { optional => 1},
content => { optional => 1},
bwlimit => { optional => 1 },
};
}

View File

@ -202,6 +202,7 @@ sub options {
content => { optional => 1 },
base => { fixed => 1, optional => 1 },
tagged_only => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -49,6 +49,7 @@ sub options {
nodes => { optional => 1 },
disable => { optional => 1 },
content => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -86,6 +86,7 @@ sub options {
content => { optional => 1 },
format => { optional => 1 },
mkdir => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -278,6 +278,7 @@ sub options {
username => { optional => 1 },
content => { optional => 1 },
krbd => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -115,6 +115,7 @@ sub options {
disable => { optional => 1 },
portal => { fixed => 1 },
content => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -205,6 +205,7 @@ sub options {
comstar_hg => { optional => 1 },
comstar_tg => { optional => 1 },
content => { optional => 1 },
bwlimit => { optional => 1 },
};
}

View File

@ -43,6 +43,7 @@ sub options {
nodes => { optional => 1 },
disable => { optional => 1 },
content => { optional => 1 },
bwlimit => { optional => 1 },
};
}