source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Task/Buildnet.pm @ 1740

Last change on this file since 1740 was 1740, checked in by nanardon, 8 years ago

Make la-sync-manager simplier, better granularity about when running modules

File size: 27.9 KB
Line 
1package LATMOS::Accounts::Task::Buildnet;
2
3use strict;
4use warnings;
5use base qw(LATMOS::Accounts::Task);
6use LATMOS::Accounts::Log;
7use LATMOS::Accounts::Utils;
8use FindBin qw($Bin);
9use POSIX qw(strftime);
10use Net::IP;
11use File::Path;
12use File::Temp qw(tempfile);
13use Net::IPv4Addr;
14use Net::IPv6Addr;
15
16=head1 NAME
17
18LATMOS::Accounts::Task::Buildnet - Task to generate network configuration files
19
20=head1 DESCRIPTION
21
22This contains functions to generate network config file from C<Netzone> and
23C<Nethost> object.
24
25These config file can be:
26
27=over 4
28
29=item DNS zone files
30
31A standard DNS zone generated from a header and entries found in bases
32
33=item DNS reverse zone files
34
35A reverse DNS zone genarated from a header and entries found in bases
36
37=item A DHCP host list
38
39A file well formated host list to be included in dhcpd config file.
40
41=back
42
43=cut
44
45# Always try because depend also on files:
46
47sub runDelay { 10 }
48
49sub init {
50    my ($self) = @_;
51    my $LA = LATMOS::Accounts->new($self->{config}, noacl => 1);
52
53    $self->{_base} = $LA->base();
54    $self->{_base} && $self->{_base} or die "Cannot load base";
55    $self->{_base}->type eq 'sql' or die "This module work only with SQL base type\n";
56    $self->{_la} = $LA;
57}
58
59sub run {
60    my ($self) = @_;
61
62    if (my $cmd = $self->_la->val('_network_', 'pre')) {
63        exec_command(
64            $cmd,
65            {
66                TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''),
67                OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''),
68                DIRECTORY => $self->_la->val('_network_', 'output_dir', ''),
69                HOOK_TYPE => 'PRE',
70            },
71        );
72    }
73
74    my %headers;
75    foreach my $zone ($self->_base->search_objects('netzone')) {
76        my $ozone = $self->_base->get_object('netzone', $zone)
77            or next;
78        # check file need regeneration:
79        $self->_check_zone_need_update($ozone) or do {
80            la_log(LA_DEBUG, "No need to rebuild %s", $ozone->id);
81            next;
82        };
83        my $header = $self->_pre_zone($ozone) or next;
84        $headers{$zone} = $header;
85    }
86    $self->_base->commit;
87
88    foreach (keys %headers) {
89        $self->gen_zone($_, $headers{$_}) or return;
90    }
91    $self->_base->rollback;
92
93    if (my $cmd = $self->_la->val('_network_', 'post')) {
94        exec_command(
95            $cmd,
96            {
97                TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''),
98                OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''),
99                DIRECTORY => $self->_la->val('_network_', 'output_dir', ''),
100                HOOK_TYPE => 'POST',
101            },
102        );
103    }
104
105    1;
106}
107
108sub _la {
109    $_[0]->{_la}
110}
111
112sub _base {
113    my ($self) = @_;
114    return $self->{_base} if ($self->{_base});
115    my $base = $self->SUPER::base;
116    return $self->{_base} = $base
117}
118
119sub _bnet_state {
120    my ($self) = @_;
121    return $self->{_bnet_state} if ($self->{_bnet_state});
122    # where trace goes:
123    my $state_file =  $self->_la->val('_default_', 'state_dir', '/');
124    $state_file .= '/buildnet_state.ini';
125    la_log(LA_DEBUG, "Status file is %s", $state_file);
126    if ($state_file && ! -w $state_file) {
127        # don't exists, we have to create it
128        open(my $handle, '>', $state_file) or do {
129            la_log(LA_ERR, "Cannot open build net status file %s",
130                $state_file);
131            return;
132        };
133        print $handle "[_default_]\n";
134        close($handle);
135    }
136    $self->{_bnet_state} = Config::IniFiles->new(
137        -file => $state_file
138    );
139}
140
141sub _write_state_file {
142    la_log(LA_DEBUG, "Writting status file");
143    $_[0]->_bnet_state->RewriteConfig;
144}
145
146sub _template_file {
147    my ($self, $ozone) = @_;
148
149    my $template =  join('/', grep { $_ } $self->_la->val('_network_', 'template_dir'),
150        $ozone->get_attributes('templateD'));
151    la_log(LA_DEBUG, "Template for %s is %s", $ozone->id, $template);
152    $template;
153}
154
155sub _output_file {
156    my ($self, $ozone) = @_;
157
158    my $path = join(
159        '/',
160        $self->_la->val('_network_', 'output_dir',
161            ($self->_la->val('_default_', 'state_dir'), $ozone->get_attributes('type'))
162        )
163    );
164
165    if (! -d $path) {
166        la_log(LA_INFO, 'Creating directory %s', $path);
167        mkpath($path) or return;
168    }
169    my $output = join('/', $path, $ozone->get_attributes('outputD'));
170    la_log(LA_DEBUG, 'output file for %s is %s', $ozone->id, $output);
171    $output;
172}
173
174=head2 get_zone_rev ($zone)
175
176Return next zone revision (DNS).
177
178This revision is formated from date + incremental serial number. If day change,
179serial start to 1. If serial goes goes over 99, head build from date is
180increment.
181
182The code ensure returned number is always highter that current one.
183
184=cut
185
186sub get_zone_rev {
187    my ($self, $ozone) = @_;
188    my $date = strftime('%Y%m%d01', localtime);
189    my $oldrev = $ozone->get_attributes('zoneRevision') || 0;
190    my $rev;
191    if ($oldrev >= $date) {
192        # same date, increment subrev
193        $rev = $oldrev + 1;
194    } else {
195        # date has changed, subrev is 1
196        $rev = $date;
197    }
198    la_log(LA_DEBUG, 'new dns revision for %s is %s', $ozone->id, $rev);
199    $ozone->set_c_fields(zoneRevision => $rev) or do {
200        return;
201    };
202    $rev
203}
204
205
206sub _check_zone_need_update {
207    my ($self, $ozone) = @_;
208
209    # If env var is set, do it anyway
210    if ($ENV{LA_BNET_FORCE}) { return 1 }
211
212    if ($ozone->get_attributes('rev') >
213        $self->_bnet_state->val($ozone->id, 'dbrev', 0)) {
214        return 1;
215    }
216
217    return 1 if (! -f $self->_output_file($ozone));
218
219
220    if ($ozone->get_attributes('type') ne 'dhcp' && $ozone->get_attributes('templateD')) {
221        my $template = $self->_template_file($ozone);
222        my $output = $self->_output_file($ozone);
223        my @tstat = stat($template);
224        my @ostat = stat($output);
225        if (($ostat[9] || 0) <= ($tstat[9] || 0)) {
226            return 1;
227        }
228    }
229
230    return;
231}
232
233sub _set_last_build {
234    my ($self, $ozone) = @_;
235
236    my $lctime = scalar(localtime);
237    la_log(LA_DEBUG, 'Update last build for zone %s (%s)', $ozone->id, $lctime);
238    $ozone->set_c_fields('lastBuild' => $lctime);
239}
240
241sub _pre_zone {
242    my ($self, $ozone) = @_;
243
244    if (!$ozone->get_attributes('templateD')) {
245        la_log(LA_ERR, "No template file for zone %s, aborting", $ozone->id);
246        return;
247    }
248
249    my $textzone = $self->_comment_zone($ozone);
250    if ($ozone->get_attributes('type') =~ /^(dns|reverse)$/) {
251        my $tzone = $self->_read_template($ozone) or return;
252        $textzone .= $tzone;
253    }
254    $self->_set_last_build($ozone);
255
256    return $textzone;
257}
258
259=head2 gen_zone($zone, $header)
260
261Generate zone file C<$zone> with header C<$header>
262
263=cut
264
265# TODO what is $header
266
267sub gen_zone {
268    my ($self, $zone, $header) = @_;
269
270    my $ozone = $self->_base->get_object('netzone', $zone)
271        or return;
272
273    la_log(LA_DEBUG, "Start building zone %s (%s)", $zone,
274        $ozone->get_attributes('type'));
275
276    $header ||= $self->_pre_zone($ozone) or return;
277
278    my $type = $ozone->get_attributes('type');
279    my $res =
280        $type eq 'dns'     ? $self->_gen_dns_zone($ozone, $header) :
281        $type eq 'reverse' ? $self->_gen_reverse_zone($ozone, $header) :
282        $type eq 'dhcp'    ? $self->_gen_dhcp_zone($ozone, $header) :
283        $type eq 'puppet'  ? $self->_gen_puppet_zone($ozone, $header) :
284        undef;
285
286    if ($res) {
287
288        if (my $cmd = $self->_la->val('_network_', 'post_file',
289                $self->_la->val('_network_', 'post_zone'))) {
290            exec_command(
291                $cmd,
292                {
293                TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''),
294                OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''),
295                DIRECTORY => $self->_la->val('_network_', 'output_dir', ''),
296                TEMPLATE_FILE => $ozone->get_attributes('templateD'),
297                OUTPUT_FILE => $ozone->get_attributes('outputD'),
298                HOOK_TYPE => 'POSTFILE',
299                },
300            );
301        }
302
303        $self->_bnet_state->newval($ozone->id, 'dbrev',
304            $ozone->get_attributes('rev'));
305        la_log LA_DEBUG, "Zone rev build point is %d for %s",
306            $ozone->get_attributes('rev'),
307            $ozone->id;
308        $self->_bnet_state->SetParameterComment(
309            $ozone->id, 'dbrev',
310            scalar(localtime));
311        $self->_write_state_file;
312
313    } else {
314        $self->_base->rollback;
315    }
316    $res
317}
318
319sub _checkzone_output {
320    my ($self, $ozone, $output) = @_;
321
322    if (!$self->_la->val('_network_', 'checkzone')) {
323        return 1;
324    }
325
326    my ($fh, $filename) = tempfile();
327
328    print $fh $output;
329    close($fh);
330
331    my $named_checkzone = $self->_la->val('_network_', 'named-checkzone',
332        '/usr/sbin/named-checkzone');
333
334    my $msg;
335    my $res = exec_command(sprintf(
336            "%s -k fail '%s' '%s'",
337            $named_checkzone,
338            $ozone->id,
339            $filename,
340        ), undef, $msg);
341    if (!$res) {
342        la_log(LA_ERR, "Error on zone %s: ", $ozone->id);
343        la_log(LA_ERR, "  msg: $_") foreach (split(/\n/, $msg));
344    } else {
345        unlink($filename);
346    }
347    $res
348}
349
350sub _comment_zone {
351    my ($self, $ozone) = @_;
352
353    my @output = ();
354    my $com_prefix =
355        $ozone->get_attributes('type') =~ /^(dhcp|puppet)$/ ? '# ' : '; ';
356    push @output, sprintf('Zone %s, type %s', $ozone->id,
357        $ozone->get_attributes('type'));
358    push @output, $ozone->get_attributes('description')
359        if ($ozone->get_attributes('description'));
360    push @output, sprintf('Generated by %s', q$Id: BuildNet.pm 6283 2011-05-20 10:16:51Z nanardon $ );
361    push @output, sprintf('          the %s', scalar(localtime) );
362    push @output, sprintf('Network: %s', join(', ', $ozone->get_attributes('net')))
363        if ($ozone->get_attributes('net'));
364    push @output, sprintf('Exclude Network: %s', join(', ',
365            $ozone->get_attributes('netExclude')))
366        if ($ozone->get_attributes('netExclude'));
367    if ($ozone->get_attributes('type') eq 'dhcp') {
368        push(@output, 'This dhcp zone include dynamic IP address')
369            if ($ozone->get_attributes('allow_dyn'));
370    }
371
372    return to_ascii(join('', map { $com_prefix . $_ . "\n" } @output) . "\n");
373}
374
375sub _comment_nethost {
376    my ($self, $nethost) = @_;
377
378    my $displayeduser;
379    if (my $owner = $nethost->get_attributes('user')) {
380        if (my $user = $self->_base->get_object('user', $owner)) {
381            $displayeduser = $user->get_attributes('displayName');
382        }
383    }
384    if ((!$displayeduser) && (my $owner = $nethost->get_attributes('owner'))) {
385        if (my $user = $self->_base->get_object('user', $owner)) {
386            $displayeduser = $user->get_attributes('displayName');
387        }
388    }
389    my @desc = ($displayeduser, $nethost->get_attributes('description'));
390
391    return to_ascii(join(', ', grep { $_ } @desc) || '');
392}
393
394sub _read_template {
395    my ($self, $ozone) = @_;
396
397    my $revision = $self->get_zone_rev($ozone) or return;
398    my $textzone = '';
399    if (open(my $handle, '<', $self->_template_file($ozone))) {
400        while (my $line = <$handle>) {
401            $line =~ s/(\d+\s*;\s*)?\@REVISION@/$revision/;
402            $textzone .= $line;
403        }
404        close($handle);
405    } else {
406        la_log(LA_ERR, "Can't open template file for zone %s", $ozone->id);
407        return;
408    }
409    return $textzone;
410}
411
412sub _gen_dns_zone {
413    my ($self, $ozone, $textzone) = @_;
414
415    my $dbzone = "\n; Comming from database:\n";
416    if ($ozone->get_attributes('net')) {
417        my $findhost = $self->_base->db->prepare_cached(q{
418            select name, value::inet as value from nethost join nethost_attributes_ips on
419            nethost.ikey = nethost_attributes_ips.okey
420            where value::inet <<= any(?) and exported = true
421            except
422            select name, value::inet from nethost join nethost_attributes_ips on
423            nethost.ikey = nethost_attributes_ips.okey
424            where value::inet <<= any(?)
425            order by value, name
426        });
427        $findhost->execute(
428            [ $ozone->get_attributes('net') ],
429            [ $ozone->get_attributes('netExclude') ],
430        ) or do {
431            la_log LA_ERR, "Cannot fetch host list: %s",
432                $self->_base->db->errstr;
433            return;
434        };
435        my %lists;
436        my %names;
437        # Storing all name in %names to check later if CNAME does not conflict
438        while (my $res = $findhost->fetchrow_hashref) {
439            $lists{$res->{name}} ||= {};
440            push(@{$lists{$res->{name}}{ip}}, $res->{value});
441            my $host_o = $self->_base->get_object('nethost', $res->{name});
442            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
443                $names{$_} = 1;
444            }
445        }
446
447        foreach my $res (sort keys %lists) {
448            my $host_o = $self->_base->get_object('nethost', $res) or do {
449                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
450                return;
451            };
452            my $desc = $self->_comment_nethost($host_o);
453            $dbzone .= $desc
454                ? '; ' . $desc . "\n"
455                : '';
456            foreach my $ip (@{$lists{$res}{ip}}) {
457                $dbzone .= sprintf(
458                    "%-30s IN    %-4s     %s\n",
459                    $res,
460                    ($ip =~ /:/ ? 'AAAA' : 'A'),
461                    $ip
462                );
463            }
464            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
465                foreach my $ip (@{$lists{$res}{ip}}) {
466                    $dbzone .= sprintf(
467                        "%-30s IN    %-4s     %s\n",
468                        $_,
469                        ($ip =~ /:/ ? 'AAAA' : 'A'),
470                        $ip
471                    );
472                }
473            }
474            foreach (grep { $_ } $host_o->get_attributes('cname')) {
475                # It is deny to have both:
476                # foo IN A
477                # foo IN CNAME
478                if ($names{$_}) {
479                    my $msg .= sprintf(
480                        'Cname %s to %s exclude because %s is already an A record',
481                        $_, $res, $_
482                    );
483                    la_log(LA_ERR, sprintf("$msg (zone %s)", $ozone->id));
484                    $dbzone .= "; $msg\n";
485                } else {
486                    $dbzone .= sprintf("%-30s IN    CNAME    %s\n", $_, $res,);
487                }
488            }
489            # SSH finger print
490            foreach my $name (grep { $_ } ($res, $host_o->get_attributes('otherName'))) {
491                foreach my $sshfp (grep { $_ } $host_o->get_attributes('sshfp')) {
492                    $dbzone .= sprintf(
493                        "%-30s IN    SSHFP    %s\n",
494                        $name,
495                        $sshfp,
496                    );
497                }
498            }
499        }
500    }
501
502    $dbzone .= "; End of data from database\n";
503
504    if (!$self->_checkzone_output($ozone, $textzone . $dbzone)) {
505        la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
506            $ozone->id);
507        return;
508    }
509
510    if (open(my $handle, '>', $self->_output_file($ozone))) {
511        print $handle $textzone;
512        print $handle $dbzone;
513        close($handle);
514        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
515            $self->_output_file($ozone));
516    } else {
517       la_log(LA_ERR, "Can't open output file for zone %s", $ozone->id);
518       return;
519   }
520   1;
521}
522
523sub _gen_reverse_zone {
524    my ($self, $ozone, $textzone) = @_;
525
526    my $domain = $ozone->get_attributes('domain') || '';
527    my $dbzone = "\n; Comming from database:\n";
528    if ($ozone->get_attributes('net')) {
529        my $findhost = $self->_base->db->prepare_cached(q{
530            select * from (
531            select * from nethost join nethost_attributes_ips on
532            nethost.ikey = nethost_attributes_ips.okey
533            where value::inet <<= ? and exported = true
534            except
535            select * from nethost join nethost_attributes_ips on
536            nethost.ikey = nethost_attributes_ips.okey
537            where value::inet <<= any(?)
538            ) as q
539            order by value::inet
540
541        });
542        $findhost->execute(
543            $ozone->get_attributes('net'),
544            [ $ozone->get_attributes('netExclude') ],
545        ) or do {
546            la_log LA_ERR, "Cannot fetch host list: %s",
547                $self->_base->db->errstr;
548            return;
549        };
550
551        # reverse is complicated:
552        my ($net) = $ozone->get_attributes('net') or do {
553        $self->base->log('Cannot fetch attribute "net" for zone %s', $ozone->id);
554        return;
555    };
556        my $netip = Net::IP->new($net) or do {
557                $self->base->log(LA_ERR, "Cannot build reverse zone %s: wrong net %s", $ozone->id, $net);
558                return;
559        };
560        my $mask = $netip->prefixlen;
561
562        while (my $res = $findhost->fetchrow_hashref) {
563            my $host_o = $self->_base->get_object('nethost', $res->{name}) or do {
564                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
565                return;
566            };
567            my $desc = $self->_comment_nethost($host_o);
568            my $reverse = $host_o->get_attributes('reverse') || ($res->{name} . '.');
569            $dbzone .= $desc
570                ? '; ' . $desc . "\n"
571                : '';
572            my $revip;
573            my $fmt;
574            if ($res->{value} =~ /:/) {
575                # IPv6
576                my $m = $mask/4;
577                $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int;
578                $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i;
579                $fmt = "%-72s IN    PTR    %s%s\n";
580            } else {
581                # ipv4
582                if ($mask > 24) {
583                    $self->base->log(LA_ERR, 'Mask for zone %s cannot be %d', $ozone->id, $mask);
584                    return;
585                }
586                my @ippart = split(/\./, $res->{value});
587                splice(@ippart, 0, $mask/8); # get rid of start of ip
588                my @nippart;
589                while (@ippart) { unshift(@nippart, shift(@ippart)) }
590                $revip = join('.', @nippart);
591                $fmt = "%-12s IN    PTR    %s%s\n";
592            }
593 
594            $dbzone .= sprintf($fmt, $revip,
595                $reverse =~ /\.$/
596                    ? ($reverse, ($domain ? "$domain." : '.'))
597                    : ($reverse, '.'));
598        }
599    }
600
601    $dbzone .= "; End of data from database\n";
602
603    if (!$self->_checkzone_output($ozone, $textzone . $dbzone)) {
604        la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
605            $ozone->id);
606        return;
607    }
608
609    if (open(my $handle, '>', $self->_output_file($ozone))) {
610        print $handle $textzone;
611        print $handle $dbzone;
612        close($handle);
613        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
614            $self->_output_file($ozone));
615    } else {
616       la_log(LA_ERR, "can't open output file %s (%s)",
617           $self->_output_file($ozone), $!);
618       return;
619   }
620   1;
621}
622
623sub _gen_dhcp_zone {
624    my ($self, $ozone, $output) = @_;
625
626    my $outzone = $ozone;
627
628    my @net;
629    if ($outzone->get_attributes('net')) {
630        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
631            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
632                join(', ', $outzone->get_attributes('net')));
633            next;
634        };
635    }
636
637    {
638        my $find = $self->_base->db->prepare(q{
639            select * from nethost where exported = true and ikey in(
640            select okey from nethost_attributes where attr = 'macaddr'
641            intersect (
642                select nethost_attributes_ips.okey from nethost_attributes_ips join
643                netzone_attributes
644                on netzone_attributes.attr = 'net' and
645                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
646                join netzone on netzone.ikey = netzone_attributes.okey
647                where netzone.name = $1
648
649                except
650                select nethost_attributes_ips.okey from nethost_attributes_ips join
651                netzone_attributes
652                on netzone_attributes.attr = 'netExclude' and
653                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
654                join netzone on netzone.ikey = netzone_attributes.okey
655                where netzone.name = $1
656                )
657            )
658            order by name
659
660            });
661        $find->execute($ozone->id) or do {
662            la_log LA_ERR, "Cannot fetch host list: %s",
663                $self->_base->db->errstr;
664            return;
665        };
666        while (my $res = $find->fetchrow_hashref) {
667            my $nethost = $res->{name};
668
669            my $obj = $self->_base->get_object('nethost', $nethost) or do {
670                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
671                return;
672            };
673
674            my $retainip;
675            if (@net) {
676                foreach my $inet (@net) {
677                    ($retainip) = grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip')
678                        and last;
679                }
680            }
681
682            $obj->get_attributes('noDynamic') && !$retainip and next;
683
684            my $desc = $self->_comment_nethost($obj);
685            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
686                $output .= $desc
687                ? '# ' . $desc . "\n"
688                : '';
689                my $fmac = $mac;
690                $fmac =~ s/://g;
691                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
692                $output .= sprintf("    hardware ethernet %s;\n", $mac);
693                $output .= sprintf("    fixed-address %s;\n", $retainip)
694                if ($retainip);
695                $output .= "}\n\n";
696            }
697        }
698    }
699    if ($ozone->get_attributes('allow_dyn')) {
700        $output .= "\n# Host without IP:\n";
701        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
702        my $find = $self->_base->db->prepare(q{
703            select * from nethost where exported = true and ikey in(
704            select okey from nethost_attributes where attr = 'macaddr'
705            } . (@dynfrom ? q{
706            intersect
707            (
708                select ikey from nethost where ikey not in
709                    (select okey from nethost_attributes_ips)
710                union
711
712                (
713                select nethost_attributes_ips.okey from nethost_attributes_ips join
714                netzone_attributes
715                on netzone_attributes.attr = 'net' and
716                   netzone_attributes.value::inet >>=
717                   nethost_attributes_ips.value::inet
718                   join netzone on netzone.ikey = netzone_attributes.okey
719                   where netzone.name = any(?)
720                except
721                select nethost_attributes_ips.okey from nethost_attributes_ips join
722                netzone_attributes
723                on netzone_attributes.attr = 'netExclude' and
724                   netzone_attributes.value::inet >>=
725                   nethost_attributes_ips.value::inet
726                   join netzone on netzone.ikey = netzone_attributes.okey
727                   where netzone.name = any(?)
728                )
729            )} : '') . q{
730            except
731            select nethost_attributes_ips.okey from nethost_attributes_ips join
732            netzone_attributes
733            on netzone_attributes.attr = 'net' and
734            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
735            join netzone on netzone.ikey = netzone_attributes.okey
736            where netzone.name = ?
737            )
738            order by name
739
740            });
741        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
742            la_log LA_ERR, "Cannot fetch host list: %s",
743                $self->_base->db->errstr;
744            return;
745        };
746        while (my $res = $find->fetchrow_hashref) {
747            my $nethost = $res->{name};
748
749            my $obj = $self->_base->get_object('nethost', $nethost);
750
751            $obj->get_attributes('noDynamic') and next;
752
753            my $desc = $self->_comment_nethost($obj);
754            foreach my $mac (grep { $_ } $obj->get_attributes('macaddr')) {
755                $output .= $desc
756                ? '# ' . $desc . "\n"
757                : '';
758                my $fmac = $mac;
759                $fmac =~ s/://g;
760                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
761                $output .= sprintf("    hardware ethernet %s;\n", $mac);
762                $output .= "}\n\n";
763            }
764        }
765    }
766
767    $output .= "# End of data from database\n";
768    if (open(my $handle, '>', $self->_output_file($outzone))) {
769        print $handle $output;
770        close($handle);
771        la_log(LA_INFO, "zone %s written into %s", $outzone->id,
772            $self->_output_file($outzone));
773    } else {
774        la_log(LA_ERR, "Can't open output file for dhcp zone %s (%s)",
775            $outzone->id, $!);
776        return;
777    }
778    1;
779}
780
781sub _gen_puppet_zone {
782    my ($self, $ozone, $output) = @_;
783
784    if ($ozone->get_attributes('net')) {
785        my $findhost = $self->_base->db->prepare_cached(q{
786            select * from (
787            select * from nethost join nethost_attributes_ips on
788            nethost.ikey = nethost_attributes_ips.okey
789            where value::inet <<= ? and exported = true
790            except
791            select * from nethost join nethost_attributes_ips on
792            nethost.ikey = nethost_attributes_ips.okey
793            where value::inet <<= any(?)
794            ) as q
795            order by value::inet
796
797        });
798        $findhost->execute(
799            $ozone->get_attributes('net'),
800            [ $ozone->get_attributes('netExclude') ],
801        ) or do {
802            la_log LA_ERR, "Cannot fetch host list: %s",
803                $self->_base->db->errstr;
804            return;
805        };
806
807        my @puppetclasses = $ozone->get_attributes('puppetClass');
808        while (my $res = $findhost->fetchrow_hashref) {
809            my $nethost = $res->{name};
810
811            my $obj = $self->_base->get_object('nethost', $nethost) or do {
812                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
813                return;
814            };
815
816            # merging classes from host and zone
817            my %classes = map { $_ => 1 } grep { $_ } (
818                $obj->get_attributes('puppetClass'),
819                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
820            );
821            my $desc = $self->_comment_nethost($obj);
822            $output .= sprintf("node '%s' {\n", $obj->id);
823            $output .= $desc
824                ? sprintf("    # %s\n", $desc)
825                : '';
826            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
827            $output .= "}\n\n";
828        }
829    }
830
831    $output .= "# End of data from database\n";
832    my $file = $self->_output_file($ozone);
833    if (open(my $handle, '>', $file)) {
834        print $handle $output;
835        close($handle);
836        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
837            $self->_output_file($ozone));
838    } else {
839        la_log(LA_ERR, "Can't open output file for puppet zone %s (%s)",
840            $ozone->id, $!);
841        return;
842    }
843    1;
844}
845
846sub reset_savepoint {
847    my ($self) = @_;
848    foreach my $zone ($self->_base->search_objects('netzone')) {
849        my $ozone = $self->_base->get_object('netzone', $zone)
850            or next;
851
852        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
853        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
854        $self->_bnet_state->SetParameterComment(
855            $ozone->id, 'dbrev',
856            'Reset savepoint the ' . scalar(localtime));
857    }
858
859    $self->_write_state_file;
860}
861
8621;
863
864__END__
865
866=head1 AUTHOR
867
868Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
869
870=head1 COPYRIGHT AND LICENSE
871
872Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
873
874This library is free software; you can redistribute it and/or modify
875it under the same terms as Perl itself, either Perl version 5.10.0 or,
876at your option, any later version of Perl 5 you may have available.
877
878=cut
Note: See TracBrowser for help on using the repository browser.