1 package PVE
::Storage
::DRBDPlugin
;
3 # FIXME: remove with 7.0
9 # FIXME remove libnet-dbus-perl dependency once this gets removed
12 use PVE
::Tools
qw(run_command trim);
14 use PVE
::Storage
::Plugin
;
15 use PVE
::JSONSchema
qw(get_standard_option);
17 use base
qw(PVE::Storage::Plugin);
21 my $default_redundancy = 2;
29 content
=> [ {images
=> 1, rootdir
=> 1}, { images
=> 1 }],
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.",
40 default => $default_redundancy,
47 redundancy
=> { optional
=> 1 },
48 content
=> { optional
=> 1 },
49 nodes
=> { optional
=> 1 },
50 disable
=> { optional
=> 1 },
51 bwlimit
=> { optional
=> 1 },
60 return $scfg->{redundancy
} || $default_redundancy;
63 sub connect_drbdmanage_service
{
65 my $bus = Net
::DBus-
>system;
67 my $service = $bus->get_service("org.drbd.drbdmanaged");
69 my $hdl = $service->get_object("/interface", "org.drbd.drbdmanaged");
77 die "got undefined drbd result\n" if !$rc;
79 # Messages for return codes 1 to 99 are not considered an error.
80 foreach my $res (@$rc) {
81 my ($code, $format, $details) = @$res;
86 if (defined($format)) {
88 push @args, $details->{$1} // ""
89 while $format =~ s
,\
%\((\w
+)\
),%,;
91 $msg = sprintf($format, @args);
94 $msg = "drbd error: got error code $code";
98 die "drbd error: $msg\n";
104 sub drbd_list_volumes
{
107 $hdl = connect_drbdmanage_service
() if !$hdl;
109 my ($rc, $res) = $hdl->list_volumes([], 0, {}, []);
114 foreach my $entry (@$res) {
115 my ($volname, $properties, $vol_list) = @$entry;
117 next if $volname !~ m/^vm-(\d+)-/;
120 # fixme: we always use volid 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;
129 $volumes->{$volname} = { format
=> 'raw', size
=> $size,
136 # Storage implementation
139 my ($class, $volname) = @_;
141 if ($volname =~ m/^(vm-(\d+)-[a-z][a-z0-9\-\_\.]*[a-z0-9]+)$/) {
142 return ('images', $1, $2, undef, undef, undef, 'raw');
145 die "unable to parse lvm volume name '$volname'\n";
148 sub filesystem_path
{
149 my ($class, $scfg, $volname, $snapname) = @_;
151 die "drbd snapshot is not implemented\n" if defined($snapname);
153 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
155 # fixme: always use volid 0?
156 my $path = "/dev/drbd/by-res/$volname/0";
158 return wantarray ?
($path, $vmid, $vtype) : $path;
162 my ($class, $storeid, $scfg, $volname) = @_;
164 die "can't create base images in drbd storage\n";
168 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
170 die "can't clone images in drbd storage\n";
174 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
176 die "unsupported format '$fmt'" if $fmt ne 'raw';
178 die "illegal name '$name' - should be 'vm-$vmid-*'\n"
179 if defined($name) && $name !~ m/^vm-$vmid-/;
181 my $hdl = connect_drbdmanage_service
();
182 my $volumes = drbd_list_volumes
($hdl);
183 my $disk_list = [ keys %$volumes ];
185 die "volume '$name' already exists\n" if defined($name) && $volumes->{$name};
186 $name //= PVE
::Storage
::Plugin
::get_next_vm_diskname
($disk_list, $storeid, $vmid, undef, $scfg);
188 my ($rc, $res) = $hdl->create_resource($name, {});
191 ($rc, $res) = $hdl->create_volume($name, $size, {});
194 ($rc, $res) = $hdl->set_drbdsetup_props(
196 target
=> "resource",
199 'allow-two-primaries' => 'yes',
203 my $redundancy = get_redundancy
($scfg);;
205 ($rc, $res) = $hdl->auto_deploy($name, $redundancy, 0, 0);
212 my ($class, $storeid, $scfg, $volname, $isBase) = @_;
214 my $hdl = connect_drbdmanage_service
();
215 my ($rc, $res) = $hdl->remove_resource($volname, 0);
222 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
224 my $vgname = $scfg->{vgname
};
226 $cache->{drbd_volumes
} = drbd_list_volumes
() if !$cache->{drbd_volumes
};
230 my $dat = $cache->{drbd_volumes
};
232 foreach my $volname (keys %$dat) {
234 my $owner = $dat->{$volname}->{vmid
};
236 my $volid = "$storeid:$volname";
239 my $found = grep { $_ eq $volid } @$vollist;
242 next if defined ($vmid) && ($owner ne $vmid);
245 my $info = $dat->{$volname};
246 $info->{volid
} = $volid;
255 my ($class, $storeid, $scfg, $cache) = @_;
257 my ($total, $avail, $used);
260 my $hdl = connect_drbdmanage_service
();
261 my $redundancy = get_redundancy
($scfg);;
262 my ($rc, $free_space, $total_space) = $hdl->cluster_free_query($redundancy);
265 $avail = $free_space*1024;
266 $total = $total_space*1024;
267 $used = $total - $avail;
272 # assume storage if offline
277 return ($total, $avail, $used, 1);
280 sub activate_storage
{
281 my ($class, $storeid, $scfg, $cache) = @_;
286 sub deactivate_storage
{
287 my ($class, $storeid, $scfg, $cache) = @_;
292 sub activate_volume
{
293 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
295 die "Snapshot not implemented on DRBD\n" if $snapname;
297 my $path = $class->path($scfg, $volname);
299 my $hdl = connect_drbdmanage_service
();
300 my $nodename = PVE
::INotify
::nodename
();
301 my ($rc, $res) = $hdl->list_assignments([$nodename], [$volname], 0, {}, []);
304 # assignment already exists?
305 return undef if @$res;
307 # create diskless assignment
308 ($rc, $res) = $hdl->assign($nodename, $volname, { diskless
=> 'true' });
311 # wait until device is accessible
312 my $print_warning = 1;
313 my $max_wait_time = 20;
314 for (my $i = 0;; $i++) {
317 last if system("dd if=$path of=/dev/null bs=512 count=1 >/dev/null 2>&1") == 0;
319 # correct, but does not work?
320 ($rc, $res) = $hdl->list_assignments([$nodename], [$volname], 0, { "cstate:deploy" => "true" }, []);
322 my $len = scalar(@$res);
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;
334 sub deactivate_volume
{
335 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
337 die "Snapshot not implemented on DRBD\n" if $snapname;
339 return undef; # fixme: should we unassign ?
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" }, []);
348 my ($rc, $res) = $hdl->unassign($nodename, $volname,0);
356 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
358 $size = ($size/1024/1024) . "M";
360 my $path = $class->path($scfg, $volname);
362 # fixme: howto implement this
363 die "drbd volume_resize is not implemented";
365 #my $cmd = ['/sbin/lvextend', '-L', $size, $path];
366 #run_command($cmd, errmsg => "error resizing volume '$path'");
371 sub volume_snapshot
{
372 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
374 die "drbd snapshot is not implemented";
377 sub volume_snapshot_rollback
{
378 my ($class, $scfg, $storeid, $volname, $snap) = @_;
380 die "drbd snapshot rollback is not implemented";
383 sub volume_snapshot_delete
{
384 my ($class, $scfg, $storeid, $volname, $snap) = @_;
386 die "drbd snapshot delete is not implemented";
389 sub volume_has_feature
{
390 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
393 copy
=> { base
=> 1, current
=> 1},
396 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
397 $class->parse_volname($volname);
403 $key = $isBase ?
'base' : 'current';
405 return 1 if $features->{$feature}->{$key};