]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Ceph.pm
do not get smart data for ceph disk list
[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;
15use PVE::RESTHandler;
16use PVE::RPCEnvironment;
17use PVE::JSONSchema qw(get_standard_option);
970236b3 18use PVE::RADOS;
a34866f0 19use PVE::CephTools;
7f4924bd 20use PVE::Diskmanage;
38db610a
DM
21
22use base qw(PVE::RESTHandler);
23
24use Data::Dumper; # fixme: remove
25
7d4fc5ef
DM
26my $get_osd_status = sub {
27 my ($rados, $osdid) = @_;
0e5816e4 28
7d4fc5ef 29 my $stat = $rados->mon_command({ prefix => 'osd dump' });
2f804640 30
7d4fc5ef 31 my $osdlist = $stat->{osds} || [];
38db610a 32
7d4fc5ef
DM
33 my $osdstat;
34 foreach my $d (@$osdlist) {
35 $osdstat->{$d->{osd}} = $d if defined($d->{osd});
36 }
37 if (defined($osdid)) {
38 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
39 return $osdstat->{$osdid};
40 }
38db610a 41
7d4fc5ef
DM
42 return $osdstat;
43};
13f4d762 44
941c0195
DM
45my $get_osd_usage = sub {
46 my ($rados) = @_;
47
48 my $osdlist = $rados->mon_command({ prefix => 'pg dump',
49 dumpcontents => [ 'osds' ]}) || [];
50
51 my $osdstat;
52 foreach my $d (@$osdlist) {
53 $osdstat->{$d->{osd}} = $d if defined($d->{osd});
54 }
55
56 return $osdstat;
57};
58
7d4fc5ef
DM
59__PACKAGE__->register_method ({
60 name => 'index',
61 path => '',
62 method => 'GET',
63 description => "Get Ceph osd list/tree.",
64 proxyto => 'node',
65 protected => 1,
90c75580
TL
66 permissions => {
67 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
68 },
7d4fc5ef
DM
69 parameters => {
70 additionalProperties => 0,
71 properties => {
72 node => get_standard_option('pve-node'),
73 },
74 },
75 # fixme: return a list instead of extjs tree format ?
76 returns => {
77 type => "object",
78 },
79 code => sub {
80 my ($param) = @_;
13f4d762 81
7d4fc5ef 82 PVE::CephTools::check_ceph_inited();
13f4d762 83
7d4fc5ef
DM
84 my $rados = PVE::RADOS->new();
85 my $res = $rados->mon_command({ prefix => 'osd tree' });
13f4d762 86
7d4fc5ef 87 die "no tree nodes found\n" if !($res && $res->{nodes});
13f4d762 88
7d4fc5ef 89 my $osdhash = &$get_osd_status($rados);
13f4d762 90
941c0195
DM
91 my $usagehash = &$get_osd_usage($rados);
92
7d4fc5ef
DM
93 my $nodes = {};
94 my $newnodes = {};
95 foreach my $e (@{$res->{nodes}}) {
96 $nodes->{$e->{id}} = $e;
97
98 my $new = {
99 id => $e->{id},
100 name => $e->{name},
101 type => $e->{type}
102 };
13f4d762 103
7d4fc5ef
DM
104 foreach my $opt (qw(status crush_weight reweight)) {
105 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
106 }
13f4d762 107
7d4fc5ef
DM
108 if (my $stat = $osdhash->{$e->{id}}) {
109 $new->{in} = $stat->{in} if defined($stat->{in});
110 }
13f4d762 111
941c0195
DM
112 if (my $stat = $usagehash->{$e->{id}}) {
113 $new->{total_space} = ($stat->{kb} || 1) * 1024;
114 $new->{bytes_used} = ($stat->{kb_used} || 0) * 1024;
115 $new->{percent_used} = ($new->{bytes_used}*100)/$new->{total_space};
cc81005a
DM
116 if (my $d = $stat->{fs_perf_stat}) {
117 $new->{commit_latency_ms} = $d->{commit_latency_ms};
118 $new->{apply_latency_ms} = $d->{apply_latency_ms};
119 }
941c0195
DM
120 }
121
7d4fc5ef 122 $newnodes->{$e->{id}} = $new;
13f4d762
DM
123 }
124
7d4fc5ef
DM
125 foreach my $e (@{$res->{nodes}}) {
126 my $new = $newnodes->{$e->{id}};
127 if ($e->{children} && scalar(@{$e->{children}})) {
128 $new->{children} = [];
129 $new->{leaf} = 0;
130 foreach my $cid (@{$e->{children}}) {
131 $nodes->{$cid}->{parent} = $e->{id};
132 if ($nodes->{$cid}->{type} eq 'osd' &&
133 $e->{type} eq 'host') {
134 $newnodes->{$cid}->{host} = $e->{name};
135 }
136 push @{$new->{children}}, $newnodes->{$cid};
137 }
138 } else {
139 $new->{leaf} = ($e->{id} >= 0) ? 1 : 0;
140 }
0e5816e4
DM
141 }
142
4aed5e3e 143 my $roots = [];
7d4fc5ef
DM
144 foreach my $e (@{$res->{nodes}}) {
145 if (!$nodes->{$e->{id}}->{parent}) {
4aed5e3e 146 push @$roots, $newnodes->{$e->{id}};
7d4fc5ef 147 }
0e5816e4
DM
148 }
149
4aed5e3e 150 die "no root node\n" if !@$roots;
13f4d762 151
4aed5e3e 152 my $data = { root => { leaf => 0, children => $roots } };
0e5816e4 153
7d4fc5ef
DM
154 return $data;
155 }});
13f4d762 156
7d4fc5ef
DM
157__PACKAGE__->register_method ({
158 name => 'createosd',
159 path => '',
160 method => 'POST',
161 description => "Create OSD",
162 proxyto => 'node',
163 protected => 1,
164 parameters => {
165 additionalProperties => 0,
166 properties => {
167 node => get_standard_option('pve-node'),
168 dev => {
169 description => "Block device name.",
170 type => 'string',
171 },
172 journal_dev => {
173 description => "Block device name for journal.",
174 optional => 1,
175 type => 'string',
176 },
177 fstype => {
178 description => "File system type.",
179 type => 'string',
180 enum => ['xfs', 'ext4', 'btrfs'],
181 default => 'xfs',
182 optional => 1,
183 },
184 },
185 },
186 returns => { type => 'string' },
187 code => sub {
188 my ($param) = @_;
13f4d762 189
7d4fc5ef 190 my $rpcenv = PVE::RPCEnvironment::get();
13f4d762 191
7d4fc5ef 192 my $authuser = $rpcenv->get_user();
13f4d762 193
7d4fc5ef 194 PVE::CephTools::check_ceph_inited();
0e5816e4 195
7d4fc5ef 196 PVE::CephTools::setup_pve_symlinks();
a7a7fb00 197
7d4fc5ef 198 my $journal_dev;
a7a7fb00 199
7d4fc5ef 200 if ($param->{journal_dev} && ($param->{journal_dev} ne $param->{dev})) {
7f4924bd 201 $journal_dev = PVE::Diskmanage::verify_blockdev_path($param->{journal_dev});
7d4fc5ef 202 }
13f4d762 203
7f4924bd 204 $param->{dev} = PVE::Diskmanage::verify_blockdev_path($param->{dev});
a7a7fb00 205
7f4924bd 206 my $disklist = PVE::Diskmanage::get_disks();
13f4d762 207
7d4fc5ef
DM
208 my $devname = $param->{dev};
209 $devname =~ s|/dev/||;
210
211 my $diskinfo = $disklist->{$devname};
212 die "unable to get device info for '$devname'\n"
213 if !$diskinfo;
13f4d762 214
7d4fc5ef
DM
215 die "device '$param->{dev}' is in use\n"
216 if $diskinfo->{used};
0e5816e4 217
7d4fc5ef
DM
218 my $rados = PVE::RADOS->new();
219 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
220 die "unable to get fsid\n" if !$monstat->{monmap} || !$monstat->{monmap}->{fsid};
0e5816e4 221
7d4fc5ef
DM
222 my $fsid = $monstat->{monmap}->{fsid};
223 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
0e5816e4 224
7d4fc5ef 225 my $ceph_bootstrap_osd_keyring = PVE::CephTools::get_config('ceph_bootstrap_osd_keyring');
0e5816e4 226
7d4fc5ef 227 if (! -f $ceph_bootstrap_osd_keyring) {
1f3e956a 228 my $bindata = $rados->mon_command({ prefix => 'auth get', entity => 'client.bootstrap-osd', format => 'plain' });
7d4fc5ef
DM
229 PVE::Tools::file_set_contents($ceph_bootstrap_osd_keyring, $bindata);
230 };
231
232 my $worker = sub {
233 my $upid = shift;
0e5816e4 234
7d4fc5ef 235 my $fstype = $param->{fstype} || 'xfs';
0e5816e4 236
7d4fc5ef 237 print "create OSD on $param->{dev} ($fstype)\n";
0e5816e4 238
7d4fc5ef 239 my $ccname = PVE::CephTools::get_config('ccname');
0e5816e4 240
7d4fc5ef
DM
241 my $cmd = ['ceph-disk', 'prepare', '--zap-disk', '--fs-type', $fstype,
242 '--cluster', $ccname, '--cluster-uuid', $fsid ];
38db610a 243
7d4fc5ef
DM
244 if ($journal_dev) {
245 print "using device '$journal_dev' for journal\n";
246 push @$cmd, '--journal-dev', $param->{dev}, $journal_dev;
247 } else {
248 push @$cmd, $param->{dev};
249 }
250
251 run_command($cmd);
252 };
38db610a 253
7d4fc5ef 254 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
38db610a
DM
255 }});
256
13f4d762 257__PACKAGE__->register_method ({
7d4fc5ef
DM
258 name => 'destroyosd',
259 path => '{osdid}',
260 method => 'DELETE',
261 description => "Destroy OSD",
13f4d762
DM
262 proxyto => 'node',
263 protected => 1,
264 parameters => {
265 additionalProperties => 0,
266 properties => {
267 node => get_standard_option('pve-node'),
7d4fc5ef
DM
268 osdid => {
269 description => 'OSD ID',
270 type => 'integer',
0e5816e4 271 },
7d4fc5ef
DM
272 cleanup => {
273 description => "If set, we remove partition table entries.",
274 type => 'boolean',
275 optional => 1,
276 default => 0,
13f4d762
DM
277 },
278 },
13f4d762 279 },
7d4fc5ef 280 returns => { type => 'string' },
13f4d762
DM
281 code => sub {
282 my ($param) = @_;
283
7d4fc5ef 284 my $rpcenv = PVE::RPCEnvironment::get();
13f4d762 285
7d4fc5ef 286 my $authuser = $rpcenv->get_user();
13f4d762 287
7d4fc5ef 288 PVE::CephTools::check_ceph_inited();
0e5816e4 289
7d4fc5ef 290 my $osdid = $param->{osdid};
0e5816e4 291
7d4fc5ef
DM
292 my $rados = PVE::RADOS->new();
293 my $osdstat = &$get_osd_status($rados, $osdid);
13f4d762 294
7d4fc5ef
DM
295 die "osd is in use (in == 1)\n" if $osdstat->{in};
296 #&$run_ceph_cmd(['osd', 'out', $osdid]);
68e0c4bd 297
7d4fc5ef 298 die "osd is still runnung (up == 1)\n" if $osdstat->{up};
68e0c4bd 299
7d4fc5ef 300 my $osdsection = "osd.$osdid";
68e0c4bd 301
7d4fc5ef
DM
302 my $worker = sub {
303 my $upid = shift;
68e0c4bd 304
7d4fc5ef
DM
305 # reopen with longer timeout
306 $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
68e0c4bd 307
7d4fc5ef 308 print "destroy OSD $osdsection\n";
68e0c4bd 309
7d4fc5ef
DM
310 eval { PVE::CephTools::ceph_service_cmd('stop', $osdsection); };
311 warn $@ if $@;
68e0c4bd 312
7d4fc5ef
DM
313 print "Remove $osdsection from the CRUSH map\n";
314 $rados->mon_command({ prefix => "osd crush remove", name => $osdsection, format => 'plain' });
68e0c4bd 315
7d4fc5ef
DM
316 print "Remove the $osdsection authentication key.\n";
317 $rados->mon_command({ prefix => "auth del", entity => $osdsection, format => 'plain' });
318
319 print "Remove OSD $osdsection\n";
320 $rados->mon_command({ prefix => "osd rm", ids => [ $osdsection ], format => 'plain' });
321
322 # try to unmount from standard mount point
323 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
324
325 my $remove_partition = sub {
326 my ($disklist, $part) = @_;
327
328 return if !$part || (! -b $part );
329
330 foreach my $real_dev (keys %$disklist) {
331 my $diskinfo = $disklist->{$real_dev};
332 next if !$diskinfo->{gpt};
333 if ($part =~ m|^/dev/${real_dev}(\d+)$|) {
334 my $partnum = $1;
335 print "remove partition $part (disk '/dev/${real_dev}', partnum $partnum)\n";
336 eval { run_command(['/sbin/sgdisk', '-d', $partnum, "/dev/${real_dev}"]); };
337 warn $@ if $@;
338 last;
68e0c4bd
DM
339 }
340 }
7d4fc5ef 341 };
68e0c4bd 342
7d4fc5ef
DM
343 my $journal_part;
344 my $data_part;
345
346 if ($param->{cleanup}) {
347 my $jpath = "$mountpoint/journal";
348 $journal_part = abs_path($jpath);
349
350 if (my $fd = IO::File->new("/proc/mounts", "r")) {
351 while (defined(my $line = <$fd>)) {
352 my ($dev, $path, $fstype) = split(/\s+/, $line);
353 next if !($dev && $path && $fstype);
354 next if $dev !~ m|^/dev/|;
355 if ($path eq $mountpoint) {
356 $data_part = abs_path($dev);
357 last;
358 }
359 }
360 close($fd);
68e0c4bd
DM
361 }
362 }
7d4fc5ef
DM
363
364 print "Unmount OSD $osdsection from $mountpoint\n";
365 eval { run_command(['umount', $mountpoint]); };
366 if (my $err = $@) {
367 warn $err;
368 } elsif ($param->{cleanup}) {
7f4924bd 369 my $disklist = PVE::Diskmanage::get_disks();
7d4fc5ef
DM
370 &$remove_partition($disklist, $journal_part);
371 &$remove_partition($disklist, $data_part);
372 }
68e0c4bd 373 };
68e0c4bd 374
7d4fc5ef 375 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
68e0c4bd
DM
376 }});
377
38db610a 378__PACKAGE__->register_method ({
7d4fc5ef
DM
379 name => 'in',
380 path => '{osdid}/in',
38db610a 381 method => 'POST',
7d4fc5ef 382 description => "ceph osd in",
38db610a
DM
383 proxyto => 'node',
384 protected => 1,
90c75580
TL
385 permissions => {
386 check => ['perm', '/', [ 'Sys.Modify' ]],
387 },
38db610a
DM
388 parameters => {
389 additionalProperties => 0,
390 properties => {
391 node => get_standard_option('pve-node'),
7d4fc5ef
DM
392 osdid => {
393 description => 'OSD ID',
38db610a 394 type => 'integer',
38db610a
DM
395 },
396 },
397 },
7d4fc5ef 398 returns => { type => "null" },
38db610a
DM
399 code => sub {
400 my ($param) = @_;
401
7d4fc5ef 402 PVE::CephTools::check_ceph_inited();
38db610a 403
7d4fc5ef 404 my $osdid = $param->{osdid};
0e5816e4 405
7d4fc5ef 406 my $rados = PVE::RADOS->new();
f7e342ea 407
7d4fc5ef 408 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
f7e342ea 409
7d4fc5ef 410 my $osdsection = "osd.$osdid";
38db610a 411
7d4fc5ef 412 $rados->mon_command({ prefix => "osd in", ids => [ $osdsection ], format => 'plain' });
38db610a
DM
413
414 return undef;
415 }});
416
417__PACKAGE__->register_method ({
7d4fc5ef
DM
418 name => 'out',
419 path => '{osdid}/out',
38db610a 420 method => 'POST',
7d4fc5ef 421 description => "ceph osd out",
38db610a
DM
422 proxyto => 'node',
423 protected => 1,
90c75580
TL
424 permissions => {
425 check => ['perm', '/', [ 'Sys.Modify' ]],
426 },
38db610a
DM
427 parameters => {
428 additionalProperties => 0,
429 properties => {
430 node => get_standard_option('pve-node'),
7d4fc5ef
DM
431 osdid => {
432 description => 'OSD ID',
433 type => 'integer',
434 },
38db610a
DM
435 },
436 },
7d4fc5ef 437 returns => { type => "null" },
38db610a
DM
438 code => sub {
439 my ($param) = @_;
440
a34866f0 441 PVE::CephTools::check_ceph_inited();
38db610a 442
7d4fc5ef 443 my $osdid = $param->{osdid};
38db610a 444
7d4fc5ef 445 my $rados = PVE::RADOS->new();
38db610a 446
7d4fc5ef 447 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
38db610a 448
7d4fc5ef 449 my $osdsection = "osd.$osdid";
38db610a 450
7d4fc5ef 451 $rados->mon_command({ prefix => "osd out", ids => [ $osdsection ], format => 'plain' });
38db610a 452
7d4fc5ef
DM
453 return undef;
454 }});
38db610a 455
7d4fc5ef
DM
456package PVE::API2::Ceph;
457
458use strict;
459use warnings;
460use File::Basename;
461use File::Path;
462use POSIX qw (LONG_MAX);
463use Cwd qw(abs_path);
464use IO::Dir;
465use UUID;
466use Net::IP;
467
468use PVE::SafeSyslog;
469use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
470use PVE::Exception qw(raise raise_param_exc);
471use PVE::INotify;
472use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
473use PVE::AccessControl;
474use PVE::Storage;
475use PVE::RESTHandler;
476use PVE::RPCEnvironment;
477use PVE::JSONSchema qw(get_standard_option);
478use JSON;
479use PVE::RADOS;
480use PVE::CephTools;
481
482use base qw(PVE::RESTHandler);
483
484use Data::Dumper; # fixme: remove
485
486my $pve_osd_default_journal_size = 1024*5;
487
488__PACKAGE__->register_method ({
489 subclass => "PVE::API2::CephOSD",
490 path => 'osd',
491});
492
493__PACKAGE__->register_method ({
494 name => 'index',
495 path => '',
496 method => 'GET',
497 description => "Directory index.",
498 permissions => { user => 'all' },
90c75580
TL
499 permissions => {
500 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
501 },
7d4fc5ef
DM
502 parameters => {
503 additionalProperties => 0,
504 properties => {
505 node => get_standard_option('pve-node'),
506 },
507 },
508 returns => {
509 type => 'array',
510 items => {
511 type => "object",
512 properties => {},
513 },
514 links => [ { rel => 'child', href => "{name}" } ],
515 },
516 code => sub {
517 my ($param) = @_;
518
519 my $result = [
520 { name => 'init' },
521 { name => 'mon' },
522 { name => 'osd' },
523 { name => 'pools' },
524 { name => 'stop' },
525 { name => 'start' },
526 { name => 'status' },
527 { name => 'crush' },
528 { name => 'config' },
529 { name => 'log' },
530 { name => 'disks' },
531 ];
532
533 return $result;
534 }});
535
536__PACKAGE__->register_method ({
537 name => 'disks',
538 path => 'disks',
539 method => 'GET',
540 description => "List local disks.",
541 proxyto => 'node',
542 protected => 1,
90c75580
TL
543 permissions => {
544 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
545 },
7d4fc5ef
DM
546 parameters => {
547 additionalProperties => 0,
548 properties => {
549 node => get_standard_option('pve-node'),
550 type => {
551 description => "Only list specific types of disks.",
552 type => 'string',
553 enum => ['unused', 'journal_disks'],
554 optional => 1,
555 },
556 },
557 },
558 returns => {
559 type => 'array',
560 items => {
561 type => "object",
562 properties => {
563 dev => { type => 'string' },
564 used => { type => 'string', optional => 1 },
565 gpt => { type => 'boolean' },
566 size => { type => 'integer' },
567 osdid => { type => 'integer' },
568 vendor => { type => 'string', optional => 1 },
569 model => { type => 'string', optional => 1 },
570 serial => { type => 'string', optional => 1 },
571 },
572 },
573 # links => [ { rel => 'child', href => "{}" } ],
574 },
575 code => sub {
576 my ($param) = @_;
577
578 PVE::CephTools::check_ceph_inited();
579
5fd5c30d 580 my $disks = PVE::Diskmanage::get_disks(undef, 1);
7d4fc5ef
DM
581
582 my $res = [];
583 foreach my $dev (keys %$disks) {
584 my $d = $disks->{$dev};
585 if ($param->{type}) {
586 if ($param->{type} eq 'journal_disks') {
587 next if $d->{osdid} >= 0;
588 next if !$d->{gpt};
589 } elsif ($param->{type} eq 'unused') {
590 next if $d->{used};
591 } else {
592 die "internal error"; # should not happen
593 }
594 }
595
596 $d->{dev} = "/dev/$dev";
597 push @$res, $d;
598 }
599
600 return $res;
601 }});
602
603__PACKAGE__->register_method ({
604 name => 'config',
605 path => 'config',
606 method => 'GET',
90c75580
TL
607 permissions => {
608 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
609 },
7d4fc5ef
DM
610 description => "Get Ceph configuration.",
611 parameters => {
612 additionalProperties => 0,
613 properties => {
614 node => get_standard_option('pve-node'),
615 },
616 },
617 returns => { type => 'string' },
618 code => sub {
619 my ($param) = @_;
620
621 PVE::CephTools::check_ceph_inited();
622
623 my $path = PVE::CephTools::get_config('pve_ceph_cfgpath');
624 return PVE::Tools::file_get_contents($path);
625
626 }});
627
628__PACKAGE__->register_method ({
629 name => 'listmon',
630 path => 'mon',
631 method => 'GET',
632 description => "Get Ceph monitor list.",
633 proxyto => 'node',
634 protected => 1,
90c75580
TL
635 permissions => {
636 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
637 },
7d4fc5ef
DM
638 parameters => {
639 additionalProperties => 0,
640 properties => {
641 node => get_standard_option('pve-node'),
642 },
643 },
644 returns => {
645 type => 'array',
646 items => {
647 type => "object",
648 properties => {
649 name => { type => 'string' },
650 addr => { type => 'string' },
651 },
652 },
653 links => [ { rel => 'child', href => "{name}" } ],
654 },
655 code => sub {
656 my ($param) = @_;
657
658 PVE::CephTools::check_ceph_inited();
659
660 my $res = [];
661
662 my $cfg = PVE::CephTools::parse_ceph_config();
663
664 my $monhash = {};
665 foreach my $section (keys %$cfg) {
666 my $d = $cfg->{$section};
667 if ($section =~ m/^mon\.(\S+)$/) {
668 my $monid = $1;
669 if ($d->{'mon addr'} && $d->{'host'}) {
670 $monhash->{$monid} = {
671 addr => $d->{'mon addr'},
672 host => $d->{'host'},
673 name => $monid,
674 }
675 }
676 }
677 }
678
679 eval {
680 my $rados = PVE::RADOS->new();
681 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
682 my $mons = $monstat->{monmap}->{mons};
683 foreach my $d (@$mons) {
684 next if !defined($d->{name});
685 $monhash->{$d->{name}}->{rank} = $d->{rank};
686 $monhash->{$d->{name}}->{addr} = $d->{addr};
687 if (grep { $_ eq $d->{rank} } @{$monstat->{quorum}}) {
688 $monhash->{$d->{name}}->{quorum} = 1;
689 }
690 }
691 };
692 warn $@ if $@;
693
694 return PVE::RESTHandler::hash_to_array($monhash, 'name');
695 }});
696
697__PACKAGE__->register_method ({
698 name => 'init',
699 path => 'init',
700 method => 'POST',
701 description => "Create initial ceph default configuration and setup symlinks.",
702 proxyto => 'node',
703 protected => 1,
90c75580
TL
704 permissions => {
705 check => ['perm', '/', [ 'Sys.Modify' ]],
706 },
7d4fc5ef
DM
707 parameters => {
708 additionalProperties => 0,
709 properties => {
710 node => get_standard_option('pve-node'),
711 network => {
712 description => "Use specific network for all ceph related traffic",
713 type => 'string', format => 'CIDR',
714 optional => 1,
715 maxLength => 128,
716 },
717 size => {
718 description => 'Number of replicas per object',
719 type => 'integer',
720 default => 2,
721 optional => 1,
722 minimum => 1,
723 maximum => 3,
724 },
725 pg_bits => {
3cba09d5
FG
726 description => "Placement group bits, used to specify the " .
727 "default number of placement groups.\n\nNOTE: 'osd pool " .
728 "default pg num' does not work for default pools.",
7d4fc5ef
DM
729 type => 'integer',
730 default => 6,
731 optional => 1,
732 minimum => 6,
733 maximum => 14,
734 },
735 },
736 },
737 returns => { type => 'null' },
738 code => sub {
739 my ($param) = @_;
740
741 PVE::CephTools::check_ceph_installed();
742
743 # simply load old config if it already exists
744 my $cfg = PVE::CephTools::parse_ceph_config();
745
746 if (!$cfg->{global}) {
747
748 my $fsid;
749 my $uuid;
750
751 UUID::generate($uuid);
752 UUID::unparse($uuid, $fsid);
753
754 $cfg->{global} = {
755 'fsid' => $fsid,
7d4fc5ef
DM
756 'auth cluster required' => 'cephx',
757 'auth service required' => 'cephx',
758 'auth client required' => 'cephx',
759 'filestore xattr use omap' => 'true',
760 'osd journal size' => $pve_osd_default_journal_size,
761 'osd pool default min size' => 1,
762 };
763
764 # this does not work for default pools
765 #'osd pool default pg num' => $pg_num,
766 #'osd pool default pgp num' => $pg_num,
767 }
768
769 $cfg->{global}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
770 $cfg->{osd}->{keyring} = '/var/lib/ceph/osd/ceph-$id/keyring';
771
772 $cfg->{global}->{'osd pool default size'} = $param->{size} if $param->{size};
773
774 if ($param->{pg_bits}) {
775 $cfg->{global}->{'osd pg bits'} = $param->{pg_bits};
776 $cfg->{global}->{'osd pgp bits'} = $param->{pg_bits};
777 }
778
779 if ($param->{network}) {
780 $cfg->{global}->{'public network'} = $param->{network};
781 $cfg->{global}->{'cluster network'} = $param->{network};
782 }
783
784 PVE::CephTools::write_ceph_config($cfg);
785
786 PVE::CephTools::setup_pve_symlinks();
787
788 return undef;
789 }});
790
791my $find_node_ip = sub {
792 my ($cidr) = @_;
793
7d4fc5ef 794 my $net = Net::IP->new($cidr) || die Net::IP::Error() . "\n";
905c2f51
WB
795 my $id = $net->version == 6 ? 'address6' : 'address';
796
797 my $config = PVE::INotify::read_file('interfaces');
798 my $ifaces = $config->{ifaces};
7d4fc5ef 799
905c2f51 800 foreach my $iface (keys %$ifaces) {
957a59b3 801 my $d = $ifaces->{$iface};
905c2f51
WB
802 next if !$d->{$id};
803 my $a = Net::IP->new($d->{$id});
7d4fc5ef 804 next if !$a;
905c2f51 805 return $d->{$id} if $net->overlaps($a);
7d4fc5ef
DM
806 }
807
808 die "unable to find local address within network '$cidr'\n";
809};
810
811__PACKAGE__->register_method ({
812 name => 'createmon',
813 path => 'mon',
814 method => 'POST',
815 description => "Create Ceph Monitor",
816 proxyto => 'node',
817 protected => 1,
90c75580
TL
818 permissions => {
819 check => ['perm', '/', [ 'Sys.Modify' ]],
820 },
7d4fc5ef
DM
821 parameters => {
822 additionalProperties => 0,
823 properties => {
824 node => get_standard_option('pve-node'),
825 },
826 },
827 returns => { type => 'string' },
828 code => sub {
829 my ($param) = @_;
830
831 PVE::CephTools::check_ceph_inited();
832
833 PVE::CephTools::setup_pve_symlinks();
834
835 my $rpcenv = PVE::RPCEnvironment::get();
836
837 my $authuser = $rpcenv->get_user();
838
839 my $cfg = PVE::CephTools::parse_ceph_config();
840
841 my $moncount = 0;
842
843 my $monaddrhash = {};
844
845 foreach my $section (keys %$cfg) {
846 next if $section eq 'global';
847 my $d = $cfg->{$section};
848 if ($section =~ m/^mon\./) {
849 $moncount++;
850 if ($d->{'mon addr'}) {
851 $monaddrhash->{$d->{'mon addr'}} = $section;
852 }
853 }
854 }
38db610a
DM
855
856 my $monid;
857 for (my $i = 0; $i < 7; $i++) {
858 if (!$cfg->{"mon.$i"}) {
859 $monid = $i;
860 last;
861 }
862 }
863 die "unable to find usable monitor id\n" if !defined($monid);
864
f7e342ea
DM
865 my $monsection = "mon.$monid";
866 my $ip;
867 if (my $pubnet = $cfg->{global}->{'public network'}) {
868 $ip = &$find_node_ip($pubnet);
869 } else {
870 $ip = PVE::Cluster::remote_node_ip($param->{node});
871 }
872
98901f1d 873 my $monaddr = Net::IP::ip_is_ipv6($ip) ? "[$ip]:6789" : "$ip:6789";
38db610a
DM
874 my $monname = $param->{node};
875
876 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
877 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
878 if $monaddrhash->{$monaddr};
879
52d7be41
DM
880 my $worker = sub {
881 my $upid = shift;
38db610a 882
a34866f0
DM
883 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
884
52d7be41
DM
885 if (! -f $pve_ckeyring_path) {
886 run_command("ceph-authtool $pve_ckeyring_path --create-keyring " .
887 "--gen-key -n client.admin");
888 }
38db610a 889
a34866f0 890 my $pve_mon_key_path = PVE::CephTools::get_config('pve_mon_key_path');
52d7be41
DM
891 if (! -f $pve_mon_key_path) {
892 run_command("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
893 run_command("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
894 "--cap mds 'allow' " .
895 "--cap osd 'allow *' " .
896 "--cap mon 'allow *'");
897 run_command("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
898 run_command("mv $pve_mon_key_path.tmp $pve_mon_key_path");
38db610a
DM
899 }
900
a34866f0
DM
901 my $ccname = PVE::CephTools::get_config('ccname');
902
52d7be41
DM
903 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
904 -d $mondir && die "monitor filesystem '$mondir' already exist\n";
905
906 my $monmap = "/tmp/monmap";
907
908 eval {
909 mkdir $mondir;
910
911 if ($moncount > 0) {
7d4fc5ef 912 my $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
970236b3
DM
913 my $mapdata = $rados->mon_command({ prefix => 'mon getmap', format => 'plain' });
914 PVE::Tools::file_set_contents($monmap, $mapdata);
52d7be41
DM
915 } else {
916 run_command("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
917 }
38db610a 918
52d7be41
DM
919 run_command("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
920 };
921 my $err = $@;
922 unlink $monmap;
923 if ($err) {
924 File::Path::remove_tree($mondir);
925 die $err;
926 }
38db610a 927
52d7be41
DM
928 $cfg->{$monsection} = {
929 'host' => $monname,
930 'mon addr' => $monaddr,
931 };
38db610a 932
a34866f0 933 PVE::CephTools::write_ceph_config($cfg);
38db610a 934
a34866f0 935 PVE::CephTools::ceph_service_cmd('start', $monsection);
52d7be41 936 };
38db610a 937
52d7be41 938 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
38db610a
DM
939 }});
940
941__PACKAGE__->register_method ({
942 name => 'destroymon',
39e1ad70
DM
943 path => 'mon/{monid}',
944 method => 'DELETE',
38db610a
DM
945 description => "Destroy Ceph monitor.",
946 proxyto => 'node',
947 protected => 1,
90c75580
TL
948 permissions => {
949 check => ['perm', '/', [ 'Sys.Modify' ]],
950 },
38db610a
DM
951 parameters => {
952 additionalProperties => 0,
953 properties => {
954 node => get_standard_option('pve-node'),
955 monid => {
956 description => 'Monitor ID',
957 type => 'integer',
958 },
959 },
960 },
52d7be41 961 returns => { type => 'string' },
38db610a
DM
962 code => sub {
963 my ($param) = @_;
964
52d7be41
DM
965 my $rpcenv = PVE::RPCEnvironment::get();
966
967 my $authuser = $rpcenv->get_user();
968
a34866f0 969 PVE::CephTools::check_ceph_inited();
38db610a 970
a34866f0 971 my $cfg = PVE::CephTools::parse_ceph_config();
38db610a
DM
972
973 my $monid = $param->{monid};
974 my $monsection = "mon.$monid";
975
36fd0190 976 my $rados = PVE::RADOS->new();
970236b3 977 my $monstat = $rados->mon_command({ prefix => 'mon_status' });
38db610a
DM
978 my $monlist = $monstat->{monmap}->{mons};
979
980 die "no such monitor id '$monid'\n"
981 if !defined($cfg->{$monsection});
982
a34866f0
DM
983 my $ccname = PVE::CephTools::get_config('ccname');
984
38db610a
DM
985 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
986 -d $mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
987
988 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
989
52d7be41
DM
990 my $worker = sub {
991 my $upid = shift;
38db610a 992
2f804640 993 # reopen with longer timeout
7d4fc5ef 994 $rados = PVE::RADOS->new(timeout => PVE::CephTools::get_config('long_rados_timeout'));
f26b46db 995
6e3c2f47 996 $rados->mon_command({ prefix => "mon remove", name => $monid, format => 'plain' });
38db610a 997
a34866f0 998 eval { PVE::CephTools::ceph_service_cmd('stop', $monsection); };
52d7be41 999 warn $@ if $@;
38db610a 1000
52d7be41 1001 delete $cfg->{$monsection};
a34866f0 1002 PVE::CephTools::write_ceph_config($cfg);
52d7be41
DM
1003 File::Path::remove_tree($mondir);
1004 };
1005
1006 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
38db610a
DM
1007 }});
1008
1009__PACKAGE__->register_method ({
1010 name => 'stop',
1011 path => 'stop',
1012 method => 'POST',
1013 description => "Stop ceph services.",
1014 proxyto => 'node',
1015 protected => 1,
90c75580
TL
1016 permissions => {
1017 check => ['perm', '/', [ 'Sys.Modify' ]],
1018 },
38db610a
DM
1019 parameters => {
1020 additionalProperties => 0,
1021 properties => {
1022 node => get_standard_option('pve-node'),
68e0c4bd
DM
1023 service => {
1024 description => 'Ceph service name.',
1025 type => 'string',
1026 optional => 1,
1027 pattern => '(mon|mds|osd)\.[A-Za-z0-9]{1,32}',
1028 },
38db610a
DM
1029 },
1030 },
68e0c4bd 1031 returns => { type => 'string' },
38db610a
DM
1032 code => sub {
1033 my ($param) = @_;
1034
68e0c4bd
DM
1035 my $rpcenv = PVE::RPCEnvironment::get();
1036
1037 my $authuser = $rpcenv->get_user();
1038
a34866f0 1039 PVE::CephTools::check_ceph_inited();
38db610a 1040
a34866f0 1041 my $cfg = PVE::CephTools::parse_ceph_config();
38db610a
DM
1042 scalar(keys %$cfg) || die "no configuration\n";
1043
68e0c4bd
DM
1044 my $worker = sub {
1045 my $upid = shift;
38db610a 1046
68e0c4bd
DM
1047 my $cmd = ['stop'];
1048 if ($param->{service}) {
1049 push @$cmd, $param->{service};
1050 }
1051
a34866f0 1052 PVE::CephTools::ceph_service_cmd(@$cmd);
68e0c4bd
DM
1053 };
1054
1055 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
1056 $authuser, $worker);
38db610a
DM
1057 }});
1058
1059__PACKAGE__->register_method ({
1060 name => 'start',
1061 path => 'start',
1062 method => 'POST',
1063 description => "Start ceph services.",
1064 proxyto => 'node',
1065 protected => 1,
90c75580
TL
1066 permissions => {
1067 check => ['perm', '/', [ 'Sys.Modify' ]],
1068 },
38db610a
DM
1069 parameters => {
1070 additionalProperties => 0,
1071 properties => {
1072 node => get_standard_option('pve-node'),
68e0c4bd
DM
1073 service => {
1074 description => 'Ceph service name.',
1075 type => 'string',
1076 optional => 1,
1077 pattern => '(mon|mds|osd)\.[A-Za-z0-9]{1,32}',
1078 },
38db610a
DM
1079 },
1080 },
68e0c4bd 1081 returns => { type => 'string' },
38db610a
DM
1082 code => sub {
1083 my ($param) = @_;
1084
68e0c4bd
DM
1085 my $rpcenv = PVE::RPCEnvironment::get();
1086
1087 my $authuser = $rpcenv->get_user();
1088
a34866f0 1089 PVE::CephTools::check_ceph_inited();
38db610a 1090
a34866f0 1091 my $cfg = PVE::CephTools::parse_ceph_config();
38db610a
DM
1092 scalar(keys %$cfg) || die "no configuration\n";
1093
68e0c4bd
DM
1094 my $worker = sub {
1095 my $upid = shift;
38db610a 1096
68e0c4bd
DM
1097 my $cmd = ['start'];
1098 if ($param->{service}) {
1099 push @$cmd, $param->{service};
1100 }
1101
a34866f0 1102 PVE::CephTools::ceph_service_cmd(@$cmd);
68e0c4bd
DM
1103 };
1104
1105 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
1106 $authuser, $worker);
38db610a
DM
1107 }});
1108
1109__PACKAGE__->register_method ({
1110 name => 'status',
1111 path => 'status',
1112 method => 'GET',
1113 description => "Get ceph status.",
1114 proxyto => 'node',
1115 protected => 1,
90c75580
TL
1116 permissions => {
1117 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1118 },
38db610a
DM
1119 parameters => {
1120 additionalProperties => 0,
1121 properties => {
1122 node => get_standard_option('pve-node'),
1123 },
1124 },
1125 returns => { type => 'object' },
1126 code => sub {
1127 my ($param) = @_;
1128
a34866f0 1129 PVE::CephTools::check_ceph_enabled();
38db610a 1130
36fd0190 1131 my $rados = PVE::RADOS->new();
970236b3 1132 return $rados->mon_command({ prefix => 'status' });
38db610a
DM
1133 }});
1134
b0537f7b
DM
1135__PACKAGE__->register_method ({
1136 name => 'lspools',
1137 path => 'pools',
1138 method => 'GET',
1139 description => "List all pools.",
1140 proxyto => 'node',
1141 protected => 1,
90c75580
TL
1142 permissions => {
1143 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1144 },
b0537f7b
DM
1145 parameters => {
1146 additionalProperties => 0,
1147 properties => {
1148 node => get_standard_option('pve-node'),
1149 },
1150 },
1151 returns => {
1152 type => 'array',
1153 items => {
1154 type => "object",
1155 properties => {
1156 pool => { type => 'integer' },
1157 pool_name => { type => 'string' },
1158 size => { type => 'integer' },
1159 },
1160 },
1161 links => [ { rel => 'child', href => "{pool_name}" } ],
1162 },
1163 code => sub {
1164 my ($param) = @_;
1165
a34866f0 1166 PVE::CephTools::check_ceph_inited();
b0537f7b 1167
36fd0190 1168 my $rados = PVE::RADOS->new();
d54f1126
DM
1169
1170 my $stats = {};
1171 my $res = $rados->mon_command({ prefix => 'df' });
6f9ea1c1
WL
1172 my $total = $res->{stats}->{total_avail_bytes} || 0;
1173
d54f1126
DM
1174 foreach my $d (@{$res->{pools}}) {
1175 next if !$d->{stats};
1176 next if !defined($d->{id});
1177 $stats->{$d->{id}} = $d->{stats};
1178 }
1179
1180 $res = $rados->mon_command({ prefix => 'osd dump' });
b0537f7b
DM
1181
1182 my $data = [];
1183 foreach my $e (@{$res->{pools}}) {
1184 my $d = {};
1185 foreach my $attr (qw(pool pool_name size min_size pg_num crush_ruleset)) {
1186 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1187 }
d54f1126
DM
1188 if (my $s = $stats->{$d->{pool}}) {
1189 $d->{bytes_used} = $s->{bytes_used};
6f9ea1c1
WL
1190 $d->{percent_used} = ($s->{bytes_used} / $total)*100
1191 if $s->{max_avail} && $total;
d54f1126 1192 }
b0537f7b
DM
1193 push @$data, $d;
1194 }
1195
d54f1126 1196
b0537f7b
DM
1197 return $data;
1198 }});
1199
1200__PACKAGE__->register_method ({
1201 name => 'createpool',
7d4fc5ef 1202 path => 'pools',
38db610a 1203 method => 'POST',
7d4fc5ef 1204 description => "Create POOL",
38db610a
DM
1205 proxyto => 'node',
1206 protected => 1,
90c75580
TL
1207 permissions => {
1208 check => ['perm', '/', [ 'Sys.Modify' ]],
1209 },
38db610a
DM
1210 parameters => {
1211 additionalProperties => 0,
1212 properties => {
1213 node => get_standard_option('pve-node'),
7d4fc5ef
DM
1214 name => {
1215 description => "The name of the pool. It must be unique.",
38db610a 1216 type => 'string',
43d85563 1217 },
7d4fc5ef
DM
1218 size => {
1219 description => 'Number of replicas per object',
1220 type => 'integer',
1221 default => 2,
0e5816e4 1222 optional => 1,
7d4fc5ef
DM
1223 minimum => 1,
1224 maximum => 3,
0e5816e4 1225 },
7d4fc5ef
DM
1226 min_size => {
1227 description => 'Minimum number of replicas per object',
1228 type => 'integer',
1229 default => 1,
1230 optional => 1,
1231 minimum => 1,
1232 maximum => 3,
1233 },
1234 pg_num => {
1235 description => "Number of placement groups.",
1236 type => 'integer',
1237 default => 64,
1238 optional => 1,
1239 minimum => 8,
1240 maximum => 32768,
1241 },
1242 crush_ruleset => {
1243 description => "The ruleset to use for mapping object placement in the cluster.",
1244 type => 'integer',
1245 minimum => 0,
1246 maximum => 32768,
1247 default => 0,
43d85563
DM
1248 optional => 1,
1249 },
38db610a
DM
1250 },
1251 },
7d4fc5ef 1252 returns => { type => 'null' },
38db610a
DM
1253 code => sub {
1254 my ($param) = @_;
1255
a34866f0 1256 PVE::CephTools::check_ceph_inited();
38db610a 1257
7d4fc5ef 1258 my $pve_ckeyring_path = PVE::CephTools::get_config('pve_ckeyring_path');
13f4d762 1259
7d4fc5ef
DM
1260 die "not fully configured - missing '$pve_ckeyring_path'\n"
1261 if ! -f $pve_ckeyring_path;
13f4d762 1262
7d4fc5ef
DM
1263 my $pg_num = $param->{pg_num} || 64;
1264 my $size = $param->{size} || 2;
1265 my $min_size = $param->{min_size} || 1;
1266 my $ruleset = $param->{crush_ruleset} || 0;
36fd0190 1267 my $rados = PVE::RADOS->new();
38db610a 1268
7d4fc5ef
DM
1269 $rados->mon_command({
1270 prefix => "osd pool create",
1271 pool => $param->{name},
1272 pg_num => int($pg_num),
1273# this does not work for unknown reason
1274# properties => ["size=$size", "min_size=$min_size", "crush_ruleset=$ruleset"],
1275 format => 'plain',
1276 });
52d7be41 1277
7d4fc5ef
DM
1278 $rados->mon_command({
1279 prefix => "osd pool set",
1280 pool => $param->{name},
1281 var => 'min_size',
1282 val => $min_size,
1283 format => 'plain',
1284 });
a34866f0 1285
7d4fc5ef
DM
1286 $rados->mon_command({
1287 prefix => "osd pool set",
1288 pool => $param->{name},
1289 var => 'size',
1290 val => $size,
1291 format => 'plain',
1292 });
0e5816e4 1293
7d4fc5ef
DM
1294 if (defined($param->{crush_ruleset})) {
1295 $rados->mon_command({
1296 prefix => "osd pool set",
1297 pool => $param->{name},
1298 var => 'crush_ruleset',
1299 val => $param->{crush_ruleset},
1300 format => 'plain',
1301 });
1302 }
52d7be41 1303
7d4fc5ef 1304 return undef;
38db610a
DM
1305 }});
1306
1307__PACKAGE__->register_method ({
7d4fc5ef
DM
1308 name => 'destroypool',
1309 path => 'pools/{name}',
39e1ad70 1310 method => 'DELETE',
7d4fc5ef 1311 description => "Destroy pool",
38db610a
DM
1312 proxyto => 'node',
1313 protected => 1,
90c75580
TL
1314 permissions => {
1315 check => ['perm', '/', [ 'Sys.Modify' ]],
1316 },
38db610a
DM
1317 parameters => {
1318 additionalProperties => 0,
1319 properties => {
1320 node => get_standard_option('pve-node'),
7d4fc5ef
DM
1321 name => {
1322 description => "The name of the pool. It must be unique.",
1323 type => 'string',
0e5816e4 1324 },
76dc2ad0
DC
1325 force => {
1326 description => "If true, destroys pool even if in use",
1327 type => 'boolean',
1328 optional => 1,
1329 default => 0,
1330 }
38db610a
DM
1331 },
1332 },
7d4fc5ef 1333 returns => { type => 'null' },
38db610a
DM
1334 code => sub {
1335 my ($param) = @_;
1336
a34866f0 1337 PVE::CephTools::check_ceph_inited();
38db610a 1338
76dc2ad0
DC
1339 # if not forced, destroy ceph pool only when no
1340 # vm disks are on it anymore
1341 if (!$param->{force}) {
1342 my $storagecfg = PVE::Storage::config();
1343 foreach my $storageid (keys %{$storagecfg->{ids}}) {
1344 my $storage = $storagecfg->{ids}->{$storageid};
1345 next if $storage->{type} ne 'rbd';
1346 next if $storage->{pool} ne $param->{name};
1347
1348 # check if any vm disks are on the pool
1349 my $res = PVE::Storage::vdisk_list($storagecfg, $storageid);
1350 die "ceph pool '$param->{name}' still in use by storage '$storageid'\n"
1351 if @{$res->{$storageid}} != 0;
1352 }
1353 }
1354
36fd0190 1355 my $rados = PVE::RADOS->new();
7d4fc5ef
DM
1356 # fixme: '--yes-i-really-really-mean-it'
1357 $rados->mon_command({
1358 prefix => "osd pool delete",
1359 pool => $param->{name},
1360 pool2 => $param->{name},
1361 sure => '--yes-i-really-really-mean-it',
1362 format => 'plain',
1363 });
52d7be41 1364
7d4fc5ef 1365 return undef;
38db610a 1366 }});
2f692121 1367
a34866f0 1368
2f692121
DM
1369__PACKAGE__->register_method ({
1370 name => 'crush',
1371 path => 'crush',
1372 method => 'GET',
1373 description => "Get OSD crush map",
1374 proxyto => 'node',
1375 protected => 1,
90c75580
TL
1376 permissions => {
1377 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1378 },
2f692121
DM
1379 parameters => {
1380 additionalProperties => 0,
1381 properties => {
1382 node => get_standard_option('pve-node'),
1383 },
1384 },
1385 returns => { type => 'string' },
1386 code => sub {
1387 my ($param) = @_;
1388
a34866f0 1389 PVE::CephTools::check_ceph_inited();
2f692121 1390
8b336060
DM
1391 # this produces JSON (difficult to read for the user)
1392 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
2f692121 1393
8b336060
DM
1394 my $txt = '';
1395
1396 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1397 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1398
36fd0190 1399 my $rados = PVE::RADOS->new();
970236b3 1400
8b336060 1401 eval {
970236b3
DM
1402 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
1403 PVE::Tools::file_set_contents($mapfile, $bindata);
8b336060
DM
1404 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1405 $txt = PVE::Tools::file_get_contents($mapdata);
1406 };
1407 my $err = $@;
1408
1409 unlink $mapfile;
1410 unlink $mapdata;
1411
1412 die $err if $err;
1413
2f692121
DM
1414 return $txt;
1415 }});
1416
570278fa
DM
1417__PACKAGE__->register_method({
1418 name => 'log',
1419 path => 'log',
1420 method => 'GET',
1421 description => "Read ceph log",
1422 proxyto => 'node',
1423 permissions => {
1424 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1425 },
1426 protected => 1,
1427 parameters => {
1428 additionalProperties => 0,
1429 properties => {
1430 node => get_standard_option('pve-node'),
1431 start => {
1432 type => 'integer',
1433 minimum => 0,
1434 optional => 1,
1435 },
1436 limit => {
1437 type => 'integer',
1438 minimum => 0,
1439 optional => 1,
1440 },
1441 },
1442 },
1443 returns => {
1444 type => 'array',
1445 items => {
1446 type => "object",
1447 properties => {
1448 n => {
1449 description=> "Line number",
1450 type=> 'integer',
1451 },
1452 t => {
1453 description=> "Line text",
1454 type => 'string',
1455 }
1456 }
1457 }
1458 },
1459 code => sub {
1460 my ($param) = @_;
1461
1462 my $rpcenv = PVE::RPCEnvironment::get();
1463 my $user = $rpcenv->get_user();
1464 my $node = $param->{node};
1465
1466 my $logfile = "/var/log/ceph/ceph.log";
1467 my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
1468
1469 $rpcenv->set_result_attrib('total', $count);
1470
1471 return $lines;
1472 }});
1473
2f692121 1474