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