]>
Commit | Line | Data |
---|---|---|
40cd7d27 AD |
1 | package PVE::Storage::NexentaPlugin; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use IO::File; | |
6 | use HTTP::Request; | |
7 | use LWP::UserAgent; | |
8 | use MIME::Base64; | |
9 | use JSON; | |
10 | use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach); | |
11 | use PVE::Storage::Plugin; | |
12 | use PVE::JSONSchema qw(get_standard_option); | |
13 | ||
14 | use base qw(PVE::Storage::Plugin); | |
15 | ||
2677f623 DM |
16 | sub nexenta_request { |
17 | my ($scfg, $json) = @_; | |
40cd7d27 | 18 | |
120ca009 | 19 | my $uri = ( $scfg->{ssl} ? "https" : "http" ) . "://" . $scfg->{portal} . ":2000/rest/nms/"; |
2677f623 | 20 | my $req = HTTP::Request->new( 'POST', $uri ); |
40cd7d27 | 21 | |
2677f623 DM |
22 | $req->header( 'Content-Type' => 'application/json' ); |
23 | $req->content( $json ); | |
24 | my $token = encode_base64("$scfg->{login}:$scfg->{password}"); | |
25 | $req->header( Authorization => "Basic $token" ); | |
40cd7d27 | 26 | |
2677f623 DM |
27 | my $ua = LWP::UserAgent->new; # You might want some options here |
28 | my $res = $ua->request($req); | |
29 | if (!$res->is_success) { | |
30 | die $res->content; | |
31 | } | |
120ca009 AD |
32 | my $obj = eval { from_json($res->content); }; |
33 | die "JSON not valid. Content: " . $res->content if ($@); | |
34 | die "Nexenta API Error: " . $obj->{error}->{message} if $obj->{error}->{message}; | |
2677f623 DM |
35 | return $obj->{result} if $obj->{result}; |
36 | return 1; | |
37 | } | |
40cd7d27 AD |
38 | |
39 | sub nexenta_list_lun_mapping_entries { | |
2677f623 | 40 | my ($zvol, $scfg) = @_; |
40cd7d27 | 41 | |
2677f623 DM |
42 | my $json = '{"method": "list_lun_mapping_entries","object" : "scsidisk","params": ["'.$scfg->{pool}.'/'.$zvol.'"]}'; |
43 | my $map = nexenta_request($scfg,$json); | |
44 | return $map if $map; | |
45 | return undef; | |
40cd7d27 AD |
46 | } |
47 | ||
48 | sub nexenta_add_lun_mapping_entry { | |
2677f623 | 49 | my ($zvol, $scfg) = @_; |
40cd7d27 | 50 | |
2677f623 | 51 | my $json = '{"method": "add_lun_mapping_entry","object" : "scsidisk","params": ["'.$scfg->{pool}.'/'.$zvol.'",{"target_group": "All"}]}'; |
40cd7d27 | 52 | |
120ca009 | 53 | nexenta_request($scfg, $json); |
2677f623 | 54 | return 1; |
40cd7d27 AD |
55 | } |
56 | ||
40cd7d27 | 57 | sub nexenta_delete_lu { |
2677f623 | 58 | my ($zvol, $scfg) = @_; |
40cd7d27 | 59 | |
2677f623 | 60 | my $json = '{"method": "delete_lu","object" : "scsidisk","params": ["'.$scfg->{pool}.'/'.$zvol.'"]}'; |
120ca009 | 61 | nexenta_request($scfg, $json); |
2677f623 | 62 | return 1; |
40cd7d27 AD |
63 | } |
64 | ||
65 | sub nexenta_create_lu { | |
66 | my ($zvol, $scfg) = @_; | |
67 | ||
2677f623 | 68 | my $json = '{"method": "create_lu","object" : "scsidisk","params": ["'.$scfg->{pool}.'/'.$zvol.'",{}]}'; |
40cd7d27 | 69 | |
120ca009 | 70 | nexenta_request($scfg, $json); |
2677f623 | 71 | return 1; |
40cd7d27 AD |
72 | } |
73 | ||
74 | sub nexenta_create_zvol { | |
2677f623 | 75 | my ($zvol, $size, $scfg) = @_; |
40cd7d27 | 76 | |
2677f623 DM |
77 | my $blocksize = $scfg->{blocksize}; |
78 | my $nexentapool = $scfg->{pool}; | |
40cd7d27 | 79 | |
2677f623 | 80 | my $json = '{"method": "create","object" : "zvol","params": ["'.$nexentapool.'/'.$zvol.'", "'.$size.'KB", "'.$blocksize.'", "1"]}'; |
40cd7d27 | 81 | |
120ca009 | 82 | nexenta_request($scfg, $json); |
2677f623 | 83 | return 1; |
40cd7d27 | 84 | } |
2677f623 | 85 | |
40cd7d27 AD |
86 | sub nexenta_delete_zvol { |
87 | my ($zvol, $scfg) = @_; | |
40cd7d27 | 88 | |
2677f623 DM |
89 | sleep 5; |
90 | my $json = '{"method": "destroy","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$zvol.'", ""]}'; | |
120ca009 | 91 | nexenta_request($scfg, $json); |
2677f623 | 92 | return 1; |
40cd7d27 | 93 | } |
40cd7d27 AD |
94 | |
95 | sub nexenta_list_zvol { | |
96 | my ($scfg) = @_; | |
97 | ||
2677f623 DM |
98 | my $json = '{"method": "get_names","object" : "zvol","params": [""]}'; |
99 | my $volumes = {}; | |
40cd7d27 | 100 | |
2677f623 DM |
101 | my $zvols = nexenta_request($scfg, $json); |
102 | return undef if !$zvols; | |
40cd7d27 | 103 | |
2677f623 | 104 | my $list = {}; |
40cd7d27 | 105 | |
2677f623 DM |
106 | foreach my $zvol (@$zvols) { |
107 | my @values = split('/', $zvol); | |
108 | #$volumes->{$values[0]}->{$values[1]}->{volname} = $values[1]; | |
40cd7d27 | 109 | |
2677f623 DM |
110 | my $pool = $values[0]; |
111 | my $image = $values[1]; | |
112 | my $owner; | |
113 | if ($image =~ m/^(vm-(\d+)-\S+)$/) { | |
114 | $owner = $2; | |
40cd7d27 AD |
115 | } |
116 | ||
2677f623 DM |
117 | $list->{$pool}->{$image} = { |
118 | name => $image, | |
119 | size => "", | |
120 | vmid => $owner | |
121 | }; | |
40cd7d27 | 122 | |
2677f623 | 123 | } |
40cd7d27 | 124 | |
2677f623 | 125 | return $list; |
40cd7d27 AD |
126 | } |
127 | ||
2677f623 | 128 | # Configuration |
40cd7d27 AD |
129 | |
130 | sub type { | |
131 | return 'nexenta'; | |
132 | } | |
133 | ||
134 | sub plugindata { | |
135 | return { | |
136 | content => [ {images => 1}, { images => 1 }], | |
137 | }; | |
138 | } | |
139 | ||
40cd7d27 AD |
140 | sub properties { |
141 | return { | |
40cd7d27 AD |
142 | login => { |
143 | description => "login", | |
144 | type => 'string', | |
145 | }, | |
146 | password => { | |
147 | description => "password", | |
148 | type => 'string', | |
149 | }, | |
150 | blocksize => { | |
151 | description => "block size", | |
152 | type => 'string', | |
153 | }, | |
120ca009 AD |
154 | ssl => { |
155 | description => "ssl", | |
156 | type => 'boolean', | |
157 | }, | |
40cd7d27 AD |
158 | }; |
159 | } | |
160 | ||
161 | sub options { | |
162 | return { | |
4f01611d AD |
163 | nodes => { optional => 1 }, |
164 | disable => { optional => 1 }, | |
40cd7d27 AD |
165 | target => { fixed => 1 }, |
166 | portal => { fixed => 1 }, | |
167 | login => { fixed => 1 }, | |
168 | password => { fixed => 1 }, | |
169 | pool => { fixed => 1 }, | |
170 | blocksize => { fixed => 1 }, | |
0f94c1c9 | 171 | ssl => { optional => 1 }, |
40cd7d27 AD |
172 | content => { optional => 1 }, |
173 | }; | |
120ca009 | 174 | |
40cd7d27 AD |
175 | } |
176 | ||
177 | # Storage implementation | |
178 | ||
179 | sub parse_volname { | |
180 | my ($class, $volname) = @_; | |
181 | ||
40cd7d27 AD |
182 | if ($volname =~ m/^(vm-(\d+)-\S+)$/) { |
183 | return ('images', $1, $2); | |
184 | } | |
185 | ||
186 | return('images',$volname,''); | |
187 | #die "unable to parse lvm volume name '$volname'\n"; | |
188 | } | |
189 | ||
190 | sub path { | |
191 | my ($class, $scfg, $volname) = @_; | |
192 | ||
193 | my ($vtype, $name, $vmid) = $class->parse_volname($volname); | |
194 | ||
195 | my $target = $scfg->{target}; | |
196 | my $portal = $scfg->{portal}; | |
197 | ||
198 | my $map = nexenta_list_lun_mapping_entries($name,$scfg); | |
199 | die "could not find lun number" if !$map; | |
200 | my $lun = @$map[0]->{lun}; | |
034e4411 AD |
201 | $lun =~ m/^(\d+)$/ or die "lun is not OK\n"; |
202 | $lun = $1; | |
40cd7d27 AD |
203 | my $path = "iscsi://$portal/$target/$lun"; |
204 | ||
205 | return ($path, $vmid, $vtype); | |
206 | } | |
207 | ||
208 | ||
209 | sub alloc_image { | |
210 | my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_; | |
211 | ||
212 | die "unsupported format '$fmt'" if $fmt ne 'raw'; | |
213 | ||
2677f623 | 214 | die "illegal name '$name' - sould be 'vm-$vmid-*'\n" |
40cd7d27 AD |
215 | if $name && $name !~ m/^vm-$vmid-/; |
216 | ||
40cd7d27 AD |
217 | my $nexentapool = $scfg->{'pool'}; |
218 | ||
219 | if (!$name) { | |
40cd7d27 AD |
220 | my $volumes = nexenta_list_zvol($scfg); |
221 | die "unable de get zvol list" if !$volumes; | |
222 | ||
223 | for (my $i = 1; $i < 100; $i++) { | |
224 | ||
225 | my $tn = "vm-$vmid-disk-$i"; | |
226 | if (!defined ($volumes->{$nexentapool}->{$tn})) { | |
227 | $name = $tn; | |
228 | last; | |
229 | } | |
230 | } | |
231 | } | |
232 | ||
233 | die "unable to allocate an image name for VM $vmid in storage '$storeid'\n" | |
234 | if !$name; | |
235 | ||
120ca009 | 236 | eval { nexenta_create_zvol($name, $size, $scfg); }; |
40cd7d27 | 237 | sleep 1; |
120ca009 | 238 | eval { nexenta_create_lu($name, $scfg); }; |
40cd7d27 | 239 | sleep 1; |
120ca009 | 240 | nexenta_add_lun_mapping_entry($name, $scfg); |
40cd7d27 AD |
241 | |
242 | return $name; | |
243 | } | |
244 | ||
245 | sub free_image { | |
246 | my ($class, $storeid, $scfg, $volname) = @_; | |
247 | ||
248 | my ($vtype, $name, $vmid) = $class->parse_volname($volname); | |
249 | ||
120ca009 | 250 | eval { nexenta_delete_lu($name, $scfg); }; |
40cd7d27 | 251 | sleep 5; |
120ca009 | 252 | nexenta_delete_zvol($name, $scfg); |
40cd7d27 AD |
253 | |
254 | ||
255 | return undef; | |
256 | } | |
257 | ||
258 | sub list_images { | |
259 | my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_; | |
260 | ||
261 | $cache->{nexenta} = nexenta_list_zvol($scfg) if !$cache->{nexenta}; | |
262 | my $nexentapool = $scfg->{pool}; | |
263 | my $res = []; | |
264 | if (my $dat = $cache->{nexenta}->{$nexentapool}) { | |
265 | foreach my $image (keys %$dat) { | |
266 | ||
267 | my $volname = $dat->{$image}->{name}; | |
268 | ||
269 | my $volid = "$storeid:$volname"; | |
270 | ||
271 | ||
272 | my $owner = $dat->{$volname}->{vmid}; | |
273 | if ($vollist) { | |
274 | my $found = grep { $_ eq $volid } @$vollist; | |
275 | next if !$found; | |
276 | } else { | |
277 | next if defined ($vmid) && ($owner ne $vmid); | |
278 | } | |
279 | ||
280 | my $info = $dat->{$volname}; | |
281 | $info->{volid} = $volid; | |
282 | ||
283 | push @$res, $info; | |
284 | ||
285 | } | |
286 | } | |
287 | ||
288 | return $res; | |
289 | } | |
290 | ||
291 | sub status { | |
292 | my ($class, $storeid, $scfg, $cache) = @_; | |
293 | ||
294 | my $total = 0; | |
295 | my $free = 0; | |
296 | my $used = 0; | |
297 | my $active = 1; | |
298 | return ($total,$free,$used,$active); | |
299 | ||
300 | return undef; | |
301 | } | |
302 | ||
303 | sub activate_storage { | |
304 | my ($class, $storeid, $scfg, $cache) = @_; | |
305 | return 1; | |
306 | } | |
307 | ||
308 | sub deactivate_storage { | |
309 | my ($class, $storeid, $scfg, $cache) = @_; | |
310 | return 1; | |
311 | } | |
312 | ||
313 | sub activate_volume { | |
314 | my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_; | |
315 | return 1; | |
316 | } | |
317 | ||
318 | sub deactivate_volume { | |
319 | my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_; | |
320 | return 1; | |
321 | } | |
322 | ||
c3013a8b AD |
323 | sub volume_size_info { |
324 | my ($class, $scfg, $storeid, $volname, $timeout) = @_; | |
325 | ||
60301ee0 AD |
326 | my $json = '{"method": "get_child_prop","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$volname.'", "size_bytes"]}'; |
327 | my $size = nexenta_request($scfg, $json); | |
328 | return $size; | |
c3013a8b AD |
329 | } |
330 | ||
69971d8b AD |
331 | sub volume_resize { |
332 | my ($class, $scfg, $storeid, $volname, $size, $running) = @_; | |
333 | ||
334 | my $json = '{"method": "set_child_prop","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$volname.'", "volsize", "'.($size/1024).'KB"]}'; | |
120ca009 | 335 | nexenta_request($scfg, $json); |
69971d8b AD |
336 | return undef; |
337 | } | |
338 | ||
5223286c AD |
339 | sub volume_snapshot { |
340 | my ($class, $scfg, $storeid, $volname, $snap, $running) = @_; | |
341 | ||
342 | my $json = '{"method": "create_snapshot","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$volname.'", "'.$snap.'", ""]}'; | |
343 | nexenta_request($scfg, $json); | |
344 | return undef; | |
345 | } | |
346 | ||
4c6c6423 AD |
347 | sub volume_snapshot_rollback { |
348 | my ($class, $scfg, $storeid, $volname, $snap) = @_; | |
349 | ||
350 | eval { nexenta_delete_lu($volname, $scfg); }; | |
351 | ||
352 | my $json = '{"method": "rollback","object" : "snapshot","params": ["'.$scfg->{pool}.'/'.$volname.'@'.$snap.'", ""]}'; | |
353 | nexenta_request($scfg, $json); | |
354 | ||
355 | eval { nexenta_create_lu($volname, $scfg); }; | |
356 | ||
357 | nexenta_add_lun_mapping_entry($volname, $scfg); | |
358 | } | |
359 | ||
5d8f5e22 AD |
360 | sub volume_snapshot_delete { |
361 | my ($class, $scfg, $storeid, $volname, $snap, $running) = @_; | |
362 | ||
363 | my $json = '{"method": "destroy","object" : "snapshot","params": ["'.$scfg->{pool}.'/'.$volname.'@'.$snap.'"]}'; | |
364 | nexenta_request($scfg, $json); | |
365 | } | |
366 | ||
40cd7d27 | 367 | 1; |