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