[pve-devel] [PATCH manager v2 3/8] ceph: add autoscale_status to api calls

Alwin Antreich a.antreich at proxmox.com
Tue Nov 24 11:58:06 CET 2020


the properties target_size_ratio and target_size_bytes are the only two
options by ceph to set on a pool. The updated pool list shows now
autoscale settings & status. Including the new target PGs. To make it
easier for new users to get/set the correct amount of PGs.

And use parameter extraction instead of the unneeded ref copy for params.

Signed-off-by: Alwin Antreich <a.antreich at proxmox.com>
---
 PVE/API2/Ceph/POOLS.pm | 131 +++++++++++++++++++++++++++++++----------
 PVE/CLI/pveceph.pm     |   3 +
 PVE/Ceph/Tools.pm      |  21 +++++++
 3 files changed, 123 insertions(+), 32 deletions(-)

diff --git a/PVE/API2/Ceph/POOLS.pm b/PVE/API2/Ceph/POOLS.pm
index 19fc1b7e..324a1f79 100644
--- a/PVE/API2/Ceph/POOLS.pm
+++ b/PVE/API2/Ceph/POOLS.pm
@@ -3,6 +3,7 @@ package PVE::API2::Ceph::POOLS;
 use strict;
 use warnings;
 
+use PVE::Tools qw(extract_param);
 use PVE::Ceph::Tools;
 use PVE::Ceph::Services;
 use PVE::JSONSchema qw(get_standard_option);
@@ -67,6 +68,19 @@ my $ceph_pool_common_options = sub {
 	    default => 'warn',
 	    optional => 1,
 	},
+	target_size => {
+	    description => "The estimated target size of the pool for the PG autoscaler.",
+	    title => 'PG Autoscale Target Size',
+	    type => 'string',
+	    pattern => '(\d+(?:\.\d+)?)([KMGT])?',
+	    optional => 1,
+	},
+	target_size_ratio => {
+	    description => "The estimated target ratio of the pool for the PG autoscaler.",
+	    title => 'PG Autoscale Target Ratio',
+	    type => 'number',
+	    optional => 1,
+	},
     };
 
     if ($nodefault) {
@@ -105,6 +119,30 @@ my $get_storages = sub {
     return $res;
 };
 
+my $get_autoscale_status = sub {
+    my ($rados, $pool) = @_;
+
+    $rados = PVE::RADOS->new() if !defined($rados);
+
+    my $autoscale = $rados->mon_command({
+	    prefix => 'osd pool autoscale-status'});
+
+    my $data;
+    foreach my $p (@$autoscale) {
+	$p->{would_adjust} = "$p->{would_adjust}"; # boolean
+	push @$data, $p;
+    }
+
+    if ($pool) {
+	foreach my $p (@$data) {
+	    next if $p->{pool_name} ne $pool;
+	    $data = $p;
+	}
+    }
+
+    return $data;
+};
+
 
 __PACKAGE__->register_method ({
     name => 'lspools',
@@ -127,16 +165,20 @@ __PACKAGE__->register_method ({
 	items => {
 	    type => "object",
 	    properties => {
-		pool => { type => 'integer', title => 'ID' },
-		pool_name => { type => 'string', title => 'Name' },
-		size => { type => 'integer', title => 'Size' },
-		min_size => { type => 'integer', title => 'Min Size' },
-		pg_num => { type => 'integer', title => 'PG Num' },
-		pg_autoscale_mode => { type => 'string', optional => 1, title => 'PG Autoscale Mode' },
-		crush_rule => { type => 'integer', title => 'Crush Rule' },
-		crush_rule_name => { type => 'string', title => 'Crush Rule Name' },
-		percent_used => { type => 'number', title => '%-Used' },
-		bytes_used => { type => 'integer', title => 'Used' },
+		pool              => { type => 'integer', title => 'ID' },
+		pool_name         => { type => 'string',  title => 'Name' },
+		size              => { type => 'integer', title => 'Size' },
+		min_size          => { type => 'integer', title => 'Min Size' },
+		pg_num            => { type => 'integer', title => 'PG Num' },
+		pg_num_final      => { type => 'integer', title => 'Optimal # of PGs' },
+		pg_autoscale_mode => { type => 'string',  title => 'PG Autoscale Mode', optional => 1 },
+		crush_rule        => { type => 'integer', title => 'Crush Rule' },
+		crush_rule_name   => { type => 'string',  title => 'Crush Rule Name' },
+		percent_used      => { type => 'number',  title => '%-Used' },
+		bytes_used        => { type => 'integer', title => 'Used' },
+		target_size       => { type => 'integer', title => 'PG Autoscale Target Size', optional => 1 },
+		target_size_ratio => { type => 'number',  title => 'PG Autoscale Target Ratio',optional => 1, },
+		autoscale_status  => { type => 'object',  title => 'Autoscale Status', optional => 1 },
 	    },
 	},
 	links => [ { rel => 'child', href => "{pool_name}" } ],
@@ -176,12 +218,18 @@ __PACKAGE__->register_method ({
 	    'pg_autoscale_mode',
 	];
 
+	my $autoscale = $get_autoscale_status->($rados);
+
 	foreach my $e (@{$res->{pools}}) {
 	    my $d = {};
 	    foreach my $attr (@$attr_list) {
 		$d->{$attr} = $e->{$attr} if defined($e->{$attr});
 	    }
 
+	    # some info is nested under options instead
+	    $d->{target_size} = $e->{options}->{target_size_bytes};
+	    $d->{target_size_ratio} = $e->{options}->{target_size_ratio};
+
 	    if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) {
 		$d->{crush_rule_name} = $rules->{$d->{crush_rule}};
 	    }
@@ -190,10 +238,17 @@ __PACKAGE__->register_method ({
 		$d->{bytes_used} = $s->{bytes_used};
 		$d->{percent_used} = $s->{percent_used};
 	    }
+
+	    foreach my $p (@$autoscale) {
+		next if ($p->{pool_id} ne $e->{pool});
+		$d->{autoscale_status} = $p;
+		# pick some info directly from the autoscale_status
+		$d->{pg_num_final} = $p->{pg_num_final};
+	    }
+
 	    push @$data, $d;
 	}
 
-
 	return $data;
     }});
 
@@ -233,34 +288,37 @@ __PACKAGE__->register_method ({
 	PVE::Cluster::check_cfs_quorum();
 	PVE::Ceph::Tools::check_ceph_configured();
 
-	my $pool = $param->{name};
+	my $pool = extract_param($param, 'name');
+	my $add_storages = extract_param($param, 'add_storages');
+	delete $param->{node}; # not a ceph option
+
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $user = $rpcenv->get_user();
 
-	if ($param->{add_storages}) {
+	# Ceph uses target_size_bytes
+	if (defined($param->{'target_size'})) {
+	    my $target_sizestr = extract_param($param, 'target_size');
+	    $param->{target_size_bytes} = PVE::Ceph::Tools::convert_to_bytes($target_sizestr);
+	}
+
+	if (defined($add_storages)) {
 	    $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
 	    die "pool name contains characters which are illegal for storage naming\n"
 		if !PVE::JSONSchema::parse_storage_id($pool);
 	}
 
-	my $ceph_param = \%$param;
-	for my $item ('add_storages', 'name', 'node') {
-	    # not ceph parameters
-	    delete $ceph_param->{$item};
-	}
-
 	# pool defaults
-	$ceph_param->{pg_num} //= 128;
-	$ceph_param->{size} //= 3;
-	$ceph_param->{min_size} //= 2;
-	$ceph_param->{application} //= 'rbd';
-	$ceph_param->{pg_autoscale_mode} //= 'warn';
+	$param->{pg_num} //= 128;
+	$param->{size} //= 3;
+	$param->{min_size} //= 2;
+	$param->{application} //= 'rbd';
+	$param->{pg_autoscale_mode} //= 'warn';
 
 	my $worker = sub {
 
-	    PVE::Ceph::Tools::create_pool($pool, $ceph_param);
+	    PVE::Ceph::Tools::create_pool($pool, $param);
 
-	    if ($param->{add_storages}) {
+	    if (defined($add_storages)) {
 		my $err;
 		eval { $add_storage->($pool, "${pool}"); };
 		if ($@) {
@@ -391,15 +449,17 @@ __PACKAGE__->register_method ({
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $authuser = $rpcenv->get_user();
 
-	my $pool = $param->{name};
-	my $ceph_param = \%$param;
-	for my $item ('name', 'node') {
-	    # not ceph parameters
-	    delete $ceph_param->{$item};
+	my $pool = extract_param($param, 'name');
+	delete $param->{node};
+
+	# Ceph uses target_size_bytes
+	if (defined($param->{'target_size'})) {
+	    my $target_sizestr = extract_param($param, 'target_size');
+	    $param->{target_size_bytes} = PVE::Ceph::Tools::convert_to_bytes($target_sizestr);
 	}
 
 	my $worker = sub {
-	    PVE::Ceph::Tools::set_pool($pool, $ceph_param);
+	    PVE::Ceph::Tools::set_pool($pool, $param);
 	};
 
 	return $rpcenv->fork_worker('cephsetpool', $pool,  $authuser, $worker);
@@ -448,6 +508,7 @@ __PACKAGE__->register_method ({
 	    use_gmt_hitset         => { type => 'boolean', title => 'use_gmt_hitset' },
 	    fast_read              => { type => 'boolean', title => 'Fast Read' },
 	    statistics             => { type => 'object', title => 'Statistics', optional => 1 },
+	    autoscale_status       => { type => 'object', title => 'Autoscale Status', optional => 1 },
 	    %{ $ceph_pool_common_options->() },
 	},
     },
@@ -483,8 +544,12 @@ __PACKAGE__->register_method ({
 	    hashpspool             => "$res->{hashpspool}",
 	    use_gmt_hitset         => "$res->{use_gmt_hitset}",
 	    fast_read              => "$res->{fast_read}",
+	    # is only avialable if set
+	    target_size            => $res->{target_size_bytes} // undef,
+	    target_size_ratio      => $res->{target_size_ratio} // undef,
 	};
 
+
 	if ($verbose) {
 	    my $stats;
 	    my $res = $rados->mon_command({ prefix => 'df' });
@@ -498,6 +563,8 @@ __PACKAGE__->register_method ({
 	    my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", });
 	    my @application = keys %$apps;
 	    $data->{application} = $application[0];
+
+	    $data->{autoscale_status} = $get_autoscale_status->($rados, $pool),
 	}
 
 	return $data;
diff --git a/PVE/CLI/pveceph.pm b/PVE/CLI/pveceph.pm
index 69421ca6..b7b68540 100755
--- a/PVE/CLI/pveceph.pm
+++ b/PVE/CLI/pveceph.pm
@@ -187,7 +187,10 @@ our $cmddef = {
 		    'size',
 		    'min_size',
 		    'pg_num',
+		    'pg_num_final',
 		    'pg_autoscale_mode',
+		    'target_size',
+		    'target_size_ratio',
 		    'crush_rule_name',
 		    'percent_used',
 		    'bytes_used',
diff --git a/PVE/Ceph/Tools.pm b/PVE/Ceph/Tools.pm
index 12d309be..d95e8676 100644
--- a/PVE/Ceph/Tools.pm
+++ b/PVE/Ceph/Tools.pm
@@ -487,4 +487,25 @@ sub ceph_cluster_status {
     return $status;
 }
 
+sub convert_to_bytes {
+    my ($sizestr) = shift;
+
+    die "internal error" if $sizestr !~ m/^(\d+(?:\.\d+)?)([KMGT])?$/;
+    my ($newsize, $unit) = ($1, $2);
+
+    if ($unit) {
+	if ($unit eq 'K') {
+	    $newsize = $newsize * 1024;
+	} elsif ($unit eq 'M') {
+	    $newsize = $newsize * 1024 * 1024;
+	} elsif ($unit eq 'G') {
+	    $newsize = $newsize * 1024 * 1024 * 1024;
+	} elsif ($unit eq 'T') {
+	    $newsize = $newsize * 1024 * 1024 * 1024 * 1024;
+	}
+    }
+
+    return int($newsize);
+}
+
 1;
-- 
2.27.0






More information about the pve-devel mailing list