]> git.proxmox.com Git - pve-storage.git/blob - PVE/Storage/NexentaPlugin.pm
nexenta: use import_lu on rollback
[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
73 sub nexenta_import_lu {
74 my ($scfg, $zvol) = @_;
75
76 nexenta_request($scfg, 'import_lu', 'scsidisk', "$scfg->{pool}/$zvol");
77 }
78
79 sub nexenta_create_zvol {
80 my ($scfg, $zvol, $size) = @_;
81
82 nexenta_request($scfg, 'create', 'zvol', "$scfg->{pool}/$zvol", "${size}KB",
83 $scfg->{blocksize}, 1);
84 }
85
86 sub nexenta_delete_zvol {
87 my ($scfg, $zvol) = @_;
88
89 nexenta_request($scfg, 'destroy', 'zvol', "$scfg->{pool}/$zvol", '-r');
90 }
91
92 sub nexenta_list_zvol {
93 my ($scfg) = @_;
94
95 my $zvols = nexenta_request($scfg, 'get_names', 'zvol', '');
96 return undef if !$zvols;
97
98 my $list = {};
99 foreach my $zvol (@$zvols) {
100 my @values = split('/', $zvol);
101
102 my $pool = $values[0];
103 my $image = $values[1];
104 my $owner;
105 if ($image =~ m/^(vm-(\d+)-\S+)$/) {
106 $owner = $2;
107 }
108
109 $list->{$pool}->{$image} = {
110 name => $image,
111 size => nexenta_get_zvol_size($scfg, $zvol),
112 format => 'raw',
113 vmid => $owner
114 };
115 }
116
117 return $list;
118 }
119
120 # Configuration
121
122 sub type {
123 return 'nexenta';
124 }
125
126 sub plugindata {
127 return {
128 content => [ {images => 1}, { images => 1 }],
129 };
130 }
131
132 sub properties {
133 return {
134 login => {
135 description => "login",
136 type => 'string',
137 },
138 password => {
139 description => "password",
140 type => 'string',
141 },
142 blocksize => {
143 description => "block size",
144 type => 'string',
145 },
146 ssl => {
147 description => "ssl",
148 type => 'boolean',
149 },
150 };
151 }
152
153 sub options {
154 return {
155 nodes => { optional => 1 },
156 disable => { optional => 1 },
157 target => { fixed => 1 },
158 portal => { fixed => 1 },
159 login => { fixed => 1 },
160 password => { fixed => 1 },
161 pool => { fixed => 1 },
162 blocksize => { fixed => 1 },
163 ssl => { optional => 1 },
164 content => { optional => 1 },
165 };
166 }
167
168 # Storage implementation
169
170 sub parse_volname {
171 my ($class, $volname) = @_;
172
173 if ($volname =~ m/^(vm-(\d+)-\S+)$/) {
174 return ('images', $1, $2);
175 }
176
177 die "unable to parse nexenta 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 my $tn = "vm-$vmid-disk-$i";
215 if (!defined ($volumes->{$nexentapool}->{$tn})) {
216 $name = $tn;
217 last;
218 }
219 }
220 }
221
222 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
223 if !$name;
224
225 nexenta_create_zvol($scfg, $name, $size);
226 nexenta_create_lu($scfg, $name);
227 nexenta_add_lun_mapping_entry($scfg, $name);
228
229 return $name;
230 }
231
232 sub free_image {
233 my ($class, $storeid, $scfg, $volname) = @_;
234
235 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
236
237 nexenta_delete_lu($scfg, $name);
238 nexenta_delete_zvol($scfg, $name);
239
240 return undef;
241 }
242
243 sub list_images {
244 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
245
246 $cache->{nexenta} = nexenta_list_zvol($scfg) if !$cache->{nexenta};
247 my $nexentapool = $scfg->{pool};
248 my $res = [];
249 if (my $dat = $cache->{nexenta}->{$nexentapool}) {
250 foreach my $image (keys %$dat) {
251
252 my $volname = $dat->{$image}->{name};
253
254 my $volid = "$storeid:$volname";
255
256 my $owner = $dat->{$volname}->{vmid};
257 if ($vollist) {
258 my $found = grep { $_ eq $volid } @$vollist;
259 next if !$found;
260 } else {
261 next if defined ($vmid) && ($owner ne $vmid);
262 }
263
264 my $info = $dat->{$volname};
265 $info->{volid} = $volid;
266
267 push @$res, $info;
268
269 }
270 }
271
272 return $res;
273 }
274
275 sub status {
276 my ($class, $storeid, $scfg, $cache) = @_;
277
278 my $total = 0;
279 my $free = 0;
280 my $used = 0;
281 my $active = 1;
282 return ($total,$free,$used,$active);
283
284 return undef;
285 }
286
287 sub activate_storage {
288 my ($class, $storeid, $scfg, $cache) = @_;
289 return 1;
290 }
291
292 sub deactivate_storage {
293 my ($class, $storeid, $scfg, $cache) = @_;
294 return 1;
295 }
296
297 sub activate_volume {
298 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
299 return 1;
300 }
301
302 sub deactivate_volume {
303 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
304 return 1;
305 }
306
307 sub volume_size_info {
308 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
309
310 return nexenta_get_zvol_size($scfg, "$scfg->{pool}/$volname"),
311 }
312
313 sub volume_resize {
314 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
315
316 nexenta_request($scfg, 'set_child_prop', 'zvol', "$scfg->{pool}/$volname", 'volsize', ($size/1024) . 'KB');
317 }
318
319 sub volume_snapshot {
320 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
321
322 nexenta_request($scfg, 'create_snapshot', 'zvol', "$scfg->{pool}/$volname", $snap, '');
323 }
324
325 sub volume_snapshot_rollback {
326 my ($class, $scfg, $storeid, $volname, $snap) = @_;
327
328 nexenta_delete_lu($scfg, $volname);
329
330 nexenta_request($scfg, 'rollback', 'snapshot', "$scfg->{pool}/$volname\@$snap", '');
331
332 nexenta_import_lu($scfg, $volname);
333
334 nexenta_add_lun_mapping_entry($scfg, $volname);
335 }
336
337 sub volume_snapshot_delete {
338 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
339
340 nexenta_request($scfg, 'destroy', 'snapshot', "$scfg->{pool}/$volname\@$snap", '');
341 }
342
343 1;