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

Last change on this file since 1180 was 1180, checked in by nanardon, 12 years ago

merge Buildnet into regular task module

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