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