mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-08-17 04:18:45 +00:00

By only setting properties that have changed, we can avoid potential errors in the task. For example, if one configures the "nosizechange" property on a pool, to prevent accidental size changes, the task will now only error if the user is actually trying to change the size. Prior to this patch, we would always try to set all parameters, even if they were the same value. In the above example, this would result in the task ending in error state, as we are not allowed to change the size. To disable size changing you can run the following command: ceph osd pool set {pool} nosizechange 1 Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com> Reviewed-by: Christoph Heiss <c.heiss@proxmox.com> Tested-by: Christoph Heiss <c.heiss@proxmox.com>
693 lines
17 KiB
Perl
693 lines
17 KiB
Perl
package PVE::Ceph::Tools;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Path;
|
|
use File::Basename;
|
|
use IO::File;
|
|
use JSON;
|
|
|
|
use PVE::Tools qw(run_command dir_glob_foreach extract_param);
|
|
use PVE::Cluster qw(cfs_read_file);
|
|
use PVE::RADOS;
|
|
use PVE::Ceph::Services;
|
|
use PVE::CephConfig;
|
|
|
|
my $ccname = 'ceph'; # ceph cluster name
|
|
my $ceph_cfgdir = "/etc/ceph";
|
|
my $pve_ceph_cfgpath = "/etc/pve/$ccname.conf";
|
|
my $ceph_cfgpath = "$ceph_cfgdir/$ccname.conf";
|
|
my $pve_ceph_cfgdir = "/etc/pve/ceph";
|
|
|
|
my $pve_ceph_crash_key_path = "$pve_ceph_cfgdir/$ccname.client.crash.keyring";
|
|
my $pve_mon_key_path = "/etc/pve/priv/$ccname.mon.keyring";
|
|
my $pve_ckeyring_path = "/etc/pve/priv/$ccname.client.admin.keyring";
|
|
my $ckeyring_path = "/etc/ceph/ceph.client.admin.keyring";
|
|
my $ceph_bootstrap_osd_keyring = "/var/lib/ceph/bootstrap-osd/$ccname.keyring";
|
|
my $ceph_bootstrap_mds_keyring = "/var/lib/ceph/bootstrap-mds/$ccname.keyring";
|
|
my $ceph_mds_data_dir = '/var/lib/ceph/mds';
|
|
|
|
my $ceph_service = {
|
|
ceph_bin => "/usr/bin/ceph",
|
|
ceph_mon => "/usr/bin/ceph-mon",
|
|
ceph_mgr => "/usr/bin/ceph-mgr",
|
|
ceph_osd => "/usr/bin/ceph-osd",
|
|
ceph_mds => "/usr/bin/ceph-mds",
|
|
ceph_volume => '/usr/sbin/ceph-volume',
|
|
};
|
|
|
|
my $config_values = {
|
|
ccname => $ccname,
|
|
pve_ceph_cfgdir => $pve_ceph_cfgdir,
|
|
ceph_mds_data_dir => $ceph_mds_data_dir,
|
|
long_rados_timeout => 60,
|
|
};
|
|
|
|
my $config_files = {
|
|
pve_ceph_cfgpath => $pve_ceph_cfgpath,
|
|
pve_ceph_crash_key_path => $pve_ceph_crash_key_path,
|
|
pve_mon_key_path => $pve_mon_key_path,
|
|
pve_ckeyring_path => $pve_ckeyring_path,
|
|
ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring,
|
|
ceph_bootstrap_mds_keyring => $ceph_bootstrap_mds_keyring,
|
|
ceph_cfgpath => $ceph_cfgpath,
|
|
};
|
|
|
|
sub get_local_version {
|
|
my ($noerr) = @_;
|
|
|
|
return undef if !check_ceph_installed('ceph_bin', $noerr);
|
|
|
|
my $ceph_version;
|
|
run_command(
|
|
[ $ceph_service->{ceph_bin}, '--version' ],
|
|
noerr => $noerr,
|
|
outfunc => sub { $ceph_version = shift if !defined $ceph_version },
|
|
);
|
|
|
|
return undef if !defined $ceph_version;
|
|
|
|
my ($version, $buildcommit, $subversions) = parse_ceph_version($ceph_version);
|
|
|
|
return undef if !defined($version);
|
|
|
|
# return (version, buildid, [major, minor, ...]) : major;
|
|
return wantarray ? ($version, $buildcommit, $subversions) : $subversions->[0];
|
|
}
|
|
|
|
sub parse_ceph_version : prototype($) {
|
|
my ($ceph_version) = @_;
|
|
|
|
my $re_ceph_version = qr/
|
|
# Skip ahead to the version, which may optionally start with 'v'
|
|
^ceph.*\sv?
|
|
|
|
# Parse the version X.Y, X.Y.Z, etc.
|
|
( \d+ (?:\.\d+)+ ) \s+
|
|
|
|
# Parse the git commit hash between parentheses
|
|
(?: \( ([a-zA-Z0-9]+) \) )
|
|
/x;
|
|
|
|
if ($ceph_version =~ /$re_ceph_version/) {
|
|
my ($version, $buildcommit) = ($1, $2);
|
|
my $subversions = [ split(/\.|-/, $version) ];
|
|
|
|
# return (version, buildid, [major, minor, ...]) : major;
|
|
return wantarray
|
|
? ($version, $buildcommit, $subversions)
|
|
: $subversions->[0];
|
|
}
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub get_cluster_versions {
|
|
my ($service, $noerr) = @_;
|
|
|
|
my $rados = PVE::RADOS->new();
|
|
my $cmd = $service ? "$service versions" : 'versions';
|
|
return $rados->mon_command({ prefix => $cmd });
|
|
}
|
|
|
|
sub get_config {
|
|
my $key = shift;
|
|
|
|
my $value = $config_values->{$key} // $config_files->{$key};
|
|
|
|
die "no such ceph config '$key'" if ! defined($value);
|
|
|
|
return $value;
|
|
}
|
|
|
|
sub purge_all_ceph_files {
|
|
my ($services) = @_;
|
|
my $is_local_mon;
|
|
my $monlist = [ split(',', PVE::CephConfig::get_monaddr_list($pve_ceph_cfgpath)) ];
|
|
|
|
foreach my $service (keys %$services) {
|
|
my $type = $services->{$service};
|
|
next if (!%$type);
|
|
|
|
foreach my $name (keys %$type) {
|
|
my $dir_exists = $type->{$name}->{direxists};
|
|
|
|
$is_local_mon = grep($type->{$name}->{addr}, @$monlist)
|
|
if $service eq 'mon';
|
|
|
|
my $path = "/var/lib/ceph/$service";
|
|
$path = '/var/log/ceph' if $service eq 'logs';
|
|
if ($dir_exists) {
|
|
my $err;
|
|
File::Path::remove_tree($path, {
|
|
keep_root => 1,
|
|
error => \$err,
|
|
});
|
|
warn "Error removing path, '$path'\n" if @$err;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (scalar @$monlist > 0 && !$is_local_mon) {
|
|
warn "Foreign MON address in ceph.conf. Keeping config & keyrings\n"
|
|
} else {
|
|
print "Removing config & keyring files\n";
|
|
for my $file (%$config_files) {
|
|
unlink $file if (-e $file);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub purge_all_ceph_services {
|
|
my ($services) = @_;
|
|
|
|
foreach my $service (keys %$services) {
|
|
my $type = $services->{$service};
|
|
next if (!%$type);
|
|
|
|
foreach my $name (keys %$type) {
|
|
my $service_exists = $type->{$name}->{service};
|
|
|
|
if ($service_exists) {
|
|
eval { PVE::Ceph::Services::ceph_service_cmd('disable', "$service.$name") };
|
|
warn "Could not disable ceph-$service\@$name, error: $@\n" if $@;
|
|
|
|
eval { PVE::Ceph::Services::ceph_service_cmd('stop', "$service.$name") };
|
|
warn "Could not stop ceph-$service\@$name, error: $@\n" if $@;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub ceph_install_flag_file { return '/run/pve-ceph-install-flag' };
|
|
|
|
sub check_ceph_installed {
|
|
my ($service, $noerr) = @_;
|
|
|
|
$service = 'ceph_bin' if !defined($service);
|
|
|
|
# NOTE: the flag file is checked as on a new installation, the binary gets
|
|
# extracted by dpkg before the installation is finished
|
|
if (! -x $ceph_service->{$service} || -f ceph_install_flag_file()) {
|
|
die "binary not installed: $ceph_service->{$service}\n" if !$noerr;
|
|
return undef;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
sub check_ceph_configured {
|
|
|
|
check_ceph_inited();
|
|
|
|
die "ceph not fully configured - missing '$pve_ckeyring_path'\n"
|
|
if ! -f $pve_ckeyring_path;
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub check_ceph_inited {
|
|
my ($noerr) = @_;
|
|
|
|
return undef if !check_ceph_installed('ceph_mon', $noerr);
|
|
|
|
my @errors;
|
|
|
|
push(@errors, "missing '$pve_ceph_cfgpath'") if ! -f $pve_ceph_cfgpath;
|
|
push(@errors, "missing '$pve_ceph_cfgdir'") if ! -d $pve_ceph_cfgdir;
|
|
|
|
if (@errors) {
|
|
my $err = 'pveceph configuration not initialized - ' . join(', ', @errors) . "\n";
|
|
die $err if !$noerr;
|
|
return undef;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub check_ceph_enabled {
|
|
my ($noerr) = @_;
|
|
|
|
return undef if !check_ceph_inited($noerr);
|
|
|
|
if (! -f $ceph_cfgpath) {
|
|
die "pveceph configuration not enabled\n" if !$noerr;
|
|
return undef;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
my $set_pool_setting = sub {
|
|
my ($pool, $setting, $value, $rados) = @_;
|
|
|
|
my $command;
|
|
if ($setting eq 'application') {
|
|
$command = {
|
|
prefix => "osd pool application enable",
|
|
pool => "$pool",
|
|
app => "$value",
|
|
};
|
|
} else {
|
|
$command = {
|
|
prefix => "osd pool set",
|
|
pool => "$pool",
|
|
var => "$setting",
|
|
val => "$value",
|
|
format => 'plain',
|
|
};
|
|
}
|
|
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
eval { $rados->mon_command($command); };
|
|
return $@ ? $@ : undef;
|
|
};
|
|
|
|
sub set_pool {
|
|
my ($pool, $param) = @_;
|
|
|
|
my $rados = PVE::RADOS->new();
|
|
|
|
if (get_pool_type($pool, $rados) eq 'erasure') {
|
|
#remove parameters that cannot be changed for erasure coded pools
|
|
my $ignore_params = ['size', 'crush_rule'];
|
|
for my $setting (@$ignore_params) {
|
|
if ($param->{$setting}) {
|
|
print "cannot set '${setting}' for erasure coded pool\n";
|
|
delete $param->{$setting};
|
|
}
|
|
}
|
|
}
|
|
# by default, pool size always resets min_size, so set it as first item
|
|
# https://tracker.ceph.com/issues/44862
|
|
my $keys = [ grep { $_ ne 'size' } sort keys %$param ];
|
|
unshift @$keys, 'size' if exists $param->{size};
|
|
|
|
my $current_properties = get_pool_properties($pool, $rados);
|
|
|
|
for my $setting (@$keys) {
|
|
my $value = $param->{$setting};
|
|
|
|
if (defined($current_properties->{$setting}) && $value eq $current_properties->{$setting}) {
|
|
print "skipping '${setting}', did not change\n";
|
|
delete $param->{$setting};
|
|
next;
|
|
}
|
|
|
|
print "pool $pool: applying $setting = $value\n";
|
|
if (my $err = $set_pool_setting->($pool, $setting, $value, $rados)) {
|
|
print "$err";
|
|
} else {
|
|
delete $param->{$setting};
|
|
}
|
|
}
|
|
|
|
if (scalar(keys %$param) > 0) {
|
|
my $missing = join(', ', sort keys %$param );
|
|
die "Could not set: $missing\n";
|
|
}
|
|
|
|
}
|
|
|
|
sub get_pool_properties {
|
|
my ($pool, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
my $command = {
|
|
prefix => "osd pool get",
|
|
pool => "$pool",
|
|
var => "all",
|
|
format => 'json',
|
|
};
|
|
return $rados->mon_command($command);
|
|
}
|
|
|
|
sub get_pool_type {
|
|
my ($pool, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
return 'erasure' if get_pool_properties($pool, $rados)->{erasure_code_profile};
|
|
return 'replicated';
|
|
}
|
|
|
|
sub create_pool {
|
|
my ($pool, $param, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
|
|
my $pg_num = $param->{pg_num} || 128;
|
|
|
|
my $mon_params = {
|
|
prefix => "osd pool create",
|
|
pool => $pool,
|
|
pg_num => int($pg_num),
|
|
format => 'plain',
|
|
};
|
|
$mon_params->{pool_type} = extract_param($param, 'pool_type') if $param->{pool_type};
|
|
$mon_params->{erasure_code_profile} = extract_param($param, 'erasure_code_profile')
|
|
if $param->{erasure_code_profile};
|
|
|
|
$rados->mon_command($mon_params);
|
|
|
|
set_pool($pool, $param);
|
|
|
|
}
|
|
|
|
sub ls_pools {
|
|
my ($pool, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
|
|
my $res = $rados->mon_command({ prefix => "osd lspools" });
|
|
|
|
return $res;
|
|
}
|
|
|
|
sub destroy_pool {
|
|
my ($pool, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
|
|
# fixme: '--yes-i-really-really-mean-it'
|
|
$rados->mon_command({
|
|
prefix => "osd pool delete",
|
|
pool => $pool,
|
|
pool2 => $pool,
|
|
'yes_i_really_really_mean_it' => JSON::true,
|
|
format => 'plain',
|
|
});
|
|
}
|
|
|
|
# we get something like:
|
|
#[{
|
|
# 'metadata_pool_id' => 2,
|
|
# 'data_pool_ids' => [ 1 ],
|
|
# 'metadata_pool' => 'cephfs_metadata',
|
|
# 'data_pools' => [ 'cephfs_data' ],
|
|
# 'name' => 'cephfs',
|
|
#}]
|
|
sub ls_fs {
|
|
my ($rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
|
|
my $res = $rados->mon_command({ prefix => "fs ls" });
|
|
|
|
return $res;
|
|
}
|
|
|
|
sub create_fs {
|
|
my ($fs, $param, $rados) = @_;
|
|
|
|
if (!defined($rados)) {
|
|
$rados = PVE::RADOS->new();
|
|
}
|
|
|
|
$rados->mon_command({
|
|
prefix => "fs new",
|
|
fs_name => $fs,
|
|
metadata => $param->{pool_metadata},
|
|
data => $param->{pool_data},
|
|
format => 'plain',
|
|
});
|
|
}
|
|
|
|
sub destroy_fs {
|
|
my ($fs, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !defined($rados);
|
|
|
|
$rados->mon_command({
|
|
prefix => "fs rm",
|
|
fs_name => $fs,
|
|
'yes_i_really_mean_it' => JSON::true,
|
|
format => 'plain',
|
|
});
|
|
}
|
|
|
|
sub setup_pve_symlinks {
|
|
# fail if we find a real file instead of a link
|
|
if (-f $ceph_cfgpath) {
|
|
my $lnk = readlink($ceph_cfgpath);
|
|
die "file '$ceph_cfgpath' already exists and is not a symlink to $pve_ceph_cfgpath\n"
|
|
if !$lnk || $lnk ne $pve_ceph_cfgpath;
|
|
} else {
|
|
mkdir $ceph_cfgdir;
|
|
symlink($pve_ceph_cfgpath, $ceph_cfgpath) ||
|
|
die "unable to create symlink '$ceph_cfgpath' - $!\n";
|
|
}
|
|
my $ceph_uid = getpwnam('ceph');
|
|
my $ceph_gid = getgrnam('ceph');
|
|
chown $ceph_uid, $ceph_gid, $ceph_cfgdir;
|
|
}
|
|
|
|
sub get_or_create_admin_keyring {
|
|
if (! -f $pve_ckeyring_path) {
|
|
run_command("ceph-authtool --create-keyring $pve_ckeyring_path " .
|
|
"--gen-key -n client.admin " .
|
|
"--cap mon 'allow *' " .
|
|
"--cap osd 'allow *' " .
|
|
"--cap mds 'allow *' " .
|
|
"--cap mgr 'allow *' ");
|
|
# we do not want to overwrite it
|
|
if (! -f $ckeyring_path) {
|
|
run_command("cp $pve_ckeyring_path $ckeyring_path");
|
|
run_command("chown ceph:ceph $ckeyring_path");
|
|
}
|
|
}
|
|
return $pve_ckeyring_path;
|
|
}
|
|
|
|
# is also used in `pve-init-ceph-crash` helper
|
|
sub create_or_update_crash_keyring_file {
|
|
my ($rados) = @_;
|
|
|
|
if (!defined($rados)) {
|
|
$rados = PVE::RADOS->new();
|
|
}
|
|
|
|
my $output = $rados->mon_command({
|
|
prefix => 'auth get-or-create',
|
|
entity => 'client.crash',
|
|
caps => [
|
|
mon => 'profile crash',
|
|
mgr => 'profile crash',
|
|
],
|
|
format => 'plain',
|
|
});
|
|
|
|
if (-f $pve_ceph_crash_key_path) {
|
|
my $contents = PVE::Tools::file_get_contents($pve_ceph_crash_key_path);
|
|
|
|
if ($contents ne $output) {
|
|
PVE::Tools::file_set_contents($pve_ceph_crash_key_path, $output);
|
|
return 1;
|
|
}
|
|
} else {
|
|
PVE::Tools::file_set_contents($pve_ceph_crash_key_path, $output);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
# get ceph-volume managed osds
|
|
sub ceph_volume_list {
|
|
my $result = {};
|
|
|
|
if (!check_ceph_installed('ceph_volume', 1)) {
|
|
return $result;
|
|
}
|
|
|
|
my $output = '';
|
|
my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'list', '--format', 'json' ];
|
|
run_command($cmd, outfunc => sub { $output .= shift });
|
|
|
|
$result = eval { decode_json($output) };
|
|
warn $@ if $@;
|
|
return $result;
|
|
}
|
|
|
|
sub ceph_volume_zap {
|
|
my ($osdid, $destroy) = @_;
|
|
|
|
die "no osdid given\n" if !defined($osdid);
|
|
|
|
my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'zap', '--osd-id', $osdid ];
|
|
push @$cmd, '--destroy' if $destroy;
|
|
|
|
run_command($cmd);
|
|
}
|
|
|
|
sub get_db_wal_sizes {
|
|
my $res = {};
|
|
|
|
my $rados = PVE::RADOS->new();
|
|
my $db_config = $rados->mon_command({ prefix => 'config-key dump', key => 'config/' });
|
|
|
|
$res->{db} = $db_config->{"config/osd/bluestore_block_db_size"} //
|
|
$db_config->{"config/global/bluestore_block_db_size"};
|
|
|
|
$res->{wal} = $db_config->{"config/osd/bluestore_block_wal_size"} //
|
|
$db_config->{"config/global/bluestore_block_wal_size"};
|
|
|
|
if (!$res->{db} || !$res->{wal}) {
|
|
my $cfg = cfs_read_file('ceph.conf');
|
|
if (!$res->{db}) {
|
|
$res->{db} = $cfg->{osd}->{bluestore_block_db_size} //
|
|
$cfg->{global}->{bluestore_block_db_size};
|
|
}
|
|
|
|
if (!$res->{wal}) {
|
|
$res->{wal} = $cfg->{osd}->{bluestore_block_wal_size} //
|
|
$cfg->{global}->{bluestore_block_wal_size};
|
|
}
|
|
}
|
|
|
|
return $res;
|
|
}
|
|
sub get_possible_osd_flags {
|
|
my $possible_flags = {
|
|
pause => {
|
|
description => 'Pauses read and writes.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
noup => {
|
|
description => 'OSDs are not allowed to start.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
nodown => {
|
|
description => 'OSD failure reports are being ignored, such that the monitors will not mark OSDs down.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
noout => {
|
|
description => 'OSDs will not automatically be marked out after the configured interval.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
noin => {
|
|
description => 'OSDs that were previously marked out will not be marked back in when they start.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
nobackfill => {
|
|
description => 'Backfilling of PGs is suspended.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
norebalance => {
|
|
description => 'Rebalancing of PGs is suspended.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
norecover => {
|
|
description => 'Recovery of PGs is suspended.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
noscrub => {
|
|
description => 'Scrubbing is disabled.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
'nodeep-scrub' => {
|
|
description => 'Deep Scrubbing is disabled.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
notieragent => {
|
|
description => 'Cache tiering activity is suspended.',
|
|
type => 'boolean',
|
|
optional=> 1,
|
|
},
|
|
};
|
|
return $possible_flags;
|
|
}
|
|
|
|
sub get_real_flag_name {
|
|
my ($flag) = @_;
|
|
|
|
# the 'pause' flag gets always set to both 'pauserd' and 'pausewr'
|
|
# so decide that the 'pause' flag is set if we detect 'pauserd'
|
|
my $flagmap = {
|
|
'pause' => 'pauserd',
|
|
};
|
|
|
|
return $flagmap->{$flag} // $flag;
|
|
}
|
|
|
|
sub ceph_cluster_status {
|
|
my ($rados) = @_;
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
|
|
my $status = $rados->mon_command({ prefix => 'status' });
|
|
$status->{health} = $rados->mon_command({ prefix => 'health', detail => 'detail' });
|
|
|
|
if (!exists $status->{monmap}->{mons}) { # octopus moved most info out of status, re-add
|
|
$status->{monmap} = $rados->mon_command({ prefix => 'mon dump' });
|
|
$status->{mgrmap} = $rados->mon_command({ prefix => 'mgr dump' });
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
sub ecprofile_exists {
|
|
my ($name, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
|
|
my $res = $rados->mon_command({ prefix => 'osd erasure-code-profile ls' });
|
|
|
|
my $profiles = { map { $_ => 1 } @$res };
|
|
return $profiles->{$name};
|
|
}
|
|
|
|
sub create_ecprofile {
|
|
my ($name, $k, $m, $failure_domain, $device_class, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
|
|
$failure_domain = 'host' if !$failure_domain;
|
|
|
|
my $profile = [
|
|
"crush-failure-domain=${failure_domain}",
|
|
"k=${k}",
|
|
"m=${m}",
|
|
];
|
|
|
|
push(@$profile, "crush-device-class=${device_class}") if $device_class;
|
|
|
|
$rados->mon_command({
|
|
prefix => 'osd erasure-code-profile set',
|
|
name => $name,
|
|
profile => $profile,
|
|
});
|
|
}
|
|
|
|
sub destroy_ecprofile {
|
|
my ($profile, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
|
|
my $command = {
|
|
prefix => 'osd erasure-code-profile rm',
|
|
name => $profile,
|
|
format => 'plain',
|
|
};
|
|
return $rados->mon_command($command);
|
|
}
|
|
|
|
sub get_ecprofile_name {
|
|
my ($name) = @_;
|
|
return "pve_ec_${name}";
|
|
}
|
|
|
|
sub destroy_crush_rule {
|
|
my ($rule, $rados) = @_;
|
|
$rados = PVE::RADOS->new() if !$rados;
|
|
|
|
my $command = {
|
|
prefix => 'osd crush rule rm',
|
|
name => $rule,
|
|
format => 'plain',
|
|
};
|
|
return $rados->mon_command($command);
|
|
}
|
|
|
|
1;
|