]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Ceph.pm
add wipe_disk option when destroying ceph disk
[pve-manager.git] / PVE / API2 / Ceph.pm
CommitLineData
7d4fc5ef 1package PVE::API2::CephOSD;
38db610a
DM
2
3use strict;
4use warnings;
13f4d762 5use Cwd qw(abs_path);
98901f1d 6use Net::IP;
38db610a
DM
7
8use PVE::SafeSyslog;
13f4d762 9use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
38db610a
DM
10use PVE::Exception qw(raise raise_param_exc);
11use PVE::INotify;
12use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
13use PVE::AccessControl;
14use PVE::Storage;
ae672a64 15use PVE::API2::Storage::Config;
38db610a
DM
16use PVE::RESTHandler;
17use PVE::RPCEnvironment;
18use PVE::JSONSchema qw(get_standard_option);
970236b3 19use PVE::RADOS;
a34866f0 20use PVE::CephTools;
7f4924bd 21use PVE::Diskmanage;
38db610a
DM
22
23use base qw(PVE::RESTHandler);
24
25use Data::Dumper; # fixme: remove
26
7d4fc5ef
DM
27my $get_osd_status = sub {
28 my ($rados, $osdid) = @_;
0e5816e4 29
7d4fc5ef 30 my $stat = $rados->mon_command({ prefix => 'osd dump' });
2f804640 31
7d4fc5ef 32 my $osdlist = $stat->{osds} || [];
38db610a 33
85c17d96
DC
34 my $flags = $stat->{flags} || undef;
35
7d4fc5ef
DM
36 my $osdstat;
37 foreach my $d (@$osdlist) {
be753927 38 $osdstat->{$d->{osd}} = $d if defined($d->{osd});
7d4fc5ef
DM
39 }
40 if (defined($osdid)) {
41 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
42 return $osdstat->{$osdid};
43 }
38db610a 44
85c17d96 45 return wantarray? ($osdstat, $flags):$osdstat;
7d4fc5ef 46};
13f4d762 47
941c0195
DM
48my $get_osd_usage = sub {
49 my ($rados) = @_;
50
be753927 51 my $osdlist = $rados->mon_command({ prefix => 'pg dump',
941c0195
DM
52 dumpcontents => [ 'osds' ]}) || [];
53
54 my $osdstat;
55 foreach my $d (@$osdlist) {
be753927 56 $osdstat->{$d->{osd}} = $d if defined($d->{osd});
941c0195
DM
57 }
58
59 return $osdstat;
60};
61
7d4fc5ef
DM
62__PACKAGE__->register_method ({
63 name => 'index',
64 path => '',
65 method => 'GET',
66 description => "Get Ceph osd list/tree.",
67 proxyto => 'node',
68 protected => 1,
90c75580
TL
69 permissions => {
70 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
71 },
7d4fc5ef 72 parameters => {
be753927 73 additionalProperties => 0,
7d4fc5ef
DM
74 properties => {
75 node => get_standard_option('pve-node'),
76 },
77 },
78 # fixme: return a list instead of extjs tree format ?
79 returns => {
80 type => "object",
81 },
82 code => sub {
83 my ($param) = @_;
13f4d762 84
7d4fc5ef 85 PVE::CephTools::check_ceph_inited();
13f4d762 86
7d4fc5ef
DM
87 my $rados = PVE::RADOS->new();
88 my $res = $rados->mon_command({ prefix => 'osd tree' });
13f4d762 89
7d4fc5ef 90 die "no tree nodes found\n" if !($res && $res->{nodes});
13f4d762 91
85c17d96 92 my ($osdhash, $flags) = &$get_osd_status($rados);
13f4d762 93
941c0195
DM
94 my $usagehash = &$get_osd_usage($rados);
95
4d422ffc
DC
96 my $osdmetadata_tmp = $rados->mon_command({ prefix => 'osd metadata' });
97
98 my $osdmetadata = {};
99 foreach my $osd (@$osdmetadata_tmp) {
100 $osdmetadata->{$osd->{id}} = $osd;
101 }
102
7d4fc5ef
DM
103 my $nodes = {};
104 my $newnodes = {};
105 foreach my $e (@{$res->{nodes}}) {
106 $nodes->{$e->{id}} = $e;
be753927
DC
107
108 my $new = {
109 id => $e->{id},
110 name => $e->{name},
7d4fc5ef
DM
111 type => $e->{type}
112 };
13f4d762 113
33a7e157 114 foreach my $opt (qw(status crush_weight reweight device_class)) {
7d4fc5ef
DM
115 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
116 }
13f4d762 117
7d4fc5ef
DM
118 if (my $stat = $osdhash->{$e->{id}}) {
119 $new->{in} = $stat->{in} if defined($stat->{in});
120 }
13f4d762 121
941c0195
DM
122 if (my $stat = $usagehash->{$e->{id}}) {
123 $new->{total_space} = ($stat->{kb} || 1) * 1024;
124 $new->{bytes_used} = ($stat->{kb_used} || 0) * 1024;
125 $new->{percent_used} = ($new->{bytes_used}*100)/$new->{total_space};
f4fe197d 126 if (my $d = $stat->{perf_stat}) {
cc81005a
DM
127 $new->{commit_latency_ms} = $d->{commit_latency_ms};
128 $new->{apply_latency_ms} = $d->{apply_latency_ms};
129 }
941c0195
DM
130 }
131
4d422ffc
DC
132 my $osdmd = $osdmetadata->{$e->{id}};
133 if ($e->{type} eq 'osd' && $osdmd) {
134 if ($osdmd->{bluefs}) {
135 $new->{osdtype} = 'bluestore';
136 $new->{blfsdev} = $osdmd->{bluestore_bdev_dev_node};
137 $new->{dbdev} = $osdmd->{bluefs_db_dev_node};
138 $new->{waldev} = $osdmd->{bluefs_wal_dev_node};
139 } else {
140 $new->{osdtype} = 'filestore';
141 }
142 }
143
7d4fc5ef 144 $newnodes->{$e->{id}} = $new;
13f4d762
DM
145 }
146
7d4fc5ef
DM
147 foreach my $e (@{$res->{nodes}}) {
148 my $new = $newnodes->{$e->{id}};
149 if ($e->{children} && scalar(@{$e->{children}})) {
150 $new->{children} = [];
151 $new->{leaf} = 0;
152 foreach my $cid (@{$e->{children}}) {
153 $nodes->{$cid}->{parent} = $e->{id};
154 if ($nodes->{$cid}->{type} eq 'osd' &&
155 $e->{type} eq 'host') {
156 $newnodes->{$cid}->{host} = $e->{name};
157 }
158 push @{$new->{children}}, $newnodes->{$cid};
159 }
160 } else {
161 $new->{leaf} = ($e->{id} >= 0) ? 1 : 0;
162 }
0e5816e4
DM
163 }
164
4aed5e3e 165 my $roots = [];
7d4fc5ef
DM
166 foreach my $e (@{$res->{nodes}}) {
167 if (!$nodes->{$e->{id}}->{parent}) {
4aed5e3e 168 push @$roots, $newnodes->{$e->{id}};
7d4fc5ef 169 }
0e5816e4
DM
170 }
171
4aed5e3e 172 die "no root node\n" if !@$roots;
13f4d762 173
4aed5e3e 174 my $data = { root => { leaf => 0, children => $roots } };
0e5816e4 175
85c17d96
DC
176 # we want this for the noout flag
177 $data->{flags} = $flags if $flags;
178
7d4fc5ef
DM
179 return $data;
180 }});
13f4d762 181
7d4fc5ef
DM
182__PACKAGE__->register_method ({
183 name => 'createosd',
184 path => '',
185 method => 'POST',
186 description => "Create OSD",
187 proxyto => 'node',
188 protected => 1,
189 parameters => {
be753927 190 additionalProperties => 0,
7d4fc5ef
DM
191 properties => {
192 node => get_standard_option('pve-node'),
193 dev => {
194 description => "Block device name.",
195 type => 'string',
196 },
197 journal_dev => {
8d64bd8c
DC
198 description => "Block device name for journal (filestore) or block.db (bluestore).",
199 optional => 1,
200 type => 'string',
201 },
202 wal_dev => {
203 description => "Block device name for block.wal (bluestore only).",
7d4fc5ef
DM
204 optional => 1,
205 type => 'string',
206 },
207 fstype => {
50239dba 208 description => "File system type (filestore only).",
7d4fc5ef 209 type => 'string',
29f633a8 210 enum => ['xfs', 'ext4'],
7d4fc5ef
DM
211 default => 'xfs',
212 optional => 1,
213 },
50239dba 214 bluestore => {
815a2bc1 215 description => "Use bluestore instead of filestore. This is the default.",
50239dba 216 type => 'boolean',
815a2bc1 217 default => 1,
50239dba
FG
218 optional => 1,
219 },
7d4fc5ef
DM
220 },
221 },
222 returns => { type => 'string' },
223 code => sub {
224 my ($param) = @_;
13f4d762 225
7d4fc5ef 226 my $rpcenv = PVE::RPCEnvironment::get();
13f4d762 227
7d4fc5ef 228 my $authuser = $rpcenv->get_user();
13f4d762 229
50239dba
FG
230 raise_param_exc({ 'bluestore' => "conflicts with parameter 'fstype'" })
231 if (defined($param->{fstype}) && defined($param->{bluestore}) && $param->{bluestore});
232
7d4fc5ef 233 PVE::CephTools::check_ceph_inited();
0e5816e4 234
7d4fc5ef 235 PVE::CephTools::setup_pve_symlinks();
a7a7fb00 236
c64c04dd
AA
237 PVE::CephTools::check_ceph_installed('ceph_osd');
238
815a2bc1 239 my $bluestore = $param->{bluestore} // 1;
8d64bd8c 240
7d4fc5ef 241 my $journal_dev;
8d64bd8c 242 my $wal_dev;
a7a7fb00 243
7d4fc5ef 244 if ($param->{journal_dev} && ($param->{journal_dev} ne $param->{dev})) {
7f4924bd 245 $journal_dev = PVE::Diskmanage::verify_blockdev_path($param->{journal_dev});
8d64bd8c
DC
246 }
247
248 if ($param->{wal_dev} &&
249 ($param->{wal_dev} ne $param->{dev}) &&
250 (!$param->{journal_dev} || $param->{wal_dev} ne $param->{journal_dev})) {
251 raise_param_exc({ 'wal_dev' => "can only be set with paramater 'bluestore'"})
252 if !$bluestore;
253 $wal_dev = PVE::Diskmanage::verify_blockdev_path($param->{wal_dev});
7d4fc5ef 254 }
13f4d762 255
7f4924bd 256 $param->{dev} = PVE::Diskmanage::verify_blockdev_path($param->{dev});
a7a7fb00 257
7d4fc5ef
DM
258 my $devname = $param->{dev};
259 $devname =~ s|/dev/||;
929376d7
DC
260
261 my $disklist = PVE::Diskmanage::get_disks($devname, 1);
262
7d4fc5ef
DM
263 my $diskinfo = $disklist->{$devname};
264 die "unable to get device info for '$devname'\n"
265 if !$diskinfo;
13f4d762 266
be753927 267 die "device '$param->{dev}' is in use\n"
7d4fc5ef 268 if $diskinfo->{used};
0e5816e4 269
929376d7 270 my $devpath = $diskinfo->{devpath};
7d4fc5ef
DM
271 my $rados = PVE::RADOS->new();
272 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
273 die "unable to get fsid\n" if !$monstat->{monmap} || !$monstat->{monmap}->{fsid};
0e5816e4 274
7d4fc5ef
DM
275 my $fsid = $monstat->{monmap}->{fsid};
276 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
0e5816e4 277
7d4fc5ef 278 my $ceph_bootstrap_osd_keyring = PVE::CephTools::get_config('ceph_bootstrap_osd_keyring');
0e5816e4 279
7d4fc5ef 280 if (! -f $ceph_bootstrap_osd_keyring) {
1f3e956a 281 my $bindata = $rados->mon_command({ prefix => 'auth get', entity => 'client.bootstrap-osd', format => 'plain' });
7d4fc5ef
DM
282 PVE::Tools::file_set_contents($ceph_bootstrap_osd_keyring, $bindata);
283 };
be753927 284
7d4fc5ef
DM
285 my $worker = sub {
286 my $upid = shift;
0e5816e4 287
7d4fc5ef 288 my $fstype = $param->{fstype} || 'xfs';
0e5816e4 289
0e5816e4 290
7d4fc5ef 291 my $ccname = PVE::CephTools::get_config('ccname');
0e5816e4 292
50239dba 293 my $cmd = ['ceph-disk', 'prepare', '--zap-disk',
7d4fc5ef 294 '--cluster', $ccname, '--cluster-uuid', $fsid ];
38db610a 295
8d64bd8c 296 if ($bluestore) {
50239dba
FG
297 print "create OSD on $devpath (bluestore)\n";
298 push @$cmd, '--bluestore';
8d64bd8c
DC
299
300 if ($journal_dev) {
301 print "using device '$journal_dev' for block.db\n";
302 push @$cmd, '--block.db', $journal_dev;
303 }
304
305 if ($wal_dev) {
306 print "using device '$wal_dev' for block.wal\n";
307 push @$cmd, '--block.wal', $wal_dev;
308 }
309
310 push @$cmd, $devpath;
50239dba
FG
311 } else {
312 print "create OSD on $devpath ($fstype)\n";
b6c42726 313 push @$cmd, '--filestore', '--fs-type', $fstype;
8d64bd8c
DC
314 if ($journal_dev) {
315 print "using device '$journal_dev' for journal\n";
316 push @$cmd, '--journal-dev', $devpath, $journal_dev;
317 } else {
318 push @$cmd, $devpath;
319 }
50239dba
FG
320 }
321
be753927 322
7d4fc5ef
DM
323 run_command($cmd);
324 };
38db610a 325
7d4fc5ef 326 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
38db610a
DM
327 }});
328
13f4d762 329__PACKAGE__->register_method ({
7d4fc5ef
DM
330 name => 'destroyosd',
331 path => '{osdid}',
332 method => 'DELETE',
333 description => "Destroy OSD",
13f4d762
DM
334 proxyto => 'node',
335 protected => 1,
336 parameters => {
be753927 337 additionalProperties => 0,
13f4d762
DM
338 properties => {
339 node => get_standard_option('pve-node'),
7d4fc5ef
DM
340 osdid => {
341 description => 'OSD ID',
342 type => 'integer',
0e5816e4 343 },
7d4fc5ef
DM
344 cleanup => {
345 description => "If set, we remove partition table entries.",
346 type => 'boolean',
347 optional => 1,
348 default => 0,
13f4d762
DM
349 },
350 },
13f4d762 351 },
7d4fc5ef 352 returns => { type => 'string' },
13f4d762
DM
353 code => sub {
354 my ($param) = @_;
355
7d4fc5ef 356 my $rpcenv = PVE::RPCEnvironment::get();
13f4d762 357
7d4fc5ef 358 my $authuser = $rpcenv->get_user();
13f4d762 359
7d4fc5ef 360 PVE::CephTools::check_ceph_inited();
0e5816e4 361
7d4fc5ef 362 my $osdid = $param->{osdid};
0e5816e4 363
7d4fc5ef
DM
364 my $rados = PVE::RADOS->new();
365 my $osdstat = &$get_osd_status($rados, $osdid);
13f4d762 366
7d4fc5ef
DM
367 die "osd is in use (in == 1)\n" if $osdstat->{in};
368 #&$run_ceph_cmd(['osd', 'out', $osdid]);
68e0c4bd 369
7d4fc5ef 370 die "osd is still runnung (up == 1)\n" if $osdstat->{up};
68e0c4bd 371
7d4fc5ef 372 my $osdsection = "osd.$osdid";
68e0c4bd 373
7d4fc5ef
DM
374 my $worker = sub {
375 my $upid = shift;
68e0c4bd 376
7d4fc5ef 377 # reopen with longer timeout
be753927 378 $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
68e0c4bd 379
7d4fc5ef 380 print "destroy OSD $osdsection\n";
68e0c4bd 381
8ccae777
FG
382 eval {
383 PVE::CephTools::ceph_service_cmd('stop', $osdsection);
384 PVE::CephTools::ceph_service_cmd('disable', $osdsection);
385 };
7d4fc5ef 386 warn $@ if $@;
68e0c4bd 387
7d4fc5ef
DM
388 print "Remove $osdsection from the CRUSH map\n";
389 $rados->mon_command({ prefix => "osd crush remove", name => $osdsection, format => 'plain' });
68e0c4bd 390
7d4fc5ef
DM
391 print "Remove the $osdsection authentication key.\n";
392 $rados->mon_command({ prefix => "auth del", entity => $osdsection, format => 'plain' });
393
394 print "Remove OSD $osdsection\n";
395 $rados->mon_command({ prefix => "osd rm", ids => [ $osdsection ], format => 'plain' });
396
397 # try to unmount from standard mount point
398 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
399
1b3caf4f 400 my $disks_to_wipe = {};
7d4fc5ef 401 my $remove_partition = sub {
84aed461 402 my ($part) = @_;
7d4fc5ef
DM
403
404 return if !$part || (! -b $part );
84aed461
WL
405 my $partnum = PVE::Diskmanage::get_partnum($part);
406 my $devpath = PVE::Diskmanage::get_blockdev($part);
407
408 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
409 eval { run_command(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
410 warn $@ if $@;
1b3caf4f
DL
411
412 $disks_to_wipe->{$devpath} = 1;
7d4fc5ef 413 };
68e0c4bd 414
bb7d5aa9 415 my $partitions_to_remove = [];
be753927 416
7d4fc5ef 417 if ($param->{cleanup}) {
7d4fc5ef
DM
418 if (my $fd = IO::File->new("/proc/mounts", "r")) {
419 while (defined(my $line = <$fd>)) {
420 my ($dev, $path, $fstype) = split(/\s+/, $line);
421 next if !($dev && $path && $fstype);
422 next if $dev !~ m|^/dev/|;
423 if ($path eq $mountpoint) {
bb7d5aa9
DC
424 my $data_part = abs_path($dev);
425 push @$partitions_to_remove, $data_part;
7d4fc5ef
DM
426 last;
427 }
428 }
429 close($fd);
68e0c4bd 430 }
bb7d5aa9
DC
431
432 foreach my $path (qw(journal block block.db block.wal)) {
433 my $part = abs_path("$mountpoint/$path");
434 if ($part) {
435 push @$partitions_to_remove, $part;
436 }
437 }
68e0c4bd 438 }
7d4fc5ef 439
1b3caf4f 440
7d4fc5ef 441 print "Unmount OSD $osdsection from $mountpoint\n";
84aed461 442 eval { run_command(['/bin/umount', $mountpoint]); };
7d4fc5ef
DM
443 if (my $err = $@) {
444 warn $err;
445 } elsif ($param->{cleanup}) {
ef3d095b 446 #be aware of the ceph udev rules which can remount.
bb7d5aa9
DC
447 foreach my $part (@$partitions_to_remove) {
448 $remove_partition->($part);
449 }
1b3caf4f
DL
450 foreach my $devpath (keys %$disks_to_wipe) {
451 print "wipe disk: $devpath\n";
452 eval { run_command(['/bin/dd', 'if=/dev/zero', "of=${devpath}", 'bs=1M', 'count=200', 'conv=fdatasync']); };
453 warn $@ if $@;
454 }
7d4fc5ef 455 }
68e0c4bd 456 };
68e0c4bd 457
7d4fc5ef 458 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
68e0c4bd
DM
459 }});
460
38db610a 461__PACKAGE__->register_method ({
7d4fc5ef
DM
462 name => 'in',
463 path => '{osdid}/in',
38db610a 464 method => 'POST',
7d4fc5ef 465 description => "ceph osd in",
38db610a
DM
466 proxyto => 'node',
467 protected => 1,
90c75580
TL
468 permissions => {
469 check => ['perm', '/', [ 'Sys.Modify' ]],
470 },
38db610a 471 parameters => {
be753927 472 additionalProperties => 0,
38db610a
DM
473 properties => {
474 node => get_standard_option('pve-node'),
7d4fc5ef
DM
475 osdid => {
476 description => 'OSD ID',
38db610a 477 type => 'integer',
38db610a
DM
478 },
479 },
480 },
7d4fc5ef 481 returns => { type => "null" },
38db610a
DM
482 code => sub {
483 my ($param) = @_;
484
7d4fc5ef 485 PVE::CephTools::check_ceph_inited();
38db610a 486
7d4fc5ef 487 my $osdid = $param->{osdid};
0e5816e4 488
7d4fc5ef 489 my $rados = PVE::RADOS->new();
f7e342ea 490
7d4fc5ef 491 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
f7e342ea 492
7d4fc5ef 493 my $osdsection = "osd.$osdid";
38db610a 494
7d4fc5ef 495 $rados->mon_command({ prefix => "osd in", ids => [ $osdsection ], format => 'plain' });
38db610a
DM
496
497 return undef;
498 }});
499
500__PACKAGE__->register_method ({
7d4fc5ef
DM
501 name => 'out',
502 path => '{osdid}/out',
38db610a 503 method => 'POST',
7d4fc5ef 504 description => "ceph osd out",
38db610a
DM
505 proxyto => 'node',
506 protected => 1,
90c75580
TL
507 permissions => {
508 check => ['perm', '/', [ 'Sys.Modify' ]],
509 },
38db610a 510 parameters => {
be753927 511 additionalProperties => 0,
38db610a
DM
512 properties => {
513 node => get_standard_option('pve-node'),
7d4fc5ef
DM
514 osdid => {
515 description => 'OSD ID',
516 type => 'integer',
517 },
38db610a
DM
518 },
519 },
7d4fc5ef 520 returns => { type => "null" },
38db610a
DM
521 code => sub {
522 my ($param) = @_;
523
a34866f0 524 PVE::CephTools::check_ceph_inited();
38db610a 525
7d4fc5ef 526 my $osdid = $param->{osdid};
38db610a 527
7d4fc5ef 528 my $rados = PVE::RADOS->new();
38db610a 529
7d4fc5ef 530 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
38db610a 531
7d4fc5ef 532 my $osdsection = "osd.$osdid";
38db610a 533
7d4fc5ef 534 $rados->mon_command({ prefix => "osd out", ids => [ $osdsection ], format => 'plain' });
38db610a 535
7d4fc5ef
DM
536 return undef;
537 }});
38db610a 538
7d4fc5ef
DM
539package PVE::API2::Ceph;
540
541use strict;
542use warnings;
543use File::Basename;
544use File::Path;
545use POSIX qw (LONG_MAX);
546use Cwd qw(abs_path);
547use IO::Dir;
548use UUID;
549use Net::IP;
550
551use PVE::SafeSyslog;
552use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
553use PVE::Exception qw(raise raise_param_exc);
554use PVE::INotify;
555use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
556use PVE::AccessControl;
557use PVE::Storage;
558use PVE::RESTHandler;
559use PVE::RPCEnvironment;
560use PVE::JSONSchema qw(get_standard_option);
561use JSON;
562use PVE::RADOS;
563use PVE::CephTools;
9f4ff798 564use PVE::Network;
7d4fc5ef
DM
565
566use base qw(PVE::RESTHandler);
567
568use Data::Dumper; # fixme: remove
569
570my $pve_osd_default_journal_size = 1024*5;
571
572__PACKAGE__->register_method ({
be753927 573 subclass => "PVE::API2::CephOSD",
7d4fc5ef
DM
574 path => 'osd',
575});
576
577__PACKAGE__->register_method ({
578 name => 'index',
579 path => '',
580 method => 'GET',
581 description => "Directory index.",
582 permissions => { user => 'all' },
90c75580
TL
583 permissions => {
584 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
585 },
7d4fc5ef 586 parameters => {
be753927 587 additionalProperties => 0,
7d4fc5ef
DM
588 properties => {
589 node => get_standard_option('pve-node'),
590 },
591 },
592 returns => {
593 type => 'array',
594 items => {
595 type => "object",
596 properties => {},
597 },
598 links => [ { rel => 'child', href => "{name}" } ],
599 },
600 code => sub {
601 my ($param) = @_;
602
603 my $result = [
604 { name => 'init' },
605 { name => 'mon' },
606 { name => 'osd' },
607 { name => 'pools' },
608 { name => 'stop' },
609 { name => 'start' },
610 { name => 'status' },
611 { name => 'crush' },
612 { name => 'config' },
613 { name => 'log' },
614 { name => 'disks' },
a46ad02a 615 { name => 'flags' },
d2692b86 616 { name => 'rules' },
7d4fc5ef
DM
617 ];
618
619 return $result;
620 }});
621
622__PACKAGE__->register_method ({
623 name => 'disks',
624 path => 'disks',
625 method => 'GET',
626 description => "List local disks.",
627 proxyto => 'node',
628 protected => 1,
90c75580
TL
629 permissions => {
630 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
631 },
7d4fc5ef 632 parameters => {
be753927 633 additionalProperties => 0,
7d4fc5ef
DM
634 properties => {
635 node => get_standard_option('pve-node'),
636 type => {
637 description => "Only list specific types of disks.",
638 type => 'string',
639 enum => ['unused', 'journal_disks'],
640 optional => 1,
641 },
642 },
643 },
644 returns => {
645 type => 'array',
646 items => {
647 type => "object",
648 properties => {
649 dev => { type => 'string' },
650 used => { type => 'string', optional => 1 },
651 gpt => { type => 'boolean' },
652 size => { type => 'integer' },
653 osdid => { type => 'integer' },
654 vendor => { type => 'string', optional => 1 },
655 model => { type => 'string', optional => 1 },
656 serial => { type => 'string', optional => 1 },
657 },
658 },
659 # links => [ { rel => 'child', href => "{}" } ],
660 },
661 code => sub {
662 my ($param) = @_;
663
664 PVE::CephTools::check_ceph_inited();
665
5fd5c30d 666 my $disks = PVE::Diskmanage::get_disks(undef, 1);
7d4fc5ef
DM
667
668 my $res = [];
669 foreach my $dev (keys %$disks) {
670 my $d = $disks->{$dev};
671 if ($param->{type}) {
672 if ($param->{type} eq 'journal_disks') {
673 next if $d->{osdid} >= 0;
674 next if !$d->{gpt};
675 } elsif ($param->{type} eq 'unused') {
676 next if $d->{used};
677 } else {
678 die "internal error"; # should not happen
679 }
680 }
681
682 $d->{dev} = "/dev/$dev";
be753927 683 push @$res, $d;
7d4fc5ef
DM
684 }
685
686 return $res;
687 }});
688
689__PACKAGE__->register_method ({
690 name => 'config',
691 path => 'config',
692 method => 'GET',
90c75580
TL
693 permissions => {
694 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
695 },
7d4fc5ef
DM
696 description => "Get Ceph configuration.",
697 parameters => {
be753927 698 additionalProperties => 0,
7d4fc5ef
DM
699 properties => {
700 node => get_standard_option('pve-node'),
701 },
702 },
703 returns => { type => 'string' },
704 code => sub {
705 my ($param) = @_;
706
707 PVE::CephTools::check_ceph_inited();
708
709 my $path = PVE::CephTools::get_config('pve_ceph_cfgpath');
710 return PVE::Tools::file_get_contents($path);
711
712 }});
713
ae672a64
FG
714my $add_storage = sub {
715 my ($pool, $storeid, $krbd) = @_;
716
717 my $storage_params = {
718 type => 'rbd',
719 pool => $pool,
720 storage => $storeid,
721 krbd => $krbd // 0,
722 content => $krbd ? 'rootdir' : 'images',
723 };
724
725 PVE::API2::Storage::Config->create($storage_params);
726};
727
f4aae93b
FG
728my $get_storages = sub {
729 my ($pool) = @_;
730
731 my $cfg = PVE::Storage::config();
732
733 my $storages = $cfg->{ids};
734 my $res = {};
735 foreach my $storeid (keys %$storages) {
736 my $curr = $storages->{$storeid};
737 $res->{$storeid} = $storages->{$storeid}
738 if $curr->{type} eq 'rbd' && $pool eq $curr->{pool};
739 }
740
741 return $res;
742};
743
7d4fc5ef
DM
744__PACKAGE__->register_method ({
745 name => 'listmon',
746 path => 'mon',
747 method => 'GET',
748 description => "Get Ceph monitor list.",
749 proxyto => 'node',
750 protected => 1,
90c75580
TL
751 permissions => {
752 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
753 },
7d4fc5ef 754 parameters => {
be753927 755 additionalProperties => 0,
7d4fc5ef
DM
756 properties => {
757 node => get_standard_option('pve-node'),
758 },
759 },
760 returns => {
761 type => 'array',
762 items => {
763 type => "object",
764 properties => {
765 name => { type => 'string' },
766 addr => { type => 'string' },
767 },
768 },
769 links => [ { rel => 'child', href => "{name}" } ],
770 },
771 code => sub {
772 my ($param) = @_;
773
774 PVE::CephTools::check_ceph_inited();
775
776 my $res = [];
777
778 my $cfg = PVE::CephTools::parse_ceph_config();
779
780 my $monhash = {};
781 foreach my $section (keys %$cfg) {
782 my $d = $cfg->{$section};
783 if ($section =~ m/^mon\.(\S+)$/) {
784 my $monid = $1;
785 if ($d->{'mon addr'} && $d->{'host'}) {
786 $monhash->{$monid} = {
787 addr => $d->{'mon addr'},
788 host => $d->{'host'},
789 name => $monid,
790 }
791 }
792 }
793 }
794
795 eval {
796 my $rados = PVE::RADOS->new();
797 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
798 my $mons = $monstat->{monmap}->{mons};
799 foreach my $d (@$mons) {
800 next if !defined($d->{name});
be753927 801 $monhash->{$d->{name}}->{rank} = $d->{rank};
7d4fc5ef
DM
802 $monhash->{$d->{name}}->{addr} = $d->{addr};
803 if (grep { $_ eq $d->{rank} } @{$monstat->{quorum}}) {
804 $monhash->{$d->{name}}->{quorum} = 1;
805 }
806 }
807 };
808 warn $@ if $@;
809
810 return PVE::RESTHandler::hash_to_array($monhash, 'name');
811 }});
812
813__PACKAGE__->register_method ({
814 name => 'init',
815 path => 'init',
816 method => 'POST',
817 description => "Create initial ceph default configuration and setup symlinks.",
818 proxyto => 'node',
819 protected => 1,
90c75580
TL
820 permissions => {
821 check => ['perm', '/', [ 'Sys.Modify' ]],
822 },
7d4fc5ef 823 parameters => {
be753927 824 additionalProperties => 0,
7d4fc5ef
DM
825 properties => {
826 node => get_standard_option('pve-node'),
827 network => {
be753927 828 description => "Use specific network for all ceph related traffic",
7d4fc5ef
DM
829 type => 'string', format => 'CIDR',
830 optional => 1,
831 maxLength => 128,
832 },
833 size => {
207f4932
FG
834 description => 'Targeted number of replicas per object',
835 type => 'integer',
836 default => 3,
837 optional => 1,
838 minimum => 1,
839 maximum => 7,
840 },
841 min_size => {
842 description => 'Minimum number of available replicas per object to allow I/O',
7d4fc5ef
DM
843 type => 'integer',
844 default => 2,
845 optional => 1,
846 minimum => 1,
83663637 847 maximum => 7,
7d4fc5ef
DM
848 },
849 pg_bits => {
3cba09d5
FG
850 description => "Placement group bits, used to specify the " .
851 "default number of placement groups.\n\nNOTE: 'osd pool " .
852 "default pg num' does not work for default pools.",
7d4fc5ef
DM
853 type => 'integer',
854 default => 6,
855 optional => 1,
856 minimum => 6,
857 maximum => 14,
858 },
4280f25c 859 disable_cephx => {
97f050bb
FG
860 description => "Disable cephx authentification.\n\n" .
861 "WARNING: cephx is a security feature protecting against " .
862 "man-in-the-middle attacks. Only consider disabling cephx ".
863 "if your network is private!",
77bb90b0
AD
864 type => 'boolean',
865 optional => 1,
866 default => 0,
4280f25c 867 },
7d4fc5ef
DM
868 },
869 },
870 returns => { type => 'null' },
871 code => sub {
872 my ($param) = @_;
873
c64c04dd
AA
874 my $version = PVE::CephTools::get_local_version(1);
875
876 if (!$version || $version < 12) {
e16cd81f 877 die "Ceph Luminous required - please run 'pveceph install'\n";
c64c04dd
AA
878 } else {
879 PVE::CephTools::check_ceph_installed('ceph_bin');
880 }
7d4fc5ef
DM
881
882 # simply load old config if it already exists
883 my $cfg = PVE::CephTools::parse_ceph_config();
884
885 if (!$cfg->{global}) {
886
887 my $fsid;
888 my $uuid;
889
890 UUID::generate($uuid);
891 UUID::unparse($uuid, $fsid);
892
4280f25c 893 my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
77bb90b0 894
7d4fc5ef
DM
895 $cfg->{global} = {
896 'fsid' => $fsid,
77bb90b0
AD
897 'auth cluster required' => $auth,
898 'auth service required' => $auth,
899 'auth client required' => $auth,
7d4fc5ef 900 'osd journal size' => $pve_osd_default_journal_size,
207f4932
FG
901 'osd pool default size' => $param->{size} // 3,
902 'osd pool default min size' => $param->{min_size} // 2,
2e9d791e 903 'mon allow pool delete' => 'true',
7d4fc5ef
DM
904 };
905
be753927 906 # this does not work for default pools
7d4fc5ef 907 #'osd pool default pg num' => $pg_num,
be753927 908 #'osd pool default pgp num' => $pg_num,
7d4fc5ef 909 }
be753927 910
7d4fc5ef
DM
911 $cfg->{global}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
912 $cfg->{osd}->{keyring} = '/var/lib/ceph/osd/ceph-$id/keyring';
913
7d4fc5ef
DM
914 if ($param->{pg_bits}) {
915 $cfg->{global}->{'osd pg bits'} = $param->{pg_bits};
916 $cfg->{global}->{'osd pgp bits'} = $param->{pg_bits};
917 }
918
919 if ($param->{network}) {
920 $cfg->{global}->{'public network'} = $param->{network};
921 $cfg->{global}->{'cluster network'} = $param->{network};
922 }
923
924 PVE::CephTools::write_ceph_config($cfg);
925
926 PVE::CephTools::setup_pve_symlinks();
927
928 return undef;
929 }});
930
9aad4e2e
TL
931my $find_mon_ip = sub {
932 my ($pubnet, $node, $overwrite_ip) = @_;
7d4fc5ef 933
9aad4e2e
TL
934 if (!$pubnet) {
935 return $overwrite_ip // PVE::Cluster::remote_node_ip($node);
936 }
905c2f51 937
9aad4e2e
TL
938 my $allowed_ips = PVE::Network::get_local_ip_from_cidr($pubnet);
939 die "No IP configured and up from ceph public network '$pubnet'\n"
940 if scalar(@$allowed_ips) < 1;
7d4fc5ef 941
9aad4e2e
TL
942 if (!$overwrite_ip) {
943 if (scalar(@$allowed_ips) == 1) {
944 return $allowed_ips->[0];
945 }
946 die "Multiple IPs for ceph public network '$pubnet' detected on $node:\n".
947 join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
948 } else {
949 if (grep { $_ eq $overwrite_ip } @$allowed_ips) {
950 return $overwrite_ip;
951 }
952 die "Monitor IP '$overwrite_ip' not in ceph public network '$pubnet'\n"
953 if !PVE::Network::is_ip_in_cidr($overwrite_ip, $pubnet);
7d4fc5ef 954
9aad4e2e
TL
955 die "Specified monitor IP '$overwrite_ip' not configured or up on $node!\n";
956 }
7d4fc5ef
DM
957};
958
c05ff7b4
DC
959my $create_mgr = sub {
960 my ($rados, $id) = @_;
961
962 my $clustername = PVE::CephTools::get_config('ccname');
963 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$id";
964 my $mgrkeyring = "$mgrdir/keyring";
965 my $mgrname = "mgr.$id";
966
967 die "ceph manager directory '$mgrdir' already exists\n"
968 if -d $mgrdir;
969
970 print "creating manager directory '$mgrdir'\n";
971 mkdir $mgrdir;
972 print "creating keys for '$mgrname'\n";
973 my $output = $rados->mon_command({ prefix => 'auth get-or-create',
974 entity => $mgrname,
975 caps => [
976 mon => 'allow profile mgr',
977 osd => 'allow *',
978 mds => 'allow *',
979 ],
980 format => 'plain'});
981 PVE::Tools::file_set_contents($mgrkeyring, $output);
982
983 print "setting owner for directory\n";
984 run_command(["chown", 'ceph:ceph', '-R', $mgrdir]);
985
986 print "enabling service 'ceph-mgr\@$id.service'\n";
987 PVE::CephTools::ceph_service_cmd('enable', $mgrname);
988 print "starting service 'ceph-mgr\@$id.service'\n";
989 PVE::CephTools::ceph_service_cmd('start', $mgrname);
990};
991
992my $destroy_mgr = sub {
993 my ($mgrid) = @_;
994
995 my $clustername = PVE::CephTools::get_config('ccname');
996 my $mgrname = "mgr.$mgrid";
997 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
998
999 die "ceph manager directory '$mgrdir' not found\n"
1000 if ! -d $mgrdir;
1001
1002 print "disabling service 'ceph-mgr\@$mgrid.service'\n";
1003 PVE::CephTools::ceph_service_cmd('disable', $mgrname);
1004 print "stopping service 'ceph-mgr\@$mgrid.service'\n";
1005 PVE::CephTools::ceph_service_cmd('stop', $mgrname);
1006
1007 print "removing manager directory '$mgrdir'\n";
1008 File::Path::remove_tree($mgrdir);
1009};
1010
7d4fc5ef
DM
1011__PACKAGE__->register_method ({
1012 name => 'createmon',
1013 path => 'mon',
1014 method => 'POST',
c05ff7b4 1015 description => "Create Ceph Monitor and Manager",
7d4fc5ef
DM
1016 proxyto => 'node',
1017 protected => 1,
90c75580
TL
1018 permissions => {
1019 check => ['perm', '/', [ 'Sys.Modify' ]],
1020 },
7d4fc5ef 1021 parameters => {
be753927 1022 additionalProperties => 0,
7d4fc5ef
DM
1023 properties => {
1024 node => get_standard_option('pve-node'),
c05ff7b4
DC
1025 id => {
1026 type => 'string',
1027 optional => 1,
1028 pattern => '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1029 description => "The ID for the monitor, when omitted the same as the nodename",
1030 },
1031 'exclude-manager' => {
1032 type => 'boolean',
1033 optional => 1,
1034 default => 0,
1035 description => "When set, only a monitor will be created.",
1036 },
9aad4e2e
TL
1037 'mon-address' => {
1038 description => 'Overwrites autodetected monitor IP address. ' .
1039 'Must be in the public network of ceph.',
1040 type => 'string', format => 'ip',
1041 optional => 1,
1042 },
7d4fc5ef
DM
1043 },
1044 },
1045 returns => { type => 'string' },
1046 code => sub {
1047 my ($param) = @_;
1048
c64c04dd
AA
1049 PVE::CephTools::check_ceph_installed('ceph_mon');
1050
1051 PVE::CephTools::check_ceph_installed('ceph_mgr')
1052 if (!$param->{'exclude-manager'});
1053
7d4fc5ef
DM
1054 PVE::CephTools::check_ceph_inited();
1055
1056 PVE::CephTools::setup_pve_symlinks();
1057
1058 my $rpcenv = PVE::RPCEnvironment::get();
1059
1060 my $authuser = $rpcenv->get_user();
1061
1062 my $cfg = PVE::CephTools::parse_ceph_config();
1063
1064 my $moncount = 0;
1065
be753927 1066 my $monaddrhash = {};
7d4fc5ef 1067
3279b1d2
WL
1068 my $systemd_managed = PVE::CephTools::systemd_managed();
1069
7d4fc5ef
DM
1070 foreach my $section (keys %$cfg) {
1071 next if $section eq 'global';
1072 my $d = $cfg->{$section};
1073 if ($section =~ m/^mon\./) {
1074 $moncount++;
1075 if ($d->{'mon addr'}) {
1076 $monaddrhash->{$d->{'mon addr'}} = $section;
1077 }
1078 }
1079 }
38db610a 1080
c05ff7b4 1081 my $monid = $param->{id} // $param->{node};
38db610a 1082
be753927 1083 my $monsection = "mon.$monid";
9aad4e2e
TL
1084 my $pubnet = $cfg->{global}->{'public network'};
1085 my $ip = $find_mon_ip->($pubnet, $param->{node}, $param->{'mon-address'});
f7e342ea 1086
98901f1d 1087 my $monaddr = Net::IP::ip_is_ipv6($ip) ? "[$ip]:6789" : "$ip:6789";
38db610a
DM
1088 my $monname = $param->{node};
1089
1090 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
be753927 1091 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
38db610a
DM
1092 if $monaddrhash->{$monaddr};
1093
52d7be41
DM
1094 my $worker = sub {
1095 my $upid = shift;
38db610a 1096
a34866f0
DM
1097 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
1098
52d7be41
DM
1099 if (! -f $pve_ckeyring_path) {
1100 run_command("ceph-authtool $pve_ckeyring_path --create-keyring " .
1101 "--gen-key -n client.admin");
1102 }
38db610a 1103
a34866f0 1104 my $pve_mon_key_path = PVE::CephTools::get_config('pve_mon_key_path');
52d7be41
DM
1105 if (! -f $pve_mon_key_path) {
1106 run_command("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
1107 run_command("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
1108 "--cap mds 'allow' " .
1109 "--cap osd 'allow *' " .
d197634b 1110 "--cap mgr 'allow *' " .
52d7be41 1111 "--cap mon 'allow *'");
3279b1d2
WL
1112 run_command("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1113 run_command("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
52d7be41
DM
1114 run_command("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
1115 run_command("mv $pve_mon_key_path.tmp $pve_mon_key_path");
38db610a
DM
1116 }
1117
a34866f0
DM
1118 my $ccname = PVE::CephTools::get_config('ccname');
1119
52d7be41
DM
1120 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1121 -d $mondir && die "monitor filesystem '$mondir' already exist\n";
be753927 1122
52d7be41 1123 my $monmap = "/tmp/monmap";
c05ff7b4 1124
52d7be41
DM
1125 eval {
1126 mkdir $mondir;
1127
3279b1d2
WL
1128 run_command("chown ceph:ceph $mondir") if $systemd_managed;
1129
52d7be41 1130 if ($moncount > 0) {
87eb0fc2 1131 my $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
970236b3
DM
1132 my $mapdata = $rados->mon_command({ prefix => 'mon getmap', format => 'plain' });
1133 PVE::Tools::file_set_contents($monmap, $mapdata);
52d7be41
DM
1134 } else {
1135 run_command("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
1136 }
38db610a 1137
52d7be41 1138 run_command("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
3279b1d2 1139 run_command("chown ceph:ceph -R $mondir") if $systemd_managed;
52d7be41
DM
1140 };
1141 my $err = $@;
1142 unlink $monmap;
1143 if ($err) {
1144 File::Path::remove_tree($mondir);
1145 die $err;
1146 }
38db610a 1147
52d7be41
DM
1148 $cfg->{$monsection} = {
1149 'host' => $monname,
1150 'mon addr' => $monaddr,
1151 };
38db610a 1152
a34866f0 1153 PVE::CephTools::write_ceph_config($cfg);
38db610a 1154
cc5bb515
FG
1155 my $create_keys_pid = fork();
1156 if (!defined($create_keys_pid)) {
1157 die "Could not spawn ceph-create-keys to create bootstrap keys\n";
1158 } elsif ($create_keys_pid == 0) {
1159 exit PVE::Tools::run_command(['ceph-create-keys', '-i', $monid]);
1160 } else {
1161 PVE::CephTools::ceph_service_cmd('start', $monsection);
19bada0c 1162
cc5bb515
FG
1163 if ($systemd_managed) {
1164 #to ensure we have the correct startup order.
1165 eval { PVE::Tools::run_command(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
1166 warn "Enable ceph-mon\@${monid}.service manually"if $@;
1167 }
1168 waitpid($create_keys_pid, 0);
19bada0c 1169 }
c05ff7b4
DC
1170
1171 # create manager
1172 if (!$param->{'exclude-manager'}) {
87eb0fc2 1173 my $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
c05ff7b4
DC
1174 $create_mgr->($rados, $monid);
1175 }
52d7be41 1176 };
38db610a 1177
52d7be41 1178 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
38db610a
DM
1179 }});
1180
1181__PACKAGE__->register_method ({
1182 name => 'destroymon',
39e1ad70
DM
1183 path => 'mon/{monid}',
1184 method => 'DELETE',
c05ff7b4 1185 description => "Destroy Ceph Monitor and Manager.",
38db610a
DM
1186 proxyto => 'node',
1187 protected => 1,
90c75580
TL
1188 permissions => {
1189 check => ['perm', '/', [ 'Sys.Modify' ]],
1190 },
38db610a 1191 parameters => {
be753927 1192 additionalProperties => 0,
38db610a
DM
1193 properties => {
1194 node => get_standard_option('pve-node'),
1195 monid => {
1196 description => 'Monitor ID',
c05ff7b4
DC
1197 type => 'string',
1198 pattern => '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
38db610a 1199 },
c05ff7b4
DC
1200 'exclude-manager' => {
1201 type => 'boolean',
1202 default => 0,
1203 optional => 1,
1204 description => "When set, removes only the monitor, not the manager"
1205 }
38db610a
DM
1206 },
1207 },
52d7be41 1208 returns => { type => 'string' },
38db610a
DM
1209 code => sub {
1210 my ($param) = @_;
1211
52d7be41
DM
1212 my $rpcenv = PVE::RPCEnvironment::get();
1213
1214 my $authuser = $rpcenv->get_user();
1215
a34866f0 1216 PVE::CephTools::check_ceph_inited();
38db610a 1217
a34866f0 1218 my $cfg = PVE::CephTools::parse_ceph_config();
be753927 1219
38db610a 1220 my $monid = $param->{monid};
be753927 1221 my $monsection = "mon.$monid";
38db610a 1222
36fd0190 1223 my $rados = PVE::RADOS->new();
970236b3 1224 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
38db610a
DM
1225 my $monlist = $monstat->{monmap}->{mons};
1226
be753927 1227 die "no such monitor id '$monid'\n"
38db610a
DM
1228 if !defined($cfg->{$monsection});
1229
a34866f0
DM
1230 my $ccname = PVE::CephTools::get_config('ccname');
1231
38db610a
DM
1232 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1233 -d $mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
1234
1235 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
1236
52d7be41
DM
1237 my $worker = sub {
1238 my $upid = shift;
38db610a 1239
2f804640 1240 # reopen with longer timeout
be753927 1241 $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
f26b46db 1242
6e3c2f47 1243 $rados->mon_command({ prefix => "mon remove", name => $monid, format => 'plain' });
38db610a 1244
a34866f0 1245 eval { PVE::CephTools::ceph_service_cmd('stop', $monsection); };
52d7be41 1246 warn $@ if $@;
38db610a 1247
52d7be41 1248 delete $cfg->{$monsection};
a34866f0 1249 PVE::CephTools::write_ceph_config($cfg);
52d7be41 1250 File::Path::remove_tree($mondir);
c05ff7b4
DC
1251
1252 # remove manager
1253 if (!$param->{'exclude-manager'}) {
1254 eval { $destroy_mgr->($monid); };
1255 warn $@ if $@;
1256 }
52d7be41
DM
1257 };
1258
1259 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
38db610a
DM
1260 }});
1261
ca68ac3e
DC
1262__PACKAGE__->register_method ({
1263 name => 'createmgr',
1264 path => 'mgr',
1265 method => 'POST',
1266 description => "Create Ceph Manager",
1267 proxyto => 'node',
1268 protected => 1,
1269 permissions => {
1270 check => ['perm', '/', [ 'Sys.Modify' ]],
1271 },
1272 parameters => {
1273 additionalProperties => 0,
1274 properties => {
1275 node => get_standard_option('pve-node'),
1276 id => {
1277 type => 'string',
1278 optional => 1,
1279 pattern => '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1280 description => "The ID for the manager, when omitted the same as the nodename",
1281 },
1282 },
1283 },
1284 returns => { type => 'string' },
1285 code => sub {
1286 my ($param) = @_;
1287
c64c04dd
AA
1288 PVE::CephTools::check_ceph_installed('ceph_mgr');
1289
ca68ac3e
DC
1290 PVE::CephTools::check_ceph_inited();
1291
1292 my $rpcenv = PVE::RPCEnvironment::get();
1293
1294 my $authuser = $rpcenv->get_user();
1295
1296 my $mgrid = $param->{id} // $param->{node};
1297
1298 my $worker = sub {
1299 my $upid = shift;
1300
1301 my $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
1302
1303 $create_mgr->($rados, $mgrid);
1304 };
1305
1306 return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
1307 }});
1308
1309__PACKAGE__->register_method ({
1310 name => 'destroymgr',
1311 path => 'mgr/{id}',
1312 method => 'DELETE',
1313 description => "Destroy Ceph Manager.",
1314 proxyto => 'node',
1315 protected => 1,
1316 permissions => {
1317 check => ['perm', '/', [ 'Sys.Modify' ]],
1318 },
1319 parameters => {
1320 additionalProperties => 0,
1321 properties => {
1322 node => get_standard_option('pve-node'),
1323 id => {
1324 description => 'The ID of the manager',
1325 type => 'string',
1326 pattern => '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1327 },
1328 },
1329 },
1330 returns => { type => 'string' },
1331 code => sub {
1332 my ($param) = @_;
1333
1334 my $rpcenv = PVE::RPCEnvironment::get();
1335
1336 my $authuser = $rpcenv->get_user();
1337
1338 PVE::CephTools::check_ceph_inited();
1339
1340 my $mgrid = $param->{id};
1341
1342 my $worker = sub {
1343 my $upid = shift;
1344
1345 $destroy_mgr->($mgrid);
1346 };
1347
1348 return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
1349 }});
1350
38db610a
DM
1351__PACKAGE__->register_method ({
1352 name => 'stop',
1353 path => 'stop',
1354 method => 'POST',
1355 description => "Stop ceph services.",
1356 proxyto => 'node',
1357 protected => 1,
90c75580
TL
1358 permissions => {
1359 check => ['perm', '/', [ 'Sys.Modify' ]],
1360 },
38db610a 1361 parameters => {
be753927 1362 additionalProperties => 0,
38db610a
DM
1363 properties => {
1364 node => get_standard_option('pve-node'),
68e0c4bd
DM
1365 service => {
1366 description => 'Ceph service name.',
1367 type => 'string',
1368 optional => 1,
b0e5ae21 1369 pattern => '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
68e0c4bd 1370 },
38db610a
DM
1371 },
1372 },
68e0c4bd 1373 returns => { type => 'string' },
38db610a
DM
1374 code => sub {
1375 my ($param) = @_;
1376
68e0c4bd
DM
1377 my $rpcenv = PVE::RPCEnvironment::get();
1378
1379 my $authuser = $rpcenv->get_user();
1380
a34866f0 1381 PVE::CephTools::check_ceph_inited();
38db610a 1382
a34866f0 1383 my $cfg = PVE::CephTools::parse_ceph_config();
38db610a
DM
1384 scalar(keys %$cfg) || die "no configuration\n";
1385
68e0c4bd
DM
1386 my $worker = sub {
1387 my $upid = shift;
38db610a 1388
68e0c4bd
DM
1389 my $cmd = ['stop'];
1390 if ($param->{service}) {
1391 push @$cmd, $param->{service};
1392 }
1393
a34866f0 1394 PVE::CephTools::ceph_service_cmd(@$cmd);
68e0c4bd
DM
1395 };
1396
1397 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
1398 $authuser, $worker);
38db610a
DM
1399 }});
1400
1401__PACKAGE__->register_method ({
1402 name => 'start',
1403 path => 'start',
1404 method => 'POST',
1405 description => "Start ceph services.",
1406 proxyto => 'node',
1407 protected => 1,
90c75580
TL
1408 permissions => {
1409 check => ['perm', '/', [ 'Sys.Modify' ]],
1410 },
38db610a 1411 parameters => {
be753927 1412 additionalProperties => 0,
38db610a
DM
1413 properties => {
1414 node => get_standard_option('pve-node'),
68e0c4bd
DM
1415 service => {
1416 description => 'Ceph service name.',
1417 type => 'string',
1418 optional => 1,
b0e5ae21 1419 pattern => '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
68e0c4bd 1420 },
38db610a
DM
1421 },
1422 },
68e0c4bd 1423 returns => { type => 'string' },
38db610a
DM
1424 code => sub {
1425 my ($param) = @_;
1426
68e0c4bd
DM
1427 my $rpcenv = PVE::RPCEnvironment::get();
1428
1429 my $authuser = $rpcenv->get_user();
1430
a34866f0 1431 PVE::CephTools::check_ceph_inited();
38db610a 1432
a34866f0 1433 my $cfg = PVE::CephTools::parse_ceph_config();
38db610a
DM
1434 scalar(keys %$cfg) || die "no configuration\n";
1435
68e0c4bd
DM
1436 my $worker = sub {
1437 my $upid = shift;
38db610a 1438
68e0c4bd
DM
1439 my $cmd = ['start'];
1440 if ($param->{service}) {
1441 push @$cmd, $param->{service};
1442 }
1443
a34866f0 1444 PVE::CephTools::ceph_service_cmd(@$cmd);
68e0c4bd
DM
1445 };
1446
1447 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
1448 $authuser, $worker);
38db610a
DM
1449 }});
1450
1451__PACKAGE__->register_method ({
1452 name => 'status',
1453 path => 'status',
1454 method => 'GET',
1455 description => "Get ceph status.",
1456 proxyto => 'node',
1457 protected => 1,
90c75580
TL
1458 permissions => {
1459 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1460 },
38db610a 1461 parameters => {
be753927 1462 additionalProperties => 0,
38db610a
DM
1463 properties => {
1464 node => get_standard_option('pve-node'),
1465 },
1466 },
1467 returns => { type => 'object' },
1468 code => sub {
1469 my ($param) = @_;
1470
a34866f0 1471 PVE::CephTools::check_ceph_enabled();
38db610a 1472
36fd0190 1473 my $rados = PVE::RADOS->new();
84caf265
DC
1474 my $status = $rados->mon_command({ prefix => 'status' });
1475 $status->{health} = $rados->mon_command({ prefix => 'health', detail => 'detail' });
1476 return $status;
38db610a
DM
1477 }});
1478
b0537f7b
DM
1479__PACKAGE__->register_method ({
1480 name => 'lspools',
1481 path => 'pools',
1482 method => 'GET',
1483 description => "List all pools.",
1484 proxyto => 'node',
1485 protected => 1,
90c75580
TL
1486 permissions => {
1487 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1488 },
b0537f7b 1489 parameters => {
be753927 1490 additionalProperties => 0,
b0537f7b
DM
1491 properties => {
1492 node => get_standard_option('pve-node'),
1493 },
1494 },
1495 returns => {
1496 type => 'array',
1497 items => {
1498 type => "object",
1499 properties => {
1500 pool => { type => 'integer' },
1501 pool_name => { type => 'string' },
1502 size => { type => 'integer' },
1503 },
1504 },
1505 links => [ { rel => 'child', href => "{pool_name}" } ],
1506 },
1507 code => sub {
1508 my ($param) = @_;
1509
a34866f0 1510 PVE::CephTools::check_ceph_inited();
b0537f7b 1511
36fd0190 1512 my $rados = PVE::RADOS->new();
d54f1126
DM
1513
1514 my $stats = {};
1515 my $res = $rados->mon_command({ prefix => 'df' });
6f9ea1c1 1516
d54f1126
DM
1517 foreach my $d (@{$res->{pools}}) {
1518 next if !$d->{stats};
1519 next if !defined($d->{id});
1520 $stats->{$d->{id}} = $d->{stats};
1521 }
1522
1523 $res = $rados->mon_command({ prefix => 'osd dump' });
c9508b5d
DC
1524 my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'});
1525
1526 my $rules = {};
1527 for my $rule (@$rulestmp) {
1528 $rules->{$rule->{rule_id}} = $rule->{rule_name};
1529 }
b0537f7b
DM
1530
1531 my $data = [];
1532 foreach my $e (@{$res->{pools}}) {
1533 my $d = {};
2db28c03 1534 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
b0537f7b
DM
1535 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1536 }
c9508b5d
DC
1537
1538 if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) {
1539 $d->{crush_rule_name} = $rules->{$d->{crush_rule}};
1540 }
1541
d54f1126
DM
1542 if (my $s = $stats->{$d->{pool}}) {
1543 $d->{bytes_used} = $s->{bytes_used};
dae96e48 1544 $d->{percent_used} = $s->{percent_used};
d54f1126 1545 }
b0537f7b
DM
1546 push @$data, $d;
1547 }
1548
d54f1126 1549
b0537f7b
DM
1550 return $data;
1551 }});
1552
1553__PACKAGE__->register_method ({
1554 name => 'createpool',
7d4fc5ef 1555 path => 'pools',
38db610a 1556 method => 'POST',
7d4fc5ef 1557 description => "Create POOL",
38db610a
DM
1558 proxyto => 'node',
1559 protected => 1,
90c75580
TL
1560 permissions => {
1561 check => ['perm', '/', [ 'Sys.Modify' ]],
1562 },
38db610a 1563 parameters => {
be753927 1564 additionalProperties => 0,
38db610a
DM
1565 properties => {
1566 node => get_standard_option('pve-node'),
7d4fc5ef
DM
1567 name => {
1568 description => "The name of the pool. It must be unique.",
38db610a 1569 type => 'string',
43d85563 1570 },
7d4fc5ef
DM
1571 size => {
1572 description => 'Number of replicas per object',
1573 type => 'integer',
6747b0a9 1574 default => 3,
0e5816e4 1575 optional => 1,
7d4fc5ef 1576 minimum => 1,
83663637 1577 maximum => 7,
0e5816e4 1578 },
7d4fc5ef
DM
1579 min_size => {
1580 description => 'Minimum number of replicas per object',
1581 type => 'integer',
6747b0a9 1582 default => 2,
7d4fc5ef
DM
1583 optional => 1,
1584 minimum => 1,
83663637 1585 maximum => 7,
7d4fc5ef
DM
1586 },
1587 pg_num => {
1588 description => "Number of placement groups.",
1589 type => 'integer',
1590 default => 64,
1591 optional => 1,
1592 minimum => 8,
1593 maximum => 32768,
1594 },
2db28c03
DC
1595 crush_rule => {
1596 description => "The rule to use for mapping object placement in the cluster.",
1597 type => 'string',
43d85563
DM
1598 optional => 1,
1599 },
6c11e921
DC
1600 application => {
1601 description => "The application of the pool, 'rbd' by default.",
1602 type => 'string',
1603 enum => ['rbd', 'cephfs', 'rgw'],
1604 optional => 1,
60811ad7
FG
1605 },
1606 add_storages => {
1607 description => "Configure VM and CT storages using the new pool.",
1608 type => 'boolean',
1609 optional => 1,
1610 },
38db610a
DM
1611 },
1612 },
8c426204 1613 returns => { type => 'string' },
38db610a
DM
1614 code => sub {
1615 my ($param) = @_;
1616
68f4def4 1617 PVE::Cluster::check_cfs_quorum();
a34866f0 1618 PVE::CephTools::check_ceph_inited();
38db610a 1619
7d4fc5ef 1620 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
13f4d762 1621
be753927 1622 die "not fully configured - missing '$pve_ckeyring_path'\n"
7d4fc5ef 1623 if ! -f $pve_ckeyring_path;
13f4d762 1624
0c0d43a7 1625 my $pool = $param->{name};
8c426204
FG
1626 my $rpcenv = PVE::RPCEnvironment::get();
1627 my $user = $rpcenv->get_user();
0c0d43a7 1628
60811ad7 1629 if ($param->{add_storages}) {
60811ad7
FG
1630 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
1631 die "pool name contains characters which are illegal for storage naming\n"
1632 if !PVE::JSONSchema::parse_storage_id($pool);
1633 }
1634
7d4fc5ef 1635 my $pg_num = $param->{pg_num} || 64;
6747b0a9
DC
1636 my $size = $param->{size} || 3;
1637 my $min_size = $param->{min_size} || 2;
6c11e921 1638 my $application = $param->{application} // 'rbd';
38db610a 1639
8c426204 1640 my $worker = sub {
a34866f0 1641
8c426204
FG
1642 my $rados = PVE::RADOS->new();
1643 $rados->mon_command({
1644 prefix => "osd pool create",
1645 pool => $pool,
1646 pg_num => int($pg_num),
1647 format => 'plain',
1648 });
0e5816e4 1649
be753927 1650 $rados->mon_command({
7d4fc5ef 1651 prefix => "osd pool set",
0c0d43a7 1652 pool => $pool,
8c426204
FG
1653 var => 'min_size',
1654 val => $min_size,
2db28c03 1655 format => 'plain',
7d4fc5ef 1656 });
52d7be41 1657
8c426204
FG
1658 $rados->mon_command({
1659 prefix => "osd pool set",
0c0d43a7 1660 pool => $pool,
8c426204
FG
1661 var => 'size',
1662 val => $size,
1663 format => 'plain',
1664 });
6c11e921 1665
8c426204
FG
1666 if (defined($param->{crush_rule})) {
1667 $rados->mon_command({
1668 prefix => "osd pool set",
1669 pool => $pool,
1670 var => 'crush_rule',
1671 val => $param->{crush_rule},
1672 format => 'plain',
1673 });
60811ad7 1674 }
8c426204
FG
1675
1676 $rados->mon_command({
1677 prefix => "osd pool application enable",
1678 pool => $pool,
1679 app => $application,
1680 });
1681
1682 if ($param->{add_storages}) {
1683 my $err;
1684 eval { $add_storage->($pool, "${pool}_vm", 0); };
1685 if ($@) {
1686 warn "failed to add VM storage: $@";
1687 $err = 1;
1688 }
1689 eval { $add_storage->($pool, "${pool}_ct", 1); };
1690 if ($@) {
1691 warn "failed to add CT storage: $@";
1692 $err = 1;
1693 }
1694 die "adding storages for pool '$pool' failed, check log and add manually!\n"
1695 if $err;
60811ad7 1696 }
8c426204 1697 };
60811ad7 1698
8c426204 1699 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
38db610a
DM
1700 }});
1701
a46ad02a
DC
1702__PACKAGE__->register_method ({
1703 name => 'get_flags',
1704 path => 'flags',
1705 method => 'GET',
1706 description => "get all set ceph flags",
1707 proxyto => 'node',
1708 protected => 1,
1709 permissions => {
1710 check => ['perm', '/', [ 'Sys.Audit' ]],
1711 },
1712 parameters => {
1713 additionalProperties => 0,
1714 properties => {
1715 node => get_standard_option('pve-node'),
1716 },
1717 },
1718 returns => { type => 'string' },
1719 code => sub {
1720 my ($param) = @_;
1721
1722 PVE::CephTools::check_ceph_inited();
1723
1724 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
1725
1726 die "not fully configured - missing '$pve_ckeyring_path'\n"
1727 if ! -f $pve_ckeyring_path;
1728
1729 my $rados = PVE::RADOS->new();
1730
1731 my $stat = $rados->mon_command({ prefix => 'osd dump' });
1732
1733 return $stat->{flags} // '';
1734 }});
1735
1736__PACKAGE__->register_method ({
1737 name => 'set_flag',
1738 path => 'flags/{flag}',
1739 method => 'POST',
1740 description => "Set a ceph flag",
1741 proxyto => 'node',
1742 protected => 1,
1743 permissions => {
1744 check => ['perm', '/', [ 'Sys.Modify' ]],
1745 },
1746 parameters => {
1747 additionalProperties => 0,
1748 properties => {
1749 node => get_standard_option('pve-node'),
1750 flag => {
1751 description => 'The ceph flag to set/unset',
1752 type => 'string',
1753 enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1754 },
1755 },
1756 },
1757 returns => { type => 'null' },
1758 code => sub {
1759 my ($param) = @_;
1760
1761 PVE::CephTools::check_ceph_inited();
1762
1763 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
1764
1765 die "not fully configured - missing '$pve_ckeyring_path'\n"
1766 if ! -f $pve_ckeyring_path;
1767
1768 my $set = $param->{set} // !$param->{unset};
1769 my $rados = PVE::RADOS->new();
1770
1771 $rados->mon_command({
1772 prefix => "osd set",
1773 key => $param->{flag},
1774 });
1775
1776 return undef;
1777 }});
1778
1779__PACKAGE__->register_method ({
1780 name => 'unset_flag',
1781 path => 'flags/{flag}',
1782 method => 'DELETE',
1783 description => "Unset a ceph flag",
1784 proxyto => 'node',
1785 protected => 1,
1786 permissions => {
1787 check => ['perm', '/', [ 'Sys.Modify' ]],
1788 },
1789 parameters => {
1790 additionalProperties => 0,
1791 properties => {
1792 node => get_standard_option('pve-node'),
1793 flag => {
1794 description => 'The ceph flag to set/unset',
1795 type => 'string',
1796 enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1797 },
1798 },
1799 },
1800 returns => { type => 'null' },
1801 code => sub {
1802 my ($param) = @_;
1803
1804 PVE::CephTools::check_ceph_inited();
1805
1806 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
1807
1808 die "not fully configured - missing '$pve_ckeyring_path'\n"
1809 if ! -f $pve_ckeyring_path;
1810
1811 my $set = $param->{set} // !$param->{unset};
1812 my $rados = PVE::RADOS->new();
1813
1814 $rados->mon_command({
1815 prefix => "osd unset",
1816 key => $param->{flag},
1817 });
1818
1819 return undef;
1820 }});
1821
38db610a 1822__PACKAGE__->register_method ({
7d4fc5ef
DM
1823 name => 'destroypool',
1824 path => 'pools/{name}',
39e1ad70 1825 method => 'DELETE',
7d4fc5ef 1826 description => "Destroy pool",
38db610a
DM
1827 proxyto => 'node',
1828 protected => 1,
90c75580
TL
1829 permissions => {
1830 check => ['perm', '/', [ 'Sys.Modify' ]],
1831 },
38db610a 1832 parameters => {
be753927 1833 additionalProperties => 0,
38db610a
DM
1834 properties => {
1835 node => get_standard_option('pve-node'),
7d4fc5ef
DM
1836 name => {
1837 description => "The name of the pool. It must be unique.",
1838 type => 'string',
0e5816e4 1839 },
76dc2ad0
DC
1840 force => {
1841 description => "If true, destroys pool even if in use",
1842 type => 'boolean',
1843 optional => 1,
1844 default => 0,
f6144f34
FG
1845 },
1846 remove_storages => {
1847 description => "Remove all pveceph-managed storages configured for this pool",
1848 type => 'boolean',
1849 optional => 1,
1850 default => 0,
1851 },
38db610a
DM
1852 },
1853 },
8c426204 1854 returns => { type => 'string' },
38db610a
DM
1855 code => sub {
1856 my ($param) = @_;
1857
a34866f0 1858 PVE::CephTools::check_ceph_inited();
38db610a 1859
f6144f34
FG
1860 my $rpcenv = PVE::RPCEnvironment::get();
1861 my $user = $rpcenv->get_user();
1862 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
1863 if $param->{remove_storages};
1864
0c0d43a7 1865 my $pool = $param->{name};
76dc2ad0 1866
8c426204 1867 my $worker = sub {
0a521b66
FG
1868 my $storages = $get_storages->($pool);
1869
1870 # if not forced, destroy ceph pool only when no
1871 # vm disks are on it anymore
1872 if (!$param->{force}) {
1873 my $storagecfg = PVE::Storage::config();
1874 foreach my $storeid (keys %$storages) {
1875 my $storage = $storages->{$storeid};
1876
1877 # check if any vm disks are on the pool
1878 print "checking storage '$storeid' for RBD images..\n";
1879 my $res = PVE::Storage::vdisk_list($storagecfg, $storeid);
1880 die "ceph pool '$pool' still in use by storage '$storeid'\n"
1881 if @{$res->{$storeid}} != 0;
1882 }
1883 }
1884
8c426204
FG
1885 my $rados = PVE::RADOS->new();
1886 # fixme: '--yes-i-really-really-mean-it'
1887 $rados->mon_command({
1888 prefix => "osd pool delete",
1889 pool => $pool,
1890 pool2 => $pool,
1891 sure => '--yes-i-really-really-mean-it',
1892 format => 'plain',
1893 });
1894
1895 if ($param->{remove_storages}) {
1896 my $err;
1897 foreach my $storeid (keys %$storages) {
1898 # skip external clusters, not managed by pveceph
1899 next if $storages->{$storeid}->{monhost};
1900 eval { PVE::API2::Storage::Config->delete({storage => $storeid}) };
1901 if ($@) {
1902 warn "failed to remove storage '$storeid': $@\n";
1903 $err = 1;
1904 }
f6144f34 1905 }
8c426204
FG
1906 die "failed to remove (some) storages - check log and remove manually!\n"
1907 if $err;
f6144f34 1908 }
8c426204
FG
1909 };
1910 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
38db610a 1911 }});
2f692121 1912
a34866f0 1913
2f692121
DM
1914__PACKAGE__->register_method ({
1915 name => 'crush',
1916 path => 'crush',
1917 method => 'GET',
1918 description => "Get OSD crush map",
1919 proxyto => 'node',
1920 protected => 1,
90c75580
TL
1921 permissions => {
1922 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1923 },
2f692121 1924 parameters => {
be753927 1925 additionalProperties => 0,
2f692121
DM
1926 properties => {
1927 node => get_standard_option('pve-node'),
1928 },
1929 },
1930 returns => { type => 'string' },
1931 code => sub {
1932 my ($param) = @_;
1933
a34866f0 1934 PVE::CephTools::check_ceph_inited();
2f692121 1935
8b336060
DM
1936 # this produces JSON (difficult to read for the user)
1937 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
2f692121 1938
8b336060
DM
1939 my $txt = '';
1940
1941 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1942 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1943
36fd0190 1944 my $rados = PVE::RADOS->new();
be753927 1945
8b336060 1946 eval {
970236b3
DM
1947 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
1948 PVE::Tools::file_set_contents($mapfile, $bindata);
8b336060
DM
1949 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1950 $txt = PVE::Tools::file_get_contents($mapdata);
1951 };
1952 my $err = $@;
1953
1954 unlink $mapfile;
1955 unlink $mapdata;
1956
1957 die $err if $err;
be753927 1958
2f692121
DM
1959 return $txt;
1960 }});
1961
570278fa 1962__PACKAGE__->register_method({
be753927
DC
1963 name => 'log',
1964 path => 'log',
570278fa
DM
1965 method => 'GET',
1966 description => "Read ceph log",
1967 proxyto => 'node',
1968 permissions => {
1969 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1970 },
1971 protected => 1,
1972 parameters => {
be753927 1973 additionalProperties => 0,
570278fa
DM
1974 properties => {
1975 node => get_standard_option('pve-node'),
1976 start => {
1977 type => 'integer',
1978 minimum => 0,
1979 optional => 1,
1980 },
1981 limit => {
1982 type => 'integer',
1983 minimum => 0,
1984 optional => 1,
1985 },
1986 },
1987 },
1988 returns => {
1989 type => 'array',
be753927 1990 items => {
570278fa
DM
1991 type => "object",
1992 properties => {
1993 n => {
1994 description=> "Line number",
1995 type=> 'integer',
1996 },
1997 t => {
1998 description=> "Line text",
1999 type => 'string',
2000 }
2001 }
2002 }
2003 },
2004 code => sub {
2005 my ($param) = @_;
2006
2007 my $rpcenv = PVE::RPCEnvironment::get();
2008 my $user = $rpcenv->get_user();
2009 my $node = $param->{node};
2010
2011 my $logfile = "/var/log/ceph/ceph.log";
2012 my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
2013
2014 $rpcenv->set_result_attrib('total', $count);
be753927
DC
2015
2016 return $lines;
570278fa
DM
2017 }});
2018
d2692b86
DC
2019__PACKAGE__->register_method ({
2020 name => 'rules',
2021 path => 'rules',
2022 method => 'GET',
2023 description => "List ceph rules.",
2024 proxyto => 'node',
2025 protected => 1,
2026 permissions => {
2027 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
2028 },
2029 parameters => {
2030 additionalProperties => 0,
2031 properties => {
2032 node => get_standard_option('pve-node'),
2033 },
2034 },
2035 returns => {
2036 type => 'array',
2037 items => {
2038 type => "object",
2039 properties => {},
2040 },
2041 links => [ { rel => 'child', href => "{name}" } ],
2042 },
2043 code => sub {
2044 my ($param) = @_;
2045
2046 PVE::CephTools::check_ceph_inited();
2f692121 2047
d2692b86
DC
2048 my $rados = PVE::RADOS->new();
2049
2050 my $rules = $rados->mon_command({ prefix => 'osd crush rule ls' });
2051
2052 my $res = [];
2053
2054 foreach my $rule (@$rules) {
2055 push @$res, { name => $rule };
2056 }
2057
2058 return $res;
2059 }});