]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Ceph.pm
ui: resource tree: add usage percentage to storage tooltip
[pve-manager.git] / PVE / API2 / Ceph.pm
CommitLineData
7d4fc5ef
DM
1package PVE::API2::Ceph;
2
3use strict;
4use warnings;
0cd34b00 5
7d4fc5ef 6use File::Path;
7d4fc5ef 7use Net::IP;
0cd34b00 8use UUID;
7d4fc5ef 9
6fb08cb9 10use PVE::Ceph::Tools;
27439be6 11use PVE::Ceph::Services;
c31f487e 12use PVE::Cluster qw(cfs_read_file cfs_write_file);
0cd34b00 13use PVE::JSONSchema qw(get_standard_option);
9f4ff798 14use PVE::Network;
0cd34b00
TL
15use PVE::RADOS;
16use PVE::RESTHandler;
17use PVE::RPCEnvironment;
18use PVE::Storage;
063b6d5e 19use PVE::Tools qw(run_command file_get_contents file_set_contents extract_param);
7d4fc5ef 20
26cf20bc 21use PVE::API2::Ceph::Cfg;
79fa41a2 22use PVE::API2::Ceph::OSD;
7e1a9d25 23use PVE::API2::Ceph::FS;
b82649cc 24use PVE::API2::Ceph::MDS;
4fec2764 25use PVE::API2::Ceph::MGR;
98fe93ae 26use PVE::API2::Ceph::MON;
b2e005d7 27use PVE::API2::Ceph::Pool;
0cd34b00 28use PVE::API2::Storage::Config;
7d4fc5ef 29
0cd34b00 30use base qw(PVE::RESTHandler);
7d4fc5ef
DM
31
32my $pve_osd_default_journal_size = 1024*5;
33
26cf20bc
AL
34__PACKAGE__->register_method ({
35 subclass => "PVE::API2::Ceph::Cfg",
36 path => 'cfg',
37});
38
7d4fc5ef 39__PACKAGE__->register_method ({
79fa41a2 40 subclass => "PVE::API2::Ceph::OSD",
7d4fc5ef
DM
41 path => 'osd',
42});
43
b82649cc
TL
44__PACKAGE__->register_method ({
45 subclass => "PVE::API2::Ceph::MDS",
46 path => 'mds',
47});
48
4fec2764
DC
49__PACKAGE__->register_method ({
50 subclass => "PVE::API2::Ceph::MGR",
51 path => 'mgr',
52});
53
98fe93ae
DC
54__PACKAGE__->register_method ({
55 subclass => "PVE::API2::Ceph::MON",
56 path => 'mon',
57});
58
7e1a9d25
TL
59__PACKAGE__->register_method ({
60 subclass => "PVE::API2::Ceph::FS",
61 path => 'fs',
62});
63
b2e005d7
AL
64__PACKAGE__->register_method ({
65 subclass => "PVE::API2::Ceph::Pool",
66 path => 'pool',
67});
68
7d4fc5ef
DM
69__PACKAGE__->register_method ({
70 name => 'index',
71 path => '',
72 method => 'GET',
73 description => "Directory index.",
74 permissions => { user => 'all' },
90c75580
TL
75 permissions => {
76 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
77 },
7d4fc5ef 78 parameters => {
be753927 79 additionalProperties => 0,
7d4fc5ef
DM
80 properties => {
81 node => get_standard_option('pve-node'),
82 },
83 },
84 returns => {
85 type => 'array',
86 items => {
87 type => "object",
88 properties => {},
89 },
90 links => [ { rel => 'child', href => "{name}" } ],
91 },
92 code => sub {
93 my ($param) = @_;
94
95 my $result = [
56b03211 96 { name => 'cmd-safety' },
26cf20bc 97 { name => 'cfg' },
56b03211
AL
98 { name => 'crush' },
99 { name => 'fs' },
7d4fc5ef 100 { name => 'init' },
56b03211
AL
101 { name => 'log' },
102 { name => 'mds' },
103 { name => 'mgr' },
7d4fc5ef
DM
104 { name => 'mon' },
105 { name => 'osd' },
f507ec30 106 { name => 'pool' },
2d7d6c9a 107 { name => 'restart' },
d2692b86 108 { name => 'rules' },
56b03211
AL
109 { name => 'start' },
110 { name => 'status' },
111 { name => 'stop' },
7d4fc5ef
DM
112 ];
113
114 return $result;
115 }});
116
7d4fc5ef
DM
117__PACKAGE__->register_method ({
118 name => 'init',
119 path => 'init',
120 method => 'POST',
121 description => "Create initial ceph default configuration and setup symlinks.",
122 proxyto => 'node',
123 protected => 1,
90c75580
TL
124 permissions => {
125 check => ['perm', '/', [ 'Sys.Modify' ]],
126 },
7d4fc5ef 127 parameters => {
be753927 128 additionalProperties => 0,
7d4fc5ef
DM
129 properties => {
130 node => get_standard_option('pve-node'),
131 network => {
be753927 132 description => "Use specific network for all ceph related traffic",
7d4fc5ef
DM
133 type => 'string', format => 'CIDR',
134 optional => 1,
135 maxLength => 128,
136 },
7519b848
TL
137 'cluster-network' => {
138 description => "Declare a separate cluster network, OSDs will route" .
139 "heartbeat, object replication and recovery traffic over it",
140 type => 'string', format => 'CIDR',
141 requires => 'network',
142 optional => 1,
143 maxLength => 128,
144 },
7d4fc5ef 145 size => {
207f4932
FG
146 description => 'Targeted number of replicas per object',
147 type => 'integer',
148 default => 3,
149 optional => 1,
150 minimum => 1,
151 maximum => 7,
152 },
153 min_size => {
154 description => 'Minimum number of available replicas per object to allow I/O',
7d4fc5ef
DM
155 type => 'integer',
156 default => 2,
157 optional => 1,
158 minimum => 1,
83663637 159 maximum => 7,
7d4fc5ef
DM
160 },
161 pg_bits => {
3cba09d5
FG
162 description => "Placement group bits, used to specify the " .
163 "default number of placement groups.\n\nNOTE: 'osd pool " .
164 "default pg num' does not work for default pools.",
7d4fc5ef
DM
165 type => 'integer',
166 default => 6,
167 optional => 1,
168 minimum => 6,
169 maximum => 14,
170 },
4280f25c 171 disable_cephx => {
76189130 172 description => "Disable cephx authentication.\n\n" .
97f050bb
FG
173 "WARNING: cephx is a security feature protecting against " .
174 "man-in-the-middle attacks. Only consider disabling cephx ".
175 "if your network is private!",
77bb90b0
AD
176 type => 'boolean',
177 optional => 1,
178 default => 0,
4280f25c 179 },
7d4fc5ef
DM
180 },
181 },
182 returns => { type => 'null' },
183 code => sub {
184 my ($param) = @_;
185
6fb08cb9 186 my $version = PVE::Ceph::Tools::get_local_version(1);
c64c04dd 187
b3d8b5f5
DC
188 if (!$version || $version < 14) {
189 die "Ceph Nautilus required - please run 'pveceph install'\n";
c64c04dd 190 } else {
6fb08cb9 191 PVE::Ceph::Tools::check_ceph_installed('ceph_bin');
c64c04dd 192 }
7d4fc5ef 193
50d5fd6a
FG
194 my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
195
7d4fc5ef 196 # simply load old config if it already exists
3e4c0f06
DC
197 PVE::Cluster::cfs_lock_file('ceph.conf', undef, sub {
198 my $cfg = cfs_read_file('ceph.conf');
7d4fc5ef 199
3e4c0f06 200 if (!$cfg->{global}) {
7d4fc5ef 201
3e4c0f06
DC
202 my $fsid;
203 my $uuid;
7d4fc5ef 204
3e4c0f06
DC
205 UUID::generate($uuid);
206 UUID::unparse($uuid, $fsid);
7d4fc5ef 207
3e4c0f06
DC
208 $cfg->{global} = {
209 'fsid' => $fsid,
210 'auth cluster required' => $auth,
211 'auth service required' => $auth,
212 'auth client required' => $auth,
213 'osd pool default size' => $param->{size} // 3,
214 'osd pool default min size' => $param->{min_size} // 2,
215 'mon allow pool delete' => 'true',
216 };
7d4fc5ef 217
3e4c0f06
DC
218 # this does not work for default pools
219 #'osd pool default pg num' => $pg_num,
220 #'osd pool default pgp num' => $pg_num,
221 }
be753927 222
d851d63e
DC
223 if ($auth eq 'cephx') {
224 $cfg->{client}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
225 }
7d4fc5ef 226
3e4c0f06
DC
227 if ($param->{pg_bits}) {
228 $cfg->{global}->{'osd pg bits'} = $param->{pg_bits};
229 $cfg->{global}->{'osd pgp bits'} = $param->{pg_bits};
230 }
7d4fc5ef 231
3e4c0f06
DC
232 if ($param->{network}) {
233 $cfg->{global}->{'public network'} = $param->{network};
234 $cfg->{global}->{'cluster network'} = $param->{network};
235 }
7d4fc5ef 236
3e4c0f06
DC
237 if ($param->{'cluster-network'}) {
238 $cfg->{global}->{'cluster network'} = $param->{'cluster-network'};
239 }
7519b848 240
3e4c0f06 241 cfs_write_file('ceph.conf', $cfg);
7d4fc5ef 242
d851d63e
DC
243 if ($auth eq 'cephx') {
244 PVE::Ceph::Tools::get_or_create_admin_keyring();
245 }
3e4c0f06
DC
246 PVE::Ceph::Tools::setup_pve_symlinks();
247 });
8e2b5110 248 die $@ if $@;
7d4fc5ef
DM
249
250 return undef;
251 }});
252
38db610a
DM
253__PACKAGE__->register_method ({
254 name => 'stop',
255 path => 'stop',
256 method => 'POST',
257 description => "Stop ceph services.",
258 proxyto => 'node',
259 protected => 1,
90c75580
TL
260 permissions => {
261 check => ['perm', '/', [ 'Sys.Modify' ]],
262 },
38db610a 263 parameters => {
be753927 264 additionalProperties => 0,
38db610a
DM
265 properties => {
266 node => get_standard_option('pve-node'),
68e0c4bd
DM
267 service => {
268 description => 'Ceph service name.',
269 type => 'string',
270 optional => 1,
33a9c70a 271 default => 'ceph.target',
7e98f79e 272 pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
68e0c4bd 273 },
38db610a
DM
274 },
275 },
68e0c4bd 276 returns => { type => 'string' },
38db610a
DM
277 code => sub {
278 my ($param) = @_;
279
68e0c4bd
DM
280 my $rpcenv = PVE::RPCEnvironment::get();
281
282 my $authuser = $rpcenv->get_user();
283
6fb08cb9 284 PVE::Ceph::Tools::check_ceph_inited();
38db610a 285
c31f487e 286 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
287 scalar(keys %$cfg) || die "no configuration\n";
288
68e0c4bd
DM
289 my $worker = sub {
290 my $upid = shift;
38db610a 291
68e0c4bd
DM
292 my $cmd = ['stop'];
293 if ($param->{service}) {
294 push @$cmd, $param->{service};
295 }
296
27439be6 297 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
298 };
299
300 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
301 $authuser, $worker);
38db610a
DM
302 }});
303
304__PACKAGE__->register_method ({
305 name => 'start',
306 path => 'start',
307 method => 'POST',
308 description => "Start ceph services.",
309 proxyto => 'node',
310 protected => 1,
90c75580
TL
311 permissions => {
312 check => ['perm', '/', [ 'Sys.Modify' ]],
313 },
38db610a 314 parameters => {
be753927 315 additionalProperties => 0,
38db610a
DM
316 properties => {
317 node => get_standard_option('pve-node'),
68e0c4bd
DM
318 service => {
319 description => 'Ceph service name.',
320 type => 'string',
321 optional => 1,
33a9c70a 322 default => 'ceph.target',
7e98f79e 323 pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
68e0c4bd 324 },
38db610a
DM
325 },
326 },
68e0c4bd 327 returns => { type => 'string' },
38db610a
DM
328 code => sub {
329 my ($param) = @_;
330
68e0c4bd
DM
331 my $rpcenv = PVE::RPCEnvironment::get();
332
333 my $authuser = $rpcenv->get_user();
334
6fb08cb9 335 PVE::Ceph::Tools::check_ceph_inited();
38db610a 336
c31f487e 337 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
338 scalar(keys %$cfg) || die "no configuration\n";
339
68e0c4bd
DM
340 my $worker = sub {
341 my $upid = shift;
38db610a 342
68e0c4bd
DM
343 my $cmd = ['start'];
344 if ($param->{service}) {
345 push @$cmd, $param->{service};
346 }
347
27439be6 348 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
349 };
350
351 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
352 $authuser, $worker);
38db610a
DM
353 }});
354
342c0830
DC
355__PACKAGE__->register_method ({
356 name => 'restart',
357 path => 'restart',
358 method => 'POST',
359 description => "Restart ceph services.",
360 proxyto => 'node',
361 protected => 1,
362 permissions => {
363 check => ['perm', '/', [ 'Sys.Modify' ]],
364 },
365 parameters => {
366 additionalProperties => 0,
367 properties => {
368 node => get_standard_option('pve-node'),
369 service => {
370 description => 'Ceph service name.',
371 type => 'string',
372 optional => 1,
33a9c70a 373 default => 'ceph.target',
7e98f79e 374 pattern => '(mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
342c0830
DC
375 },
376 },
377 },
378 returns => { type => 'string' },
379 code => sub {
380 my ($param) = @_;
381
382 my $rpcenv = PVE::RPCEnvironment::get();
383
384 my $authuser = $rpcenv->get_user();
385
6fb08cb9 386 PVE::Ceph::Tools::check_ceph_inited();
342c0830 387
c31f487e 388 my $cfg = cfs_read_file('ceph.conf');
342c0830
DC
389 scalar(keys %$cfg) || die "no configuration\n";
390
391 my $worker = sub {
392 my $upid = shift;
393
394 my $cmd = ['restart'];
395 if ($param->{service}) {
396 push @$cmd, $param->{service};
397 }
398
27439be6 399 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
342c0830
DC
400 };
401
402 return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
403 $authuser, $worker);
404 }});
405
38db610a
DM
406__PACKAGE__->register_method ({
407 name => 'status',
408 path => 'status',
409 method => 'GET',
410 description => "Get ceph status.",
411 proxyto => 'node',
412 protected => 1,
90c75580
TL
413 permissions => {
414 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
415 },
38db610a 416 parameters => {
be753927 417 additionalProperties => 0,
38db610a
DM
418 properties => {
419 node => get_standard_option('pve-node'),
420 },
421 },
422 returns => { type => 'object' },
423 code => sub {
424 my ($param) = @_;
425
0cfc6856 426 PVE::Ceph::Tools::check_ceph_inited();
38db610a 427
e25dda25 428 return PVE::Ceph::Tools::ceph_cluster_status();
38db610a
DM
429 }});
430
38db610a 431
2f692121
DM
432__PACKAGE__->register_method ({
433 name => 'crush',
434 path => 'crush',
435 method => 'GET',
436 description => "Get OSD crush map",
437 proxyto => 'node',
438 protected => 1,
90c75580
TL
439 permissions => {
440 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
441 },
2f692121 442 parameters => {
be753927 443 additionalProperties => 0,
2f692121
DM
444 properties => {
445 node => get_standard_option('pve-node'),
446 },
447 },
448 returns => { type => 'string' },
449 code => sub {
450 my ($param) = @_;
451
6fb08cb9 452 PVE::Ceph::Tools::check_ceph_inited();
2f692121 453
8b336060
DM
454 # this produces JSON (difficult to read for the user)
455 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
2f692121 456
8b336060
DM
457 my $txt = '';
458
459 my $mapfile = "/var/tmp/ceph-crush.map.$$";
460 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
461
36fd0190 462 my $rados = PVE::RADOS->new();
be753927 463
8b336060 464 eval {
970236b3 465 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
400742e4 466 file_set_contents($mapfile, $bindata);
8b336060 467 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
400742e4 468 $txt = file_get_contents($mapdata);
8b336060
DM
469 };
470 my $err = $@;
471
472 unlink $mapfile;
473 unlink $mapdata;
474
475 die $err if $err;
be753927 476
2f692121
DM
477 return $txt;
478 }});
479
570278fa 480__PACKAGE__->register_method({
be753927
DC
481 name => 'log',
482 path => 'log',
570278fa
DM
483 method => 'GET',
484 description => "Read ceph log",
485 proxyto => 'node',
486 permissions => {
487 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
488 },
489 protected => 1,
490 parameters => {
be753927 491 additionalProperties => 0,
570278fa
DM
492 properties => {
493 node => get_standard_option('pve-node'),
494 start => {
495 type => 'integer',
496 minimum => 0,
497 optional => 1,
498 },
499 limit => {
500 type => 'integer',
501 minimum => 0,
502 optional => 1,
503 },
504 },
505 },
506 returns => {
507 type => 'array',
be753927 508 items => {
570278fa
DM
509 type => "object",
510 properties => {
511 n => {
512 description=> "Line number",
513 type=> 'integer',
514 },
515 t => {
516 description=> "Line text",
517 type => 'string',
518 }
519 }
520 }
521 },
522 code => sub {
523 my ($param) = @_;
c56d75b4 524
6fb08cb9 525 PVE::Ceph::Tools::check_ceph_inited();
570278fa
DM
526
527 my $rpcenv = PVE::RPCEnvironment::get();
528 my $user = $rpcenv->get_user();
529 my $node = $param->{node};
530
531 my $logfile = "/var/log/ceph/ceph.log";
532 my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
533
534 $rpcenv->set_result_attrib('total', $count);
be753927
DC
535
536 return $lines;
570278fa
DM
537 }});
538
d2692b86
DC
539__PACKAGE__->register_method ({
540 name => 'rules',
541 path => 'rules',
542 method => 'GET',
543 description => "List ceph rules.",
544 proxyto => 'node',
545 protected => 1,
546 permissions => {
547 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
548 },
549 parameters => {
550 additionalProperties => 0,
551 properties => {
552 node => get_standard_option('pve-node'),
553 },
554 },
555 returns => {
556 type => 'array',
557 items => {
558 type => "object",
b62ba85a
AL
559 properties => {
560 name => {
561 description => "Name of the CRUSH rule.",
562 type => "string",
563 }
564 },
d2692b86
DC
565 },
566 links => [ { rel => 'child', href => "{name}" } ],
567 },
568 code => sub {
569 my ($param) = @_;
570
6fb08cb9 571 PVE::Ceph::Tools::check_ceph_inited();
2f692121 572
d2692b86
DC
573 my $rados = PVE::RADOS->new();
574
575 my $rules = $rados->mon_command({ prefix => 'osd crush rule ls' });
576
577 my $res = [];
578
579 foreach my $rule (@$rules) {
580 push @$res, { name => $rule };
581 }
582
583 return $res;
584 }});
79fa41a2 585
ccb281a7
AL
586__PACKAGE__->register_method ({
587 name => 'cmd_safety',
588 path => 'cmd-safety',
589 method => 'GET',
590 description => "Heuristical check if it is safe to perform an action.",
591 proxyto => 'node',
592 protected => 1,
593 permissions => {
136f5724 594 check => ['perm', '/', [ 'Sys.Audit' ]],
ccb281a7
AL
595 },
596 parameters => {
597 additionalProperties => 0,
598 properties => {
599 node => get_standard_option('pve-node'),
600 service => {
601 description => 'Service type',
602 type => 'string',
603 enum => ['osd', 'mon', 'mds'],
604 },
605 id => {
606 description => 'ID of the service',
607 type => 'string',
608 },
609 action => {
610 description => 'Action to check',
611 type => 'string',
612 enum => ['stop', 'destroy'],
613 },
614 },
615 },
616 returns => {
617 type => 'object',
618 properties => {
619 safe => {
620 type => 'boolean',
621 description => 'If it is safe to run the command.',
622 },
623 status => {
624 type => 'string',
625 optional => 1,
626 description => 'Status message given by Ceph.'
627 },
628 },
629 },
630 code => sub {
631 my ($param) = @_;
632
633 PVE::Ceph::Tools::check_ceph_inited();
634
635 my $id = $param->{id};
636 my $service = $param->{service};
637 my $action = $param->{action};
638
639 my $rados = PVE::RADOS->new();
640
641 my $supported_actions = {
642 osd => {
643 stop => 'ok-to-stop',
644 destroy => 'safe-to-destroy',
645 },
646 mon => {
647 stop => 'ok-to-stop',
648 destroy => 'ok-to-rm',
649 },
650 mds => {
651 stop => 'ok-to-stop',
652 },
653 };
654
655 die "Service does not support this action: ${service}: ${action}\n"
656 if !$supported_actions->{$service}->{$action};
657
658 my $result = {
659 safe => 0,
660 status => '',
661 };
662
663 my $params = {
664 prefix => "${service} $supported_actions->{$service}->{$action}",
665 format => 'plain',
666 };
667 if ($service eq 'mon' && $action eq 'destroy') {
668 $params->{id} = $id;
669 } else {
670 $params->{ids} = [ $id ];
671 }
672
673 $result = $rados->mon_cmd($params, 1);
674 die $@ if $@;
675
676 $result->{safe} = $result->{return_code} == 0 ? 1 : 0;
677 $result->{status} = $result->{status_message};
678
679 return $result;
680 }});
681
79fa41a2 6821;