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

Last change on this file since 2112 was 2112, checked in by nanardon, 7 years ago

Log de duration for building netzone files

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