pve-manager/PVE/API2/Scan.pm
Dominik Csapak 782be7dae2 api/Scan: allow to scan a nodes PCI devices
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2018-11-22 13:30:21 +01:00

550 lines
12 KiB
Perl

package PVE::API2::Scan;
use strict;
use warnings;
use PVE::SafeSyslog;
use PVE::Storage;
use PVE::Storage::LVMPlugin;
use PVE::SysFSTools;
use PVE::JSONSchema qw(get_standard_option);
use PVE::RESTHandler;
use base qw(PVE::RESTHandler);
__PACKAGE__->register_method ({
name => 'index',
path => '',
method => 'GET',
description => "Index of available scan methods",
permissions => {
user => 'all',
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => { method => { type => 'string'} },
},
links => [ { rel => 'child', href => "{method}" } ],
},
code => sub {
my ($param) = @_;
my $res = [
{ method => 'lvm' },
{ method => 'iscsi' },
{ method => 'nfs' },
{ method => 'glusterfs' },
{ method => 'usb' },
{ method => 'zfs' },
{ method => 'cifs' },
{ method => 'pci' },
];
return $res;
}});
__PACKAGE__->register_method ({
name => 'zfsscan',
path => 'zfs',
method => 'GET',
description => "Scan zfs pool list on local node.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
pool => {
description => "ZFS pool name.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
return PVE::Storage::scan_zfs();
}});
__PACKAGE__->register_method ({
name => 'nfsscan',
path => 'nfs',
method => 'GET',
description => "Scan remote NFS server.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
server => {
description => "The server address (name or IP).",
type => 'string', format => 'pve-storage-server',
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
path => {
description => "The exported path.",
type => 'string',
},
options => {
description => "NFS export options.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
my $server = $param->{server};
my $res = PVE::Storage::scan_nfs($server);
my $data = [];
foreach my $k (keys %$res) {
push @$data, { path => $k, options => $res->{$k} };
}
return $data;
}});
__PACKAGE__->register_method ({
name => 'cifsscan',
path => 'cifs',
method => 'GET',
description => "Scan remote CIFS server.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
server => {
description => "The server address (name or IP).",
type => 'string', format => 'pve-storage-server',
},
username => {
description => "User name.",
type => 'string',
optional => 1,
},
password => {
description => "User password.",
type => 'string',
optional => 1,
},
domain => {
description => "SMB domain (Workgroup).",
type => 'string',
optional => 1,
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
share => {
description => "The cifs share name.",
type => 'string',
},
description => {
description => "Descriptive text from server.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
my $server = $param->{server};
my $username = $param->{username};
my $password = $param->{password};
my $domain = $param->{domain};
my $res = PVE::Storage::scan_cifs($server, $username, $password, $domain);
my $data = [];
foreach my $k (keys %$res) {
next if $k =~ m/NT_STATUS_/;
push @$data, { share => $k, description => $res->{$k} };
}
return $data;
}});
# Note: GlusterFS currently does not have an equivalent of showmount.
# As workaround, we simply use nfs showmount.
# see http://www.gluster.org/category/volumes/
__PACKAGE__->register_method ({
name => 'glusterfsscan',
path => 'glusterfs',
method => 'GET',
description => "Scan remote GlusterFS server.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
server => {
description => "The server address (name or IP).",
type => 'string', format => 'pve-storage-server',
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
volname => {
description => "The volume name.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
my $server = $param->{server};
my $res = PVE::Storage::scan_nfs($server);
my $data = [];
foreach my $path (keys %$res) {
if ($path =~ m!^/([^\s/]+)$!) {
push @$data, { volname => $1 };
}
}
return $data;
}});
__PACKAGE__->register_method ({
name => 'iscsiscan',
path => 'iscsi',
method => 'GET',
description => "Scan remote iSCSI server.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
portal => {
description => "The iSCSI portal (IP or DNS name with optional port).",
type => 'string', format => 'pve-storage-portal-dns',
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
target => {
description => "The iSCSI target name.",
type => 'string',
},
portal => {
description => "The iSCSI portal name.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
my $res = PVE::Storage::scan_iscsi($param->{portal});
my $data = [];
foreach my $k (keys %$res) {
push @$data, { target => $k, portal => join(',', @{$res->{$k}}) };
}
return $data;
}});
__PACKAGE__->register_method ({
name => 'lvmscan',
path => 'lvm',
method => 'GET',
description => "List local LVM volume groups.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
vg => {
description => "The LVM logical volume group name.",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
my $res = PVE::Storage::LVMPlugin::lvm_vgs();
return PVE::RESTHandler::hash_to_array($res, 'vg');
}});
__PACKAGE__->register_method ({
name => 'lvmthinscan',
path => 'lvmthin',
method => 'GET',
description => "List local LVM Thin Pools.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/storage', ['Datastore.Allocate']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
vg => {
type => 'string',
pattern => '[a-zA-Z0-9\.\+\_][a-zA-Z0-9\.\+\_\-]+', # see lvm(8) manpage
maxLength => 100,
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
lv => {
description => "The LVM Thin Pool name (LVM logical volume).",
type => 'string',
},
},
},
},
code => sub {
my ($param) = @_;
return PVE::Storage::LvmThinPlugin::list_thinpools($param->{vg});
}});
__PACKAGE__->register_method ({
name => 'usbscan',
path => 'usb',
method => 'GET',
description => "List local USB devices.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/', ['Sys.Modify']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
busnum => { type => 'integer'},
devnum => { type => 'integer'},
port => { type => 'integer'},
usbpath => { type => 'string', optional => 1},
level => { type => 'integer'},
class => { type => 'integer'},
vendid => { type => 'string'},
prodid => { type => 'string'},
speed => { type => 'string'},
product => { type => 'string', optional => 1 },
serial => { type => 'string', optional => 1 },
manufacturer => { type => 'string', optional => 1 },
},
},
},
code => sub {
my ($param) = @_;
return PVE::SysFSTools::scan_usb();
}});
my $default_class_blacklist = "05;06;08;0b";
__PACKAGE__->register_method ({
name => 'pciscan',
path => 'pci',
method => 'GET',
description => "List local PCI devices.",
protected => 1,
proxyto => "node",
permissions => {
check => ['perm', '/', ['Sys.Modify']],
},
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
'pci-class-blacklist' => {
type => 'string',
format => 'string-list',
default => $default_class_blacklist,
optional => 1,
description => "A list of blacklisted PCI classes, which will ".
"not be returned. Following are filtered by ".
"default: Memory Controller (05), Bridge (06), ".
"Generic System Peripheral (08) and ".
"Processor (0b).",
},
verbose => {
type => 'boolean',
default => 1,
optional => 1,
description => "If disabled, does only print the PCI IDs. "
."Otherwise, additional information like vendor "
."and device will be returned.",
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
id => {
type => 'string',
description => "The PCI ID.",
},
class => {
type => 'string',
description => 'The PCI Class of the device.',
},
vendor => {
type => 'string',
description => 'The Vendor ID.',
},
vendor_name => {
type => 'string',
optional => 1,
},
device => {
type => 'string',
description => 'The Device ID.',
},
device_name => {
type => 'string',
optional => 1,
},
subsystem_vendor => {
type => 'string',
description => 'The Subsystem Vendor ID.',
optional => 1,
},
subsystem_vendor_name => {
type => 'string',
optional => 1,
},
subsystem_device => {
type => 'string',
description => 'The Subsystem Device ID.',
optional => 1,
},
subsystem_device_name => {
type => 'string',
optional => 1,
},
iommugroup => {
type => 'integer',
description => "The IOMMU group in which the device is in.".
" If no IOMMU group is detected, it is set to -1.",
},
mdev => {
type => 'boolean',
optional => 1,
description => "If set, marks that the device is capable "
."of creating mediated devices.",
}
},
},
},
code => sub {
my ($param) = @_;
my $blacklist = $param->{'pci-class-blacklist'} // $default_class_blacklist;
my $class_regex = join('|', PVE::Tools::split_list($blacklist));
my $filter;
if ($class_regex ne '') {
$filter = sub {
my ($pcidevice) = @_;
if ($pcidevice->{class} =~ m/^0x(?:$class_regex)/) {
return 0;
}
return 1;
};
}
my $verbose = $param->{verbose} // 1;
return PVE::SysFSTools::lspci($filter, $verbose);
}});
1;