]>
Commit | Line | Data |
---|---|---|
7d4fc5ef DM |
1 | package PVE::API2::Ceph; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
0cd34b00 | 5 | |
7d4fc5ef | 6 | use File::Path; |
7d4fc5ef | 7 | use Net::IP; |
0cd34b00 | 8 | use UUID; |
7d4fc5ef | 9 | |
6fb08cb9 | 10 | use PVE::Ceph::Tools; |
27439be6 | 11 | use PVE::Ceph::Services; |
c31f487e | 12 | use PVE::Cluster qw(cfs_read_file cfs_write_file); |
0cd34b00 | 13 | use PVE::JSONSchema qw(get_standard_option); |
9f4ff798 | 14 | use PVE::Network; |
0cd34b00 TL |
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); | |
7d4fc5ef | 20 | |
79fa41a2 | 21 | use PVE::API2::Ceph::OSD; |
7e1a9d25 | 22 | use PVE::API2::Ceph::FS; |
b82649cc | 23 | use PVE::API2::Ceph::MDS; |
4fec2764 | 24 | use PVE::API2::Ceph::MGR; |
98fe93ae | 25 | use PVE::API2::Ceph::MON; |
0cd34b00 | 26 | use PVE::API2::Storage::Config; |
7d4fc5ef | 27 | |
0cd34b00 | 28 | use base qw(PVE::RESTHandler); |
7d4fc5ef DM |
29 | |
30 | my $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 | 196 | my $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 |
210 | my $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 | |
1070 | 1; |