File Coverage

blib/lib/Data/Collector.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Data::Collector;
2             {
3             $Data::Collector::VERSION = '0.15';
4             }
5             # ABSTRACT: Collect information from multiple sources
6              
7 1     1   25305 use Carp;
  1         3  
  1         73  
8 1     1   489 use Moose;
  0            
  0            
9             use MooseX::Types::Set::Object;
10             use Module::Pluggable::Object;
11             use Class::Load 'try_load_class';
12             use namespace::autoclean;
13              
14             has 'format' => ( is => 'ro', isa => 'Str', default => 'JSON' );
15             has 'format_args' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
16             has 'engine' => ( is => 'ro', isa => 'Str', default => 'OpenSSH' );
17             has 'engine_args' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
18             has 'info_args' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
19             has 'engine_object' => (
20             is => 'ro',
21             isa => 'Object',
22             lazy_build => 1,
23             );
24              
25             has 'data' => (
26             is => 'rw',
27             isa => 'HashRef',
28             traits => ['Hash'],
29             default => sub { {} },
30             );
31              
32             has 'infos' => (
33             is => 'ro',
34             isa => 'Set::Object',
35             coerce => 1,
36             default => sub { Set::Object->new },
37             );
38              
39             has 'exclude_infos' => (
40             is => 'ro',
41             isa => 'Set::Object',
42             coerce => 1,
43             default => sub { Set::Object->new },
44             );
45              
46             has 'os' => (
47             is => 'rw',
48             isa => 'Str',
49             trigger => sub { shift->load_os(@_) },
50             predicate => 'has_os',
51             );
52              
53             sub _build_engine_object {
54             my $self = shift;
55             my $type = $self->engine;
56             my $class = "Data::Collector::Engine::$type";
57              
58             my ( $res, $reason ) = try_load_class($class);
59             $res or die "Can't load engine: $reason\n";
60              
61             return $class->new( %{ $self->engine_args } );
62             }
63              
64             sub BUILD {
65             my $self = shift;
66              
67             if ( ! $self->has_os ) {
68             # default if not run by App.pm
69             $self->os('CentOS');
70             }
71             }
72              
73             sub load_os {
74             my ( $self, $new_os, $old_os ) = @_;
75             }
76              
77             sub collect {
78             my $self = shift;
79             my $engine = $self->engine_object;
80              
81             # lazy calling the connect
82             if ( ! $engine->connected ) {
83             $engine->connect;
84             $engine->connected(1);
85             }
86              
87             my $object = Module::Pluggable::Object->new(
88             search_path => 'Data::Collector::Info',
89             require => 1,
90             );
91              
92             foreach my $class ( $object->plugins ) {
93             $self->load_info($class);
94             }
95              
96             if ( $engine->connected ) {
97             $engine->disconnect;
98             $engine->connected(0);
99             }
100              
101             return $self->serialize;
102             }
103              
104             sub load_info {
105             my ( $self, $class ) = @_;
106              
107             my @levels = split /\:\:/, $class;
108             my $level = $levels[-1];
109              
110             if ( $self->infos->members ) {
111             # we got specific infos requested
112             if ( ! $self->infos->has($level) ) {
113             # this info is not on the infos list
114             return;
115             }
116             }
117              
118             if ( $self->exclude_infos->has($level) ) {
119             # this info is on the exclusion list
120             return;
121             }
122              
123             my $info = $class->new(
124             engine => $self->engine_object,
125             %{ $self->info_args->{ lc $level } },
126             );
127              
128             my %data = %{ $info->all() };
129              
130             %data and $self->data( {
131             %{ $self->data },
132             %data,
133             } );
134             }
135              
136             sub serialize {
137             my $self = shift;
138             my $format = $self->format;
139             my $class = "Data::Collector::Serializer::$format";
140              
141             eval "use $class";
142             $@ && die "Can't load serializer '$class': $@";
143              
144             my $serializer = $class->new( %{ $self->format_args } );
145              
146             return $serializer->serialize( $self->data );
147             }
148              
149             sub clear_registry { Data::Collector::Info->clear_registry }
150              
151             __PACKAGE__->meta->make_immutable;
152             1;
153              
154              
155              
156             =pod
157              
158             =head1 NAME
159              
160             Data::Collector - Collect information from multiple sources
161              
162             =head1 VERSION
163              
164             version 0.15
165              
166             =head1 SYNOPSIS
167              
168             Data::Collector collects various information from multiple sources and makes it
169             available in different formats, similar to Puppet's Facter.
170              
171             use Data::Collector;
172              
173             my $collector = Data::Collector->new(
174             engine => 'OpenSSH', # default
175             engine_args => { password => read_password('Pass: ') },
176             format => 'JSON', # default
177             );
178              
179             my %data = $collector->collect;
180             ...
181              
182             Data::Collector uses I<Info>s to determine what information is collected. It
183             then uses I<Serialize>rs to serialize that information.
184              
185             An important concept in Data::Collector is that it does not use any modules to
186             fetch the information, only shell commands. This might seem like a pain at first
187             but it allows it to be run on remote machines without any RPC server/client
188             set up. It might be changed in the future, but (at least now) it seems unlikely.
189              
190             The main purpose of Data::Collector is to facilitate an information gatherning
191             subsystem, much like Puppet's Facter, to be used in system monitoring and
192             administration.
193              
194             However, Data::Collector is much more dynamic. It supports any number of engines
195             and formats. Thus, it can be used for push or pull situations, can work with
196             monitoring systems, integrate with testing suites and otherwise a pretty wide
197             variety of situations.
198              
199             =head1 ATTRIBUTES
200              
201             =head2 engine(Str)
202              
203             The engine that will be used to collect the information. This is the underlying
204             layer that will gather the information. The default is OpenSSH, you can use
205             any other one you want and even create your own.
206              
207             By implementing your own, you can have fetching done via database queries,
208             online searching, local system commands or even telnet, if that's what you're
209             using.
210              
211             =head2 engine_args(HashRef)
212              
213             Any arguments that the engine might need. These are passed to the engine's
214             I<new> method. Other than making sure it's a hash reference, the value is not
215             checked and is left for the engine's discression.
216              
217             L<Data::Collector::Engine::OpenSSH> requires a I<host>, and allows a I<user>
218             and I<passwd>.
219              
220             =head2 format(Str)
221              
222             This is the format in which you want the information. This will most likely
223             refer to the serializer you want, but it doesn't have to be. For example,
224             you could implement your own I<Serializer> which will actually be a module
225             to push all the changes you want in a database you have.
226              
227             The default is JSON.
228              
229             =head2 format_args(HashRef)
230              
231             Much like I<engine_args>, you can supply any additional arguments that will
232             reach the serializer's I<new> method.
233              
234             =head2 info_args(HashRef)
235              
236             Much like I<engine_args> and I<info_args>, you can supply any additional
237             arguments that should go to specific Info module's I<new> method.
238              
239             info_args => {
240             IFaces => {
241             ignore_ip => ['127.0.0.1'],
242             ignore_iface => ['lo'],
243             },
244             },
245              
246             =head2 data(HashRef)
247              
248             While (and post) collecting, this attribute contains all the information
249             [being] gathered. It is this data that is sent to the serializer in order
250             to do whatever it wants with it.
251              
252             =head2 engine_object(Object)
253              
254             This attributes holds the engine object. This should probably be left for
255             either testing or advanced usage. Please refrain from playing with it if
256             you're unsure how it works.
257              
258             =head1 SUBROUTINES/METHODS
259              
260             =head2 collect
261              
262             The main function of Data::Collector. It runs all the information collecting
263             modules. When it is done, it runs the I<serialize> method in order to serialize
264             the information fetched.
265              
266             =head2 serialize
267              
268             Loads the serializer (according to the I<format> selected) and asks it to
269             serialize the data it collected.
270              
271             This method can be run manually as well, but it is automatically run when
272             you run I<collect>.
273              
274             =head2 clear_registry
275              
276             Clears the information registry. The registry keeps all the keys of different
277             information modules. The registry makes sure information modules don't step on
278             each other.
279              
280             This is merely a helper method. It simply runs:
281              
282             Data::Collector::Info->clear_registry;
283              
284             This is actually only a mere helper method.
285              
286             =head2 BUILD
287              
288             Internal initialize subroutine that sets the default OS to CentOS.
289              
290             =head2 load_info
291              
292             Loads all the infos available.
293              
294             =head2 load_os
295              
296             Currently not being used.
297              
298             =head1 BUGS
299              
300             Please report any bugs or feature requests to C<bug-data-collector at rt.cpan.org>, or through
301             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Collector>. I will be notified, and then you'll
302             automatically be notified of progress on your bug as I make changes.
303              
304             =head1 SUPPORT
305              
306             You can find documentation for this module with the perldoc command.
307              
308             perldoc Data::Collector
309              
310             You can also look for information at:
311              
312             =over 4
313              
314             =item * RT: CPAN's request tracker
315              
316             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Data-Collector>
317              
318             =item * AnnoCPAN: Annotated CPAN documentation
319              
320             L<http://annocpan.org/dist/Data-Collector>
321              
322             =item * CPAN Ratings
323              
324             L<http://cpanratings.perl.org/d/Data-Collector>
325              
326             =item * Search CPAN
327              
328             L<http://search.cpan.org/dist/Data-Collector/>
329              
330             =back
331              
332             =head1 AUTHOR
333              
334             Sawyer X <xsawyerx@cpan.org>
335              
336             =head1 COPYRIGHT AND LICENSE
337              
338             This software is copyright (c) 2012 by Sawyer X.
339              
340             This is free software; you can redistribute it and/or modify it under
341             the same terms as the Perl 5 programming language system itself.
342              
343             =cut
344              
345              
346             __END__
347