]> git.proxmox.com Git - pve-storage.git/blame - PVE/API2/Disks/Directory.pm
api: list thin pools: add volume group to properties
[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 {
e99bc248
FE
215 PVE::Diskmanage::assert_disk_unused($dev);
216
a2c34371
FE
217 my $part = $dev;
218
05d91712
FE
219 if (PVE::Diskmanage::is_partition($dev)) {
220 eval { PVE::Diskmanage::change_parttype($dev, '8300'); };
221 warn $@ if $@;
222 } else {
a2c34371
FE
223 # create partition
224 my $cmd = [$SGDISK, '-n1', '-t1:8300', $dev];
225 print "# ", join(' ', @$cmd), "\n";
226 run_command($cmd);
227
228 my ($devname) = $dev =~ m|^/dev/(.*)$|;
229 $part = "/dev/";
230 dir_glob_foreach("/sys/block/$devname", qr/\Q$devname\E.+/, sub {
231 my ($partition) = @_;
232 $part .= $partition;
233 });
234 }
793d720c
DC
235
236 # create filesystem
a2c34371 237 my $cmd = [$MKFS, '-t', $type, $part];
793d720c
DC
238 print "# ", join(' ', @$cmd), "\n";
239 run_command($cmd);
240
241 # create systemd mount unit and enable & start it
242 my $ini = {
243 'Unit' => {
244 'Description' => "Mount storage '$name' under /mnt/pve",
245 },
246 'Install' => {
247 'WantedBy' => 'multi-user.target',
248 },
249 };
250
251 my $uuid_path;
252 my $uuid;
253
254 $cmd = [$BLKID, $part, '-o', 'export'];
255 print "# ", join(' ', @$cmd), "\n";
256 run_command($cmd, outfunc => sub {
257 my ($line) = @_;
258
259 if ($line =~ m/^UUID=(.*)$/) {
260 $uuid = $1;
261 $uuid_path = "/dev/disk/by-uuid/$uuid";
262 }
263 });
264
265 die "could not get UUID of device '$part'\n" if !$uuid;
266
267 $ini->{'Mount'} = {
268 'What' => $uuid_path,
269 'Where' => $path,
270 'Type' => $type,
271 'Options' => 'defaults',
272 };
273
274 $write_ini->($ini, $mountunitpath);
275
21a75847
FE
276 # FIXME: Remove once we depend on systemd >= v249.
277 # Work around udev bug https://github.com/systemd/systemd/issues/18525 to ensure the
278 # udev database is updated and the $uuid_path symlink is actually created!
279 eval { run_command(['udevadm', 'trigger', $part]); };
280 warn $@ if $@;
281
793d720c
DC
282 run_command(['systemctl', 'daemon-reload']);
283 run_command(['systemctl', 'enable', $mountunitname]);
284 run_command(['systemctl', 'start', $mountunitname]);
285
286 if ($param->{add_storage}) {
287 my $storage_params = {
288 type => 'dir',
289 storage => $name,
4ec588fe 290 content => 'rootdir,images,iso,backup,vztmpl,snippets',
793d720c
DC
291 is_mountpoint => 1,
292 path => $path,
293 nodes => $node,
294 };
295
296 PVE::API2::Storage::Config->create($storage_params);
297 }
298 });
793d720c
DC
299 };
300
793d720c
DC
301 return $rpcenv->fork_worker('dircreate', $name, $user, $worker);
302 }});
303
3041;