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

Last change on this file since 1569 was 1529, checked in by nanardon, 9 years ago

Ensure Network task return true unless error

File size: 27.4 KB
RevLine 
[861]1package LATMOS::Accounts::Task::Buildnet;
2
3use strict;
4use warnings;
5use base qw(LATMOS::Accounts::Task);
[1180]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;
[861]15
[1023]16=head1 NAME
17
18LATMOS::Accounts::Task::Buildnet - Task to generate network configuration files
19
[1180]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
[1023]43=cut
44
[861]45# Always try because depend also on files:
46sub needupd {
47    return 1;
48}
49
[1180]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
[861]60sub run {
61    my ($self) = @_;
62
[1180]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    }
[861]74
[1180]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    }
[1529]105
106    1;
[861]107}
108
[1180]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
[1248]323    if (!$self->_la->val('_network_', 'checkzone')) {
[1225]324        return 1;
325    }
326
[1180]327    my ($fh, $filename) = tempfile();
328
329    print $fh $output;
330    close($fh);
331
[1249]332    my $named_checkzone = $self->_la->val('_network_', 'named-checkzone',
333        '/usr/sbin/named-checkzone');
334
[1180]335    my $msg;
336    my $res = exec_command(sprintf(
337            "%s -k fail '%s' '%s'",
[1249]338            $named_checkzone,
[1180]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 $ );
[1245]362    push @output, sprintf('          the %s', scalar(localtime) );
[1180]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
[1226]379    my $displayeduser;
[1180]380    if (my $owner = $nethost->get_attributes('user')) {
381        if (my $user = $self->_base->get_object('user', $owner)) {
[1226]382            $displayeduser = $user->get_attributes('displayName');
[1180]383        }
384    }
[1226]385    if ((!$displayeduser) && (my $owner = $nethost->get_attributes('owner'))) {
[1180]386        if (my $user = $self->_base->get_object('user', $owner)) {
[1226]387            $displayeduser = $user->get_attributes('displayName');
[1180]388        }
389    }
[1226]390    my @desc = ($displayeduser, $nethost->get_attributes('description'));
[1180]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',
[1218]482                        $_, $res, $_
[1180]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            }
[1227]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                }
[1225]499            }
[1180]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 ($mask) = ($ozone->get_attributes('net') =~ m:/(\d+)$:);
554
555        while (my $res = $findhost->fetchrow_hashref) {
556            my $host_o = $self->_base->get_object('nethost', $res->{name}) or do {
557                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
558                return;
559            };
560            my $desc = $self->_comment_nethost($host_o);
561            my $reverse = $host_o->get_attributes('reverse');
562            $dbzone .= $desc
563                ? '; ' . $desc . "\n"
564                : '';
565            my $revip;
566            my $fmt;
567            if ($res->{value} =~ /:/) {
568                # IPv6
569                my $m = $mask/4;
570                $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int;
571                $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i;
572                $fmt = "%-72s IN    PTR    %s%s\n";
573            } else {
574                # ipv4
575                my @ippart = split(/\./, $res->{value});
576                splice(@ippart, 0, $mask/8); # get rid of start of ip
577                my @nippart;
578                while (@ippart) { unshift(@nippart, shift(@ippart)) }
579                $revip = join('.', @nippart);
580                $fmt = "%-12s IN    PTR    %s%s\n";
581            }
582            $dbzone .= sprintf($fmt, $revip,
583                $reverse
584                    ? ($reverse, '.')
585                    : ($res->{name}, ($domain ? ".$domain." : '')));
586        }
587    }
588
589    $dbzone .= "; End of data from database\n";
590
591    if (!$self->_checkzone_output($ozone, $textzone . $dbzone)) {
592        la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
593            $ozone->id);
594        return;
595    }
596
597    if (open(my $handle, '>', $self->_output_file($ozone))) {
598        print $handle $textzone;
599        print $handle $dbzone;
600        close($handle);
601        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
602            $self->_output_file($ozone));
603    } else {
604       la_log(LA_ERR, "can't open output file %s (%s)",
605           $self->_output_file($ozone), $!);
606       return;
607   }
608   1;
609}
610
611sub _gen_dhcp_zone {
612    my ($self, $ozone, $output) = @_;
613
614    my $outzone = $ozone;
615
616    my @net;
617    if ($outzone->get_attributes('net')) {
618        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
619            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
620                join(', ', $outzone->get_attributes('net')));
621            next;
622        };
623    }
624
625    {
626        my $find = $self->_base->db->prepare(q{
627            select * from nethost where exported = true and ikey in(
628            select okey from nethost_attributes where attr = 'macaddr'
629            intersect (
630                select nethost_attributes_ips.okey from nethost_attributes_ips join
631                netzone_attributes
632                on netzone_attributes.attr = 'net' 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                except
638                select nethost_attributes_ips.okey from nethost_attributes_ips join
639                netzone_attributes
640                on netzone_attributes.attr = 'netExclude' and
641                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
642                join netzone on netzone.ikey = netzone_attributes.okey
643                where netzone.name = $1
644                )
645            )
646            order by name
647
648            });
649        $find->execute($ozone->id) or do {
650            la_log LA_ERR, "Cannot fetch host list: %s",
651                $self->_base->db->errstr;
652            return;
653        };
654        while (my $res = $find->fetchrow_hashref) {
655            my $nethost = $res->{name};
656
657            my $obj = $self->_base->get_object('nethost', $nethost) or do {
658                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
659                return;
660            };
661
662            my $retainip;
663            if (@net) {
664                foreach my $inet (@net) {
665                    ($retainip) = grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip')
666                        and last;
667                }
668            }
669
670            $obj->get_attributes('noDynamic') && !$retainip and next;
671
672            my $desc = $self->_comment_nethost($obj);
673            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
674                $output .= $desc
675                ? '# ' . $desc . "\n"
676                : '';
677                my $fmac = $mac;
678                $fmac =~ s/://g;
679                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
680                $output .= sprintf("    hardware ethernet %s;\n", $mac);
681                $output .= sprintf("    fixed-address %s;\n", $retainip)
682                if ($retainip);
683                $output .= "}\n\n";
684            }
685        }
686    }
687    if ($ozone->get_attributes('allow_dyn')) {
688        $output .= "\n# Host without IP:\n";
689        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
690        my $find = $self->_base->db->prepare(q{
691            select * from nethost where exported = true and ikey in(
692            select okey from nethost_attributes where attr = 'macaddr'
693            } . (@dynfrom ? q{
694            intersect
695            (
696                select ikey from nethost where ikey not in
697                    (select okey from nethost_attributes_ips)
698                union
699
700                (
701                select nethost_attributes_ips.okey from nethost_attributes_ips join
702                netzone_attributes
703                on netzone_attributes.attr = 'net' 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                except
709                select nethost_attributes_ips.okey from nethost_attributes_ips join
710                netzone_attributes
711                on netzone_attributes.attr = 'netExclude' and
712                   netzone_attributes.value::inet >>=
713                   nethost_attributes_ips.value::inet
714                   join netzone on netzone.ikey = netzone_attributes.okey
715                   where netzone.name = any(?)
716                )
717            )} : '') . q{
718            except
719            select nethost_attributes_ips.okey from nethost_attributes_ips join
720            netzone_attributes
721            on netzone_attributes.attr = 'net' and
722            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
723            join netzone on netzone.ikey = netzone_attributes.okey
724            where netzone.name = ?
725            )
726            order by name
727
728            });
729        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
730            la_log LA_ERR, "Cannot fetch host list: %s",
731                $self->_base->db->errstr;
732            return;
733        };
734        while (my $res = $find->fetchrow_hashref) {
735            my $nethost = $res->{name};
736
737            my $obj = $self->_base->get_object('nethost', $nethost);
738
739            $obj->get_attributes('noDynamic') and next;
740
741            my $desc = $self->_comment_nethost($obj);
742            foreach my $mac (grep { $_ } $obj->get_attributes('macaddr')) {
743                $output .= $desc
744                ? '# ' . $desc . "\n"
745                : '';
746                my $fmac = $mac;
747                $fmac =~ s/://g;
748                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
749                $output .= sprintf("    hardware ethernet %s;\n", $mac);
750                $output .= "}\n\n";
751            }
752        }
753    }
754
755    $output .= "# End of data from database\n";
756    if (open(my $handle, '>', $self->_output_file($outzone))) {
757        print $handle $output;
758        close($handle);
759        la_log(LA_INFO, "zone %s written into %s", $outzone->id,
760            $self->_output_file($outzone));
761    } else {
762        la_log(LA_ERR, "Can't open output file for dhcp zone %s (%s)",
763            $outzone->id, $!);
764        return;
765    }
766    1;
767}
768
769sub _gen_puppet_zone {
770    my ($self, $ozone, $output) = @_;
771
772    if ($ozone->get_attributes('net')) {
773        my $findhost = $self->_base->db->prepare_cached(q{
774            select * from (
775            select * from nethost join nethost_attributes_ips on
776            nethost.ikey = nethost_attributes_ips.okey
777            where value::inet <<= ? and exported = true
778            except
779            select * from nethost join nethost_attributes_ips on
780            nethost.ikey = nethost_attributes_ips.okey
781            where value::inet <<= any(?)
782            ) as q
783            order by value::inet
784
785        });
786        $findhost->execute(
787            $ozone->get_attributes('net'),
788            [ $ozone->get_attributes('netExclude') ],
789        ) or do {
790            la_log LA_ERR, "Cannot fetch host list: %s",
791                $self->_base->db->errstr;
792            return;
793        };
794
795        my @puppetclasses = $ozone->get_attributes('puppetClass');
796        while (my $res = $findhost->fetchrow_hashref) {
797            my $nethost = $res->{name};
798
799            my $obj = $self->_base->get_object('nethost', $nethost) or do {
800                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
801                return;
802            };
803
804            # merging classes from host and zone
805            my %classes = map { $_ => 1 } grep { $_ } (
806                $obj->get_attributes('puppetClass'),
807                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
808            );
809            my $desc = $self->_comment_nethost($obj);
810            $output .= sprintf("node '%s' {\n", $obj->id);
811            $output .= $desc
812                ? sprintf("    # %s\n", $desc)
813                : '';
814            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
815            $output .= "}\n\n";
816        }
817    }
818
819    $output .= "# End of data from database\n";
820    my $file = $self->_output_file($ozone);
821    if (open(my $handle, '>', $file)) {
822        print $handle $output;
823        close($handle);
824        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
825            $self->_output_file($ozone));
826    } else {
827        la_log(LA_ERR, "Can't open output file for puppet zone %s (%s)",
828            $ozone->id, $!);
829        return;
830    }
831    1;
832}
833
[1206]834sub reset_savepoint {
[1201]835    my ($self) = @_;
836    foreach my $zone ($self->_base->search_objects('netzone')) {
837        my $ozone = $self->_base->get_object('netzone', $zone)
838            or next;
839
840        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
[1206]841        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
[1201]842        $self->_bnet_state->SetParameterComment(
843            $ozone->id, 'dbrev',
[1206]844            'Reset savepoint the ' . scalar(localtime));
[1201]845    }
846
847    $self->_write_state_file;
848}
849
[861]8501;
[1023]851
852__END__
853
854=head1 AUTHOR
855
856Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
857
858=head1 COPYRIGHT AND LICENSE
859
860Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
861
862This library is free software; you can redistribute it and/or modify
863it under the same terms as Perl itself, either Perl version 5.10.0 or,
864at your option, any later version of Perl 5 you may have available.
865
866=cut
Note: See TracBrowser for help on using the repository browser.