]>
Commit | Line | Data |
---|---|---|
6fb08cb9 | 1 | package PVE::Ceph::Tools; |
a34866f0 DM |
2 | |
3 | use strict; | |
4 | use warnings; | |
f8346b52 | 5 | |
a34866f0 | 6 | use File::Path; |
b436dca8 | 7 | use File::Basename; |
f8346b52 | 8 | use IO::File; |
48e8a06d | 9 | use JSON; |
a34866f0 | 10 | |
3bd128d7 | 11 | use PVE::Tools qw(run_command dir_glob_foreach extract_param); |
48e8a06d | 12 | use PVE::Cluster qw(cfs_read_file); |
f96d7012 | 13 | use PVE::RADOS; |
91dfa228 AA |
14 | use PVE::Ceph::Services; |
15 | use PVE::CephConfig; | |
a34866f0 DM |
16 | |
17 | my $ccname = 'ceph'; # ceph cluster name | |
18 | my $ceph_cfgdir = "/etc/ceph"; | |
19 | my $pve_ceph_cfgpath = "/etc/pve/$ccname.conf"; | |
20 | my $ceph_cfgpath = "$ceph_cfgdir/$ccname.conf"; | |
a51a28e3 | 21 | my $pve_ceph_cfgdir = "/etc/pve/ceph"; |
a34866f0 DM |
22 | |
23 | my $pve_mon_key_path = "/etc/pve/priv/$ccname.mon.keyring"; | |
24 | my $pve_ckeyring_path = "/etc/pve/priv/$ccname.client.admin.keyring"; | |
17709566 | 25 | my $ckeyring_path = "/etc/ceph/ceph.client.admin.keyring"; |
a34866f0 DM |
26 | my $ceph_bootstrap_osd_keyring = "/var/lib/ceph/bootstrap-osd/$ccname.keyring"; |
27 | my $ceph_bootstrap_mds_keyring = "/var/lib/ceph/bootstrap-mds/$ccname.keyring"; | |
b82649cc | 28 | my $ceph_mds_data_dir = '/var/lib/ceph/mds'; |
a34866f0 | 29 | |
c64c04dd AA |
30 | my $ceph_service = { |
31 | ceph_bin => "/usr/bin/ceph", | |
32 | ceph_mon => "/usr/bin/ceph-mon", | |
33 | ceph_mgr => "/usr/bin/ceph-mgr", | |
b82649cc TL |
34 | ceph_osd => "/usr/bin/ceph-osd", |
35 | ceph_mds => "/usr/bin/ceph-mds", | |
48e8a06d | 36 | ceph_volume => '/usr/sbin/ceph-volume', |
c64c04dd | 37 | }; |
a34866f0 | 38 | |
68b6c239 | 39 | my $config_values = { |
a34866f0 | 40 | ccname => $ccname, |
a51a28e3 | 41 | pve_ceph_cfgdir => $pve_ceph_cfgdir, |
68b6c239 MC |
42 | ceph_mds_data_dir => $ceph_mds_data_dir, |
43 | long_rados_timeout => 60, | |
44 | }; | |
45 | ||
46 | my $config_files = { | |
a34866f0 DM |
47 | pve_ceph_cfgpath => $pve_ceph_cfgpath, |
48 | pve_mon_key_path => $pve_mon_key_path, | |
49 | pve_ckeyring_path => $pve_ckeyring_path, | |
50 | ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring, | |
51 | ceph_bootstrap_mds_keyring => $ceph_bootstrap_mds_keyring, | |
91dfa228 | 52 | ceph_cfgpath => $ceph_cfgpath, |
a34866f0 DM |
53 | }; |
54 | ||
c64c04dd AA |
55 | sub get_local_version { |
56 | my ($noerr) = @_; | |
57 | ||
6fb08cb9 | 58 | if (check_ceph_installed('ceph_bin', $noerr)) { |
c64c04dd | 59 | my $ceph_version; |
3248590d TL |
60 | run_command( |
61 | [ $ceph_service->{ceph_bin}, '--version' ], | |
62 | noerr => $noerr, | |
63 | outfunc => sub { $ceph_version = shift if !defined $ceph_version }, | |
64 | ); | |
65 | return undef if !defined $ceph_version; | |
66 | ||
2a080be1 | 67 | if ($ceph_version =~ /^ceph.*\sv?(\d+(?:\.\d+)+(?:-pve\d+)?)\s+(?:\(([a-zA-Z0-9]+)\))?/) { |
3248590d TL |
68 | my ($version, $buildcommit) = ($1, $2); |
69 | my $subversions = [ split(/\.|-/, $version) ]; | |
70 | ||
71 | # return (version, buildid, major, minor, ...) : major; | |
72 | return wantarray | |
73 | ? ($version, $buildcommit, $subversions) | |
74 | : $subversions->[0]; | |
c64c04dd AA |
75 | } |
76 | } | |
77 | ||
78 | return undef; | |
79 | } | |
80 | ||
8ba0d0a0 FG |
81 | sub get_cluster_versions { |
82 | my ($service, $noerr) = @_; | |
83 | ||
84 | my $rados = PVE::RADOS->new(); | |
85 | my $cmd = $service ? "$service versions" : 'versions'; | |
86 | return $rados->mon_command({ prefix => $cmd }); | |
87 | } | |
88 | ||
a34866f0 DM |
89 | sub get_config { |
90 | my $key = shift; | |
91 | ||
04ed3d1b | 92 | my $value = $config_values->{$key} // $config_files->{$key}; |
a34866f0 | 93 | |
68b6c239 | 94 | die "no such ceph config '$key'" if ! defined($value); |
a34866f0 DM |
95 | |
96 | return $value; | |
97 | } | |
98 | ||
a34866f0 | 99 | sub purge_all_ceph_files { |
91dfa228 AA |
100 | my ($services) = @_; |
101 | my $is_local_mon; | |
102 | my $monlist = [ split(',', PVE::CephConfig::get_monaddr_list($pve_ceph_cfgpath)) ]; | |
103 | ||
104 | foreach my $service (keys %$services) { | |
105 | my $type = $services->{$service}; | |
106 | next if (!%$type); | |
107 | ||
108 | foreach my $name (keys %$type) { | |
109 | my $dir_exists = $type->{$name}->{direxists}; | |
110 | ||
111 | $is_local_mon = grep($type->{$name}->{addr}, @$monlist) | |
112 | if $service eq 'mon'; | |
113 | ||
114 | my $path = "/var/lib/ceph/$service"; | |
115 | $path = '/var/log/ceph' if $service eq 'logs'; | |
116 | if ($dir_exists) { | |
117 | my $err; | |
118 | File::Path::remove_tree($path, { | |
119 | keep_root => 1, | |
120 | error => \$err, | |
121 | }); | |
122 | warn "Error removing path, '$path'\n" if @$err; | |
123 | } | |
124 | } | |
125 | } | |
a34866f0 | 126 | |
91dfa228 AA |
127 | if (scalar @$monlist > 0 && !$is_local_mon) { |
128 | warn "Foreign MON address in ceph.conf. Keeping config & keyrings\n" | |
129 | } else { | |
130 | print "Removing config & keyring files\n"; | |
68b6c239 | 131 | for my $file (%$config_files) { |
91dfa228 AA |
132 | unlink $file if (-e $file); |
133 | } | |
134 | } | |
135 | } | |
a34866f0 | 136 | |
91dfa228 AA |
137 | sub purge_all_ceph_services { |
138 | my ($services) = @_; | |
139 | ||
140 | foreach my $service (keys %$services) { | |
141 | my $type = $services->{$service}; | |
142 | next if (!%$type); | |
143 | ||
144 | foreach my $name (keys %$type) { | |
145 | my $service_exists = $type->{$name}->{service}; | |
146 | ||
147 | if ($service_exists) { | |
75cac279 TL |
148 | eval { PVE::Ceph::Services::ceph_service_cmd('disable', "$service.$name") }; |
149 | warn "Could not disable ceph-$service\@$name, error: $@\n" if $@; | |
150 | ||
151 | eval { PVE::Ceph::Services::ceph_service_cmd('stop', "$service.$name") }; | |
152 | warn "Could not stop ceph-$service\@$name, error: $@\n" if $@; | |
91dfa228 AA |
153 | } |
154 | } | |
155 | } | |
a34866f0 DM |
156 | } |
157 | ||
9f6dc075 TL |
158 | sub ceph_install_flag_file { return '/run/pve-ceph-install-flag' }; |
159 | ||
a34866f0 | 160 | sub check_ceph_installed { |
c64c04dd AA |
161 | my ($service, $noerr) = @_; |
162 | ||
163 | $service = 'ceph_bin' if !defined($service); | |
a34866f0 | 164 | |
d380d000 | 165 | # NOTE: the flag file is checked as on a new installation, the binary gets |
4dd27d50 | 166 | # extracted by dpkg before the installation is finished |
9f6dc075 | 167 | if (! -x $ceph_service->{$service} || -f ceph_install_flag_file()) { |
c64c04dd | 168 | die "binary not installed: $ceph_service->{$service}\n" if !$noerr; |
a34866f0 DM |
169 | return undef; |
170 | } | |
171 | ||
172 | return 1; | |
173 | } | |
174 | ||
7ef69f33 TL |
175 | |
176 | sub check_ceph_configured { | |
177 | ||
178 | check_ceph_inited(); | |
179 | ||
180 | die "ceph not fully configured - missing '$pve_ckeyring_path'\n" | |
181 | if ! -f $pve_ckeyring_path; | |
182 | ||
183 | return 1; | |
184 | } | |
185 | ||
a34866f0 DM |
186 | sub check_ceph_inited { |
187 | my ($noerr) = @_; | |
188 | ||
315304f3 | 189 | return undef if !check_ceph_installed('ceph_mon', $noerr); |
6f8bf83d | 190 | |
a51a28e3 MC |
191 | my @errors; |
192 | ||
193 | push(@errors, "missing '$pve_ceph_cfgpath'") if ! -f $pve_ceph_cfgpath; | |
194 | push(@errors, "missing '$pve_ceph_cfgdir'") if ! -d $pve_ceph_cfgdir; | |
195 | ||
196 | if (@errors) { | |
197 | my $err = 'pveceph configuration not initialized - ' . join(', ', @errors) . "\n"; | |
198 | die $err if !$noerr; | |
a34866f0 DM |
199 | return undef; |
200 | } | |
201 | ||
202 | return 1; | |
203 | } | |
204 | ||
205 | sub check_ceph_enabled { | |
206 | my ($noerr) = @_; | |
207 | ||
208 | return undef if !check_ceph_inited($noerr); | |
209 | ||
210 | if (! -f $ceph_cfgpath) { | |
211 | die "pveceph configuration not enabled\n" if !$noerr; | |
212 | return undef; | |
213 | } | |
214 | ||
215 | return 1; | |
216 | } | |
217 | ||
11d74274 | 218 | my $set_pool_setting = sub { |
5f4efb88 | 219 | my ($pool, $setting, $value, $rados) = @_; |
11d74274 AA |
220 | |
221 | my $command; | |
222 | if ($setting eq 'application') { | |
223 | $command = { | |
224 | prefix => "osd pool application enable", | |
225 | pool => "$pool", | |
226 | app => "$value", | |
227 | }; | |
228 | } else { | |
229 | $command = { | |
230 | prefix => "osd pool set", | |
231 | pool => "$pool", | |
232 | var => "$setting", | |
233 | val => "$value", | |
234 | format => 'plain', | |
235 | }; | |
236 | } | |
237 | ||
5f4efb88 | 238 | $rados = PVE::RADOS->new() if !$rados; |
11d74274 AA |
239 | eval { $rados->mon_command($command); }; |
240 | return $@ ? $@ : undef; | |
241 | }; | |
242 | ||
50adb131 AA |
243 | sub set_pool { |
244 | my ($pool, $param) = @_; | |
245 | ||
5f4efb88 AL |
246 | my $rados = PVE::RADOS->new(); |
247 | ||
248 | if (get_pool_type($pool, $rados) eq 'erasure') { | |
249 | #remove parameters that cannot be changed for erasure coded pools | |
250 | my $ignore_params = ['size', 'crush_rule']; | |
251 | for my $setting (@$ignore_params) { | |
252 | if ($param->{$setting}) { | |
253 | print "cannot set '${setting}' for erasure coded pool\n"; | |
254 | delete $param->{$setting}; | |
255 | } | |
256 | } | |
257 | } | |
69c0ff3e | 258 | # by default, pool size always resets min_size, so set it as first item |
11d74274 | 259 | # https://tracker.ceph.com/issues/44862 |
69c0ff3e TL |
260 | my $keys = [ grep { $_ ne 'size' } sort keys %$param ]; |
261 | unshift @$keys, 'size' if exists $param->{size}; | |
11d74274 | 262 | |
69c0ff3e | 263 | for my $setting (@$keys) { |
11d74274 | 264 | my $value = $param->{$setting}; |
50adb131 | 265 | |
cb83113d | 266 | print "pool $pool: applying $setting = $value\n"; |
5f4efb88 | 267 | if (my $err = $set_pool_setting->($pool, $setting, $value, $rados)) { |
11d74274 | 268 | print "$err"; |
50adb131 AA |
269 | } else { |
270 | delete $param->{$setting}; | |
271 | } | |
272 | } | |
273 | ||
68f94af8 TL |
274 | if (scalar(keys %$param) > 0) { |
275 | my $missing = join(', ', sort keys %$param ); | |
276 | die "Could not set: $missing\n"; | |
50adb131 AA |
277 | } |
278 | ||
279 | } | |
280 | ||
34a2222d | 281 | sub get_pool_properties { |
23c407e5 TL |
282 | my ($pool, $rados) = @_; |
283 | $rados = PVE::RADOS->new() if !defined($rados); | |
34a2222d AL |
284 | my $command = { |
285 | prefix => "osd pool get", | |
286 | pool => "$pool", | |
287 | var => "all", | |
288 | format => 'json', | |
289 | }; | |
34a2222d AL |
290 | return $rados->mon_command($command); |
291 | } | |
292 | ||
5f4efb88 AL |
293 | sub get_pool_type { |
294 | my ($pool, $rados) = @_; | |
295 | $rados = PVE::RADOS->new() if !defined($rados); | |
296 | return 'erasure' if get_pool_properties($pool, $rados)->{erasure_code_profile}; | |
297 | return 'replicated'; | |
298 | } | |
299 | ||
f96d7012 TL |
300 | sub create_pool { |
301 | my ($pool, $param, $rados) = @_; | |
24f3f2bc | 302 | $rados = PVE::RADOS->new() if !defined($rados); |
f96d7012 | 303 | |
6ad70a2b | 304 | my $pg_num = $param->{pg_num} || 128; |
f96d7012 | 305 | |
3bd128d7 | 306 | my $mon_params = { |
f96d7012 TL |
307 | prefix => "osd pool create", |
308 | pool => $pool, | |
309 | pg_num => int($pg_num), | |
310 | format => 'plain', | |
3bd128d7 AL |
311 | }; |
312 | $mon_params->{pool_type} = extract_param($param, 'pool_type') if $param->{pool_type}; | |
313 | $mon_params->{erasure_code_profile} = extract_param($param, 'erasure_code_profile') | |
314 | if $param->{erasure_code_profile}; | |
315 | ||
316 | $rados->mon_command($mon_params); | |
f96d7012 | 317 | |
50adb131 | 318 | set_pool($pool, $param); |
f96d7012 TL |
319 | |
320 | } | |
321 | ||
7e1a9d25 TL |
322 | sub ls_pools { |
323 | my ($pool, $rados) = @_; | |
24f3f2bc | 324 | $rados = PVE::RADOS->new() if !defined($rados); |
7e1a9d25 TL |
325 | |
326 | my $res = $rados->mon_command({ prefix => "osd lspools" }); | |
327 | ||
328 | return $res; | |
329 | } | |
330 | ||
f96d7012 TL |
331 | sub destroy_pool { |
332 | my ($pool, $rados) = @_; | |
24f3f2bc | 333 | $rados = PVE::RADOS->new() if !defined($rados); |
f96d7012 TL |
334 | |
335 | # fixme: '--yes-i-really-really-mean-it' | |
336 | $rados->mon_command({ | |
337 | prefix => "osd pool delete", | |
338 | pool => $pool, | |
339 | pool2 => $pool, | |
f8eade23 | 340 | 'yes_i_really_really_mean_it' => JSON::true, |
f96d7012 TL |
341 | format => 'plain', |
342 | }); | |
343 | } | |
344 | ||
0ab69d6e DC |
345 | # we get something like: |
346 | #[{ | |
347 | # 'metadata_pool_id' => 2, | |
348 | # 'data_pool_ids' => [ 1 ], | |
349 | # 'metadata_pool' => 'cephfs_metadata', | |
350 | # 'data_pools' => [ 'cephfs_data' ], | |
351 | # 'name' => 'cephfs', | |
352 | #}] | |
353 | sub ls_fs { | |
354 | my ($rados) = @_; | |
24f3f2bc | 355 | $rados = PVE::RADOS->new() if !defined($rados); |
0ab69d6e DC |
356 | |
357 | my $res = $rados->mon_command({ prefix => "fs ls" }); | |
358 | ||
359 | return $res; | |
360 | } | |
361 | ||
362 | sub create_fs { | |
363 | my ($fs, $param, $rados) = @_; | |
364 | ||
365 | if (!defined($rados)) { | |
366 | $rados = PVE::RADOS->new(); | |
367 | } | |
368 | ||
369 | $rados->mon_command({ | |
370 | prefix => "fs new", | |
371 | fs_name => $fs, | |
372 | metadata => $param->{pool_metadata}, | |
373 | data => $param->{pool_data}, | |
374 | format => 'plain', | |
375 | }); | |
376 | } | |
377 | ||
02c1e98e DC |
378 | sub destroy_fs { |
379 | my ($fs, $rados) = @_; | |
24f3f2bc | 380 | $rados = PVE::RADOS->new() if !defined($rados); |
02c1e98e DC |
381 | |
382 | $rados->mon_command({ | |
383 | prefix => "fs rm", | |
384 | fs_name => $fs, | |
385 | 'yes_i_really_mean_it' => JSON::true, | |
386 | format => 'plain', | |
387 | }); | |
388 | } | |
389 | ||
a34866f0 DM |
390 | sub setup_pve_symlinks { |
391 | # fail if we find a real file instead of a link | |
392 | if (-f $ceph_cfgpath) { | |
393 | my $lnk = readlink($ceph_cfgpath); | |
e881cf2a | 394 | die "file '$ceph_cfgpath' already exists and is not a symlink to $pve_ceph_cfgpath\n" |
a34866f0 DM |
395 | if !$lnk || $lnk ne $pve_ceph_cfgpath; |
396 | } else { | |
c5a673ed | 397 | mkdir $ceph_cfgdir; |
a34866f0 DM |
398 | symlink($pve_ceph_cfgpath, $ceph_cfgpath) || |
399 | die "unable to create symlink '$ceph_cfgpath' - $!\n"; | |
400 | } | |
c18db15b TL |
401 | my $ceph_uid = getpwnam('ceph'); |
402 | my $ceph_gid = getgrnam('ceph'); | |
403 | chown $ceph_uid, $ceph_gid, $ceph_cfgdir; | |
a34866f0 DM |
404 | } |
405 | ||
d558d296 DC |
406 | sub get_or_create_admin_keyring { |
407 | if (! -f $pve_ckeyring_path) { | |
408 | run_command("ceph-authtool --create-keyring $pve_ckeyring_path " . | |
409 | "--gen-key -n client.admin " . | |
410 | "--cap mon 'allow *' " . | |
411 | "--cap osd 'allow *' " . | |
412 | "--cap mds 'allow *' " . | |
413 | "--cap mgr 'allow *' "); | |
414 | # we do not want to overwrite it | |
415 | if (! -f $ckeyring_path) { | |
416 | run_command("cp $pve_ckeyring_path $ckeyring_path"); | |
ea60e3b7 | 417 | run_command("chown ceph:ceph $ckeyring_path"); |
d558d296 DC |
418 | } |
419 | } | |
420 | return $pve_ckeyring_path; | |
421 | } | |
422 | ||
48e8a06d DC |
423 | # get ceph-volume managed osds |
424 | sub ceph_volume_list { | |
425 | my $result = {}; | |
48e8a06d DC |
426 | |
427 | if (!check_ceph_installed('ceph_volume', 1)) { | |
428 | return $result; | |
429 | } | |
430 | ||
d79e9eb5 TL |
431 | my $output = ''; |
432 | my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'list', '--format', 'json' ]; | |
433 | run_command($cmd, outfunc => sub { $output .= shift }); | |
48e8a06d DC |
434 | |
435 | $result = eval { decode_json($output) }; | |
436 | warn $@ if $@; | |
437 | return $result; | |
438 | } | |
439 | ||
440 | sub ceph_volume_zap { | |
441 | my ($osdid, $destroy) = @_; | |
442 | ||
443 | die "no osdid given\n" if !defined($osdid); | |
444 | ||
d79e9eb5 | 445 | my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'zap', '--osd-id', $osdid ]; |
48e8a06d DC |
446 | push @$cmd, '--destroy' if $destroy; |
447 | ||
448 | run_command($cmd); | |
449 | } | |
450 | ||
d4e7f1bf DC |
451 | sub get_db_wal_sizes { |
452 | my $res = {}; | |
453 | ||
454 | my $rados = PVE::RADOS->new(); | |
455 | my $db_config = $rados->mon_command({ prefix => 'config-key dump', key => 'config/' }); | |
456 | ||
457 | $res->{db} = $db_config->{"config/osd/bluestore_block_db_size"} // | |
458 | $db_config->{"config/global/bluestore_block_db_size"}; | |
459 | ||
460 | $res->{wal} = $db_config->{"config/osd/bluestore_block_wal_size"} // | |
461 | $db_config->{"config/global/bluestore_block_wal_size"}; | |
462 | ||
463 | if (!$res->{db} || !$res->{wal}) { | |
464 | my $cfg = cfs_read_file('ceph.conf'); | |
465 | if (!$res->{db}) { | |
466 | $res->{db} = $cfg->{osd}->{bluestore_block_db_size} // | |
467 | $cfg->{global}->{bluestore_block_db_size}; | |
468 | } | |
469 | ||
470 | if (!$res->{wal}) { | |
471 | $res->{wal} = $cfg->{osd}->{bluestore_block_wal_size} // | |
472 | $cfg->{global}->{bluestore_block_wal_size}; | |
473 | } | |
474 | } | |
475 | ||
476 | return $res; | |
477 | } | |
735f24eb TL |
478 | sub get_possible_osd_flags { |
479 | my $possible_flags = { | |
480 | pause => { | |
481 | description => 'Pauses read and writes.', | |
482 | type => 'boolean', | |
483 | optional=> 1, | |
484 | }, | |
485 | noup => { | |
486 | description => 'OSDs are not allowed to start.', | |
487 | type => 'boolean', | |
488 | optional=> 1, | |
489 | }, | |
490 | nodown => { | |
491 | description => 'OSD failure reports are being ignored, such that the monitors will not mark OSDs down.', | |
492 | type => 'boolean', | |
493 | optional=> 1, | |
494 | }, | |
495 | noout => { | |
496 | description => 'OSDs will not automatically be marked out after the configured interval.', | |
497 | type => 'boolean', | |
498 | optional=> 1, | |
499 | }, | |
500 | noin => { | |
501 | description => 'OSDs that were previously marked out will not be marked back in when they start.', | |
502 | type => 'boolean', | |
503 | optional=> 1, | |
504 | }, | |
505 | nobackfill => { | |
506 | description => 'Backfilling of PGs is suspended.', | |
507 | type => 'boolean', | |
508 | optional=> 1, | |
509 | }, | |
510 | norebalance => { | |
511 | description => 'Rebalancing of PGs is suspended.', | |
512 | type => 'boolean', | |
513 | optional=> 1, | |
514 | }, | |
515 | norecover => { | |
516 | description => 'Recovery of PGs is suspended.', | |
517 | type => 'boolean', | |
518 | optional=> 1, | |
519 | }, | |
520 | noscrub => { | |
521 | description => 'Scrubbing is disabled.', | |
522 | type => 'boolean', | |
523 | optional=> 1, | |
524 | }, | |
525 | 'nodeep-scrub' => { | |
526 | description => 'Deep Scrubbing is disabled.', | |
527 | type => 'boolean', | |
528 | optional=> 1, | |
529 | }, | |
530 | notieragent => { | |
531 | description => 'Cache tiering activity is suspended.', | |
532 | type => 'boolean', | |
533 | optional=> 1, | |
534 | }, | |
535 | }; | |
536 | return $possible_flags; | |
537 | } | |
538 | ||
539 | sub get_real_flag_name { | |
540 | my ($flag) = @_; | |
541 | ||
542 | # the 'pause' flag gets always set to both 'pauserd' and 'pausewr' | |
543 | # so decide that the 'pause' flag is set if we detect 'pauserd' | |
544 | my $flagmap = { | |
545 | 'pause' => 'pauserd', | |
546 | }; | |
547 | ||
548 | return $flagmap->{$flag} // $flag; | |
549 | } | |
d4e7f1bf | 550 | |
e25dda25 AA |
551 | sub ceph_cluster_status { |
552 | my ($rados) = @_; | |
553 | $rados = PVE::RADOS->new() if !$rados; | |
554 | ||
e25dda25 | 555 | my $status = $rados->mon_command({ prefix => 'status' }); |
e25dda25 AA |
556 | $status->{health} = $rados->mon_command({ prefix => 'health', detail => 'detail' }); |
557 | ||
fdf79b4e | 558 | if (!exists $status->{monmap}->{mons}) { # octopus moved most info out of status, re-add |
e25dda25 AA |
559 | $status->{monmap} = $rados->mon_command({ prefix => 'mon dump' }); |
560 | $status->{mgrmap} = $rados->mon_command({ prefix => 'mgr dump' }); | |
561 | } | |
562 | ||
563 | return $status; | |
564 | } | |
565 | ||
34a2222d | 566 | sub ecprofile_exists { |
23c407e5 TL |
567 | my ($name, $rados) = @_; |
568 | $rados = PVE::RADOS->new() if !$rados; | |
34a2222d | 569 | |
34a2222d AL |
570 | my $res = $rados->mon_command({ prefix => 'osd erasure-code-profile ls' }); |
571 | ||
572 | my $profiles = { map { $_ => 1 } @$res }; | |
573 | return $profiles->{$name}; | |
574 | } | |
575 | ||
576 | sub create_ecprofile { | |
23c407e5 TL |
577 | my ($name, $k, $m, $failure_domain, $device_class, $rados) = @_; |
578 | $rados = PVE::RADOS->new() if !$rados; | |
34a2222d AL |
579 | |
580 | $failure_domain = 'host' if !$failure_domain; | |
581 | ||
582 | my $profile = [ | |
583 | "crush-failure-domain=${failure_domain}", | |
584 | "k=${k}", | |
585 | "m=${m}", | |
586 | ]; | |
587 | ||
588 | push(@$profile, "crush-device-class=${device_class}") if $device_class; | |
589 | ||
34a2222d AL |
590 | $rados->mon_command({ |
591 | prefix => 'osd erasure-code-profile set', | |
592 | name => $name, | |
593 | profile => $profile, | |
594 | }); | |
595 | } | |
596 | ||
597 | sub destroy_ecprofile { | |
23c407e5 TL |
598 | my ($profile, $rados) = @_; |
599 | $rados = PVE::RADOS->new() if !$rados; | |
34a2222d | 600 | |
34a2222d AL |
601 | my $command = { |
602 | prefix => 'osd erasure-code-profile rm', | |
603 | name => $profile, | |
604 | format => 'plain', | |
605 | }; | |
606 | return $rados->mon_command($command); | |
607 | } | |
608 | ||
609 | sub get_ecprofile_name { | |
610 | my ($name) = @_; | |
611 | return "pve_ec_${name}"; | |
612 | } | |
613 | ||
614 | sub destroy_crush_rule { | |
23c407e5 TL |
615 | my ($rule, $rados) = @_; |
616 | $rados = PVE::RADOS->new() if !$rados; | |
617 | ||
34a2222d AL |
618 | my $command = { |
619 | prefix => 'osd crush rule rm', | |
620 | name => $rule, | |
621 | format => 'plain', | |
622 | }; | |
623 | return $rados->mon_command($command); | |
624 | } | |
625 | ||
a34866f0 | 626 | 1; |