]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Ceph.pm
cleanup: api/subscription: keep variable declarations closer to use
[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;
19use PVE::Tools qw(run_command file_get_contents file_set_contents);
7d4fc5ef 20
79fa41a2 21use PVE::API2::Ceph::OSD;
7e1a9d25 22use PVE::API2::Ceph::FS;
b82649cc 23use PVE::API2::Ceph::MDS;
4fec2764 24use PVE::API2::Ceph::MGR;
98fe93ae 25use PVE::API2::Ceph::MON;
0cd34b00 26use PVE::API2::Storage::Config;
7d4fc5ef 27
0cd34b00 28use base qw(PVE::RESTHandler);
7d4fc5ef
DM
29
30my $pve_osd_default_journal_size = 1024*5;
31
32__PACKAGE__->register_method ({
79fa41a2 33 subclass => "PVE::API2::Ceph::OSD",
7d4fc5ef
DM
34 path => 'osd',
35});
36
b82649cc
TL
37__PACKAGE__->register_method ({
38 subclass => "PVE::API2::Ceph::MDS",
39 path => 'mds',
40});
41
4fec2764
DC
42__PACKAGE__->register_method ({
43 subclass => "PVE::API2::Ceph::MGR",
44 path => 'mgr',
45});
46
98fe93ae
DC
47__PACKAGE__->register_method ({
48 subclass => "PVE::API2::Ceph::MON",
49 path => 'mon',
50});
51
7e1a9d25
TL
52__PACKAGE__->register_method ({
53 subclass => "PVE::API2::Ceph::FS",
54 path => 'fs',
55});
56
7d4fc5ef
DM
57__PACKAGE__->register_method ({
58 name => 'index',
59 path => '',
60 method => 'GET',
61 description => "Directory index.",
62 permissions => { user => 'all' },
90c75580
TL
63 permissions => {
64 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
65 },
7d4fc5ef 66 parameters => {
be753927 67 additionalProperties => 0,
7d4fc5ef
DM
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' },
7e1a9d25 88 { name => 'fs' },
b82649cc 89 { name => 'mds' },
7d4fc5ef
DM
90 { name => 'stop' },
91 { name => 'start' },
92 { name => 'status' },
93 { name => 'crush' },
94 { name => 'config' },
95 { name => 'log' },
96 { name => 'disks' },
a46ad02a 97 { name => 'flags' },
d2692b86 98 { name => 'rules' },
7d4fc5ef
DM
99 ];
100
101 return $result;
102 }});
103
104__PACKAGE__->register_method ({
105 name => 'disks',
106 path => 'disks',
107 method => 'GET',
108 description => "List local disks.",
109 proxyto => 'node',
110 protected => 1,
90c75580
TL
111 permissions => {
112 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
113 },
7d4fc5ef 114 parameters => {
be753927 115 additionalProperties => 0,
7d4fc5ef
DM
116 properties => {
117 node => get_standard_option('pve-node'),
118 type => {
119 description => "Only list specific types of disks.",
120 type => 'string',
121 enum => ['unused', 'journal_disks'],
122 optional => 1,
123 },
124 },
125 },
126 returns => {
127 type => 'array',
128 items => {
129 type => "object",
130 properties => {
131 dev => { type => 'string' },
132 used => { type => 'string', optional => 1 },
133 gpt => { type => 'boolean' },
134 size => { type => 'integer' },
135 osdid => { type => 'integer' },
136 vendor => { type => 'string', optional => 1 },
137 model => { type => 'string', optional => 1 },
138 serial => { type => 'string', optional => 1 },
139 },
140 },
141 # links => [ { rel => 'child', href => "{}" } ],
142 },
143 code => sub {
144 my ($param) = @_;
145
6fb08cb9 146 PVE::Ceph::Tools::check_ceph_inited();
7d4fc5ef 147
5fd5c30d 148 my $disks = PVE::Diskmanage::get_disks(undef, 1);
7d4fc5ef
DM
149
150 my $res = [];
151 foreach my $dev (keys %$disks) {
152 my $d = $disks->{$dev};
153 if ($param->{type}) {
154 if ($param->{type} eq 'journal_disks') {
155 next if $d->{osdid} >= 0;
156 next if !$d->{gpt};
157 } elsif ($param->{type} eq 'unused') {
158 next if $d->{used};
159 } else {
160 die "internal error"; # should not happen
161 }
162 }
163
164 $d->{dev} = "/dev/$dev";
be753927 165 push @$res, $d;
7d4fc5ef
DM
166 }
167
168 return $res;
169 }});
170
171__PACKAGE__->register_method ({
172 name => 'config',
173 path => 'config',
174 method => 'GET',
90c75580
TL
175 permissions => {
176 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
177 },
7d4fc5ef
DM
178 description => "Get Ceph configuration.",
179 parameters => {
be753927 180 additionalProperties => 0,
7d4fc5ef
DM
181 properties => {
182 node => get_standard_option('pve-node'),
183 },
184 },
185 returns => { type => 'string' },
186 code => sub {
187 my ($param) = @_;
188
6fb08cb9 189 PVE::Ceph::Tools::check_ceph_inited();
7d4fc5ef 190
6fb08cb9 191 my $path = PVE::Ceph::Tools::get_config('pve_ceph_cfgpath');
400742e4 192 return file_get_contents($path);
7d4fc5ef
DM
193
194 }});
195
ae672a64 196my $add_storage = sub {
6281777b 197 my ($pool, $storeid) = @_;
ae672a64
FG
198
199 my $storage_params = {
200 type => 'rbd',
201 pool => $pool,
202 storage => $storeid,
6281777b
AA
203 krbd => 0,
204 content => 'rootdir,images',
ae672a64
FG
205 };
206
207 PVE::API2::Storage::Config->create($storage_params);
208};
209
f4aae93b
FG
210my $get_storages = sub {
211 my ($pool) = @_;
212
213 my $cfg = PVE::Storage::config();
214
215 my $storages = $cfg->{ids};
216 my $res = {};
217 foreach my $storeid (keys %$storages) {
218 my $curr = $storages->{$storeid};
219 $res->{$storeid} = $storages->{$storeid}
220 if $curr->{type} eq 'rbd' && $pool eq $curr->{pool};
221 }
222
223 return $res;
224};
225
7d4fc5ef
DM
226__PACKAGE__->register_method ({
227 name => 'init',
228 path => 'init',
229 method => 'POST',
230 description => "Create initial ceph default configuration and setup symlinks.",
231 proxyto => 'node',
232 protected => 1,
90c75580
TL
233 permissions => {
234 check => ['perm', '/', [ 'Sys.Modify' ]],
235 },
7d4fc5ef 236 parameters => {
be753927 237 additionalProperties => 0,
7d4fc5ef
DM
238 properties => {
239 node => get_standard_option('pve-node'),
240 network => {
be753927 241 description => "Use specific network for all ceph related traffic",
7d4fc5ef
DM
242 type => 'string', format => 'CIDR',
243 optional => 1,
244 maxLength => 128,
245 },
7519b848
TL
246 'cluster-network' => {
247 description => "Declare a separate cluster network, OSDs will route" .
248 "heartbeat, object replication and recovery traffic over it",
249 type => 'string', format => 'CIDR',
250 requires => 'network',
251 optional => 1,
252 maxLength => 128,
253 },
7d4fc5ef 254 size => {
207f4932
FG
255 description => 'Targeted number of replicas per object',
256 type => 'integer',
257 default => 3,
258 optional => 1,
259 minimum => 1,
260 maximum => 7,
261 },
262 min_size => {
263 description => 'Minimum number of available replicas per object to allow I/O',
7d4fc5ef
DM
264 type => 'integer',
265 default => 2,
266 optional => 1,
267 minimum => 1,
83663637 268 maximum => 7,
7d4fc5ef
DM
269 },
270 pg_bits => {
3cba09d5
FG
271 description => "Placement group bits, used to specify the " .
272 "default number of placement groups.\n\nNOTE: 'osd pool " .
273 "default pg num' does not work for default pools.",
7d4fc5ef
DM
274 type => 'integer',
275 default => 6,
276 optional => 1,
277 minimum => 6,
278 maximum => 14,
279 },
4280f25c 280 disable_cephx => {
97f050bb
FG
281 description => "Disable cephx authentification.\n\n" .
282 "WARNING: cephx is a security feature protecting against " .
283 "man-in-the-middle attacks. Only consider disabling cephx ".
284 "if your network is private!",
77bb90b0
AD
285 type => 'boolean',
286 optional => 1,
287 default => 0,
4280f25c 288 },
7d4fc5ef
DM
289 },
290 },
291 returns => { type => 'null' },
292 code => sub {
293 my ($param) = @_;
294
6fb08cb9 295 my $version = PVE::Ceph::Tools::get_local_version(1);
c64c04dd
AA
296
297 if (!$version || $version < 12) {
e16cd81f 298 die "Ceph Luminous required - please run 'pveceph install'\n";
c64c04dd 299 } else {
6fb08cb9 300 PVE::Ceph::Tools::check_ceph_installed('ceph_bin');
c64c04dd 301 }
7d4fc5ef
DM
302
303 # simply load old config if it already exists
c31f487e 304 my $cfg = cfs_read_file('ceph.conf');
7d4fc5ef
DM
305
306 if (!$cfg->{global}) {
307
308 my $fsid;
309 my $uuid;
310
311 UUID::generate($uuid);
312 UUID::unparse($uuid, $fsid);
313
4280f25c 314 my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
77bb90b0 315
7d4fc5ef
DM
316 $cfg->{global} = {
317 'fsid' => $fsid,
77bb90b0
AD
318 'auth cluster required' => $auth,
319 'auth service required' => $auth,
320 'auth client required' => $auth,
7d4fc5ef 321 'osd journal size' => $pve_osd_default_journal_size,
207f4932
FG
322 'osd pool default size' => $param->{size} // 3,
323 'osd pool default min size' => $param->{min_size} // 2,
2e9d791e 324 'mon allow pool delete' => 'true',
7d4fc5ef
DM
325 };
326
be753927 327 # this does not work for default pools
7d4fc5ef 328 #'osd pool default pg num' => $pg_num,
be753927 329 #'osd pool default pgp num' => $pg_num,
7d4fc5ef 330 }
be753927 331
7d4fc5ef
DM
332 $cfg->{global}->{keyring} = '/etc/pve/priv/$cluster.$name.keyring';
333 $cfg->{osd}->{keyring} = '/var/lib/ceph/osd/ceph-$id/keyring';
334
7d4fc5ef
DM
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
7519b848
TL
345 if ($param->{'cluster-network'}) {
346 $cfg->{global}->{'cluster network'} = $param->{'cluster-network'};
347 }
348
c31f487e 349 cfs_write_file('ceph.conf', $cfg);
7d4fc5ef 350
6fb08cb9 351 PVE::Ceph::Tools::setup_pve_symlinks();
7d4fc5ef
DM
352
353 return undef;
354 }});
355
38db610a
DM
356__PACKAGE__->register_method ({
357 name => 'stop',
358 path => 'stop',
359 method => 'POST',
360 description => "Stop ceph services.",
361 proxyto => 'node',
362 protected => 1,
90c75580
TL
363 permissions => {
364 check => ['perm', '/', [ 'Sys.Modify' ]],
365 },
38db610a 366 parameters => {
be753927 367 additionalProperties => 0,
38db610a
DM
368 properties => {
369 node => get_standard_option('pve-node'),
68e0c4bd
DM
370 service => {
371 description => 'Ceph service name.',
372 type => 'string',
373 optional => 1,
33a9c70a 374 default => 'ceph.target',
b0e5ae21 375 pattern => '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
68e0c4bd 376 },
38db610a
DM
377 },
378 },
68e0c4bd 379 returns => { type => 'string' },
38db610a
DM
380 code => sub {
381 my ($param) = @_;
382
68e0c4bd
DM
383 my $rpcenv = PVE::RPCEnvironment::get();
384
385 my $authuser = $rpcenv->get_user();
386
6fb08cb9 387 PVE::Ceph::Tools::check_ceph_inited();
38db610a 388
c31f487e 389 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
390 scalar(keys %$cfg) || die "no configuration\n";
391
68e0c4bd
DM
392 my $worker = sub {
393 my $upid = shift;
38db610a 394
68e0c4bd
DM
395 my $cmd = ['stop'];
396 if ($param->{service}) {
397 push @$cmd, $param->{service};
398 }
399
27439be6 400 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
401 };
402
403 return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
404 $authuser, $worker);
38db610a
DM
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,
90c75580
TL
414 permissions => {
415 check => ['perm', '/', [ 'Sys.Modify' ]],
416 },
38db610a 417 parameters => {
be753927 418 additionalProperties => 0,
38db610a
DM
419 properties => {
420 node => get_standard_option('pve-node'),
68e0c4bd
DM
421 service => {
422 description => 'Ceph service name.',
423 type => 'string',
424 optional => 1,
33a9c70a 425 default => 'ceph.target',
b0e5ae21 426 pattern => '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
68e0c4bd 427 },
38db610a
DM
428 },
429 },
68e0c4bd 430 returns => { type => 'string' },
38db610a
DM
431 code => sub {
432 my ($param) = @_;
433
68e0c4bd
DM
434 my $rpcenv = PVE::RPCEnvironment::get();
435
436 my $authuser = $rpcenv->get_user();
437
6fb08cb9 438 PVE::Ceph::Tools::check_ceph_inited();
38db610a 439
c31f487e 440 my $cfg = cfs_read_file('ceph.conf');
38db610a
DM
441 scalar(keys %$cfg) || die "no configuration\n";
442
68e0c4bd
DM
443 my $worker = sub {
444 my $upid = shift;
38db610a 445
68e0c4bd
DM
446 my $cmd = ['start'];
447 if ($param->{service}) {
448 push @$cmd, $param->{service};
449 }
450
27439be6 451 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
68e0c4bd
DM
452 };
453
454 return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
455 $authuser, $worker);
38db610a
DM
456 }});
457
342c0830
DC
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,
33a9c70a 476 default => 'ceph.target',
342c0830
DC
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
6fb08cb9 489 PVE::Ceph::Tools::check_ceph_inited();
342c0830 490
c31f487e 491 my $cfg = cfs_read_file('ceph.conf');
342c0830
DC
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
27439be6 502 PVE::Ceph::Services::ceph_service_cmd(@$cmd);
342c0830
DC
503 };
504
505 return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
506 $authuser, $worker);
507 }});
508
38db610a
DM
509__PACKAGE__->register_method ({
510 name => 'status',
511 path => 'status',
512 method => 'GET',
513 description => "Get ceph status.",
514 proxyto => 'node',
515 protected => 1,
90c75580
TL
516 permissions => {
517 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
518 },
38db610a 519 parameters => {
be753927 520 additionalProperties => 0,
38db610a
DM
521 properties => {
522 node => get_standard_option('pve-node'),
523 },
524 },
525 returns => { type => 'object' },
526 code => sub {
527 my ($param) = @_;
528
6fb08cb9 529 PVE::Ceph::Tools::check_ceph_enabled();
38db610a 530
36fd0190 531 my $rados = PVE::RADOS->new();
84caf265
DC
532 my $status = $rados->mon_command({ prefix => 'status' });
533 $status->{health} = $rados->mon_command({ prefix => 'health', detail => 'detail' });
534 return $status;
38db610a
DM
535 }});
536
b0537f7b
DM
537__PACKAGE__->register_method ({
538 name => 'lspools',
539 path => 'pools',
540 method => 'GET',
541 description => "List all pools.",
542 proxyto => 'node',
543 protected => 1,
90c75580
TL
544 permissions => {
545 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
546 },
b0537f7b 547 parameters => {
be753927 548 additionalProperties => 0,
b0537f7b
DM
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
6fb08cb9 568 PVE::Ceph::Tools::check_ceph_inited();
b0537f7b 569
36fd0190 570 my $rados = PVE::RADOS->new();
d54f1126
DM
571
572 my $stats = {};
573 my $res = $rados->mon_command({ prefix => 'df' });
6f9ea1c1 574
d54f1126
DM
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' });
c9508b5d
DC
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 }
b0537f7b
DM
588
589 my $data = [];
590 foreach my $e (@{$res->{pools}}) {
591 my $d = {};
2db28c03 592 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
b0537f7b
DM
593 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
594 }
c9508b5d
DC
595
596 if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) {
597 $d->{crush_rule_name} = $rules->{$d->{crush_rule}};
598 }
599
d54f1126
DM
600 if (my $s = $stats->{$d->{pool}}) {
601 $d->{bytes_used} = $s->{bytes_used};
dae96e48 602 $d->{percent_used} = $s->{percent_used};
d54f1126 603 }
b0537f7b
DM
604 push @$data, $d;
605 }
606
d54f1126 607
b0537f7b
DM
608 return $data;
609 }});
610
611__PACKAGE__->register_method ({
612 name => 'createpool',
7d4fc5ef 613 path => 'pools',
38db610a 614 method => 'POST',
7d4fc5ef 615 description => "Create POOL",
38db610a
DM
616 proxyto => 'node',
617 protected => 1,
90c75580
TL
618 permissions => {
619 check => ['perm', '/', [ 'Sys.Modify' ]],
620 },
38db610a 621 parameters => {
be753927 622 additionalProperties => 0,
38db610a
DM
623 properties => {
624 node => get_standard_option('pve-node'),
7d4fc5ef
DM
625 name => {
626 description => "The name of the pool. It must be unique.",
38db610a 627 type => 'string',
43d85563 628 },
7d4fc5ef
DM
629 size => {
630 description => 'Number of replicas per object',
631 type => 'integer',
6747b0a9 632 default => 3,
0e5816e4 633 optional => 1,
7d4fc5ef 634 minimum => 1,
83663637 635 maximum => 7,
0e5816e4 636 },
7d4fc5ef
DM
637 min_size => {
638 description => 'Minimum number of replicas per object',
639 type => 'integer',
6747b0a9 640 default => 2,
7d4fc5ef
DM
641 optional => 1,
642 minimum => 1,
83663637 643 maximum => 7,
7d4fc5ef
DM
644 },
645 pg_num => {
646 description => "Number of placement groups.",
647 type => 'integer',
ba454377 648 default => 128,
7d4fc5ef
DM
649 optional => 1,
650 minimum => 8,
651 maximum => 32768,
652 },
2db28c03
DC
653 crush_rule => {
654 description => "The rule to use for mapping object placement in the cluster.",
655 type => 'string',
43d85563
DM
656 optional => 1,
657 },
6c11e921
DC
658 application => {
659 description => "The application of the pool, 'rbd' by default.",
660 type => 'string',
661 enum => ['rbd', 'cephfs', 'rgw'],
662 optional => 1,
60811ad7
FG
663 },
664 add_storages => {
6281777b 665 description => "Configure VM and CT storage using the new pool.",
60811ad7
FG
666 type => 'boolean',
667 optional => 1,
668 },
38db610a
DM
669 },
670 },
8c426204 671 returns => { type => 'string' },
38db610a
DM
672 code => sub {
673 my ($param) = @_;
674
68f4def4 675 PVE::Cluster::check_cfs_quorum();
6fb08cb9 676 PVE::Ceph::Tools::check_ceph_inited();
38db610a 677
6fb08cb9 678 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
13f4d762 679
be753927 680 die "not fully configured - missing '$pve_ckeyring_path'\n"
7d4fc5ef 681 if ! -f $pve_ckeyring_path;
13f4d762 682
0c0d43a7 683 my $pool = $param->{name};
8c426204
FG
684 my $rpcenv = PVE::RPCEnvironment::get();
685 my $user = $rpcenv->get_user();
0c0d43a7 686
60811ad7 687 if ($param->{add_storages}) {
60811ad7
FG
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
ba454377 693 my $pg_num = $param->{pg_num} || 128;
6747b0a9
DC
694 my $size = $param->{size} || 3;
695 my $min_size = $param->{min_size} || 2;
6c11e921 696 my $application = $param->{application} // 'rbd';
38db610a 697
8c426204 698 my $worker = sub {
a34866f0 699
6fb08cb9 700 PVE::Ceph::Tools::create_pool($pool, $param);
8c426204
FG
701
702 if ($param->{add_storages}) {
703 my $err;
6281777b 704 eval { $add_storage->($pool, "${pool}"); };
8c426204 705 if ($@) {
6281777b 706 warn "failed to add storage: $@";
8c426204
FG
707 $err = 1;
708 }
6281777b 709 die "adding storage for pool '$pool' failed, check log and add manually!\n"
8c426204 710 if $err;
60811ad7 711 }
8c426204 712 };
60811ad7 713
8c426204 714 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
38db610a
DM
715 }});
716
a46ad02a
DC
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
6fb08cb9 737 PVE::Ceph::Tools::check_ceph_inited();
a46ad02a 738
6fb08cb9 739 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
a46ad02a
DC
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
6fb08cb9 776 PVE::Ceph::Tools::check_ceph_inited();
a46ad02a 777
6fb08cb9 778 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
a46ad02a
DC
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
6fb08cb9 819 PVE::Ceph::Tools::check_ceph_inited();
a46ad02a 820
6fb08cb9 821 my $pve_ckeyring_path = PVE::Ceph::Tools::get_config('pve_ckeyring_path');
a46ad02a
DC
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
38db610a 837__PACKAGE__->register_method ({
7d4fc5ef
DM
838 name => 'destroypool',
839 path => 'pools/{name}',
39e1ad70 840 method => 'DELETE',
7d4fc5ef 841 description => "Destroy pool",
38db610a
DM
842 proxyto => 'node',
843 protected => 1,
90c75580
TL
844 permissions => {
845 check => ['perm', '/', [ 'Sys.Modify' ]],
846 },
38db610a 847 parameters => {
be753927 848 additionalProperties => 0,
38db610a
DM
849 properties => {
850 node => get_standard_option('pve-node'),
7d4fc5ef
DM
851 name => {
852 description => "The name of the pool. It must be unique.",
853 type => 'string',
0e5816e4 854 },
76dc2ad0
DC
855 force => {
856 description => "If true, destroys pool even if in use",
857 type => 'boolean',
858 optional => 1,
859 default => 0,
f6144f34
FG
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 },
38db610a
DM
867 },
868 },
8c426204 869 returns => { type => 'string' },
38db610a
DM
870 code => sub {
871 my ($param) = @_;
872
6fb08cb9 873 PVE::Ceph::Tools::check_ceph_inited();
38db610a 874
f6144f34
FG
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
0c0d43a7 880 my $pool = $param->{name};
76dc2ad0 881
8c426204 882 my $worker = sub {
0a521b66
FG
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
6fb08cb9 900 PVE::Ceph::Tools::destroy_pool($pool);
8c426204
FG
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 }
f6144f34 912 }
8c426204
FG
913 die "failed to remove (some) storages - check log and remove manually!\n"
914 if $err;
f6144f34 915 }
8c426204
FG
916 };
917 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
38db610a 918 }});
2f692121 919
a34866f0 920
2f692121
DM
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,
90c75580
TL
928 permissions => {
929 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
930 },
2f692121 931 parameters => {
be753927 932 additionalProperties => 0,
2f692121
DM
933 properties => {
934 node => get_standard_option('pve-node'),
935 },
936 },
937 returns => { type => 'string' },
938 code => sub {
939 my ($param) = @_;
940
6fb08cb9 941 PVE::Ceph::Tools::check_ceph_inited();
2f692121 942
8b336060
DM
943 # this produces JSON (difficult to read for the user)
944 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
2f692121 945
8b336060
DM
946 my $txt = '';
947
948 my $mapfile = "/var/tmp/ceph-crush.map.$$";
949 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
950
36fd0190 951 my $rados = PVE::RADOS->new();
be753927 952
8b336060 953 eval {
970236b3 954 my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
400742e4 955 file_set_contents($mapfile, $bindata);
8b336060 956 run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
400742e4 957 $txt = file_get_contents($mapdata);
8b336060
DM
958 };
959 my $err = $@;
960
961 unlink $mapfile;
962 unlink $mapdata;
963
964 die $err if $err;
be753927 965
2f692121
DM
966 return $txt;
967 }});
968
570278fa 969__PACKAGE__->register_method({
be753927
DC
970 name => 'log',
971 path => 'log',
570278fa
DM
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 => {
be753927 980 additionalProperties => 0,
570278fa
DM
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',
be753927 997 items => {
570278fa
DM
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) = @_;
c56d75b4 1013
6fb08cb9 1014 PVE::Ceph::Tools::check_ceph_inited();
570278fa
DM
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);
be753927
DC
1024
1025 return $lines;
570278fa
DM
1026 }});
1027
d2692b86
DC
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
6fb08cb9 1055 PVE::Ceph::Tools::check_ceph_inited();
2f692121 1056
d2692b86
DC
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 }});
79fa41a2
DC
1069
10701;