]>
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 }, | |
120ca009 | 171 | ssl => { fixed => 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}; | |
201 | ||
40cd7d27 AD |
202 | my $path = "iscsi://$portal/$target/$lun"; |
203 | ||
204 | return ($path, $vmid, $vtype); | |
205 | } | |
206 | ||
207 | ||
208 | sub alloc_image { | |
209 | my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_; | |
210 | ||
211 | die "unsupported format '$fmt'" if $fmt ne 'raw'; | |
212 | ||
2677f623 | 213 | die "illegal name '$name' - sould be 'vm-$vmid-*'\n" |
40cd7d27 AD |
214 | if $name && $name !~ m/^vm-$vmid-/; |
215 | ||
40cd7d27 AD |
216 | my $nexentapool = $scfg->{'pool'}; |
217 | ||
218 | if (!$name) { | |
40cd7d27 AD |
219 | my $volumes = nexenta_list_zvol($scfg); |
220 | die "unable de get zvol list" if !$volumes; | |
221 | ||
222 | for (my $i = 1; $i < 100; $i++) { | |
223 | ||
224 | my $tn = "vm-$vmid-disk-$i"; | |
225 | if (!defined ($volumes->{$nexentapool}->{$tn})) { | |
226 | $name = $tn; | |
227 | last; | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | die "unable to allocate an image name for VM $vmid in storage '$storeid'\n" | |
233 | if !$name; | |
234 | ||
120ca009 | 235 | eval { nexenta_create_zvol($name, $size, $scfg); }; |
40cd7d27 | 236 | sleep 1; |
120ca009 | 237 | eval { nexenta_create_lu($name, $scfg); }; |
40cd7d27 | 238 | sleep 1; |
120ca009 | 239 | nexenta_add_lun_mapping_entry($name, $scfg); |
40cd7d27 AD |
240 | |
241 | return $name; | |
242 | } | |
243 | ||
244 | sub free_image { | |
245 | my ($class, $storeid, $scfg, $volname) = @_; | |
246 | ||
247 | my ($vtype, $name, $vmid) = $class->parse_volname($volname); | |
248 | ||
120ca009 | 249 | eval { nexenta_delete_lu($name, $scfg); }; |
40cd7d27 | 250 | sleep 5; |
120ca009 | 251 | nexenta_delete_zvol($name, $scfg); |
40cd7d27 AD |
252 | |
253 | ||
254 | return undef; | |
255 | } | |
256 | ||
257 | sub list_images { | |
258 | my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_; | |
259 | ||
260 | $cache->{nexenta} = nexenta_list_zvol($scfg) if !$cache->{nexenta}; | |
261 | my $nexentapool = $scfg->{pool}; | |
262 | my $res = []; | |
263 | if (my $dat = $cache->{nexenta}->{$nexentapool}) { | |
264 | foreach my $image (keys %$dat) { | |
265 | ||
266 | my $volname = $dat->{$image}->{name}; | |
267 | ||
268 | my $volid = "$storeid:$volname"; | |
269 | ||
270 | ||
271 | my $owner = $dat->{$volname}->{vmid}; | |
272 | if ($vollist) { | |
273 | my $found = grep { $_ eq $volid } @$vollist; | |
274 | next if !$found; | |
275 | } else { | |
276 | next if defined ($vmid) && ($owner ne $vmid); | |
277 | } | |
278 | ||
279 | my $info = $dat->{$volname}; | |
280 | $info->{volid} = $volid; | |
281 | ||
282 | push @$res, $info; | |
283 | ||
284 | } | |
285 | } | |
286 | ||
287 | return $res; | |
288 | } | |
289 | ||
290 | sub status { | |
291 | my ($class, $storeid, $scfg, $cache) = @_; | |
292 | ||
293 | my $total = 0; | |
294 | my $free = 0; | |
295 | my $used = 0; | |
296 | my $active = 1; | |
297 | return ($total,$free,$used,$active); | |
298 | ||
299 | return undef; | |
300 | } | |
301 | ||
302 | sub activate_storage { | |
303 | my ($class, $storeid, $scfg, $cache) = @_; | |
304 | return 1; | |
305 | } | |
306 | ||
307 | sub deactivate_storage { | |
308 | my ($class, $storeid, $scfg, $cache) = @_; | |
309 | return 1; | |
310 | } | |
311 | ||
312 | sub activate_volume { | |
313 | my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_; | |
314 | return 1; | |
315 | } | |
316 | ||
317 | sub deactivate_volume { | |
318 | my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_; | |
319 | return 1; | |
320 | } | |
321 | ||
c3013a8b AD |
322 | sub volume_size_info { |
323 | my ($class, $scfg, $storeid, $volname, $timeout) = @_; | |
324 | ||
60301ee0 AD |
325 | my $json = '{"method": "get_child_prop","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$volname.'", "size_bytes"]}'; |
326 | my $size = nexenta_request($scfg, $json); | |
327 | return $size; | |
c3013a8b AD |
328 | } |
329 | ||
69971d8b AD |
330 | sub volume_resize { |
331 | my ($class, $scfg, $storeid, $volname, $size, $running) = @_; | |
332 | ||
333 | my $json = '{"method": "set_child_prop","object" : "zvol","params": ["'.$scfg->{pool}.'/'.$volname.'", "volsize", "'.($size/1024).'KB"]}'; | |
120ca009 | 334 | nexenta_request($scfg, $json); |
69971d8b AD |
335 | return undef; |
336 | } | |
337 | ||
40cd7d27 | 338 | 1; |