Skip to content

Commit

Permalink
Remote WMI inventory support (#429)
Browse files Browse the repository at this point in the history
  • Loading branch information
g-bougard authored Dec 22, 2017
1 parent f8275ec commit db3e459
Show file tree
Hide file tree
Showing 205 changed files with 1,562 additions and 84 deletions.
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ esx:
injector:
* Support --no-ssl-check option to avoid checking server SSL certificate

wmi-inventory:
* Added new task and script to permit agent-less inventory on win32 platform based
on remote WMI support.

2.3.21 Mon, 31 Jul 2017
core:
* Service/daemon refactoring:
Expand Down
1 change: 1 addition & 0 deletions Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ recommends 'Net::Write::Layer2' => '0';

install_script 'bin/fusioninventory-agent';
install_script 'bin/fusioninventory-win32-service' if $OSNAME eq 'MSWin32';
install_script 'bin/fusioninventory-wmi' if $OSNAME eq 'MSWin32';
install_script 'bin/fusioninventory-injector';
install_script 'bin/fusioninventory-inventory';
install_script 'bin/fusioninventory-wakeonlan';
Expand Down
186 changes: 186 additions & 0 deletions bin/fusioninventory-wmi
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/perl

use strict;
use warnings;

use lib './lib';
use setup;

use English qw(-no_match_vars) ;
use Getopt::Long;
use Pod::Usage;
use UNIVERSAL::require;

use FusionInventory::Agent::Config;
use FusionInventory::Agent::Version;
use FusionInventory::Agent::Logger;
use FusionInventory::Agent::Inventory;
use FusionInventory::Agent::Task::WMI::Version;

Getopt::Long::Configure( "no_ignorecase" );

my $options = {
debug => 0
};

GetOptions(
$options,
'backend-collect-timeout=s',
'conf-file=s',
'config=s',
'ca-cert-dir=s',
'ca-cert-file=s',
'debug+',
'help',
'host|h=s',
'local|l=s',
'logger=s',
'logfile=s',
'no-category=s',
'no-ssl-check',
'no-compression|C',
'pass|p=s',
'scan-homedirs',
'scan-profiles',
'server|s=s',
'tag|t=s',
'user|u=s',
'version'
) or pod2usage(-verbose => 0);

my $PROVIDER = $FusionInventory::Agent::Version::PROVIDER;
my $VERSION = FusionInventory::Agent::Task::WMI::Version::VERSION;

# Set AGENT_STRING to be included in inventory as VERSIONCLIENT
$FusionInventory::Agent::AGENT_STRING = "$PROVIDER-WMI-Inventory_v$VERSION";
$FusionInventory::Agent::VERSION_STRING = "$PROVIDER WMI Inventory ($VERSION)";

unshift @{$FusionInventory::Agent::Version::COMMENTS},
"** THIS IS A DEVELOPMENT RELEASE **"
if ($FusionInventory::Agent::Version::VERSION =~ /^\d+\.\d+\.(99\d\d|\d+-dev)$/);

if ($options->{version}) {
map { print $_."\n" }
lc($PROVIDER) . "-wmi $VERSION",
"based on $PROVIDER Agent v$FusionInventory::Agent::Version::VERSION",
@{$FusionInventory::Agent::Version::COMMENTS};
exit 0;
}

die lc($PROVIDER) . "-wmi only supported under win32 platform\n"
unless ($OSNAME eq 'MSWin32');

pod2usage(-verbose => 0, -exitstatus => 0)
if ($options->{help} || !$options->{host});

if ($options->{'conf-file'}) {
if ($options->{config}) {
if ($options->{config} ne 'file') {
print STDERR
"don't use --conf-file with $options->{config} backend";
exit 1;
}
} else {
$options->{config} = 'file';
}
}

# Under win32, registry config is automatically loaded and may populate server target
# So we prefer to explicitly unset server target if only local is wanted without config defined
if ($options->{'local'} && !$options->{'server'} && !$options->{'config'}) {
$options->{'server'} = "";
}

unless ($options->{'no-win32-ole-workaround'}) {
# From here we may need to avoid crashes due to not thread-safe Win32::OLE
FusionInventory::Agent::Tools::Win32->require();
FusionInventory::Agent::Tools::Win32::start_Win32_OLE_Worker();
}

my $config = FusionInventory::Agent::Config->new(
options => $options,
);

my $logger = FusionInventory::Agent::Logger->new(config => $config);

# Get targets
my $targets = $config->getTargets(
logger => $logger,
vardir => $setup{vardir}
);

die "No target defined, aborting\n"
unless $targets;

FusionInventory::Agent::Task::WMI->require();

foreach my $target (@{$targets}) {
my $wmitask = FusionInventory::Agent::Task::WMI->new(
target => $target,
logger => $logger,
datadir => $setup{datadir},
config => $config
);

die "Connection failure\n"
unless $wmitask->connect(%{$options});
$wmitask->run();
}

exit(0);


__END__
=head1 NAME
fusioninventory-wmi - Win32 remote inventory
=head1 SYNOPSIS
fusioninventory-wmi [options] --host <host> [--user <user>] [--pass <pass>] [--server server|--local path]
General options:
--help this menu
-t --tag tag tag for the inventoried machine
Remote machine options:
-h --host hostname hostname - mandatory option
-u --user username user name
-p --pass xxxx user password
Target definition options:
-s --server=URI send tasks result to a server
-l --local=PATH write tasks results locally
Inventory task options:
--backend-collect-timeout=TIME timeout for inventory modules execution
(default: 180s)
--no-category=CATEGORY do not list given category items
--scan-homedirs scan user home directories (false)
--scan-profiles scan user profiles (false)
Network options:
--ca-cert-dir=DIRECTORY CA certificates directory
--ca-cert-file=FILE CA certificates file
--no-ssl-check do not check server SSL certificate
(false)
-C --no-compression do not compress communication with server
(false)
Logging options:
--logger=BACKEND logger backend (stderr)
--logfile=FILE log file
Configuration options:
--config=BACKEND configuration backend
--conf-file=FILE configuration file
=head1 EXAMPLES
% fusioninventory-wmi --host 172.xx.xxx.xxx --user foo --password bar --local=.
=head1 DESCRIPTION
F<fusioninventory-wmi> creates inventory of remote Win32 machine.
It uses the WMI interface of the remote server.
27 changes: 26 additions & 1 deletion lib/FusionInventory/Agent/Inventory.pm
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ sub new {
fields => \%fields,
content => {
HARDWARE => {
ARCHNAME => $Config{archname},
VMSYSTEM => "Physical" # Default value
},
VERSIONCLIENT => $FusionInventory::Agent::AGENT_STRING ||
Expand All @@ -149,6 +148,20 @@ sub new {
return $self;
}

sub getRemote {
my ($self) = @_;

return $self->{_remote} || '';
}

sub setRemote {
my ($self, $task) = @_;

$self->{_remote} = $task || '';

return $self->{_remote};
}

sub getDeviceId {
my ($self) = @_;

Expand Down Expand Up @@ -630,3 +643,15 @@ compatibility.
At the end of the process IF the inventory was saved
correctly, the last_state is saved.
=head2 getRemote()
Method to get the parent task remote status.
Returns the string set by setRemote() API or an empty string.
=head2 setRemote([$task])
Method to set or reset the parent task remote status.
Without $task parameter, the API resets the parent remote status to an empty string.
44 changes: 34 additions & 10 deletions lib/FusionInventory/Agent/Task.pm
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,19 @@ sub abort {
}

sub getModules {
my ($class, $prefix) = @_;
my ($self, $task) = @_;

# allow to be called as an instance method
$class = ref $class ? ref $class : $class;
$task = 'Inventory' unless $task;

# use %INC to retrieve the root directory for this task
my $file = module2file($class);
my $file = module2file(__PACKAGE__."::".ucfirst($task));
my $rootdir = $INC{$file};
$rootdir =~ s/.pm$//;
return unless -d $rootdir;

# find a list of modules from files in this directory
my $root = $file;
$root =~ s/.pm$//;
$root .= "/$prefix" if $prefix;
my @modules;
my $wanted = sub {
return unless -f $_;
Expand All @@ -59,6 +57,20 @@ sub getModules {
return @modules
}

sub getRemote {
my ($self) = @_;

return $self->{_remote} || '';
}

sub setRemote {
my ($self, $task) = @_;

$self->{_remote} = $task || '';

return $self->{_remote};
}

1;
__END__
Expand Down Expand Up @@ -109,9 +121,21 @@ This is a method to be implemented by each subclass.
Abort running task immediatly.
=head2 getModules($prefix)
=head2 getModules($task)
Return a list of modules for the task. All modules installed at the same
location than this package, belonging to __PACKAGE__::$task namespace, will be
returned. If not optional $task is given, base search namespace will be
__PACKAGE__::Inventory instead.
=head2 getRemote()
Method to get the task remote status.
Returns the string set by setRemote() API or an empty string.
=head2 setRemote([$task])
Method to set or reset the task remote status.
Return a list of modules for this task. All modules installed at the same
location than this package, belonging to __PACKAGE__ namespace, will be
returned. If optional $prefix is given, base search namespace will be
__PACKAGE__/$prefix instead.
Without $task parameter, the API resets the remote status to an empty string.
2 changes: 1 addition & 1 deletion lib/FusionInventory/Agent/Task/ESX.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ sub createInventory {
tag => $tag
);

$inventory->setHardware({ ARCHNAME => 'remote' });
$inventory->setRemote('esx');

$inventory->setBios( $host->getBiosInfo() );

Expand Down
20 changes: 16 additions & 4 deletions lib/FusionInventory/Agent/Task/Inventory.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package FusionInventory::Agent::Task::Inventory;

use strict;
use warnings;

use parent 'FusionInventory::Agent::Task';

use Config;
Expand All @@ -13,6 +14,9 @@ use FusionInventory::Agent::Inventory;

use FusionInventory::Agent::Task::Inventory::Version;

# Preload Module base class
use FusionInventory::Agent::Task::Inventory::Module;

our $VERSION = FusionInventory::Agent::Task::Inventory::Version::VERSION;

sub isEnabled {
Expand Down Expand Up @@ -54,6 +58,9 @@ sub run {
tag => $self->{config}->{'tag'}
);

# Set inventory as remote if running remote inventory like from wmi task
$inventory->setRemote($self->getRemote()) if $self->getRemote();

if (not $ENV{PATH}) {
# set a minimal PATH if none is set (#1129, #1747)
$ENV{PATH} =
Expand Down Expand Up @@ -154,8 +161,12 @@ sub _initModulesList {
my $logger = $self->{logger};
my $config = $self->{config};

my @modules = $self->getModules('');
die "no inventory module found" if !@modules;
my @modules = $self->getModules('Inventory');
die "no inventory module found\n" if !@modules;

# Select isEnabled function to test
my $isEnabledFunction = "isEnabled" ;
$isEnabledFunction .= "ForRemote" if $self->getRemote();

# first pass: compute all relevant modules
foreach my $module (sort @modules) {
Expand All @@ -165,7 +176,8 @@ sub _initModulesList {
join('::', @components[0 .. $#components -1]) : '';

# Just skip Version package as not an inventory package module
if ($module =~ /FusionInventory::Agent::Task::Inventory::Version$/) {
# Also skip Module as not a real module but the base class for any module
if ($module =~ /FusionInventory::Agent::Task::Inventory::(Version|Module)$/) {
$self->{modules}->{$module}->{enabled} = 0;
next;
}
Expand All @@ -186,7 +198,7 @@ sub _initModulesList {

my $enabled = runFunction(
module => $module,
function => "isEnabled",
function => $isEnabledFunction,
logger => $logger,
timeout => $config->{'backend-collect-timeout'},
params => {
Expand Down
Loading

0 comments on commit db3e459

Please sign in to comment.