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

Last change on this file since 2286 was 2186, checked in by nanardon, 5 years ago

Fix function name

File size: 33.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 $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 = (
434        $displayeduser,
435        $nethost->get_attributes('site'),
436        $nethost->get_attributes('physicalDeliveryOfficeName'),
437        $nethost->get_attributes('description'),
438    );
439
440    return to_ascii(join(', ', grep { $_ } @desc) || '');
441}
442
443
444# return undef on fatal error, depending zone type
445sub _read_template {
446    my ($self, $ozone) = @_;
447
448    my $revision = $self->get_zone_rev($ozone) or return;
449    if (open(my $handle, '<', $self->_template_file($ozone))) {
450        my $textzone = '';
451        while (my $line = <$handle>) {
452            $line =~ s/(\d+\s*;\s*)?\@REVISION@/$revision/;
453            $textzone .= $line;
454        }
455        close($handle);
456        return $textzone;
457    } else {
458        if ($ozone->get_attributes('type') =~ /^(dns|reverse)$/) {
459            la_log(LA_ERR, "Can't open template file for zone %s: %s", $ozone->id, $!);
460            return;
461        } else {
462            return '';
463        }
464    }
465}
466
467sub _gen_dns_zone {
468    my ($self, $ozone) = @_;
469
470    my $dbzone = '';
471    if ($ozone->get_attributes('net')) {
472        my $findhost = $self->_base->db->prepare_cached(q{
473            select name, value::inet as value from nethost join nethost_attributes_ips on
474            nethost.ikey = nethost_attributes_ips.okey
475            where value::inet <<= any(?) and exported = true
476            except
477            select name, value::inet from nethost join nethost_attributes_ips on
478            nethost.ikey = nethost_attributes_ips.okey
479            where value::inet <<= any(?)
480            order by value, name
481        });
482        $findhost->execute(
483            [ $ozone->get_attributes('net') ],
484            [ $ozone->get_attributes('netExclude') ],
485        ) or do {
486            la_log LA_ERR, "Cannot fetch host list: %s",
487                $self->_base->db->errstr;
488            return;
489        };
490        my %lists;
491        my %names;
492        # Storing all name in %names to check later if CNAME does not conflict
493        while (my $res = $findhost->fetchrow_hashref) {
494            $lists{$res->{name}} ||= {};
495            push(@{$lists{$res->{name}}{ip}}, $res->{value});
496            my $host_o = $self->_base->get_object('nethost', $res->{name});
497            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
498                $names{$_} = 1;
499            }
500        }
501
502        foreach my $res (sort keys %lists) {
503            my $host_o = $self->_base->get_object('nethost', $res) or do {
504                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
505                return;
506            };
507            my $desc = $self->_comment_nethost($host_o);
508            $dbzone .= $desc
509                ? '; ' . $desc . "\n"
510                : '';
511            foreach my $ip (@{$lists{$res}{ip}}) {
512                $dbzone .= sprintf(
513                    "%-30s IN    %-4s     %s\n",
514                    $res,
515                    ($ip =~ /:/ ? 'AAAA' : 'A'),
516                    $ip
517                );
518            }
519            foreach (grep { $_ } $host_o->get_attributes('otherName')) {
520                foreach my $ip (@{$lists{$res}{ip}}) {
521                    $dbzone .= sprintf(
522                        "%-30s IN    %-4s     %s\n",
523                        $_,
524                        ($ip =~ /:/ ? 'AAAA' : 'A'),
525                        $ip
526                    );
527                }
528            }
529            foreach (grep { $_ } $host_o->get_attributes('cname')) {
530                # It is deny to have both:
531                # foo IN A
532                # foo IN CNAME
533                if ($names{$_}) {
534                    my $msg .= sprintf(
535                        'Cname %s to %s exclude because %s is already an A record',
536                        $_, $res, $_
537                    );
538                    la_log(LA_ERR, sprintf("$msg (zone %s)", $ozone->id));
539                    $dbzone .= "; $msg\n";
540                } else {
541                    $dbzone .= sprintf("%-30s IN    CNAME    %s\n", $_, $res,);
542                }
543            }
544            # SSH finger print
545            foreach my $name (grep { $_ } ($res, $host_o->get_attributes('otherName'))) {
546                foreach my $sshfp (grep { $_ } $host_o->get_attributes('sshfp')) {
547                    $dbzone .= sprintf(
548                        "%-30s IN    SSHFP    %s\n",
549                        $name,
550                        $sshfp,
551                    );
552                }
553            }
554        }
555    }
556
557    return $dbzone;
558}
559
560sub _gen_reverse_zone {
561    my ($self, $ozone) = @_;
562
563    my $domain = $ozone->get_attributes('domain') || '';
564    my $dbzone = '';
565    if ($ozone->get_attributes('net')) {
566        my $findhost = $self->_base->db->prepare_cached(q{
567            select * from (
568            select * from nethost join nethost_attributes_ips on
569            nethost.ikey = nethost_attributes_ips.okey
570            where value::inet <<= ? and exported = true
571            except
572            select * from nethost join nethost_attributes_ips on
573            nethost.ikey = nethost_attributes_ips.okey
574            where value::inet <<= any(?)
575            ) as q
576            order by value::inet
577
578        });
579        $findhost->execute(
580            $ozone->get_attributes('net'),
581            [ $ozone->get_attributes('netExclude') ],
582        ) or do {
583            la_log LA_ERR, "Cannot fetch host list: %s",
584                $self->_base->db->errstr;
585            return;
586        };
587
588        # reverse is complicated:
589        my ($net) = $ozone->get_attributes('net') or do {
590        $self->base->log('Cannot fetch attribute "net" for zone %s', $ozone->id);
591        return;
592    };
593        my $netip = Net::IP->new($net) or do {
594                $self->base->log(LA_ERR, "Cannot build reverse zone %s: wrong net %s", $ozone->id, $net);
595                return;
596        };
597        my $mask = $netip->prefixlen;
598
599        while (my $res = $findhost->fetchrow_hashref) {
600            my $host_o = $self->_base->get_object('nethost', $res->{name}) or do {
601                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
602                return;
603            };
604            my $desc = $self->_comment_nethost($host_o);
605            my $reverse = $host_o->get_attributes('reverse') || ($res->{name} . '.');
606            $dbzone .= $desc
607                ? '; ' . $desc . "\n"
608                : '';
609            my $revip;
610            my $fmt;
611            if ($res->{value} =~ /:/) {
612                # IPv6
613                my $m = $mask/4;
614                $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int;
615                $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i;
616                $fmt = "%-72s IN    PTR    %s%s\n";
617            } else {
618                # ipv4
619                if ($mask > 24) {
620                    $self->_base->log(LA_ERR, 'Mask for zone %s cannot be %d', $ozone->id, $mask);
621                    return;
622                }
623                my @ippart = split(/\./, $res->{value});
624                splice(@ippart, 0, $mask/8); # get rid of start of ip
625                my @nippart;
626                while (@ippart) { unshift(@nippart, shift(@ippart)) }
627                $revip = join('.', @nippart);
628                $fmt = "%-12s IN    PTR    %s%s\n";
629            }
630 
631            $dbzone .= sprintf($fmt, $revip,
632                $reverse =~ /\.$/
633                    ? ($reverse, ($domain ? "$domain." : '.'))
634                    : ($reverse, '.'));
635        }
636    }
637
638    return $dbzone;
639}
640
641sub _gen_dhcp_zone {
642    my ($self, $ozone) = @_;
643
644    my $outzone = $ozone;
645    my $output = '';
646
647    my @net;
648    if ($outzone->get_attributes('net')) {
649        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
650            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
651                join(', ', $outzone->get_attributes('net')));
652            next;
653        };
654    }
655
656    {
657        my $find = $self->_base->db->prepare(q{
658            select * from nethost where exported = true and ikey in(
659            select okey from nethost_attributes where attr = 'macaddr'
660            intersect (
661                select nethost_attributes_ips.okey from nethost_attributes_ips join
662                netzone_attributes
663                on netzone_attributes.attr = 'net' and
664                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
665                join netzone on netzone.ikey = netzone_attributes.okey
666                where netzone.name = $1
667
668                except
669                select nethost_attributes_ips.okey from nethost_attributes_ips join
670                netzone_attributes
671                on netzone_attributes.attr = 'netExclude' and
672                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
673                join netzone on netzone.ikey = netzone_attributes.okey
674                where netzone.name = $1
675                )
676            )
677            order by name
678
679            });
680        $find->execute($ozone->id) or do {
681            la_log LA_ERR, "Cannot fetch host list: %s",
682                $self->_base->db->errstr;
683            return;
684        };
685        while (my $res = $find->fetchrow_hashref) {
686            my $nethost = $res->{name};
687
688            my $obj = $self->_base->get_object('nethost', $nethost) or do {
689                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
690                return;
691            };
692
693            my @retainip;
694            if (@net) {
695                foreach my $inet (@net) {
696                    push(@retainip, grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip'));
697                }
698            }
699
700            $obj->get_attributes('noDynamic') && !@retainip and next;
701
702            my $desc = $self->_comment_nethost($obj);
703            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
704                my %done;
705                foreach my $retainip (sort @retainip) {
706                    $output .= $desc
707                       ? '# ' . $desc . "\n"
708                        : '';
709                    my ($fixedparam, $label, $netLockIp) = $retainip =~ /^\d+(\.\d+){3}$/
710                        ? ('fixed-address', '', '127.0.0.1')
711                        : ('fixed-address6', '-6', '::1');
712                    # If two ip are given for same protocol keep only first
713                    $done{$fixedparam} and next;
714                    $done{$fixedparam} = 1;
715                    my $fmac = $mac;
716                    $fmac =~ s/://g;
717                    $output .= sprintf("host %s-%s%s {\n", $nethost, lc($fmac), $label);
718                    $output .= sprintf("    hardware ethernet %s;\n", $mac);
719                    if ($obj->get_attributes('netLocked')) {
720                        $output .= sprintf("    $fixedparam %s;\n", $netLockIp);
721                    } else {
722                        $output .= sprintf("    $fixedparam %s;\n", $retainip)
723                            if ($retainip);
724                    }
725                    $output .= "}\n\n";
726                }
727            }
728        }
729    }
730    if ($ozone->get_attributes('allow_dyn')) {
731        $output .= "\n# Host without IP:\n";
732        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
733        my $find = $self->_base->db->prepare(q{
734            select * from nethost where exported = true and ikey in(
735            select okey from nethost_attributes where attr = 'macaddr'
736            } . (@dynfrom ? q{
737            intersect
738            (
739                select ikey from nethost where ikey not in
740                    (select okey from nethost_attributes_ips)
741                union
742
743                (
744                select nethost_attributes_ips.okey from nethost_attributes_ips join
745                netzone_attributes
746                on netzone_attributes.attr = 'net' and
747                   netzone_attributes.value::inet >>=
748                   nethost_attributes_ips.value::inet
749                   join netzone on netzone.ikey = netzone_attributes.okey
750                   where netzone.name = any(?)
751                except
752                select nethost_attributes_ips.okey from nethost_attributes_ips join
753                netzone_attributes
754                on netzone_attributes.attr = 'netExclude' and
755                   netzone_attributes.value::inet >>=
756                   nethost_attributes_ips.value::inet
757                   join netzone on netzone.ikey = netzone_attributes.okey
758                   where netzone.name = any(?)
759                )
760            )} : '') . q{
761            except
762            select nethost_attributes_ips.okey from nethost_attributes_ips join
763            netzone_attributes
764            on netzone_attributes.attr = 'net' and
765            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
766            join netzone on netzone.ikey = netzone_attributes.okey
767            where netzone.name = ?
768            )
769            order by name
770
771            });
772        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
773            la_log LA_ERR, "Cannot fetch host list: %s",
774                $self->_base->db->errstr;
775            return;
776        };
777        while (my $res = $find->fetchrow_hashref) {
778            my $nethost = $res->{name};
779
780            my $obj = $self->_base->get_object('nethost', $nethost);
781
782            $obj->get_attributes('noDynamic') and next;
783
784            my $desc = $self->_comment_nethost($obj);
785            foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
786                $output .= $desc
787                    ? '# ' . $desc . "\n"
788                    : '';
789                my $fmac = $mac;
790                $fmac =~ s/://g;
791                if ($obj->get_attributes('netLocked')) {
792                    my ($ipv4, $ipv6);
793                    foreach ($ozone->get_attributes('net')) {
794                        if ($_ =~ /^\d+(\.\d+){3}\/(\d+)$/) {
795                            $ipv4 = 1;
796                        } else {
797                            $ipv6 = 1;
798                        }
799                    }
800
801                    if ($ipv4) {
802                        $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
803                        $output .= sprintf("    hardware ethernet %s;\n", $mac);
804                        $output .= sprintf("    fixed-address 127.0.0.1;\n");
805                        $output .= "}\n\n";
806                    }
807                    if ($ipv6) {
808                        $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
809                        $output .= sprintf("    hardware ethernet %s;\n", $mac);
810                        $output .= sprintf("    fixed-address6 ::1\n");
811                        $output .= "}\n\n";
812                    }
813                } else {
814                    $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac));
815                    $output .= sprintf("    hardware ethernet %s;\n", $mac);
816                    $output .= "}\n\n";
817                }
818            }
819        }
820    }
821
822    $output
823}
824
825sub _gen_radius_zone {
826    my ($self, $ozone) = @_;
827
828    my $outzone = $ozone;
829    my $output = '';
830
831    my @net;
832    if ($outzone->get_attributes('net')) {
833        @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do {
834            la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id,
835                join(', ', $outzone->get_attributes('net')));
836            next;
837        };
838    }
839
840    my %hosts;
841
842    {
843        my $find = $self->_base->db->prepare(q{
844            select * from nethost where exported = true and ikey in(
845            select okey from nethost_attributes where attr = 'macaddr'
846            intersect (
847                select nethost_attributes_ips.okey from nethost_attributes_ips join
848                netzone_attributes
849                on netzone_attributes.attr = 'net' and
850                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
851                join netzone on netzone.ikey = netzone_attributes.okey
852                where netzone.name = $1
853
854                except
855                select nethost_attributes_ips.okey from nethost_attributes_ips join
856                netzone_attributes
857                on netzone_attributes.attr = 'netExclude' and
858                netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
859                join netzone on netzone.ikey = netzone_attributes.okey
860                where netzone.name = $1
861                )
862            )
863            order by name
864
865            });
866        $find->execute($ozone->id) or do {
867            la_log LA_ERR, "Cannot fetch host list: %s",
868                $self->_base->db->errstr;
869            return;
870        };
871        while (my $res = $find->fetchrow_hashref) {
872            $hosts{ $res->{name} } = 1;
873        }
874    }
875    if ($ozone->get_attributes('allow_dyn')) {
876        my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom');
877        my $find = $self->_base->db->prepare(q{
878            select * from nethost where exported = true and ikey in(
879            select okey from nethost_attributes where attr = 'macaddr'
880            } . (@dynfrom ? q{
881            intersect
882            (
883                select ikey from nethost where ikey not in
884                    (select okey from nethost_attributes_ips)
885                union
886
887                (
888                select nethost_attributes_ips.okey from nethost_attributes_ips join
889                netzone_attributes
890                on netzone_attributes.attr = 'net' and
891                   netzone_attributes.value::inet >>=
892                   nethost_attributes_ips.value::inet
893                   join netzone on netzone.ikey = netzone_attributes.okey
894                   where netzone.name = any(?)
895                except
896                select nethost_attributes_ips.okey from nethost_attributes_ips join
897                netzone_attributes
898                on netzone_attributes.attr = 'netExclude' and
899                   netzone_attributes.value::inet >>=
900                   nethost_attributes_ips.value::inet
901                   join netzone on netzone.ikey = netzone_attributes.okey
902                   where netzone.name = any(?)
903                )
904            )} : '') . q{
905            except
906            select nethost_attributes_ips.okey from nethost_attributes_ips join
907            netzone_attributes
908            on netzone_attributes.attr = 'net' and
909            netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet
910            join netzone on netzone.ikey = netzone_attributes.okey
911            where netzone.name = ?
912            )
913            order by name
914
915            });
916        $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do {
917            la_log LA_ERR, "Cannot fetch host list: %s",
918                $self->_base->db->errstr;
919            return;
920        };
921        while (my $res = $find->fetchrow_hashref) {
922            $hosts{ $res->{name} } = 1;
923        }
924    }
925
926    my @parameters = grep { $_ } $outzone->get_attributes('hostParams');
927    foreach my $nethost (sort keys %hosts) {
928        my $obj = $self->_base->get_object('nethost', $nethost);
929
930        $obj->get_attributes('netLocked') and next;
931        $obj->get_attributes('noDynamic') and next;
932
933        $output .= "# Host: $nethost\n";
934        my $desc = $self->_comment_nethost($obj);
935        $output .= $desc
936            ? '# ' . $desc . "\n"
937            : '';
938        foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) {
939            my $fmac = $mac;
940            $fmac =~ s/://g;
941            $output .= sprintf("%s ClearText-Password := \"%s\"\n", lc($fmac), lc($fmac));
942            $output .= join (",\n", map { "    $_" } @parameters) . "\n" if(@parameters);
943            $output .= "\n";
944        }
945        $output .= "\n";
946    }
947
948    $output;
949}
950
951sub _gen_puppet_zone {
952    my ($self, $ozone) = @_;
953
954    my $output = '';
955
956    if ($ozone->get_attributes('net')) {
957        my $findhost = $self->_base->db->prepare_cached(q{
958            select * from (
959            select * from nethost join nethost_attributes_ips on
960            nethost.ikey = nethost_attributes_ips.okey
961            where value::inet <<= ? and exported = true
962            except
963            select * from nethost join nethost_attributes_ips on
964            nethost.ikey = nethost_attributes_ips.okey
965            where value::inet <<= any(?)
966            ) as q
967            order by value::inet
968
969        });
970        $findhost->execute(
971            $ozone->get_attributes('net'),
972            [ $ozone->get_attributes('netExclude') ],
973        ) or do {
974            la_log LA_ERR, "Cannot fetch host list: %s",
975                $self->_base->db->errstr;
976            return;
977        };
978
979        my @puppetclasses = $ozone->get_attributes('puppetClass');
980        while (my $res = $findhost->fetchrow_hashref) {
981            my $nethost = $res->{name};
982
983            my $obj = $self->_base->get_object('nethost', $nethost) or do {
984                la_log LA_ERR, "Cannot fetch host %s", $res->{name};
985                return;
986            };
987
988            # merging classes from host and zone
989            my %classes = map { $_ => 1 } grep { $_ } (
990                $obj->get_attributes('puppetClass'),
991                ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses),
992            );
993            my $desc = $self->_comment_nethost($obj);
994            $output .= sprintf("node '%s' {\n", $obj->id);
995            $output .= $desc
996                ? sprintf("    # %s\n", $desc)
997                : '';
998            $output .= join('', map { "    class '$_'\n" } sort keys %classes);
999            $output .= "}\n\n";
1000        }
1001    }
1002
1003    $output
1004}
1005
1006sub reset_savepoint {
1007    my ($self) = @_;
1008    foreach my $zone ($self->_base->search_objects('netzone')) {
1009        my $ozone = $self->_base->get_object('netzone', $zone)
1010            or next;
1011
1012        $self->_bnet_state->newval($ozone->id, 'dbrev', 0);
1013        la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id;
1014        $self->_bnet_state->SetParameterComment(
1015            $ozone->id, 'dbrev',
1016            'Reset savepoint the ' . scalar(localtime));
1017    }
1018
1019    $self->_write_state_file;
1020}
1021
10221;
1023
1024__END__
1025
1026=head1 AUTHOR
1027
1028Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
1029
1030=head1 COPYRIGHT AND LICENSE
1031
1032Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
1033
1034This library is free software; you can redistribute it and/or modify
1035it under the same terms as Perl itself, either Perl version 5.10.0 or,
1036at your option, any later version of Perl 5 you may have available.
1037
1038=cut
Note: See TracBrowser for help on using the repository browser.