File Coverage

blib/lib/App/Manoc/Netwalker/Poller/ServerTask.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package App::Manoc::Netwalker::Poller::ServerTask;
2             #ABSTRACT: Server poller task
3              
4 1     1   2887 use Moose;
  1         2  
  1         6  
5              
6             our $VERSION = '2.99.2'; ##TRIAL VERSION
7              
8 1     1   5773 use Try::Tiny;
  1         3  
  1         53  
9              
10 1     1   7 use App::Manoc::Netwalker::Poller::TaskReport;
  1         2  
  1         16  
11 1     1   4 use App::Manoc::Manifold;
  1         2  
  1         28  
12 1     1   65 use App::Manoc::IPAddress::IPv4;
  0            
  0            
13              
14              
15             has 'server_id' => (
16             is => 'ro',
17             isa => 'Int',
18             required => 1
19             );
20              
21              
22             has 'server_entry' => (
23             is => 'ro',
24             isa => 'Maybe[Object]',
25             lazy => 1,
26             builder => '_build_server_entry',
27             );
28              
29              
30             has 'nwinfo' => (
31             is => 'ro',
32             isa => 'Object',
33             lazy => 1,
34             builder => '_build_nwinfo',
35             );
36              
37              
38             has 'source' => (
39             is => 'ro',
40             lazy => 1,
41             builder => '_build_source',
42             );
43              
44              
45             has 'config_source' => (
46             is => 'ro',
47             lazy => 1,
48             builder => '_build_config_source',
49             );
50              
51              
52             has 'task_report' => (
53             is => 'ro',
54             required => 0,
55             builder => '_build_task_report',
56             );
57              
58              
59             with
60             'App::Manoc::Netwalker::Poller::BaseTask',
61             'App::Manoc::Logger::Role';
62              
63             #----------------------------------------------------------------------#
64             # #
65             # A t t r i b u t e s B u i l d e r #
66             # #
67             #----------------------------------------------------------------------#
68              
69             sub _build_server_entry {
70             my $self = shift;
71             my $id = $self->server_id;
72              
73             return $self->schema->resultset('Server')->find($id);
74             }
75              
76             sub _build_nwinfo {
77             my $self = shift;
78              
79             return $self->server_entry->netwalker_info;
80             }
81              
82             sub _create_manifold {
83             my $self = shift;
84             my $manifold_name = shift;
85             my %params = @_;
86              
87             my $manifold;
88             try {
89             $manifold = App::Manoc::Manifold->new_manifold( $manifold_name, %params );
90             }
91             catch {
92             my $error = "Internal error while creating manifold $manifold_name: $_";
93             $self->log->debug($error);
94             return;
95             };
96              
97             $manifold or $self->log->debug("Manifold constructor returned undef");
98             return $manifold;
99             }
100              
101             sub _build_source {
102             my $self = shift;
103              
104             my $entry = $self->server_entry;
105             my $nwinfo = $self->nwinfo;
106              
107             my $host = $entry->address->unpadded;
108              
109             my $manifold_name = $nwinfo->manifold;
110             $self->log->debug("Using Manifold $manifold_name");
111              
112             my %params = (
113             host => $host,
114             credentials => $self->credentials,
115             use_sudo => $nwinfo->use_sudo,
116             );
117              
118             my $source = $self->_create_manifold( $manifold_name, %params );
119              
120             if ( !$source ) {
121             my $error = "Cannot create source with manifold $manifold_name";
122             $self->log->error($error);
123             $self->task_report->add_error($error);
124             return;
125             }
126              
127             # auto connect
128             if ( !$source->connect() ) {
129             my $error = "Cannot connect to $host";
130             $self->log->error($error);
131             $self->task_report->add_error($error);
132             return;
133             }
134             return $source;
135             }
136              
137             sub _build_task_report {
138             my $self = shift;
139              
140             $self->server_entry or return;
141             my $server_address = $self->server_entry->address->address;
142             return App::Manoc::Netwalker::Poller::TaskReport->new( host => $server_address );
143             }
144              
145             #----------------------------------------------------------------------#
146             # #
147             # D a t a u p d a t e #
148             # #
149             #----------------------------------------------------------------------#
150              
151              
152             sub update {
153             my $self = shift;
154              
155             # check if there is a server object in the DB
156             my $entry = $self->server_entry;
157             unless ($entry) {
158             $self->log->error( "Cannot find server id ", $self->server_id );
159             return;
160             }
161              
162             # load netwalker info from DB
163             my $nwinfo = $self->nwinfo;
164             unless ($nwinfo) {
165             $self->log->error( "No netwalker info for server", $entry->hostname );
166             return;
167             }
168              
169             # try to connect and update nwinfo accordingly
170             $self->log->info( "Connecting to server ", $entry->hostname, " ", $entry->address );
171             if ( !$self->source ) {
172             $self->reschedule_on_failure;
173             $nwinfo->offline(1);
174             $nwinfo->update();
175             return;
176             }
177              
178             $self->update_server_info;
179              
180             $nwinfo->get_packages and
181             $self->update_packages;
182              
183             $nwinfo->get_vms and
184             $self->fetch_virtual_machines;
185              
186             $self->reschedule_on_success;
187             $nwinfo->last_visited( $self->timestamp );
188             $nwinfo->offline(0);
189              
190             $nwinfo->update();
191             $self->log->debug( "Server ", $entry->hostname, " ", $entry->address, " updated" );
192              
193             return 1;
194             }
195              
196              
197             sub update_server_info {
198             my $self = shift;
199              
200             my $source = $self->source;
201             my $server_entry = $self->server_entry;
202             my $nw_entry = $self->nwinfo;
203              
204             my $name = $source->name;
205             $nw_entry->name($name);
206             if ( defined($name) && $name ne $server_entry->hostname ) {
207             if ( $server_entry->hostname ) {
208             my $msg = "Name mismatch " . $server_entry->hostname . " $name";
209             $self->log->warn($msg);
210             }
211             else {
212             $server_entry->hostname($name);
213             $server_entry->update;
214             }
215             }
216              
217             $nw_entry->model( $source->model );
218             $nw_entry->os( $source->os );
219             $nw_entry->os_ver( $source->os_ver );
220             $nw_entry->vendor( $source->vendor );
221             $nw_entry->serial( $source->serial );
222              
223             if ( $source->does('App::Manoc::ManifoldRole::Host') ) {
224             $self->log->debug("Source implements host");
225              
226             $nw_entry->kernel( $source->kernel );
227             $nw_entry->kernel_ver( $source->kernel_ver );
228              
229             $nw_entry->cpu_model( $source->cpu_model );
230             $nw_entry->n_procs( $source->cpu_count );
231             $nw_entry->ram_memory( $source->ram_memory );
232              
233             $nw_entry->update_vm and $self->update_vm;
234             }
235              
236             $nw_entry->boottime( $source->boottime || 0 );
237              
238             $nw_entry->update;
239             }
240              
241              
242             sub update_vm {
243             my $self = shift;
244              
245             my $source = $self->source;
246             my $server_entry = $self->server_entry;
247             my $nw_entry = $self->nwinfo;
248             my $vm = $server_entry->vm;
249              
250             my $uuid = $source->uuid;
251             return unless defined($uuid);
252              
253             # nothing to change
254             defined($vm) && defined( $vm->identifier ) && lc($uuid) eq lc( $vm->identifier ) and
255             return;
256              
257             if ( defined($vm) && !defined( $vm->identifier ) ) {
258             $self->log->debug("Setting related vm uuid $uuid");
259             $vm->identifier($uuid);
260             $vm->update;
261             }
262             else {
263             my @virtual_machines;
264              
265             if ( defined($vm) && defined( $vm->hypervisor ) ) {
266             $self->log->debug(
267             "Searching for vm with $uuid in the same hypervisor or infrastructure");
268              
269             # check if there is already an unused vm with the given uuid in the same hypervisor
270             # or infrastructure
271             my $hypervisor = $vm->hypervisor;
272             my $vm_rs = (
273             $hypervisor->virtinfr ? $hypervisor->virtinfr->virtual_machines :
274             $hypervisor->virtual_machines
275             );
276             @virtual_machines = $vm_rs->unused->search(
277             {
278             identifier => { -in => [ lc($uuid), uc($uuid) ] }
279             }
280             );
281             }
282             elsif ( !defined( $server_entry->serverhw ) ) {
283             $self->log->debug("Searching for vm with $uuid and compatible name");
284              
285             my @names;
286             my $hostname = $server_entry->hostname;
287             push @names, $hostname;
288             $hostname =~ /^([^.]+)\./ and push @names, $1;
289              
290             @virtual_machines = $self->schema->resultset('VirtualMachine')->unused->search(
291             {
292             identifier => { -in => [ lc($uuid), uc($uuid) ] },
293             name => { -in => \@names }
294             }
295             );
296             }
297             if ( @virtual_machines == 1 ) {
298             my $new_vm = $virtual_machines[0];
299             $self->log->debug("Associating vm $uuid");
300             $server_entry->vm($new_vm);
301             $server_entry->update;
302             }
303             }
304             }
305              
306              
307             sub fetch_virtual_machines {
308             my $self = shift;
309              
310             my $source = $self->source;
311             return unless $source->does('App::Manoc::ManifoldRole::Hypervisor');
312              
313             my $server = $self->server_entry;
314             return unless $server->is_hypervisor;
315              
316             my $server_id = $server->id;
317             my $timestamp = $self->timestamp;
318              
319             my $vm_rs = (
320             $server->virtinfr ? $server->virtinfr->virtual_machines :
321             $server->virtual_machines
322             );
323              
324             my $vm_list = $source->virtual_machines;
325              
326             return unless $vm_list;
327              
328             foreach my $vm_info (@$vm_list) {
329             my $uuid = $vm_info->{uuid};
330              
331             my $vm;
332              
333             # search for uuid in hypervisor/virtualinfr
334             my @vms = $vm_rs->search( { identifier => $uuid } );
335             if ( @vms > 1 ) {
336             $self->log->warn(
337             "More than a vm with the same uuid $uuid. Info will not be updated.");
338             next;
339             }
340             if ( @vms == 1 ) {
341             $vm = $vms[0];
342             }
343              
344             # search for uuid among detached vms
345             if ( !$vm ) {
346             $self->log->debug("Searching detached vm with $uuid.");
347             my @detached_vms = $self->schema->resultset('VirtualMachine')->search(
348             {
349             identifier => $uuid,
350             hypervisor_id => undef,
351             virtinfr_id => undef
352             }
353             );
354             if ( @detached_vms == 1 ) {
355             $vm = $detached_vms[0];
356             $self->log->info("Reclaimed detached vm $uuid.");
357             $vm->hypervisor($server);
358             }
359             }
360              
361             # create a new vm
362             if ( !$vm ) {
363             $self->log->info("Creating new vm $uuid.");
364              
365             $vm = $self->schema->resultset('VirtualMachine')->new_result( {} );
366             $vm->identifier($uuid);
367             $vm->hypervisor($server);
368             }
369              
370             # update vm info
371             $self->log->debug("Updated vm $uuid.");
372             $vm->name( $vm_info->{name} );
373             $vm->vcpus( $vm_info->{vcpus} );
374             $vm->ram_memory( $vm_info->{memsize} );
375             $vm->update_or_insert();
376              
377             $self->schema->resultset('HostedVm')->register_tuple(
378             hypervisor_id => $server_id,
379             vm_id => $vm->id,
380             timestamp => $timestamp,
381             );
382             } # end of virtual machines loop
383              
384             }
385              
386              
387             sub update_packages {
388             my $self = shift;
389              
390             my $source = $self->source;
391             return unless $source->does('App::Manoc::ManifoldRole::Host');
392              
393             my $schema = $self->schema;
394             my $server = $self->server_entry;
395              
396             my $pkgs = $source->installed_sw;
397              
398             $server->installed_sw_pkgs->delete;
399             foreach my $p (@$pkgs) {
400             my ( $name, $version ) = @$p;
401              
402             my $pkg = $schema->resultset('SoftwarePkg')->find_or_create( { name => $name } );
403             $server->update_or_create_related(
404             installed_sw_pkgs => { software_pkg => $pkg, version => $version } );
405             }
406             }
407              
408             1;
409              
410             # Local Variables:
411             # mode: cperl
412             # indent-tabs-mode: nil
413             # cperl-indent-level: 4
414             # cperl-indent-parens-as-block: t
415             # End:
416              
417             __END__
418              
419             =pod
420              
421             =head1 NAME
422              
423             App::Manoc::Netwalker::Poller::ServerTask - Server poller task
424              
425             =head1 VERSION
426              
427             version 2.99.2
428              
429             =head1 CONSUMED ROLES
430              
431             =over 4
432              
433              
434              
435             =back
436              
437             * App::Manoc::Netwalker::Poller::BaseTask
438             * App::Manoc::Logger::Role
439              
440             =head1 ATTRIBUTES
441              
442             =head2 server_id
443              
444             The id in Manoc DB of the server to poll
445              
446             =head2 server_entry
447              
448             The Server row in Manoc DB identified by C<server_id>
449              
450             =head2 nwinfo
451              
452             The curresponding ServerNWInfo row for C<server_entry>
453              
454             =head2 source
455              
456             The source for information, i.e. a connected Manifold object.
457              
458             =head2 config_source
459              
460             The source for server configuration backup
461              
462             =head2 task_report
463              
464             =head1 METHODS
465              
466             =head2 update
467              
468             Update server information using data from C<source>. Reschedule next
469             poll.
470              
471             =head2 update_server_info
472              
473             Called by C<update>, updates info in C<nwinfo>.
474              
475             =head2 update_vm
476              
477             If server is hosted on a virtual machine and the vm uuid is available,
478             update the associated virtual machine in Manoc DB.
479              
480             =head2 update_packages
481              
482             Updates the list of software packages installed on the server
483              
484             Requires a Manifold consuming the L<App::Manoc::ManifoldRole::Host> role.
485              
486             =head2 fetch_virtual_machines
487              
488             If the server is an hypervisor update the list of its currently hosted
489             virtual machines.
490              
491             Requires a Manifold consuming the
492             L<App::Manoc::ManifoldRole::Hypervisor> role.
493              
494             =head1 AUTHORS
495              
496             =over 4
497              
498             =item *
499              
500             Gabriele Mambrini <gmambro@cpan.org>
501              
502             =item *
503              
504             Enrico Liguori
505              
506             =back
507              
508             =head1 COPYRIGHT AND LICENSE
509              
510             This software is copyright (c) 2017 by Gabriele Mambrini.
511              
512             This is free software; you can redistribute it and/or modify it under
513             the same terms as the Perl 5 programming language system itself.
514              
515             =cut