add pool API
authorDietmar Maurer <dietmar@proxmox.com>
Thu, 26 Jan 2012 11:42:01 +0000 (12:42 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Thu, 26 Jan 2012 11:42:01 +0000 (12:42 +0100)
PVE/API2/AccessControl.pm
PVE/API2/Group.pm
PVE/API2/Makefile
PVE/API2/Pool.pm [new file with mode: 0644]
PVE/AccessControl.pm
PVE/RPCEnvironment.pm
pveum

index a407648..913bdd8 100644 (file)
@@ -15,6 +15,7 @@ use PVE::API2::User;
 use PVE::API2::Group;
 use PVE::API2::Role;
 use PVE::API2::ACL;
+use PVE::API2::Pool;
 
 use base qw(PVE::RESTHandler);
 
@@ -44,6 +45,11 @@ __PACKAGE__->register_method ({
 });
 
 __PACKAGE__->register_method ({
+    subclass => "PVE::API2::Pool",  
+    path => 'pools',
+});
+
+__PACKAGE__->register_method ({
     name => 'index', 
     path => '', 
     method => 'GET',
index 883c45d..373e8e2 100644 (file)
@@ -148,10 +148,10 @@ __PACKAGE__->register_method ({
                die "group '$group' does not exist\n" 
                    if !$data;
 
-               $data->{comment} = $param->{comment} if $param->{comment};
+               $data->{comment} = $param->{comment} if defined($param->{comment});
                
                cfs_write_file("user.cfg", $usercfg);
-           }, "create group failed");
+           }, "update group failed");
 
        return undef;
     }});
index 64aa8d3..c9d44df 100644 (file)
@@ -1,10 +1,11 @@
 
-API2_SOURCES=           \
-       AccessControl.pm \
-       Domains.pm       \
-       ACL.pm           \
-       Role.pm          \
-       Group.pm         \
+API2_SOURCES=                  \
+       AccessControl.pm        \
+       Pool.pm                 \
+       Domains.pm              \
+       ACL.pm                  \
+       Role.pm                 \
+       Group.pm                \
        User.pm
 
 .PHONY: install
diff --git a/PVE/API2/Pool.pm b/PVE/API2/Pool.pm
new file mode 100644 (file)
index 0000000..f224777
--- /dev/null
@@ -0,0 +1,275 @@
+package PVE::API2::Pool;
+
+use strict;
+use warnings;
+use PVE::Exception qw(raise_param_exc);
+use PVE::Cluster qw (cfs_read_file cfs_write_file);
+use PVE::AccessControl;
+
+use PVE::SafeSyslog;
+
+use Data::Dumper; # fixme: remove
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $extract_pool_data = sub {
+    my ($data, $full) = @_;
+
+    my $res = {};
+
+    $res->{comment} = $data->{comment} if defined($data->{comment});
+
+    return $res if !$full;
+
+    $res->{vms} = $data->{vms} ? [ keys %{$data->{vms}} ] : [];
+    $res->{storages} = $data->{storage} ? [ keys %{$data->{storage}} ] : [];
+
+    return $res;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index', 
+    path => '', 
+    method => 'GET',
+    description => "Pool index.",
+    permissions => { 
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {},
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               poolid => { type => 'string' },
+           },
+       },
+       links => [ { rel => 'child', href => "{poolid}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+    
+       my $rpcenv = PVE::RPCEnvironment::get();
+
+       my $res = [];
+
+       my $usercfg = $rpcenv->{user_cfg};
+
+       foreach my $poolpath (keys %{$usercfg->{pools}}) {
+           my $entry = &$extract_pool_data($usercfg->{pools}->{$poolpath});
+           $entry->{poolid} = $poolpath;
+           push @$res, $entry;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create_pool', 
+    protected => 1,
+    path => '', 
+    method => 'POST',
+    permissions => { 
+       check => ['perm', '/access', ['Sys.Modify']],
+    },
+    description => "Create new pool.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           poolid => { type => 'string', format => 'pve-poolid' },
+           comment => { type => 'string', optional => 1 },
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::AccessControl::lock_user_config(
+           sub {
+                       
+               my $usercfg = cfs_read_file("user.cfg");
+
+               my $pool = $param->{poolid};
+
+               die "pool '$pool' already exists\n" 
+                   if $usercfg->{pools}->{$pool};
+
+               $usercfg->{pools}->{$pool} = { vms => {}, storage => {} };
+
+               $usercfg->{pools}->{$pool}->{comment} = $param->{comment} if $param->{comment};
+
+               cfs_write_file("user.cfg", $usercfg);
+           }, "create pool failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update_pool', 
+    protected => 1,
+    path => '{poolid}', 
+    method => 'PUT',
+    permissions => { 
+       check => ['perm', '/access', ['Sys.Modify']],
+    },
+    description => "Update pool data.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           poolid => { type => 'string', format => 'pve-poolid' },
+           comment => { type => 'string', optional => 1 },
+           vms => { 
+               description => "List of virtual machines.",
+               type => 'string',  format => 'pve-vmid-list',  
+               optional => 1,
+           },
+           storage => { 
+               description => "List of storage IDs.",
+               type => 'string',  format => 'pve-storage-id-list',  
+               optional => 1,
+           },
+           delete => {
+               description => "Remove vms/storage (instead of adding it).",
+               type => 'boolean', 
+               optional => 1,
+           },
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::AccessControl::lock_user_config(
+           sub {
+                       
+               my $usercfg = cfs_read_file("user.cfg");
+
+               my $pool = $param->{poolid};
+       
+               my $data = $usercfg->{pools}->{$pool};
+
+               die "pool '$pool' does not exist\n" 
+                   if !$data;
+
+               $data->{comment} = $param->{comment} if defined($param->{comment});
+               
+               if (defined($param->{vms})) {
+                   foreach my $vmid (PVE::Tools::split_list($param->{vms})) {
+                       if ($param->{delete}) {
+                           die "VM $vmid is not a pool member\n"
+                               if !$data->{vms}->{$vmid};
+                           delete $data->{vms}->{$vmid};
+                           delete $usercfg->{vms}->{$vmid};
+                       } else {
+                           die "VM $vmid is already a pool member\n"
+                               if $data->{vms}->{$vmid};
+                           die "VM $vmid belongs to pool '$usercfg->{vms}->{$vmid}'\n"
+                               if $usercfg->{vms}->{$vmid};
+
+                           $data->{vms}->{$vmid} = 1;
+                           $usercfg->{vms}->{$vmid} = 1;
+                       }
+                   }
+               }
+
+               if (defined($param->{storage})) {
+                   foreach my $storeid (PVE::Tools::split_list($param->{storage})) {
+                       if ($param->{delete}) {
+                           die "Storage '$storeid' is not a pool member\n"
+                               if !$data->{storage}->{$storeid};
+                           delete $data->{storage}->{$storeid};
+                       } else {
+                           die "Storage '$storeid' is already a pool member\n"
+                               if $data->{storage}->{$storeid};
+
+                           $data->{storage}->{$storeid} = 1;
+                       }
+                   }
+               }
+
+               cfs_write_file("user.cfg", $usercfg);
+           }, "update pools failed");
+
+       return undef;
+    }});
+
+# fixme: return format!
+__PACKAGE__->register_method ({
+    name => 'read_pool', 
+    path => '{poolid}', 
+    method => 'GET',
+    permissions => { 
+       check => ['perm', '/access', ['Sys.Audit']],
+    },
+    description => "Get group configuration.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           poolid => {type => 'string', format => 'pve-poolid' },
+       },
+    },
+    returns => {},
+    code => sub {
+       my ($param) = @_;
+
+       my $usercfg = cfs_read_file("user.cfg");
+
+       my $pool = $param->{poolid};
+       
+       my $data = $usercfg->{pools}->{$pool};
+
+       die "pool '$pool' does not exist\n" 
+           if !$data;
+       return &$extract_pool_data($data, 1);
+    }});
+
+
+__PACKAGE__->register_method ({
+    name => 'delete_pool', 
+    protected => 1,
+    path => '{poolid}', 
+    method => 'DELETE',
+    permissions => { 
+       check => ['perm', '/access', ['Sys.Modify']],
+    },
+    description => "Delete group.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           poolid => { type => 'string', format => 'pve-poolid' },
+       }
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       PVE::AccessControl::lock_user_config(
+           sub {
+
+               my $usercfg = cfs_read_file("user.cfg");
+
+               my $pool = $param->{poolid};
+       
+               my $data = $usercfg->{pools}->{$pool};
+               
+               die "pool '$pool' does not exist\n" 
+                   if !$data;
+       
+               delete ($usercfg->{pools}->{$pool});
+
+               PVE::AccessControl::delete_pool_acl($pool, $usercfg);
+
+               cfs_write_file("user.cfg", $usercfg);
+           }, "delete pool failed");
+       
+       return undef;
+    }});
+
+1;
index 331823e..0dce3e6 100644 (file)
@@ -514,8 +514,8 @@ sub delete_user_acl {
        delete ($usercfg->{acl}->{$acl}->{users}->{$username}) 
            if $usercfg->{acl}->{$acl}->{users}->{$username};
     }
-
 }
+
 sub delete_group_acl {
 
     my ($group, $usercfg) = @_;
@@ -525,7 +525,18 @@ sub delete_group_acl {
        delete ($usercfg->{acl}->{$acl}->{groups}->{$group}) 
            if $usercfg->{acl}->{$acl}->{groups}->{$group};
     }
+}
+
+sub delete_pool_acl {
+
+    my ($pool, $usercfg) = @_;
 
+    my $path = "/pool/$pool";
+
+    foreach my $aclpath (keys %{$usercfg->{acl}}) {
+       delete ($usercfg->{acl}->{$aclpath})
+           if $usercfg->{acl}->{$aclpath} eq 'path';
+    }
 }
 
 # we automatically create some predefined roles by splitting privs
@@ -749,6 +760,20 @@ sub verify_rolename {
     return $rolename;
 }
 
+PVE::JSONSchema::register_format('pve-poolid', \&verify_groupname);
+sub verify_poolname {
+    my ($poolname, $noerr) = @_;
+
+    if ($poolname !~ m/^[A-Za-z0-9\.\-_]+$/) {
+
+       die "pool name '$poolname' contains invalid characters\n" if !$noerr;
+
+       return undef;
+    }
+    
+    return $poolname;
+}
+
 PVE::JSONSchema::register_format('pve-priv', \&verify_privname);
 sub verify_privname {
     my ($priv, $noerr) = @_;
@@ -915,42 +940,42 @@ sub parse_user_config {
                warn "user config - ignore invalid path in acl '$pathtxt'\n";
            }
        } elsif ($et eq 'pool') {
-           my ($pathtxt, $comment, $vmlist, $storelist) = @data;
-
-           if (my $path = normalize_path($pathtxt)) {
-               # make sure to add the pool (even if there are no members)
-               $cfg->{pools}->{$path} = { vms => {}, storage => {} } if !$cfg->{pools}->{$path};
+           my ($pool, $comment, $vmlist, $storelist) = @data;
 
-               $cfg->{pools}->{$path}->{comment} = PVE::Tools::decode_text($comment) if $comment;
+           if (!verify_poolname($pool, 1)) {
+               warn "user config - ignore pool '$pool' - invalid characters in pool name\n";
+               next;
+           }
 
-               foreach my $vmid (split_list($vmlist)) {
-                   if ($vmid !~ m/^\d+$/) {
-                       warn "user config - ignore invalid vmid '$vmid' in pool '$path'\n";
-                       next;
-                   }
-                   $vmid = int($vmid);
+           # make sure to add the pool (even if there are no members)
+           $cfg->{pools}->{$pool} = { vms => {}, storage => {} } if !$cfg->{pools}->{$pool};
 
-                   if ($cfg->{vms}->{$vmid}) {
-                       warn "user config - ignore duplicate vmid '$vmid' in pool '$path'\n";
-                       next;
-                   }
+           $cfg->{pools}->{$pool}->{comment} = PVE::Tools::decode_text($comment) if $comment;
 
-                   $cfg->{pools}->{$path}->{vms}->{$vmid} = 1;
-                   
-                   # record vmid ==> pool relation
-                   $cfg->{vms}->{$vmid} = $path;
+           foreach my $vmid (split_list($vmlist)) {
+               if ($vmid !~ m/^\d+$/) {
+                   warn "user config - ignore invalid vmid '$vmid' in pool '$pool'\n";
+                   next;
                }
+               $vmid = int($vmid);
 
-               foreach my $storeid (split_list($storelist)) {
-                   if ($storeid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
-                       warn "user config - ignore invalid storage '$storeid' in pool '$path'\n";
-                       next;
-                   }
-                   $cfg->{pools}->{$path}->{storage}->{$storeid} = 1;
+               if ($cfg->{vms}->{$vmid}) {
+                   warn "user config - ignore duplicate vmid '$vmid' in pool '$pool'\n";
+                   next;
                }
 
-           } else {
-               warn "user config - ignore invalid path in pool'$pathtxt'\n";
+               $cfg->{pools}->{$pool}->{vms}->{$vmid} = 1;
+                   
+               # record vmid ==> pool relation
+               $cfg->{vms}->{$vmid} = $pool;
+           }
+
+           foreach my $storeid (split_list($storelist)) {
+               if ($storeid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
+                   warn "user config - ignore invalid storage '$storeid' in pool '$pool'\n";
+                   next;
+               }
+               $cfg->{pools}->{$pool}->{storage}->{$storeid} = 1;
            }
        } else {
            warn "user config - ignore config line: $line\n";
@@ -1174,12 +1199,12 @@ sub write_user_config {
 
     $data .= "\n";
 
-    foreach my $path (keys %{$cfg->{pools}}) {
-       my $d = $cfg->{pools}->{$path};
+    foreach my $pool (keys %{$cfg->{pools}}) {
+       my $d = $cfg->{pools}->{$pool};
        my $vmlist = join (',', keys %{$d->{vms}});
        my $storelist = join (',', keys %{$d->{storage}});
        my $comment = $d->{comment} ? PVE::Tools::encode_text($d->{comment}) : '';      
-       $data .= "pool:$path:$comment:$vmlist:$storelist:\n";
+       $data .= "pool:$pool:$comment:$vmlist:$storelist:\n";
     }
 
     $data .= "\n";
index 0b8147e..e6651d0 100644 (file)
@@ -106,9 +106,9 @@ my $compile_acl_path = sub {
     if (!$data->{poolroles}) {
        $data->{poolroles} = {}; 
  
-       foreach my $poolpath (keys %{$cfg->{pools}}) {
-           my $d = $cfg->{pools}->{$poolpath};
-           my @ra = PVE::AccessControl::roles($cfg, $user, "/pool$poolpath"); # pool roles
+       foreach my $pool (keys %{$cfg->{pools}}) {
+           my $d = $cfg->{pools}->{$pool};
+           my @ra = PVE::AccessControl::roles($cfg, $user, "/pool/$pool"); # pool roles
            next if !scalar(@ra);
            foreach my $vmid (keys %{$d->{vms}}) {
                for my $role (@ra) {
diff --git a/pveum b/pveum
index 2c468d0..9214d1f 100755 (executable)
--- a/pveum
+++ b/pveum
@@ -14,6 +14,7 @@ use PVE::API2::User;
 use PVE::API2::Group;
 use PVE::API2::Role;
 use PVE::API2::ACL;
+use PVE::API2::Pool;
 use PVE::API2::AccessControl;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::CLIHandler;
@@ -72,6 +73,10 @@ my $cmddef = {
     rolemod => [ 'PVE::API2::Role', 'update_role', ['roleid'] ],
     roledel => [ 'PVE::API2::Role', 'delete_role', ['roleid'] ],
 
+    pooladd => [ 'PVE::API2::Pool', 'create_pool', ['poolid'] ],
+    poolmod => [ 'PVE::API2::Pool', 'update_pool', ['poolid'] ],
+    pooldel => [ 'PVE::API2::Pool', 'delete_pool', ['poolid'] ],
+
     aclmod => [ 'PVE::API2::ACL', 'update_acl', ['path', 'roles'], { delete => 0 }],
     acldel => [ 'PVE::API2::ACL', 'update_acl', ['path', 'roles'], { delete => 1 }],
 };