]>
Commit | Line | Data |
---|---|---|
b6cf0a66 DM |
1 | package PVE::API2::Storage::Config; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::SafeSyslog; | |
7 | use PVE::Cluster qw(cfs_read_file cfs_write_file); | |
8 | use PVE::Storage; | |
9 | use HTTP::Status qw(:constants); | |
10 | use Storable qw(dclone); | |
11 | use PVE::JSONSchema qw(get_standard_option); | |
12 | ||
13 | use Data::Dumper; # fixme: remove | |
14 | ||
15 | use PVE::RESTHandler; | |
16 | ||
17 | use base qw(PVE::RESTHandler); | |
18 | ||
19 | my @ctypes = qw(images vztmpl iso backup); | |
20 | ||
21 | my $storage_type_enum = ['dir', 'nfs', 'lvm', 'iscsi']; | |
22 | ||
23 | my $api_storage_config = sub { | |
24 | my ($cfg, $storeid) = @_; | |
25 | ||
26 | my $scfg = dclone(PVE::Storage::storage_config ($cfg, $storeid)); | |
27 | $scfg->{storage} = $storeid; | |
28 | delete $scfg->{priority}; | |
29 | $scfg->{digest} = $cfg->{digest}; | |
30 | $scfg->{content} = PVE::Storage::content_hash_to_string($scfg->{content}); | |
31 | ||
32 | if ($scfg->{nodes}) { | |
33 | $scfg->{nodes} = join(',', keys(%{$scfg->{nodes}})); | |
34 | } | |
35 | ||
36 | return $scfg; | |
37 | }; | |
38 | ||
39 | __PACKAGE__->register_method ({ | |
40 | name => 'index', | |
41 | path => '', | |
42 | method => 'GET', | |
43 | description => "Storage index.", | |
44 | parameters => { | |
45 | additionalProperties => 0, | |
46 | properties => { | |
47 | type => { | |
48 | description => "Only list storage of specific type", | |
49 | type => 'string', | |
50 | enum => $storage_type_enum, | |
51 | optional => 1, | |
52 | }, | |
53 | ||
54 | }, | |
55 | }, | |
56 | returns => { | |
57 | type => 'array', | |
58 | items => { | |
59 | type => "object", | |
60 | properties => { storage => { type => 'string'} }, | |
61 | }, | |
62 | links => [ { rel => 'child', href => "{storage}" } ], | |
63 | }, | |
64 | code => sub { | |
65 | my ($param) = @_; | |
66 | ||
67 | my $cfg = cfs_read_file("storage.cfg"); | |
68 | ||
69 | my @sids = PVE::Storage::storage_ids($cfg); | |
70 | ||
71 | my $res = []; | |
72 | foreach my $storeid (@sids) { | |
73 | my $scfg = &$api_storage_config($cfg, $storeid); | |
74 | next if $param->{type} && $param->{type} ne $scfg->{type}; | |
75 | push @$res, $scfg; | |
76 | } | |
77 | ||
78 | return $res; | |
79 | }}); | |
80 | ||
81 | __PACKAGE__->register_method ({ | |
82 | name => 'read', | |
83 | path => '{storage}', | |
84 | method => 'GET', | |
85 | description => "Read storage configuration.", | |
86 | parameters => { | |
87 | additionalProperties => 0, | |
88 | properties => { | |
89 | storage => get_standard_option('pve-storage-id'), | |
90 | }, | |
91 | }, | |
92 | returns => {}, | |
93 | code => sub { | |
94 | my ($param) = @_; | |
95 | ||
96 | my $cfg = cfs_read_file("storage.cfg"); | |
97 | ||
98 | return &$api_storage_config($cfg, $param->{storage}); | |
99 | }}); | |
100 | ||
101 | __PACKAGE__->register_method ({ | |
102 | name => 'create', | |
103 | protected => 1, | |
104 | path => '', | |
105 | method => 'POST', | |
106 | description => "Create a new storage.", | |
107 | parameters => { | |
108 | additionalProperties => 0, | |
109 | properties => { | |
110 | storage => get_standard_option('pve-storage-id'), | |
111 | nodes => get_standard_option('pve-node-list', { optional => 1 }), | |
112 | type => { | |
113 | type => 'string', | |
114 | enum => $storage_type_enum, | |
115 | }, | |
116 | path => { | |
117 | type => 'string', format => 'pve-storage-path', | |
118 | optional => 1, | |
119 | }, | |
120 | export => { | |
121 | type => 'string', format => 'pve-storage-path', | |
122 | optional => 1, | |
123 | }, | |
124 | server => { | |
125 | type => 'string', format => 'pve-storage-server', | |
126 | optional => 1, | |
127 | }, | |
128 | options => { | |
129 | type => 'string', format => 'pve-storage-options', | |
130 | optional => 1, | |
131 | }, | |
132 | target => { | |
133 | type => 'string', | |
134 | optional => 1, | |
135 | }, | |
136 | vgname => { | |
137 | type => 'string', format => 'pve-storage-vgname', | |
138 | optional => 1, | |
139 | }, | |
140 | base => { | |
141 | type => 'string', format => 'pve-volume-id', | |
142 | optional => 1, | |
143 | }, | |
144 | portal => { | |
145 | type => 'string', format => 'pve-storage-portal-dns', | |
146 | optional => 1, | |
147 | }, | |
148 | content => { | |
149 | type => 'string', format => 'pve-storage-content-list', | |
150 | optional => 1, | |
151 | }, | |
152 | disable => { | |
153 | type => 'boolean', | |
154 | optional => 1, | |
155 | }, | |
156 | shared => { | |
157 | type => 'boolean', | |
158 | optional => 1, | |
159 | }, | |
160 | 'format' => { | |
161 | type => 'string', format => 'pve-storage-format', | |
162 | optional => 1, | |
163 | }, | |
164 | }, | |
165 | }, | |
166 | returns => { type => 'null' }, | |
167 | code => sub { | |
168 | my ($param) = @_; | |
169 | ||
170 | my $type = $param->{type}; | |
171 | delete $param->{type}; | |
172 | ||
173 | my $storeid = $param->{storage}; | |
174 | delete $param->{storage}; | |
175 | ||
176 | if ($param->{portal}) { | |
177 | $param->{portal} = PVE::Storage::resolv_portal($param->{portal}); | |
178 | } | |
179 | ||
180 | my $opts = PVE::Storage::parse_options($storeid, $type, $param, 1); | |
181 | ||
182 | PVE::Storage::lock_storage_config( | |
183 | sub { | |
184 | ||
185 | my $cfg = cfs_read_file('storage.cfg'); | |
186 | ||
187 | if (my $scfg = PVE::Storage::storage_config ($cfg, $storeid, 1)) { | |
188 | die "storage ID '$storeid' already defined\n"; | |
189 | } | |
190 | ||
191 | $cfg->{ids}->{$storeid} = $opts; | |
192 | ||
193 | if ($type eq 'lvm' && $opts->{base}) { | |
194 | ||
195 | my ($baseid, $volname) = PVE::Storage::parse_volume_id ($opts->{base}); | |
196 | ||
197 | my $basecfg = PVE::Storage::storage_config ($cfg, $baseid, 1); | |
198 | die "base storage ID '$baseid' does not exist\n" if !$basecfg; | |
199 | ||
200 | # we only support iscsi for now | |
201 | if (!($basecfg->{type} eq 'iscsi')) { | |
202 | die "unsupported base type '$basecfg->{type}'"; | |
203 | } | |
204 | ||
205 | my $path = PVE::Storage::path ($cfg, $opts->{base}); | |
206 | ||
207 | PVE::Storage::activate_storage($cfg, $baseid); | |
208 | ||
209 | PVE::Storage::lvm_create_volume_group ($path, $opts->{vgname}, $opts->{shared}); | |
210 | } | |
211 | ||
212 | # try to activate if enabled on local node, | |
213 | # we only do this to detect errors/problems sooner | |
214 | if (PVE::Storage::storage_check_enabled($cfg, $storeid, undef, 1)) { | |
215 | PVE::Storage::activate_storage($cfg, $storeid); | |
216 | } | |
217 | ||
218 | cfs_write_file('storage.cfg', $cfg); | |
219 | ||
220 | }, "create storage failed"); | |
221 | ||
222 | }}); | |
223 | ||
224 | __PACKAGE__->register_method ({ | |
225 | name => 'update', | |
226 | protected => 1, | |
227 | path => '{storage}', | |
228 | method => 'PUT', | |
229 | description => "Update storage configuration.", | |
230 | parameters => { | |
231 | additionalProperties => 0, | |
232 | properties => { | |
233 | storage => get_standard_option('pve-storage-id'), | |
234 | nodes => get_standard_option('pve-node-list', { optional => 1 }), | |
235 | content => { | |
236 | type => 'string', format => 'pve-storage-content-list', | |
237 | optional => 1, | |
238 | }, | |
239 | 'format' => { | |
240 | type => 'string', format => 'pve-storage-format', | |
241 | optional => 1, | |
242 | }, | |
243 | disable => { | |
244 | type => 'boolean', | |
245 | optional => 1, | |
246 | }, | |
247 | shared => { | |
248 | type => 'boolean', | |
249 | optional => 1, | |
250 | }, | |
251 | options => { | |
252 | type => 'string', format => 'pve-storage-options', | |
253 | optional => 1, | |
254 | }, | |
255 | digest => { | |
256 | type => 'string', | |
257 | optional => 1, | |
258 | } | |
259 | }, | |
260 | }, | |
261 | returns => { type => 'null' }, | |
262 | code => sub { | |
263 | my ($param) = @_; | |
264 | ||
265 | my $storeid = $param->{storage}; | |
266 | delete($param->{storage}); | |
267 | ||
268 | my $digest = $param->{digest}; | |
269 | delete($param->{digest}); | |
270 | ||
271 | PVE::Storage::lock_storage_config( | |
272 | sub { | |
273 | ||
274 | my $cfg = cfs_read_file('storage.cfg'); | |
275 | ||
276 | PVE::Storage::assert_if_modified ($cfg, $digest); | |
277 | ||
278 | my $scfg = PVE::Storage::storage_config ($cfg, $storeid); | |
279 | ||
280 | my $opts = PVE::Storage::parse_options($storeid, $scfg->{type}, $param); | |
281 | ||
282 | foreach my $k (%$opts) { | |
283 | $scfg->{$k} = $opts->{$k}; | |
284 | } | |
285 | ||
286 | cfs_write_file('storage.cfg', $cfg); | |
287 | ||
288 | }, "update storage failed"); | |
289 | ||
290 | return undef; | |
291 | }}); | |
292 | ||
293 | __PACKAGE__->register_method ({ | |
294 | name => 'delete', | |
295 | protected => 1, | |
296 | path => '{storage}', # /storage/config/{storage} | |
297 | method => 'DELETE', | |
298 | description => "Delete storage configuration.", | |
299 | parameters => { | |
300 | additionalProperties => 0, | |
301 | properties => { | |
302 | storage => get_standard_option('pve-storage-id'), | |
303 | }, | |
304 | }, | |
305 | returns => { type => 'null' }, | |
306 | code => sub { | |
307 | my ($param) = @_; | |
308 | ||
309 | my $storeid = $param->{storage}; | |
310 | delete($param->{storage}); | |
311 | ||
312 | PVE::Storage::lock_storage_config( | |
313 | sub { | |
314 | ||
315 | my $cfg = cfs_read_file('storage.cfg'); | |
316 | ||
317 | die "can't remove storage - storage is used as base of another storage\n" | |
318 | if PVE::Storage::storage_is_used ($cfg, $storeid); | |
319 | ||
320 | delete ($cfg->{ids}->{$storeid}); | |
321 | ||
322 | cfs_write_file('storage.cfg', $cfg); | |
323 | ||
324 | }, "delete storage failed"); | |
325 | ||
326 | return undef; | |
327 | }}); | |
328 | ||
329 | 1; |