]> git.proxmox.com Git - pve-manager.git/blobdiff - PVE/API2/Ceph.pm
ceph: init: allow pool deletion by default
[pve-manager.git] / PVE / API2 / Ceph.pm
index 3f19e02b1717e6324918bf72a8a60737a4e430ff..89b8eb5b1af3d067e379bcec7d7892d6b16e6c7f 100644 (file)
@@ -3,6 +3,7 @@ package PVE::API2::CephOSD;
 use strict;
 use warnings;
 use Cwd qw(abs_path);
+use Net::IP;
 
 use PVE::SafeSyslog;
 use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
@@ -16,6 +17,7 @@ use PVE::RPCEnvironment;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::RADOS;
 use PVE::CephTools;
+use PVE::Diskmanage;
 
 use base qw(PVE::RESTHandler);
 
@@ -28,6 +30,8 @@ my $get_osd_status = sub {
 
     my $osdlist = $stat->{osds} || [];
 
+    my $flags = $stat->{flags} || undef;
+
     my $osdstat;
     foreach my $d (@$osdlist) {
        $osdstat->{$d->{osd}} = $d if defined($d->{osd});    
@@ -37,7 +41,7 @@ my $get_osd_status = sub {
        return $osdstat->{$osdid};
     }
 
-    return $osdstat;
+    return wantarray? ($osdstat, $flags):$osdstat;
 };
 
 my $get_osd_usage = sub {
@@ -61,6 +65,9 @@ __PACKAGE__->register_method ({
     description => "Get Ceph osd list/tree.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -81,7 +88,7 @@ __PACKAGE__->register_method ({
 
         die "no tree nodes found\n" if !($res && $res->{nodes});
 
-       my $osdhash = &$get_osd_status($rados);
+       my ($osdhash, $flags) = &$get_osd_status($rados);
 
        my $usagehash = &$get_osd_usage($rados);
 
@@ -135,17 +142,19 @@ __PACKAGE__->register_method ({
            }
        }
 
-       my $rootnode;
+       my $roots = [];
        foreach my $e (@{$res->{nodes}}) {
            if (!$nodes->{$e->{id}}->{parent}) {
-               $rootnode = $newnodes->{$e->{id}};
-               last;
+               push @$roots, $newnodes->{$e->{id}};
            }
        }
 
-       die "no root node\n" if !$rootnode;
+       die "no root node\n" if !@$roots;
+
+       my $data = { root => { leaf =>  0, children => $roots } };
 
-       my $data = { root => $rootnode };
+       # we want this for the noout flag
+       $data->{flags} = $flags if $flags;
 
        return $data;
     }});
@@ -194,16 +203,16 @@ __PACKAGE__->register_method ({
        my $journal_dev;
 
        if ($param->{journal_dev} && ($param->{journal_dev} ne $param->{dev})) {
-            $journal_dev = PVE::CephTools::verify_blockdev_path($param->{journal_dev});
+            $journal_dev = PVE::Diskmanage::verify_blockdev_path($param->{journal_dev});
        }
 
-        $param->{dev} = PVE::CephTools::verify_blockdev_path($param->{dev});
-
-       my $disklist = PVE::CephTools::list_disks();
+        $param->{dev} = PVE::Diskmanage::verify_blockdev_path($param->{dev});
 
        my $devname = $param->{dev};
        $devname =~ s|/dev/||;
-       
+
+       my $disklist = PVE::Diskmanage::get_disks($devname, 1);
+
        my $diskinfo = $disklist->{$devname};
        die "unable to get device info for '$devname'\n"
            if !$diskinfo;
@@ -211,6 +220,7 @@ __PACKAGE__->register_method ({
        die "device '$param->{dev}' is in use\n" 
            if $diskinfo->{used};
 
+       my $devpath = $diskinfo->{devpath};
        my $rados = PVE::RADOS->new();
        my $monstat = $rados->mon_command({ prefix => 'mon_status' });
        die "unable to get fsid\n" if !$monstat->{monmap} || !$monstat->{monmap}->{fsid};
@@ -221,7 +231,7 @@ __PACKAGE__->register_method ({
        my $ceph_bootstrap_osd_keyring = PVE::CephTools::get_config('ceph_bootstrap_osd_keyring');
 
        if (! -f $ceph_bootstrap_osd_keyring) {
-           my $bindata = $rados->mon_command({ prefix => 'auth get client.bootstrap-osd', format => 'plain' });
+           my $bindata = $rados->mon_command({ prefix => 'auth get', entity => 'client.bootstrap-osd', format => 'plain' });
            PVE::Tools::file_set_contents($ceph_bootstrap_osd_keyring, $bindata);
        };
         
@@ -230,7 +240,7 @@ __PACKAGE__->register_method ({
 
            my $fstype = $param->{fstype} || 'xfs';
 
-           print "create OSD on $param->{dev} ($fstype)\n";
+           print "create OSD on $devpath ($fstype)\n";
 
            my $ccname = PVE::CephTools::get_config('ccname');
 
@@ -239,9 +249,9 @@ __PACKAGE__->register_method ({
 
            if ($journal_dev) {
                print "using device '$journal_dev' for journal\n";
-               push @$cmd, '--journal-dev', $param->{dev}, $journal_dev;
+               push @$cmd, '--journal-dev', $devpath, $journal_dev;
            } else {
-               push @$cmd, $param->{dev};
+               push @$cmd, $devpath;
            }
            
            run_command($cmd);
@@ -319,21 +329,15 @@ __PACKAGE__->register_method ({
            my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
 
            my $remove_partition = sub {
-               my ($disklist, $part) = @_;
+               my ($part) = @_;
 
                return if !$part || (! -b $part );
-                  
-               foreach my $real_dev (keys %$disklist) {
-                   my $diskinfo = $disklist->{$real_dev};
-                   next if !$diskinfo->{gpt};
-                   if ($part =~ m|^/dev/${real_dev}(\d+)$|) {
-                       my $partnum = $1;
-                       print "remove partition $part (disk '/dev/${real_dev}', partnum $partnum)\n";
-                       eval { run_command(['/sbin/sgdisk', '-d', $partnum, "/dev/${real_dev}"]); };
-                       warn $@ if $@;
-                       last;
-                   }
-               }
+               my $partnum = PVE::Diskmanage::get_partnum($part);
+               my $devpath = PVE::Diskmanage::get_blockdev($part);
+
+               print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
+               eval { run_command(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
+               warn $@ if $@;
            };
 
            my $journal_part;
@@ -358,13 +362,13 @@ __PACKAGE__->register_method ({
            }
 
            print "Unmount OSD $osdsection from  $mountpoint\n";
-           eval { run_command(['umount', $mountpoint]); };
+           eval { run_command(['/bin/umount', $mountpoint]); };
            if (my $err = $@) {
                warn $err;
            } elsif ($param->{cleanup}) {
-               my $disklist = PVE::CephTools::list_disks();
-               &$remove_partition($disklist, $journal_part);
-               &$remove_partition($disklist, $data_part);
+               #be aware of the ceph udev rules which can remount.
+               &$remove_partition($data_part);
+               &$remove_partition($journal_part);
            }
        };
 
@@ -378,6 +382,9 @@ __PACKAGE__->register_method ({
     description => "ceph osd in",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -414,6 +421,9 @@ __PACKAGE__->register_method ({
     description => "ceph osd out",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -486,6 +496,9 @@ __PACKAGE__->register_method ({
     method => 'GET',
     description => "Directory index.",
     permissions => { user => 'all' },
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -515,6 +528,7 @@ __PACKAGE__->register_method ({
            { name => 'config' },
            { name => 'log' },
            { name => 'disks' },
+           { name => 'flags' },
        ];
 
        return $result;
@@ -527,6 +541,9 @@ __PACKAGE__->register_method ({
     description => "List local disks.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -561,7 +578,7 @@ __PACKAGE__->register_method ({
 
        PVE::CephTools::check_ceph_inited();
 
-       my $disks = PVE::CephTools::list_disks();
+       my $disks = PVE::Diskmanage::get_disks(undef, 1);
 
        my $res = [];
        foreach my $dev (keys %$disks) {
@@ -588,6 +605,9 @@ __PACKAGE__->register_method ({
     name => 'config',
     path => 'config',
     method => 'GET',
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     description => "Get Ceph configuration.",
     parameters => {
        additionalProperties => 0,
@@ -613,6 +633,9 @@ __PACKAGE__->register_method ({
     description => "Get Ceph monitor list.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -679,6 +702,9 @@ __PACKAGE__->register_method ({
     description => "Create initial ceph default configuration and setup symlinks.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -695,16 +721,27 @@ __PACKAGE__->register_method ({
                default => 2,
                optional => 1,
                minimum => 1,
-               maximum => 3,
+               maximum => 7,
            },
            pg_bits => {
-               description => "Placement group bits, used to specify the default number of placement groups (Note: 'osd pool default pg num' does not work for deafult pools)",
+               description => "Placement group bits, used to specify the " .
+                   "default number of placement groups.\n\nNOTE: 'osd pool " .
+                   "default pg num' does not work for default pools.",
                type => 'integer',
                default => 6,
                optional => 1,
                minimum => 6,
                maximum => 14,
            },
+           disable_cephx => {
+               description => "Disable cephx authentification.\n\n" .
+                   "WARNING: cephx is a security feature protecting against " .
+                   "man-in-the-middle attacks. Only consider disabling cephx ".
+                   "if your network is private!",
+               type => 'boolean',
+               optional => 1,
+               default => 0,
+           },
        },
     },
     returns => { type => 'null' },
@@ -724,15 +761,16 @@ __PACKAGE__->register_method ({
            UUID::generate($uuid);
            UUID::unparse($uuid, $fsid);
 
+           my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
+
            $cfg->{global} = {
                'fsid' => $fsid,
-               'auth supported' => 'cephx',
-               'auth cluster required' => 'cephx',
-               'auth service required' => 'cephx',
-               'auth client required' => 'cephx',
-               'filestore xattr use omap' => 'true',
+               'auth cluster required' => $auth,
+               'auth service required' => $auth,
+               'auth client required' => $auth,
                'osd journal size' => $pve_osd_default_journal_size,
                'osd pool default min size' => 1,
+               'mon allow pool delete' => 'true',
            };
 
            # this does not work for default pools 
@@ -765,16 +803,18 @@ __PACKAGE__->register_method ({
 my $find_node_ip = sub {
     my ($cidr) = @_;
 
-    my $config = PVE::INotify::read_file('interfaces');
-
     my $net = Net::IP->new($cidr) || die Net::IP::Error() . "\n";
+    my $id = $net->version == 6 ? 'address6' : 'address';
 
-    foreach my $iface (keys %$config) {
-       my $d = $config->{$iface};
-       next if !$d->{address};
-       my $a = Net::IP->new($d->{address});
+    my $config = PVE::INotify::read_file('interfaces');
+    my $ifaces = $config->{ifaces};
+
+    foreach my $iface (keys %$ifaces) {
+       my $d = $ifaces->{$iface};
+       next if !$d->{$id};
+       my $a = Net::IP->new($d->{$id});
        next if !$a;
-       return $d->{address} if $net->overlaps($a);
+       return $d->{$id} if $net->overlaps($a);
     }
 
     die "unable to find local address within network '$cidr'\n";
@@ -787,6 +827,9 @@ __PACKAGE__->register_method ({
     description => "Create Ceph Monitor",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -811,6 +854,8 @@ __PACKAGE__->register_method ({
 
        my $monaddrhash = {}; 
 
+       my $systemd_managed = PVE::CephTools::systemd_managed();
+
        foreach my $section (keys %$cfg) {
            next if $section eq 'global';
            my $d = $cfg->{$section};
@@ -839,7 +884,7 @@ __PACKAGE__->register_method ({
            $ip = PVE::Cluster::remote_node_ip($param->{node});
        }
 
-       my $monaddr = "$ip:6789";
+       my $monaddr = Net::IP::ip_is_ipv6($ip) ? "[$ip]:6789" : "$ip:6789";
        my $monname = $param->{node};
 
        die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
@@ -863,6 +908,8 @@ __PACKAGE__->register_method ({
                            "--cap mds 'allow' " .
                            "--cap osd 'allow *' " .
                            "--cap mon 'allow *'");
+               run_command("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
+               run_command("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
                run_command("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
                run_command("mv $pve_mon_key_path.tmp $pve_mon_key_path");
            }
@@ -877,6 +924,8 @@ __PACKAGE__->register_method ({
            eval {
                mkdir $mondir;
 
+               run_command("chown ceph:ceph $mondir") if $systemd_managed;
+
                if ($moncount > 0) {
                    my $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
                    my $mapdata = $rados->mon_command({ prefix => 'mon getmap', format => 'plain' });
@@ -886,6 +935,7 @@ __PACKAGE__->register_method ({
                }
 
                run_command("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
+               run_command("chown ceph:ceph -R $mondir") if $systemd_managed;
            };
            my $err = $@;
            unlink $monmap;
@@ -901,7 +951,21 @@ __PACKAGE__->register_method ({
 
            PVE::CephTools::write_ceph_config($cfg);
 
-           PVE::CephTools::ceph_service_cmd('start', $monsection);
+           my $create_keys_pid = fork();
+           if (!defined($create_keys_pid)) {
+               die "Could not spawn ceph-create-keys to create bootstrap keys\n";
+           } elsif ($create_keys_pid == 0) {
+               exit PVE::Tools::run_command(['ceph-create-keys', '-i', $monid]);
+           } else {
+               PVE::CephTools::ceph_service_cmd('start', $monsection);
+
+               if ($systemd_managed) {
+                   #to ensure we have the correct startup order.
+                   eval { PVE::Tools::run_command(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
+                   warn "Enable ceph-mon\@${monid}.service manually"if $@;
+               }
+               waitpid($create_keys_pid, 0);
+           }
        };
 
        return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
@@ -914,6 +978,9 @@ __PACKAGE__->register_method ({
     description => "Destroy Ceph monitor.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -979,6 +1046,9 @@ __PACKAGE__->register_method ({
     description => "Stop ceph services.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1026,6 +1096,9 @@ __PACKAGE__->register_method ({
     description => "Start ceph services.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1073,6 +1146,9 @@ __PACKAGE__->register_method ({
     description => "Get ceph status.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1096,6 +1172,9 @@ __PACKAGE__->register_method ({
     description => "List all pools.",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1123,8 +1202,8 @@ __PACKAGE__->register_method ({
 
        my $stats = {};
        my $res = $rados->mon_command({ prefix => 'df' });
-       my $total = $res->{stats}->{total_space} || 0;
-       $total = $total * 1024;
+       my $total = $res->{stats}->{total_avail_bytes} || 0;
+
        foreach my $d (@{$res->{pools}}) {
            next if !$d->{stats};
            next if !defined($d->{id});
@@ -1141,7 +1220,8 @@ __PACKAGE__->register_method ({
            }
            if (my $s = $stats->{$d->{pool}}) {
                $d->{bytes_used} = $s->{bytes_used};
-               $d->{percent_used} = ($d->{bytes_used}*100)/$total if $total;
+               $d->{percent_used} = ($s->{bytes_used} / $total)*100
+                   if $s->{max_avail} && $total;
            }
            push @$data, $d;
        }
@@ -1157,6 +1237,9 @@ __PACKAGE__->register_method ({
     description => "Create POOL",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1171,7 +1254,7 @@ __PACKAGE__->register_method ({
                default => 2,
                optional => 1,
                minimum => 1,
-               maximum => 3,
+               maximum => 7,
            },
            min_size => {
                description => 'Minimum number of replicas per object',
@@ -1179,7 +1262,7 @@ __PACKAGE__->register_method ({
                default => 1,
                optional => 1,
                minimum => 1,
-               maximum => 3,
+               maximum => 7,
            },
            pg_num => {
                description => "Number of placement groups.",
@@ -1254,6 +1337,126 @@ __PACKAGE__->register_method ({
        return undef;
     }});
 
+__PACKAGE__->register_method ({
+    name => 'get_flags',
+    path => 'flags',
+    method => 'GET',
+    description => "get all set ceph flags",
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => { type => 'string' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::CephTools::check_ceph_inited();
+
+       my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
+
+       die "not fully configured - missing '$pve_ckeyring_path'\n"
+           if ! -f $pve_ckeyring_path;
+
+       my $rados = PVE::RADOS->new();
+
+       my $stat = $rados->mon_command({ prefix => 'osd dump' });
+
+       return $stat->{flags} // '';
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'set_flag',
+    path => 'flags/{flag}',
+    method => 'POST',
+    description => "Set a ceph flag",
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           flag => {
+               description => 'The ceph flag to set/unset',
+               type => 'string',
+               enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
+           },
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::CephTools::check_ceph_inited();
+
+       my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
+
+       die "not fully configured - missing '$pve_ckeyring_path'\n"
+           if ! -f $pve_ckeyring_path;
+
+       my $set = $param->{set} // !$param->{unset};
+       my $rados = PVE::RADOS->new();
+
+       $rados->mon_command({
+           prefix => "osd set",
+           key => $param->{flag},
+       });
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'unset_flag',
+    path => 'flags/{flag}',
+    method => 'DELETE',
+    description => "Unset a ceph flag",
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           flag => {
+               description => 'The ceph flag to set/unset',
+               type => 'string',
+               enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
+           },
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::CephTools::check_ceph_inited();
+
+       my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
+
+       die "not fully configured - missing '$pve_ckeyring_path'\n"
+           if ! -f $pve_ckeyring_path;
+
+       my $set = $param->{set} // !$param->{unset};
+       my $rados = PVE::RADOS->new();
+
+       $rados->mon_command({
+           prefix => "osd unset",
+           key => $param->{flag},
+       });
+
+       return undef;
+    }});
+
 __PACKAGE__->register_method ({
     name => 'destroypool',
     path => 'pools/{name}',
@@ -1261,6 +1464,9 @@ __PACKAGE__->register_method ({
     description => "Destroy pool",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {
@@ -1269,6 +1475,12 @@ __PACKAGE__->register_method ({
                description => "The name of the pool. It must be unique.",
                type => 'string',
            },
+           force => {
+               description => "If true, destroys pool even if in use",
+               type => 'boolean',
+               optional => 1,
+               default => 0,
+           }
        },
     },
     returns => { type => 'null' },
@@ -1277,6 +1489,22 @@ __PACKAGE__->register_method ({
 
        PVE::CephTools::check_ceph_inited();
 
+       # if not forced, destroy ceph pool only when no
+       # vm disks are on it anymore
+       if (!$param->{force}) {
+           my $storagecfg = PVE::Storage::config();
+           foreach my $storageid (keys %{$storagecfg->{ids}}) {
+               my $storage = $storagecfg->{ids}->{$storageid};
+               next if $storage->{type} ne 'rbd';
+               next if $storage->{pool} ne $param->{name};
+
+               # check if any vm disks are on the pool
+               my $res = PVE::Storage::vdisk_list($storagecfg, $storageid);
+               die "ceph pool '$param->{name}' still in use by storage '$storageid'\n"
+                   if @{$res->{$storageid}} != 0;
+           }
+       }
+
        my $rados = PVE::RADOS->new();
        # fixme: '--yes-i-really-really-mean-it'
        $rados->mon_command({ 
@@ -1298,6 +1526,9 @@ __PACKAGE__->register_method ({
     description => "Get OSD crush map",
     proxyto => 'node',
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+    },
     parameters => {
        additionalProperties => 0,
        properties => {