From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with UTF8SMTPS id 2AB9662ECC for ; Tue, 24 Nov 2020 14:53:56 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with UTF8SMTP id 2050BDE35 for ; Tue, 24 Nov 2020 14:53:26 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with UTF8SMTPS id 8E7C5DE19 for ; Tue, 24 Nov 2020 14:53:23 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with UTF8SMTP id 5A336406E4; Tue, 24 Nov 2020 14:53:23 +0100 (CET) To: Proxmox VE development discussion , Alwin Antreich References: <20201124105811.1416723-1-a.antreich@proxmox.com> <20201124105811.1416723-2-a.antreich@proxmox.com> From: Dominik Csapak Message-ID: Date: Tue, 24 Nov 2020 14:53:20 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Thunderbird/84.0 MIME-Version: 1.0 In-Reply-To: <20201124105811.1416723-2-a.antreich@proxmox.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.318 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment NICE_REPLY_A -0.001 Looks like a legit reply (A) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [pools.pm, pveceph.pm, ceph.pm, osd.pm, mon.pm, mds.pm, fs.pm] Subject: Re: [pve-devel] [PATCH manager v2 1/8] api: ceph: subclass pools X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Nov 2020 13:53:56 -0000 mhmm.. you did not simply move the code, you added at least the 'titles' it would be better to have a commit which really only moves the code to a new file or at least mention it in the commit message one comment inline On 11/24/20 11:58 AM, Alwin Antreich wrote: > for better handling and since the pool endpoints got more entries. > > Signed-off-by: Alwin Antreich > --- > PVE/API2/Ceph/Makefile | 1 + > PVE/API2/Ceph.pm | 380 +------------------------------------- > PVE/API2/Ceph/POOLS.pm | 404 +++++++++++++++++++++++++++++++++++++++++ > PVE/CLI/pveceph.pm | 9 +- > 4 files changed, 416 insertions(+), 378 deletions(-) > create mode 100644 PVE/API2/Ceph/POOLS.pm > > diff --git a/PVE/API2/Ceph/Makefile b/PVE/API2/Ceph/Makefile > index 5b6493d5..65c7b862 100644 > --- a/PVE/API2/Ceph/Makefile > +++ b/PVE/API2/Ceph/Makefile > @@ -5,6 +5,7 @@ PERLSOURCE= \ > MON.pm \ > OSD.pm \ > FS.pm \ > + POOLS.pm \ why POOLS.pm and not Pools.pm ? the others are only capitalized because they are abbreviations i'd prefer Pools.pm (but this is debatable...) > MDS.pm > > all: > diff --git a/PVE/API2/Ceph.pm b/PVE/API2/Ceph.pm > index c3a3091d..8e7e525e 100644 > --- a/PVE/API2/Ceph.pm > +++ b/PVE/API2/Ceph.pm > @@ -20,6 +20,7 @@ use PVE::Tools qw(run_command file_get_contents file_set_contents); > > use PVE::API2::Ceph::OSD; > use PVE::API2::Ceph::FS; > +use PVE::API2::Ceph::POOLS; > use PVE::API2::Ceph::MDS; > use PVE::API2::Ceph::MGR; > use PVE::API2::Ceph::MON; > @@ -54,6 +55,11 @@ __PACKAGE__->register_method ({ > path => 'fs', > }); > > +__PACKAGE__->register_method ({ > + subclass => "PVE::API2::Ceph::POOLS", > + path => 'pools', > +}); > + > __PACKAGE__->register_method ({ > name => 'index', > path => '', > @@ -239,35 +245,6 @@ __PACKAGE__->register_method ({ > return $res; > }}); > > -my $add_storage = sub { > - my ($pool, $storeid) = @_; > - > - my $storage_params = { > - type => 'rbd', > - pool => $pool, > - storage => $storeid, > - krbd => 0, > - content => 'rootdir,images', > - }; > - > - PVE::API2::Storage::Config->create($storage_params); > -}; > - > -my $get_storages = sub { > - my ($pool) = @_; > - > - my $cfg = PVE::Storage::config(); > - > - my $storages = $cfg->{ids}; > - my $res = {}; > - foreach my $storeid (keys %$storages) { > - my $curr = $storages->{$storeid}; > - $res->{$storeid} = $storages->{$storeid} > - if $curr->{type} eq 'rbd' && $pool eq $curr->{pool}; > - } > - > - return $res; > -}; > > __PACKAGE__->register_method ({ > name => 'init', > @@ -583,227 +560,6 @@ __PACKAGE__->register_method ({ > return PVE::Ceph::Tools::ceph_cluster_status(); > }}); > > -__PACKAGE__->register_method ({ > - name => 'lspools', > - path => 'pools', > - method => 'GET', > - description => "List all pools.", > - proxyto => 'node', > - protected => 1, > - permissions => { > - check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1], > - }, > - parameters => { > - additionalProperties => 0, > - properties => { > - node => get_standard_option('pve-node'), > - }, > - }, > - returns => { > - type => 'array', > - 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' }, > - }, > - }, > - links => [ { rel => 'child', href => "{pool_name}" } ], > - }, > - code => sub { > - my ($param) = @_; > - > - PVE::Ceph::Tools::check_ceph_inited(); > - > - my $rados = PVE::RADOS->new(); > - > - my $stats = {}; > - my $res = $rados->mon_command({ prefix => 'df' }); > - > - foreach my $d (@{$res->{pools}}) { > - next if !$d->{stats}; > - next if !defined($d->{id}); > - $stats->{$d->{id}} = $d->{stats}; > - } > - > - $res = $rados->mon_command({ prefix => 'osd dump' }); > - my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'}); > - > - my $rules = {}; > - for my $rule (@$rulestmp) { > - $rules->{$rule->{rule_id}} = $rule->{rule_name}; > - } > - > - my $data = []; > - my $attr_list = [ > - 'pool', > - 'pool_name', > - 'size', > - 'min_size', > - 'pg_num', > - 'crush_rule', > - 'pg_autoscale_mode', > - ]; > - > - foreach my $e (@{$res->{pools}}) { > - my $d = {}; > - foreach my $attr (@$attr_list) { > - $d->{$attr} = $e->{$attr} if defined($e->{$attr}); > - } > - > - if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) { > - $d->{crush_rule_name} = $rules->{$d->{crush_rule}}; > - } > - > - if (my $s = $stats->{$d->{pool}}) { > - $d->{bytes_used} = $s->{bytes_used}; > - $d->{percent_used} = $s->{percent_used}; > - } > - push @$data, $d; > - } > - > - > - return $data; > - }}); > - > - > -my $ceph_pool_common_options = sub { > - my ($nodefault) = shift; > - my $options = { > - name => { > - description => "The name of the pool. It must be unique.", > - type => 'string', > - }, > - size => { > - description => 'Number of replicas per object', > - type => 'integer', > - default => 3, > - optional => 1, > - minimum => 1, > - maximum => 7, > - }, > - min_size => { > - description => 'Minimum number of replicas per object', > - type => 'integer', > - default => 2, > - optional => 1, > - minimum => 1, > - maximum => 7, > - }, > - pg_num => { > - description => "Number of placement groups.", > - type => 'integer', > - default => 128, > - optional => 1, > - minimum => 8, > - maximum => 32768, > - }, > - crush_rule => { > - description => "The rule to use for mapping object placement in the cluster.", > - type => 'string', > - optional => 1, > - }, > - application => { > - description => "The application of the pool.", > - default => 'rbd', > - type => 'string', > - enum => ['rbd', 'cephfs', 'rgw'], > - optional => 1, > - }, > - pg_autoscale_mode => { > - description => "The automatic PG scaling mode of the pool.", > - type => 'string', > - enum => ['on', 'off', 'warn'], > - default => 'warn', > - optional => 1, > - }, > - }; > - > - if ($nodefault) { > - delete $options->{$_}->{default} for keys %$options; > - } > - return $options; > -}; > - > - > -__PACKAGE__->register_method ({ > - name => 'createpool', > - path => 'pools', > - method => 'POST', > - description => "Create POOL", > - proxyto => 'node', > - protected => 1, > - permissions => { > - check => ['perm', '/', [ 'Sys.Modify' ]], > - }, > - parameters => { > - additionalProperties => 0, > - properties => { > - node => get_standard_option('pve-node'), > - add_storages => { > - description => "Configure VM and CT storage using the new pool.", > - type => 'boolean', > - optional => 1, > - }, > - %{ $ceph_pool_common_options->() }, > - }, > - }, > - returns => { type => 'string' }, > - code => sub { > - my ($param) = @_; > - > - PVE::Cluster::check_cfs_quorum(); > - PVE::Ceph::Tools::check_ceph_configured(); > - > - my $pool = $param->{name}; > - my $rpcenv = PVE::RPCEnvironment::get(); > - my $user = $rpcenv->get_user(); > - > - if ($param->{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'; > - > - my $worker = sub { > - > - PVE::Ceph::Tools::create_pool($pool, $ceph_param); > - > - if ($param->{add_storages}) { > - my $err; > - eval { $add_storage->($pool, "${pool}"); }; > - if ($@) { > - warn "failed to add storage: $@"; > - $err = 1; > - } > - die "adding storage for pool '$pool' failed, check log and add manually!\n" > - if $err; > - } > - }; > - > - return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker); > - }}); > > my $possible_flags = PVE::Ceph::Tools::get_possible_osd_flags(); > my $possible_flags_list = [ sort keys %$possible_flags ]; > @@ -913,130 +669,6 @@ __PACKAGE__->register_method ({ > return undef; > }}); > > -__PACKAGE__->register_method ({ > - name => 'destroypool', > - path => 'pools/{name}', > - method => 'DELETE', > - description => "Destroy pool", > - proxyto => 'node', > - protected => 1, > - permissions => { > - check => ['perm', '/', [ 'Sys.Modify' ]], > - }, > - parameters => { > - additionalProperties => 0, > - properties => { > - node => get_standard_option('pve-node'), > - name => { > - description => "The name of the pool. It must be unique.", > - type => 'string', > - }, > - force => { > - description => "If true, destroys pool even if in use", > - type => 'boolean', > - optional => 1, > - default => 0, > - }, > - remove_storages => { > - description => "Remove all pveceph-managed storages configured for this pool", > - type => 'boolean', > - optional => 1, > - default => 0, > - }, > - }, > - }, > - returns => { type => 'string' }, > - code => sub { > - my ($param) = @_; > - > - PVE::Ceph::Tools::check_ceph_inited(); > - > - my $rpcenv = PVE::RPCEnvironment::get(); > - my $user = $rpcenv->get_user(); > - $rpcenv->check($user, '/storage', ['Datastore.Allocate']) > - if $param->{remove_storages}; > - > - my $pool = $param->{name}; > - > - my $worker = sub { > - my $storages = $get_storages->($pool); > - > - # if not forced, destroy ceph pool only when no > - # vm disks are on it anymore > - if (!$param->{force}) { > - my $storagecfg = PVE::Storage::config(); > - foreach my $storeid (keys %$storages) { > - my $storage = $storages->{$storeid}; > - > - # check if any vm disks are on the pool > - print "checking storage '$storeid' for RBD images..\n"; > - my $res = PVE::Storage::vdisk_list($storagecfg, $storeid); > - die "ceph pool '$pool' still in use by storage '$storeid'\n" > - if @{$res->{$storeid}} != 0; > - } > - } > - > - PVE::Ceph::Tools::destroy_pool($pool); > - > - if ($param->{remove_storages}) { > - my $err; > - foreach my $storeid (keys %$storages) { > - # skip external clusters, not managed by pveceph > - next if $storages->{$storeid}->{monhost}; > - eval { PVE::API2::Storage::Config->delete({storage => $storeid}) }; > - if ($@) { > - warn "failed to remove storage '$storeid': $@\n"; > - $err = 1; > - } > - } > - die "failed to remove (some) storages - check log and remove manually!\n" > - if $err; > - } > - }; > - return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker); > - }}); > - > - > -__PACKAGE__->register_method ({ > - name => 'setpool', > - path => 'pools/{name}', > - method => 'PUT', > - description => "Change POOL settings", > - proxyto => 'node', > - protected => 1, > - permissions => { > - check => ['perm', '/', [ 'Sys.Modify' ]], > - }, > - parameters => { > - additionalProperties => 0, > - properties => { > - node => get_standard_option('pve-node'), > - %{ $ceph_pool_common_options->('nodefault') }, > - }, > - }, > - returns => { type => 'string' }, > - code => sub { > - my ($param) = @_; > - > - PVE::Ceph::Tools::check_ceph_configured(); > - > - 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 $worker = sub { > - PVE::Ceph::Tools::set_pool($pool, $ceph_param); > - }; > - > - return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker); > - }}); > - > > __PACKAGE__->register_method ({ > name => 'crush', > diff --git a/PVE/API2/Ceph/POOLS.pm b/PVE/API2/Ceph/POOLS.pm > new file mode 100644 > index 00000000..744f2bce > --- /dev/null > +++ b/PVE/API2/Ceph/POOLS.pm > @@ -0,0 +1,404 @@ > +package PVE::API2::Ceph::POOLS; > + > +use strict; > +use warnings; > + > +use PVE::Ceph::Tools; > +use PVE::Ceph::Services; > +use PVE::JSONSchema qw(get_standard_option); > +use PVE::RADOS; > +use PVE::RESTHandler; > +use PVE::RPCEnvironment; > +use PVE::Storage; > + > +use PVE::API2::Storage::Config; > + > +use base qw(PVE::RESTHandler); > + > +my $ceph_pool_common_options = sub { > + my ($nodefault) = shift; > + my $options = { > + name => { > + title => 'Name', > + description => "The name of the pool. It must be unique.", > + type => 'string', > + }, > + size => { > + description => 'Number of replicas per object', > + title => 'Size', > + type => 'integer', > + default => 3, > + optional => 1, > + minimum => 1, > + maximum => 7, > + }, > + min_size => { > + description => 'Minimum number of replicas per object', > + title => 'Min Size', > + type => 'integer', > + default => 2, > + optional => 1, > + minimum => 1, > + maximum => 7, > + }, > + pg_num => { > + description => "Number of placement groups.", > + title => 'PG Num', > + type => 'integer', > + default => 128, > + optional => 1, > + minimum => 8, > + maximum => 32768, > + }, > + crush_rule => { > + description => "The rule to use for mapping object placement in the cluster.", > + title => 'Crush Rule Name', > + type => 'string', > + optional => 1, > + }, > + application => { > + description => "The application of the pool.", > + title => 'Application', > + default => 'rbd', > + type => 'string', > + enum => ['rbd', 'cephfs', 'rgw'], > + optional => 1, > + }, > + pg_autoscale_mode => { > + description => "The automatic PG scaling mode of the pool.", > + title => 'PG Autoscale Mode', > + type => 'string', > + enum => ['on', 'off', 'warn'], > + default => 'warn', > + optional => 1, > + }, > + }; > + > + if ($nodefault) { > + delete $options->{$_}->{default} for keys %$options; > + } > + return $options; > +}; > + > +my $add_storage = sub { > + my ($pool, $storeid) = @_; > + > + my $storage_params = { > + type => 'rbd', > + pool => $pool, > + storage => $storeid, > + krbd => 0, > + content => 'rootdir,images', > + }; > + > + PVE::API2::Storage::Config->create($storage_params); > +}; > + > +my $get_storages = sub { > + my ($pool) = @_; > + > + my $cfg = PVE::Storage::config(); > + > + my $storages = $cfg->{ids}; > + my $res = {}; > + foreach my $storeid (keys %$storages) { > + my $curr = $storages->{$storeid}; > + $res->{$storeid} = $storages->{$storeid} > + if $curr->{type} eq 'rbd' && $pool eq $curr->{pool}; > + } > + > + return $res; > +}; > + > + > +__PACKAGE__->register_method ({ > + name => 'lspools', > + path => '', > + method => 'GET', > + description => "List all pools.", > + proxyto => 'node', > + protected => 1, > + permissions => { > + check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + }, > + }, > + returns => { > + type => 'array', > + 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' }, > + }, > + }, > + links => [ { rel => 'child', href => "{pool_name}" } ], > + }, > + code => sub { > + my ($param) = @_; > + > + PVE::Ceph::Tools::check_ceph_inited(); > + > + my $rados = PVE::RADOS->new(); > + > + my $stats = {}; > + my $res = $rados->mon_command({ prefix => 'df' }); > + > + foreach my $d (@{$res->{pools}}) { > + next if !$d->{stats}; > + next if !defined($d->{id}); > + $stats->{$d->{id}} = $d->{stats}; > + } > + > + $res = $rados->mon_command({ prefix => 'osd dump' }); > + my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'}); > + > + my $rules = {}; > + for my $rule (@$rulestmp) { > + $rules->{$rule->{rule_id}} = $rule->{rule_name}; > + } > + > + my $data = []; > + my $attr_list = [ > + 'pool', > + 'pool_name', > + 'size', > + 'min_size', > + 'pg_num', > + 'crush_rule', > + 'pg_autoscale_mode', > + ]; > + > + foreach my $e (@{$res->{pools}}) { > + my $d = {}; > + foreach my $attr (@$attr_list) { > + $d->{$attr} = $e->{$attr} if defined($e->{$attr}); > + } > + > + if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) { > + $d->{crush_rule_name} = $rules->{$d->{crush_rule}}; > + } > + > + if (my $s = $stats->{$d->{pool}}) { > + $d->{bytes_used} = $s->{bytes_used}; > + $d->{percent_used} = $s->{percent_used}; > + } > + push @$data, $d; > + } > + > + > + return $data; > + }}); > + > + > +# FIXME: use pools/{pool_name} with PVE 7.0 > +__PACKAGE__->register_method ({ > + name => 'createpool', > + path => '', > + method => 'POST', > + description => "Create POOL", > + proxyto => 'node', > + protected => 1, > + permissions => { > + check => ['perm', '/', [ 'Sys.Modify' ]], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + add_storages => { > + description => "Configure VM and CT storage using the new pool.", > + type => 'boolean', > + optional => 1, > + }, > + %{ $ceph_pool_common_options->() }, > + }, > + }, > + returns => { type => 'string' }, > + code => sub { > + my ($param) = @_; > + > + PVE::Cluster::check_cfs_quorum(); > + PVE::Ceph::Tools::check_ceph_configured(); > + > + my $pool = $param->{name}; > + my $rpcenv = PVE::RPCEnvironment::get(); > + my $user = $rpcenv->get_user(); > + > + if ($param->{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'; > + > + my $worker = sub { > + > + PVE::Ceph::Tools::create_pool($pool, $ceph_param); > + > + if ($param->{add_storages}) { > + my $err; > + eval { $add_storage->($pool, "${pool}"); }; > + if ($@) { > + warn "failed to add storage: $@"; > + $err = 1; > + } > + die "adding storage for pool '$pool' failed, check log and add manually!\n" > + if $err; > + } > + }; > + > + return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker); > + }}); > + > + > +__PACKAGE__->register_method ({ > + name => 'destroypool', > + path => '{name}', > + method => 'DELETE', > + description => "Destroy pool", > + proxyto => 'node', > + protected => 1, > + permissions => { > + check => ['perm', '/', [ 'Sys.Modify' ]], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + name => { > + description => "The name of the pool. It must be unique.", > + type => 'string', > + }, > + force => { > + description => "If true, destroys pool even if in use", > + type => 'boolean', > + optional => 1, > + default => 0, > + }, > + remove_storages => { > + description => "Remove all pveceph-managed storages configured for this pool", > + type => 'boolean', > + optional => 1, > + default => 0, > + }, > + }, > + }, > + returns => { type => 'string' }, > + code => sub { > + my ($param) = @_; > + > + PVE::Ceph::Tools::check_ceph_inited(); > + > + my $rpcenv = PVE::RPCEnvironment::get(); > + my $user = $rpcenv->get_user(); > + $rpcenv->check($user, '/storage', ['Datastore.Allocate']) > + if $param->{remove_storages}; > + > + my $pool = $param->{name}; > + > + my $worker = sub { > + my $storages = $get_storages->($pool); > + > + # if not forced, destroy ceph pool only when no > + # vm disks are on it anymore > + if (!$param->{force}) { > + my $storagecfg = PVE::Storage::config(); > + foreach my $storeid (keys %$storages) { > + my $storage = $storages->{$storeid}; > + > + # check if any vm disks are on the pool > + print "checking storage '$storeid' for RBD images..\n"; > + my $res = PVE::Storage::vdisk_list($storagecfg, $storeid); > + die "ceph pool '$pool' still in use by storage '$storeid'\n" > + if @{$res->{$storeid}} != 0; > + } > + } > + > + PVE::Ceph::Tools::destroy_pool($pool); > + > + if ($param->{remove_storages}) { > + my $err; > + foreach my $storeid (keys %$storages) { > + # skip external clusters, not managed by pveceph > + next if $storages->{$storeid}->{monhost}; > + eval { PVE::API2::Storage::Config->delete({storage => $storeid}) }; > + if ($@) { > + warn "failed to remove storage '$storeid': $@\n"; > + $err = 1; > + } > + } > + die "failed to remove (some) storages - check log and remove manually!\n" > + if $err; > + } > + }; > + return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker); > + }}); > + > + > +__PACKAGE__->register_method ({ > + name => 'setpool', > + path => '{name}', > + method => 'PUT', > + description => "Change POOL settings", > + proxyto => 'node', > + protected => 1, > + permissions => { > + check => ['perm', '/', [ 'Sys.Modify' ]], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + %{ $ceph_pool_common_options->('nodefault') }, > + }, > + }, > + returns => { type => 'string' }, > + code => sub { > + my ($param) = @_; > + > + PVE::Ceph::Tools::check_ceph_configured(); > + > + 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 $worker = sub { > + PVE::Ceph::Tools::set_pool($pool, $ceph_param); > + }; > + > + return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker); > + }}); > + > + > +1; > diff --git a/PVE/CLI/pveceph.pm b/PVE/CLI/pveceph.pm > index 3d7bf2b1..69421ca6 100755 > --- a/PVE/CLI/pveceph.pm > +++ b/PVE/CLI/pveceph.pm > @@ -21,6 +21,7 @@ use PVE::Ceph::Tools; > use PVE::Ceph::Services; > use PVE::API2::Ceph; > use PVE::API2::Ceph::FS; > +use PVE::API2::Ceph::POOLS; > use PVE::API2::Ceph::MDS; > use PVE::API2::Ceph::MGR; > use PVE::API2::Ceph::MON; > @@ -178,7 +179,7 @@ __PACKAGE__->register_method ({ > our $cmddef = { > init => [ 'PVE::API2::Ceph', 'init', [], { node => $nodename } ], > pool => { > - ls => [ 'PVE::API2::Ceph', 'lspools', [], { node => $nodename }, sub { > + ls => [ 'PVE::API2::Ceph::POOLS', 'lspools', [], { node => $nodename }, sub { > my ($data, $schema, $options) = @_; > PVE::CLIFormatter::print_api_result($data, $schema, > [ > @@ -193,9 +194,9 @@ our $cmddef = { > ], > $options); > }, $PVE::RESTHandler::standard_output_options], > - create => [ 'PVE::API2::Ceph', 'createpool', ['name'], { node => $nodename }], > - destroy => [ 'PVE::API2::Ceph', 'destroypool', ['name'], { node => $nodename } ], > - set => [ 'PVE::API2::Ceph', 'setpool', ['name'], { node => $nodename } ], > + create => [ 'PVE::API2::Ceph::POOLS', 'createpool', ['name'], { node => $nodename }], > + destroy => [ 'PVE::API2::Ceph::POOLS', 'destroypool', ['name'], { node => $nodename } ], > + set => [ 'PVE::API2::Ceph::POOLS', 'setpool', ['name'], { node => $nodename } ], > }, > lspools => { alias => 'pool ls' }, > createpool => { alias => 'pool create' }, >