As we mount this manually and thus systemd doesn't know about any dependency for cephFS mounts, this got umounted only at the last stage of shutdown, where network wasn't active anymore. But, CephFS needs to be connected to an active MDS for a clean unmount so without network this mount would delay shutdown for quite a bit, until after some minutes systemd gave up and forced unmount. So tell systemd that this mount requires network, which can be done with the '_netdev'[0] mount option, that lucky for us can be also passed to a mount call and isn't only available for fstab. with this a mount gets, among others: > Wants=network-online.target > Before=umount.target remote-fs.target > After=remote-fs-pre.target system.slice network.target network-online.target -.mount Which does the trick for us. [0]: https://www.freedesktop.org/software/systemd/man/systemd.mount.html#_netdev Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
193 lines
5.3 KiB
Perl
193 lines
5.3 KiB
Perl
package PVE::Storage::CephFSPlugin;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use IO::File;
|
|
use Net::IP;
|
|
use File::Path;
|
|
|
|
use PVE::Tools qw(run_command);
|
|
use PVE::ProcFSTools;
|
|
use PVE::Storage::Plugin;
|
|
use PVE::JSONSchema qw(get_standard_option);
|
|
use PVE::Storage::CephTools;
|
|
|
|
use base qw(PVE::Storage::Plugin);
|
|
|
|
sub cephfs_is_mounted {
|
|
my ($scfg, $storeid, $mountdata) = @_;
|
|
|
|
my $cmd_option = PVE::Storage::CephTools::ceph_connect_option($scfg, $storeid);
|
|
my $configfile = $cmd_option->{ceph_conf};
|
|
my $server = $cmd_option->{mon_host} // PVE::Storage::CephTools::get_monaddr_list($configfile);
|
|
|
|
my $subdir = $scfg->{subdir} // '/';
|
|
my $mountpoint = $scfg->{path};
|
|
my $source = "$server:$subdir";
|
|
|
|
$mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata;
|
|
return $mountpoint if grep {
|
|
$_->[2] =~ m#^ceph|fuse\.ceph-fuse# &&
|
|
$_->[0] =~ m#^\Q$source\E|ceph-fuse$# &&
|
|
$_->[1] eq $mountpoint
|
|
} @$mountdata;
|
|
|
|
warn "A filesystem is already mounted on $mountpoint\n"
|
|
if grep { $_->[1] eq $mountpoint } @$mountdata;
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub cephfs_mount {
|
|
my ($scfg, $storeid) = @_;
|
|
|
|
my $cmd;
|
|
my $mountpoint = $scfg->{path};
|
|
my $subdir = $scfg->{subdir} // '/';
|
|
|
|
my $cmd_option = PVE::Storage::CephTools::ceph_connect_option($scfg, $storeid);
|
|
my $configfile = $cmd_option->{ceph_conf};
|
|
my $secretfile = $cmd_option->{keyring};
|
|
my $server = $cmd_option->{mon_host} // PVE::Storage::CephTools::get_monaddr_list($configfile);
|
|
|
|
# fuse -> client-enforced quotas (kernel doesn't), updates w/ ceph-fuse pkg
|
|
# kernel -> better performance, less frequent updates
|
|
if ($scfg->{fuse}) {
|
|
# FIXME: ceph-fuse client complains about missing ceph.conf or keyring if
|
|
# not provided on its default locations but still connects. Fix upstream??
|
|
$cmd = ['/usr/bin/ceph-fuse', '-n', "client.$cmd_option->{userid}", '-m', $server];
|
|
push @$cmd, '--keyfile', $secretfile if defined($secretfile);
|
|
push @$cmd, '-r', $subdir if !($subdir =~ m|^/$|);
|
|
push @$cmd, $mountpoint;
|
|
push @$cmd, '--conf', $configfile if defined($configfile);
|
|
} else {
|
|
my $source = "$server:$subdir";
|
|
$cmd = ['/bin/mount', '-t', 'ceph', $source, $mountpoint, '-o', "name=$cmd_option->{userid}"];
|
|
push @$cmd, '-o', "secretfile=$secretfile" if defined($secretfile);
|
|
}
|
|
# tell systemd that we're network dependent, else it umounts us to late on
|
|
# shutdown, when we couldn't connect to the active MDS and thus unmount
|
|
# hangs and delays shutdown/reboot (man systemd.mount)
|
|
push @$cmd, '-o', '_netdev';
|
|
|
|
if ($scfg->{options}) {
|
|
push @$cmd, '-o', $scfg->{options};
|
|
}
|
|
|
|
run_command($cmd, errmsg => "mount error");
|
|
}
|
|
|
|
# Configuration
|
|
|
|
sub type {
|
|
return 'cephfs';
|
|
}
|
|
|
|
sub plugindata {
|
|
return {
|
|
content => [ { vztmpl => 1, iso => 1, backup => 1},
|
|
{ backup => 1 }],
|
|
};
|
|
}
|
|
|
|
sub properties {
|
|
return {
|
|
fuse => {
|
|
description => "Mount CephFS through FUSE.",
|
|
type => 'boolean',
|
|
},
|
|
subdir => {
|
|
description => "Subdir to mount.",
|
|
type => 'string', format => 'pve-storage-path',
|
|
},
|
|
};
|
|
}
|
|
|
|
sub options {
|
|
return {
|
|
path => { fixed => 1 },
|
|
monhost => { optional => 1},
|
|
nodes => { optional => 1 },
|
|
subdir => { optional => 1 },
|
|
disable => { optional => 1 },
|
|
options => { optional => 1 },
|
|
username => { optional => 1 },
|
|
content => { optional => 1 },
|
|
format => { optional => 1 },
|
|
mkdir => { optional => 1 },
|
|
fuse => { optional => 1 },
|
|
bwlimit => { optional => 1 },
|
|
};
|
|
}
|
|
|
|
sub check_config {
|
|
my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_;
|
|
|
|
$config->{path} = "/mnt/pve/$sectionId" if $create && !$config->{path};
|
|
|
|
return $class->SUPER::check_config($sectionId, $config, $create, $skipSchemaCheck);
|
|
}
|
|
|
|
# Storage implementation
|
|
|
|
sub on_add_hook {
|
|
my ($class, $storeid, $scfg, %param) = @_;
|
|
|
|
return if defined($scfg->{monhost}); # nothing to do if not pve managed ceph
|
|
|
|
PVE::Storage::CephTools::ceph_create_keyfile($scfg->{type}, $storeid);
|
|
}
|
|
|
|
sub on_delete_hook {
|
|
my ($class, $storeid, $scfg) = @_;
|
|
|
|
return if defined($scfg->{monhost}); # nothing to do if not pve managed ceph
|
|
|
|
PVE::Storage::CephTools::ceph_remove_keyfile($scfg->{type}, $storeid);
|
|
}
|
|
|
|
sub status {
|
|
my ($class, $storeid, $scfg, $cache) = @_;
|
|
|
|
$cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts();
|
|
|
|
return undef if !cephfs_is_mounted($scfg, $storeid, $cache->{mountdata});
|
|
|
|
return $class->SUPER::status($storeid, $scfg, $cache);
|
|
}
|
|
|
|
sub activate_storage {
|
|
my ($class, $storeid, $scfg, $cache) = @_;
|
|
|
|
$cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts();
|
|
|
|
# NOTE: mkpath may hang if storage is mounted but not reachable
|
|
if (!cephfs_is_mounted($scfg, $storeid, $cache->{mountdata})) {
|
|
my $path = $scfg->{path};
|
|
|
|
mkpath $path if !(defined($scfg->{mkdir}) && !$scfg->{mkdir});
|
|
|
|
die "unable to activate storage '$storeid' - " .
|
|
"directory '$path' does not exist\n" if ! -d $path;
|
|
|
|
cephfs_mount($scfg, $storeid);
|
|
}
|
|
|
|
$class->SUPER::activate_storage($storeid, $scfg, $cache);
|
|
}
|
|
|
|
sub deactivate_storage {
|
|
my ($class, $storeid, $scfg, $cache) = @_;
|
|
|
|
$cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts();
|
|
|
|
my $path = $scfg->{path};
|
|
|
|
if (cephfs_is_mounted($scfg, $storeid, $cache->{mountdata})) {
|
|
run_command(['/bin/umount', $path], errmsg => 'umount error');
|
|
}
|
|
}
|
|
|
|
1;
|