]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Ceph.pm
ceph: mark global pg bits setting as deprecated
[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 160 },
9b15baf2 161 # TODO: deprecrated, remove with PVE 9
7d4fc5ef 162 pg_bits => {
3cba09d5 163 description => "Placement group bits, used to specify the " .
9b15baf2
MS
164 "default number of placement groups.\n\nDepreacted. This " .
165 "setting was deprecated in recent Ceph versions.",
7d4fc5ef
DM
166 type => 'integer',
167 default => 6,
168 optional => 1,
169 minimum => 6,
170 maximum => 14,
171 },
4280f25c 172 disable_cephx => {
76189130 173 description => "Disable cephx authentication.\n\n" .
97f050bb
FG
174 "WARNING: cephx is a security feature protecting against " .
175 "man-in-the-middle attacks. Only consider disabling cephx ".
176 "if your network is private!",
77bb90b0
AD
177 type => 'boolean',
178 optional => 1,
179 default => 0,
4280f25c 180 },
7d4fc5ef
DM
181 },
182 },
183 returns => { type => 'null' },
184 code => sub {
185 my ($param) = @_;
186
6fb08cb9 187 my $version = PVE::Ceph::Tools::get_local_version(1);
c64c04dd 188
b3d8b5f5
DC
189 if (!$version || $version < 14) {
190 die "Ceph Nautilus required - please run 'pveceph install'\n";
c64c04dd 191 } else {
6fb08cb9 192 PVE::Ceph::Tools::check_ceph_installed('ceph_bin');
c64c04dd 193 }
7d4fc5ef 194
50d5fd6a
FG
195 my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
196
7d4fc5ef 197 # simply load old config if it already exists
3e4c0f06
DC
198 PVE::Cluster::cfs_lock_file('ceph.conf', undef, sub {
199 my $cfg = cfs_read_file('ceph.conf');
7d4fc5ef 200
3e4c0f06 201 if (!$cfg->{global}) {
7d4fc5ef 202
3e4c0f06
DC
203 my $fsid;
204 my $uuid;
7d4fc5ef 205
3e4c0f06
DC
206 UUID::generate($uuid);
207 UUID::unparse($uuid, $fsid);
7d4fc5ef 208
3e4c0f06
DC
209 $cfg->{global} = {
210 'fsid' => $fsid,
211 'auth cluster required' => $auth,
212 'auth service required' => $auth,
213 'auth client required' => $auth,
214 'osd pool default size' => $param->{size} // 3,
215 'osd pool default min size' => $param->{min_size} // 2,
216 'mon allow pool delete' => 'true',
217 };
7d4fc5ef 218
3e4c0f06
DC
219 # this does not work for default pools
220 #'osd pool default pg num' => $pg_num,
221 #'osd pool default pgp num' => $pg_num,
222 }
be753927 223
d851d63e
DC
224 if ($auth eq 'cephx') {
225 $cfg->{client}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
226 }
7d4fc5ef 227
3e4c0f06
DC
228 if ($param->{network}) {
229 $cfg->{global}->{'public network'} = $param->{network};
230 $cfg->{global}->{'cluster network'} = $param->{network};
231 }
7d4fc5ef 232
3e4c0f06
DC
233 if ($param->{'cluster-network'}) {
234 $cfg->{global}->{'cluster network'} = $param->{'cluster-network'};
235 }
7519b848 236
3e4c0f06 237 cfs_write_file('ceph.conf', $cfg);
7d4fc5ef 238
d851d63e
DC
239 if ($auth eq 'cephx') {
240 PVE::Ceph::Tools::get_or_create_admin_keyring();
241 }
3e4c0f06
DC
242 PVE::Ceph::Tools::setup_pve_symlinks();
243 });
8e2b5110 244 die $@ if $@;
7d4fc5ef
DM
245
246 return undef;
247 }});
248
38db610a
DM
249__PACKAGE__->register_method ({
250 name => 'stop',
251 path => 'stop',
252 method => 'POST',
253 description => "Stop ceph services.",
254 proxyto => 'node',
255 protected => 1,
90c75580
TL
256 permissions => {
257 check => ['perm', '/', [ 'Sys.Modify' ]],
258 },
38db610a 259 parameters => {
be753927 260 additionalProperties => 0,
38db610a
DM
261 properties => {
262 node => get_standard_option('pve-node'),
68e0c4bd
DM
263 service => {
264 description => 'Ceph service name.',
265 type => 'string',
266 optional => 1,
33a9c70a 267 default => 'ceph.target',
7e98f79e 268 pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
68e0c4bd 269 },
38db610a
DM
270 },
271 },
68e0c4bd 272 returns => { type => 'string' },
38db610a
DM
273 code => sub {
274 my ($param) = @_;
275
68e0c4bd
DM
276 my $rpcenv = PVE::RPCEnvironment::get();
277
278 my $authuser = $rpcenv->get_user();
279
6fb08cb9 280 PVE::Ceph::Tools::check_ceph_inited();
38db610a 281
c31f487e 282 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
283 scalar(keys %$cfg) || die "no configuration\n";
284
68e0c4bd
DM
285 my $worker = sub {
286 my $upid = shift;
38db610a 287
68e0c4bd
DM
288 my $cmd = ['stop'];
289 if ($param->{service}) {
290 push @$cmd, $param->{service};
291 }
292
27439be6 293 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
294 };
295
296 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
297 $authuser, $worker);
38db610a
DM
298 }});
299
300__PACKAGE__->register_method ({
301 name => 'start',
302 path => 'start',
303 method => 'POST',
304 description => "Start ceph services.",
305 proxyto => 'node',
306 protected => 1,
90c75580
TL
307 permissions => {
308 check => ['perm', '/', [ 'Sys.Modify' ]],
309 },
38db610a 310 parameters => {
be753927 311 additionalProperties => 0,
38db610a
DM
312 properties => {
313 node => get_standard_option('pve-node'),
68e0c4bd
DM
314 service => {
315 description => 'Ceph service name.',
316 type => 'string',
317 optional => 1,
33a9c70a 318 default => 'ceph.target',
7e98f79e 319 pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
68e0c4bd 320 },
38db610a
DM
321 },
322 },
68e0c4bd 323 returns => { type => 'string' },
38db610a
DM
324 code => sub {
325 my ($param) = @_;
326
68e0c4bd
DM
327 my $rpcenv = PVE::RPCEnvironment::get();
328
329 my $authuser = $rpcenv->get_user();
330
6fb08cb9 331 PVE::Ceph::Tools::check_ceph_inited();
38db610a 332
c31f487e 333 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
334 scalar(keys %$cfg) || die "no configuration\n";
335
68e0c4bd
DM
336 my $worker = sub {
337 my $upid = shift;
38db610a 338
68e0c4bd
DM
339 my $cmd = ['start'];
340 if ($param->{service}) {
341 push @$cmd, $param->{service};
342 }
343
27439be6 344 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
345 };
346
347 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
348 $authuser, $worker);
38db610a
DM
349 }});
350
342c0830
DC
351__PACKAGE__->register_method ({
352 name => 'restart',
353 path => 'restart',
354 method => 'POST',
355 description => "Restart ceph services.",
356 proxyto => 'node',
357 protected => 1,
358 permissions => {
359 check => ['perm', '/', [ 'Sys.Modify' ]],
360 },
361 parameters => {
362 additionalProperties => 0,
363 properties => {
364 node => get_standard_option('pve-node'),
365 service => {
366 description => 'Ceph service name.',
367 type => 'string',
368 optional => 1,
33a9c70a 369 default => 'ceph.target',
7e98f79e 370 pattern => '(mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
342c0830
DC
371 },
372 },
373 },
374 returns => { type => 'string' },
375 code => sub {
376 my ($param) = @_;
377
378 my $rpcenv = PVE::RPCEnvironment::get();
379
380 my $authuser = $rpcenv->get_user();
381
6fb08cb9 382 PVE::Ceph::Tools::check_ceph_inited();
342c0830 383
c31f487e 384 my $cfg = cfs_read_file('ceph.conf');
342c0830
DC
385 scalar(keys %$cfg) || die "no configuration\n";
386
387 my $worker = sub {
388 my $upid = shift;
389
390 my $cmd = ['restart'];
391 if ($param->{service}) {
392 push @$cmd, $param->{service};
393 }
394
27439be6 395 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
342c0830
DC
396 };
397
398 return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
399 $authuser, $worker);
400 }});
401
38db610a
DM
402__PACKAGE__->register_method ({
403 name => 'status',
404 path => 'status',
405 method => 'GET',
406 description => "Get ceph status.",
407 proxyto => 'node',
408 protected => 1,
90c75580
TL
409 permissions => {
410 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
411 },
38db610a 412 parameters => {
be753927 413 additionalProperties => 0,
38db610a
DM
414 properties => {
415 node => get_standard_option('pve-node'),
416 },
417 },
418 returns => { type => 'object' },
419 code => sub {
420 my ($param) = @_;
421
0cfc6856 422 PVE::Ceph::Tools::check_ceph_inited();
38db610a 423
e25dda25 424 return PVE::Ceph::Tools::ceph_cluster_status();
38db610a
DM
425 }});
426
38db610a 427
2f692121
DM
428__PACKAGE__->register_method ({
429 name => 'crush',
430 path => 'crush',
431 method => 'GET',
432 description => "Get OSD crush map",
433 proxyto => 'node',
434 protected => 1,
90c75580
TL
435 permissions => {
436 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
437 },
2f692121 438 parameters => {
be753927 439 additionalProperties => 0,
2f692121
DM
440 properties => {
441 node => get_standard_option('pve-node'),
442 },
443 },
444 returns => { type => 'string' },
445 code => sub {
446 my ($param) = @_;
447
6fb08cb9 448 PVE::Ceph::Tools::check_ceph_inited();
2f692121 449
8b336060
DM
450 # this produces JSON (difficult to read for the user)
451 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
2f692121 452
8b336060
DM
453 my $txt = '';
454
455 my $mapfile = "/var/tmp/ceph-crush.map.$$";
456 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
457
36fd0190 458 my $rados = PVE::RADOS->new();
be753927 459
8b336060 460 eval {
970236b3 461 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
400742e4 462 file_set_contents($mapfile, $bindata);
8b336060 463 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
400742e4 464 $txt = file_get_contents($mapdata);
8b336060
DM
465 };
466 my $err = $@;
467
468 unlink $mapfile;
469 unlink $mapdata;
470
471 die $err if $err;
be753927 472
2f692121
DM
473 return $txt;
474 }});
475
570278fa 476__PACKAGE__->register_method({
be753927
DC
477 name => 'log',
478 path => 'log',
570278fa
DM
479 method => 'GET',
480 description => "Read ceph log",
481 proxyto => 'node',
482 permissions => {
483 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
484 },
485 protected => 1,
486 parameters => {
be753927 487 additionalProperties => 0,
570278fa
DM
488 properties => {
489 node => get_standard_option('pve-node'),
490 start => {
491 type => 'integer',
492 minimum => 0,
493 optional => 1,
494 },
495 limit => {
496 type => 'integer',
497 minimum => 0,
498 optional => 1,
499 },
500 },
501 },
502 returns => {
503 type => 'array',
be753927 504 items => {
570278fa
DM
505 type => "object",
506 properties => {
507 n => {
508 description=> "Line number",
509 type=> 'integer',
510 },
511 t => {
512 description=> "Line text",
513 type => 'string',
514 }
515 }
516 }
517 },
518 code => sub {
519 my ($param) = @_;
c56d75b4 520
6fb08cb9 521 PVE::Ceph::Tools::check_ceph_inited();
570278fa
DM
522
523 my $rpcenv = PVE::RPCEnvironment::get();
524 my $user = $rpcenv->get_user();
525 my $node = $param->{node};
526
527 my $logfile = "/var/log/ceph/ceph.log";
528 my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
529
530 $rpcenv->set_result_attrib('total', $count);
be753927
DC
531
532 return $lines;
570278fa
DM
533 }});
534
d2692b86
DC
535__PACKAGE__->register_method ({
536 name => 'rules',
537 path => 'rules',
538 method => 'GET',
539 description => "List ceph rules.",
540 proxyto => 'node',
541 protected => 1,
542 permissions => {
543 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
544 },
545 parameters => {
546 additionalProperties => 0,
547 properties => {
548 node => get_standard_option('pve-node'),
549 },
550 },
551 returns => {
552 type => 'array',
553 items => {
554 type => "object",
b62ba85a
AL
555 properties => {
556 name => {
557 description => "Name of the CRUSH rule.",
558 type => "string",
559 }
560 },
d2692b86
DC
561 },
562 links => [ { rel => 'child', href => "{name}" } ],
563 },
564 code => sub {
565 my ($param) = @_;
566
6fb08cb9 567 PVE::Ceph::Tools::check_ceph_inited();
2f692121 568
d2692b86
DC
569 my $rados = PVE::RADOS->new();
570
571 my $rules = $rados->mon_command({ prefix => 'osd crush rule ls' });
572
573 my $res = [];
574
575 foreach my $rule (@$rules) {
576 push @$res, { name => $rule };
577 }
578
579 return $res;
580 }});
79fa41a2 581
ccb281a7
AL
582__PACKAGE__->register_method ({
583 name => 'cmd_safety',
584 path => 'cmd-safety',
585 method => 'GET',
586 description => "Heuristical check if it is safe to perform an action.",
587 proxyto => 'node',
588 protected => 1,
589 permissions => {
136f5724 590 check => ['perm', '/', [ 'Sys.Audit' ]],
ccb281a7
AL
591 },
592 parameters => {
593 additionalProperties => 0,
594 properties => {
595 node => get_standard_option('pve-node'),
596 service => {
597 description => 'Service type',
598 type => 'string',
599 enum => ['osd', 'mon', 'mds'],
600 },
601 id => {
602 description => 'ID of the service',
603 type => 'string',
604 },
605 action => {
606 description => 'Action to check',
607 type => 'string',
608 enum => ['stop', 'destroy'],
609 },
610 },
611 },
612 returns => {
613 type => 'object',
614 properties => {
615 safe => {
616 type => 'boolean',
617 description => 'If it is safe to run the command.',
618 },
619 status => {
620 type => 'string',
621 optional => 1,
622 description => 'Status message given by Ceph.'
623 },
624 },
625 },
626 code => sub {
627 my ($param) = @_;
628
629 PVE::Ceph::Tools::check_ceph_inited();
630
631 my $id = $param->{id};
632 my $service = $param->{service};
633 my $action = $param->{action};
634
635 my $rados = PVE::RADOS->new();
636
637 my $supported_actions = {
638 osd => {
639 stop => 'ok-to-stop',
640 destroy => 'safe-to-destroy',
641 },
642 mon => {
643 stop => 'ok-to-stop',
644 destroy => 'ok-to-rm',
645 },
646 mds => {
647 stop => 'ok-to-stop',
648 },
649 };
650
651 die "Service does not support this action: ${service}: ${action}\n"
652 if !$supported_actions->{$service}->{$action};
653
654 my $result = {
655 safe => 0,
656 status => '',
657 };
658
659 my $params = {
660 prefix => "${service} $supported_actions->{$service}->{$action}",
661 format => 'plain',
662 };
663 if ($service eq 'mon' && $action eq 'destroy') {
664 $params->{id} = $id;
665 } else {
666 $params->{ids} = [ $id ];
667 }
668
669 $result = $rados->mon_cmd($params, 1);
670 die $@ if $@;
671
672 $result->{safe} = $result->{return_code} == 0 ? 1 : 0;
673 $result->{status} = $result->{status_message};
674
675 return $result;
676 }});
677
79fa41a2 6781;