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

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

use 'savepoint' word instead checkpoint

File size: 26.8 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    my ($fh, $filename) = tempfile();
322
323    print $fh $output;
324    close($fh);
325
326    my $msg;
327    my $res = exec_command(sprintf(
328            "%s -k fail '%s' '%s'",
329            '/usr/sbin/named-checkzone',
330            $ozone->id,
331            $filename,
332        ), undef, $msg);
333    if (!$res) {
334        la_log(LA_ERR, "Error on zone %s: ", $ozone->id);
335        la_log(LA_ERR, "  msg: $_") foreach (split(/\n/, $msg));
336    } else {
337        unlink($filename);
338    }
339    $res
340}
341
342sub _comment_zone {
343    my ($self, $ozone) = @_;
344
345    my @output = ();
346    my $com_prefix =
347        $ozone->get_attributes('type') =~ /^(dhcp|puppet)$/ ? '# ' : '; ';
348    push @output, sprintf('Zone %s, type %s', $ozone->id,
349        $ozone->get_attributes('type'));
350    push @output, $ozone->get_attributes('description')
351        if ($ozone->get_attributes('description'));
352    push @output, sprintf('Generated by %s', q$Id: BuildNet.pm 6283 2011-05-20 10:16:51Z nanardon $ );
353    push @output, sprintf('  The %s', scalar(localtime) );
354    push @output, sprintf('Network: %s', join(', ', $ozone->get_attributes('net')))
355        if ($ozone->get_attributes('net'));
356    push @output, sprintf('Exclude Network: %s', join(', ',
357            $ozone->get_attributes('netExclude')))
358        if ($ozone->get_attributes('netExclude'));
359    if ($ozone->get_attributes('type') eq 'dhcp') {
360        push(@output, 'This dhcp zone include dynamic IP address')
361            if ($ozone->get_attributes('allow_dyn'));
362    }
363
364    return to_ascii(join('', map { $com_prefix . $_ . "\n" } @output) . "\n");
365}
366
367sub _comment_nethost {
368    my ($self, $nethost) = @_;
369
370    my $user;
371    if (my $owner = $nethost->get_attributes('user')) {
372        if (my $user = $self->_base->get_object('user', $owner)) {
373            $user = $user->get_attributes('displayName');
374        }
375    }
376    if ((!$user) && (my $owner = $nethost->get_attributes('owner'))) {
377        if (my $user = $self->_base->get_object('user', $owner)) {
378            $user = $user->get_attributes('displayName');
379        }
380    }
381    my @desc = ($user, $nethost->get_attributes('description'));
382
383    return to_ascii(join(', ', grep { $_ } @desc) || '');
384}
385
386sub _read_template {
387    my ($self, $ozone) = @_;
388
389    my $revision = $self->get_zone_rev($ozone) or return;
390    my $textzone = '';
391    if (open(my $handle, '<', $self->_template_file($ozone))) {
392        while (my $line = <$handle>) {
393            $line =~ s/(\d+\s*;\s*)?\@REVISION@/$revision/;
394            $textzone .= $line;
395        }
396        close($handle);
397    } else {
398        la_log(LA_ERR, "Can't open template file for zone %s", $ozone->id);
399        return;
400    }
401    return $textzone;
402}
403
404sub _gen_dns_zone {
405    my ($self, $ozone, $textzone) = @_;
406
407    my $dbzone = "\n; Comming from database:\n";
408    if ($ozone->get_attributes('net')) {
409        my $findhost = $self->_base->db->prepare_cached(q{
410            select name, value::inet as value from nethost join nethost_attributes_ips on
411            nethost.ikey = nethost_attributes_ips.okey
412            where value::inet <<= any(?) and exported = true
413            except
414            select name, value::inet from nethost join nethost_attributes_ips on
415            nethost.ikey = nethost_attributes_ips.okey
416            where value::inet <<= any(?)
417            order by value, name
418        });
419        $findhost->execute(
420            [ $ozone->get_attributes('net') ],
421            [ $ozone->get_attributes('netExclude') ],
422        ) or do {
423            la_log LA_ERR, "Cannot fetch host list: %s",
424                $self->_base->db->errstr;
425            return;
426        };
427        my %lists;
428        my %names;
429        # Storing all name in %names to check later if CNAME does not conflict
430        while (my $res = $findhost->fetchrow_hashref) {
431            $lists{$res->{name}} ||= {};
432            push(@{$lists{$res->{name}}{ip}}, $res->{value});
433            my $host_o = $self->_base->get_object('nethost', $res->{name});
434            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
435                $names{$_} = 1;
436            }
437        }
438
439        foreach my $res (sort keys %lists) {
440            my $host_o = $self->_base->get_object('nethost', $res) or do {
441                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
442                return;
443            };
444            my $desc = $self->_comment_nethost($host_o);
445            $dbzone .= $desc
446                ? '; ' . $desc . "\n"
447                : '';
448            foreach my $ip (@{$lists{$res}{ip}}) {
449                $dbzone .= sprintf(
450                    "%-30s IN    %-4s     %s\n",
451                    $res,
452                    ($ip =~ /:/ ? 'AAAA' : 'A'),
453                    $ip
454                );
455            }
456            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
457                foreach my $ip (@{$lists{$res}{ip}}) {
458                    $dbzone .= sprintf(
459                        "%-30s IN    %-4s     %s\n",
460                        $_,
461                        ($ip =~ /:/ ? 'AAAA' : 'A'),
462                        $ip
463                    );
464                }
465            }
466            foreach (grep { $_ } $host_o->get_attributes('cname')) {
467                # It is deny to have both:
468                # foo IN A
469                # foo IN CNAME
470                if ($names{$_}) {
471                    my $msg .= sprintf(
472                        'Cname %s to %s exclude because %s is already an A record',
473                        $_, $res->{name}, $_
474                    );
475                    la_log(LA_ERR, sprintf("$msg (zone %s)", $ozone->id));
476                    $dbzone .= "; $msg\n";
477                } else {
478                    $dbzone .= sprintf("%-30s IN    CNAME    %s\n", $_, $res,);
479                }
480            }
481        }
482    }
483
484    $dbzone .= "; End of data from database\n";
485
486    if (!$self->_checkzone_output($ozone, $textzone . $dbzone)) {
487        la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
488            $ozone->id);
489        return;
490    }
491
492    if (open(my $handle, '>', $self->_output_file($ozone))) {
493        print $handle $textzone;
494        print $handle $dbzone;
495        close($handle);
496        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
497            $self->_output_file($ozone));
498    } else {
499       la_log(LA_ERR, "Can't open output file for zone %s", $ozone->id);
500       return;
501   }
502   1;
503}
504
505sub _gen_reverse_zone {
506    my ($self, $ozone, $textzone) = @_;
507
508    my $domain = $ozone->get_attributes('domain') || '';
509    my $dbzone = "\n; Comming from database:\n";
510    if ($ozone->get_attributes('net')) {
511        my $findhost = $self->_base->db->prepare_cached(q{
512            select * from (
513            select * from nethost join nethost_attributes_ips on
514            nethost.ikey = nethost_attributes_ips.okey
515            where value::inet <<= ? and exported = true
516            except
517            select * from nethost join nethost_attributes_ips on
518            nethost.ikey = nethost_attributes_ips.okey
519            where value::inet <<= any(?)
520            ) as q
521            order by value::inet
522
523        });
524        $findhost->execute(
525            $ozone->get_attributes('net'),
526            [ $ozone->get_attributes('netExclude') ],
527        ) or do {
528            la_log LA_ERR, "Cannot fetch host list: %s",
529                $self->_base->db->errstr;
530            return;
531        };
532
533        # reverse is complicated:
534        my ($mask) = ($ozone->get_attributes('net') =~ m:/(\d+)$:);
535
536        while (my $res = $findhost->fetchrow_hashref) {
537            my $host_o = $self->_base->get_object('nethost', $res->{name}) or do {
538                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
539                return;
540            };
541            my $desc = $self->_comment_nethost($host_o);
542            my $reverse = $host_o->get_attributes('reverse');
543            $dbzone .= $desc
544                ? '; ' . $desc . "\n"
545                : '';
546            my $revip;
547            my $fmt;
548            if ($res->{value} =~ /:/) {
549                # IPv6
550                my $m = $mask/4;
551                $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int;
552                $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i;
553                $fmt = "%-72s IN    PTR    %s%s\n";
554            } else {
555                # ipv4
556                my @ippart = split(/\./, $res->{value});
557                splice(@ippart, 0, $mask/8); # get rid of start of ip
558                my @nippart;
559                while (@ippart) { unshift(@nippart, shift(@ippart)) }
560                $revip = join('.', @nippart);
561                $fmt = "%-12s IN    PTR    %s%s\n";
562            }
563            $dbzone .= sprintf($fmt, $revip,
564                $reverse
565                    ? ($reverse, '.')
566                    : ($res->{name}, ($domain ? ".$domain." : '')));
567        }
568    }
569
570    $dbzone .= "; End of data from database\n";
571
572    if (!$self->_checkzone_output($ozone, $textzone . $dbzone)) {
573        la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
574            $ozone->id);
575        return;
576    }
577
578    if (open(my $handle, '>', $self->_output_file($ozone))) {
579        print $handle $textzone;
580        print $handle $dbzone;
581        close($handle);
582        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
583            $self->_output_file($ozone));
584    } else {
585       la_log(LA_ERR, "can't open output file %s (%s)",
586           $self->_output_file($ozone), $!);
587       return;
588   }
589   1;
590}
591
592sub _gen_dhcp_zone {
593    my ($self, $ozone, $output) = @_;
594
595    my $outzone = $ozone;
596
597    my @net;
598    if ($outzone->get_attributes('net')) {
599        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
600            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
601                join(', ', $outzone->get_attributes('net')));
602            next;
603        };
604    }
605
606    {
607        my $find = $self->_base->db->prepare(q{
608            select * from nethost where exported = true and ikey in(
609            select okey from nethost_attributes where attr = 'macaddr'
610            intersect (
611                select nethost_attributes_ips.okey from nethost_attributes_ips join
612                netzone_attributes
613                on netzone_attributes.attr = 'net' and
614                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
615                join netzone on netzone.ikey = netzone_attributes.okey
616                where netzone.name = $1
617
618                except
619                select nethost_attributes_ips.okey from nethost_attributes_ips join
620                netzone_attributes
621                on netzone_attributes.attr = 'netExclude' and
622                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
623                join netzone on netzone.ikey = netzone_attributes.okey
624                where netzone.name = $1
625                )
626            )
627            order by name
628
629            });
630        $find->execute($ozone->id) or do {
631            la_log LA_ERR, "Cannot fetch host list: %s",
632                $self->_base->db->errstr;
633            return;
634        };
635        while (my $res = $find->fetchrow_hashref) {
636            my $nethost = $res->{name};
637
638            my $obj = $self->_base->get_object('nethost', $nethost) or do {
639                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
640                return;
641            };
642
643            my $retainip;
644            if (@net) {
645                foreach my $inet (@net) {
646                    ($retainip) = grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip')
647                        and last;
648                }
649            }
650
651            $obj->get_attributes('noDynamic') && !$retainip and next;
652
653            my $desc = $self->_comment_nethost($obj);
654            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
655                $output .= $desc
656                ? '# ' . $desc . "\n"
657                : '';
658                my $fmac = $mac;
659                $fmac =~ s/://g;
660                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
661                $output .= sprintf("    hardware ethernet %s;\n", $mac);
662                $output .= sprintf("    fixed-address %s;\n", $retainip)
663                if ($retainip);
664                $output .= "}\n\n";
665            }
666        }
667    }
668    if ($ozone->get_attributes('allow_dyn')) {
669        $output .= "\n# Host without IP:\n";
670        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
671        my $find = $self->_base->db->prepare(q{
672            select * from nethost where exported = true and ikey in(
673            select okey from nethost_attributes where attr = 'macaddr'
674            } . (@dynfrom ? q{
675            intersect
676            (
677                select ikey from nethost where ikey not in
678                    (select okey from nethost_attributes_ips)
679                union
680
681                (
682                select nethost_attributes_ips.okey from nethost_attributes_ips join
683                netzone_attributes
684                on netzone_attributes.attr = 'net' and
685                   netzone_attributes.value::inet >>=
686                   nethost_attributes_ips.value::inet
687                   join netzone on netzone.ikey = netzone_attributes.okey
688                   where netzone.name = any(?)
689                except
690                select nethost_attributes_ips.okey from nethost_attributes_ips join
691                netzone_attributes
692                on netzone_attributes.attr = 'netExclude' and
693                   netzone_attributes.value::inet >>=
694                   nethost_attributes_ips.value::inet
695                   join netzone on netzone.ikey = netzone_attributes.okey
696                   where netzone.name = any(?)
697                )
698            )} : '') . q{
699            except
700            select nethost_attributes_ips.okey from nethost_attributes_ips join
701            netzone_attributes
702            on netzone_attributes.attr = 'net' and
703            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
704            join netzone on netzone.ikey = netzone_attributes.okey
705            where netzone.name = ?
706            )
707            order by name
708
709            });
710        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
711            la_log LA_ERR, "Cannot fetch host list: %s",
712                $self->_base->db->errstr;
713            return;
714        };
715        while (my $res = $find->fetchrow_hashref) {
716            my $nethost = $res->{name};
717
718            my $obj = $self->_base->get_object('nethost', $nethost);
719
720            $obj->get_attributes('noDynamic') and next;
721
722            my $desc = $self->_comment_nethost($obj);
723            foreach my $mac (grep { $_ } $obj->get_attributes('macaddr')) {
724                $output .= $desc
725                ? '# ' . $desc . "\n"
726                : '';
727                my $fmac = $mac;
728                $fmac =~ s/://g;
729                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
730                $output .= sprintf("    hardware ethernet %s;\n", $mac);
731                $output .= "}\n\n";
732            }
733        }
734    }
735
736    $output .= "# End of data from database\n";
737    if (open(my $handle, '>', $self->_output_file($outzone))) {
738        print $handle $output;
739        close($handle);
740        la_log(LA_INFO, "zone %s written into %s", $outzone->id,
741            $self->_output_file($outzone));
742    } else {
743        la_log(LA_ERR, "Can't open output file for dhcp zone %s (%s)",
744            $outzone->id, $!);
745        return;
746    }
747    1;
748}
749
750sub _gen_puppet_zone {
751    my ($self, $ozone, $output) = @_;
752
753    if ($ozone->get_attributes('net')) {
754        my $findhost = $self->_base->db->prepare_cached(q{
755            select * from (
756            select * from nethost join nethost_attributes_ips on
757            nethost.ikey = nethost_attributes_ips.okey
758            where value::inet <<= ? and exported = true
759            except
760            select * from nethost join nethost_attributes_ips on
761            nethost.ikey = nethost_attributes_ips.okey
762            where value::inet <<= any(?)
763            ) as q
764            order by value::inet
765
766        });
767        $findhost->execute(
768            $ozone->get_attributes('net'),
769            [ $ozone->get_attributes('netExclude') ],
770        ) or do {
771            la_log LA_ERR, "Cannot fetch host list: %s",
772                $self->_base->db->errstr;
773            return;
774        };
775
776        my @puppetclasses = $ozone->get_attributes('puppetClass');
777        while (my $res = $findhost->fetchrow_hashref) {
778            my $nethost = $res->{name};
779
780            my $obj = $self->_base->get_object('nethost', $nethost) or do {
781                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
782                return;
783            };
784
785            # merging classes from host and zone
786            my %classes = map { $_ => 1 } grep { $_ } (
787                $obj->get_attributes('puppetClass'),
788                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
789            );
790            my $desc = $self->_comment_nethost($obj);
791            $output .= sprintf("node '%s' {\n", $obj->id);
792            $output .= $desc
793                ? sprintf("    # %s\n", $desc)
794                : '';
795            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
796            $output .= "}\n\n";
797        }
798    }
799
800    $output .= "# End of data from database\n";
801    my $file = $self->_output_file($ozone);
802    $file =~ s/(\.pp)?$/\.pp/; # adding .pp extension if not existing
803    if (open(my $handle, '>', $file)) {
804        print $handle $output;
805        close($handle);
806        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
807            $self->_output_file($ozone));
808    } else {
809        la_log(LA_ERR, "Can't open output file for puppet zone %s (%s)",
810            $ozone->id, $!);
811        return;
812    }
813    1;
814}
815
816sub reset_savepoint {
817    my ($self) = @_;
818    foreach my $zone ($self->_base->search_objects('netzone')) {
819        my $ozone = $self->_base->get_object('netzone', $zone)
820            or next;
821
822        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
823        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
824        $self->_bnet_state->SetParameterComment(
825            $ozone->id, 'dbrev',
826            'Reset savepoint the ' . scalar(localtime));
827    }
828
829    $self->_write_state_file;
830}
831
8321;
833
834__END__
835
836=head1 AUTHOR
837
838Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
839
840=head1 COPYRIGHT AND LICENSE
841
842Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
843
844This library is free software; you can redistribute it and/or modify
845it under the same terms as Perl itself, either Perl version 5.10.0 or,
846at your option, any later version of Perl 5 you may have available.
847
848=cut
Note: See TracBrowser for help on using the repository browser.