]>
git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Disks/Directory.pm
4fdb06874d0754a55ac4e9beba80664db45960b9
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';
206 my $path = "/mnt/pve/$name";
207 my $mountunitname = PVE
::Systemd
::escape_unit
($path, 1) . ".mount";
208 my $mountunitpath = "/etc/systemd/system/$mountunitname";
210 $dev = PVE
::Diskmanage
::verify_blockdev_path
($dev);
211 PVE
::Diskmanage
::assert_disk_unused
($dev);
213 my $storage_params = {
216 content
=> 'rootdir,images,iso,backup,vztmpl,snippets',
221 my $verify_params = [qw(path)];
223 if ($param->{add_storage
}) {
224 PVE
::API2
::Storage
::Config-
>create_or_update(
233 my $mounted = PVE
::Diskmanage
::mounted_paths
();
234 die "the path for '${name}' is already mounted: ${path} ($mounted->{$path})\n"
235 if $mounted->{$path};
236 die "a systemd mount unit already exists: ${mountunitpath}\n" if -e
$mountunitpath;
239 PVE
::Diskmanage
::locked_disk_action
(sub {
240 PVE
::Diskmanage
::assert_disk_unused
($dev);
244 if (PVE
::Diskmanage
::is_partition
($dev)) {
245 eval { PVE
::Diskmanage
::change_parttype
($dev, '8300'); };
249 my $cmd = [$SGDISK, '-n1', '-t1:8300', $dev];
250 print "# ", join(' ', @$cmd), "\n";
253 my ($devname) = $dev =~ m
|^/dev/(.*)$|;
255 dir_glob_foreach
("/sys/block/$devname", qr
/\Q
$devname\E.+/, sub {
256 my ($partition) = @_;
262 my $cmd = [$MKFS, '-t', $type, $part];
263 print "# ", join(' ', @$cmd), "\n";
266 # create systemd mount unit and enable & start it
269 'Description' => "Mount storage '$name' under /mnt/pve",
272 'WantedBy' => 'multi-user.target',
279 $cmd = [$BLKID, $part, '-o', 'export'];
280 print "# ", join(' ', @$cmd), "\n";
281 run_command
($cmd, outfunc
=> sub {
284 if ($line =~ m/^UUID=(.*)$/) {
286 $uuid_path = "/dev/disk/by-uuid/$uuid";
290 die "could not get UUID of device '$part'\n" if !$uuid;
293 'What' => $uuid_path,
296 'Options' => 'defaults',
299 $write_ini->($ini, $mountunitpath);
301 PVE
::Diskmanage
::udevadm_trigger
($part);
303 run_command
(['systemctl', 'daemon-reload']);
304 run_command
(['systemctl', 'enable', $mountunitname]);
305 run_command
(['systemctl', 'start', $mountunitname]);
307 if ($param->{add_storage
}) {
308 PVE
::API2
::Storage
::Config-
>create_or_update(
318 return $rpcenv->fork_worker('dircreate', $name, $user, $worker);
321 __PACKAGE__-
>register_method ({
328 check
=> ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
330 description
=> "Unmounts the storage and removes the mount unit.",
332 additionalProperties
=> 0,
334 node
=> get_standard_option
('pve-node'),
335 name
=> get_standard_option
('pve-storage-id'),
336 'cleanup-config' => {
337 description
=> "Marks associated storage(s) as not available on this node anymore ".
338 "or removes them from the configuration (if configured for this node only).",
344 description
=> "Also wipe disk so it can be repurposed afterwards.",
351 returns
=> { type
=> 'string' },
355 my $rpcenv = PVE
::RPCEnvironment
::get
();
356 my $user = $rpcenv->get_user();
358 my $name = $param->{name
};
359 my $node = $param->{node
};
362 my $path = "/mnt/pve/$name";
363 my $mountunitname = PVE
::Systemd
::escape_unit
($path, 1) . ".mount";
364 my $mountunitpath = "/etc/systemd/system/$mountunitname";
366 PVE
::Diskmanage
::locked_disk_action
(sub {
368 if ($param->{'cleanup-disks'}) {
369 my $unit = $read_ini->($mountunitpath);
371 my $dev = PVE
::Diskmanage
::verify_blockdev_path
($unit->{'Mount'}->{'What'});
374 # clean up whole device if this is the only partition
376 my $info = PVE
::Diskmanage
::get_disks
($dev, 1, 1);
377 die "unable to obtain information for disk '$dev'\n" if !$info->{$dev};
378 $to_wipe = $info->{$dev}->{parent
}
379 if $info->{$dev}->{parent
} && scalar(keys $info->%*) == 2;
382 run_command
(['systemctl', 'stop', $mountunitname]);
383 run_command
(['systemctl', 'disable', $mountunitname]);
385 unlink $mountunitpath or $! == ENOENT
or die "cannot remove $mountunitpath - $!\n";
388 if ($param->{'cleanup-config'}) {
391 return $scfg->{type
} eq 'dir' && $scfg->{path
} eq $path;
393 eval { PVE
::API2
::Storage
::Config-
>cleanup_storages_for_node($match, $node); };
394 warn $config_err = $@ if $@;
398 PVE
::Diskmanage
::wipe_blockdev
($to_wipe);
399 PVE
::Diskmanage
::udevadm_trigger
($to_wipe);
402 die "config cleanup failed - $config_err" if $config_err;
406 return $rpcenv->fork_worker('dirremove', $name, $user, $worker);