moved host iteration into state so host/group storage generation is easy
Robert 'phaylon' Sedlacek [Fri, 29 Jun 2012 21:16:41 +0000 (21:16 +0000)]
bin/system-introspector
lib/System/Introspector/Config.pm
lib/System/Introspector/State.pm

index 1f09084..5e23b39 100755 (executable)
@@ -10,9 +10,6 @@ use System::Introspector::Config;
 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,
     'h|help'        => sub { pod2usage(0) },
@@ -21,9 +18,6 @@ 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
@@ -35,34 +29,13 @@ $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";
-    print "Group $group at $group_dir\n";
-    my $storage = File::Tree::Snapshot->new(
-        storage_path    => $group_dir,
-        allow_empty     => $allow_empty,
-    );
-    $storage->create
-        unless $storage->exists;
-    my $state = System::Introspector::State->new(
-        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 {
-        $storage->commit;
-    }
-}
+
+my $state = System::Introspector::State->new(
+    config => $config,
+    root   => $storage_dir,
+);
+
+$state->gather(@update_groups);
 
 __END__
 
index 4ebb33f..98697ab 100644 (file)
@@ -19,6 +19,14 @@ sub groups { keys %{ $_[0]->config->{group} || {} } }
 
 sub has_group { exists $_[0]->config->{group}{ $_[1] } }
 
+sub hosts {
+    my ($self) = @_;
+    my $host_spec = $self->config->{host};
+    return ref($host_spec) ? @$host_spec : $host_spec;
+}
+
+sub user { $_[0]->config->{user} }
+
 my $_get_inherited = sub {
     my $data = shift;
     $data ||= {};
index 3ea7ad5..3f20876 100644 (file)
@@ -1,36 +1,57 @@
 package System::Introspector::State;
 use Moo;
+use File::Tree::Snapshot;
 use System::Introspector::Gatherer;
 
 use JSON::Diffable qw( encode_json );
 
 has config => (is => 'ro', required => 1);
 
-has introspectors => (is => 'lazy');
+has root => (is => 'ro', required => 1);
 
-has host => (is => 'ro');
+sub user { $_[0]->config->user }
 
-has user => (is => 'ro');
+sub sudo_user { $_[0]->config->sudo_user }
 
-has sudo_user => (is => 'ro');
+sub gather {
+    my ($self, @groups) = @_;
+    for my $host ($self->config->hosts) {
+        $self->fetch_and_store($host, @groups);
+    }
+    return 1;
+}
 
-has storage => (is => 'ro', required => 1);
+sub introspectors {
+    my ($self, $group) = @_;
+    return $self->config->config_for_group($group)->{introspect};
+}
 
-has node_path => (is => 'lazy');
+sub fetch_and_store {
+    my ($self, $host, @groups) = @_;
+    my $data = $self->fetch($host, @groups);
+    return $self->_store($host, $data);
+}
 
 sub fetch {
-    my ($self) = @_;
-    my $spec = $self->introspectors;
+    my ($self, $host, @groups) = @_;
+    return +{ map {
+        ($_, $self->fetch_group($host, $_));
+    } @groups };
+}
+
+sub fetch_group {
+    my ($self, $host, $group) = @_;
+    my $spec = $self->introspectors($group);
     my (@sudo, @nosudo);
     push(@{ $spec->{$_}{sudo} ? \@sudo : \@nosudo}, [$_, $spec->{$_}])
         for sort keys %$spec;
     my %report;
     if (@nosudo) {
-        my $gatherer = $self->_create_gatherer;
+        my $gatherer = $self->_create_gatherer(host => $host);
         %report = %{ $self->_fetch_with_gatherer($gatherer, @nosudo) || {} };
     }
     if (@sudo) {
-        my $gatherer = $self->_create_gatherer(sudo => 1);
+        my $gatherer = $self->_create_gatherer(sudo => 1, host => $host);
         %report = (%report, %{ $self->_fetch_with_gatherer($gatherer, @sudo) || {} });
     }
     return \%report;
@@ -49,49 +70,44 @@ sub _fetch_with_gatherer {
     return \%report;
 }
 
-sub fetch_and_store {
-    my ($self) = @_;
-    my $data = $self->fetch;
-    return $self->_store($data);
-}
-
-sub _build_node_path {
-    my ($self) = @_;
-    return defined($self->host)
-        ? sprintf('host/%s', $self->host)
-        : 'local';
-}
-
-sub _build_introspectors {
-    my ($self) = @_;
-    return $self->config->{introspect};
+sub storage {
+    my ($self, @path) = @_;
+    my $storage = File::Tree::Snapshot->new(
+        allow_empty  => 0,
+        storage_path => join('/', $self->root, @path),
+    );
+    $storage->create
+        unless $storage->exists;
+    return $storage;
 }
 
 sub _store {
-    my ($self, $data) = @_;
-    my $storage = $self->storage;
-    my @files;
-    for my $class (sort keys %$data) {
-        my $file = sprintf '%s.json', join '/',
-            node => $self->node_path,
-            map lc, map {
-                s{([a-z0-9])([A-Z])}{${1}_${2}}g;
-                $_;
-            } split m{::}, $class;
-        my $fh = $storage->open('>:utf8', $file, mkpath => 1);
-        print "Writing $file\n";
-        print $fh encode_json($data->{$class});
-        push @files, $storage->file($file);
+    my ($self, $host, $data) = @_;
+    for my $group (sort keys %$data) {
+        my $storage = $self->storage($host, $group);
+        my $gathered = $data->{$group};
+        my @files;
+        for my $class (sort keys %$gathered) {
+            my $file = sprintf '%s.json', join '/',
+                map lc, map {
+                    s{([a-z0-9])([A-Z])}{${1}_${2}}g;
+                    $_;
+                } split m{::}, $class;
+            my $fh = $storage->open('>:utf8', $file, mkpath => 1);
+            print "Writing $file\n";
+            print $fh encode_json($gathered->{$class});
+            push @files, $storage->file($file);
+        }
+        $self->_cleanup($storage, [@files]);
+        $storage->commit;
     }
-    $self->_cleanup(\@files);
     return 1;
 }
 
 sub _cleanup {
-    my ($self, $known_files) = @_;
+    my ($self, $storage, $known_files) = @_;
     my %known = map { ($_ => 1) } @$known_files;
-    my $data_dir = $self->storage->file(node => $self->node_path);
-    my @files = $self->storage->find_files('json', node =>  $self->node_path);
+    my @files = $storage->find_files('json');
     for my $file (@files) {
         next if $known{$file};
         print "Removing $file\n";
@@ -105,7 +121,7 @@ sub _create_gatherer {
     my ($self, %arg) = @_;
     return System::Introspector::Gatherer->new_from_spec(
         user        => $self->user,
-        host        => $self->host,
+        host        => $arg{host},
         sudo_user   => $arg{sudo} && $self->sudo_user,
     );
 }