reworked gatherer to be more flexible, added sudo support
Robert 'phaylon' Sedlacek [Thu, 31 May 2012 00:16:02 +0000 (00:16 +0000)]
bin/system-introspector
lib/System/Introspector/Config.pm
lib/System/Introspector/Gatherer.pm
lib/System/Introspector/Gatherer/Bridge.pm [new file with mode: 0644]
lib/System/Introspector/State.pm

index 0c16443..1f09084 100644 (file)
@@ -11,6 +11,7 @@ GetOptions(
     'c|config=s'    => \my $config_file,
     's|storage=s'   => \my $storage_dir,
     'H|host=s'      => \my $hostname,
+    'U|user=s'      => \my $username,
     'allow-empty'   => \my $allow_empty,
     'a|all'         => \my $update_all,
     'g|group=s'     => \my @update_groups,
@@ -20,6 +21,9 @@ GetOptions(
 die "Requires --all or --group option\n"
     unless $update_all or @update_groups;
 
+die "The --user option also requires a --host to be set\n"
+    if defined($username) and not defined($hostname);
+
 my $config = System::Introspector::Config->new(
     config_file => (defined($config_file)
                     ? $config_file
@@ -31,6 +35,8 @@ $config->has_group($_) or die "Unknown group '$_'\n"
 
 @update_groups = $config->groups
     if $update_all;
+    
+my $sudo_user = $config->sudo_user;
 
 for my $group (@update_groups) {
     my $group_dir = "$storage_dir/$group";
@@ -42,12 +48,15 @@ for my $group (@update_groups) {
     $storage->create
         unless $storage->exists;
     my $state = System::Introspector::State->new(
-        defined($hostname) ? (host => $hostname) : (),
+        defined($hostname)  ? (host => $hostname) : (),
+        defined($username)  ? (user => $username) : (),
+        defined($sudo_user) ? (sudo_user => $sudo_user) : (),
         storage => $storage,
         config  => $config->config_for_group($group),
     );
     eval { $state->fetch_and_store };
     if (my $error = $@) {
+        warn "Error: $error\n";
         $storage->reset;
     }
     else {
index 8b51c31..4ebb33f 100644 (file)
@@ -13,10 +13,38 @@ sub _build_config {
     return \%config;
 }
 
+sub sudo_user { $_[0]->config->{sudo_user} }
+
 sub groups { keys %{ $_[0]->config->{group} || {} } }
 
 sub has_group { exists $_[0]->config->{group}{ $_[1] } }
 
-sub config_for_group { $_[0]->config->{group}{ $_[1] } }
+my $_get_inherited = sub {
+    my $data = shift;
+    $data ||= {};
+    return
+        map  { ($_ => $data->{$_}) }
+        grep { exists $data->{$_} }
+        qw( sudo );
+};
+
+sub config_for_group {
+    my ($self, $name) = @_;
+    my %common;
+    my $config = $self->config;
+    %common = (%common, $config->$_get_inherited);
+    my $group = $config->{group}{ $name };
+    %common = (%common, $group->$_get_inherited);
+    return {
+        introspect => {
+            map {
+                ($_ => {
+                    %common,
+                    %{ $group->{introspect}{ $_ } || {} },
+                });
+            } keys %{ $group->{introspect} || {} },
+        },
+    };
+}
 
 1;
index 17ac433..43ffff4 100644 (file)
@@ -1,12 +1,51 @@
 package System::Introspector::Gatherer;
 use Moo;
+use Object::Remote;
+use Object::Remote::Future;
+use System::Introspector::Gatherer::Bridge;
 use Module::Runtime qw( use_module );
 
 sub gather {
     my ($self, $class, $args) = @_;
-    return use_module("System::Introspector::$class")
+    return use_module("System::Introspector::Probe::$class")
         ->new($args)
         ->gather;
 }
 
+sub _new_direct {
+    my ($class, $remote) = @_;
+    return $class->new::on($remote);
+}
+
+sub _new_bridged {
+    my ($class, $bridge, $remote) = @_;
+    return System::Introspector::Gatherer::Bridge->new::on($bridge,
+        remote_spec  => $remote,
+        remote_class => $class,
+    );
+}
+
+sub new_from_spec {
+    my ($class, %arg) = @_;
+    my ($user, $host, $sudo_user) = @arg{qw( user host sudo_user )};
+    my $sudo = defined($sudo_user) ? sprintf('%s@', $sudo_user) : undef;
+    if (defined $host) {
+        my $remote = join '@', grep defined, $user, $host;
+        if (defined $sudo_user) {
+            return $class->_new_bridged($remote, $sudo);
+        }
+        else {
+            return $class->_new_direct($remote);
+        }
+    }
+    else {
+        if (defined $sudo_user) {
+            return $class->_new_direct($sudo);
+        }
+        else {
+            return $class->new;
+        }
+    }
+}
+
 1;
diff --git a/lib/System/Introspector/Gatherer/Bridge.pm b/lib/System/Introspector/Gatherer/Bridge.pm
new file mode 100644 (file)
index 0000000..dd6cd19
--- /dev/null
@@ -0,0 +1,17 @@
+package System::Introspector::Gatherer::Bridge;
+use Object::Remote;
+use Object::Remote::Future;
+use Moo;
+
+has remote_spec => (is => 'ro', required => 1);
+has remote_class => (is => 'ro', required => 1);
+has remote => (is => 'lazy');
+
+sub _build_remote {
+    my ($self) = @_;
+    return $self->remote_class->new::on($self->remote_spec);
+}
+
+sub gather { (shift)->remote->gather(@_) }
+
+1;
index 3506a1c..2d423b1 100644 (file)
@@ -1,8 +1,5 @@
 package System::Introspector::State;
 use Moo;
-use Data::YAML::Writer;
-use Object::Remote;
-use Object::Remote::Future;
 use System::Introspector::Gatherer;
 
 use JSON::Diffable qw( encode_json );
@@ -13,25 +10,49 @@ has introspectors => (is => 'lazy');
 
 has host => (is => 'ro');
 
+has user => (is => 'ro');
+
+has sudo_user => (is => 'ro');
+
 has storage => (is => 'ro', required => 1);
 
 has node_path => (is => 'lazy');
 
 sub fetch {
     my ($self) = @_;
-    my $gatherer = $self->_create_gatherer;
-    my $spec     = $self->introspectors;
+    my $spec = $self->introspectors;
+    my (@sudo, @nosudo);
+    push(@{ $spec->{$_}{sudo} ? \@sudo : \@nosudo}, [$_, $spec->{$_}])
+        for sort keys %$spec;
     my %report;
-    for my $class_base (sort keys %$spec) {
+    if (@nosudo) {
+        my $gatherer = $self->_create_gatherer;
+        %report = %{ $self->_fetch_with_gatherer($gatherer, @nosudo) || {} };
+    }
+    if (@sudo) {
+        my $gatherer = $self->_create_gatherer(sudo => 1);
+        %report = (%report, %{ $self->_fetch_with_gatherer($gatherer, @sudo) || {} });
+    }
+    return \%report;
+}
+
+sub _fetch_with_gatherer {
+    my ($self, $gatherer, @spec) = @_;
+    my %report;
+    for my $class_spec (@spec) {
+        my ($class_base, $args) = @$class_spec;
+        print "Gathering $class_base data\n";
         $report{ $class_base } = $gatherer
-            ->gather($class_base, $spec->{ $class_base });
+            ->gather($class_base, $args);
     }
+    print "All gathered\n";
     return \%report;
 }
 
 sub fetch_and_store {
     my ($self) = @_;
-    return $self->_store($self->fetch);
+    my $data = $self->fetch;
+    return $self->_store($data);
 }
 
 sub _build_node_path {
@@ -81,11 +102,12 @@ sub _cleanup {
 }
 
 sub _create_gatherer {
-    my ($self) = @_;
-    if (defined( my $host = $self->host )) {
-        return System::Introspector::Gatherer->new::on($host);
-    }
-    return System::Introspector::Gatherer->new;
+    my ($self, %arg) = @_;
+    return System::Introspector::Gatherer->new_from_spec(
+        user        => $self->user,
+        host        => $self->host,
+        sudo_user   => $arg{sudo} && $self->sudo_user,
+    );
 }
 
 1;