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

Last change on this file since 1226 was 1226, checked in by nanardon, 11 years ago

Fix comment about host in zone

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