]> git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Storage/Config.pm
new plugin architecture
[pve-storage.git] / PVE / API2 / Storage / Config.pm
1 package PVE::API2::Storage::Config;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param);
8 use PVE::Cluster qw(cfs_read_file cfs_write_file);
9 use PVE::Storage;
10 use PVE::Storage::Plugin;
11 use HTTP::Status qw(:constants);
12 use Storable qw(dclone);
13 use PVE::JSONSchema qw(get_standard_option);
14 use PVE::RPCEnvironment;
15
16 use PVE::RESTHandler;
17
18 use base qw(PVE::RESTHandler);
19
20 my @ctypes = qw(images vztmpl iso backup);
21
22 my $storage_type_enum = PVE::Storage::Plugin->lookup_types();
23
24 my $api_storage_config = sub {
25 my ($cfg, $storeid) = @_;
26
27 my $scfg = dclone(PVE::Storage::storage_config($cfg, $storeid));
28 $scfg->{storage} = $storeid;
29 $scfg->{digest} = $cfg->{digest};
30 $scfg->{content} = PVE::Storage::Plugin->encode_value($scfg->{type}, 'content', $scfg->{content});
31
32 if ($scfg->{nodes}) {
33 $scfg->{nodes} = PVE::Storage::Plugin->encode_value($scfg->{type}, 'nodes', $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 permissions => {
45 description => "Only list entries where you have 'Datastore.Audit' or 'Datastore.AllocateSpace' permissions on '/storage/<storage>'",
46 user => 'all',
47 },
48 parameters => {
49 additionalProperties => 0,
50 properties => {
51 type => {
52 description => "Only list storage of specific type",
53 type => 'string',
54 enum => $storage_type_enum,
55 optional => 1,
56 },
57
58 },
59 },
60 returns => {
61 type => 'array',
62 items => {
63 type => "object",
64 properties => { storage => { type => 'string'} },
65 },
66 links => [ { rel => 'child', href => "{storage}" } ],
67 },
68 code => sub {
69 my ($param) = @_;
70
71 my $rpcenv = PVE::RPCEnvironment::get();
72 my $authuser = $rpcenv->get_user();
73
74 my $cfg = cfs_read_file("storage.cfg");
75
76 my @sids = PVE::Storage::storage_ids($cfg);
77
78 my $res = [];
79 foreach my $storeid (@sids) {
80 my $privs = [ 'Datastore.Audit', 'Datastore.AllocateSpace' ];
81 next if !$rpcenv->check_any($authuser, "/storage/$storeid", $privs, 1);
82
83 my $scfg = &$api_storage_config($cfg, $storeid);
84 next if $param->{type} && $param->{type} ne $scfg->{type};
85 push @$res, $scfg;
86 }
87
88 return $res;
89 }});
90
91 __PACKAGE__->register_method ({
92 name => 'read',
93 path => '{storage}',
94 method => 'GET',
95 description => "Read storage configuration.",
96 permissions => {
97 check => ['perm', '/storage/{storage}', ['Datastore.Allocate']],
98 },
99 parameters => {
100 additionalProperties => 0,
101 properties => {
102 storage => get_standard_option('pve-storage-id'),
103 },
104 },
105 returns => {},
106 code => sub {
107 my ($param) = @_;
108
109 my $cfg = cfs_read_file("storage.cfg");
110
111 return &$api_storage_config($cfg, $param->{storage});
112 }});
113
114 __PACKAGE__->register_method ({
115 name => 'create',
116 protected => 1,
117 path => '',
118 method => 'POST',
119 description => "Create a new storage.",
120 permissions => {
121 check => ['perm', '/storage', ['Datastore.Allocate']],
122 },
123 parameters => PVE::Storage::Plugin->createSchema(),
124 returns => { type => 'null' },
125 code => sub {
126 my ($param) = @_;
127
128 my $type = extract_param($param, 'type');
129 my $storeid = extract_param($param, 'storage');
130
131 if ($param->{portal}) {
132 $param->{portal} = PVE::Storage::resolv_portal($param->{portal});
133 }
134
135 my $plugin = PVE::Storage::Plugin->lookup($type);
136 my $opts = $plugin->check_config($storeid, $param, 1, 1);
137
138 PVE::Storage::lock_storage_config(
139 sub {
140
141 my $cfg = cfs_read_file('storage.cfg');
142
143 if (my $scfg = PVE::Storage::storage_config($cfg, $storeid, 1)) {
144 die "storage ID '$storeid' already defined\n";
145 }
146
147 $cfg->{ids}->{$storeid} = $opts;
148
149 if ($type eq 'lvm' && $opts->{base}) {
150
151 my ($baseid, $volname) = PVE::Storage::parse_volume_id($opts->{base});
152
153 my $basecfg = PVE::Storage::storage_config ($cfg, $baseid, 1);
154 die "base storage ID '$baseid' does not exist\n" if !$basecfg;
155
156 # we only support iscsi for now
157 if (!($basecfg->{type} eq 'iscsi')) {
158 die "unsupported base type '$basecfg->{type}'";
159 }
160
161 my $path = PVE::Storage::path($cfg, $opts->{base});
162
163 PVE::Storage::activate_storage($cfg, $baseid);
164
165 PVE::Storage::LVMPlugin::lvm_create_volume_group($path, $opts->{vgname}, $opts->{shared});
166 }
167
168 # try to activate if enabled on local node,
169 # we only do this to detect errors/problems sooner
170 if (PVE::Storage::storage_check_enabled($cfg, $storeid, undef, 1)) {
171 PVE::Storage::activate_storage($cfg, $storeid);
172 }
173
174 cfs_write_file('storage.cfg', $cfg);
175
176 }, "create storage failed");
177
178 return undef;
179 }});
180
181 __PACKAGE__->register_method ({
182 name => 'update',
183 protected => 1,
184 path => '{storage}',
185 method => 'PUT',
186 description => "Update storage configuration.",
187 permissions => {
188 check => ['perm', '/storage', ['Datastore.Allocate']],
189 },
190 parameters => PVE::Storage::Plugin->updateSchema(),
191 returns => { type => 'null' },
192 code => sub {
193 my ($param) = @_;
194
195 my $storeid = extract_param($param, 'storage');
196 my $digest = extract_param($param, 'digest');
197
198 PVE::Storage::lock_storage_config(
199 sub {
200
201 my $cfg = cfs_read_file('storage.cfg');
202
203 PVE::SectionConfig::assert_if_modified($cfg, $digest);
204
205 my $scfg = PVE::Storage::storage_config($cfg, $storeid);
206
207 my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
208 my $opts = $plugin->check_config($storeid, $param, 0, 1);
209
210 foreach my $k (%$opts) {
211 $scfg->{$k} = $opts->{$k};
212 }
213
214 cfs_write_file('storage.cfg', $cfg);
215
216 }, "update storage failed");
217
218 return undef;
219 }});
220
221 __PACKAGE__->register_method ({
222 name => 'delete',
223 protected => 1,
224 path => '{storage}', # /storage/config/{storage}
225 method => 'DELETE',
226 description => "Delete storage configuration.",
227 permissions => {
228 check => ['perm', '/storage', ['Datastore.Allocate']],
229 },
230 parameters => {
231 additionalProperties => 0,
232 properties => {
233 storage => get_standard_option('pve-storage-id'),
234 },
235 },
236 returns => { type => 'null' },
237 code => sub {
238 my ($param) = @_;
239
240 my $storeid = extract_param($param, 'storage');
241
242 PVE::Storage::lock_storage_config(
243 sub {
244
245 my $cfg = cfs_read_file('storage.cfg');
246
247 die "can't remove storage - storage is used as base of another storage\n"
248 if PVE::Storage::storage_is_used($cfg, $storeid);
249
250 delete $cfg->{ids}->{$storeid};
251
252 cfs_write_file('storage.cfg', $cfg);
253
254 }, "delete storage failed");
255
256 return undef;
257 }});
258
259 1;