pve-manager/PVE/API2/Ceph/MDS.pm
Maximiliano Sandoval ab70343982 fix #4808: ceph: mds create: use snake_case when setting options
As suggested in [1], it is recommended to use `_` in all cases when
dealing with config files. Note that this is for creation only, and we
enforce that there cannot be an existing MDS with the same ID, so we
do not have to bother how ceph would handle the case where both exist.

[1] https://docs.ceph.com/en/reef/rados/configuration/ceph-conf/#option-names

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-06 16:57:10 +02:00

234 lines
5.7 KiB
Perl

package PVE::API2::Ceph::MDS;
use strict;
use warnings;
use PVE::Ceph::Tools;
use PVE::Ceph::Services;
use PVE::Cluster qw(cfs_read_file cfs_write_file);
use PVE::INotify;
use PVE::JSONSchema qw(get_standard_option);
use PVE::RADOS;
use PVE::RESTHandler;
use PVE::RPCEnvironment;
use base qw(PVE::RESTHandler);
__PACKAGE__->register_method ({
name => 'index',
path => '',
method => 'GET',
description => "MDS directory index.",
permissions => {
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
},
proxyto => 'node',
protected => 1,
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
name => {
description => "The name (ID) for the MDS",
},
addr => {
type => 'string',
optional => 1,
},
host => {
type => 'string',
optional => 1,
},
state => {
type => 'string',
description => 'State of the MDS',
},
standby_replay => {
type => 'boolean',
optional => 1,
description => 'If true, the standby MDS is polling the active MDS for faster recovery (hot standby).',
},
rank => {
type => 'integer',
optional => 1,
},
},
},
links => [ { rel => 'child', href => "{name}" } ],
},
code => sub {
my ($param) = @_;
PVE::Ceph::Tools::check_ceph_inited();
my $res = [];
my $cfg = cfs_read_file('ceph.conf');
my $rados = PVE::RADOS->new();
my $mds_hash = PVE::Ceph::Services::get_services_info("mds", $cfg, $rados);
my $mds_state = PVE::Ceph::Services::get_cluster_mds_state($rados);
foreach my $name (keys %$mds_state) {
my $d = $mds_state->{$name};
# just overwrite, this always provides more info
$mds_hash->{$name}->{$_} = $d->{$_} for keys %$d;
}
return PVE::RESTHandler::hash_to_array($mds_hash, 'name');
}
});
__PACKAGE__->register_method ({
name => 'createmds',
path => '{name}',
method => 'POST',
description => "Create Ceph Metadata Server (MDS)",
proxyto => 'node',
protected => 1,
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
name => {
type => 'string',
optional => 1,
default => 'nodename',
pattern => PVE::Ceph::Services::SERVICE_REGEX,
maxLength => 200,
description => "The ID for the mds, when omitted the same as the nodename",
},
hotstandby => {
type => 'boolean',
optional => 1,
default => '0',
description => "Determines whether a ceph-mds daemon should poll and replay the log of an active MDS. ".
"Faster switch on MDS failure, but needs more idle resources.",
},
},
},
returns => { type => 'string' },
code => sub {
my ($param) = @_;
PVE::Ceph::Tools::check_ceph_installed('ceph_mds');
PVE::Ceph::Tools::check_ceph_inited();
my $rpcenv = PVE::RPCEnvironment::get();
my $authuser = $rpcenv->get_user();
my $nodename = $param->{node};
$nodename = INotify::nodename() if $nodename eq 'localhost';
my $mds_id = $param->{name} // $nodename;
my $worker = sub {
my $timeout = PVE::Ceph::Tools::get_config('long_rados_timeout');
my $rados = PVE::RADOS->new(timeout => $timeout);
my $cfg = cfs_read_file('ceph.conf');
my $section = "mds.$mds_id";
if (defined($cfg->{$section})) {
die "MDS '$mds_id' already referenced in ceph config, abort!\n"
}
if (!defined($cfg->{mds}->{keyring})) {
# $id isn't a perl variable but a ceph metavariable
my $keyring = '/var/lib/ceph/mds/ceph-$id/keyring';
$cfg->{mds}->{keyring} = $keyring;
}
$cfg->{$section}->{host} = $nodename;
$cfg->{$section}->{'mds_standby_for_name'} = 'pve';
if ($param->{hotstandby}) {
$cfg->{$section}->{'mds_standby_replay'} = 'true';
}
cfs_write_file('ceph.conf', $cfg);
eval { PVE::Ceph::Services::create_mds($mds_id, $rados) };
if (my $err = $@) {
# we abort early if the section is defined, so we know that we
# wrote it at this point. Do not auto remove the service, could
# do real harm for previously manual setup MDS
warn "Encountered error, remove '$section' from ceph.conf\n";
my $cfg = cfs_read_file('ceph.conf');
delete $cfg->{$section};
cfs_write_file('ceph.conf', $cfg);
die "$err\n";
}
};
return $rpcenv->fork_worker('cephcreatemds', "mds.$mds_id", $authuser, $worker);
}
});
__PACKAGE__->register_method ({
name => 'destroymds',
path => '{name}',
method => 'DELETE',
description => "Destroy Ceph Metadata Server",
proxyto => 'node',
protected => 1,
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
name => {
description => 'The name (ID) of the mds',
type => 'string',
pattern => PVE::Ceph::Services::SERVICE_REGEX,
},
},
},
returns => { type => 'string' },
code => sub {
my ($param) = @_;
my $rpcenv = PVE::RPCEnvironment::get();
my $authuser = $rpcenv->get_user();
PVE::Ceph::Tools::check_ceph_inited();
my $mds_id = $param->{name};
my $worker = sub {
my $timeout = PVE::Ceph::Tools::get_config('long_rados_timeout');
my $rados = PVE::RADOS->new(timeout => $timeout);
my $cfg = cfs_read_file('ceph.conf');
if (defined($cfg->{"mds.$mds_id"})) {
delete $cfg->{"mds.$mds_id"};
cfs_write_file('ceph.conf', $cfg);
}
PVE::Ceph::Services::destroy_mds($mds_id, $rados);
};
return $rpcenv->fork_worker('cephdestroymds', "mds.$mds_id", $authuser, $worker);
}
});
1;