adds the basic api calls to list/get/create/update/delete device
mappings

these api calls are only per node, so it only affects
the node specific mapping (thought consistency checks are
done for the whole config, e.g if an id exists already on another
node with a different type)

Signed-off-by: Dominik Csapak <d.csa...@proxmox.com>
---
 PVE/API2/Hardware.pm         |   6 +
 PVE/API2/Hardware/Makefile   |   1 +
 PVE/API2/Hardware/Mapping.pm | 708 +++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)
 create mode 100644 PVE/API2/Hardware/Mapping.pm

diff --git a/PVE/API2/Hardware.pm b/PVE/API2/Hardware.pm
index f59bfbe0..ab7b5e63 100644
--- a/PVE/API2/Hardware.pm
+++ b/PVE/API2/Hardware.pm
@@ -8,6 +8,7 @@ use PVE::RESTHandler;
 
 use PVE::API2::Hardware::PCI;
 use PVE::API2::Hardware::USB;
+use PVE::API2::Hardware::Mapping;
 
 use base qw(PVE::RESTHandler);
 
@@ -21,6 +22,10 @@ __PACKAGE__->register_method ({
     path => 'usb',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Hardware::Mapping",
+    path => "mapping",
+});
 
 __PACKAGE__->register_method ({
     name => 'index',
@@ -50,6 +55,7 @@ __PACKAGE__->register_method ({
        my $res = [
            { type => 'pci' },
            { type => 'usb' },
+           { type => 'mapping' },
        ];
 
        return $res;
diff --git a/PVE/API2/Hardware/Makefile b/PVE/API2/Hardware/Makefile
index d27d2201..9f5f3231 100644
--- a/PVE/API2/Hardware/Makefile
+++ b/PVE/API2/Hardware/Makefile
@@ -3,6 +3,7 @@ include ../../../defines.mk
 PERLSOURCE=                    \
        PCI.pm                  \
        USB.pm                  \
+       Mapping.pm                      \
 
 all:
 
diff --git a/PVE/API2/Hardware/Mapping.pm b/PVE/API2/Hardware/Mapping.pm
new file mode 100644
index 00000000..72a46ffe
--- /dev/null
+++ b/PVE/API2/Hardware/Mapping.pm
@@ -0,0 +1,708 @@
+package PVE::API2::Hardware::Mapping::USB;
+
+use strict;
+use warnings;
+
+use Storable qw(dclone);
+
+use PVE::Cluster qw(cfs_lock_file);
+use PVE::HardwareMap;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(extract_param);
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "USB Hardware Mapping",
+    permissions => {
+       description => "Only lists entries where you have 'Hardware.Audit', 
'Hardware.Use', 'Hardware.Configure' permissions on '/hardware/<name>'.",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { name => { type => 'string'} },
+       },
+       links => [ { rel => 'child', href => "{name}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+       my $node = $param->{node};
+
+       my $cfg = PVE::HardwareMap::config();
+
+       my $res = [];
+
+       my $privs = ['Hardware.Audit', 'Hardware.Use', 'Hardware.Configure'];
+
+       for my $id (keys $cfg->{usb}->%*) {
+
+           next if !defined($cfg->{usb}->{$id}->{$node});
+           next if !$rpcenv->check_hw_perm($authuser, $id, $privs, 1, 1);
+
+           my $entry = dclone($cfg->{usb}->{$id}->{$node});
+           $entry->{name} = $id;
+           $entry->{node} = $node;
+           $entry->{type} = 'usb';
+
+           eval {
+               PVE::HardwareMap::assert_device_valid('usb', $entry);
+           };
+           if (my $err = $@) {
+               $entry->{valid} = 0;
+               $entry->{errmsg} = "$err";
+           } else {
+               $entry->{valid} = 1;
+           }
+
+           push @$res, $entry;
+       }
+
+       return $res;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'get',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'GET',
+    description => "GET Hardware Mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Audit']],
+                   ['perm', '/hardware/{name}', ['Hardware.Audit']],
+                ],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           name => {
+               type => 'string',
+               format => 'pve-configid',
+           },
+           node => get_standard_option('pve-node'),
+       }
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = PVE::HardwareMap::config();
+       my $name = $param->{name};
+       my $node = $param->{node};
+
+       die "mapping '$param->{name}' not found on '$param->{node}'\n"
+           if !defined($cfg->{usb}->{$name}) || 
!defined($cfg->{usb}->{$name}->{$node});
+
+       my $data = dclone($cfg->{usb}->{$name}->{$node});
+
+       eval {
+           PVE::HardwareMap::assert_device_valid('usb', $data);
+       };
+       if (my $err = $@) {
+           $data->{valid} = 0;
+           $data->{errmsg} = "$err";
+       } else {
+           $data->{valid} = 1;
+       }
+
+       $data->{digest} = $cfg->{digest};
+
+       return $data;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    proxyto => 'node',
+    path => '',
+    method => 'POST',
+    description => "Create a new hardware mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    # todo parameters
+    parameters => PVE::HardwareMap::createSchema('usb'),
+    returns => {
+       type => 'null',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $name = extract_param($param, 'name');
+       my $node = extract_param($param, 'node');
+
+       PVE::HardwareMap::assert_device_valid('usb', $param);
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           # avoid autovification
+           if (defined($cfg->{usb}->{$name}) && 
defined($cfg->{usb}->{$name}->{$node})) {
+               die "mapping '$name' for node '$node' already defined\n";
+           }
+
+           for my $type (keys %$cfg) {
+               next if $type eq 'usb';
+               next if $type eq 'digest';
+               die "'$name' already defined as type '$type'\n"
+                   if defined($cfg->{$type}->{$name});
+           }
+
+           $cfg->{usb}->{$name}->{$node} = $param;
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "create hardware mapping failed");
+
+       return;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'PUT',
+    description => "Update a hardware mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    parameters => PVE::HardwareMap::updateSchema('usb'),
+    returns => {
+       type => 'null',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $digest = extract_param($param, 'digest');
+       my $delete = extract_param($param, 'delete');
+       my $name = extract_param($param, 'name');
+       my $node = extract_param($param, 'node');
+       if ($delete) {
+           $delete = [ PVE::Tools::split_list($delete) ];
+       }
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           PVE::Tools::assert_if_modified($cfg->{digest}, $digest) if 
defined($digest);
+
+           die "no mapping '$name' on node '$node'\n" if !$cfg->{usb}->{$name} 
|| !$cfg->{usb}->{$name}->{$node};
+           my $data = $cfg->{usb}->{$name}->{$node};
+
+           for my $k (keys %$param) {
+               $data->{$k} = $param->{$k};
+           }
+
+           if ($delete) {
+               my $options = PVE::HardwareMap::options('usb');
+               for my $k (@$delete) {
+                   my $d = $options->{$k} || die "no such option '$k'\n";
+                   die "unable to delete required option '$k'\n" if 
!$d->{optional};
+                   die "unable to delete fixed option '$k'\n" if $d->{fixed};
+                   die "cannot set and delete property '$k' at the same 
time!\n"
+                       if defined($param->{$k});
+
+                   delete $data->{$k};
+               }
+           }
+
+           PVE::HardwareMap::assert_device_valid('usb', $data);
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "update hardware mapping failed");
+
+       return;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'DELETE',
+    description => "Remove Hardware Mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           name => {
+               type => 'string',
+               format => 'pve-configid',
+           },
+       }
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $name = $param->{name};
+       my $node = $param->{node};
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           if ($cfg->{usb}->{$name}) {
+               delete $cfg->{usb}->{$name}->{$node};
+               if (keys $cfg->{usb}->{$name}->%* < 1) {
+                   delete $cfg->{usb}->{$name};
+               }
+           }
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "delete hardware mapping failed");
+
+       return;
+    }});
+
+package PVE::API2::Hardware::Mapping::PCI;
+
+use strict;
+use warnings;
+
+use Storable qw(dclone);
+
+use PVE::Cluster qw(cfs_lock_file);
+use PVE::HardwareMap;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(extract_param);
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "PCI Hardware Mapping",
+    permissions => {
+       description => "Only lists entries where you have 'Hardware.Audit', 
'Hardware.Use', 'Hardware.Configure' permissions on '/hardware/<name>'.",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { name => { type => 'string'} },
+       },
+       links => [ { rel => 'child', href => "{name}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+       my $node = $param->{node};
+
+       my $cfg = PVE::HardwareMap::config();
+
+       my $res = [];
+
+       my $privs = ['Hardware.Audit', 'Hardware.Use', 'Hardware.Configure'];
+
+       for my $id (keys $cfg->{pci}->%*) {
+
+           next if !defined($cfg->{pci}->{$id}->{$node});
+           next if !$rpcenv->check_hw_perm($authuser, $id, $privs, 1, 1);
+
+           my $entry = dclone($cfg->{pci}->{$id}->{$node});
+           $entry->{name} = $id;
+           $entry->{node} = $node;
+           $entry->{type} = 'pci';
+
+           eval {
+               PVE::HardwareMap::assert_device_valid('pci', $entry);
+           };
+           if (my $err = $@) {
+               $entry->{valid} = 0;
+               $entry->{errmsg} = "$err";
+           } else {
+               $entry->{valid} = 1;
+           }
+
+           push @$res, $entry;
+       }
+
+       return $res;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'get',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'GET',
+    description => "GET Hardware Mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Audit']],
+                   ['perm', '/hardware/{name}', ['Hardware.Audit']],
+                ],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           name => {
+               type => 'string',
+               format => 'pve-configid',
+           },
+           node => get_standard_option('pve-node'),
+       }
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = PVE::HardwareMap::config();
+       my $name = $param->{name};
+       my $node = $param->{node};
+
+       die "mapping '$param->{name}' not found on '$param->{node}'\n"
+           if !defined($cfg->{pci}->{$name}) || 
!defined($cfg->{pci}->{$name}->{$node});
+
+       my $data = dclone($cfg->{pci}->{$name}->{$node});
+
+       eval {
+           PVE::HardwareMap::assert_device_valid('pci', $data);
+       };
+       if (my $err = $@) {
+           $data->{valid} = 0;
+           $data->{errmsg} = "$err";
+       } else {
+           $data->{valid} = 1;
+       }
+
+       $data->{digest} = $cfg->{digest};
+
+       return $data;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    proxyto => 'node',
+    path => '',
+    method => 'POST',
+    description => "Create a new hardware mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    # todo parameters
+    parameters => PVE::HardwareMap::createSchema('pci'),
+    returns => {
+       type => 'null',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $name = extract_param($param, 'name');
+       my $node = extract_param($param, 'node');
+
+       PVE::HardwareMap::assert_device_valid('pci', $param);
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           # avoid autovification
+           if (defined($cfg->{pci}->{$name}) && 
defined($cfg->{pci}->{$name}->{$node})) {
+               die "mapping '$name' for node '$node' already defined\n";
+           }
+
+           for my $type (keys %$cfg) {
+               next if $type eq 'pci';
+               next if $type eq 'digest';
+               die "'$name' already defined as type '$type'\n"
+                   if defined($cfg->{$type}->{$name});
+           }
+
+           $cfg->{pci}->{$name}->{$node} = $param;
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "create hardware mapping failed");
+
+       return;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'PUT',
+    description => "Update a hardware mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    parameters => PVE::HardwareMap::updateSchema('pci'),
+    returns => {
+       type => 'null',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $digest = extract_param($param, 'digest');
+       my $delete = extract_param($param, 'delete');
+       my $name = extract_param($param, 'name');
+       my $node = extract_param($param, 'node');
+       if ($delete) {
+           $delete = [ PVE::Tools::split_list($delete) ];
+       }
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           PVE::Tools::assert_if_modified($cfg->{digest}, $digest) if 
defined($digest);
+
+           die "no mapping '$name' on node '$node'\n" if !$cfg->{pci}->{$name} 
|| !$cfg->{pci}->{$name}->{$node};
+           my $data = $cfg->{pci}->{$name}->{$node};
+
+           for my $k (keys %$param) {
+               $data->{$k} = $param->{$k};
+           }
+
+           if ($delete) {
+               my $options = PVE::HardwareMap::options('pci');
+               for my $k (@$delete) {
+                   my $d = $options->{$k} || die "no such option '$k'\n";
+                   die "unable to delete required option '$k'\n" if 
!$d->{optional};
+                   die "unable to delete fixed option '$k'\n" if $d->{fixed};
+                   die "cannot set and delete property '$k' at the same 
time!\n"
+                       if defined($param->{$k});
+
+                   delete $data->{$k};
+               }
+           }
+
+           PVE::HardwareMap::assert_device_valid('pci', $data);
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "update hardware mapping failed");
+
+       return;
+    },
+});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    proxyto => 'node',
+    path => '{name}',
+    method => 'DELETE',
+    description => "Remove Hardware Mapping.",
+    permissions => {
+       check => [ 'and',
+                   ['perm', '/node/{node}', ['Sys.Modify']],
+                   ['perm', '/hardware/{name}', ['Hardware.Configure']],
+                ],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           name => {
+               type => 'string',
+               format => 'pve-configid',
+           },
+       }
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $name = $param->{name};
+       my $node = $param->{node};
+
+       PVE::HardwareMap::lock_config(sub {
+           my $cfg = PVE::HardwareMap::config();
+
+           if ($cfg->{pci}->{$name}) {
+               delete $cfg->{pci}->{$name}->{$node};
+               if (keys $cfg->{pci}->{$name}->%* < 1) {
+                   delete $cfg->{pci}->{$name};
+               }
+           }
+
+           PVE::HardwareMap::write_config($cfg);
+
+       }, "delete hardware mapping failed");
+
+       return;
+    }});
+
+package PVE::API2::Hardware::Mapping;
+
+use strict;
+use warnings;
+
+use Storable qw(dclone);
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Hardware::Mapping::PCI",
+    path => 'pci',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Hardware::Mapping::USB",
+    path => 'usb',
+});
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "Index of hardware types",
+    permissions => {
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { type => { type => 'string'} },
+       },
+       links => [ { rel => 'child', href => "{type}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $res = [
+           { type => 'pci' },
+           { type => 'usb' },
+           { type => 'all' },
+       ];
+
+       return $res;
+    }
+});
+
+__PACKAGE__->register_method ({
+    name => 'get_all',
+    path => 'all',
+    protected => 1,
+    proxyto => 'node',
+    method => 'GET',
+    description => "Hardware Mapping",
+    permissions => {
+       description => "Only lists entries where you have 'Hardware.Audit', 
'Hardware.Use', 'Hardware.Configure' permissions on '/hardware/<name>'.",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { name => { type => 'string'} },
+       },
+       links => [ { rel => 'child', href => "{name}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+       my $node = $param->{node};
+
+       my $cfg = PVE::HardwareMap::config();
+
+       my $res = [];
+
+       my $privs = ['Hardware.Audit', 'Hardware.Use', 'Hardware.Configure'];
+
+       for my $type (keys $cfg->%*) {
+           next if $type eq 'digest';
+           for my $id (keys $cfg->{$type}->%*) {
+
+               next if !defined($cfg->{$type}->{$id}->{$node});
+               next if !$rpcenv->check_hw_perm($authuser, $id, $privs, 1, 1);
+
+               my $entry = dclone($cfg->{$type}->{$id}->{$node});
+               $entry->{name} = $id;
+               $entry->{node} = $node;
+               $entry->{type} = $type;
+
+               eval {
+                   PVE::HardwareMap::assert_device_valid($type, $entry);
+               };
+               if (my $err = $@) {
+                   $entry->{valid} = 0;
+                   $entry->{errmsg} = "$err";
+               } else {
+                   $entry->{valid} = 1;
+               }
+
+               push @$res, $entry;
+           }
+       }
+
+       return $res;
+    },
+});
+
+1;
-- 
2.30.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to