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