cleanup json files, not yml ones
[scpubgit/System-Introspector.git] / lib / System / Introspector / State.pm
1 package System::Introspector::State;
2 use Moo;
3 use System::Introspector::Gatherer;
4
5 use JSON::Diffable qw( encode_json );
6
7 has config => (is => 'ro', required => 1);
8
9 has introspectors => (is => 'lazy');
10
11 has host => (is => 'ro');
12
13 has user => (is => 'ro');
14
15 has sudo_user => (is => 'ro');
16
17 has storage => (is => 'ro', required => 1);
18
19 has node_path => (is => 'lazy');
20
21 sub fetch {
22     my ($self) = @_;
23     my $spec = $self->introspectors;
24     my (@sudo, @nosudo);
25     push(@{ $spec->{$_}{sudo} ? \@sudo : \@nosudo}, [$_, $spec->{$_}])
26         for sort keys %$spec;
27     my %report;
28     if (@nosudo) {
29         my $gatherer = $self->_create_gatherer;
30         %report = %{ $self->_fetch_with_gatherer($gatherer, @nosudo) || {} };
31     }
32     if (@sudo) {
33         my $gatherer = $self->_create_gatherer(sudo => 1);
34         %report = (%report, %{ $self->_fetch_with_gatherer($gatherer, @sudo) || {} });
35     }
36     return \%report;
37 }
38
39 sub _fetch_with_gatherer {
40     my ($self, $gatherer, @spec) = @_;
41     my %report;
42     for my $class_spec (@spec) {
43         my ($class_base, $args) = @$class_spec;
44         print "Gathering $class_base data\n";
45         $report{ $class_base } = $gatherer
46             ->gather($class_base, $args);
47     }
48     print "All gathered\n";
49     return \%report;
50 }
51
52 sub fetch_and_store {
53     my ($self) = @_;
54     my $data = $self->fetch;
55     return $self->_store($data);
56 }
57
58 sub _build_node_path {
59     my ($self) = @_;
60     return defined($self->host)
61         ? sprintf('host/%s', $self->host)
62         : 'local';
63 }
64
65 sub _build_introspectors {
66     my ($self) = @_;
67     return $self->config->{introspect};
68 }
69
70 sub _store {
71     my ($self, $data) = @_;
72     my $storage = $self->storage;
73     my @files;
74     for my $class (sort keys %$data) {
75         my $file = sprintf '%s.json', join '/',
76             node => $self->node_path,
77             map lc, map {
78                 s{([a-z0-9])([A-Z])}{${1}_${2}}g;
79                 $_;
80             } split m{::}, $class;
81         my $fh = $storage->open('>:utf8', $file, mkpath => 1);
82         print "Writing $file\n";
83         print $fh encode_json($data->{$class});
84         push @files, $storage->file($file);
85     }
86     $self->_cleanup(\@files);
87     return 1;
88 }
89
90 sub _cleanup {
91     my ($self, $known_files) = @_;
92     my %known = map { ($_ => 1) } @$known_files;
93     my $data_dir = $self->storage->file(node => $self->node_path);
94     my @files = $self->storage->find_files('json', node =>  $self->node_path);
95     for my $file (@files) {
96         next if $known{$file};
97         print "Removing $file\n";
98         unlink($file)
99             or die "Unable to remove '$file': $!\n";
100     }
101     return 1;
102 }
103
104 sub _create_gatherer {
105     my ($self, %arg) = @_;
106     return System::Introspector::Gatherer->new_from_spec(
107         user        => $self->user,
108         host        => $self->host,
109         sudo_user   => $arg{sudo} && $self->sudo_user,
110     );
111 }
112
113 1;
114
115 =head1 NAME
116
117 System::Introspector::State - Gather system state
118
119 =head1 SYNOPSIS
120
121     my $state = System::Introspector::State->new(
122         host    => 'foo.example.com',
123         storage => $storage_obj,
124         config  => {
125             introspect => [qw( ProbeName )],
126         },
127     );
128
129     my $data = $state->fetch;
130     $state->fetch_and_store;
131
132 =head1 DESCRIPTION
133
134 Gathers system introspection data based on configuration and stores
135 it with a L<File::Tree::Snapshot> object.
136
137 =head1 ATTRIBUTES
138
139 =head2 config
140
141 A hash reference containing a C<introspect> key with an array reference
142 value containing a list of probe names without the
143 C<System::Introspector::Probe::> prefix. This attribute is required.
144
145 =head2 host
146
147 An optional hostname. If no hostname is supplied, the local configuration
148 data will be fetched.
149
150 =head2 storage
151
152 A L<File::Tree::Snapshot> object.
153
154 =head1 METHODS
155
156 =head2 fetch
157
158     my $data = $state->fetch;
159
160 Fetches all probe data.
161
162 =head2 fetch_and_store
163
164     $state->fetch_and_store;
165
166 Fetches all probe data and stores it in the L</storage>.
167
168 =cut