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