]>
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 | 323 | next if $curr->{type} ne 'rbd'; |
655080ee | 324 | $curr->{pool} = 'rbd' if !defined $curr->{pool}; # set default |
29fe1eea AL |
325 | if ( |
326 | $pool eq $curr->{pool} || | |
327 | (defined $curr->{'data-pool'} && $pool eq $curr->{'data-pool'}) | |
328 | ) { | |
329 | $res->{$storeid} = $storages->{$storeid}; | |
330 | } | |
56d02a86 AA |
331 | } |
332 | ||
333 | return $res; | |
334 | }; | |
335 | ||
f35e7fcd TL |
336 | my $ec_format = { |
337 | k => { | |
338 | type => 'integer', | |
339 | description => "Number of data chunks. Will create an erasure coded pool plus a" | |
340 | ." replicated pool for metadata.", | |
136f761b | 341 | minimum => 2, |
f35e7fcd TL |
342 | }, |
343 | m => { | |
344 | type => 'integer', | |
345 | description => "Number of coding chunks. Will create an erasure coded pool plus a" | |
346 | ." replicated pool for metadata.", | |
347 | minimum => 1, | |
348 | }, | |
349 | 'failure-domain' => { | |
350 | type => 'string', | |
351 | description => "CRUSH failure domain. Default is 'host'. Will create an erasure" | |
352 | ." coded pool plus a replicated pool for metadata.", | |
353 | format_description => 'domain', | |
354 | optional => 1, | |
136f761b | 355 | default => 'host', |
f35e7fcd TL |
356 | }, |
357 | 'device-class' => { | |
358 | type => 'string', | |
359 | description => "CRUSH device class. Will create an erasure coded pool plus a" | |
360 | ." replicated pool for metadata.", | |
361 | format_description => 'class', | |
362 | optional => 1, | |
363 | }, | |
364 | profile => { | |
365 | description => "Override the erasure code (EC) profile to use. Will create an" | |
366 | ." erasure coded pool plus a replicated pool for metadata.", | |
367 | type => 'string', | |
368 | format_description => 'profile', | |
369 | optional => 1, | |
370 | }, | |
371 | }; | |
372 | ||
373 | sub ec_parse_and_check { | |
374 | my ($property, $rados) = @_; | |
375 | return if !$property; | |
376 | ||
377 | my $ec = parse_property_string($ec_format, $property); | |
378 | ||
379 | die "Erasure code profile '$ec->{profile}' does not exist.\n" | |
380 | if $ec->{profile} && !PVE::Ceph::Tools::ecprofile_exists($ec->{profile}, $rados); | |
381 | ||
382 | return $ec; | |
383 | } | |
384 | ||
56d02a86 AA |
385 | |
386 | __PACKAGE__->register_method ({ | |
387 | name => 'createpool', | |
388 | path => '', | |
389 | method => 'POST', | |
f35e7fcd | 390 | description => "Create Ceph pool", |
56d02a86 AA |
391 | proxyto => 'node', |
392 | protected => 1, | |
393 | permissions => { | |
394 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
395 | }, | |
396 | parameters => { | |
397 | additionalProperties => 0, | |
398 | properties => { | |
399 | node => get_standard_option('pve-node'), | |
400 | add_storages => { | |
4605d6fd | 401 | description => "Configure VM and CT storage using the new pool.", |
56d02a86 AA |
402 | type => 'boolean', |
403 | optional => 1, | |
4605d6fd | 404 | default => "0; for erasure coded pools: 1", |
56d02a86 | 405 | }, |
f35e7fcd | 406 | 'erasure-coding' => { |
ea9eea01 TL |
407 | description => "Create an erasure coded pool for RBD with an accompaning" |
408 | ." replicated pool for metadata storage. With EC, the common ceph options 'size'," | |
409 | ." 'min_size' and 'crush_rule' parameters will be applied to the metadata pool.", | |
3bd128d7 | 410 | type => 'string', |
f35e7fcd | 411 | format => $ec_format, |
3bd128d7 AL |
412 | optional => 1, |
413 | }, | |
56d02a86 AA |
414 | %{ $ceph_pool_common_options->() }, |
415 | }, | |
416 | }, | |
417 | returns => { type => 'string' }, | |
418 | code => sub { | |
419 | my ($param) = @_; | |
420 | ||
421 | PVE::Cluster::check_cfs_quorum(); | |
422 | PVE::Ceph::Tools::check_ceph_configured(); | |
423 | ||
3bd128d7 | 424 | my $pool = my $name = extract_param($param, 'name'); |
56d02a86 AA |
425 | my $node = extract_param($param, 'node'); |
426 | my $add_storages = extract_param($param, 'add_storages'); | |
427 | ||
428 | my $rpcenv = PVE::RPCEnvironment::get(); | |
429 | my $user = $rpcenv->get_user(); | |
5a3d7942 AA |
430 | # Ceph uses target_size_bytes |
431 | if (defined($param->{'target_size'})) { | |
432 | my $target_sizestr = extract_param($param, 'target_size'); | |
433 | $param->{target_size_bytes} = PVE::JSONSchema::parse_size($target_sizestr); | |
434 | } | |
435 | ||
07316e6d TL |
436 | my $rados = PVE::RADOS->new(); |
437 | my $ec = ec_parse_and_check(extract_param($param, 'erasure-coding'), $rados); | |
438 | $add_storages = 1 if $ec && !defined($add_storages); | |
439 | ||
56d02a86 AA |
440 | if ($add_storages) { |
441 | $rpcenv->check($user, '/storage', ['Datastore.Allocate']); | |
442 | die "pool name contains characters which are illegal for storage naming\n" | |
443 | if !PVE::JSONSchema::parse_storage_id($pool); | |
444 | } | |
445 | ||
446 | # pool defaults | |
447 | $param->{pg_num} //= 128; | |
448 | $param->{size} //= 3; | |
449 | $param->{min_size} //= 2; | |
450 | $param->{application} //= 'rbd'; | |
451 | $param->{pg_autoscale_mode} //= 'warn'; | |
452 | ||
f35e7fcd TL |
453 | my $worker = sub { |
454 | # reopen with longer timeout | |
455 | $rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout')); | |
456 | ||
457 | if ($ec) { | |
458 | if (!$ec->{profile}) { | |
459 | $ec->{profile} = PVE::Ceph::Tools::get_ecprofile_name($pool, $rados); | |
460 | eval { | |
461 | PVE::Ceph::Tools::create_ecprofile( | |
462 | $ec->@{'profile', 'k', 'm', 'failure-domain', 'device-class'}, | |
463 | $rados, | |
464 | ); | |
465 | }; | |
466 | die "could not create erasure code profile '$ec->{profile}': $@\n" if $@; | |
467 | print "created new erasure code profile '$ec->{profile}'\n"; | |
468 | } | |
3bd128d7 | 469 | |
f35e7fcd TL |
470 | my $ec_data_param = {}; |
471 | # copy all params, should be a flat hash | |
472 | $ec_data_param = { map { $_ => $param->{$_} } keys %$param }; | |
3bd128d7 | 473 | |
f35e7fcd TL |
474 | $ec_data_param->{pool_type} = 'erasure'; |
475 | $ec_data_param->{allow_ec_overwrites} = 'true'; | |
476 | $ec_data_param->{erasure_code_profile} = $ec->{profile}; | |
477 | delete $ec_data_param->{size}; | |
478 | delete $ec_data_param->{min_size}; | |
9569939a | 479 | delete $ec_data_param->{crush_rule}; |
3bd128d7 | 480 | |
f35e7fcd TL |
481 | # metadata pool should be ok with 32 PGs |
482 | $param->{pg_num} = 32; | |
483 | ||
484 | $pool = "${name}-metadata"; | |
485 | $ec->{data_pool} = "${name}-data"; | |
486 | ||
487 | PVE::Ceph::Tools::create_pool($ec->{data_pool}, $ec_data_param, $rados); | |
488 | } | |
56d02a86 | 489 | |
f35e7fcd | 490 | PVE::Ceph::Tools::create_pool($pool, $param, $rados); |
3bd128d7 | 491 | |
56d02a86 | 492 | if ($add_storages) { |
f35e7fcd | 493 | eval { $add_storage->($pool, "${name}", $ec->{data_pool}) }; |
3bd128d7 | 494 | die "adding PVE storage for ceph pool '$name' failed: $@\n" if $@; |
56d02a86 AA |
495 | } |
496 | }; | |
497 | ||
498 | return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker); | |
499 | }}); | |
500 | ||
501 | ||
502 | __PACKAGE__->register_method ({ | |
503 | name => 'destroypool', | |
504 | path => '{name}', | |
505 | method => 'DELETE', | |
506 | description => "Destroy pool", | |
507 | proxyto => 'node', | |
508 | protected => 1, | |
509 | permissions => { | |
510 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
511 | }, | |
512 | parameters => { | |
513 | additionalProperties => 0, | |
514 | properties => { | |
515 | node => get_standard_option('pve-node'), | |
516 | name => { | |
517 | description => "The name of the pool. It must be unique.", | |
518 | type => 'string', | |
519 | }, | |
520 | force => { | |
521 | description => "If true, destroys pool even if in use", | |
522 | type => 'boolean', | |
523 | optional => 1, | |
524 | default => 0, | |
525 | }, | |
526 | remove_storages => { | |
527 | description => "Remove all pveceph-managed storages configured for this pool", | |
528 | type => 'boolean', | |
529 | optional => 1, | |
530 | default => 0, | |
531 | }, | |
3bd128d7 | 532 | remove_ecprofile => { |
f35e7fcd | 533 | description => "Remove the erasure code profile. Defaults to true, if applicable.", |
3bd128d7 AL |
534 | type => 'boolean', |
535 | optional => 1, | |
536 | default => 1, | |
537 | }, | |
56d02a86 AA |
538 | }, |
539 | }, | |
540 | returns => { type => 'string' }, | |
541 | code => sub { | |
542 | my ($param) = @_; | |
543 | ||
544 | PVE::Ceph::Tools::check_ceph_inited(); | |
545 | ||
546 | my $rpcenv = PVE::RPCEnvironment::get(); | |
547 | my $user = $rpcenv->get_user(); | |
548 | $rpcenv->check($user, '/storage', ['Datastore.Allocate']) | |
549 | if $param->{remove_storages}; | |
550 | ||
551 | my $pool = $param->{name}; | |
552 | ||
553 | my $worker = sub { | |
554 | my $storages = $get_storages->($pool); | |
555 | ||
556 | # if not forced, destroy ceph pool only when no | |
557 | # vm disks are on it anymore | |
558 | if (!$param->{force}) { | |
559 | my $storagecfg = PVE::Storage::config(); | |
560 | foreach my $storeid (keys %$storages) { | |
561 | my $storage = $storages->{$storeid}; | |
562 | ||
563 | # check if any vm disks are on the pool | |
564 | print "checking storage '$storeid' for RBD images..\n"; | |
565 | my $res = PVE::Storage::vdisk_list($storagecfg, $storeid); | |
566 | die "ceph pool '$pool' still in use by storage '$storeid'\n" | |
567 | if @{$res->{$storeid}} != 0; | |
568 | } | |
569 | } | |
f35e7fcd | 570 | my $rados = PVE::RADOS->new(); |
56d02a86 | 571 | |
f35e7fcd | 572 | my $pool_properties = PVE::Ceph::Tools::get_pool_properties($pool, $rados); |
3bd128d7 | 573 | |
f35e7fcd | 574 | PVE::Ceph::Tools::destroy_pool($pool, $rados); |
56d02a86 | 575 | |
3bd128d7 | 576 | if (my $ecprofile = $pool_properties->{erasure_code_profile}) { |
f35e7fcd | 577 | print "found erasure coded profile '$ecprofile', destroying its CRUSH rule\n"; |
3bd128d7 | 578 | my $crush_rule = $pool_properties->{crush_rule}; |
f35e7fcd | 579 | eval { PVE::Ceph::Tools::destroy_crush_rule($crush_rule, $rados); }; |
3bd128d7 AL |
580 | warn "removing crush rule '${crush_rule}' failed: $@\n" if $@; |
581 | ||
f35e7fcd TL |
582 | if ($param->{remove_ecprofile} // 1) { |
583 | print "destroying erasure coded profile '$ecprofile'\n"; | |
584 | eval { PVE::Ceph::Tools::destroy_ecprofile($ecprofile, $rados) }; | |
3bd128d7 AL |
585 | warn "removing EC profile '${ecprofile}' failed: $@\n" if $@; |
586 | } | |
587 | } | |
588 | ||
56d02a86 AA |
589 | if ($param->{remove_storages}) { |
590 | my $err; | |
591 | foreach my $storeid (keys %$storages) { | |
592 | # skip external clusters, not managed by pveceph | |
593 | next if $storages->{$storeid}->{monhost}; | |
594 | eval { PVE::API2::Storage::Config->delete({storage => $storeid}) }; | |
595 | if ($@) { | |
596 | warn "failed to remove storage '$storeid': $@\n"; | |
597 | $err = 1; | |
598 | } | |
599 | } | |
600 | die "failed to remove (some) storages - check log and remove manually!\n" | |
601 | if $err; | |
602 | } | |
603 | }; | |
604 | return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker); | |
605 | }}); | |
606 | ||
607 | ||
608 | __PACKAGE__->register_method ({ | |
609 | name => 'setpool', | |
610 | path => '{name}', | |
611 | method => 'PUT', | |
612 | description => "Change POOL settings", | |
613 | proxyto => 'node', | |
614 | protected => 1, | |
615 | permissions => { | |
616 | check => ['perm', '/', [ 'Sys.Modify' ]], | |
617 | }, | |
618 | parameters => { | |
619 | additionalProperties => 0, | |
620 | properties => { | |
621 | node => get_standard_option('pve-node'), | |
622 | %{ $ceph_pool_common_options->('nodefault') }, | |
623 | }, | |
624 | }, | |
625 | returns => { type => 'string' }, | |
626 | code => sub { | |
627 | my ($param) = @_; | |
628 | ||
629 | PVE::Ceph::Tools::check_ceph_configured(); | |
630 | ||
631 | my $rpcenv = PVE::RPCEnvironment::get(); | |
632 | my $authuser = $rpcenv->get_user(); | |
633 | ||
51d6db58 AA |
634 | my $pool = extract_param($param, 'name'); |
635 | my $node = extract_param($param, 'node'); | |
56d02a86 | 636 | |
5a3d7942 AA |
637 | # Ceph uses target_size_bytes |
638 | if (defined($param->{'target_size'})) { | |
639 | my $target_sizestr = extract_param($param, 'target_size'); | |
640 | $param->{target_size_bytes} = PVE::JSONSchema::parse_size($target_sizestr); | |
641 | } | |
642 | ||
56d02a86 | 643 | my $worker = sub { |
51d6db58 | 644 | PVE::Ceph::Tools::set_pool($pool, $param); |
56d02a86 AA |
645 | }; |
646 | ||
647 | return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker); | |
648 | }}); | |
649 | ||
650 | ||
54ba7dd9 AA |
651 | __PACKAGE__->register_method ({ |
652 | name => 'getpool', | |
653 | path => '{name}', | |
654 | method => 'GET', | |
655 | description => "List pool settings.", | |
656 | proxyto => 'node', | |
657 | protected => 1, | |
658 | permissions => { | |
659 | check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1], | |
660 | }, | |
661 | parameters => { | |
662 | additionalProperties => 0, | |
663 | properties => { | |
664 | node => get_standard_option('pve-node'), | |
665 | name => { | |
666 | description => "The name of the pool. It must be unique.", | |
667 | type => 'string', | |
668 | }, | |
669 | verbose => { | |
670 | type => 'boolean', | |
671 | default => 0, | |
672 | optional => 1, | |
673 | description => "If enabled, will display additional data". | |
674 | "(eg. statistics).", | |
675 | }, | |
676 | }, | |
677 | }, | |
678 | returns => { | |
679 | type => "object", | |
680 | properties => { | |
681 | id => { type => 'integer', title => 'ID' }, | |
682 | pgp_num => { type => 'integer', title => 'PGP num' }, | |
683 | noscrub => { type => 'boolean', title => 'noscrub' }, | |
684 | 'nodeep-scrub' => { type => 'boolean', title => 'nodeep-scrub' }, | |
685 | nodelete => { type => 'boolean', title => 'nodelete' }, | |
686 | nopgchange => { type => 'boolean', title => 'nopgchange' }, | |
687 | nosizechange => { type => 'boolean', title => 'nosizechange' }, | |
688 | write_fadvise_dontneed => { type => 'boolean', title => 'write_fadvise_dontneed' }, | |
689 | hashpspool => { type => 'boolean', title => 'hashpspool' }, | |
690 | use_gmt_hitset => { type => 'boolean', title => 'use_gmt_hitset' }, | |
691 | fast_read => { type => 'boolean', title => 'Fast Read' }, | |
692 | application_list => { type => 'array', title => 'Application', optional => 1 }, | |
693 | statistics => { type => 'object', title => 'Statistics', optional => 1 }, | |
5a3d7942 | 694 | autoscale_status => { type => 'object', title => 'Autoscale Status', optional => 1 }, |
54ba7dd9 AA |
695 | %{ $ceph_pool_common_options->() }, |
696 | }, | |
697 | }, | |
698 | code => sub { | |
699 | my ($param) = @_; | |
700 | ||
701 | PVE::Ceph::Tools::check_ceph_inited(); | |
702 | ||
703 | my $verbose = $param->{verbose}; | |
704 | my $pool = $param->{name}; | |
705 | ||
706 | my $rados = PVE::RADOS->new(); | |
707 | my $res = $rados->mon_command({ | |
708 | prefix => 'osd pool get', | |
709 | pool => "$pool", | |
710 | var => 'all', | |
711 | }); | |
712 | ||
713 | my $data = { | |
714 | id => $res->{pool_id}, | |
715 | name => $pool, | |
716 | size => $res->{size}, | |
717 | min_size => $res->{min_size}, | |
718 | pg_num => $res->{pg_num}, | |
5a3d7942 | 719 | pg_num_min => $res->{pg_num_min}, |
54ba7dd9 AA |
720 | pgp_num => $res->{pgp_num}, |
721 | crush_rule => $res->{crush_rule}, | |
722 | pg_autoscale_mode => $res->{pg_autoscale_mode}, | |
723 | noscrub => "$res->{noscrub}", | |
724 | 'nodeep-scrub' => "$res->{'nodeep-scrub'}", | |
725 | nodelete => "$res->{nodelete}", | |
726 | nopgchange => "$res->{nopgchange}", | |
727 | nosizechange => "$res->{nosizechange}", | |
728 | write_fadvise_dontneed => "$res->{write_fadvise_dontneed}", | |
729 | hashpspool => "$res->{hashpspool}", | |
730 | use_gmt_hitset => "$res->{use_gmt_hitset}", | |
731 | fast_read => "$res->{fast_read}", | |
5a3d7942 AA |
732 | target_size => $res->{target_size_bytes}, |
733 | target_size_ratio => $res->{target_size_ratio}, | |
54ba7dd9 AA |
734 | }; |
735 | ||
736 | if ($verbose) { | |
737 | my $stats; | |
738 | my $res = $rados->mon_command({ prefix => 'df' }); | |
739 | ||
5a3d7942 AA |
740 | # pg_autoscaler module is not enabled in Nautilus |
741 | # avoid partial read further down, use new rados instance | |
742 | my $autoscale_status = eval { $get_autoscale_status->() }; | |
743 | $data->{autoscale_status} = $autoscale_status->{$pool}; | |
744 | ||
54ba7dd9 AA |
745 | foreach my $d (@{$res->{pools}}) { |
746 | next if !$d->{stats}; | |
747 | next if !defined($d->{name}) && !$d->{name} ne "$pool"; | |
748 | $data->{statistics} = $d->{stats}; | |
749 | } | |
750 | ||
751 | my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", }); | |
752 | $data->{application_list} = [ keys %$apps ]; | |
753 | } | |
754 | ||
755 | return $data; | |
756 | }}); | |
757 | ||
758 | ||
56d02a86 | 759 | 1; |