]> git.proxmox.com Git - pve-manager.git/blame - PVE/CephTools.pm
Change Ct restore rest call
[pve-manager.git] / PVE / CephTools.pm
CommitLineData
a34866f0
DM
1package PVE::CephTools;
2
3use strict;
4use warnings;
5use File::Basename;
6use File::Path;
7use POSIX qw (LONG_MAX);
8use Cwd qw(abs_path);
7d4fc5ef 9use IO::Dir;
a34866f0 10
7d4fc5ef 11use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
a34866f0
DM
12
13my $ccname = 'ceph'; # ceph cluster name
14my $ceph_cfgdir = "/etc/ceph";
15my $pve_ceph_cfgpath = "/etc/pve/$ccname.conf";
16my $ceph_cfgpath = "$ceph_cfgdir/$ccname.conf";
17
18my $pve_mon_key_path = "/etc/pve/priv/$ccname.mon.keyring";
19my $pve_ckeyring_path = "/etc/pve/priv/$ccname.client.admin.keyring";
20my $ceph_bootstrap_osd_keyring = "/var/lib/ceph/bootstrap-osd/$ccname.keyring";
21my $ceph_bootstrap_mds_keyring = "/var/lib/ceph/bootstrap-mds/$ccname.keyring";
22
23my $ceph_bin = "/usr/bin/ceph";
24
25my $config_hash = {
26 ccname => $ccname,
27 pve_ceph_cfgpath => $pve_ceph_cfgpath,
28 pve_mon_key_path => $pve_mon_key_path,
29 pve_ckeyring_path => $pve_ckeyring_path,
30 ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring,
31 ceph_bootstrap_mds_keyring => $ceph_bootstrap_mds_keyring,
7d4fc5ef 32 long_rados_timeout => 60,
a34866f0
DM
33};
34
35sub get_config {
36 my $key = shift;
37
38 my $value = $config_hash->{$key};
39
40 die "no such ceph config '$key'" if !$value;
41
42 return $value;
43}
44
45sub verify_blockdev_path {
46 my ($path) = @_;
47
48 $path = abs_path($path);
49
50 die "got unusual device path '$path'\n" if $path !~ m|^/dev/(.*)$|;
51
52 $path = "/dev/$1"; # untaint
53
54 die "no such block device '$path'\n"
55 if ! -b $path;
56
57 return $path;
58};
59
60sub purge_all_ceph_files {
61 # fixme: this is very dangerous - should we really support this function?
62
63 unlink $ceph_cfgpath;
64
65 unlink $pve_ceph_cfgpath;
66 unlink $pve_ckeyring_path;
67 unlink $pve_mon_key_path;
68
69 unlink $ceph_bootstrap_osd_keyring;
70 unlink $ceph_bootstrap_mds_keyring;
71
72 system("rm -rf /var/lib/ceph/mon/ceph-*");
73
74 # remove osd?
75}
76
77sub check_ceph_installed {
78 my ($noerr) = @_;
79
80 if (! -x $ceph_bin) {
81 die "ceph binaries not installed\n" if !$noerr;
82 return undef;
83 }
84
85 return 1;
86}
87
88sub check_ceph_inited {
89 my ($noerr) = @_;
90
91 return undef if !check_ceph_installed($noerr);
92
93 if (! -f $pve_ceph_cfgpath) {
94 die "pveceph configuration not initialized\n" if !$noerr;
95 return undef;
96 }
97
98 return 1;
99}
100
101sub check_ceph_enabled {
102 my ($noerr) = @_;
103
104 return undef if !check_ceph_inited($noerr);
105
106 if (! -f $ceph_cfgpath) {
107 die "pveceph configuration not enabled\n" if !$noerr;
108 return undef;
109 }
110
111 return 1;
112}
113
114sub parse_ceph_config {
115 my ($filename) = @_;
116
117 $filename = $pve_ceph_cfgpath if !$filename;
118
119 my $cfg = {};
120
121 return $cfg if ! -f $filename;
122
123 my $fh = IO::File->new($filename, "r") ||
124 die "unable to open '$filename' - $!\n";
125
126 my $section;
127
128 while (defined(my $line = <$fh>)) {
129 $line =~ s/[;#].*$//;
130 $line =~ s/^\s+//;
131 $line =~ s/\s+$//;
132 next if !$line;
133
134 $section = $1 if $line =~ m/^\[(\S+)\]$/;
135 if (!$section) {
136 warn "no section - skip: $line\n";
137 next;
138 }
139
140 if ($line =~ m/^(.*\S)\s*=\s*(\S.*)$/) {
141 $cfg->{$section}->{$1} = $2;
142 }
143
144 }
145
146 return $cfg;
147}
148
149sub write_ceph_config {
150 my ($cfg) = @_;
151
152 my $out = '';
153
154 my $cond_write_sec = sub {
155 my $re = shift;
156
157 foreach my $section (keys %$cfg) {
158 next if $section !~ m/^$re$/;
159 $out .= "[$section]\n";
160 foreach my $key (sort keys %{$cfg->{$section}}) {
161 $out .= "\t $key = $cfg->{$section}->{$key}\n";
162 }
163 $out .= "\n";
164 }
165 };
166
167 &$cond_write_sec('global');
19924e77 168 &$cond_write_sec('client');
0fe9bdd5
DM
169 &$cond_write_sec('mds');
170 &$cond_write_sec('mds\..*');
a34866f0
DM
171 &$cond_write_sec('mon');
172 &$cond_write_sec('osd');
173 &$cond_write_sec('mon\..*');
174 &$cond_write_sec('osd\..*');
175
176 PVE::Tools::file_set_contents($pve_ceph_cfgpath, $out);
177}
178
179sub setup_pve_symlinks {
180 # fail if we find a real file instead of a link
181 if (-f $ceph_cfgpath) {
182 my $lnk = readlink($ceph_cfgpath);
183 die "file '$ceph_cfgpath' already exists\n"
184 if !$lnk || $lnk ne $pve_ceph_cfgpath;
185 } else {
186 symlink($pve_ceph_cfgpath, $ceph_cfgpath) ||
187 die "unable to create symlink '$ceph_cfgpath' - $!\n";
188 }
189}
190
191sub ceph_service_cmd {
c3959a07
DM
192 # ceph daemons does not call 'setsid', so we do that ourself
193 # (fork_worker send KILL to whole process group)
194 PVE::Tools::run_command(['setsid', 'service', 'ceph', '-c', $pve_ceph_cfgpath, @_]);
a34866f0
DM
195}
196
7d4fc5ef
DM
197sub list_disks {
198 my $disklist = {};
199
200 my $fd = IO::File->new("/proc/mounts", "r") ||
201 die "unable to open /proc/mounts - $!\n";
202
203 my $mounted = {};
204
205 while (defined(my $line = <$fd>)) {
206 my ($dev, $path, $fstype) = split(/\s+/, $line);
207 next if !($dev && $path && $fstype);
208 next if $dev !~ m|^/dev/|;
209 my $real_dev = abs_path($dev);
210 $mounted->{$real_dev} = $path;
211 }
212 close($fd);
213
214 my $dev_is_mounted = sub {
215 my ($dev) = @_;
216 return $mounted->{$dev};
217 };
218
219 my $dir_is_epmty = sub {
220 my ($dir) = @_;
221
222 my $dh = IO::Dir->new ($dir);
223 return 1 if !$dh;
224
225 while (defined(my $tmp = $dh->read)) {
226 next if $tmp eq '.' || $tmp eq '..';
227 $dh->close;
228 return 0;
229 }
230 $dh->close;
231 return 1;
232 };
233
234 my $journal_uuid = '45b0969e-9b03-4f30-b4c6-b4b80ceff106';
235
236 my $journalhash = {};
237 dir_glob_foreach('/dev/disk/by-parttypeuuid', "$journal_uuid\..+", sub {
238 my ($entry) = @_;
239 my $real_dev = abs_path("/dev/disk/by-parttypeuuid/$entry");
240 $journalhash->{$real_dev} = 1;
241 });
242
243 dir_glob_foreach('/sys/block', '.*', sub {
244 my ($dev) = @_;
245
246 return if $dev eq '.';
247 return if $dev eq '..';
248
249 return if $dev =~ m|^ram\d+$|; # skip ram devices
250 return if $dev =~ m|^loop\d+$|; # skip loop devices
251 return if $dev =~ m|^md\d+$|; # skip md devices
252 return if $dev =~ m|^dm-.*$|; # skip dm related things
253 return if $dev =~ m|^fd\d+$|; # skip Floppy
254 return if $dev =~ m|^sr\d+$|; # skip CDs
255
256 my $devdir = "/sys/block/$dev/device";
257 return if ! -d $devdir;
258
259 my $size = file_read_firstline("/sys/block/$dev/size");
260 return if !$size;
261
262 $size = $size * 512;
263
264 my $info = `udevadm info --path /sys/block/$dev --query all`;
265 return if !$info;
266
267 return if $info !~ m/^E: DEVTYPE=disk$/m;
268 return if $info =~ m/^E: ID_CDROM/m;
269
270 my $serial = 'unknown';
271 if ($info =~ m/^E: ID_SERIAL_SHORT=(\S+)$/m) {
272 $serial = $1;
273 }
274
275 my $gpt = 0;
276 if ($info =~ m/^E: ID_PART_TABLE_TYPE=gpt$/m) {
277 $gpt = 1;
278 }
279
280 # detect SSD (fixme - currently only works for ATA disks)
281 my $rpm = 7200; # default guess
282 if ($info =~ m/^E: ID_ATA_ROTATION_RATE_RPM=(\d+)$/m) {
283 $rpm = $1;
284 }
285
286 my $vendor = file_read_firstline("$devdir/vendor") || 'unknown';
287 my $model = file_read_firstline("$devdir/model") || 'unknown';
288
289 my $used;
290
291 $used = 'LVM' if !&$dir_is_epmty("/sys/block/$dev/holders");
292
293 $used = 'mounted' if &$dev_is_mounted("/dev/$dev");
294
295 $disklist->{$dev} = {
296 vendor => $vendor,
297 model => $model,
298 size => $size,
299 serial => $serial,
300 gpt => $gpt,
301 rmp => $rpm,
302 };
303
304 my $osdid = -1;
305
306 my $journal_count = 0;
307
308 my $found_partitions;
309 my $found_lvm;
310 my $found_mountpoints;
311 dir_glob_foreach("/sys/block/$dev", "$dev.+", sub {
312 my ($part) = @_;
313
314 $found_partitions = 1;
315
316 if (my $mp = &$dev_is_mounted("/dev/$part")) {
317 $found_mountpoints = 1;
318 if ($mp =~ m|^/var/lib/ceph/osd/ceph-(\d+)$|) {
319 $osdid = $1;
320 }
321 }
322 if (!&$dir_is_epmty("/sys/block/$dev/$part/holders")) {
323 $found_lvm = 1;
324 }
325 $journal_count++ if $journalhash->{"/dev/$part"};
326 });
327
328 $used = 'mounted' if $found_mountpoints && !$used;
329 $used = 'LVM' if $found_lvm && !$used;
330 $used = 'partitions' if $found_partitions && !$used;
331
332 $disklist->{$dev}->{used} = $used if $used;
333 $disklist->{$dev}->{osdid} = $osdid;
334 $disklist->{$dev}->{journals} = $journal_count;
335 });
336
337 return $disklist;
338}
339
a34866f0 3401;