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