EduLima22
Contributor
Contributor

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==
Reply
0 Kudos