]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph/Pools.pm
1 package PVE
::API2
::Ceph
::Pools
;
7 use PVE
::Ceph
::Services
;
8 use PVE
::JSONSchema
qw(get_standard_option);
11 use PVE
::RPCEnvironment
;
13 use PVE
::Tools
qw(extract_param);
15 use PVE
::API2
::Storage
::Config
;
17 use base
qw(PVE::RESTHandler);
19 my $get_autoscale_status = sub {
22 $rados = PVE
::RADOS-
>new() if !defined($rados);
24 my $autoscale = $rados->mon_command({
25 prefix
=> 'osd pool autoscale-status'});
28 foreach my $p (@$autoscale) {
29 $data->{$p->{pool_name
}} = $p;
36 __PACKAGE__-
>register_method ({
40 description
=> "List all pools.",
44 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
47 additionalProperties
=> 0,
49 node
=> get_standard_option
('pve-node'),
79 title
=> 'min. PG Num',
84 title
=> 'Optimal PG Num',
87 pg_autoscale_mode
=> {
89 title
=> 'PG Autoscale Mode',
94 title
=> 'Crush Rule',
98 title
=> 'Crush Rule Name',
110 title
=> 'PG Autoscale Target Size',
113 target_size_ratio
=> {
115 title
=> 'PG Autoscale Target Ratio',
118 autoscale_status
=> {
120 title
=> 'Autoscale Status',
125 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
130 PVE
::Ceph
::Tools
::check_ceph_inited
();
132 my $rados = PVE
::RADOS-
>new();
135 my $res = $rados->mon_command({ prefix
=> 'df' });
137 foreach my $d (@{$res->{pools
}}) {
138 next if !$d->{stats
};
139 next if !defined($d->{id
});
140 $stats->{$d->{id
}} = $d->{stats
};
143 $res = $rados->mon_command({ prefix
=> 'osd dump' });
144 my $rulestmp = $rados->mon_command({ prefix
=> 'osd crush rule dump'});
147 for my $rule (@$rulestmp) {
148 $rules->{$rule->{rule_id
}} = $rule->{rule_name
};
162 # pg_autoscaler module is not enabled in Nautilus
163 my $autoscale = eval { $get_autoscale_status->($rados) };
165 foreach my $e (@{$res->{pools
}}) {
167 foreach my $attr (@$attr_list) {
168 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
172 $d->{autoscale_status
} = $autoscale->{$d->{pool_name
}};
173 $d->{pg_num_final
} = $d->{autoscale_status
}->{pg_num_final
};
174 # some info is nested under options instead
175 $d->{pg_num_min
} = $e->{options
}->{pg_num_min
};
176 $d->{target_size
} = $e->{options
}->{target_size_bytes
};
177 $d->{target_size_ratio
} = $e->{options
}->{target_size_ratio
};
180 if (defined($d->{crush_rule
}) && defined($rules->{$d->{crush_rule
}})) {
181 $d->{crush_rule_name
} = $rules->{$d->{crush_rule
}};
184 if (my $s = $stats->{$d->{pool
}}) {
185 $d->{bytes_used
} = $s->{bytes_used
};
186 $d->{percent_used
} = $s->{percent_used
};
196 my $ceph_pool_common_options = sub {
197 my ($nodefault) = shift;
201 description
=> "The name of the pool. It must be unique.",
206 description
=> 'Number of replicas per object',
215 description
=> 'Minimum number of replicas per object',
224 description
=> "Number of placement groups.",
232 title
=> 'min. PG Num',
233 description
=> "Minimal number of placement groups.",
239 title
=> 'Crush Rule Name',
240 description
=> "The rule to use for mapping object placement in the cluster.",
245 title
=> 'Application',
246 description
=> "The application of the pool.",
249 enum
=> ['rbd', 'cephfs', 'rgw'],
252 pg_autoscale_mode
=> {
253 title
=> 'PG Autoscale Mode',
254 description
=> "The automatic PG scaling mode of the pool.",
256 enum
=> ['on', 'off', 'warn'],
261 description
=> "The estimated target size of the pool for the PG autoscaler.",
262 title
=> 'PG Autoscale Target Size',
264 pattern
=> '^(\d+(\.\d+)?)([KMGT])?$',
267 target_size_ratio
=> {
268 description
=> "The estimated target ratio of the pool for the PG autoscaler.",
269 title
=> 'PG Autoscale Target Ratio',
276 delete $options->{$_}->{default} for keys %$options;
282 my $add_storage = sub {
283 my ($pool, $storeid, $data_pool) = @_;
285 my $storage_params = {
290 content
=> 'rootdir,images',
293 $storage_params->{'data-pool'} = $data_pool if $data_pool;
295 PVE
::API2
::Storage
::Config-
>create($storage_params);
298 my $get_storages = sub {
301 my $cfg = PVE
::Storage
::config
();
303 my $storages = $cfg->{ids
};
305 foreach my $storeid (keys %$storages) {
306 my $curr = $storages->{$storeid};
307 next if $curr->{type
} ne 'rbd';
309 $pool eq $curr->{pool
} ||
310 (defined $curr->{'data-pool'} && $pool eq $curr->{'data-pool'})
312 $res->{$storeid} = $storages->{$storeid};
320 __PACKAGE__-
>register_method ({
321 name
=> 'createpool',
324 description
=> "Create POOL",
328 check
=> ['perm', '/', [ 'Sys.Modify' ]],
331 additionalProperties
=> 0,
333 node
=> get_standard_option
('pve-node'),
335 description
=> "Configure VM and CT storage using the new pool. ".
336 "Always enabled for erasure coded pools.",
342 description
=> "Number of data chunks. Will create an erasure coded pool plus a ".
343 "replicated pool for metadata.",
348 description
=> "Number of coding chunks. Will create an erasure coded pool plus a ".
349 "replicated pool for metadata.",
352 'failure-domain' => {
354 description
=> "CRUSH failure domain. Default is 'host'. Will create an erasure ".
355 "coded pool plus a replicated pool for metadata.",
360 description
=> "CRUSH device class. Will create an erasure coded pool plus a ".
361 "replicated pool for metadata.",
365 description
=> "Override the erasure code (EC) profile to use. Will create an ".
366 "erasure coded pool plus a replicated pool for metadata.",
370 %{ $ceph_pool_common_options->() },
373 returns
=> { type
=> 'string' },
377 PVE
::Cluster
::check_cfs_quorum
();
378 PVE
::Ceph
::Tools
::check_ceph_configured
();
380 my $pool = my $name = extract_param
($param, 'name');
381 my $node = extract_param
($param, 'node');
382 my $add_storages = extract_param
($param, 'add_storages');
384 my $ec_k = extract_param
($param, 'k');
385 my $ec_m = extract_param
($param, 'm');
386 my $ec_failure_domain = extract_param
($param, 'failure-domain');
387 my $ec_device_class = extract_param
($param, 'device-class');
391 my $ecprofile = extract_param
($param, 'ecprofile');
392 die "Erasure code profile '$ecprofile' does not exist.\n"
393 if $ecprofile && !PVE
::Ceph
::Tools
::ecprofile_exists
($ecprofile);
395 if ($ec_k || $ec_m || $ec_failure_domain || $ec_device_class) {
396 die "'k' and 'm' parameters are needed for an erasure coded pool\n"
402 $is_ec = 1 if $ecprofile;
403 $add_storages = 1 if $is_ec;
405 my $rpcenv = PVE
::RPCEnvironment
::get
();
406 my $user = $rpcenv->get_user();
408 # Ceph uses target_size_bytes
409 if (defined($param->{'target_size'})) {
410 my $target_sizestr = extract_param
($param, 'target_size');
411 $param->{target_size_bytes
} = PVE
::JSONSchema
::parse_size
($target_sizestr);
415 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
416 die "pool name contains characters which are illegal for storage naming\n"
417 if !PVE
::JSONSchema
::parse_storage_id
($pool);
421 $param->{pg_num
} //= 128;
422 $param->{size
} //= 3;
423 $param->{min_size
} //= 2;
424 $param->{application
} //= 'rbd';
425 $param->{pg_autoscale_mode
} //= 'warn';
430 $ecprofile = PVE
::Ceph
::Tools
::get_ecprofile_name
($pool);
432 PVE
::Ceph
::Tools
::create_ecprofile
(
440 die "could not create erasure code profile '$ecprofile': $@\n" if $@;
444 # copy all params, should be a flat hash
445 $data_param = { map { $_ => $param->{$_} } keys %$param };
447 $data_param->{pool_type
} = 'erasure';
448 $data_param->{allow_ec_overwrites
} = 'true';
449 $data_param->{erasure_code_profile
} = $ecprofile;
450 delete $data_param->{size
};
451 delete $data_param->{min_size
};
453 # metadata pool should be ok with 32 PGs
454 $param->{pg_num
} = 32;
456 $pool = "${name}-metadata";
457 $data_pool = "${name}-data";
461 PVE
::Ceph
::Tools
::create_pool
($pool, $param);
463 PVE
::Ceph
::Tools
::create_pool
($data_pool, $data_param) if $is_ec;
466 eval { $add_storage->($pool, "${name}", $data_pool) };
467 die "adding PVE storage for ceph pool '$name' failed: $@\n" if $@;
471 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
475 __PACKAGE__-
>register_method ({
476 name
=> 'destroypool',
479 description
=> "Destroy pool",
483 check
=> ['perm', '/', [ 'Sys.Modify' ]],
486 additionalProperties
=> 0,
488 node
=> get_standard_option
('pve-node'),
490 description
=> "The name of the pool. It must be unique.",
494 description
=> "If true, destroys pool even if in use",
500 description
=> "Remove all pveceph-managed storages configured for this pool",
505 remove_ecprofile
=> {
506 description
=> "Remove the erasure code profile. Used for erasure code pools. Default is true",
513 returns
=> { type
=> 'string' },
517 PVE
::Ceph
::Tools
::check_ceph_inited
();
519 my $rpcenv = PVE
::RPCEnvironment
::get
();
520 my $user = $rpcenv->get_user();
521 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
522 if $param->{remove_storages
};
524 my $pool = $param->{name
};
525 my $remove_ecprofile = $param->{remove_ecprofile
} // 1;
528 my $storages = $get_storages->($pool);
530 # if not forced, destroy ceph pool only when no
531 # vm disks are on it anymore
532 if (!$param->{force
}) {
533 my $storagecfg = PVE
::Storage
::config
();
534 foreach my $storeid (keys %$storages) {
535 my $storage = $storages->{$storeid};
537 # check if any vm disks are on the pool
538 print "checking storage '$storeid' for RBD images..\n";
539 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storeid);
540 die "ceph pool '$pool' still in use by storage '$storeid'\n"
541 if @{$res->{$storeid}} != 0;
545 my $pool_properties = PVE
::Ceph
::Tools
::get_pool_properties
($pool);
547 PVE
::Ceph
::Tools
::destroy_pool
($pool);
549 if (my $ecprofile = $pool_properties->{erasure_code_profile
}) {
550 my $crush_rule = $pool_properties->{crush_rule
};
551 eval { PVE
::Ceph
::Tools
::destroy_crush_rule
($crush_rule); };
552 warn "removing crush rule '${crush_rule}' failed: $@\n" if $@;
554 if ($remove_ecprofile) {
555 eval { PVE
::Ceph
::Tools
::destroy_ecprofile
($ecprofile) };
556 warn "removing EC profile '${ecprofile}' failed: $@\n" if $@;
560 if ($param->{remove_storages
}) {
562 foreach my $storeid (keys %$storages) {
563 # skip external clusters, not managed by pveceph
564 next if $storages->{$storeid}->{monhost
};
565 eval { PVE
::API2
::Storage
::Config-
>delete({storage
=> $storeid}) };
567 warn "failed to remove storage '$storeid': $@\n";
571 die "failed to remove (some) storages - check log and remove manually!\n"
575 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
579 __PACKAGE__-
>register_method ({
583 description
=> "Change POOL settings",
587 check
=> ['perm', '/', [ 'Sys.Modify' ]],
590 additionalProperties
=> 0,
592 node
=> get_standard_option
('pve-node'),
593 %{ $ceph_pool_common_options->('nodefault') },
596 returns
=> { type
=> 'string' },
600 PVE
::Ceph
::Tools
::check_ceph_configured
();
602 my $rpcenv = PVE
::RPCEnvironment
::get
();
603 my $authuser = $rpcenv->get_user();
605 my $pool = extract_param
($param, 'name');
606 my $node = extract_param
($param, 'node');
608 # Ceph uses target_size_bytes
609 if (defined($param->{'target_size'})) {
610 my $target_sizestr = extract_param
($param, 'target_size');
611 $param->{target_size_bytes
} = PVE
::JSONSchema
::parse_size
($target_sizestr);
615 PVE
::Ceph
::Tools
::set_pool
($pool, $param);
618 return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker);
622 __PACKAGE__-
>register_method ({
626 description
=> "List pool settings.",
630 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
633 additionalProperties
=> 0,
635 node
=> get_standard_option
('pve-node'),
637 description
=> "The name of the pool. It must be unique.",
644 description
=> "If enabled, will display additional data".
652 id
=> { type
=> 'integer', title
=> 'ID' },
653 pgp_num
=> { type
=> 'integer', title
=> 'PGP num' },
654 noscrub
=> { type
=> 'boolean', title
=> 'noscrub' },
655 'nodeep-scrub' => { type
=> 'boolean', title
=> 'nodeep-scrub' },
656 nodelete
=> { type
=> 'boolean', title
=> 'nodelete' },
657 nopgchange
=> { type
=> 'boolean', title
=> 'nopgchange' },
658 nosizechange
=> { type
=> 'boolean', title
=> 'nosizechange' },
659 write_fadvise_dontneed
=> { type
=> 'boolean', title
=> 'write_fadvise_dontneed' },
660 hashpspool
=> { type
=> 'boolean', title
=> 'hashpspool' },
661 use_gmt_hitset
=> { type
=> 'boolean', title
=> 'use_gmt_hitset' },
662 fast_read
=> { type
=> 'boolean', title
=> 'Fast Read' },
663 application_list
=> { type
=> 'array', title
=> 'Application', optional
=> 1 },
664 statistics
=> { type
=> 'object', title
=> 'Statistics', optional
=> 1 },
665 autoscale_status
=> { type
=> 'object', title
=> 'Autoscale Status', optional
=> 1 },
666 %{ $ceph_pool_common_options->() },
672 PVE
::Ceph
::Tools
::check_ceph_inited
();
674 my $verbose = $param->{verbose
};
675 my $pool = $param->{name
};
677 my $rados = PVE
::RADOS-
>new();
678 my $res = $rados->mon_command({
679 prefix
=> 'osd pool get',
685 id
=> $res->{pool_id
},
687 size
=> $res->{size
},
688 min_size
=> $res->{min_size
},
689 pg_num
=> $res->{pg_num
},
690 pg_num_min
=> $res->{pg_num_min
},
691 pgp_num
=> $res->{pgp_num
},
692 crush_rule
=> $res->{crush_rule
},
693 pg_autoscale_mode
=> $res->{pg_autoscale_mode
},
694 noscrub
=> "$res->{noscrub}",
695 'nodeep-scrub' => "$res->{'nodeep-scrub'}",
696 nodelete
=> "$res->{nodelete}",
697 nopgchange
=> "$res->{nopgchange}",
698 nosizechange
=> "$res->{nosizechange}",
699 write_fadvise_dontneed
=> "$res->{write_fadvise_dontneed}",
700 hashpspool
=> "$res->{hashpspool}",
701 use_gmt_hitset
=> "$res->{use_gmt_hitset}",
702 fast_read
=> "$res->{fast_read}",
703 target_size
=> $res->{target_size_bytes
},
704 target_size_ratio
=> $res->{target_size_ratio
},
709 my $res = $rados->mon_command({ prefix
=> 'df' });
711 # pg_autoscaler module is not enabled in Nautilus
712 # avoid partial read further down, use new rados instance
713 my $autoscale_status = eval { $get_autoscale_status->() };
714 $data->{autoscale_status
} = $autoscale_status->{$pool};
716 foreach my $d (@{$res->{pools
}}) {
717 next if !$d->{stats
};
718 next if !defined($d->{name
}) && !$d->{name
} ne "$pool";
719 $data->{statistics
} = $d->{stats
};
722 my $apps = $rados->mon_command({ prefix
=> "osd pool application get", pool
=> "$pool", });
723 $data->{application_list
} = [ keys %$apps ];