Thanks Pascal,
I'll try this solution as soon as possible!
However, will this change may be undone after an update?
The main point is the lack of information about the USB devices on vCenter.
I bypassed this limitation through the script below. I'm not sure it will work in all scenarios.
Not my best Perl script but it has worked very well.
--x--
#!/usr/bin/perl
use strict;
use warnings;
use VMware::VILib;
use VMware::VIRuntime;
use Net::SSH::Expect;
#$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
############################################################################
sub host_ssh_manager($$) {
my $h = shift;
my $state = shift;
if ($state) {
$state = "starting";
} else {
$state = "stopping";
}
my $servicemanager = Vim::get_view(
mo_ref => $h->get_property('configManager.serviceSystem')
);
my $hostservices = $servicemanager->serviceInfo->service;
foreach my $s (@$hostservices) {
if ($s->label eq 'SSH') {
if (! $s->running) {
eval {
if ($state eq 'starting') {
$servicemanager->StartService(id => $s->key);
print "SSH started.\n";
}
};
} else {
eval {
if ($state eq 'stopping') {
$servicemanager->StopService(id => $s->key);
print "SSH stopped.\n";
}
};
}
if ($@) {
print "Error $state SSH service.\n";
}
last;
}
}
}
############################################################################
sub main() {
my %opts = (
host => {
type => "=s",
help => "Target Host",
required => 0,
},
cluster => {
type => "=s",
help => "Target Cluster",
required => 0,
},
);
Opts::add_options(%opts);
Opts::parse();
Opts::validate();
Util::connect();
my $host_username;
my $host_password;
my $my_host = Opts::get_option('host');
$my_host =~ s/\%(..)/chr(hex($1))/gie if (defined($my_host));
my $my_cluster = Opts::get_option('cluster');
$my_cluster =~ s/\%(..)/chr(hex($1))/gie if (defined($my_cluster));
print "Host username: ";
chop($host_username = );
print "Host password: ";
system("stty -echo");
chop($host_password = );
print "\n";
system("stty echo");
my $list_host;
my $host_obj;
# Host name is specified on command line
if (defined($my_host)) {
$host_obj = Vim::find_entity_view(
view_type => 'HostSystem',
properties => [
'name',
'runtime.powerState',
'vm',
'configManager.serviceSystem'
],
filter => {'name' => "$my_host"},
);
if (defined($host_obj)) {
if ($host_obj->get_property('runtime.powerState')->val
eq 'poweredOn') {
push @$list_host, $host_obj;
}
}
}
# Cluster name is specified on command line
if (defined($my_cluster)) {
my $cluster_obj = Vim::find_entity_view(
view_type => 'ClusterComputeResource',
properties => [
'name',
'host',
],
filter => {'name' => "$my_cluster"},
);
if (defined($cluster_obj)) {
print "Cluster: $my_cluster\n";
my $cluster_hosts = $cluster_obj->host;
foreach my $h (@$cluster_hosts) {
$host_obj = Vim::get_view(
mo_ref => $h,
properties => [
'configManager.serviceSystem',
'name',
'runtime.powerState',
'vm',
]
);
if ($host_obj->get_property('runtime.powerState')->val
eq 'poweredOn') {
push @$list_host, $host_obj;
}
}
}
}
# Search for USB devices
foreach my $h (@$list_host) {
my $hostname = $h->name;
print "Host: " . $hostname . "\n";
# Search VMs
my $list_vm = $h->vm;
my %vms;
my %paths;
my %dongles;
foreach my $vm_ref (@$list_vm) {
my $vm_obj = Vim::get_view(
mo_ref => $vm_ref,
properties => [
'config.hardware.device',
'name',
]
);
my $vm_name = $vm_obj->name;
my $vm_devices = $vm_obj->{'config.hardware.device'};
# Search USB devices
foreach my $device (@$vm_devices) {
if($device->isa('VirtualUSB')) {
my $usbdevice_name = $device->backing->deviceName;
# Unfortunately, USB path (?) is the "primary key" for vCenter.
my $usbdevice_path =
($usbdevice_name =~ /(path:\S+)/)[0];
if ($usbdevice_path =~ /path:(\d+)\/\d+\/(\d+)/) {
$usbdevice_path = "path:" . $1 . "/X/" . $2;
}
my $usbdevice_info = $device->deviceInfo->summary;
if (defined($usbdevice_path) &&
defined($usbdevice_info)) {
if (! $device->connected) {
$vms{$vm_name}{$usbdevice_path} =
"DEVICE NOT PRESENT!";
} else {
$vms{$vm_name}{$usbdevice_path} = $usbdevice_info;
}
$paths{$usbdevice_path} = $vm_name;
}
}
}
}
# Start sshd on host
host_ssh_manager($h,1);
# Search information on the host's logs by ssh command lines
my $ssh = Net::SSH::Expect->new (
host => "$hostname",
password=> "$host_password",
user => "$host_username",
);
my $login_output = $ssh->login();
my ($exec,$line,$date,$vm,$device,$available);
my ($serial,$bus,$level,$port,$path);
my $usblog = 1;
my $usbdevices = 1;
# Look at /dev/usbdevices.
if ($usbdevices) {
$exec = $ssh->send("cat /dev/usbdevices");
$level = 0;
while (defined($line = $ssh->read_line())) {
if ($line =~ /^T:\s+Bus=\s*(\d+)\sLev=\s*(\d+)\sPrnt=\s*(\d+)\sPort=\s*(\d+)\sCnt=\s*(\d+)\sDev#=\s*(\S+)\s/) {
$bus = $1;
$level = $2;
$port = $4;
if ($level > 1) {
$path = sprintf("path:%d/X/%d",$bus,$port);
} elsif ($level == 1) {
$path = sprintf("path:%d/%d",$bus,$port);
}
$device = $serial = "";
$available = 0;
}
elsif ($line =~ /V:\s+Available for Passthrough/) {
$available = 1;
}
elsif ($line =~ /Product=(.*)/) {
$device = $1;
}
elsif ($line =~ /SerialNumber=(.*)/) {
$serial = $1;
}
elsif ($line =~ /^E:/ && $path && $device && $available) {
$dongles{$path}{device} = $device;
$dongles{$path}{serial} = $serial;
$path = $device = $serial = $port = $bus = "";
$level = $available = 0;
}
}
}
# Look at /var/log/usb.log (ESXi 5.0).
if ($usblog) {
$exec = $ssh->send("grep 'USBArb: Device' /var/log/usb.log");
while (defined($line = $ssh->read_line())) {
$date = $vm = $device = $serial = $path = 0;
if ($line =~ /^([^\ ]+)/) {
$date = $1;
}
if ($line =~ /:name:(.*?) vid/) {
$device = $1;
}
if ($line =~ /(path:[^\ ]+) /) {
$path = $1;
if ($path =~ /path:(\d+)\/\d+\/(\d+)/) {
$path = "path:" . $1 . "/X/" . $2;
}
}
if ($line =~ /serialnum:([^\ ]+) /) {
$serial = $1;
}
if ($line =~ /owner:(\S+)/) {
$vm = $1;
}
if ($device && $path && $vm &&
defined($dongles{$path})
) {
if ($vm !~ /\(null\)/) {
$dongles{$path}{vm} = $vm;
} else {
undef $dongles{$path}{vm};
}
$dongles{$path}{date} = $date;
$dongles{$path}{device} = $device;
$dongles{$path}{serial} = $serial;
}
}
}
if (!%dongles) {
print "\n";
print "\tOBS: No dongles found in the host logs!";
print "\n";
}
# Stop sshd
host_ssh_manager($h,0);
# Show the list of VMs with USB dongles
foreach my $vm_name (sort keys %vms) {
print "\tVM: " . $vm_name . "\n";
foreach my $path (sort keys %{$vms{$vm_name}}) {
if (defined($dongles{$path}{vm})) {
if ($dongles{$path}{vm} eq $vm_name) {
print "\t\tPath: " . $path;
print "\n";
print "\t\tDongle: " . $dongles{$path}{device};
print "\n";
print "\t\tSerial: " . $dongles{$path}{serial};
print "\n";
print "\t\tConnected in: " . $dongles{$path}{date};
print "\n";
print "\n";
} else {
print "\t\t>>> Dongle not found in the host logs.";
print "\n";
print "\t\tPath: " . $path;
print "\n";
print "\t\tDongle: " . $vms{$vm_name}{$path};
print "\n";
print "\n";
}
} else {
print "\t\t>>> Dongle not found in the host logs.";
print "\n";
print "\t\tPath: " . $path;
print "\n";
print "\t\tDongle: " . $vms{$vm_name}{$path};
print "\n";
if (defined($dongles{$path}{serial})) {
print "\t\tSerial: " . $dongles{$path}{serial};
print "\n";
}
print "\n";
}
}
}
print "\n";
print "\tDongles connected to the host but not associated with VMs:\n";
foreach my $path (sort keys %dongles) {
if (! defined($dongles{$path}{vm}) &&
! defined($paths{$path})) {
print "\t\tPath: " . $path;
print "\n";
print "\t\tDongle: " . $dongles{$path}{device};
print "\n";
print "\t\tSerial: " . $dongles{$path}{serial};
print "\n";
if (defined($dongles{$path}{date})) {
print "\t\tConnected in: " . $dongles{$path}{date};
print "\n";
}
print "\n";
}
}
print "\n";
}
Util::disconnect();
}
main();
#==eof==