]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/RBDPlugin.pm
use integer size in rbd size parameter
[pve-storage.git] / PVE / Storage / RBDPlugin.pm
CommitLineData
0509010d
AD
1package PVE::Storage::RBDPlugin;
2
3use strict;
4use warnings;
5use IO::File;
6use PVE::Tools qw(run_command trim);
7use PVE::Storage::Plugin;
8use PVE::JSONSchema qw(get_standard_option);
9
10use base qw(PVE::Storage::Plugin);
11
249cb647
SP
12sub rbd_unittobytes {
13 {
14 "M" => 1024*1024,
15 "G" => 1024*1024*1024,
16 "T" => 1024*1024*1024*1024,
17 }
18}
19
411476cd
DM
20my $rbd_cmd = sub {
21 my ($scfg, $storeid, $op, @options) = @_;
0509010d 22
e5427b00 23 my $monhost = $scfg->{monhost};
0509010d
AD
24 $monhost =~ s/;/,/g;
25
1440604a
AD
26 my $keyring = "/etc/pve/priv/ceph/${storeid}.keyring";
27 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
28 my $username = $scfg->{username} ? $scfg->{username} : 'admin';
29
30 my $cmd = ['/usr/bin/rbd', '-p', $pool, '-m', $monhost];
31
32 if(-e $keyring){
33 push @$cmd, '-n', "client.$username";
34 push @$cmd, '--keyring', $keyring;
35 push @$cmd, '--auth_supported', 'cephx';
36 }else{
37 push @$cmd, '--auth_supported', 'none';
38 }
39
40 push @$cmd, $op;
3e195ccc 41
411476cd 42 push @$cmd, @options if scalar(@options);
3e195ccc 43
411476cd
DM
44 return $cmd;
45};
0509010d 46
69589444
AD
47my $rados_cmd = sub {
48 my ($scfg, $storeid, $op, @options) = @_;
49
50 my $monhost = $scfg->{monhost};
51 $monhost =~ s/;/,/g;
52
1440604a
AD
53 my $keyring = "/etc/pve/priv/ceph/${storeid}.keyring";
54 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
55 my $username = $scfg->{username} ? $scfg->{username} : 'admin';
56
57 my $cmd = ['/usr/bin/rados', '-p', $pool, '-m', $monhost];
58
59 if(-e $keyring){
60 push @$cmd, '-n', "client.$username";
61 push @$cmd, '--keyring', $keyring;
62 push @$cmd, '--auth_supported', 'cephx';
63 }else{
64 push @$cmd, '--auth_supported', 'none';
65 }
66
67 push @$cmd, $op;
69589444
AD
68
69 push @$cmd, @options if scalar(@options);
70
71 return $cmd;
72};
73
411476cd
DM
74sub rbd_ls {
75 my ($scfg, $storeid) = @_;
d70e7f6c 76
7cb2889a 77 my $cmd = &$rbd_cmd($scfg, $storeid, 'ls', '-l');
1440604a 78 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
d70e7f6c 79
411476cd 80 my $list = {};
0509010d 81
8c3abf12 82 my $parser = sub {
411476cd 83 my $line = shift;
0509010d 84
ca1e168a
AD
85 if ($line =~ m/^((vm|base)-(\d+)-disk-\d+)\s+(\d+)(M|G|T)\s((\S+)\/((vm|base)-\d+-\S+@\S+))?/) {
86 my ($image, $owner, $size, $unit, $parent) = ($1, $3, $4, $5, $8);
0509010d 87
1440604a 88 $list->{$pool}->{$image} = {
411476cd 89 name => $image,
249cb647 90 size => $size*rbd_unittobytes()->{$unit},
62b98a65 91 parent => $parent,
411476cd
DM
92 vmid => $owner
93 };
94 }
8c3abf12
DM
95 };
96
97 eval {
98 run_command($cmd, errmsg => "rbd error", errfunc => sub {}, outfunc => $parser);
99 };
100 my $err = $@;
101
102 die $err if $err && $err !~ m/doesn't contain rbd images/ ;
411476cd
DM
103
104 return $list;
0509010d
AD
105}
106
62b98a65 107sub rbd_volume_info {
992e6835
AD
108 my ($scfg, $storeid, $volname, $snap) = @_;
109
110 my $cmd = undef;
111
112 if($snap){
113 $cmd = &$rbd_cmd($scfg, $storeid, 'info', $volname, '--snap', $snap);
114 }else{
115 $cmd = &$rbd_cmd($scfg, $storeid, 'info', $volname);
116 }
e110213e 117
e110213e 118 my $size = undef;
62b98a65 119 my $parent = undef;
992e6835
AD
120 my $format = undef;
121 my $protected = undef;
62b98a65 122
e110213e
AD
123 my $parser = sub {
124 my $line = shift;
125
249cb647
SP
126 if ($line =~ m/size (\d+) (M|G|T)B in (\d+) objects/) {
127 $size = $1 * rbd_unittobytes()->{$2} if ($1);
62b98a65
AD
128 } elsif ($line =~ m/parent:\s(\S+)\/(\S+)/) {
129 $parent = $2;
992e6835
AD
130 } elsif ($line =~ m/format:\s(\d+)/) {
131 $format = $1;
132 } elsif ($line =~ m/protected:\s(\S+)/) {
133 $protected = 1 if $1 eq "True";
e110213e 134 }
992e6835 135
e110213e
AD
136 };
137
138 run_command($cmd, errmsg => "rbd error", errfunc => sub {}, outfunc => $parser);
139
992e6835 140 return ($size, $parent, $format, $protected);
e110213e
AD
141}
142
0509010d
AD
143sub addslashes {
144 my $text = shift;
145 $text =~ s/;/\\;/g;
146 $text =~ s/:/\\:/g;
147 return $text;
148}
149
e5427b00 150# Configuration
0509010d 151
e5427b00
AD
152PVE::JSONSchema::register_format('pve-storage-monhost', \&parse_monhost);
153sub parse_monhost {
0509010d
AD
154 my ($name, $noerr) = @_;
155
156 if ($name !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
157 return undef if $noerr;
158 die "lvm name '$name' contains illegal characters\n";
159 }
160
161 return $name;
162}
163
0509010d
AD
164sub type {
165 return 'rbd';
166}
167
168sub plugindata {
169 return {
170 content => [ {images => 1}, { images => 1 }],
171 };
172}
173
174sub properties {
175 return {
e5427b00 176 monhost => {
0509010d 177 description => "Monitors daemon ips.",
e5427b00 178 type => 'string',
0509010d 179 },
e5427b00
AD
180 pool => {
181 description => "Pool.",
0509010d
AD
182 type => 'string',
183 },
e5427b00
AD
184 username => {
185 description => "RBD Id.",
0509010d
AD
186 type => 'string',
187 },
e5427b00 188 authsupported => {
0509010d
AD
189 description => "Authsupported.",
190 type => 'string',
191 },
192 };
193}
194
195sub options {
196 return {
35d6dfaf
AD
197 nodes => { optional => 1 },
198 disable => { optional => 1 },
e5427b00 199 monhost => { fixed => 1 },
1440604a
AD
200 pool => { optional => 1 },
201 username => { optional => 1 },
0509010d
AD
202 content => { optional => 1 },
203 };
204}
205
206# Storage implementation
207
208sub parse_volname {
209 my ($class, $volname) = @_;
210
d04c7e55
AD
211 if ($volname =~ m/^((base-(\d+)-\S+)\/)?((base)?(vm)?-(\d+)-\S+)$/) {
212 return ('images', $4, $7, $2, $3, $5);
0509010d
AD
213 }
214
215 die "unable to parse rbd volume name '$volname'\n";
216}
217
218sub path {
e5427b00 219 my ($class, $scfg, $volname, $storeid) = @_;
0509010d
AD
220
221 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
222
e5427b00 223 my $monhost = addslashes($scfg->{monhost});
1440604a
AD
224 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
225 my $username = $scfg->{username} ? $scfg->{username} : 'admin';
226
227 my $path = "rbd:$pool/$name:mon_host=$monhost";
228 my $keyring = "/etc/pve/priv/ceph/${storeid}.keyring";
229
230 if(-e $keyring ){
231 $path .= ":id=$username:auth_supported=cephx:keyring=$keyring";
232 }else{
233 $path .= ":auth_supported=none";
234 }
0509010d
AD
235
236 return ($path, $vmid, $vtype);
237}
238
5b9b9b14
AD
239my $find_free_diskname = sub {
240 my ($storeid, $scfg, $vmid) = @_;
241
242 my $rbd = rbd_ls($scfg, $storeid);
1440604a 243 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
5b9b9b14 244 my $disk_ids = {};
1440604a 245 my $dat = $rbd->{$pool};
5b9b9b14
AD
246
247 foreach my $image (keys %$dat) {
248 my $volname = $dat->{$image}->{name};
249 if ($volname =~ m/(vm|base)-$vmid-disk-(\d+)/){
250 $disk_ids->{$2} = 1;
251 }
252 }
253 #fix: can we search in $rbd hash key with a regex to find (vm|base) ?
254 for (my $i = 1; $i < 100; $i++) {
255 if (!$disk_ids->{$i}) {
256 return "vm-$vmid-disk-$i";
257 }
258 }
259
260 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n";
261};
262
5eab0272
DM
263sub create_base {
264 my ($class, $storeid, $scfg, $volname) = @_;
265
992e6835
AD
266 my $snap = '__base__';
267
268 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
269 $class->parse_volname($volname);
270
271 die "create_base not possible with base image\n" if $isBase;
272
273 my ($size, $parent, $format, undef) = rbd_volume_info($scfg, $storeid, $name);
274 die "rbd volume info on '$name' failed\n" if !($size);
275
276 die "rbd image must be at format V2" if $format ne "2";
277
278 die "volname '$volname' contains wrong information about parent $parent $basename\n"
279 if $basename && (!$parent || $parent ne $basename."@".$snap);
280
281 my $newname = $name;
282 $newname =~ s/^vm-/base-/;
283
284 my $newvolname = $basename ? "$basename/$newname" : "$newname";
285
286 my $cmd = &$rbd_cmd($scfg, $storeid, 'rename', $name, $newname);
287 run_command($cmd, errmsg => "rbd rename $name' error", errfunc => sub {});
288
289 my $running = undef; #fixme : is create_base always offline ?
290
291 $class->volume_snapshot($scfg, $storeid, $newname, $snap, $running);
292
293 my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $newname, $snap);
294
295 if (!$protected){
296 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'protect', $newname, '--snap', $snap);
297 run_command($cmd, errmsg => "rbd protect $newname snap $snap' error", errfunc => sub {});
298 }
299
300 return $newvolname;
301
5eab0272
DM
302}
303
304sub clone_image {
305 my ($class, $scfg, $storeid, $volname, $vmid) = @_;
306
f2708285
AD
307 my $snap = '__base__';
308
309 my ($vtype, $basename, $basevmid, undef, undef, $isBase) =
310 $class->parse_volname($volname);
311
312 die "clone_image onyl works on base images\n" if !$isBase;
313
314 my $name = &$find_free_diskname($storeid, $scfg, $vmid);
315
316 warn "clone $volname: $basename to $name\n";
317
318 my $newvol = "$basename/$name";
319
320 my $cmd = &$rbd_cmd($scfg, $storeid, 'clone', $basename, '--snap', $snap, $name);
321 run_command($cmd, errmsg => "rbd clone $basename' error", errfunc => sub {});
322
323 return $newvol;
5eab0272
DM
324}
325
0509010d
AD
326sub alloc_image {
327 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
328
329
e5427b00 330 die "illegal name '$name' - sould be 'vm-$vmid-*'\n"
0509010d 331 if $name && $name !~ m/^vm-$vmid-/;
0509010d 332
8b3ae518 333 $name = &$find_free_diskname($storeid, $scfg, $vmid);
0509010d 334
87cf5d40 335 my $cmd = &$rbd_cmd($scfg, $storeid, 'create', '--format' , 2, '--size', int(($size+1023)/1024), $name);
411476cd 336 run_command($cmd, errmsg => "rbd create $name' error", errfunc => sub {});
0509010d
AD
337
338 return $name;
339}
340
341sub free_image {
32437ed2 342 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
0509010d 343
42d07b9a
AD
344 my ($vtype, $name, $vmid, undef, undef, undef) =
345 $class->parse_volname($volname);
346
347 if ($isBase) {
348 my $snap = '__base__';
349 my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $name, $snap);
350 if ($protected){
351 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'unprotect', $name, '--snap', $snap);
352 run_command($cmd, errmsg => "rbd unprotect $name snap $snap' error", errfunc => sub {});
353 }
354 }
355
356 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'purge', $name);
c30470a3
AD
357 run_command($cmd, errmsg => "rbd snap purge $volname' error", outfunc => sub {}, errfunc => sub {});
358
42d07b9a 359 $cmd = &$rbd_cmd($scfg, $storeid, 'rm', $name);
411476cd 360 run_command($cmd, errmsg => "rbd rm $volname' error", outfunc => sub {}, errfunc => sub {});
0509010d
AD
361
362 return undef;
363}
364
365sub list_images {
366 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
367
e5427b00 368 $cache->{rbd} = rbd_ls($scfg, $storeid) if !$cache->{rbd};
1440604a 369 my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
411476cd 370
0509010d
AD
371 my $res = [];
372
1440604a 373 if (my $dat = $cache->{rbd}->{$pool}) {
0509010d
AD
374 foreach my $image (keys %$dat) {
375
376 my $volname = $dat->{$image}->{name};
377
378 my $volid = "$storeid:$volname";
379
0509010d
AD
380 my $owner = $dat->{$volname}->{vmid};
381 if ($vollist) {
382 my $found = grep { $_ eq $volid } @$vollist;
383 next if !$found;
384 } else {
385 next if defined ($vmid) && ($owner ne $vmid);
386 }
387
388 my $info = $dat->{$volname};
389 $info->{volid} = $volid;
411476cd 390 $info->{format} = 'raw';
0509010d
AD
391
392 push @$res, $info;
393 }
394 }
395
411476cd 396 return $res;
0509010d
AD
397}
398
0509010d
AD
399sub status {
400 my ($class, $storeid, $scfg, $cache) = @_;
401
69589444
AD
402 my $cmd = &$rados_cmd($scfg, $storeid, 'df');
403
404 my $stats = {};
405
406 my $parser = sub {
407 my $line = shift;
408 if ($line =~ m/^\s+total\s(\S+)\s+(\d+)/) {
409 $stats->{$1} = $2;
410 }
411 };
412
413 eval {
414 run_command($cmd, errmsg => "rados error", errfunc => sub {}, outfunc => $parser);
415 };
416
417 my $total = $stats->{space} ? $stats->{space}*1024 : 0;
418 my $free = $stats->{avail} ? $stats->{avail}*1024 : 0;
419 my $used = $stats->{used} ? $stats->{used}*1024: 0;
0509010d 420 my $active = 1;
0509010d 421
411476cd 422 return ($total, $free, $used, $active);
0509010d
AD
423}
424
425sub activate_storage {
426 my ($class, $storeid, $scfg, $cache) = @_;
427 return 1;
428}
429
430sub deactivate_storage {
431 my ($class, $storeid, $scfg, $cache) = @_;
432 return 1;
433}
434
435sub activate_volume {
436 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
437 return 1;
438}
439
440sub deactivate_volume {
441 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
442 return 1;
443}
444
0002d9cc
AD
445sub volume_size_info {
446 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
447
81d1d017
AD
448 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
449 my ($size, undef) = rbd_volume_info($scfg, $storeid, $name);
62b98a65 450 return $size;
0002d9cc
AD
451}
452
e7a42a76
AD
453sub volume_resize {
454 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
455
456 return 1 if $running;
457
478fc06c
AD
458 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
459
460 my $cmd = &$rbd_cmd($scfg, $storeid, 'resize', '--size', ($size/1024/1024), $name);
e7a42a76
AD
461 run_command($cmd, errmsg => "rbd resize $volname' error", errfunc => sub {});
462 return undef;
463}
464
788dd8e1
AD
465sub volume_snapshot {
466 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
467
468 return 1 if $running;
469
9af33ed0
AD
470 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
471
472 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'create', '--snap', $snap, $name);
788dd8e1
AD
473 run_command($cmd, errmsg => "rbd snapshot $volname' error", errfunc => sub {});
474 return undef;
475}
476
5a2b2e2f
AD
477sub volume_snapshot_rollback {
478 my ($class, $scfg, $storeid, $volname, $snap) = @_;
479
c6ce2cc8
AD
480 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
481
482 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'rollback', '--snap', $snap, $name);
5a2b2e2f
AD
483 run_command($cmd, errmsg => "rbd snapshot $volname to $snap' error", errfunc => sub {});
484}
485
cce29bcd
AD
486sub volume_snapshot_delete {
487 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
488
489 return 1 if $running;
490
c78cb110
AD
491 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
492
493 my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'rm', '--snap', $snap, $name);
cce29bcd
AD
494 run_command($cmd, errmsg => "rbd snapshot $volname' error", errfunc => sub {});
495 return undef;
496}
497
774f21b9
AD
498sub volume_has_feature {
499 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
500
501 my $features = {
5649ccfe
AD
502 snapshot => { current => 1, snap => 1},
503 clone => { base => 1},
504 template => { current => 1},
505 copy => { base => 1, current => 1, snap => 1},
774f21b9
AD
506 };
507
1e7ae581
AD
508 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
509 $class->parse_volname($volname);
510
511 my $key = undef;
512 if($snapname){
2c5a7097 513 $key = 'snap';
1e7ae581
AD
514 }else{
515 $key = $isBase ? 'base' : 'current';
516 }
517 return 1 if $features->{$feature}->{$key};
774f21b9
AD
518
519 return undef;
520}
521
0509010d 5221;