diff --git a/PVE/API2/Ceph/MON.pm b/PVE/API2/Ceph/MON.pm index 00b8f503..60c8d8dd 100644 --- a/PVE/API2/Ceph/MON.pm +++ b/PVE/API2/Ceph/MON.pm @@ -451,6 +451,14 @@ __PACKAGE__->register_method ({ ) }; warn "$@" if $@; + + print "Configuring keyring for ceph-crash.service\n"; + eval { + PVE::Ceph::Tools::create_or_update_crash_keyring_file(); + $cfg->{'client.crash'}->{keyring} = '/etc/pve/ceph/$cluster.$name.keyring'; + cfs_write_file('ceph.conf', $cfg); + }; + warn "Unable to configure keyring for ceph-crash.service: $@" if $@; } eval { PVE::Ceph::Services::ceph_service_cmd('enable', $monsection) }; diff --git a/PVE/Ceph/Tools.pm b/PVE/Ceph/Tools.pm index 735bb116..9b97171e 100644 --- a/PVE/Ceph/Tools.pm +++ b/PVE/Ceph/Tools.pm @@ -20,6 +20,7 @@ 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"; @@ -45,6 +46,7 @@ my $config_values = { 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, @@ -420,6 +422,39 @@ sub get_or_create_admin_keyring { 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 = {}; diff --git a/bin/Makefile b/bin/Makefile index 06d8148e..b221e4b1 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -83,6 +83,7 @@ install: $(SCRIPTS) $(CLI_MANS) $(SERVICE_MANS) $(BASH_COMPLETIONS) $(ZSH_COMPLE install -m 0755 $(SCRIPTS) $(BINDIR) install -d $(USRSHARE)/helpers install -m 0755 pve-startall-delay $(USRSHARE)/helpers + install -m 0755 pve-init-ceph-crash $(USRSHARE)/helpers install -d $(MAN1DIR) install -m 0644 $(CLI_MANS) $(MAN1DIR) install -d $(MAN8DIR) diff --git a/bin/pve-init-ceph-crash b/bin/pve-init-ceph-crash new file mode 100755 index 00000000..89170b2e --- /dev/null +++ b/bin/pve-init-ceph-crash @@ -0,0 +1,142 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use List::Util qw(first); + +use PVE::Ceph::Tools; +use PVE::Cluster; +use PVE::RADOS; +use PVE::RPCEnvironment; + +my $ceph_cfg_file = 'ceph.conf'; +my $keyring_value = '/etc/pve/ceph/$cluster.$name.keyring'; + + +sub try_adapt_cfg { + my ($cfg) = @_; + + my $entity = 'client.crash'; + my $removed_key = 0; + + print("Checking whether the configuration for '$entity' needs to be updated.\n"); + + my $add_keyring = sub { + print("Setting keyring path to '$keyring_value'.\n"); + $cfg->{$entity}->{keyring} = $keyring_value; + }; + + if (!exists($cfg->{$entity})) { + print("Adding missing section for '$entity'.\n"); + $add_keyring->(); + return 1; + } + + if (exists($cfg->{$entity}->{key})) { + print("Removing existing usage of key.\n"); + delete($cfg->{$entity}->{key}); + $removed_key = 1; + } + + if (!exists($cfg->{$entity}->{keyring})) { + print("Keyring path is missing from configuration.\n"); + $add_keyring->(); + return 1; + } + + my $current_keyring_value = $cfg->{$entity}->{keyring}; + if ($current_keyring_value ne $keyring_value) { + print("Current keyring path differs from expected path.\n"); + $add_keyring->(); + return 1; + } + + return $removed_key; +} + + +sub main { + # PVE::RADOS expects an active RPC Environment because it forks itself + # and may want to clean up after + my $rpcenv = PVE::RPCEnvironment->setup_default_cli_env(); + + if (!PVE::Ceph::Tools::check_ceph_installed('ceph_bin', 1)) { + print("Ceph is not installed. No action required.\n"); + exit 0; + } + + eval { + PVE::Ceph::Tools::check_ceph_inited(); + }; + if ($@) { + print("Ceph is not initialized. No action required.\n"); + exit 0; + } + + my $rados = eval { PVE::RADOS->new() }; + my $ceph_crash_key_path = PVE::Ceph::Tools::get_config('pve_ceph_crash_key_path'); + + my $inner_err = ''; + + my $rval = PVE::Cluster::cfs_lock_file($ceph_cfg_file, undef, sub { + eval { + my $cfg = PVE::Cluster::cfs_read_file($ceph_cfg_file); + + if (!defined($rados)) { + my $has_mon_host = defined($cfg->{global}) && defined($cfg->{global}->{mon_host}); + if ($has_mon_host && $cfg->{global}->{mon_host} ne '') { + die "Connection to RADOS failed even though a monitor is configured.\n" . + "Please verify whether your configuration in '$ceph_cfg_file' is correct.\n" + } + + print( + "Connection to RADOS failed and no monitor is configured in '$ceph_cfg_file'.\n". + "Assuming that things are fine. No action required.\n" + ); + return; + } + + my $updated_keyring = PVE::Ceph::Tools::create_or_update_crash_keyring_file($rados); + + if ($updated_keyring) { + print("Keyring file '$ceph_crash_key_path' was updated.\n"); + } + + my $changed = try_adapt_cfg($cfg); + + if ($changed) { + print("Committing updated configuration to '$ceph_cfg_file'.\n"); + PVE::Cluster::cfs_write_file($ceph_cfg_file, $cfg); + print("Successfully updated configuration for 'ceph-crash.service'.\n"); + } else { + print("Configuration in '$ceph_cfg_file' does not need to be updated.\n"); + } + }; + $inner_err = $@; + + return 1; + }); + + # cfs_lock_file sets $@ explicitly to undef + my $err = $@ // ''; + + my $has_err = !defined($rval) || $inner_err || $err; + + if ($has_err) { + $err =~ s/\n*$//; + $inner_err =~ s/\n*$//; + + if (!defined($rval)) { + warn("Error while acquiring or releasing lock for '$ceph_cfg_file'.\n"); + warn("Error: $err\n") if $err ne ''; + } + + warn("Failed to configure keyring for 'ceph-crash.service'.\nError: $inner_err\n") + if $inner_err ne ''; + + exit 1; + } +} + +main(); diff --git a/debian/postinst b/debian/postinst index 58f2713f..2bd71459 100755 --- a/debian/postinst +++ b/debian/postinst @@ -82,10 +82,23 @@ EOF update_ceph_conf() { CEPH_CONF_DIR='/etc/pve/ceph' + UNIT='ceph-crash.service' + + echo "" if test ! -d "${CEPH_CONF_DIR}"; then mkdir -p "${CEPH_CONF_DIR}" fi + + # Don't fail in case user has "exotic" configuration where RADOS + # isn't available on all nodes for some reason + /usr/share/pve-manager/helpers/pve-init-ceph-crash || true + + if systemctl -q is-enabled "$UNIT" 2> /dev/null; then + deb-systemd-invoke restart "$UNIT" || true + fi + + echo "" } migrate_apt_auth_conf() {