]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
ceph: init: check for nautilus
[pve-manager.git] / PVE / API2 / Ceph.pm
1 package PVE::API2::Ceph;
2
3 use strict;
4 use warnings;
5
6 use File::Path;
7 use Net::IP;
8 use UUID;
9
10 use PVE::Ceph::Tools;
11 use PVE::Ceph::Services;
12 use PVE::Cluster qw(cfs_read_file cfs_write_file);
13 use PVE::JSONSchema qw(get_standard_option);
14 use PVE::Network;
15 use PVE::RADOS;
16 use PVE::RESTHandler;
17 use PVE::RPCEnvironment;
18 use PVE::Storage;
19 use PVE::Tools qw(run_command file_get_contents file_set_contents);
20
21 use PVE::API2::Ceph::OSD;
22 use PVE::API2::Ceph::FS;
23 use PVE::API2::Ceph::MDS;
24 use PVE::API2::Ceph::MGR;
25 use PVE::API2::Ceph::MON;
26 use PVE::API2::Storage::Config;
27
28 use base qw(PVE::RESTHandler);
29
30 my $pve_osd_default_journal_size = 1024*5;
31
32 __PACKAGE__->register_method ({
33 subclass => "PVE::API2::Ceph::OSD",
34 path => 'osd',
35 });
36
37 __PACKAGE__->register_method ({
38 subclass => "PVE::API2::Ceph::MDS",
39 path => 'mds',
40 });
41
42 __PACKAGE__->register_method ({
43 subclass => "PVE::API2::Ceph::MGR",
44 path => 'mgr',
45 });
46
47 __PACKAGE__->register_method ({
48 subclass => "PVE::API2::Ceph::MON",
49 path => 'mon',
50 });
51
52 __PACKAGE__->register_method ({
53 subclass => "PVE::API2::Ceph::FS",
54 path => 'fs',
55 });
56
57 __PACKAGE__->register_method ({
58 name => 'index',
59 path => '',
60 method => 'GET',
61 description => "Directory index.",
62 permissions => { user => 'all' },
63 permissions => {
64 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
65 },
66 parameters => {
67 additionalProperties => 0,
68 properties => {
69 node => get_standard_option('pve-node'),
70 },
71 },
72 returns => {
73 type => 'array',
74 items => {
75 type => "object",
76 properties => {},
77 },
78 links => [ { rel => 'child', href => "{name}" } ],
79 },
80 code => sub {
81 my ($param) = @_;
82
83 my $result = [
84 { name => 'init' },
85 { name => 'mon' },
86 { name => 'osd' },
87 { name => 'pools' },
88 { name => 'fs' },
89 { name => 'mds' },
90 { name => 'stop' },
91 { name => 'start' },
92 { name => 'restart' },
93 { name => 'status' },
94 { name => 'crush' },
95 { name => 'config' },
96 { name => 'log' },
97 { name => 'disks' },
98 { name => 'flags' },
99 { name => 'rules' },
100 ];
101
102 return $result;
103 }});
104
105 __PACKAGE__->register_method ({
106 name => 'disks',
107 path => 'disks',
108 method => 'GET',
109 description => "List local disks.",
110 proxyto => 'node',
111 protected => 1,
112 permissions => {
113 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
114 },
115 parameters => {
116 additionalProperties => 0,
117 properties => {
118 node => get_standard_option('pve-node'),
119 type => {
120 description => "Only list specific types of disks.",
121 type => 'string',
122 enum => ['unused', 'journal_disks'],
123 optional => 1,
124 },
125 },
126 },
127 returns => {
128 type => 'array',
129 items => {
130 type => "object",
131 properties => {
132 dev => { type => 'string' },
133 used => { type => 'string', optional => 1 },
134 gpt => { type => 'boolean' },
135 size => { type => 'integer' },
136 osdid => { type => 'integer' },
137 vendor => { type => 'string', optional => 1 },
138 model => { type => 'string', optional => 1 },
139 serial => { type => 'string', optional => 1 },
140 },
141 },
142 # links => [ { rel => 'child', href => "{}" } ],
143 },
144 code => sub {
145 my ($param) = @_;
146
147 PVE::Ceph::Tools::check_ceph_inited();
148
149 my $disks = PVE::Diskmanage::get_disks(undef, 1);
150
151 my $res = [];
152 foreach my $dev (keys %$disks) {
153 my $d = $disks->{$dev};
154 if ($param->{type}) {
155 if ($param->{type} eq 'journal_disks') {
156 next if $d->{osdid} >= 0;
157 next if !$d->{gpt};
158 } elsif ($param->{type} eq 'unused') {
159 next if $d->{used};
160 } else {
161 die "internal error"; # should not happen
162 }
163 }
164
165 $d->{dev} = "/dev/$dev";
166 push @$res, $d;
167 }
168
169 return $res;
170 }});
171
172 __PACKAGE__->register_method ({
173 name => 'config',
174 path => 'config',
175 method => 'GET',
176 proxyto => 'node',
177 permissions => {
178 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
179 },
180 description => "Get Ceph configuration.",
181 parameters => {
182 additionalProperties => 0,
183 properties => {
184 node => get_standard_option('pve-node'),
185 },
186 },
187 returns => { type => 'string' },
188 code => sub {
189 my ($param) = @_;
190
191 PVE::Ceph::Tools::check_ceph_inited();
192
193 my $path = PVE::Ceph::Tools::get_config('pve_ceph_cfgpath');
194 return file_get_contents($path);
195
196 }});
197
198 my $add_storage = sub {
199 my ($pool, $storeid) = @_;
200
201 my $storage_params = {
202 type => 'rbd',
203 pool => $pool,
204 storage => $storeid,
205 krbd => 0,
206 content => 'rootdir,images',
207 };
208
209 PVE::API2::Storage::Config->create($storage_params);
210 };
211
212 my $get_storages = sub {
213 my ($pool) = @_;
214
215 my $cfg = PVE::Storage::config();
216
217 my $storages = $cfg->{ids};
218 my $res = {};
219 foreach my $storeid (keys %$storages) {
220 my $curr = $storages->{$storeid};
221 $res->{$storeid} = $storages->{$storeid}
222 if $curr->{type} eq 'rbd' && $pool eq $curr->{pool};
223 }
224
225 return $res;
226 };
227
228 __PACKAGE__->register_method ({
229 name => 'init',
230 path => 'init',
231 method => 'POST',
232 description => "Create initial ceph default configuration and setup symlinks.",
233 proxyto => 'node',
234 protected => 1,
235 permissions => {
236 check => ['perm', '/', [ 'Sys.Modify' ]],
237 },
238 parameters => {
239 additionalProperties => 0,
240 properties => {
241 node => get_standard_option('pve-node'),
242 network => {
243 description => "Use specific network for all ceph related traffic",
244 type => 'string', format => 'CIDR',
245 optional => 1,
246 maxLength => 128,
247 },
248 'cluster-network' => {
249 description => "Declare a separate cluster network, OSDs will route" .
250 "heartbeat, object replication and recovery traffic over it",
251 type => 'string', format => 'CIDR',
252 requires => 'network',
253 optional => 1,
254 maxLength => 128,
255 },
256 size => {
257 description => 'Targeted number of replicas per object',
258 type => 'integer',
259 default => 3,
260 optional => 1,
261 minimum => 1,
262 maximum => 7,
263 },
264 min_size => {
265 description => 'Minimum number of available replicas per object to allow I/O',
266 type => 'integer',
267 default => 2,
268 optional => 1,
269 minimum => 1,
270 maximum => 7,
271 },
272 pg_bits => {
273 description => "Placement group bits, used to specify the " .
274 "default number of placement groups.\n\nNOTE: 'osd pool " .
275 "default pg num' does not work for default pools.",
276 type => 'integer',
277 default => 6,
278 optional => 1,
279 minimum => 6,
280 maximum => 14,
281 },
282 disable_cephx => {
283 description => "Disable cephx authentication.\n\n" .
284 "WARNING: cephx is a security feature protecting against " .
285 "man-in-the-middle attacks. Only consider disabling cephx ".
286 "if your network is private!",
287 type => 'boolean',
288 optional => 1,
289 default => 0,
290 },
291 },
292 },
293 returns => { type => 'null' },
294 code => sub {
295 my ($param) = @_;
296
297 my $version = PVE::Ceph::Tools::get_local_version(1);
298
299 if (!$version || $version < 14) {
300 die "Ceph Nautilus required - please run 'pveceph install'\n";
301 } else {
302 PVE::Ceph::Tools::check_ceph_installed('ceph_bin');
303 }
304
305 # simply load old config if it already exists
306 my $cfg = cfs_read_file('ceph.conf');
307
308 if (!$cfg->{global}) {
309
310 my $fsid;
311 my $uuid;
312
313 UUID::generate($uuid);
314 UUID::unparse($uuid, $fsid);
315
316 my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
317
318 $cfg->{global} = {
319 'fsid' => $fsid,
320 'auth cluster required' => $auth,
321 'auth service required' => $auth,
322 'auth client required' => $auth,
323 'osd pool default size' => $param->{size} // 3,
324 'osd pool default min size' => $param->{min_size} // 2,
325 'mon allow pool delete' => 'true',
326 };
327
328 # this does not work for default pools
329 #'osd pool default pg num' => $pg_num,
330 #'osd pool default pgp num' => $pg_num,
331 }
332
333 $cfg->{client}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
334
335 if ($param->{pg_bits}) {
336 $cfg->{global}->{'osd pg bits'} = $param->{pg_bits};
337 $cfg->{global}->{'osd pgp bits'} = $param->{pg_bits};
338 }
339
340 if ($param->{network}) {
341 $cfg->{global}->{'public network'} = $param->{network};
342 $cfg->{global}->{'cluster network'} = $param->{network};
343 }
344
345 if ($param->{'cluster-network'}) {
346 $cfg->{global}->{'cluster network'} = $param->{'cluster-network'};
347 }
348
349 cfs_write_file('ceph.conf', $cfg);
350
351 PVE::Ceph::Tools::setup_pve_symlinks();
352
353 return undef;
354 }});
355
356 __PACKAGE__->register_method ({
357 name => 'stop',
358 path => 'stop',
359 method => 'POST',
360 description => "Stop ceph services.",
361 proxyto => 'node',
362 protected => 1,
363 permissions => {
364 check => ['perm', '/', [ 'Sys.Modify' ]],
365 },
366 parameters => {
367 additionalProperties => 0,
368 properties => {
369 node => get_standard_option('pve-node'),
370 service => {
371 description => 'Ceph service name.',
372 type => 'string',
373 optional => 1,
374 default => 'ceph.target',
375 pattern => '(ceph|mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
376 },
377 },
378 },
379 returns => { type => 'string' },
380 code => sub {
381 my ($param) = @_;
382
383 my $rpcenv = PVE::RPCEnvironment::get();
384
385 my $authuser = $rpcenv->get_user();
386
387 PVE::Ceph::Tools::check_ceph_inited();
388
389 my $cfg = cfs_read_file('ceph.conf');
390 scalar(keys %$cfg) || die "no configuration\n";
391
392 my $worker = sub {
393 my $upid = shift;
394
395 my $cmd = ['stop'];
396 if ($param->{service}) {
397 push @$cmd, $param->{service};
398 }
399
400 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
401 };
402
403 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
404 $authuser, $worker);
405 }});
406
407 __PACKAGE__->register_method ({
408 name => 'start',
409 path => 'start',
410 method => 'POST',
411 description => "Start ceph services.",
412 proxyto => 'node',
413 protected => 1,
414 permissions => {
415 check => ['perm', '/', [ 'Sys.Modify' ]],
416 },
417 parameters => {
418 additionalProperties => 0,
419 properties => {
420 node => get_standard_option('pve-node'),
421 service => {
422 description => 'Ceph service name.',
423 type => 'string',
424 optional => 1,
425 default => 'ceph.target',
426 pattern => '(ceph|mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
427 },
428 },
429 },
430 returns => { type => 'string' },
431 code => sub {
432 my ($param) = @_;
433
434 my $rpcenv = PVE::RPCEnvironment::get();
435
436 my $authuser = $rpcenv->get_user();
437
438 PVE::Ceph::Tools::check_ceph_inited();
439
440 my $cfg = cfs_read_file('ceph.conf');
441 scalar(keys %$cfg) || die "no configuration\n";
442
443 my $worker = sub {
444 my $upid = shift;
445
446 my $cmd = ['start'];
447 if ($param->{service}) {
448 push @$cmd, $param->{service};
449 }
450
451 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
452 };
453
454 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
455 $authuser, $worker);
456 }});
457
458 __PACKAGE__->register_method ({
459 name => 'restart',
460 path => 'restart',
461 method => 'POST',
462 description => "Restart ceph services.",
463 proxyto => 'node',
464 protected => 1,
465 permissions => {
466 check => ['perm', '/', [ 'Sys.Modify' ]],
467 },
468 parameters => {
469 additionalProperties => 0,
470 properties => {
471 node => get_standard_option('pve-node'),
472 service => {
473 description => 'Ceph service name.',
474 type => 'string',
475 optional => 1,
476 default => 'ceph.target',
477 pattern => '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
478 },
479 },
480 },
481 returns => { type => 'string' },
482 code => sub {
483 my ($param) = @_;
484
485 my $rpcenv = PVE::RPCEnvironment::get();
486
487 my $authuser = $rpcenv->get_user();
488
489 PVE::Ceph::Tools::check_ceph_inited();
490
491 my $cfg = cfs_read_file('ceph.conf');
492 scalar(keys %$cfg) || die "no configuration\n";
493
494 my $worker = sub {
495 my $upid = shift;
496
497 my $cmd = ['restart'];
498 if ($param->{service}) {
499 push @$cmd, $param->{service};
500 }
501
502 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
503 };
504
505 return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
506 $authuser, $worker);
507 }});
508
509 __PACKAGE__->register_method ({
510 name => 'status',
511 path => 'status',
512 method => 'GET',
513 description => "Get ceph status.",
514 proxyto => 'node',
515 protected => 1,
516 permissions => {
517 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
518 },
519 parameters => {
520 additionalProperties => 0,
521 properties => {
522 node => get_standard_option('pve-node'),
523 },
524 },
525 returns => { type => 'object' },
526 code => sub {
527 my ($param) = @_;
528
529 PVE::Ceph::Tools::check_ceph_inited();
530
531 my $rados = PVE::RADOS->new();
532 my $status = $rados->mon_command({ prefix => 'status' });
533 $status->{health} = $rados->mon_command({ prefix => 'health', detail => 'detail' });
534 return $status;
535 }});
536
537 __PACKAGE__->register_method ({
538 name => 'lspools',
539 path => 'pools',
540 method => 'GET',
541 description => "List all pools.",
542 proxyto => 'node',
543 protected => 1,
544 permissions => {
545 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
546 },
547 parameters => {
548 additionalProperties => 0,
549 properties => {
550 node => get_standard_option('pve-node'),
551 },
552 },
553 returns => {
554 type => 'array',
555 items => {
556 type => "object",
557 properties => {
558 pool => { type => 'integer' },
559 pool_name => { type => 'string' },
560 size => { type => 'integer' },
561 },
562 },
563 links => [ { rel => 'child', href => "{pool_name}" } ],
564 },
565 code => sub {
566 my ($param) = @_;
567
568 PVE::Ceph::Tools::check_ceph_inited();
569
570 my $rados = PVE::RADOS->new();
571
572 my $stats = {};
573 my $res = $rados->mon_command({ prefix => 'df' });
574
575 foreach my $d (@{$res->{pools}}) {
576 next if !$d->{stats};
577 next if !defined($d->{id});
578 $stats->{$d->{id}} = $d->{stats};
579 }
580
581 $res = $rados->mon_command({ prefix => 'osd dump' });
582 my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'});
583
584 my $rules = {};
585 for my $rule (@$rulestmp) {
586 $rules->{$rule->{rule_id}} = $rule->{rule_name};
587 }
588
589 my $data = [];
590 foreach my $e (@{$res->{pools}}) {
591 my $d = {};
592 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
593 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
594 }
595
596 if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) {
597 $d->{crush_rule_name} = $rules->{$d->{crush_rule}};
598 }
599
600 if (my $s = $stats->{$d->{pool}}) {
601 $d->{bytes_used} = $s->{bytes_used};
602 $d->{percent_used} = $s->{percent_used};
603 }
604 push @$data, $d;
605 }
606
607
608 return $data;
609 }});
610
611 __PACKAGE__->register_method ({
612 name => 'createpool',
613 path => 'pools',
614 method => 'POST',
615 description => "Create POOL",
616 proxyto => 'node',
617 protected => 1,
618 permissions => {
619 check => ['perm', '/', [ 'Sys.Modify' ]],
620 },
621 parameters => {
622 additionalProperties => 0,
623 properties => {
624 node => get_standard_option('pve-node'),
625 name => {
626 description => "The name of the pool. It must be unique.",
627 type => 'string',
628 },
629 size => {
630 description => 'Number of replicas per object',
631 type => 'integer',
632 default => 3,
633 optional => 1,
634 minimum => 1,
635 maximum => 7,
636 },
637 min_size => {
638 description => 'Minimum number of replicas per object',
639 type => 'integer',
640 default => 2,
641 optional => 1,
642 minimum => 1,
643 maximum => 7,
644 },
645 pg_num => {
646 description => "Number of placement groups.",
647 type => 'integer',
648 default => 128,
649 optional => 1,
650 minimum => 8,
651 maximum => 32768,
652 },
653 crush_rule => {
654 description => "The rule to use for mapping object placement in the cluster.",
655 type => 'string',
656 optional => 1,
657 },
658 application => {
659 description => "The application of the pool, 'rbd' by default.",
660 type => 'string',
661 enum => ['rbd', 'cephfs', 'rgw'],
662 optional => 1,
663 },
664 add_storages => {
665 description => "Configure VM and CT storage using the new pool.",
666 type => 'boolean',
667 optional => 1,
668 },
669 },
670 },
671 returns => { type => 'string' },
672 code => sub {
673 my ($param) = @_;
674
675 PVE::Cluster::check_cfs_quorum();
676 PVE::Ceph::Tools::check_ceph_inited();
677
678 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
679
680 die "not fully configured - missing '$pve_ckeyring_path'\n"
681 if ! -f $pve_ckeyring_path;
682
683 my $pool = $param->{name};
684 my $rpcenv = PVE::RPCEnvironment::get();
685 my $user = $rpcenv->get_user();
686
687 if ($param->{add_storages}) {
688 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
689 die "pool name contains characters which are illegal for storage naming\n"
690 if !PVE::JSONSchema::parse_storage_id($pool);
691 }
692
693 my $pg_num = $param->{pg_num} || 128;
694 my $size = $param->{size} || 3;
695 my $min_size = $param->{min_size} || 2;
696 my $application = $param->{application} // 'rbd';
697
698 my $worker = sub {
699
700 PVE::Ceph::Tools::create_pool($pool, $param);
701
702 if ($param->{add_storages}) {
703 my $err;
704 eval { $add_storage->($pool, "${pool}"); };
705 if ($@) {
706 warn "failed to add storage: $@";
707 $err = 1;
708 }
709 die "adding storage for pool '$pool' failed, check log and add manually!\n"
710 if $err;
711 }
712 };
713
714 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
715 }});
716
717 __PACKAGE__->register_method ({
718 name => 'get_flags',
719 path => 'flags',
720 method => 'GET',
721 description => "get all set ceph flags",
722 proxyto => 'node',
723 protected => 1,
724 permissions => {
725 check => ['perm', '/', [ 'Sys.Audit' ]],
726 },
727 parameters => {
728 additionalProperties => 0,
729 properties => {
730 node => get_standard_option('pve-node'),
731 },
732 },
733 returns => { type => 'string' },
734 code => sub {
735 my ($param) = @_;
736
737 PVE::Ceph::Tools::check_ceph_inited();
738
739 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
740
741 die "not fully configured - missing '$pve_ckeyring_path'\n"
742 if ! -f $pve_ckeyring_path;
743
744 my $rados = PVE::RADOS->new();
745
746 my $stat = $rados->mon_command({ prefix => 'osd dump' });
747
748 return $stat->{flags} // '';
749 }});
750
751 __PACKAGE__->register_method ({
752 name => 'set_flag',
753 path => 'flags/{flag}',
754 method => 'POST',
755 description => "Set a ceph flag",
756 proxyto => 'node',
757 protected => 1,
758 permissions => {
759 check => ['perm', '/', [ 'Sys.Modify' ]],
760 },
761 parameters => {
762 additionalProperties => 0,
763 properties => {
764 node => get_standard_option('pve-node'),
765 flag => {
766 description => 'The ceph flag to set/unset',
767 type => 'string',
768 enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
769 },
770 },
771 },
772 returns => { type => 'null' },
773 code => sub {
774 my ($param) = @_;
775
776 PVE::Ceph::Tools::check_ceph_inited();
777
778 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
779
780 die "not fully configured - missing '$pve_ckeyring_path'\n"
781 if ! -f $pve_ckeyring_path;
782
783 my $set = $param->{set} // !$param->{unset};
784 my $rados = PVE::RADOS->new();
785
786 $rados->mon_command({
787 prefix => "osd set",
788 key => $param->{flag},
789 });
790
791 return undef;
792 }});
793
794 __PACKAGE__->register_method ({
795 name => 'unset_flag',
796 path => 'flags/{flag}',
797 method => 'DELETE',
798 description => "Unset a ceph flag",
799 proxyto => 'node',
800 protected => 1,
801 permissions => {
802 check => ['perm', '/', [ 'Sys.Modify' ]],
803 },
804 parameters => {
805 additionalProperties => 0,
806 properties => {
807 node => get_standard_option('pve-node'),
808 flag => {
809 description => 'The ceph flag to set/unset',
810 type => 'string',
811 enum => [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
812 },
813 },
814 },
815 returns => { type => 'null' },
816 code => sub {
817 my ($param) = @_;
818
819 PVE::Ceph::Tools::check_ceph_inited();
820
821 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
822
823 die "not fully configured - missing '$pve_ckeyring_path'\n"
824 if ! -f $pve_ckeyring_path;
825
826 my $set = $param->{set} // !$param->{unset};
827 my $rados = PVE::RADOS->new();
828
829 $rados->mon_command({
830 prefix => "osd unset",
831 key => $param->{flag},
832 });
833
834 return undef;
835 }});
836
837 __PACKAGE__->register_method ({
838 name => 'destroypool',
839 path => 'pools/{name}',
840 method => 'DELETE',
841 description => "Destroy pool",
842 proxyto => 'node',
843 protected => 1,
844 permissions => {
845 check => ['perm', '/', [ 'Sys.Modify' ]],
846 },
847 parameters => {
848 additionalProperties => 0,
849 properties => {
850 node => get_standard_option('pve-node'),
851 name => {
852 description => "The name of the pool. It must be unique.",
853 type => 'string',
854 },
855 force => {
856 description => "If true, destroys pool even if in use",
857 type => 'boolean',
858 optional => 1,
859 default => 0,
860 },
861 remove_storages => {
862 description => "Remove all pveceph-managed storages configured for this pool",
863 type => 'boolean',
864 optional => 1,
865 default => 0,
866 },
867 },
868 },
869 returns => { type => 'string' },
870 code => sub {
871 my ($param) = @_;
872
873 PVE::Ceph::Tools::check_ceph_inited();
874
875 my $rpcenv = PVE::RPCEnvironment::get();
876 my $user = $rpcenv->get_user();
877 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
878 if $param->{remove_storages};
879
880 my $pool = $param->{name};
881
882 my $worker = sub {
883 my $storages = $get_storages->($pool);
884
885 # if not forced, destroy ceph pool only when no
886 # vm disks are on it anymore
887 if (!$param->{force}) {
888 my $storagecfg = PVE::Storage::config();
889 foreach my $storeid (keys %$storages) {
890 my $storage = $storages->{$storeid};
891
892 # check if any vm disks are on the pool
893 print "checking storage '$storeid' for RBD images..\n";
894 my $res = PVE::Storage::vdisk_list($storagecfg, $storeid);
895 die "ceph pool '$pool' still in use by storage '$storeid'\n"
896 if @{$res->{$storeid}} != 0;
897 }
898 }
899
900 PVE::Ceph::Tools::destroy_pool($pool);
901
902 if ($param->{remove_storages}) {
903 my $err;
904 foreach my $storeid (keys %$storages) {
905 # skip external clusters, not managed by pveceph
906 next if $storages->{$storeid}->{monhost};
907 eval { PVE::API2::Storage::Config->delete({storage => $storeid}) };
908 if ($@) {
909 warn "failed to remove storage '$storeid': $@\n";
910 $err = 1;
911 }
912 }
913 die "failed to remove (some) storages - check log and remove manually!\n"
914 if $err;
915 }
916 };
917 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
918 }});
919
920
921 __PACKAGE__->register_method ({
922 name => 'crush',
923 path => 'crush',
924 method => 'GET',
925 description => "Get OSD crush map",
926 proxyto => 'node',
927 protected => 1,
928 permissions => {
929 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
930 },
931 parameters => {
932 additionalProperties => 0,
933 properties => {
934 node => get_standard_option('pve-node'),
935 },
936 },
937 returns => { type => 'string' },
938 code => sub {
939 my ($param) = @_;
940
941 PVE::Ceph::Tools::check_ceph_inited();
942
943 # this produces JSON (difficult to read for the user)
944 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
945
946 my $txt = '';
947
948 my $mapfile = "/var/tmp/ceph-crush.map.$$";
949 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
950
951 my $rados = PVE::RADOS->new();
952
953 eval {
954 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
955 file_set_contents($mapfile, $bindata);
956 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
957 $txt = file_get_contents($mapdata);
958 };
959 my $err = $@;
960
961 unlink $mapfile;
962 unlink $mapdata;
963
964 die $err if $err;
965
966 return $txt;
967 }});
968
969 __PACKAGE__->register_method({
970 name => 'log',
971 path => 'log',
972 method => 'GET',
973 description => "Read ceph log",
974 proxyto => 'node',
975 permissions => {
976 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
977 },
978 protected => 1,
979 parameters => {
980 additionalProperties => 0,
981 properties => {
982 node => get_standard_option('pve-node'),
983 start => {
984 type => 'integer',
985 minimum => 0,
986 optional => 1,
987 },
988 limit => {
989 type => 'integer',
990 minimum => 0,
991 optional => 1,
992 },
993 },
994 },
995 returns => {
996 type => 'array',
997 items => {
998 type => "object",
999 properties => {
1000 n => {
1001 description=> "Line number",
1002 type=> 'integer',
1003 },
1004 t => {
1005 description=> "Line text",
1006 type => 'string',
1007 }
1008 }
1009 }
1010 },
1011 code => sub {
1012 my ($param) = @_;
1013
1014 PVE::Ceph::Tools::check_ceph_inited();
1015
1016 my $rpcenv = PVE::RPCEnvironment::get();
1017 my $user = $rpcenv->get_user();
1018 my $node = $param->{node};
1019
1020 my $logfile = "/var/log/ceph/ceph.log";
1021 my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
1022
1023 $rpcenv->set_result_attrib('total', $count);
1024
1025 return $lines;
1026 }});
1027
1028 __PACKAGE__->register_method ({
1029 name => 'rules',
1030 path => 'rules',
1031 method => 'GET',
1032 description => "List ceph rules.",
1033 proxyto => 'node',
1034 protected => 1,
1035 permissions => {
1036 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
1037 },
1038 parameters => {
1039 additionalProperties => 0,
1040 properties => {
1041 node => get_standard_option('pve-node'),
1042 },
1043 },
1044 returns => {
1045 type => 'array',
1046 items => {
1047 type => "object",
1048 properties => {},
1049 },
1050 links => [ { rel => 'child', href => "{name}" } ],
1051 },
1052 code => sub {
1053 my ($param) = @_;
1054
1055 PVE::Ceph::Tools::check_ceph_inited();
1056
1057 my $rados = PVE::RADOS->new();
1058
1059 my $rules = $rados->mon_command({ prefix => 'osd crush rule ls' });
1060
1061 my $res = [];
1062
1063 foreach my $rule (@$rules) {
1064 push @$res, { name => $rule };
1065 }
1066
1067 return $res;
1068 }});
1069
1070 1;