Commit | Line | Data |
60e1cc39 |
1 | package System::Introspector::State; |
2 | use Moo; |
60e1cc39 |
3 | use System::Introspector::Gatherer; |
4 | |
0a11cf83 |
5 | use JSON::Diffable qw( encode_json ); |
6 | |
60e1cc39 |
7 | has config => (is => 'ro', required => 1); |
8 | |
9 | has introspectors => (is => 'lazy'); |
10 | |
11 | has host => (is => 'ro'); |
12 | |
a5e1e1c6 |
13 | has user => (is => 'ro'); |
14 | |
15 | has sudo_user => (is => 'ro'); |
16 | |
60e1cc39 |
17 | has storage => (is => 'ro', required => 1); |
18 | |
19 | has node_path => (is => 'lazy'); |
20 | |
21 | sub fetch { |
22 | my ($self) = @_; |
a5e1e1c6 |
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) = @_; |
60e1cc39 |
41 | my %report; |
a5e1e1c6 |
42 | for my $class_spec (@spec) { |
43 | my ($class_base, $args) = @$class_spec; |
44 | print "Gathering $class_base data\n"; |
60e1cc39 |
45 | $report{ $class_base } = $gatherer |
a5e1e1c6 |
46 | ->gather($class_base, $args); |
60e1cc39 |
47 | } |
a5e1e1c6 |
48 | print "All gathered\n"; |
60e1cc39 |
49 | return \%report; |
50 | } |
51 | |
52 | sub fetch_and_store { |
53 | my ($self) = @_; |
a5e1e1c6 |
54 | my $data = $self->fetch; |
55 | return $self->_store($data); |
60e1cc39 |
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) = @_; |
60e1cc39 |
72 | my $storage = $self->storage; |
73 | my @files; |
74 | for my $class (sort keys %$data) { |
0a11cf83 |
75 | my $file = sprintf '%s.json', join '/', |
60e1cc39 |
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"; |
0a11cf83 |
83 | print $fh encode_json($data->{$class}); |
60e1cc39 |
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('yml', 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 { |
a5e1e1c6 |
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 | ); |
60e1cc39 |
111 | } |
112 | |
113 | 1; |
cd5c3d43 |
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 |