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