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