Last active
June 17, 2022 08:38
-
-
Save skunkie/cbaa4512c5ce7983eee3e13aa8db7624 to your computer and use it in GitHub Desktop.
Automatic collection of info from vCenter, to GLPI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
# | |
# This tool can be used to push inventory of virtual machines and ESXi hosts from a VIServer in GLPI via FusionInventory plugin | |
use v5.10; | |
use strict; | |
use warnings; | |
use VMware::VIRuntime; | |
use POSIX 'ceil'; | |
use POSIX 'strftime'; | |
use XML::TreePP; | |
# FusionInventory module | |
use Encode; | |
use Compress::Zlib; | |
use LWP::UserAgent; | |
use DBI; | |
use Data::Dumper; # for dev only | |
$Util::script_version = "1.0"; | |
$ENV{"PERL_LWP_SSL_VERIFY_HOSTNAME"} = 0; | |
my %opts = ( | |
fusion_url => { | |
type => "=s", | |
help => "Specify the url for GLPI FusionInventory plugin", | |
required => 1, | |
}, | |
db_cnf => { | |
type => "=s", | |
help => "Specify the path to the config file to make connection to the GLPI database", | |
required => 1, | |
}, | |
vm_name => { | |
type => "=s", | |
help => "Import only this virtual machine from the VIServer", | |
required => 0, | |
}, | |
sync_location => { | |
type => "", | |
help => "Sync location by updating the table glpi_locations in the GLPI database", | |
required => 0, | |
}, | |
test => { | |
type => "", | |
help => "Only print XML data, for debugging", | |
required => 0, | |
}, | |
); | |
Opts::add_options(%opts); | |
# read/validate options and connect to the VIServer | |
Opts::parse(); | |
Opts::validate(); | |
Util::connect(); | |
# declare all variables | |
my ( | |
$clusters, | |
$clusters_names_ids, | |
$clusters_view, | |
$customFieldsMgr, | |
$db_cnf, | |
$dbh, | |
$dsn, | |
$field_vrm_owner, | |
$locations_ids, | |
$now, | |
$now_dev_id, | |
$sc, | |
$vihosts, | |
$vihosts_view, | |
$vm_views, | |
@glpi_computers, | |
); | |
# declare all subroutines | |
sub makeXML; # convert a perl object to XML | |
sub sendContent; # send the XML data to GLPI | |
sub dbFetchRow; # make an SQL query and return one resulting row (hashref) | |
sub dbFetchAll; # make an SQL query and return all resulting rows(hashref) | |
sub dbUpdate; # make an SQL update statement | |
sub syncComment; # syncronize the 'comment' field in table ' glpi_computers' | |
sub syncLocation; # syncronize the 'locations_id' field in table ' glpi_computers' | |
# path to the config file to make connection to the GLPI database | |
$db_cnf = Opts::get_option('db_cnf'); | |
# connect to the GLPI database | |
$dsn = "DBI:mysql:mysql_read_default_file=$db_cnf"; | |
$dbh = DBI->connect($dsn, undef, undef, {RaiseError => 1, PrintError => 0, mysql_enable_utf8 => 1}) | |
or die("Could not connect to database: $DBI::errstr"); | |
# get info on all locations_ids in the GLPI database | |
$locations_ids = dbFetchAll('name', 'SELECT name, id FROM glpi_locations'); | |
# get info on the custom field "VRM Owner" | |
$sc = Vim::get_service_content() or die("Cannot get info on custom fields from VIServer"); | |
$customFieldsMgr = Vim::get_view(mo_ref => $sc->customFieldsManager); | |
foreach (@{$customFieldsMgr->field}) { | |
if ($_->name eq "VRM Owner") { | |
$field_vrm_owner = $_->key; | |
last; | |
} | |
} | |
# get info on clusters in the VIServer | |
$clusters_view = Vim::find_entity_views(view_type => 'ClusterComputeResource', | |
properties => [ | |
'name', | |
'host', | |
], | |
) or die("Cannot get info on clusters from the VIServer"); | |
foreach my $cluster (@$clusters_view) { | |
$clusters_names_ids->{$cluster->{name}} = $locations_ids->{$cluster->{name}}->{id}; | |
foreach my $vihost (@{$cluster->{host}}) { | |
$clusters->{$vihost->{value}} = $locations_ids->{$cluster->{name}}; | |
} | |
} | |
# get info on CPUS of all hosts in the VIServer | |
$vihosts_view = Vim::find_entity_views(view_type => 'HostSystem', | |
properties => [ | |
'config.product', | |
'hardware.biosInfo', | |
'name', | |
'summary.hardware', | |
'summary.host', | |
], | |
) or die("Cannot get info on hosts from the VIServer"); | |
foreach my $vihost (@$vihosts_view) { | |
$vihosts->{$vihost->{'summary.host'}->{value}} = { | |
'name' => $vihost->{name}, | |
'cpuModel' => $vihost->{'summary.hardware'}->{cpuModel}, | |
'cpuMhz' => $vihost->{'summary.hardware'}->{cpuMhz}, | |
}; | |
} | |
# get info on all VMs, not templates | |
my %filter_hash = ( | |
'config.template' => 'false', | |
); | |
if (Opts::get_option('vm_name')) { | |
$filter_hash{'name'} = Opts::get_option('vm_name'); | |
} | |
$vm_views = Vim::find_entity_views(view_type => 'VirtualMachine', | |
filter => { %filter_hash }, | |
properties => ['customValue', | |
'name', | |
'guest', | |
'config', | |
'runtime', | |
], | |
) or die("Cannot get info on virtual machines from the VIServer"); | |
# disconnect from the VIServer | |
Util::disconnect(); | |
# get current datetime | |
$now = strftime "%Y-%m-%d %H:%M:%S", localtime; | |
$now_dev_id = strftime "-%Y-%m-%d-%H-%M-%S", localtime; | |
foreach my $vm (@$vm_views) { | |
my ( | |
$content, | |
$content_xml, | |
$db_fetch, | |
$description, | |
$deviceid, | |
$inventory, | |
$locations_id, | |
$nameCPU, | |
$speedCPU, | |
$vm_name, | |
$vm_owner, | |
); | |
$vm_name = $vm->{name}; | |
push @glpi_computers, $vm_name; | |
$deviceid = $vm_name . $now_dev_id; | |
# START ACCESSLOG | |
$content->{ACCESSLOG}->{LOGDATE} = $now; | |
# END ACCESSLOG | |
# START ACCOUNTINFO | |
$content->{ACCOUNTINFO} = {KEYNAME => 'TAG', | |
KEYVALUE => $clusters->{$vm->{runtime}->{host}->{value}}->{name}, | |
}; | |
# END ACCOUNTINFO | |
# START BIOS | |
$content->{BIOS} = {MMODEL => '440BX Desktop Reference Platform', | |
MSN => 'None', | |
SKUNUMBER => 'Not Specified', | |
SMANUFACTURER => 'VMware, Inc.', | |
SMODEL => 'VMware Virtual Platform', | |
SSN => $vm->{config}->{uuid}, | |
}; | |
# END BIOS | |
# START CPUS | |
$nameCPU = ($vihosts->{$vm->{runtime}->{host}->{value}})->{cpuModel}; | |
$speedCPU = ($vihosts->{$vm->{runtime}->{host}->{value}})->{cpuMhz}; | |
push @{$content->{CPUS}}, ({CORE => 1, | |
NAME => $nameCPU, | |
SPEED => $speedCPU, }) x $vm->{config}->{hardware}->{numCPU}; | |
# END CPUS | |
# START DRIVES | |
foreach my $disk (@{$vm->{guest}->{disk}}) { | |
push @{$content->{DRIVES}}, {FREE => int($disk->{freeSpace} / (1024 ** 2)), | |
TOTAL => int($disk->{capacity} / (1024 ** 2)), | |
TYPE => $disk->{diskPath}, | |
}; | |
} | |
# END DRIVES | |
# START HARDWARE | |
$description = $vm->{config}->{annotation}; | |
$content->{HARDWARE} = {CHASSIS_TYPE => 'Virtual Server', | |
DESCRIPTION => $description, | |
IPADDR => $vm->{guest}->{ipAddress}, | |
MEMORY => $vm->{config}->{hardware}->{memoryMB}, | |
NAME => $vm->{name}, | |
OSNAME => $vm->{guest}->{guestFullName}, | |
PROCESSORN => $vm->{config}->{hardware}->{numCPU}, | |
PROCESSORS => $speedCPU, | |
PROCESSORT => $nameCPU, | |
UUID => $vm->{config}->{uuid}, | |
VMSYSTEM => 'VMware', | |
WORKGROUP => $vm->{guest}->{ipStack} ? $vm->{guest}->{ipStack}->[0]->{dnsConfig}->{domainName} : '', | |
}; | |
# END HARDWARE | |
# START MEMORIES | |
$content->{MEMORIES} = {CAPTION => 'RAM slot #0', | |
DESCRIPTION => 'DIMM', | |
MEMORYCORRECTION => 'None', | |
NUMSLOTS => 1, | |
TYPE => 'DRAM', | |
CAPACITY => $vm->{config}->{hardware}->{memoryMB}, | |
}; | |
# END MEMORIES | |
# START NETWORKS | |
foreach my $net (@{$vm->{guest}->{net}}) { | |
my $ipaddress; | |
if ($net->{ipAddress}) { | |
if (ref($net->{ipAddress}) eq 'ARRAY') { | |
$ipaddress = $net->{ipAddress}[0]; # only IPv4 address | |
} | |
else { | |
$ipaddress = $net->{ipAddress}; | |
} | |
} | |
push @{$content->{NETWORKS}}, {DESCRIPTION => $net->{network}, | |
DRIVER => '', | |
IPADDRESS => $ipaddress, | |
MACADDR => $net->{macAddress}, | |
STATUS => $net->{connected} == 1 ? 'Up' : 'Down', | |
TYPE => 'Ethernet', | |
VIRTUALDEV => 0, | |
}; | |
} | |
# END NETWORKS | |
# START OPERATINGSYSTEM | |
# do not send the full name of the OS for RHEL as it is sent by the fusioninventory agent from within the OS | |
$db_fetch = dbFetchRow('SELECT go.name FROM glpi_operatingsystems AS go JOIN glpi_computers AS gc ON go.id = gc.operatingsystems_id where gc.name = ?', $vm_name); | |
$content->{OPERATINGSYSTEM} = {FULL_NAME => ($db_fetch->{name} and $vm->{guest}->{guestFullName} and $vm->{guest}->{guestFullName} =~ m|Red Hat Enterprise Linux|) | |
? $db_fetch->{name} : $vm->{guest}->{guestFullName}, | |
KERNEL_NAME => $vm->{guest}->{guestId}, | |
NAME => $vm->{guest}->{guestFamily}, | |
}; | |
# END OPERATINGSYSTEM | |
# START STORAGES | |
foreach my $device (@{$vm->{config}->{hardware}->{device}}) { | |
if (ref($device) eq 'VirtualDisk') { | |
push @{$content->{STORAGES}}, {DESCRIPTION => $device->{deviceInfo}->{label}, | |
DISKSIZE => (int($device->{capacityInKB} / 1024)), | |
INTERFACE => 'SCSI', | |
MANUFACTURER => 'VMware', | |
MODEL => 'Virtualdisk ', | |
NAME => $device->{backing}->{fileName}, | |
SERIALNUMBER => $device->{backing}->{uuid}, | |
TYPE => 'disk', | |
}; | |
} | |
} | |
# END STORAGES | |
# START USERS | |
### get VRM Owner of the VM | |
if ($field_vrm_owner and $vm->customValue) { | |
foreach my $customValue (@{$vm->customValue}) { | |
if ($customValue->key eq $field_vrm_owner) { | |
# domain\vpupkin aka down-level logon name | |
if ($customValue->value =~ m|\\(.+)$|) { | |
$vm_owner = $1; | |
} | |
# [email protected] aka user principal name | |
elsif ($customValue->value =~ m|^(.+)@|) { | |
$vm_owner = $1; | |
} | |
# vpupkin | |
else { | |
$vm_owner = $customValue->value; | |
} | |
last; | |
} | |
} | |
$content->{USERS}->{LOGIN} = $vm_owner; | |
} | |
# END USERS | |
# START VERSIONCLIENT | |
$content->{VERSIONCLIENT} = 'Perl Script for VMware'; | |
# END VERSIONCLIENT | |
$inventory = { | |
content => $content, | |
deviceid => $deviceid, | |
}; | |
$content_xml = makeXML($inventory); | |
if (Opts::get_option('test')) { | |
print $content_xml; | |
} | |
else { | |
# send the XML to the FusionInventory plugin | |
sendContent($content_xml); | |
# make changes in the table glpi_computers of the GLPI database, comment and location fields | |
$db_fetch = dbFetchRow('SELECT comment, locations_id FROM glpi_computers WHERE name = ?', $vm_name); | |
syncComment($db_fetch, $vm_name, $description); | |
if (Opts::get_option('sync_location')) { | |
$locations_id = $clusters->{$vm->{runtime}->{host}->{value}}->{id}; | |
syncLocation($db_fetch, $vm_name, $locations_id); | |
} | |
} | |
} | |
if (!Opts::get_option('vm_name') and !Opts::get_option('test')) { | |
foreach my $vihost (@$vihosts_view) { | |
my ( | |
$content, | |
$content_xml, | |
$db_fetch, | |
$description, | |
$deviceid, | |
$inventory, | |
$locations_name, | |
$locations_id, | |
$memoryMB, | |
$nameCPU, | |
$speedCPU, | |
$vihost_name, | |
); | |
if ($vihost->{name} =~ m|^([0-9]{1,3}\.){3}[0-9]{1,3}$|) { | |
$vihost_name = $vihost->{name}; | |
} | |
else { | |
$vihost_name = (split(/\./, $vihost->{name}))[0]; | |
} | |
push @glpi_computers, $vihost_name; | |
$deviceid = $vihost_name . $now_dev_id; | |
$locations_name = $clusters->{$vihost->{mo_ref}->{value}}->{name} ? $clusters->{$vihost->{mo_ref}->{value}}->{name} : ''; | |
$description = 'VMware hypervisor in cluster ' . $locations_name; | |
$memoryMB = ceil($vihost->{'summary.hardware'}->{memorySize} / 1024 ** 2); | |
# START ACCESSLOG | |
$content->{ACCESSLOG}->{LOGDATE} = $now; | |
# END ACCESSLOG | |
# START ACCOUNTINFO | |
$content->{ACCOUNTINFO} = {KEYNAME => 'TAG', | |
KEYVALUE => $locations_name, | |
}; | |
# END ACCOUNTINFO | |
# START BIOS | |
$db_fetch = dbFetchRow('SELECT serial FROM glpi_computers where name = ?', $vihost_name); | |
$content->{BIOS} = {BDATE => $vihost->{'hardware.biosInfo'}->{releaseDate}, | |
BVERSION => $vihost->{'hardware.biosInfo'}->{biosVersion}, | |
MSN => 'None', | |
SKUNUMBER => 'Not Specified', | |
SMANUFACTURER => $vihost->{'summary.hardware'}->{vendor}, | |
SMODEL => $vihost->{'summary.hardware'}->{model}, | |
SSN => $db_fetch->{serial}, | |
}; | |
# END BIOS | |
# START CPUS | |
$nameCPU = $vihost->{'summary.hardware'}->{cpuModel}; | |
$speedCPU = $vihost->{'summary.hardware'}->{cpuMhz}; | |
push @{$content->{CPUS}}, ({CORE => 1, | |
NAME => $nameCPU, | |
SPEED => $speedCPU, }) x $vihost->{'summary.hardware'}->{numCpuPkgs}; | |
# END CPUS | |
# START HARDWARE | |
$content->{HARDWARE} = {CHASSIS_TYPE => $vihost->{'summary.hardware'}->{vendor}, | |
DESCRIPTION => 'VMware hypervisor in cluster ' . $locations_name, | |
MEMORY => $memoryMB, | |
NAME => $vihost_name, | |
OSNAME => $vihost->{'config.product'}->{fullName}, | |
PROCESSORN => $vihost->{'summary.hardware'}->{numCpuPkgs}, | |
PROCESSORS => $speedCPU, | |
PROCESSORT => $nameCPU, | |
VMSYSTEM => $vihost->{'config.product'}->{licenseProductName}, | |
}; | |
# END HARDWARE | |
# START MEMORIES | |
$content->{MEMORIES} = {CAPTION => 'RAM slot #0', | |
DESCRIPTION => 'DIMM', | |
MEMORYCORRECTION => 'None', | |
NUMSLOTS => 1, | |
TYPE => 'DRAM', | |
CAPACITY => $memoryMB, | |
}; | |
# END MEMORIES | |
# START OPERATINGSYSTEM | |
$content->{OPERATINGSYSTEM} = {FULL_NAME => $vihost->{'config.product'}->{fullName}, | |
KERNEL_NAME => $vihost->{'config.product'}->{osType}, | |
NAME => $vihost->{'config.product'}->{name}, | |
}; | |
# END OPERATINGSYSTEM | |
# START VERSIONCLIENT | |
$content->{VERSIONCLIENT} = 'Perl Script for VMware'; | |
# END VERSIONCLIENT | |
$inventory = { | |
content => $content, | |
deviceid => $deviceid, | |
}; | |
$content_xml = makeXML($inventory); | |
# send the XML to the FusionInventory plugin | |
sendContent($content_xml); | |
# make changes in the table glpi_computers of the GLPI database, comment and location fields | |
$db_fetch = dbFetchRow('SELECT comment, locations_id FROM glpi_computers WHERE name = ?', $vihost_name); | |
syncComment($db_fetch, $vihost_name, $description); | |
if (Opts::get_option('sync_location')) { | |
if ($locations_id = $clusters->{$vihost->{mo_ref}->{value}}->{id}) { | |
syncLocation($db_fetch, $vihost_name, $locations_id); | |
} | |
} | |
} | |
# set field is_deleted to 1 in the table glpi_computers for those virtual machine that are not present in the VIServer | |
# on condition that their field locations_id is in the ids of the VIServer clusters | |
my $glpi_computers = dbFetchAll('name', 'SELECT name, locations_id, is_deleted FROM glpi_computers WHERE name IS NOT NULL'); | |
my @ids = values %$clusters_names_ids; | |
foreach my $glpi_computer (values %$glpi_computers) { | |
if ($glpi_computer->{locations_id} ~~ @ids) { | |
if (!($glpi_computer->{name} ~~ @glpi_computers) and $glpi_computer->{is_deleted} == 0) { | |
dbUpdate('UPDATE glpi_computers SET is_deleted = 1 WHERE name = ?', $glpi_computer->{name}); | |
} | |
# set field is_deleted to 0 if the name of the VM is reused in the VIServer | |
if (($glpi_computer->{name} ~~ @glpi_computers) and $glpi_computer->{is_deleted} == 1) { | |
dbUpdate('UPDATE glpi_computers SET is_deleted = 0 WHERE name = ?', $glpi_computer->{name}); | |
} | |
} | |
} | |
} | |
$dbh->disconnect(); | |
sub makeXML { | |
my $inventory = shift; | |
my $tpp = XML::TreePP->new(indent => 2); | |
$tpp->write({ | |
REQUEST => { | |
CONTENT => $inventory->{content}, | |
DEVICEID => $inventory->{deviceid}, | |
QUERY => "INVENTORY", | |
} | |
}); | |
} | |
sub sendContent { | |
my $content = shift; | |
my $ua = LWP::UserAgent->new( | |
agent => 'FusionInventory-Injector', | |
parse_head => 0, # No need to parse HTML | |
keep_alive => 1, | |
requests_redirectable => ['POST', 'GET', 'HEAD'] | |
); | |
my $request = HTTP::Request->new(POST => Opts::get_option('fusion_url')); | |
$request->header( | |
'Pragma' => 'no-cache', | |
'Content-type' => 'Application/x-compress' | |
); | |
$request->content(compress(encode_utf8($content))); | |
my $res = $ua->request($request); | |
return $res->is_success(); | |
} | |
sub dbFetchRow { | |
my ($query, $bind_var) = @_; | |
my $sth = $dbh->prepare($query); | |
$sth->execute($bind_var); | |
return $sth->fetchrow_hashref(); | |
} | |
sub dbFetchAll { | |
my $key_field = shift; | |
my $query = shift; | |
my $sth = $dbh->prepare($query); | |
$sth->execute(); | |
return $sth->fetchall_hashref($key_field); | |
} | |
sub dbUpdate { | |
my ($update, $bind_var1, $bind_var2) = @_; | |
my $sth = $dbh->prepare($update); | |
if (defined $bind_var2) { | |
$sth->execute($bind_var1, $bind_var2); | |
} else { | |
$sth->execute($bind_var1); | |
} | |
} | |
sub syncComment { | |
my ($db_fetch, $computer_name, $description) = @_; | |
if (!$db_fetch->{comment} or ($db_fetch->{comment} and $db_fetch->{comment} ne $description)) { | |
dbUpdate('UPDATE glpi_computers SET comment = ? WHERE name = ?', $description, $computer_name); | |
} | |
} | |
sub syncLocation { | |
my ($db_fetch, $computer_name, $locations_id) = @_; | |
if ($db_fetch->{locations_id} and $db_fetch->{locations_id} ne $locations_id) { | |
dbUpdate('UPDATE glpi_computers SET locations_id = ? WHERE name = ?', $locations_id, $computer_name); | |
} | |
} | |
__END__ | |
=head1 NAME | |
glpi_import_from_vc.pl - A tool to push inventory of virtual machines and ESXi hosts from a VIServer in GLPI via FusionInventory plugin. | |
=head1 SYNOPSIS | |
glpi_import_from_vc.pl [options] | |
Options: | |
--server (variable VI_SERVER, default 'localhost') | |
--fusion_url GLPI fusion inventory plugin URL | |
--db_cnf Specify the path to the config file to make connection to the GLPI database | |
--vm_name Import only this virtual machine from the VIServer | |
--sync_location Sync location by updating the table glpi_locations in the GLPI database | |
--test Only print XML data, for debugging | |
Examples: | |
glpi_import_from_vc.pl --server vcenter.domain.corp --credstore /opt/scripts/vicredentials.xml \ | |
--fusion_url http://fusion.domain.corp/glpi/plugins/fusioninventory/front/plugin_fusioninventory.communication.php --db_cnf /opt/scripts/glpi_db.cnf | |
=head1 DESCRIPTION | |
This tool can be used to push inventory of virtual machines and ESXi hosts from a VIServer in GLPI via FusionInventory plugin |
hello,
vicredentials.xml: https://code.vmware.com/docs/11712/vsphere-sdk-for-perl-utility-applications-reference-7-0/credstore_admin.html
glpi_db.cnf: http://www.mysql.ru/docs/man/Option_files.html
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hello,
great job!!!
what is vicredentials.xml and glpi_db.cnf format?
thx4help :)
Konrad