]> git.proxmox.com Git - pve-storage.git/commitdiff
add API for add Directory storage
authorDominik Csapak <d.csapak@proxmox.com>
Mon, 30 Jul 2018 08:26:06 +0000 (10:26 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Thu, 2 Aug 2018 10:28:27 +0000 (12:28 +0200)
creates/lists systemd mount units for /mnt/pve/.*
filetypes allowed are ext4 and xfs for now
mount with /dev/disk/by-uuid

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
PVE/API2/Disks.pm
PVE/API2/Disks/Directory.pm [new file with mode: 0644]
PVE/API2/Disks/Makefile

index c49d4391c459125decb3624875a4772d39b602a0..b501c6023280e136ad1366f13fb8abd05376119f 100644 (file)
@@ -10,6 +10,7 @@ use PVE::JSONSchema qw(get_standard_option);
 
 use PVE::API2::Disks::LVM;
 use PVE::API2::Disks::LVMThin;
+use PVE::API2::Disks::Directory;
 
 use PVE::RESTHandler;
 
@@ -25,6 +26,11 @@ __PACKAGE__->register_method ({
    path => 'lvmthin',
 });
 
+__PACKAGE__->register_method ({
+   subclass => "PVE::API2::Disks::Directory",
+   path => 'directory',
+});
+
 __PACKAGE__->register_method ({
     name => 'index',
     path => '',
@@ -55,6 +61,7 @@ __PACKAGE__->register_method ({
            { name => 'smart' },
            { name => 'lvm' },
            { name => 'lvmthin' },
+           { name => 'directory' },
            ];
 
        return $result;
diff --git a/PVE/API2/Disks/Directory.pm b/PVE/API2/Disks/Directory.pm
new file mode 100644 (file)
index 0000000..f554d6c
--- /dev/null
@@ -0,0 +1,290 @@
+package PVE::API2::Disks::Directory;
+
+use strict;
+use warnings;
+
+use PVE::Diskmanage;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::API2::Storage::Config;
+use PVE::Tools qw(run_command trim file_set_contents file_get_contents dir_glob_foreach lock_file);
+
+use PVE::RPCEnvironment;
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $SGDISK = '/sbin/sgdisk';
+my $MKFS = '/sbin/mkfs';
+my $BLKID = '/sbin/blkid';
+
+my $read_ini = sub {
+    my ($filename) = @_;
+
+    my $content = file_get_contents($filename);
+    my @lines = split /\n/, $content;
+
+    my $result = {};
+    my $section;
+
+    foreach my $line (@lines) {
+       $line = trim($line);
+       if ($line =~ m/^\[([^\]]+)\]/) {
+           $section = $1;
+           if (!defined($result->{$section})) {
+               $result->{$section} = {};
+           }
+       } elsif ($line =~ m/^(.*?)=(.*)$/) {
+           my ($key, $val) = ($1, $2);
+           if (!$section) {
+               warn "key value pair found without section, skipping\n";
+               next;
+           }
+
+           if ($result->{$section}->{$key}) {
+               # make duplicate properties to arrays to keep the order
+               my $prop = $result->{$section}->{$key};
+               if (ref($prop) eq 'ARRAY') {
+                   push @$prop, $val;
+               } else {
+                   $result->{$section}->{$key} = [$prop, $val];
+               }
+           } else {
+               $result->{$section}->{$key} = $val;
+           }
+       }
+       # ignore everything else
+    }
+
+    return $result;
+};
+
+my $write_ini = sub {
+    my ($ini, $filename) = @_;
+
+    my $content = "";
+
+    foreach my $sname (sort keys %$ini) {
+       my $section = $ini->{$sname};
+
+       $content .= "[$sname]\n";
+
+       foreach my $pname (sort keys %$section) {
+           my $prop = $section->{$pname};
+
+           if (!ref($prop)) {
+               $content .= "$pname=$prop\n";
+           } elsif (ref($prop) eq 'ARRAY') {
+               foreach my $val (@$prop) {
+                   $content .= "$pname=$val\n";
+               }
+           } else {
+               die "invalid property '$pname'\n";
+           }
+       }
+       $content .= "\n";
+    }
+
+    file_set_contents($filename, $content);
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
+    },
+    description => "PVE Managed Directory storages.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => 'object',
+           properties => {
+               unitfile => {
+                   type => 'string',
+                   description => 'The path of the mount unit'.,
+               },
+               path => {
+                   type => 'string',
+                   description => 'The mount path.',
+               },
+               device => {
+                   type => 'string',
+                   description => 'The mounted device.',
+               },
+               type => {
+                   type => 'string',
+                   description => 'The filesystem type.',
+               },
+               options => {
+                   type => 'string',
+                   description => 'The mount options.',
+               },
+           },
+       },
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $result = [];
+
+       dir_glob_foreach('/etc/systemd/system', '^mnt-pve-(.+)\.mount$', sub {
+           my ($filename, $storid) = @_;
+
+           my $unitfile = "/etc/systemd/system/$filename";
+           my $unit = $read_ini->($unitfile);
+
+           push @$result, {
+               unitfile => $unitfile,
+               path => "/mnt/pve/$storid",
+               device => $unit->{'Mount'}->{'What'},
+               type => $unit->{'Mount'}->{'Type'},
+               options => $unit->{'Mount'}->{'Options'},
+           };
+       });
+
+       return $result;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    path => '',
+    method => 'POST',
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', ['Sys.Modify', 'Datastore.Allocate']],
+    },
+    description => "Create a Filesystem on an unused disk. Will be mounted under '/mnt/pve/NAME'.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           name => get_standard_option('pve-storage-id'),
+           device => {
+               type => 'string',
+               description => 'The block device you want to create the thinpool on.',
+           },
+           add_storage => {
+               description => "Configure storage using the directory.",
+               type => 'boolean',
+               optional => 1,
+               default => 0,
+           },
+           filesystem => {
+               description => "The desired filesystem.",
+               type => 'string',
+               enum => ['ext4', 'xfs'],
+               optional => 1,
+               default => 'ext4',
+           },
+       },
+    },
+    returns => { type => 'string' },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $user = $rpcenv->get_user();
+
+       my $name = $param->{name};
+       my $dev = $param->{device};
+       my $node = $param->{node};
+       my $type = $param->{filesystem} // 'ext4';
+
+       $dev = PVE::Diskmanage::verify_blockdev_path($dev);
+       die "device $dev is already in use\n" if PVE::Diskmanage::disk_is_used($dev);
+
+       my $cfg = PVE::Storage::config();
+
+       if (my $scfg = PVE::Storage::storage_config($cfg, $name, 1)) {
+           die "storage ID '$name' already defined\n";
+       }
+
+       my $worker = sub {
+           my $path = "/mnt/pve/$name";
+           my $mountunitname = "mnt-pve-$name.mount";
+           my $mountunitpath = "/etc/systemd/system/$mountunitname";
+
+           lock_file('/run/lock/pve-diskmanage.lck', undef, sub {
+               # create partition
+               my $cmd = [$SGDISK, '-n1', '-t1:8300', $dev];
+               print "# ", join(' ', @$cmd), "\n";
+               run_command($cmd);
+
+               my $part = "${dev}1";
+
+               # create filesystem
+               $cmd = [$MKFS, '-t', $type, $part];
+               print "# ", join(' ', @$cmd), "\n";
+               run_command($cmd);
+
+               # create systemd mount unit and enable & start it
+               my $ini = {
+                   'Unit' => {
+                       'Description' => "Mount storage '$name' under /mnt/pve",
+                   },
+                   'Install' => {
+                       'WantedBy' => 'multi-user.target',
+                   },
+               };
+
+               my $uuid_path;
+               my $uuid;
+
+               $cmd = [$BLKID, $part, '-o', 'export'];
+               print "# ", join(' ', @$cmd), "\n";
+               run_command($cmd, outfunc => sub {
+                       my ($line) = @_;
+
+                       if ($line =~ m/^UUID=(.*)$/) {
+                           $uuid = $1;
+                           $uuid_path = "/dev/disk/by-uuid/$uuid";
+                       }
+                   });
+
+               die "could not get UUID of device '$part'\n" if !$uuid;
+
+               $ini->{'Mount'} = {
+                   'What' => $uuid_path,
+                   'Where' => $path,
+                   'Type' => $type,
+                   'Options' => 'defaults',
+               };
+
+               $write_ini->($ini, $mountunitpath);
+
+               run_command(['systemctl', 'daemon-reload']);
+               run_command(['systemctl', 'enable', $mountunitname]);
+               run_command(['systemctl', 'start', $mountunitname]);
+
+               if ($param->{add_storage}) {
+                   my $storage_params = {
+                       type => 'dir',
+                       storage => $name,
+                       content => 'rootdir,images,iso,backup,vztmpl',
+                       is_mountpoint => 1,
+                       path => $path,
+                       nodes => $node,
+                   };
+
+                   PVE::API2::Storage::Config->create($storage_params);
+               }
+           });
+
+           die $@ if $@;
+       };
+
+
+       return $rpcenv->fork_worker('dircreate', $name, $user, $worker);
+    }});
+
+1;
index bc2af8fe5c5e43f3ca9db8c8df6a1aa49354d80f..ccbe0047c30f0ea0cc0c3cd83ea7066892c73d35 100644 (file)
@@ -1,6 +1,7 @@
 
 SOURCES= LVM.pm\
-        LVMThin.pm
+        LVMThin.pm\
+        Directory.pm
 
 .PHONY: install
 install: