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