]> git.proxmox.com Git - pve-storage.git/blame - PVE/API2/Disks/ZFS.pm
fix descriptions of api calls
[pve-storage.git] / PVE / API2 / Disks / ZFS.pm
CommitLineData
c84106ed
DC
1package PVE::API2::Disks::ZFS;
2
3use strict;
4use warnings;
5
6use PVE::Diskmanage;
7use PVE::JSONSchema qw(get_standard_option);
8use PVE::API2::Storage::Config;
9use PVE::Storage;
10use PVE::Tools qw(run_command lock_file trim);
11
12use PVE::RPCEnvironment;
13use PVE::RESTHandler;
14
15use base qw(PVE::RESTHandler);
16
17my $ZPOOL = '/sbin/zpool';
18my $ZFS = '/sbin/zfs';
19
20__PACKAGE__->register_method ({
21 name => 'index',
22 path => '',
23 method => 'GET',
24 proxyto => 'node',
25 protected => 1,
26 permissions => {
27 check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
28 },
29 description => "List Zpools.",
30 parameters => {
31 additionalProperties => 0,
32 properties => {
33 node => get_standard_option('pve-node'),
34 },
35 },
36 returns => {
37 type => 'array',
38 items => {
39 type => 'object',
40 properties => {
41 name => {
42 type => 'string',
43 description => "",
44 },
45 size => {
46 type => 'integer',
47 description => "",
48 },
49 alloc => {
50 type => 'integer',
51 description => "",
52 },
53 free => {
54 type => 'integer',
55 description => "",
56 },
57 frag => {
58 type => 'integer',
59 description => "",
60 },
61 dedup => {
62 type => 'number',
63 description => "",
64 },
65 health => {
66 type => 'string',
67 description => "",
68 },
69 },
70 },
71 links => [ { rel => 'child', href => "{name}" } ],
72 },
73 code => sub {
74 my ($param) = @_;
75
76 if (!-f $ZPOOL) {
77 die "zfsutils-linux not installed\n";
78 }
79
80 my $propnames = [qw(name size alloc free frag dedup health)];
81 my $numbers = {
82 size => 1,
83 alloc => 1,
84 free => 1,
85 frag => 1,
86 dedup => 1,
87 };
88
89 my $cmd = [$ZPOOL,'list', '-HpPLo', join(',', @$propnames)];
90
91 my $pools = [];
92
93 run_command($cmd, outfunc => sub {
94 my ($line) = @_;
95
96 my @props = split('\s+', trim($line));
97 my $pool = {};
98 for (my $i = 0; $i < scalar(@$propnames); $i++) {
99 if ($numbers->{$propnames->[$i]}) {
100 $pool->{$propnames->[$i]} = $props[$i] + 0;
101 } else {
102 $pool->{$propnames->[$i]} = $props[$i];
103 }
104 }
105
106 push @$pools, $pool;
107 });
108
109 return $pools;
110 }});
111
112sub preparetree {
113 my ($el) = @_;
114 delete $el->{lvl};
115 if ($el->{children} && scalar(@{$el->{children}})) {
116 $el->{leaf} = 0;
117 foreach my $child (@{$el->{children}}) {
118 preparetree($child);
119 }
120 } else {
121 $el->{leaf} = 1;
122 }
123}
124
125
126__PACKAGE__->register_method ({
127 name => 'detail',
128 path => '{name}',
129 method => 'GET',
130 proxyto => 'node',
131 protected => 1,
132 permissions => {
133 check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
134 },
135 description => "Get details about a zpool.",
136 parameters => {
137 additionalProperties => 0,
138 properties => {
139 node => get_standard_option('pve-node'),
140 name => get_standard_option('pve-storage-id'),
141 },
142 },
143 returns => {
144 type => 'object'
145 },
146 code => sub {
147 my ($param) = @_;
148
149 if (!-f $ZPOOL) {
150 die "zfsutils-linux not installed\n";
151 }
152
153 my $cmd = [$ZPOOL, 'status', '-P', $param->{name}];
154
155 my $pool = {
156 lvl => 0,
157 };
158 my $vdevs = [];
159
160 my $curfield;
161 my $config = 0;
162
163 my $stack = [$pool];
164 my $curlvl = 0;
165
166 run_command($cmd, outfunc => sub {
167 my ($line) = @_;
168
169 if ($line =~ m/^\s*(\S+): (\S+.*)$/) {
170 $curfield = $1;
171 $pool->{$curfield} = $2;
172
173 $config = 0 if $curfield eq 'errors';
174 } elsif (!$config && $line =~ m/^\s+(\S+.*)$/) {
175 $pool->{$curfield} .= " " . $1;
176 } elsif (!$config && $line =~ m/^\s*config:/) {
177 $config = 1;
178 } elsif ($config && $line =~ m/^(\s+)(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) {
179 my ($space, $name, $state, $read, $write, $cksum, $msg) = ($1, $2, $3, $4, $5, $6, $7);
180 if ($space =~ m/^\t(\s+)$/) {
181 my $lvl= length($1)/2; # two spaces per level
182 my $vdev = {
183 name => $name,
184 state => $state,
185 read => $read + 0,
186 write => $write + 0,
187 cksum => $cksum + 0,
188 msg => $msg,
189 lvl => $lvl,
190 };
191
192 my $cur = pop @$stack;
193
194 if ($lvl > $curlvl) {
195 $cur->{children} = [ $vdev ];
196 push @$stack, $cur;
197 push @$stack, $vdev;
198 } elsif ($lvl == $curlvl) {
199 $cur = pop @$stack;
200 push @{$cur->{children}}, $vdev;
201 push @$stack, $cur;
202 push @$stack, $vdev;
203 } else {
204 while ($lvl <= $cur->{lvl}) {
205 $cur = pop @$stack;
206 }
207 push @{$cur->{children}}, $vdev;
208 push @$stack, $cur;
209 push @$stack, $vdev;
210 }
211 $curlvl = $lvl;
212 }
213 }
214 });
215
216 # change treenodes for extjs tree
217 $pool->{name} = delete $pool->{pool};
218 preparetree($pool);
219
220 return $pool;
221 }});
222
223__PACKAGE__->register_method ({
224 name => 'create',
225 path => '',
226 method => 'POST',
227 proxyto => 'node',
228 protected => 1,
229 permissions => {
230 check => ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
231 },
fdc863c7 232 description => "Create a ZFS pool.",
c84106ed
DC
233 parameters => {
234 additionalProperties => 0,
235 properties => {
236 node => get_standard_option('pve-node'),
237 name => get_standard_option('pve-storage-id'),
238 raidlevel => {
239 type => 'string',
240 description => 'The RAID level to use, for single disk, use mirror.',
241 enum => ['mirror', 'raid10', 'raidz', 'raidz2', 'raidz3'],
242 },
243 devices => {
5be1a092 244 type => 'string', format => 'string-list',
fdc863c7 245 description => 'The block devices you want to create the zpool on.',
c84106ed
DC
246 },
247 ashift => {
248 type => 'integer',
249 minimum => 9,
250 maximum => 16,
251 optional => 1,
252 default => 12,
7d597888 253 description => 'Pool sector size exponent.',
c84106ed
DC
254 },
255 compression => {
256 type => 'string',
257 description => 'The compression algorithm to use.',
258 enum => ['on', 'off', 'gzip', 'lz4', 'lzjb', 'zle'],
259 optional => 1,
260 default => 'on',
261 },
262 add_storage => {
fdc863c7 263 description => "Configure storage using the zpool.",
c84106ed
DC
264 type => 'boolean',
265 optional => 1,
266 default => 0,
267 },
268 },
269 },
270 returns => { type => 'string' },
271 code => sub {
272 my ($param) = @_;
273
274 my $rpcenv = PVE::RPCEnvironment::get();
275 my $user = $rpcenv->get_user();
276
277 my $name = $param->{name};
278 my $devs = [PVE::Tools::split_list($param->{devices})];
279 my $raidlvl = $param->{raidlevel};
280 my $node = $param->{node};
281 my $ashift = $param->{ashift} // 12;
282 my $compression = $param->{compression} // 'on';
283
284 foreach my $dev (@$devs) {
285 $dev = PVE::Diskmanage::verify_blockdev_path($dev);
286 die "device $dev is already in use\n" if PVE::Diskmanage::disk_is_used($dev);
287 }
288
289 my $cfg = PVE::Storage::config();
290
291 if (my $scfg = PVE::Storage::storage_config($cfg, $name, 1)) {
292 die "storage ID '$name' already defined\n";
293 }
294
295 my $numdisks = scalar(@$devs);
296 my $mindisks = {
297 mirror => 1,
298 raid10 => 4,
299 raidz => 3,
300 raidz2 => 4,
301 raidz3 => 5,
302 };
303
304 # sanity checks
305 die "raid10 needs an even number of disks\n"
306 if $raidlvl eq 'raid10' && $numdisks % 2 != 0;
307
308 die "$raidlvl needs at least $mindisks->{$raidlvl} disks\n"
309 if $numdisks < $mindisks->{$raidlvl};
310
311 my $worker = sub {
312 lock_file('/run/lock/pve-diskmanage.lck', 10, sub {
313 # create zpool with desired raidlevel
314
315 my $cmd = [$ZPOOL, 'create', '-o', "ashift=$ashift", $name];
316
317 if ($raidlvl eq 'raid10') {
318 for (my $i = 0; $i < @$devs; $i+=2) {
319 push @$cmd, 'mirror', $devs->[$i], $devs->[$i+1];
320 }
321 } else {
322 push @$cmd, $raidlvl, @$devs;
323 }
324
325 print "# ", join(' ', @$cmd), "\n";
326 run_command($cmd);
327
328 $cmd = [$ZFS, 'set', "compression=$compression", $name];
329 print "# ", join(' ', @$cmd), "\n";
330 run_command($cmd);
331
332 if ($param->{add_storage}) {
333 my $storage_params = {
334 type => 'zfspool',
335 pool => $name,
336 storage => $name,
337 content => 'rootdir,images',
338 nodes => $node,
339 };
340
341 PVE::API2::Storage::Config->create($storage_params);
342 }
343 });
344 die $@ if $@;
345 };
346
347
348 return $rpcenv->fork_worker('zfscreate', $name, $user, $worker);
349 }});
350
3511;