]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/DRBDPlugin.pm
allow to create containers on DRBD (content == rootdir)
[pve-storage.git] / PVE / Storage / DRBDPlugin.pm
CommitLineData
14770890
DM
1package PVE::Storage::DRBDPlugin;
2
3use strict;
4use warnings;
5use IO::File;
eab90afd
DM
6use Net::DBus;
7use Data::Dumper;
8
14770890 9use PVE::Tools qw(run_command trim);
28d58512 10use PVE::INotify;
14770890
DM
11use PVE::Storage::Plugin;
12use PVE::JSONSchema qw(get_standard_option);
13
14use base qw(PVE::Storage::Plugin);
15
16# Configuration
17
fb0e1d93
DM
18my $default_redundancy = 2;
19
14770890
DM
20sub type {
21 return 'drbd';
22}
23
24sub plugindata {
25 return {
3c056934 26 content => [ {images => 1, rootdir => 1}, { images => 1 }],
14770890
DM
27 };
28}
29
30sub properties {
31 return {
32 redundancy => {
33 description => "The redundancy count specifies the number of nodes to which the resource should be deployed. It must be at least 1 and at most the number of nodes in the cluster.",
34 type => 'integer',
35 minimum => 1,
36 maximum => 16,
fb0e1d93 37 default => $default_redundancy,
14770890
DM
38 },
39 };
40}
41
42sub options {
43 return {
44 redundancy => { optional => 1 },
45 nodes => { optional => 1 },
46 disable => { optional => 1 },
47 };
48}
49
eab90afd
DM
50# helper
51
fb0e1d93
DM
52sub get_redundancy {
53 my ($scfg) = @_;
54
55 return $scfg->{redundancy} || $default_redundancy;
56}
57
eab90afd
DM
58sub connect_drbdmanage_service {
59
60 my $bus = Net::DBus->system;
61
62 my $service = $bus->get_service("org.drbd.drbdmanaged");
63
64 my $hdl = $service->get_object("/interface", "org.drbd.drbdmanaged");
65
66 return $hdl;
67}
68
31ba75ff 69sub check_drbd_res {
eab90afd
DM
70 my ($rc) = @_;
71
31ba75ff 72 die "got undefined drbd result\n" if !$rc;
eab90afd 73
31ba75ff 74 foreach my $res (@$rc) {
30a1369b 75 my ($code, $format, $details) = @$res;
eab90afd 76
30a1369b 77 next if $code == 0;
eab90afd 78
30a1369b
DM
79 my $msg;
80 if (defined($format)) {
81 my @args = ();
82 push @args, $details->{$1} // ""
83 while $format =~ s,\%\((\w+)\),%,;
84
85 $msg = sprintf($format, @args);
86
87 } else {
88 $msg = "drbd error: got error code $code";
89 }
90
31ba75ff 91 chomp $msg;
31ba75ff
DM
92 die "drbd error: $msg\n";
93 }
94
95 return undef;
eab90afd
DM
96}
97
98sub drbd_list_volumes {
99 my ($hdl) = @_;
100
101 $hdl = connect_drbdmanage_service() if !$hdl;
102
103 my ($rc, $res) = $hdl->list_volumes([], 0, {}, []);
31ba75ff 104 check_drbd_res($rc);
eab90afd
DM
105
106 my $volumes = {};
107
108 foreach my $entry (@$res) {
109 my ($volname, $properties, $vol_list) = @$entry;
110
111 next if $volname !~ m/^vm-(\d+)-/;
112 my $vmid = $1;
113
114 # fixme: we always use volid 0 ?
115 my $size = 0;
116 foreach my $volentry (@$vol_list) {
117 my ($vol_id, $vol_properties) = @$volentry;
118 next if $vol_id != 0;
119 my $vol_size = $vol_properties->{vol_size} * 1024;
120 $size = $vol_size if $vol_size > $size;
121 }
122
123 $volumes->{$volname} = { format => 'raw', size => $size,
124 vmid => $vmid };
125 }
126
127 return $volumes;
128}
129
14770890
DM
130# Storage implementation
131
132sub parse_volname {
133 my ($class, $volname) = @_;
134
135 if ($volname =~ m/^(vm-(\d+)-[a-z][a-z0-9\-\_\.]*[a-z0-9]+)$/) {
7800e84d 136 return ('images', $1, $2, undef, undef, undef, 'raw');
14770890
DM
137 }
138
139 die "unable to parse lvm volume name '$volname'\n";
140}
141
142sub filesystem_path {
e67069eb
DM
143 my ($class, $scfg, $volname, $snapname) = @_;
144
145 die "drbd snapshot is not implemented\n" if defined($snapname);
14770890
DM
146
147 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
148
eab90afd
DM
149 # fixme: always use volid 0?
150 my $path = "/dev/drbd/by-res/$volname/0";
14770890
DM
151
152 return wantarray ? ($path, $vmid, $vtype) : $path;
153}
154
155sub create_base {
156 my ($class, $storeid, $scfg, $volname) = @_;
157
158 die "can't create base images in drbd storage\n";
159}
160
161sub clone_image {
162 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
163
164 die "can't clone images in drbd storage\n";
165}
166
167sub alloc_image {
168 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
169
170 die "unsupported format '$fmt'" if $fmt ne 'raw';
171
47dbb901 172 die "illegal name '$name' - should be 'vm-$vmid-*'\n"
69a093c7 173 if defined($name) && $name !~ m/^vm-$vmid-/;
14770890 174
eab90afd
DM
175 my $hdl = connect_drbdmanage_service();
176 my $volumes = drbd_list_volumes($hdl);
14770890 177
69a093c7 178 die "volume '$name' already exists\n" if defined($name) && $volumes->{$name};
eab90afd 179
69a093c7 180 if (!defined($name)) {
14770890
DM
181 for (my $i = 1; $i < 100; $i++) {
182 my $tn = "vm-$vmid-disk-$i";
eab90afd 183 if (!defined ($volumes->{$tn})) {
14770890
DM
184 $name = $tn;
185 last;
186 }
187 }
188 }
189
190 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
69a093c7 191 if !defined($name);
eab90afd
DM
192
193 my ($rc, $res) = $hdl->create_resource($name, {});
31ba75ff 194 check_drbd_res($rc);
14770890 195
eab90afd 196 ($rc, $res) = $hdl->create_volume($name, $size, {});
31ba75ff 197 check_drbd_res($rc);
14770890 198
98e250aa
DM
199 ($rc, $res) = $hdl->set_drbdsetup_props(
200 {
201 target => "resource",
202 resource => $name,
203 type => 'neto',
204 'allow-two-primaries' => 'yes',
205 });
31ba75ff 206 check_drbd_res($rc);
98e250aa 207
b0e0ed1a
DM
208 my $redundancy = get_redundancy($scfg);;
209
210 ($rc, $res) = $hdl->auto_deploy($name, $redundancy, 0, 0);
211 check_drbd_res($rc);
212
14770890
DM
213 return $name;
214}
215
216sub free_image {
217 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
218
eab90afd
DM
219 my $hdl = connect_drbdmanage_service();
220 my ($rc, $res) = $hdl->remove_resource($volname, 0);
31ba75ff 221 check_drbd_res($rc);
14770890
DM
222
223 return undef;
224}
225
226sub list_images {
227 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
228
229 my $vgname = $scfg->{vgname};
230
eab90afd
DM
231 $cache->{drbd_volumes} = drbd_list_volumes() if !$cache->{drbd_volumes};
232
14770890
DM
233 my $res = [];
234
eab90afd
DM
235 my $dat = $cache->{drbd_volumes};
236
237 foreach my $volname (keys %$dat) {
238
239 my $owner = $dat->{$volname}->{vmid};
240
241 my $volid = "$storeid:$volname";
242
243 if ($vollist) {
244 my $found = grep { $_ eq $volid } @$vollist;
245 next if !$found;
246 } else {
247 next if defined ($vmid) && ($owner ne $vmid);
248 }
249
250 my $info = $dat->{$volname};
251 $info->{volid} = $volid;
252
253 push @$res, $info;
254 }
255
14770890
DM
256 return $res;
257}
258
259sub status {
260 my ($class, $storeid, $scfg, $cache) = @_;
261
5d6a88b0
DM
262 my ($total, $avail, $used);
263
eab90afd
DM
264 eval {
265 my $hdl = connect_drbdmanage_service();
fb0e1d93
DM
266 my $redundancy = get_redundancy($scfg);;
267 my ($rc, $res) = $hdl->cluster_free_query($redundancy);
31ba75ff 268 check_drbd_res($rc);
eab90afd 269
5d6a88b0
DM
270 $avail = $res;
271 $used = 0; # fixme
272 $total = $used + $avail;
eab90afd 273
eab90afd 274 };
5d6a88b0
DM
275 if (my $err = $@) {
276 # ignore error,
277 # assume storage if offline
14770890 278
5d6a88b0
DM
279 return undef;
280 }
281
282 return ($total, $avail, $used, 1);
14770890
DM
283}
284
285sub activate_storage {
286 my ($class, $storeid, $scfg, $cache) = @_;
287
288 return undef;
289}
290
291sub deactivate_storage {
292 my ($class, $storeid, $scfg, $cache) = @_;
293
294 return undef;
295}
296
297sub activate_volume {
298 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
299
28d58512
DM
300 my $path = $class->path($scfg, $volname);
301
302 my $hdl = connect_drbdmanage_service();
303 my $nodename = PVE::INotify::nodename();
304 my ($rc, $res) = $hdl->list_assignments([$nodename], [], 0, {}, []);
31ba75ff 305 check_drbd_res($rc);
28d58512
DM
306
307 foreach my $entry (@$res) {
308 my ($node, $res_name, $props, $voldata) = @$entry;
309 if (($node eq $nodename) && ($res_name eq $volname)) {
310 return undef; # assignment already exists
311 }
312 }
313
314 # create diskless assignment
315 ($rc, $res) = $hdl->assign($nodename, $volname, { diskless => 'true' });
31ba75ff 316 check_drbd_res($rc);
466183d6 317
4959ea20 318 # wait until device is acessitble
466183d6
DM
319 my $print_warning = 1;
320 my $max_wait_time = 20;
321 for (my $i = 0;; $i++) {
99136653
DM
322 if (1) {
323 # clumsy, but works
324 last if system("dd if=$path of=/dev/null bs=512 count=1 >/dev/null 2>&1") == 0;
325 } else {
326 # correct, but does not work?
327 ($rc, $res) = $hdl->list_assignments([$nodename], [$volname], 0, { "cstate:deploy" => "true" }, []);
328 check_drbd_res($rc);
329 my $len = scalar(@$res);
330 last if $len > 0;
331 }
466183d6
DM
332 die "aborting wait - device '$path' still not readable\n" if $i > $max_wait_time;
333 print "waiting for device '$path' to become ready...\n" if $print_warning;
334 $print_warning = 0;
335 sleep(1);
336 }
4959ea20 337
14770890
DM
338 return undef;
339}
340
341sub deactivate_volume {
342 my ($class, $storeid, $scfg, $volname, $cache) = @_;
343
ae9e512e
DM
344 return undef; # fixme: should we unassign ?
345
346 # remove above return to enable this code
347 my $hdl = connect_drbdmanage_service();
348 my $nodename = PVE::INotify::nodename();
349 my ($rc, $res) = $hdl->list_assignments([$nodename], [$volname], 0,
350 { "cstate:diskless" => "true" }, []);
351 check_drbd_res($rc);
352 if (scalar(@$res)) {
353 my ($rc, $res) = $hdl->unassign($nodename, $volname,0);
354 check_drbd_res($rc);
355 }
356
14770890
DM
357 return undef;
358}
359
360sub volume_resize {
361 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
362
363 $size = ($size/1024/1024) . "M";
364
365 my $path = $class->path($scfg, $volname);
366
eab90afd
DM
367 # fixme: howto implement this
368 die "drbd volume_resize is not implemented";
14770890
DM
369
370 #my $cmd = ['/sbin/lvextend', '-L', $size, $path];
371 #run_command($cmd, errmsg => "error resizing volume '$path'");
372
373 return 1;
374}
375
376sub volume_snapshot {
377 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
378
379 die "drbd snapshot is not implemented";
380}
381
382sub volume_snapshot_rollback {
383 my ($class, $scfg, $storeid, $volname, $snap) = @_;
384
385 die "drbd snapshot rollback is not implemented";
386}
387
388sub volume_snapshot_delete {
389 my ($class, $scfg, $storeid, $volname, $snap) = @_;
390
391 die "drbd snapshot delete is not implemented";
392}
393
394sub volume_has_feature {
395 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
396
397 my $features = {
398 copy => { base => 1, current => 1},
399 };
400
401 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
402 $class->parse_volname($volname);
403
404 my $key = undef;
405 if($snapname){
406 $key = 'snap';
407 }else{
408 $key = $isBase ? 'base' : 'current';
409 }
410 return 1 if $features->{$feature}->{$key};
411
412 return undef;
413}
414
4151;