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

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

Fix reverse not ending with dot

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