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

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

Fix: dhcp zone output making fixed-address being repeat

File size: 32.1 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 $ozone = $self->_base->get_object('netzone', $zone)
270        or return;
271
272    la_log(LA_DEBUG, "Start building zone %s (%s)", $zone,
273        $ozone->get_attributes('type'));
274
275    my $header = $self->_pre_zone($ozone);
276
277    my $type = $ozone->get_attributes('type');
278    my $res =
279        $type eq 'dns'     ? $self->_gen_dns_zone($ozone, $header) :
280        $type eq 'reverse' ? $self->_gen_reverse_zone($ozone, $header) :
281        $type eq 'dhcp'    ? $self->_gen_dhcp_zone($ozone, $header) :
282        $type eq 'puppet'  ? $self->_gen_puppet_zone($ozone, $header) :
283        $type eq 'radius'  ? $self->_gen_radius_zone($ozone, $header) :
284        undef;
285
286    if (!defined($res)) {
287    }
288
289    my $textzone =
290        $header .
291        "\n" .
292        $self->_comment($ozone, "Comming from database:\n") .
293        $res .
294        $self->_comment($ozone, "End of data from database\n");
295
296
297    if ($type =~ /^(dns|reverse)$/) {
298        if (!$self->_checkzone_output($ozone, $textzone)) {
299            la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone",
300                $ozone->id);
301            return;
302        }
303    }
304
305    if (open(my $handle, '>', $self->_output_file($ozone))) {
306        print $handle $textzone;
307        close($handle);
308        la_log(LA_INFO, "zone %s written into %s", $ozone->id,
309            $self->_output_file($ozone));
310    } else {
311       la_log(LA_ERR, "Can't open output file for zone %s", $ozone->id);
312       return;
313    }
314
315    if (my $cmd = $self->_la->val('_network_', 'post_file',
316            $self->_la->val('_network_', 'post_zone'))) {
317        exec_command(
318            $cmd,
319            {
320                TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''),
321                OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''),
322                DIRECTORY => $self->_la->val('_network_', 'output_dir', ''),
323                TEMPLATE_FILE => $ozone->get_attributes('templateD'),
324                OUTPUT_FILE => $ozone->get_attributes('outputD'),
325                HOOK_TYPE => 'POSTFILE',
326            },
327        );
328    }
329
330    $self->_bnet_state->newval($ozone->id, 'dbrev',
331        $ozone->get_attributes('rev'));
332    la_log LA_DEBUG, "Zone rev build point is %d for %s",
333    $ozone->get_attributes('rev'),
334    $ozone->id;
335    $self->_bnet_state->SetParameterComment(
336        $ozone->id, 'dbrev',
337        scalar(localtime));
338    $self->_write_state_file;
339
340    1;
341}
342
343sub _checkzone_output {
344    my ($self, $ozone, $output) = @_;
345
346    if (!$self->_la->val('_network_', 'checkzone')) {
347        return 1;
348    }
349
350    my ($fh, $filename) = tempfile();
351
352    print $fh $output;
353    close($fh);
354
355    my $named_checkzone = $self->_la->val('_network_', 'named-checkzone',
356        '/usr/sbin/named-checkzone');
357
358    my $msg;
359    my $res = exec_command(sprintf(
360            "%s -k fail '%s' '%s'",
361            $named_checkzone,
362            $ozone->id,
363            $filename,
364        ), undef, $msg);
365    if (!$res) {
366        la_log(LA_ERR, "Error on zone %s: ", $ozone->id);
367        la_log(LA_ERR, "  msg: $_") foreach (split(/\n/, $msg));
368    } else {
369        unlink($filename);
370    }
371    $res
372}
373
374sub _comment {
375    my ($self, $ozone, $message, @args) = @_;
376    my $com_prefix =
377        $ozone->get_attributes('type') =~ /^(dhcp|puppet|radius)$/ ? '#' : ';';
378
379    if ($message) {
380        return(sprintf("$com_prefix $message", @args));
381    } else {
382        $com_prefix
383    }
384}
385
386sub _comment_zone {
387    my ($self, $ozone) = @_;
388
389    my @output = ();
390    my $com_prefix =
391        $ozone->get_attributes('type') =~ /^(dhcp|puppet|radius)$/ ? '# ' : '; ';
392    push @output, sprintf('Zone %s, type %s', $ozone->id,
393        $ozone->get_attributes('type'));
394    push @output, $ozone->get_attributes('description')
395        if ($ozone->get_attributes('description'));
396    push @output, sprintf('Generated by %s', q$Id: BuildNet.pm 6283 2011-05-20 10:16:51Z nanardon $ );
397    push @output, sprintf('          the %s', scalar(localtime) );
398    push @output, sprintf('Network: %s', join(', ', $ozone->get_attributes('net')))
399        if ($ozone->get_attributes('net'));
400    push @output, sprintf('Exclude Network: %s', join(', ',
401            $ozone->get_attributes('netExclude')))
402        if ($ozone->get_attributes('netExclude'));
403    if ($ozone->get_attributes('type') =~ /^(dhcp|radius)$/) {
404        my @dynFrom = grep { $_ } $ozone->get_attributes('dynFrom');
405        push(@output, sprintf('This zone include host from zone: %s', join(', ', sort @dynFrom)))
406            if (@dynFrom);
407        push(@output, 'This zone include dynamic IP address')
408            if ($ozone->get_attributes('allow_dyn'));
409    }
410
411    return to_ascii(join('', map { $com_prefix . $_ . "\n" } @output) . "\n");
412}
413
414sub _comment_nethost {
415    my ($self, $nethost) = @_;
416
417    my $displayeduser;
418    if (my $owner = $nethost->get_attributes('user')) {
419        if (my $user = $self->_base->get_object('user', $owner)) {
420            $displayeduser = $user->get_attributes('displayName');
421        }
422    }
423    if ((!$displayeduser) && (my $owner = $nethost->get_attributes('owner'))) {
424        if (my $user = $self->_base->get_object('user', $owner)) {
425            $displayeduser = $user->get_attributes('displayName');
426        }
427    }
428    my @desc = ($displayeduser, $nethost->get_attributes('description'));
429
430    return to_ascii(join(', ', grep { $_ } @desc) || '');
431}
432
433
434# return undef on fatal error, depending zone type
435sub _read_template {
436    my ($self, $ozone) = @_;
437
438    my $revision = $self->get_zone_rev($ozone) or return;
439    if (open(my $handle, '<', $self->_template_file($ozone))) {
440        my $textzone = '';
441        while (my $line = <$handle>) {
442            $line =~ s/(\d+\s*;\s*)?\@REVISION@/$revision/;
443            $textzone .= $line;
444        }
445        close($handle);
446        return $textzone;
447    } else {
448        if ($ozone->get_attributes('type') =~ /^(dns|reverse)$/) {
449            la_log(LA_ERR, "Can't open template file for zone %s: %s", $ozone->id, $!);
450            return;
451        } else {
452            return '';
453        }
454    }
455}
456
457sub _gen_dns_zone {
458    my ($self, $ozone) = @_;
459
460    my $dbzone = '';
461    if ($ozone->get_attributes('net')) {
462        my $findhost = $self->_base->db->prepare_cached(q{
463            select name, value::inet as value from nethost join nethost_attributes_ips on
464            nethost.ikey = nethost_attributes_ips.okey
465            where value::inet <<= any(?) and exported = true
466            except
467            select name, value::inet from nethost join nethost_attributes_ips on
468            nethost.ikey = nethost_attributes_ips.okey
469            where value::inet <<= any(?)
470            order by value, name
471        });
472        $findhost->execute(
473            [ $ozone->get_attributes('net') ],
474            [ $ozone->get_attributes('netExclude') ],
475        ) or do {
476            la_log LA_ERR, "Cannot fetch host list: %s",
477                $self->_base->db->errstr;
478            return;
479        };
480        my %lists;
481        my %names;
482        # Storing all name in %names to check later if CNAME does not conflict
483        while (my $res = $findhost->fetchrow_hashref) {
484            $lists{$res->{name}} ||= {};
485            push(@{$lists{$res->{name}}{ip}}, $res->{value});
486            my $host_o = $self->_base->get_object('nethost', $res->{name});
487            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
488                $names{$_} = 1;
489            }
490        }
491
492        foreach my $res (sort keys %lists) {
493            my $host_o = $self->_base->get_object('nethost', $res) or do {
494                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
495                return;
496            };
497            my $desc = $self->_comment_nethost($host_o);
498            $dbzone .= $desc
499                ? '; ' . $desc . "\n"
500                : '';
501            foreach my $ip (@{$lists{$res}{ip}}) {
502                $dbzone .= sprintf(
503                    "%-30s IN    %-4s     %s\n",
504                    $res,
505                    ($ip =~ /:/ ? 'AAAA' : 'A'),
506                    $ip
507                );
508            }
509            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
510                foreach my $ip (@{$lists{$res}{ip}}) {
511                    $dbzone .= sprintf(
512                        "%-30s IN    %-4s     %s\n",
513                        $_,
514                        ($ip =~ /:/ ? 'AAAA' : 'A'),
515                        $ip
516                    );
517                }
518            }
519            foreach (grep { $_ } $host_o->get_attributes('cname')) {
520                # It is deny to have both:
521                # foo IN A
522                # foo IN CNAME
523                if ($names{$_}) {
524                    my $msg .= sprintf(
525                        'Cname %s to %s exclude because %s is already an A record',
526                        $_, $res, $_
527                    );
528                    la_log(LA_ERR, sprintf("$msg (zone %s)", $ozone->id));
529                    $dbzone .= "; $msg\n";
530                } else {
531                    $dbzone .= sprintf("%-30s IN    CNAME    %s\n", $_, $res,);
532                }
533            }
534            # SSH finger print
535            foreach my $name (grep { $_ } ($res, $host_o->get_attributes('otherName'))) {
536                foreach my $sshfp (grep { $_ } $host_o->get_attributes('sshfp')) {
537                    $dbzone .= sprintf(
538                        "%-30s IN    SSHFP    %s\n",
539                        $name,
540                        $sshfp,
541                    );
542                }
543            }
544        }
545    }
546
547    return $dbzone;
548}
549
550sub _gen_reverse_zone {
551    my ($self, $ozone) = @_;
552
553    my $domain = $ozone->get_attributes('domain') || '';
554    my $dbzone = '';
555    if ($ozone->get_attributes('net')) {
556        my $findhost = $self->_base->db->prepare_cached(q{
557            select * from (
558            select * from nethost join nethost_attributes_ips on
559            nethost.ikey = nethost_attributes_ips.okey
560            where value::inet <<= ? and exported = true
561            except
562            select * from nethost join nethost_attributes_ips on
563            nethost.ikey = nethost_attributes_ips.okey
564            where value::inet <<= any(?)
565            ) as q
566            order by value::inet
567
568        });
569        $findhost->execute(
570            $ozone->get_attributes('net'),
571            [ $ozone->get_attributes('netExclude') ],
572        ) or do {
573            la_log LA_ERR, "Cannot fetch host list: %s",
574                $self->_base->db->errstr;
575            return;
576        };
577
578        # reverse is complicated:
579        my ($net) = $ozone->get_attributes('net') or do {
580        $self->base->log('Cannot fetch attribute "net" for zone %s', $ozone->id);
581        return;
582    };
583        my $netip = Net::IP->new($net) or do {
584                $self->base->log(LA_ERR, "Cannot build reverse zone %s: wrong net %s", $ozone->id, $net);
585                return;
586        };
587        my $mask = $netip->prefixlen;
588
589        while (my $res = $findhost->fetchrow_hashref) {
590            my $host_o = $self->_base->get_object('nethost', $res->{name}) or do {
591                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
592                return;
593            };
594            my $desc = $self->_comment_nethost($host_o);
595            my $reverse = $host_o->get_attributes('reverse') || ($res->{name} . '.');
596            $dbzone .= $desc
597                ? '; ' . $desc . "\n"
598                : '';
599            my $revip;
600            my $fmt;
601            if ($res->{value} =~ /:/) {
602                # IPv6
603                my $m = $mask/4;
604                $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int;
605                $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i;
606                $fmt = "%-72s IN    PTR    %s%s\n";
607            } else {
608                # ipv4
609                if ($mask > 24) {
610                    $self->base->log(LA_ERR, 'Mask for zone %s cannot be %d', $ozone->id, $mask);
611                    return;
612                }
613                my @ippart = split(/\./, $res->{value});
614                splice(@ippart, 0, $mask/8); # get rid of start of ip
615                my @nippart;
616                while (@ippart) { unshift(@nippart, shift(@ippart)) }
617                $revip = join('.', @nippart);
618                $fmt = "%-12s IN    PTR    %s%s\n";
619            }
620 
621            $dbzone .= sprintf($fmt, $revip,
622                $reverse =~ /\.$/
623                    ? ($reverse, ($domain ? "$domain." : '.'))
624                    : ($reverse, '.'));
625        }
626    }
627
628    return $dbzone;
629}
630
631sub _gen_dhcp_zone {
632    my ($self, $ozone) = @_;
633
634    my $outzone = $ozone;
635    my $output = '';
636
637    my @net;
638    if ($outzone->get_attributes('net')) {
639        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
640            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
641                join(', ', $outzone->get_attributes('net')));
642            next;
643        };
644    }
645
646    {
647        my $find = $self->_base->db->prepare(q{
648            select * from nethost where exported = true and ikey in(
649            select okey from nethost_attributes where attr = 'macaddr'
650            intersect (
651                select nethost_attributes_ips.okey from nethost_attributes_ips join
652                netzone_attributes
653                on netzone_attributes.attr = 'net' and
654                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
655                join netzone on netzone.ikey = netzone_attributes.okey
656                where netzone.name = $1
657
658                except
659                select nethost_attributes_ips.okey from nethost_attributes_ips join
660                netzone_attributes
661                on netzone_attributes.attr = 'netExclude' and
662                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
663                join netzone on netzone.ikey = netzone_attributes.okey
664                where netzone.name = $1
665                )
666            )
667            order by name
668
669            });
670        $find->execute($ozone->id) or do {
671            la_log LA_ERR, "Cannot fetch host list: %s",
672                $self->_base->db->errstr;
673            return;
674        };
675        while (my $res = $find->fetchrow_hashref) {
676            my $nethost = $res->{name};
677
678            my $obj = $self->_base->get_object('nethost', $nethost) or do {
679                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
680                return;
681            };
682
683            my @retainip;
684            if (@net) {
685                foreach my $inet (@net) {
686                    push(@retainip, grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip'));
687                }
688            }
689
690            $obj->get_attributes('noDynamic') && !@retainip and next;
691
692            my $desc = $self->_comment_nethost($obj);
693            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
694                my %done;
695                foreach my $retainip (sort @retainip) {
696                    $output .= $desc
697                       ? '# ' . $desc . "\n"
698                        : '';
699                    my ($fixedparam, $label) = $retainip =~ /^\d+(\.\d+){3}$/
700                        ? ('fixed-address', '')
701                        : ('fixed-address6', '-6');
702                    # If two ip are given for same protocol keep only first
703                    $done{$fixedparam} and next;
704                    $done{$fixedparam} = 1;
705                    my $fmac = $mac;
706                    $fmac =~ s/://g;
707                    $output .= sprintf("host %s-%s%s {\n", $nethost, lc($fmac), $label);
708                    $output .= sprintf("    hardware ethernet %s;\n", $mac);
709                    $output .= sprintf("    $fixedparam %s;\n", $retainip)
710                    if ($retainip);
711                    $output .= "}\n\n";
712                }
713            }
714        }
715    }
716    if ($ozone->get_attributes('allow_dyn')) {
717        $output .= "\n# Host without IP:\n";
718        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
719        my $find = $self->_base->db->prepare(q{
720            select * from nethost where exported = true and ikey in(
721            select okey from nethost_attributes where attr = 'macaddr'
722            } . (@dynfrom ? q{
723            intersect
724            (
725                select ikey from nethost where ikey not in
726                    (select okey from nethost_attributes_ips)
727                union
728
729                (
730                select nethost_attributes_ips.okey from nethost_attributes_ips join
731                netzone_attributes
732                on netzone_attributes.attr = 'net' and
733                   netzone_attributes.value::inet >>=
734                   nethost_attributes_ips.value::inet
735                   join netzone on netzone.ikey = netzone_attributes.okey
736                   where netzone.name = any(?)
737                except
738                select nethost_attributes_ips.okey from nethost_attributes_ips join
739                netzone_attributes
740                on netzone_attributes.attr = 'netExclude' and
741                   netzone_attributes.value::inet >>=
742                   nethost_attributes_ips.value::inet
743                   join netzone on netzone.ikey = netzone_attributes.okey
744                   where netzone.name = any(?)
745                )
746            )} : '') . q{
747            except
748            select nethost_attributes_ips.okey from nethost_attributes_ips join
749            netzone_attributes
750            on netzone_attributes.attr = 'net' and
751            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
752            join netzone on netzone.ikey = netzone_attributes.okey
753            where netzone.name = ?
754            )
755            order by name
756
757            });
758        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
759            la_log LA_ERR, "Cannot fetch host list: %s",
760                $self->_base->db->errstr;
761            return;
762        };
763        while (my $res = $find->fetchrow_hashref) {
764            my $nethost = $res->{name};
765
766            my $obj = $self->_base->get_object('nethost', $nethost);
767
768            $obj->get_attributes('noDynamic') and next;
769
770            my $desc = $self->_comment_nethost($obj);
771            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
772                $output .= $desc
773                ? '# ' . $desc . "\n"
774                : '';
775                my $fmac = $mac;
776                $fmac =~ s/://g;
777                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
778                $output .= sprintf("    hardware ethernet %s;\n", $mac);
779                $output .= "}\n\n";
780            }
781        }
782    }
783
784    $output
785}
786
787sub _gen_radius_zone {
788    my ($self, $ozone) = @_;
789
790    my $outzone = $ozone;
791    my $output = '';
792
793    my @net;
794    if ($outzone->get_attributes('net')) {
795        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
796            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
797                join(', ', $outzone->get_attributes('net')));
798            next;
799        };
800    }
801
802    my %hosts;
803
804    {
805        my $find = $self->_base->db->prepare(q{
806            select * from nethost where exported = true and ikey in(
807            select okey from nethost_attributes where attr = 'macaddr'
808            intersect (
809                select nethost_attributes_ips.okey from nethost_attributes_ips join
810                netzone_attributes
811                on netzone_attributes.attr = 'net' and
812                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
813                join netzone on netzone.ikey = netzone_attributes.okey
814                where netzone.name = $1
815
816                except
817                select nethost_attributes_ips.okey from nethost_attributes_ips join
818                netzone_attributes
819                on netzone_attributes.attr = 'netExclude' and
820                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
821                join netzone on netzone.ikey = netzone_attributes.okey
822                where netzone.name = $1
823                )
824            )
825            order by name
826
827            });
828        $find->execute($ozone->id) or do {
829            la_log LA_ERR, "Cannot fetch host list: %s",
830                $self->_base->db->errstr;
831            return;
832        };
833        while (my $res = $find->fetchrow_hashref) {
834            $hosts{ $res->{name} } = 1;
835        }
836    }
837    if ($ozone->get_attributes('allow_dyn')) {
838        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
839        my $find = $self->_base->db->prepare(q{
840            select * from nethost where exported = true and ikey in(
841            select okey from nethost_attributes where attr = 'macaddr'
842            } . (@dynfrom ? q{
843            intersect
844            (
845                select ikey from nethost where ikey not in
846                    (select okey from nethost_attributes_ips)
847                union
848
849                (
850                select nethost_attributes_ips.okey from nethost_attributes_ips join
851                netzone_attributes
852                on netzone_attributes.attr = 'net' and
853                   netzone_attributes.value::inet >>=
854                   nethost_attributes_ips.value::inet
855                   join netzone on netzone.ikey = netzone_attributes.okey
856                   where netzone.name = any(?)
857                except
858                select nethost_attributes_ips.okey from nethost_attributes_ips join
859                netzone_attributes
860                on netzone_attributes.attr = 'netExclude' and
861                   netzone_attributes.value::inet >>=
862                   nethost_attributes_ips.value::inet
863                   join netzone on netzone.ikey = netzone_attributes.okey
864                   where netzone.name = any(?)
865                )
866            )} : '') . q{
867            except
868            select nethost_attributes_ips.okey from nethost_attributes_ips join
869            netzone_attributes
870            on netzone_attributes.attr = 'net' and
871            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
872            join netzone on netzone.ikey = netzone_attributes.okey
873            where netzone.name = ?
874            )
875            order by name
876
877            });
878        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
879            la_log LA_ERR, "Cannot fetch host list: %s",
880                $self->_base->db->errstr;
881            return;
882        };
883        while (my $res = $find->fetchrow_hashref) {
884            $hosts{ $res->{name} } = 1;
885        }
886    }
887
888    my @parameters = grep { $_ } $outzone->get_attributes('hostParams');
889    foreach my $nethost (sort keys %hosts) {
890        my $obj = $self->_base->get_object('nethost', $nethost);
891
892        $obj->get_attributes('noDynamic') and next;
893
894        $output .= "# Host: $nethost\n";
895        my $desc = $self->_comment_nethost($obj);
896        $output .= $desc
897            ? '# ' . $desc . "\n"
898            : '';
899        foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
900            my $fmac = $mac;
901            $fmac =~ s/://g;
902            $output .= sprintf("%s Auth-Type := EAP, User-Password == \"%s\"\n", lc($fmac), lc($fmac));
903            $output .= join (",\n", map { "    $_" } @parameters) . "\n" if(@parameters);
904            $output .= "\n";
905        }
906        $output .= "\n";
907    }
908
909    $output;
910}
911
912sub _gen_puppet_zone {
913    my ($self, $ozone) = @_;
914
915    my $output = '';
916
917    if ($ozone->get_attributes('net')) {
918        my $findhost = $self->_base->db->prepare_cached(q{
919            select * from (
920            select * from nethost join nethost_attributes_ips on
921            nethost.ikey = nethost_attributes_ips.okey
922            where value::inet <<= ? and exported = true
923            except
924            select * from nethost join nethost_attributes_ips on
925            nethost.ikey = nethost_attributes_ips.okey
926            where value::inet <<= any(?)
927            ) as q
928            order by value::inet
929
930        });
931        $findhost->execute(
932            $ozone->get_attributes('net'),
933            [ $ozone->get_attributes('netExclude') ],
934        ) or do {
935            la_log LA_ERR, "Cannot fetch host list: %s",
936                $self->_base->db->errstr;
937            return;
938        };
939
940        my @puppetclasses = $ozone->get_attributes('puppetClass');
941        while (my $res = $findhost->fetchrow_hashref) {
942            my $nethost = $res->{name};
943
944            my $obj = $self->_base->get_object('nethost', $nethost) or do {
945                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
946                return;
947            };
948
949            # merging classes from host and zone
950            my %classes = map { $_ => 1 } grep { $_ } (
951                $obj->get_attributes('puppetClass'),
952                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
953            );
954            my $desc = $self->_comment_nethost($obj);
955            $output .= sprintf("node '%s' {\n", $obj->id);
956            $output .= $desc
957                ? sprintf("    # %s\n", $desc)
958                : '';
959            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
960            $output .= "}\n\n";
961        }
962    }
963
964    $output
965}
966
967sub reset_savepoint {
968    my ($self) = @_;
969    foreach my $zone ($self->_base->search_objects('netzone')) {
970        my $ozone = $self->_base->get_object('netzone', $zone)
971            or next;
972
973        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
974        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
975        $self->_bnet_state->SetParameterComment(
976            $ozone->id, 'dbrev',
977            'Reset savepoint the ' . scalar(localtime));
978    }
979
980    $self->_write_state_file;
981}
982
9831;
984
985__END__
986
987=head1 AUTHOR
988
989Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
990
991=head1 COPYRIGHT AND LICENSE
992
993Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
994
995This library is free software; you can redistribute it and/or modify
996it under the same terms as Perl itself, either Perl version 5.10.0 or,
997at your option, any later version of Perl 5 you may have available.
998
999=cut
Note: See TracBrowser for help on using the repository browser.