]>
Commit | Line | Data |
---|---|---|
56d02a86 AA |
1 | package PVE::API2::Ceph::Pools; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::Ceph::Tools; | |
7 | use PVE::Ceph::Services; | |
f35e7fcd | 8 | use PVE::JSONSchema qw(get_standard_option parse_property_string); |
56d02a86 AA |
9 | use PVE::RADOS; |
10 | use PVE::RESTHandler; | |
11 | use PVE::RPCEnvironment; | |
12 | use PVE::Storage; | |
13 | use PVE::Tools qw(extract_param); | |
14 | ||
15 | use PVE::API2::Storage::Config; | |
16 | ||
17 | use base qw(PVE::RESTHandler); | |
18 | ||
5a3d7942 AA |
19 | my $get_autoscale_status = sub { |
20 | my ($rados) = shift; | |
21 | ||
22 | $rados = PVE::RADOS->new() if !defined($rados); | |
23 | ||
24 | my $autoscale = $rados->mon_command({ | |
25 | prefix => 'osd pool autoscale-status'}); | |
26 | ||
27 | my $data; | |
28 | foreach my $p (@$autoscale) { | |
5a3d7942 AA |
29 | $data->{$p->{pool_name}} = $p; |
30 | } | |
31 | ||
32 | return $data; | |
33 | }; | |
34 | ||
35 | ||
56d02a86 AA |
36 | __PACKAGE__->register_method ({ |
37 | name => 'lspools', | |
38 | path => '', | |
39 | method => 'GET', | |
40 | description => "List all pools.", | |
41 | proxyto => 'node', | |
42 | protected => 1, | |
43 | permissions => { | |
44 | check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1], | |
45 | }, | |
46 | parameters => { | |
47 | additionalProperties => 0, | |
48 | properties => { | |
49 | node => get_standard_option('pve-node'), | |
50 | }, | |
51 | }, | |
52 | returns => { | |
53 | type => 'array', | |
54 | items => { | |
55 | type => "object", | |
56 | properties => { | |
84b08e8a TL |
57 | pool => { |
58 | type => 'integer', | |
59 | title => 'ID', | |
60 | }, | |
61 | pool_name => { | |
62 | type => 'string', | |
63 | title => 'Name', | |
64 | }, | |
65 | size => { | |
66 | type => 'integer', | |
67 | title => 'Size', | |
68 | }, | |
fca1900c AL |
69 | type => { |
70 | type => 'string', | |
71 | title => 'Type', | |
72 | enum => ['replicated', 'erasure', 'unknown'], | |
73 | }, | |
84b08e8a TL |
74 | min_size => { |
75 | type => 'integer', | |
76 | title => 'Min Size', | |
77 | }, | |
78 | pg_num => { | |
79 | type => 'integer', | |
80 | title => 'PG Num', | |
81 | }, | |
82 | pg_num_min => { | |
83 | type => 'integer', | |
84 | title => 'min. PG Num', | |
85 | optional => 1, | |
86 | }, | |
87 | pg_num_final => { | |
88 | type => 'integer', | |
89 | title => 'Optimal PG Num', | |
90 | optional => 1, | |
91 | }, | |
92 | pg_autoscale_mode => { | |
93 | type => 'string', | |
94 | title => 'PG Autoscale Mode', | |
95 | optional => 1, | |
96 | }, | |
97 | crush_rule => { | |
98 | type => 'integer', | |
99 | title => 'Crush Rule', | |
100 | }, | |
101 | crush_rule_name => { | |
102 | type => 'string', | |
103 | title => 'Crush Rule Name', | |
104 | }, | |
105 | percent_used => { | |
106 | type => 'number', | |
107 | title => '%-Used', | |
108 | }, | |
109 | bytes_used => { | |
110 | type => 'integer', | |
111 | title => 'Used', | |
112 | }, | |
113 | target_size => { | |
114 | type => 'integer', | |
115 | title => 'PG Autoscale Target Size', | |
116 | optional => 1, | |
117 | }, | |
118 | target_size_ratio => { | |
119 | type => 'number', | |
120 | title => 'PG Autoscale Target Ratio', | |
121 | optional => 1, | |
122 | }, | |
123 | autoscale_status => { | |
124 | type => 'object', | |
125 | title => 'Autoscale Status', | |
126 | optional => 1, | |
127 | }, | |
56d02a86 AA |
128 | }, |
129 | }, | |
130 | links => [ { rel => 'child', href => "{pool_name}" } ], | |
131 | }, | |
132 | code => sub { | |
133 | my ($param) = @_; | |
134 | ||
135 | PVE::Ceph::Tools::check_ceph_inited(); | |
136 | ||
137 | my $rados = PVE::RADOS->new(); | |
138 | ||
139 | my $stats = {}; | |
140 | my $res = $rados->mon_command({ prefix => 'df' }); | |
141 | ||
142 | foreach my $d (@{$res->{pools}}) { | |
143 | next if !$d->{stats}; | |
144 | next if !defined($d->{id}); | |
145 | $stats->{$d->{id}} = $d->{stats}; | |
146 | } | |
147 | ||
148 | $res = $rados->mon_command({ prefix => 'osd dump' }); | |
149 | my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'}); | |
150 | ||
151 | my $rules = {}; | |
152 | for my $rule (@$rulestmp) { | |
153 | $rules->{$rule->{rule_id}} = $rule->{rule_name}; | |
154 | } | |
155 | ||
156 | my $data = []; | |
157 | my $attr_list = [ | |
158 | 'pool', | |
159 | 'pool_name', | |
160 | 'size', | |
161 | 'min_size', | |
162 | 'pg_num', | |
163 | 'crush_rule', | |
164 | 'pg_autoscale_mode', | |
165 | ]; | |
166 | ||
5a3d7942 AA |
167 | # pg_autoscaler module is not enabled in Nautilus |
168 | my $autoscale = eval { $get_autoscale_status->($rados) }; | |
169 | ||
56d02a86 AA |
170 | foreach my $e (@{$res->{pools}}) { |
171 | my $d = {}; | |
172 | foreach my $attr (@$attr_list) { | |
173 | $d->{$attr} = $e->{$attr} if defined($e->{$attr}); | |
174 | } | |
175 | ||
5a3d7942 AA |
176 | if ($autoscale) { |
177 | $d->{autoscale_status} = $autoscale->{$d->{pool_name}}; | |
178 | $d->{pg_num_final} = $d->{autoscale_status}->{pg_num_final}; | |
179 | # some info is nested under options instead | |
180 | $d->{pg_num_min} = $e->{options}->{pg_num_min}; | |
181 | $d->{target_size} = $e->{options}->{target_size_bytes}; | |
182 | $d->{target_size_ratio} = $e->{options}->{target_size_ratio}; | |
183 | } | |
184 | ||
56d02a86 AA |
185 | if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) { |
186 | $d->{crush_rule_name} = $rules->{$d->{crush_rule}}; | |
187 | } | |
188 | ||
189 | if (my $s = $stats->{$d->{pool}}) { | |
190 | $d->{bytes_used} = $s->{bytes_used}; | |
191 | $d->{percent_used} = $s->{percent_used}; | |
192 | } | |
fca1900c AL |
193 | |
194 | # Cephs numerical pool types are barely documented. Found the following in the Ceph | |
195 | # codebase: https://github.com/ceph/ceph/blob/ff144995a849407c258bcb763daa3e03cfce5059/src/osd/osd_types.h#L1221-L1233 | |
196 | if ($e->{type} == 1) { | |
197 | $d->{type} = 'replicated'; | |
198 | } elsif ($e->{type} == 3) { | |
199 | $d->{type} = 'erasure'; | |
200 | } else { | |
201 | # we should never get here, but better be safe | |
202 | $d->{type} = 'unknown'; | |
203 | } | |
56d02a86 AA |
204 | push @$data, $d; |
205 | } | |
206 | ||
207 | ||
208 | return $data; | |
209 | }}); | |
210 | ||
211 | ||
212 | my $ceph_pool_common_options = sub { | |
213 | my ($nodefault) = shift; | |
214 | my $options = { | |
215 | name => { | |
461e2141 | 216 | title => 'Name', |
56d02a86 AA |
217 | description => "The name of the pool. It must be unique.", |
218 | type => 'string', | |
219 | }, | |
220 | size => { | |
461e2141 | 221 | title => 'Size', |
56d02a86 AA |
222 | description => 'Number of replicas per object', |
223 | type => 'integer', | |
224 | default => 3, | |
225 | optional => 1, | |
226 | minimum => 1, | |
227 | maximum => 7, | |
228 | }, | |
229 | min_size => { | |
461e2141 | 230 | title => 'Min Size', |
56d02a86 AA |
231 | description => 'Minimum number of replicas per object', |
232 | type => 'integer', | |
233 | default => 2, | |
234 | optional => 1, | |
235 | minimum => 1, | |
236 | maximum => 7, | |
237 | }, | |
238 | pg_num => { | |
461e2141 | 239 | title => 'PG Num', |
56d02a86 AA |
240 | description => "Number of placement groups.", |
241 | type => 'integer', | |
242 | default => 128, | |
243 | optional => 1, | |
6b36f368 | 244 | minimum => 1, |
56d02a86 AA |
245 | maximum => 32768, |
246 | }, | |
5a3d7942 AA |
247 | pg_num_min => { |
248 | title => 'min. PG Num', | |
249 | description => "Minimal number of placement groups.", | |
250 | type => 'integer', | |
251 | optional => 1, | |
252 | maximum => 32768, | |
253 | }, | |
56d02a86 | 254 | crush_rule => { |
461e2141 | 255 | title => 'Crush Rule Name', |
56d02a86 AA |
256 | description => "The rule to use for mapping object placement in the cluster.", |
257 | type => 'string', | |
258 | optional => 1, | |
259 | }, | |
260 | application => { | |
461e2141 | 261 | title => 'Application', |
56d02a86 AA |
262 | description => "The application of the pool.", |
263 | default => 'rbd', | |
264 | type => 'string', | |
265 | enum => ['rbd', 'cephfs', 'rgw'], | |
266 | optional => 1, | |
267 | }, | |
268 | pg_autoscale_mode => { | |
461e2141 | 269 | title => 'PG Autoscale Mode', |
56d02a86 AA |
270 | description => "The automatic PG scaling mode of the pool.", |
271 | type => 'string', | |
272 | enum => ['on', 'off', 'warn'], | |
273 | default => 'warn', | |
274 | optional => 1, | |
275 | }, | |
5a3d7942 AA |
276 | target_size => { |
277 | description => "The estimated target size of the pool for the PG autoscaler.", | |
278 | title => 'PG Autoscale Target Size', | |
279 | type => 'string', | |
280 | pattern => '^(\d+(\.\d+)?)([KMGT])?$', | |
281 | optional => 1, | |
282 | }, | |
283 | target_size_ratio => { | |
284 | description => "The estimated target ratio of the pool for the PG autoscaler.", | |
285 | title => 'PG Autoscale Target Ratio', | |
286 | type => 'number', | |
287 | optional => 1, | |
288 | }, | |
56d02a86 AA |
289 | }; |
290 | ||
291 | if ($nodefault) { | |
292 | delete $options->{$_}->{default} for keys %$options; | |
293 | } | |
294 | return $options; | |
295 | }; | |
296 | ||
297 | ||
298 | my $add_storage = sub { | |
f35e7fcd | 299 | my ($pool, $storeid, $ec_data_pool) = @_; |
56d02a86 AA |
300 | |
301 | my $storage_params = { | |
302 | type => 'rbd', | |
303 | pool => $pool, | |
304 | storage => $storeid, | |
305 | krbd => 0, | |
306 | content => 'rootdir,images', | |
307 | }; | |
308 | ||
f35e7fcd | 309 | $storage_params->{'data-pool'} = $ec_data_pool if $ec_data_pool; |
3bd128d7 | 310 | |
56d02a86 AA |
311 | PVE::API2::Storage::Config->create($storage_params); |
312 | }; | |
313 | ||
314 | my $get_storages = sub { | |
315 | my ($pool) = @_; | |
316 | ||
317 | my $cfg = PVE::Storage::config(); | |
318 | ||
319 | my $storages = $cfg->{ids}; | |
320 | my $res = {}; | |
321 | foreach my $storeid (keys %$storages) { | |
322 | my $curr = $storages->{$storeid}; | |
29fe1eea AL |
323 | next if $curr->{type} ne 'rbd'; |
324 | if ( | |
325 | $pool eq $curr->{pool} || | |
326 | (defined $curr->{'data-pool'} && $pool eq $curr->{'data-pool'}) | |
327 | ) { | |
328 | $res->{$storeid} = $storages->{$storeid}; | |
329 | } | |
56d02a86 AA |
330 | } |
331 | ||
332 | return $res; | |
333 | }; | |
334 | ||
f35e7fcd TL |
335 | my $ec_format = { |
336 | k => { | |
337 | type => 'integer', | |
338 | description => "Number of data chunks. Will create an erasure coded pool plus a" | |
339 | ." replicated pool for metadata.", | |
136f761b | 340 | minimum => 2, |
f35e7fcd TL |
341 | }, |
342 | m => { | |
343 | type => 'integer', | |
344 | description => "Number of coding chunks. Will create an erasure coded pool plus a" | |
345 | ." replicated pool for metadata.", | |
346 | minimum => 1, | |
347 | }, | |
348 | 'failure-domain' => { | |
349 | type => 'string', | |
350 | description => "CRUSH failure domain. Default is 'host'. Will create an erasure" | |
351 | ." coded pool plus a replicated pool for metadata.", | |
352 | format_description => 'domain', | |
353 | optional => 1, | |
136f761b | 354 | default => 'host', |
f35e7fcd TL |
355 | }, |
356 | 'device-class' => { | |
357 | type => 'string', | |
358 | description => "CRUSH device class. Will create an erasure coded pool plus a" | |
359 | ." replicated pool for metadata.", | |
360 | format_description => 'class', | |
361 | optional => 1, | |
362 | }, | |
363 | profile => { | |
364 | description => "Override the erasure code (EC) profile to use. Will create an" | |
365 | ." erasure coded pool plus a replicated pool for metadata.", | |
366 | type => 'string', | |
367 | format_description => 'profile', | |
368 | optional => 1, | |
369 | }, | |
370 | }; | |
371 | ||
372 | sub ec_parse_and_check { | |
373 | my ($property, $rados) = @_; | |
374 | return if !$property; | |
375 | ||
376 | my $ec = parse_property_string($ec_format, $property); | |
377 | ||
378 | die "Erasure code profile '$ec->{profile}' does not exist.\n" | |
379 | if $ec->{profile} && !PVE::Ceph::Tools::ecprofile_exists($ec->{profile}, $rados); | |
380 | ||
381 | return $ec; | |
382 | } | |
383 | ||
56d02a86 AA |
384 | |
385 | __PACKAGE__->register_method ({ | |
386 | name => 'createpool', | |
387 | path => '', | |
388 | method => 'POST', | |
f35e7fcd | 389 | description => "Create Ceph pool", |
56d02a86 AA |
390 | proxyto => 'node', |
391 | protected => 1, | |
392 | permissions => { | |
393 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
394 | }, | |
395 | parameters => { | |
396 | additionalProperties => 0, | |
397 | properties => { | |
398 | node => get_standard_option('pve-node'), | |
399 | add_storages => { | |
4605d6fd | 400 | description => "Configure VM and CT storage using the new pool.", |
56d02a86 AA |
401 | type => 'boolean', |
402 | optional => 1, | |
4605d6fd | 403 | default => "0; for erasure coded pools: 1", |
56d02a86 | 404 | }, |
f35e7fcd | 405 | 'erasure-coding' => { |
ec63b237 TL |
406 | description => "Create an erasure coded pool for RBD with an" |
407 | ." accompaning replicated pool for metadata storage.", | |
3bd128d7 | 408 | type => 'string', |
f35e7fcd | 409 | format => $ec_format, |
3bd128d7 AL |
410 | optional => 1, |
411 | }, | |
56d02a86 AA |
412 | %{ $ceph_pool_common_options->() }, |
413 | }, | |
414 | }, | |
415 | returns => { type => 'string' }, | |
416 | code => sub { | |
417 | my ($param) = @_; | |
418 | ||
419 | PVE::Cluster::check_cfs_quorum(); | |
420 | PVE::Ceph::Tools::check_ceph_configured(); | |
421 | ||
3bd128d7 | 422 | my $pool = my $name = extract_param($param, 'name'); |
56d02a86 AA |
423 | my $node = extract_param($param, 'node'); |
424 | my $add_storages = extract_param($param, 'add_storages'); | |
425 | ||
426 | my $rpcenv = PVE::RPCEnvironment::get(); | |
427 | my $user = $rpcenv->get_user(); | |
5a3d7942 AA |
428 | # Ceph uses target_size_bytes |
429 | if (defined($param->{'target_size'})) { | |
430 | my $target_sizestr = extract_param($param, 'target_size'); | |
431 | $param->{target_size_bytes} = PVE::JSONSchema::parse_size($target_sizestr); | |
432 | } | |
433 | ||
07316e6d TL |
434 | my $rados = PVE::RADOS->new(); |
435 | my $ec = ec_parse_and_check(extract_param($param, 'erasure-coding'), $rados); | |
436 | $add_storages = 1 if $ec && !defined($add_storages); | |
437 | ||
56d02a86 AA |
438 | if ($add_storages) { |
439 | $rpcenv->check($user, '/storage', ['Datastore.Allocate']); | |
440 | die "pool name contains characters which are illegal for storage naming\n" | |
441 | if !PVE::JSONSchema::parse_storage_id($pool); | |
442 | } | |
443 | ||
444 | # pool defaults | |
445 | $param->{pg_num} //= 128; | |
446 | $param->{size} //= 3; | |
447 | $param->{min_size} //= 2; | |
448 | $param->{application} //= 'rbd'; | |
449 | $param->{pg_autoscale_mode} //= 'warn'; | |
450 | ||
f35e7fcd TL |
451 | my $worker = sub { |
452 | # reopen with longer timeout | |
453 | $rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout')); | |
454 | ||
455 | if ($ec) { | |
456 | if (!$ec->{profile}) { | |
457 | $ec->{profile} = PVE::Ceph::Tools::get_ecprofile_name($pool, $rados); | |
458 | eval { | |
459 | PVE::Ceph::Tools::create_ecprofile( | |
460 | $ec->@{'profile', 'k', 'm', 'failure-domain', 'device-class'}, | |
461 | $rados, | |
462 | ); | |
463 | }; | |
464 | die "could not create erasure code profile '$ec->{profile}': $@\n" if $@; | |
465 | print "created new erasure code profile '$ec->{profile}'\n"; | |
466 | } | |
3bd128d7 | 467 | |
f35e7fcd TL |
468 | my $ec_data_param = {}; |
469 | # copy all params, should be a flat hash | |
470 | $ec_data_param = { map { $_ => $param->{$_} } keys %$param }; | |
3bd128d7 | 471 | |
f35e7fcd TL |
472 | $ec_data_param->{pool_type} = 'erasure'; |
473 | $ec_data_param->{allow_ec_overwrites} = 'true'; | |
474 | $ec_data_param->{erasure_code_profile} = $ec->{profile}; | |
475 | delete $ec_data_param->{size}; | |
476 | delete $ec_data_param->{min_size}; | |
3bd128d7 | 477 | |
f35e7fcd TL |
478 | # metadata pool should be ok with 32 PGs |
479 | $param->{pg_num} = 32; | |
480 | ||
481 | $pool = "${name}-metadata"; | |
482 | $ec->{data_pool} = "${name}-data"; | |
483 | ||
484 | PVE::Ceph::Tools::create_pool($ec->{data_pool}, $ec_data_param, $rados); | |
485 | } | |
56d02a86 | 486 | |
f35e7fcd | 487 | PVE::Ceph::Tools::create_pool($pool, $param, $rados); |
3bd128d7 | 488 | |
56d02a86 | 489 | if ($add_storages) { |
f35e7fcd | 490 | eval { $add_storage->($pool, "${name}", $ec->{data_pool}) }; |
3bd128d7 | 491 | die "adding PVE storage for ceph pool '$name' failed: $@\n" if $@; |
56d02a86 AA |
492 | } |
493 | }; | |
494 | ||
495 | return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker); | |
496 | }}); | |
497 | ||
498 | ||
499 | __PACKAGE__->register_method ({ | |
500 | name => 'destroypool', | |
501 | path => '{name}', | |
502 | method => 'DELETE', | |
503 | description => "Destroy pool", | |
504 | proxyto => 'node', | |
505 | protected => 1, | |
506 | permissions => { | |
507 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
508 | }, | |
509 | parameters => { | |
510 | additionalProperties => 0, | |
511 | properties => { | |
512 | node => get_standard_option('pve-node'), | |
513 | name => { | |
514 | description => "The name of the pool. It must be unique.", | |
515 | type => 'string', | |
516 | }, | |
517 | force => { | |
518 | description => "If true, destroys pool even if in use", | |
519 | type => 'boolean', | |
520 | optional => 1, | |
521 | default => 0, | |
522 | }, | |
523 | remove_storages => { | |
524 | description => "Remove all pveceph-managed storages configured for this pool", | |
525 | type => 'boolean', | |
526 | optional => 1, | |
527 | default => 0, | |
528 | }, | |
3bd128d7 | 529 | remove_ecprofile => { |
f35e7fcd | 530 | description => "Remove the erasure code profile. Defaults to true, if applicable.", |
3bd128d7 AL |
531 | type => 'boolean', |
532 | optional => 1, | |
533 | default => 1, | |
534 | }, | |
56d02a86 AA |
535 | }, |
536 | }, | |
537 | returns => { type => 'string' }, | |
538 | code => sub { | |
539 | my ($param) = @_; | |
540 | ||
541 | PVE::Ceph::Tools::check_ceph_inited(); | |
542 | ||
543 | my $rpcenv = PVE::RPCEnvironment::get(); | |
544 | my $user = $rpcenv->get_user(); | |
545 | $rpcenv->check($user, '/storage', ['Datastore.Allocate']) | |
546 | if $param->{remove_storages}; | |
547 | ||
548 | my $pool = $param->{name}; | |
549 | ||
550 | my $worker = sub { | |
551 | my $storages = $get_storages->($pool); | |
552 | ||
553 | # if not forced, destroy ceph pool only when no | |
554 | # vm disks are on it anymore | |
555 | if (!$param->{force}) { | |
556 | my $storagecfg = PVE::Storage::config(); | |
557 | foreach my $storeid (keys %$storages) { | |
558 | my $storage = $storages->{$storeid}; | |
559 | ||
560 | # check if any vm disks are on the pool | |
561 | print "checking storage '$storeid' for RBD images..\n"; | |
562 | my $res = PVE::Storage::vdisk_list($storagecfg, $storeid); | |
563 | die "ceph pool '$pool' still in use by storage '$storeid'\n" | |
564 | if @{$res->{$storeid}} != 0; | |
565 | } | |
566 | } | |
f35e7fcd | 567 | my $rados = PVE::RADOS->new(); |
56d02a86 | 568 | |
f35e7fcd | 569 | my $pool_properties = PVE::Ceph::Tools::get_pool_properties($pool, $rados); |
3bd128d7 | 570 | |
f35e7fcd | 571 | PVE::Ceph::Tools::destroy_pool($pool, $rados); |
56d02a86 | 572 | |
3bd128d7 | 573 | if (my $ecprofile = $pool_properties->{erasure_code_profile}) { |
f35e7fcd | 574 | print "found erasure coded profile '$ecprofile', destroying its CRUSH rule\n"; |
3bd128d7 | 575 | my $crush_rule = $pool_properties->{crush_rule}; |
f35e7fcd | 576 | eval { PVE::Ceph::Tools::destroy_crush_rule($crush_rule, $rados); }; |
3bd128d7 AL |
577 | warn "removing crush rule '${crush_rule}' failed: $@\n" if $@; |
578 | ||
f35e7fcd TL |
579 | if ($param->{remove_ecprofile} // 1) { |
580 | print "destroying erasure coded profile '$ecprofile'\n"; | |
581 | eval { PVE::Ceph::Tools::destroy_ecprofile($ecprofile, $rados) }; | |
3bd128d7 AL |
582 | warn "removing EC profile '${ecprofile}' failed: $@\n" if $@; |
583 | } | |
584 | } | |
585 | ||
56d02a86 AA |
586 | if ($param->{remove_storages}) { |
587 | my $err; | |
588 | foreach my $storeid (keys %$storages) { | |
589 | # skip external clusters, not managed by pveceph | |
590 | next if $storages->{$storeid}->{monhost}; | |
591 | eval { PVE::API2::Storage::Config->delete({storage => $storeid}) }; | |
592 | if ($@) { | |
593 | warn "failed to remove storage '$storeid': $@\n"; | |
594 | $err = 1; | |
595 | } | |
596 | } | |
597 | die "failed to remove (some) storages - check log and remove manually!\n" | |
598 | if $err; | |
599 | } | |
600 | }; | |
601 | return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker); | |
602 | }}); | |
603 | ||
604 | ||
605 | __PACKAGE__->register_method ({ | |
606 | name => 'setpool', | |
607 | path => '{name}', | |
608 | method => 'PUT', | |
609 | description => "Change POOL settings", | |
610 | proxyto => 'node', | |
611 | protected => 1, | |
612 | permissions => { | |
613 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
614 | }, | |
615 | parameters => { | |
616 | additionalProperties => 0, | |
617 | properties => { | |
618 | node => get_standard_option('pve-node'), | |
619 | %{ $ceph_pool_common_options->('nodefault') }, | |
620 | }, | |
621 | }, | |
622 | returns => { type => 'string' }, | |
623 | code => sub { | |
624 | my ($param) = @_; | |
625 | ||
626 | PVE::Ceph::Tools::check_ceph_configured(); | |
627 | ||
628 | my $rpcenv = PVE::RPCEnvironment::get(); | |
629 | my $authuser = $rpcenv->get_user(); | |
630 | ||
51d6db58 AA |
631 | my $pool = extract_param($param, 'name'); |
632 | my $node = extract_param($param, 'node'); | |
56d02a86 | 633 | |
5a3d7942 AA |
634 | # Ceph uses target_size_bytes |
635 | if (defined($param->{'target_size'})) { | |
636 | my $target_sizestr = extract_param($param, 'target_size'); | |
637 | $param->{target_size_bytes} = PVE::JSONSchema::parse_size($target_sizestr); | |
638 | } | |
639 | ||
56d02a86 | 640 | my $worker = sub { |
51d6db58 | 641 | PVE::Ceph::Tools::set_pool($pool, $param); |
56d02a86 AA |
642 | }; |
643 | ||
644 | return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker); | |
645 | }}); | |
646 | ||
647 | ||
54ba7dd9 AA |
648 | __PACKAGE__->register_method ({ |
649 | name => 'getpool', | |
650 | path => '{name}', | |
651 | method => 'GET', | |
652 | description => "List pool settings.", | |
653 | proxyto => 'node', | |
654 | protected => 1, | |
655 | permissions => { | |
656 | check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1], | |
657 | }, | |
658 | parameters => { | |
659 | additionalProperties => 0, | |
660 | properties => { | |
661 | node => get_standard_option('pve-node'), | |
662 | name => { | |
663 | description => "The name of the pool. It must be unique.", | |
664 | type => 'string', | |
665 | }, | |
666 | verbose => { | |
667 | type => 'boolean', | |
668 | default => 0, | |
669 | optional => 1, | |
670 | description => "If enabled, will display additional data". | |
671 | "(eg. statistics).", | |
672 | }, | |
673 | }, | |
674 | }, | |
675 | returns => { | |
676 | type => "object", | |
677 | properties => { | |
678 | id => { type => 'integer', title => 'ID' }, | |
679 | pgp_num => { type => 'integer', title => 'PGP num' }, | |
680 | noscrub => { type => 'boolean', title => 'noscrub' }, | |
681 | 'nodeep-scrub' => { type => 'boolean', title => 'nodeep-scrub' }, | |
682 | nodelete => { type => 'boolean', title => 'nodelete' }, | |
683 | nopgchange => { type => 'boolean', title => 'nopgchange' }, | |
684 | nosizechange => { type => 'boolean', title => 'nosizechange' }, | |
685 | write_fadvise_dontneed => { type => 'boolean', title => 'write_fadvise_dontneed' }, | |
686 | hashpspool => { type => 'boolean', title => 'hashpspool' }, | |
687 | use_gmt_hitset => { type => 'boolean', title => 'use_gmt_hitset' }, | |
688 | fast_read => { type => 'boolean', title => 'Fast Read' }, | |
689 | application_list => { type => 'array', title => 'Application', optional => 1 }, | |
690 | statistics => { type => 'object', title => 'Statistics', optional => 1 }, | |
5a3d7942 | 691 | autoscale_status => { type => 'object', title => 'Autoscale Status', optional => 1 }, |
54ba7dd9 AA |
692 | %{ $ceph_pool_common_options->() }, |
693 | }, | |
694 | }, | |
695 | code => sub { | |
696 | my ($param) = @_; | |
697 | ||
698 | PVE::Ceph::Tools::check_ceph_inited(); | |
699 | ||
700 | my $verbose = $param->{verbose}; | |
701 | my $pool = $param->{name}; | |
702 | ||
703 | my $rados = PVE::RADOS->new(); | |
704 | my $res = $rados->mon_command({ | |
705 | prefix => 'osd pool get', | |
706 | pool => "$pool", | |
707 | var => 'all', | |
708 | }); | |
709 | ||
710 | my $data = { | |
711 | id => $res->{pool_id}, | |
712 | name => $pool, | |
713 | size => $res->{size}, | |
714 | min_size => $res->{min_size}, | |
715 | pg_num => $res->{pg_num}, | |
5a3d7942 | 716 | pg_num_min => $res->{pg_num_min}, |
54ba7dd9 AA |
717 | pgp_num => $res->{pgp_num}, |
718 | crush_rule => $res->{crush_rule}, | |
719 | pg_autoscale_mode => $res->{pg_autoscale_mode}, | |
720 | noscrub => "$res->{noscrub}", | |
721 | 'nodeep-scrub' => "$res->{'nodeep-scrub'}", | |
722 | nodelete => "$res->{nodelete}", | |
723 | nopgchange => "$res->{nopgchange}", | |
724 | nosizechange => "$res->{nosizechange}", | |
725 | write_fadvise_dontneed => "$res->{write_fadvise_dontneed}", | |
726 | hashpspool => "$res->{hashpspool}", | |
727 | use_gmt_hitset => "$res->{use_gmt_hitset}", | |
728 | fast_read => "$res->{fast_read}", | |
5a3d7942 AA |
729 | target_size => $res->{target_size_bytes}, |
730 | target_size_ratio => $res->{target_size_ratio}, | |
54ba7dd9 AA |
731 | }; |
732 | ||
733 | if ($verbose) { | |
734 | my $stats; | |
735 | my $res = $rados->mon_command({ prefix => 'df' }); | |
736 | ||
5a3d7942 AA |
737 | # pg_autoscaler module is not enabled in Nautilus |
738 | # avoid partial read further down, use new rados instance | |
739 | my $autoscale_status = eval { $get_autoscale_status->() }; | |
740 | $data->{autoscale_status} = $autoscale_status->{$pool}; | |
741 | ||
54ba7dd9 AA |
742 | foreach my $d (@{$res->{pools}}) { |
743 | next if !$d->{stats}; | |
744 | next if !defined($d->{name}) && !$d->{name} ne "$pool"; | |
745 | $data->{statistics} = $d->{stats}; | |
746 | } | |
747 | ||
748 | my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", }); | |
749 | $data->{application_list} = [ keys %$apps ]; | |
750 | } | |
751 | ||
752 | return $data; | |
753 | }}); | |
754 | ||
755 | ||
56d02a86 | 756 | 1; |