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

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

allow to use optionnal template for zone type radius, dhcp, puppet, factorize code

File size: 31.7 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                    ($retainip) = grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip')
687                        and last;
688                }
689            }
690
691            $obj->get_attributes('noDynamic') && !$retainip and next;
692
693            my $desc = $self->_comment_nethost($obj);
694            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
695                $output .= $desc
696                ? '# ' . $desc . "\n"
697                : '';
698                my $fmac = $mac;
699                $fmac =~ s/://g;
700                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
701                $output .= sprintf("    hardware ethernet %s;\n", $mac);
702                $output .= sprintf("    fixed-address %s;\n", $retainip)
703                if ($retainip);
704                $output .= "}\n\n";
705            }
706        }
707    }
708    if ($ozone->get_attributes('allow_dyn')) {
709        $output .= "\n# Host without IP:\n";
710        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
711        my $find = $self->_base->db->prepare(q{
712            select * from nethost where exported = true and ikey in(
713            select okey from nethost_attributes where attr = 'macaddr'
714            } . (@dynfrom ? q{
715            intersect
716            (
717                select ikey from nethost where ikey not in
718                    (select okey from nethost_attributes_ips)
719                union
720
721                (
722                select nethost_attributes_ips.okey from nethost_attributes_ips join
723                netzone_attributes
724                on netzone_attributes.attr = 'net' and
725                   netzone_attributes.value::inet >>=
726                   nethost_attributes_ips.value::inet
727                   join netzone on netzone.ikey = netzone_attributes.okey
728                   where netzone.name = any(?)
729                except
730                select nethost_attributes_ips.okey from nethost_attributes_ips join
731                netzone_attributes
732                on netzone_attributes.attr = 'netExclude' 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                )
738            )} : '') . q{
739            except
740            select nethost_attributes_ips.okey from nethost_attributes_ips join
741            netzone_attributes
742            on netzone_attributes.attr = 'net' and
743            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
744            join netzone on netzone.ikey = netzone_attributes.okey
745            where netzone.name = ?
746            )
747            order by name
748
749            });
750        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
751            la_log LA_ERR, "Cannot fetch host list: %s",
752                $self->_base->db->errstr;
753            return;
754        };
755        while (my $res = $find->fetchrow_hashref) {
756            my $nethost = $res->{name};
757
758            my $obj = $self->_base->get_object('nethost', $nethost);
759
760            $obj->get_attributes('noDynamic') and next;
761
762            my $desc = $self->_comment_nethost($obj);
763            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
764                $output .= $desc
765                ? '# ' . $desc . "\n"
766                : '';
767                my $fmac = $mac;
768                $fmac =~ s/://g;
769                $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
770                $output .= sprintf("    hardware ethernet %s;\n", $mac);
771                $output .= "}\n\n";
772            }
773        }
774    }
775
776    $output
777}
778
779sub _gen_radius_zone {
780    my ($self, $ozone) = @_;
781
782    my $outzone = $ozone;
783    my $output = '';
784
785    my @net;
786    if ($outzone->get_attributes('net')) {
787        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
788            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
789                join(', ', $outzone->get_attributes('net')));
790            next;
791        };
792    }
793
794    my %hosts;
795
796    {
797        my $find = $self->_base->db->prepare(q{
798            select * from nethost where exported = true and ikey in(
799            select okey from nethost_attributes where attr = 'macaddr'
800            intersect (
801                select nethost_attributes_ips.okey from nethost_attributes_ips join
802                netzone_attributes
803                on netzone_attributes.attr = 'net' and
804                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
805                join netzone on netzone.ikey = netzone_attributes.okey
806                where netzone.name = $1
807
808                except
809                select nethost_attributes_ips.okey from nethost_attributes_ips join
810                netzone_attributes
811                on netzone_attributes.attr = 'netExclude' 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            )
817            order by name
818
819            });
820        $find->execute($ozone->id) or do {
821            la_log LA_ERR, "Cannot fetch host list: %s",
822                $self->_base->db->errstr;
823            return;
824        };
825        while (my $res = $find->fetchrow_hashref) {
826            $hosts{ $res->{name} } = 1;
827        }
828    }
829    if ($ozone->get_attributes('allow_dyn')) {
830        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
831        my $find = $self->_base->db->prepare(q{
832            select * from nethost where exported = true and ikey in(
833            select okey from nethost_attributes where attr = 'macaddr'
834            } . (@dynfrom ? q{
835            intersect
836            (
837                select ikey from nethost where ikey not in
838                    (select okey from nethost_attributes_ips)
839                union
840
841                (
842                select nethost_attributes_ips.okey from nethost_attributes_ips join
843                netzone_attributes
844                on netzone_attributes.attr = 'net' and
845                   netzone_attributes.value::inet >>=
846                   nethost_attributes_ips.value::inet
847                   join netzone on netzone.ikey = netzone_attributes.okey
848                   where netzone.name = any(?)
849                except
850                select nethost_attributes_ips.okey from nethost_attributes_ips join
851                netzone_attributes
852                on netzone_attributes.attr = 'netExclude' 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                )
858            )} : '') . q{
859            except
860            select nethost_attributes_ips.okey from nethost_attributes_ips join
861            netzone_attributes
862            on netzone_attributes.attr = 'net' and
863            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
864            join netzone on netzone.ikey = netzone_attributes.okey
865            where netzone.name = ?
866            )
867            order by name
868
869            });
870        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
871            la_log LA_ERR, "Cannot fetch host list: %s",
872                $self->_base->db->errstr;
873            return;
874        };
875        while (my $res = $find->fetchrow_hashref) {
876            $hosts{ $res->{name} } = 1;
877        }
878    }
879
880    my @parameters = grep { $_ } $outzone->get_attributes('hostParams');
881    foreach my $nethost (sort keys %hosts) {
882        my $obj = $self->_base->get_object('nethost', $nethost);
883
884        $obj->get_attributes('noDynamic') and next;
885
886        $output .= "# Host: $nethost\n";
887        my $desc = $self->_comment_nethost($obj);
888        $output .= $desc
889            ? '# ' . $desc . "\n"
890            : '';
891        foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
892            my $fmac = $mac;
893            $fmac =~ s/://g;
894            $output .= sprintf("%s Auth-Type := EAP, User-Password == \"%s\"\n", lc($fmac), lc($fmac));
895            $output .= join (",\n", map { "    $_" } @parameters) . "\n" if(@parameters);
896            $output .= "\n";
897        }
898        $output .= "\n";
899    }
900
901    $output;
902}
903
904sub _gen_puppet_zone {
905    my ($self, $ozone) = @_;
906
907    my $output = '';
908
909    if ($ozone->get_attributes('net')) {
910        my $findhost = $self->_base->db->prepare_cached(q{
911            select * from (
912            select * from nethost join nethost_attributes_ips on
913            nethost.ikey = nethost_attributes_ips.okey
914            where value::inet <<= ? and exported = true
915            except
916            select * from nethost join nethost_attributes_ips on
917            nethost.ikey = nethost_attributes_ips.okey
918            where value::inet <<= any(?)
919            ) as q
920            order by value::inet
921
922        });
923        $findhost->execute(
924            $ozone->get_attributes('net'),
925            [ $ozone->get_attributes('netExclude') ],
926        ) or do {
927            la_log LA_ERR, "Cannot fetch host list: %s",
928                $self->_base->db->errstr;
929            return;
930        };
931
932        my @puppetclasses = $ozone->get_attributes('puppetClass');
933        while (my $res = $findhost->fetchrow_hashref) {
934            my $nethost = $res->{name};
935
936            my $obj = $self->_base->get_object('nethost', $nethost) or do {
937                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
938                return;
939            };
940
941            # merging classes from host and zone
942            my %classes = map { $_ => 1 } grep { $_ } (
943                $obj->get_attributes('puppetClass'),
944                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
945            );
946            my $desc = $self->_comment_nethost($obj);
947            $output .= sprintf("node '%s' {\n", $obj->id);
948            $output .= $desc
949                ? sprintf("    # %s\n", $desc)
950                : '';
951            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
952            $output .= "}\n\n";
953        }
954    }
955
956    $output
957}
958
959sub reset_savepoint {
960    my ($self) = @_;
961    foreach my $zone ($self->_base->search_objects('netzone')) {
962        my $ozone = $self->_base->get_object('netzone', $zone)
963            or next;
964
965        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
966        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
967        $self->_bnet_state->SetParameterComment(
968            $ozone->id, 'dbrev',
969            'Reset savepoint the ' . scalar(localtime));
970    }
971
972    $self->_write_state_file;
973}
974
9751;
976
977__END__
978
979=head1 AUTHOR
980
981Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
982
983=head1 COPYRIGHT AND LICENSE
984
985Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
986
987This library is free software; you can redistribute it and/or modify
988it under the same terms as Perl itself, either Perl version 5.10.0 or,
989at your option, any later version of Perl 5 you may have available.
990
991=cut
Note: See TracBrowser for help on using the repository browser.