]>
git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Disks/Directory.pm
1 package PVE
::API2
::Disks
::Directory
;
9 use PVE
::JSONSchema
qw(get_standard_option);
11 use PVE
::RPCEnvironment
;
13 use PVE
::Tools
qw(run_command trim file_set_contents file_get_contents dir_glob_foreach lock_file);
15 use PVE
::API2
::Storage
::Config
;
17 use base
qw(PVE::RESTHandler);
19 my $SGDISK = '/sbin/sgdisk';
20 my $MKFS = '/sbin/mkfs';
21 my $BLKID = '/sbin/blkid';
26 my $content = file_get_contents
($filename);
27 my @lines = split /\n/, $content;
32 foreach my $line (@lines) {
34 if ($line =~ m/^\[([^\]]+)\]/) {
36 if (!defined($result->{$section})) {
37 $result->{$section} = {};
39 } elsif ($line =~ m/^(.*?)=(.*)$/) {
40 my ($key, $val) = ($1, $2);
42 warn "key value pair found without section, skipping\n";
46 if ($result->{$section}->{$key}) {
47 # make duplicate properties to arrays to keep the order
48 my $prop = $result->{$section}->{$key};
49 if (ref($prop) eq 'ARRAY') {
52 $result->{$section}->{$key} = [$prop, $val];
55 $result->{$section}->{$key} = $val;
58 # ignore everything else
65 my ($ini, $filename) = @_;
69 foreach my $sname (sort keys %$ini) {
70 my $section = $ini->{$sname};
72 $content .= "[$sname]\n";
74 foreach my $pname (sort keys %$section) {
75 my $prop = $section->{$pname};
78 $content .= "$pname=$prop\n";
79 } elsif (ref($prop) eq 'ARRAY') {
80 foreach my $val (@$prop) {
81 $content .= "$pname=$val\n";
84 die "invalid property '$pname'\n";
90 file_set_contents
($filename, $content);
93 __PACKAGE__-
>register_method ({
100 check
=> ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any
=> 1],
102 description
=> "PVE Managed Directory storages.",
104 additionalProperties
=> 0,
106 node
=> get_standard_option
('pve-node'),
116 description
=> 'The path of the mount unit.',
120 description
=> 'The mount path.',
124 description
=> 'The mounted device.',
128 description
=> 'The filesystem type.',
132 description
=> 'The mount options.',
142 dir_glob_foreach
('/etc/systemd/system', '^mnt-pve-(.+)\.mount$', sub {
143 my ($filename, $storid) = @_;
144 $storid = PVE
::Systemd
::unescape_unit
($storid);
146 my $unitfile = "/etc/systemd/system/$filename";
147 my $unit = $read_ini->($unitfile);
150 unitfile
=> $unitfile,
151 path
=> "/mnt/pve/$storid",
152 device
=> $unit->{'Mount'}->{'What'},
153 type
=> $unit->{'Mount'}->{'Type'},
154 options
=> $unit->{'Mount'}->{'Options'},
161 __PACKAGE__-
>register_method ({
168 check
=> ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
170 description
=> "Create a Filesystem on an unused disk. Will be mounted under '/mnt/pve/NAME'.",
172 additionalProperties
=> 0,
174 node
=> get_standard_option
('pve-node'),
175 name
=> get_standard_option
('pve-storage-id'),
178 description
=> 'The block device you want to create the filesystem on.',
181 description
=> "Configure storage using the directory.",
187 description
=> "The desired filesystem.",
189 enum
=> ['ext4', 'xfs'],
195 returns
=> { type
=> 'string' },
199 my $rpcenv = PVE
::RPCEnvironment
::get
();
200 my $user = $rpcenv->get_user();
202 my $name = $param->{name
};
203 my $dev = $param->{device
};
204 my $node = $param->{node
};
205 my $type = $param->{filesystem
} // 'ext4';
207 $dev = PVE
::Diskmanage
::verify_blockdev_path
($dev);
208 PVE
::Diskmanage
::assert_disk_unused
($dev);
209 PVE
::Storage
::assert_sid_unused
($name) if $param->{add_storage
};
212 my $path = "/mnt/pve/$name";
213 my $mountunitname = PVE
::Systemd
::escape_unit
($path, 1) . ".mount";
214 my $mountunitpath = "/etc/systemd/system/$mountunitname";
216 PVE
::Diskmanage
::locked_disk_action
(sub {
217 PVE
::Diskmanage
::assert_disk_unused
($dev);
221 if (PVE
::Diskmanage
::is_partition
($dev)) {
222 eval { PVE
::Diskmanage
::change_parttype
($dev, '8300'); };
226 my $cmd = [$SGDISK, '-n1', '-t1:8300', $dev];
227 print "# ", join(' ', @$cmd), "\n";
230 my ($devname) = $dev =~ m
|^/dev/(.*)$|;
232 dir_glob_foreach
("/sys/block/$devname", qr
/\Q
$devname\E.+/, sub {
233 my ($partition) = @_;
239 my $cmd = [$MKFS, '-t', $type, $part];
240 print "# ", join(' ', @$cmd), "\n";
243 # create systemd mount unit and enable & start it
246 'Description' => "Mount storage '$name' under /mnt/pve",
249 'WantedBy' => 'multi-user.target',
256 $cmd = [$BLKID, $part, '-o', 'export'];
257 print "# ", join(' ', @$cmd), "\n";
258 run_command
($cmd, outfunc
=> sub {
261 if ($line =~ m/^UUID=(.*)$/) {
263 $uuid_path = "/dev/disk/by-uuid/$uuid";
267 die "could not get UUID of device '$part'\n" if !$uuid;
270 'What' => $uuid_path,
273 'Options' => 'defaults',
276 $write_ini->($ini, $mountunitpath);
278 PVE
::Diskmanage
::udevadm_trigger
($part);
280 run_command
(['systemctl', 'daemon-reload']);
281 run_command
(['systemctl', 'enable', $mountunitname]);
282 run_command
(['systemctl', 'start', $mountunitname]);
284 if ($param->{add_storage
}) {
285 my $storage_params = {
288 content
=> 'rootdir,images,iso,backup,vztmpl,snippets',
294 PVE
::API2
::Storage
::Config-
>create($storage_params);
299 return $rpcenv->fork_worker('dircreate', $name, $user, $worker);
302 __PACKAGE__-
>register_method ({
309 check
=> ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
311 description
=> "Unmounts the storage and removes the mount unit.",
313 additionalProperties
=> 0,
315 node
=> get_standard_option
('pve-node'),
316 name
=> get_standard_option
('pve-storage-id'),
317 'cleanup-config' => {
318 description
=> "Marks associated storage(s) as not available on this node anymore ".
319 "or removes them from the configuration (if configured for this node only).",
325 description
=> "Also wipe disk so it can be repurposed afterwards.",
332 returns
=> { type
=> 'string' },
336 my $rpcenv = PVE
::RPCEnvironment
::get
();
337 my $user = $rpcenv->get_user();
339 my $name = $param->{name
};
340 my $node = $param->{node
};
343 my $path = "/mnt/pve/$name";
344 my $mountunitname = PVE
::Systemd
::escape_unit
($path, 1) . ".mount";
345 my $mountunitpath = "/etc/systemd/system/$mountunitname";
347 PVE
::Diskmanage
::locked_disk_action
(sub {
349 if ($param->{'cleanup-disks'}) {
350 my $unit = $read_ini->($mountunitpath);
352 my $dev = PVE
::Diskmanage
::verify_blockdev_path
($unit->{'Mount'}->{'What'});
355 # clean up whole device if this is the only partition
357 my $info = PVE
::Diskmanage
::get_disks
($dev, 1, 1);
358 die "unable to obtain information for disk '$dev'\n" if !$info->{$dev};
359 $to_wipe = $info->{$dev}->{parent
}
360 if $info->{$dev}->{parent
} && scalar(keys $info->%*) == 2;
363 run_command
(['systemctl', 'stop', $mountunitname]);
364 run_command
(['systemctl', 'disable', $mountunitname]);
366 unlink $mountunitpath or $! == ENOENT
or die "cannot remove $mountunitpath - $!\n";
369 if ($param->{'cleanup-config'}) {
372 return $scfg->{type
} eq 'dir' && $scfg->{path
} eq $path;
374 eval { PVE
::API2
::Storage
::Config-
>cleanup_storages_for_node($match, $node); };
375 warn $config_err = $@ if $@;
379 PVE
::Diskmanage
::wipe_blockdev
($to_wipe);
380 PVE
::Diskmanage
::udevadm_trigger
($to_wipe);
383 die "config cleanup failed - $config_err" if $config_err;
387 return $rpcenv->fork_worker('dirremove', $name, $user, $worker);