]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/ZFSDirPlugin.pm
zfs: remove duplicate $object definition
[pve-storage.git] / PVE / Storage / ZFSDirPlugin.pm
CommitLineData
5bb8e010
DM
1package PVE::Storage::ZFSDirPlugin;
2
3use strict;
4use warnings;
5use IO::File;
6use POSIX;
7use PVE::Tools qw(run_command);
8use PVE::Storage::Plugin;
9
10
11use base qw(PVE::Storage::Plugin);
12
5bb8e010
DM
13sub type {
14 return 'zfsdir';
15}
16
17sub plugindata {
18 return {
19 content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
20 { images => 1 }],
21 };
22}
23
7730694e
DM
24sub properties {
25 return {
26 blocksize => {
27 description => "block size",
28 type => 'string',
29 },
30 sparse => {
31 description => "use sparse volumes",
32 type => 'boolean',
33 },
34 };
35}
36
5bb8e010
DM
37sub options {
38 return {
39 path => { fixed => 1 },
7730694e
DM
40 pool => { fixed => 1 },
41 blocksize => { optional => 1 },
42 sparse => { optional => 1 },
43 nodes => { optional => 1 },
5bb8e010
DM
44 disable => { optional => 1 },
45 maxfiles => { optional => 1 },
46 content => { optional => 1 },
47 };
48}
49
7730694e
DM
50# static zfs helper methods
51
060ef890
DM
52sub zfs_parse_size {
53 my ($text) = @_;
54
55 return 0 if !$text;
56
57 if ($text =~ m/^(\d+(\.\d+)?)([TGMK])?$/) {
58
59 my ($size, $reminder, $unit) = ($1, $2, $3);
60
61 if ($unit) {
62 if ($unit eq 'K') {
63 $size *= 1024;
64 } elsif ($unit eq 'M') {
65 $size *= 1024*1024;
66 } elsif ($unit eq 'G') {
67 $size *= 1024*1024*1024;
68 } elsif ($unit eq 'T') {
69 $size *= 1024*1024*1024*1024;
70 } else {
71 die "got unknown zfs size unit '$unit'\n";
72 }
73 }
74
75 if ($reminder) {
76 $size = ceil($size);
77 }
78
79 return $size;
80
81 }
82
83 warn "unable to parse zfs size '$text'\n";
84
85 return 0;
86}
87
7730694e
DM
88sub zfs_parse_zvol_list {
89 my ($text) = @_;
90
91 my $list = ();
92
93 return $list if !$text;
94
95 my @lines = split /\n/, $text;
96 foreach my $line (@lines) {
97 if ($line =~ /^(.+)\s+([a-zA-Z0-9\.]+|\-)\s+(.+)$/) {
98 my $zvol = {};
99 my @parts = split /\//, $1;
100 my $name = pop @parts;
101 my $pool = join('/', @parts);
102
103 if ($pool !~ /^rpool$/) {
104 next unless $name =~ m!^(\w+)-(\d+)-(\w+)-(\d+)$!;
105 $name = $pool . '/' . $name;
106 } else {
107 next;
108 }
109
110 $zvol->{pool} = $pool;
111 $zvol->{name} = $name;
112 $zvol->{size} = zfs_parse_size($2);
113 if ($3 !~ /^-$/) {
114 $zvol->{origin} = $3;
115 }
116 push @$list, $zvol;
117 }
118 }
119
120 return $list;
121}
122
123# virtual zfs methods (subclass can overwrite them)
124
125sub zfs_request {
126 my ($class, $scfg, $timeout, $method, @params) = @_;
127
128 $timeout = 5 if !$timeout;
129
130 my $cmd = [];
131
132 if ($method eq 'zpool_list') {
133 push @$cmd = 'zpool', 'list';
134 } else {
135 push @$cmd, 'zfs', $method;
136 }
137
138 push @$cmd, @params;
139
140 my $msg = '';
141
142 my $output = sub {
143 my $line = shift;
144 $msg .= "$line\n";
145 };
146
147 run_command($cmd, outfunc => $output, timeout => $timeout);
148
149 return $msg;
150}
151
152sub zfs_get_pool_stats {
153 my ($class, $scfg) = @_;
154
155 my $available = 0;
156 my $used = 0;
157
158 my $text = $class->zfs_request($scfg, undef, 'get', '-o', 'value', '-Hp',
159 'available,used', $scfg->{pool});
160
161 my @lines = split /\n/, $text;
162
163 if($lines[0] =~ /^(\d+)$/) {
164 $available = $1;
165 }
166
167 if($lines[1] =~ /^(\d+)$/) {
168 $used = $1;
169 }
170
171 return ($available, $used);
172}
173
174sub zfs_get_zvol_size {
175 my ($class, $scfg, $zvol) = @_;
176
177 my $text = $class->zfs_request($scfg, undef, 'get', '-Hp', 'volsize', "$scfg->{pool}/$zvol");
178
179 if ($text =~ /volsize\s(\d+)/) {
180 return $1;
181 }
182
183 die "Could not get zvol size";
184}
185
186sub zfs_create_zvol {
187 my ($class, $scfg, $zvol, $size) = @_;
188
189 my $cmd = ['create'];
190
191 push @$cmd, '-s' if $scfg->{sparse};
192
193 push @$cmd, '-b', $scfg->{blocksize} if $scfg->{blocksize};
194
195 push @$cmd, '-V', "${size}k", "$scfg->{pool}/$zvol";
196
197 $class->zfs_request($scfg, undef, @$cmd);
198}
199
200sub zfs_delete_zvol {
201 my ($class, $scfg, $zvol) = @_;
202
203 $class->zfs_request($scfg, undef, 'destroy', '-r', "$scfg->{pool}/$zvol");
204}
205
206sub zfs_list_zvol {
207 my ($class, $scfg) = @_;
208
209 my $text = $class->zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin', '-t', 'volume', '-Hr');
210 my $zvols = zfs_parse_zvol_list($text);
211 return undef if !$zvols;
212
213 my $list = ();
214 foreach my $zvol (@$zvols) {
215 my @values = split('/', $zvol->{name});
216
217 my $image = pop @values;
218 my $pool = join('/', @values);
219
220 next if $image !~ m/^((vm|base)-(\d+)-\S+)$/;
221 my $owner = $3;
222
223 my $parent = $zvol->{origin};
224 if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){
225 $parent = $1;
226 }
227
228 $list->{$pool}->{$image} = {
229 name => $image,
230 size => $zvol->{size},
231 parent => $parent,
232 format => 'raw',
233 vmid => $owner
234 };
235 }
236
237 return $list;
238}
239
240sub zfs_find_free_diskname {
241 my ($class, $storeid, $scfg, $vmid) = @_;
242
243 my $name = undef;
244 my $volumes = $class->zfs_list_zvol($scfg);
245
246 my $disk_ids = {};
247 my $dat = $volumes->{$scfg->{pool}};
248
249 foreach my $image (keys %$dat) {
250 my $volname = $dat->{$image}->{name};
251 if ($volname =~ m/(vm|base)-$vmid-disk-(\d+)/){
252 $disk_ids->{$2} = 1;
253 }
254 }
255
256 for (my $i = 1; $i < 100; $i++) {
257 if (!$disk_ids->{$i}) {
258 return "vm-$vmid-disk-$i";
259 }
260 }
261
262 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n";
263}
264
b5e5f7e3
DM
265sub status {
266 my ($class, $storeid, $scfg, $cache) = @_;
267
268 my $total = 0;
269 my $free = 0;
270 my $used = 0;
271 my $active = 0;
272
273 eval {
274 ($free, $used) = $class->zfs_get_pool_stats($scfg);
275 $active = 1;
276 $total = $free + $used;
277 };
278 warn $@ if $@;
279
280 return ($total, $free, $used, $active);
281}
282
283sub volume_size_info {
284 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
285
286 return $class->zfs_get_zvol_size($scfg, $volname);
287}
288
289sub volume_snapshot {
290 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
291
292 $class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$volname\@$snap");
293}
294
295sub volume_snapshot_delete {
296 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
297
298 $class->zfs_request($scfg, undef, 'destroy', "$scfg->{pool}/$volname\@$snap");
299}
300
5bb8e010
DM
301
3021;