pve-manager/PVE/Ceph/Services.pm
Dominik Csapak e7e615768f ceph: services: do not create rados object in get_services_info
we always gave one, and the only reason why it could be undef
is that we could not connect, so it makes no sense to try again
and add unecessary time to the api call

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2019-06-18 16:17:35 +02:00

364 lines
8.9 KiB
Perl

package PVE::Ceph::Services;
use strict;
use warnings;
use PVE::Ceph::Tools;
use PVE::Cluster qw(cfs_read_file);
use PVE::Tools qw(run_command);
use PVE::RADOS;
use JSON;
use File::Path;
# checks /etc/systemd/system/ceph-* to list all services, even if not running
# also checks /var/lib/ceph/$type
sub get_local_services {
my $res = {};
for my $type (qw(mds mgr mon)) {
$res->{$type} = {};
my $path = "/etc/systemd/system/ceph-$type.target.wants";
my $regex = "ceph-$type\@(.*)\.service";
PVE::Tools::dir_glob_foreach($path, $regex, sub {
my (undef, $id) = @_;
$res->{$type}->{$id}->{service} = 1;
});
$path = "/var/lib/ceph/$type";
$regex = "([^-]+)-(.*)";
PVE::Tools::dir_glob_foreach($path, $regex, sub {
my (undef, $clustername, $id) = @_;
$res->{$type}->{$id}->{direxists} = 1;
});
}
return $res;
}
sub broadcast_ceph_services {
my $services = get_local_services();
for my $type (keys %$services) {
my $data = encode_json($services->{$type});
PVE::Cluster::broadcast_node_kv("ceph-$type", $data);
}
}
sub get_cluster_service {
my ($type) = @_;
my $raw = PVE::Cluster::get_node_kv("ceph-$type");
my $res = {};
for my $host (keys %$raw) {
$res->{$host} = eval { decode_json($raw->{$host}) };
}
return $res;
}
sub ceph_service_cmd {
my ($action, $service) = @_;
my $pve_ceph_cfgpath = PVE::Ceph::Tools::get_config('pve_ceph_cfgpath');
if ($service && $service =~ m/^(mon|osd|mds|mgr|radosgw)(\.([A-Za-z0-9\-]{1,32}))?$/) {
$service = defined($3) ? "ceph-$1\@$3" : "ceph-$1.target";
} else {
$service = "ceph.target";
}
run_command(['/bin/systemctl', $action, $service]);
}
sub get_services_info {
my ($type, $cfg, $rados) = @_;
my $result = {};
my $services = get_cluster_service($type);
foreach my $host (sort keys %$services) {
foreach my $id (sort keys %{$services->{$host}}) {
my $service = $result->{$id} = $services->{$host}->{$id};
$service->{host} = $host;
$service->{name} = $id;
$service->{state} = 'unknown';
if ($service->{service}) {
$service->{state} = 'stopped';
}
}
}
if (!$cfg) {
$cfg = cfs_read_file('ceph.conf');
}
foreach my $section (keys %$cfg) {
my $d = $cfg->{$section};
if ($section =~ m/^$type\.(\S+)$/) {
my $id = $1;
my $service = $result->{$id};
my $addr = $d->{"$type addr"} // $d->{"${type}_addr"} // $d->{host};
$service->{name} //= $id;
$service->{addr} //= $addr;
$service->{state} //= 'unknown';
$service->{host} //= $d->{host};
}
}
if (!$rados) {
return $result;
}
my $metadata = $rados->mon_command({ prefix => "$type metadata" });
foreach my $info (@$metadata) {
my $id = $info->{name} // $info->{id};
my $service = $result->{$id};
$service->{ceph_version_short} = $info->{ceph_version_short};
$service->{ceph_version} = $info->{ceph_version};
$service->{host} //= $info->{hostname};
$service->{addr} //= $info->{addr};
}
return $result;
}
# MDS
sub list_local_mds_ids {
my $mds_list = [];
my $ceph_mds_data_dir = PVE::Ceph::Tools::get_config('ceph_mds_data_dir');
my $ccname = PVE::Ceph::Tools::get_config('ccname');
PVE::Tools::dir_glob_foreach($ceph_mds_data_dir, qr/$ccname-(\S+)/, sub {
my (undef, $mds_id) = @_;
push @$mds_list, $mds_id;
});
return $mds_list;
}
sub get_cluster_mds_state {
my ($rados) = @_;
my $mds_state = {};
if (!defined($rados)) {
$rados = PVE::RADOS->new();
}
my $add_state = sub {
my ($mds) = @_;
my $state = {};
$state->{addr} = $mds->{addr};
$state->{rank} = $mds->{rank};
$state->{standby_replay} = $mds->{standby_replay} ? 1 : 0;
$state->{state} = $mds->{state};
$mds_state->{$mds->{name}} = $state;
};
my $mds_dump = $rados->mon_command({ prefix => 'mds stat' });
my $fsmap = $mds_dump->{fsmap};
foreach my $mds (@{$fsmap->{standbys}}) {
$add_state->($mds);
}
my $fs_info = $fsmap->{filesystems}->[0];
my $active_mds = $fs_info->{mdsmap}->{info};
# normally there's only one active MDS, but we can have multiple active for
# different ranks (e.g., different cephs path hierarchy). So just add all.
foreach my $mds (values %$active_mds) {
$add_state->($mds);
}
return $mds_state;
}
sub is_any_mds_active {
my ($rados) = @_;
if (!defined($rados)) {
$rados = PVE::RADOS->new();
}
my $mds_dump = $rados->mon_command({ prefix => 'mds stat' });
my $fs = $mds_dump->{fsmap}->{filesystems};
if (!($fs && scalar(@$fs) > 0)) {
return undef;
}
my $active_mds = $fs->[0]->{mdsmap}->{info};
for my $mds (values %$active_mds) {
return 1 if $mds->{state} eq 'up:active';
}
return 0;
}
sub create_mds {
my ($id, $rados) = @_;
# `ceph fs status` fails with numeric only ID.
die "ID: $id, numeric only IDs are not supported\n"
if $id =~ /^\d+$/;
if (!defined($rados)) {
$rados = PVE::RADOS->new();
}
my $ccname = PVE::Ceph::Tools::get_config('ccname');
my $service_dir = "/var/lib/ceph/mds/$ccname-$id";
my $service_keyring = "$service_dir/keyring";
my $service_name = "mds.$id";
die "ceph MDS directory '$service_dir' already exists\n"
if -d $service_dir;
print "creating MDS directory '$service_dir'\n";
eval { File::Path::mkpath($service_dir) };
my $err = $@;
die "creation MDS directory '$service_dir' failed\n" if $err;
# http://docs.ceph.com/docs/luminous/install/manual-deployment/#adding-mds
my $priv = [
mon => 'allow profile mds',
osd => 'allow rwx',
mds => 'allow *',
];
print "creating keys for '$service_name'\n";
my $output = $rados->mon_command({
prefix => 'auth get-or-create',
entity => $service_name,
caps => $priv,
format => 'plain',
});
PVE::Tools::file_set_contents($service_keyring, $output);
print "setting ceph as owner for service directory\n";
run_command(["chown", 'ceph:ceph', '-R', $service_dir]);
print "enabling service 'ceph-mds\@$id.service'\n";
ceph_service_cmd('enable', $service_name);
print "starting service 'ceph-mds\@$id.service'\n";
ceph_service_cmd('start', $service_name);
broadcast_ceph_services();
return undef;
};
sub destroy_mds {
my ($id, $rados) = @_;
if (!defined($rados)) {
$rados = PVE::RADOS->new();
}
my $ccname = PVE::Ceph::Tools::get_config('ccname');
my $service_name = "mds.$id";
my $service_dir = "/var/lib/ceph/mds/$ccname-$id";
print "disabling service 'ceph-mds\@$id.service'\n";
ceph_service_cmd('disable', $service_name);
print "stopping service 'ceph-mds\@$id.service'\n";
ceph_service_cmd('stop', $service_name);
if (-d $service_dir) {
print "removing ceph-mds directory '$service_dir'\n";
File::Path::remove_tree($service_dir);
} else {
warn "cannot cleanup MDS $id directory, '$service_dir' not found\n"
}
print "removing ceph auth for '$service_name'\n";
$rados->mon_command({
prefix => 'auth del',
entity => $service_name,
format => 'plain'
});
broadcast_ceph_services();
return undef;
};
# MGR
sub create_mgr {
my ($id, $rados) = @_;
my $clustername = PVE::Ceph::Tools::get_config('ccname');
my $mgrdir = "/var/lib/ceph/mgr/$clustername-$id";
my $mgrkeyring = "$mgrdir/keyring";
my $mgrname = "mgr.$id";
die "ceph manager directory '$mgrdir' already exists\n"
if -d $mgrdir;
print "creating manager directory '$mgrdir'\n";
mkdir $mgrdir;
print "creating keys for '$mgrname'\n";
my $output = $rados->mon_command({ prefix => 'auth get-or-create',
entity => $mgrname,
caps => [
mon => 'allow profile mgr',
osd => 'allow *',
mds => 'allow *',
],
format => 'plain'});
PVE::Tools::file_set_contents($mgrkeyring, $output);
print "setting owner for directory\n";
run_command(["chown", 'ceph:ceph', '-R', $mgrdir]);
print "enabling service 'ceph-mgr\@$id.service'\n";
ceph_service_cmd('enable', $mgrname);
print "starting service 'ceph-mgr\@$id.service'\n";
ceph_service_cmd('start', $mgrname);
broadcast_ceph_services();
return undef;
}
sub destroy_mgr {
my ($mgrid, $rados) = @_;
my $clustername = PVE::Ceph::Tools::get_config('ccname');
my $mgrname = "mgr.$mgrid";
my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
die "ceph manager directory '$mgrdir' not found\n"
if ! -d $mgrdir;
print "disabling service 'ceph-mgr\@$mgrid.service'\n";
ceph_service_cmd('disable', $mgrname);
print "stopping service 'ceph-mgr\@$mgrid.service'\n";
ceph_service_cmd('stop', $mgrname);
print "removing manager directory '$mgrdir'\n";
File::Path::remove_tree($mgrdir);
print "removing authkeys for $mgrname\n";
if (!$rados) {
$rados = PVE::RADOS->new();
}
$rados->mon_command({ prefix => 'auth del', entity => "$mgrname" });
broadcast_ceph_services();
return undef;
}
1;