]> git.proxmox.com Git - pve-storage.git/blame - PVE/API2/Disks/Directory.pm
rename check_available to assert_sid_unused
[pve-storage.git] / PVE / API2 / Disks / Directory.pm
CommitLineData
793d720c
DC
1package PVE::API2::Disks::Directory;
2
3use strict;
4use warnings;
5
6use PVE::Diskmanage;
7use PVE::JSONSchema qw(get_standard_option);
8use PVE::API2::Storage::Config;
9use PVE::Tools qw(run_command trim file_set_contents file_get_contents dir_glob_foreach lock_file);
10
11use PVE::RPCEnvironment;
12use PVE::RESTHandler;
13
14use base qw(PVE::RESTHandler);
15
16my $SGDISK = '/sbin/sgdisk';
17my $MKFS = '/sbin/mkfs';
18my $BLKID = '/sbin/blkid';
19
20my $read_ini = sub {
21 my ($filename) = @_;
22
23 my $content = file_get_contents($filename);
24 my @lines = split /\n/, $content;
25
26 my $result = {};
27 my $section;
28
29 foreach my $line (@lines) {
30 $line = trim($line);
31 if ($line =~ m/^\[([^\]]+)\]/) {
32 $section = $1;
33 if (!defined($result->{$section})) {
34 $result->{$section} = {};
35 }
36 } elsif ($line =~ m/^(.*?)=(.*)$/) {
37 my ($key, $val) = ($1, $2);
38 if (!$section) {
39 warn "key value pair found without section, skipping\n";
40 next;
41 }
42
43 if ($result->{$section}->{$key}) {
44 # make duplicate properties to arrays to keep the order
45 my $prop = $result->{$section}->{$key};
46 if (ref($prop) eq 'ARRAY') {
47 push @$prop, $val;
48 } else {
49 $result->{$section}->{$key} = [$prop, $val];
50 }
51 } else {
52 $result->{$section}->{$key} = $val;
53 }
54 }
55 # ignore everything else
56 }
57
58 return $result;
59};
60
61my $write_ini = sub {
62 my ($ini, $filename) = @_;
63
64 my $content = "";
65
66 foreach my $sname (sort keys %$ini) {
67 my $section = $ini->{$sname};
68
69 $content .= "[$sname]\n";
70
71 foreach my $pname (sort keys %$section) {
72 my $prop = $section->{$pname};
73
74 if (!ref($prop)) {
75 $content .= "$pname=$prop\n";
76 } elsif (ref($prop) eq 'ARRAY') {
77 foreach my $val (@$prop) {
78 $content .= "$pname=$val\n";
79 }
80 } else {
81 die "invalid property '$pname'\n";
82 }
83 }
84 $content .= "\n";
85 }
86
87 file_set_contents($filename, $content);
88};
89
90__PACKAGE__->register_method ({
91 name => 'index',
92 path => '',
93 method => 'GET',
94 proxyto => 'node',
95 protected => 1,
96 permissions => {
97 check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
98 },
99 description => "PVE Managed Directory storages.",
100 parameters => {
101 additionalProperties => 0,
102 properties => {
103 node => get_standard_option('pve-node'),
104 },
105 },
106 returns => {
107 type => 'array',
108 items => {
109 type => 'object',
110 properties => {
111 unitfile => {
112 type => 'string',
fd87487b 113 description => 'The path of the mount unit.',
793d720c
DC
114 },
115 path => {
116 type => 'string',
117 description => 'The mount path.',
118 },
119 device => {
120 type => 'string',
121 description => 'The mounted device.',
122 },
123 type => {
124 type => 'string',
125 description => 'The filesystem type.',
126 },
127 options => {
128 type => 'string',
129 description => 'The mount options.',
130 },
131 },
132 },
133 },
134 code => sub {
135 my ($param) = @_;
136
137 my $result = [];
138
139 dir_glob_foreach('/etc/systemd/system', '^mnt-pve-(.+)\.mount$', sub {
140 my ($filename, $storid) = @_;
141
142 my $unitfile = "/etc/systemd/system/$filename";
143 my $unit = $read_ini->($unitfile);
144
145 push @$result, {
146 unitfile => $unitfile,
147 path => "/mnt/pve/$storid",
148 device => $unit->{'Mount'}->{'What'},
149 type => $unit->{'Mount'}->{'Type'},
150 options => $unit->{'Mount'}->{'Options'},
151 };
152 });
153
154 return $result;
155 }});
156
157__PACKAGE__->register_method ({
158 name => 'create',
159 path => '',
160 method => 'POST',
161 proxyto => 'node',
162 protected => 1,
163 permissions => {
164 check => ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
165 },
166 description => "Create a Filesystem on an unused disk. Will be mounted under '/mnt/pve/NAME'.",
167 parameters => {
168 additionalProperties => 0,
169 properties => {
170 node => get_standard_option('pve-node'),
171 name => get_standard_option('pve-storage-id'),
172 device => {
173 type => 'string',
fdc863c7 174 description => 'The block device you want to create the filesystem on.',
793d720c
DC
175 },
176 add_storage => {
177 description => "Configure storage using the directory.",
178 type => 'boolean',
179 optional => 1,
180 default => 0,
181 },
182 filesystem => {
183 description => "The desired filesystem.",
184 type => 'string',
185 enum => ['ext4', 'xfs'],
186 optional => 1,
187 default => 'ext4',
188 },
189 },
190 },
191 returns => { type => 'string' },
192 code => sub {
193 my ($param) = @_;
194
195 my $rpcenv = PVE::RPCEnvironment::get();
196 my $user = $rpcenv->get_user();
197
198 my $name = $param->{name};
199 my $dev = $param->{device};
200 my $node = $param->{node};
201 my $type = $param->{filesystem} // 'ext4';
202
203 $dev = PVE::Diskmanage::verify_blockdev_path($dev);
76c1e57b 204 PVE::Diskmanage::check_unused($dev);
9280153e 205 PVE::Storage::assert_sid_unused($name) if $param->{add_storage};
793d720c
DC
206
207 my $worker = sub {
208 my $path = "/mnt/pve/$name";
209 my $mountunitname = "mnt-pve-$name.mount";
210 my $mountunitpath = "/etc/systemd/system/$mountunitname";
211
e39e8ee2 212 PVE::Diskmanage::locked_disk_action(sub {
793d720c
DC
213 # create partition
214 my $cmd = [$SGDISK, '-n1', '-t1:8300', $dev];
215 print "# ", join(' ', @$cmd), "\n";
216 run_command($cmd);
217
401b56fc
DC
218 my ($devname) = $dev =~ m|^/dev/(.*)$|;
219 my $part = "/dev/";
220 dir_glob_foreach("/sys/block/$devname", qr/\Q$devname\E.+/, sub {
221 my ($partition) = @_;
222 $part .= $partition;
223 });
793d720c
DC
224
225 # create filesystem
226 $cmd = [$MKFS, '-t', $type, $part];
227 print "# ", join(' ', @$cmd), "\n";
228 run_command($cmd);
229
230 # create systemd mount unit and enable & start it
231 my $ini = {
232 'Unit' => {
233 'Description' => "Mount storage '$name' under /mnt/pve",
234 },
235 'Install' => {
236 'WantedBy' => 'multi-user.target',
237 },
238 };
239
240 my $uuid_path;
241 my $uuid;
242
243 $cmd = [$BLKID, $part, '-o', 'export'];
244 print "# ", join(' ', @$cmd), "\n";
245 run_command($cmd, outfunc => sub {
246 my ($line) = @_;
247
248 if ($line =~ m/^UUID=(.*)$/) {
249 $uuid = $1;
250 $uuid_path = "/dev/disk/by-uuid/$uuid";
251 }
252 });
253
254 die "could not get UUID of device '$part'\n" if !$uuid;
255
256 $ini->{'Mount'} = {
257 'What' => $uuid_path,
258 'Where' => $path,
259 'Type' => $type,
260 'Options' => 'defaults',
261 };
262
263 $write_ini->($ini, $mountunitpath);
264
265 run_command(['systemctl', 'daemon-reload']);
266 run_command(['systemctl', 'enable', $mountunitname]);
267 run_command(['systemctl', 'start', $mountunitname]);
268
269 if ($param->{add_storage}) {
270 my $storage_params = {
271 type => 'dir',
272 storage => $name,
273 content => 'rootdir,images,iso,backup,vztmpl',
274 is_mountpoint => 1,
275 path => $path,
276 nodes => $node,
277 };
278
279 PVE::API2::Storage::Config->create($storage_params);
280 }
281 });
793d720c
DC
282 };
283
793d720c
DC
284 return $rpcenv->fork_worker('dircreate', $name, $user, $worker);
285 }});
286
2871;