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

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

Improve radius zone output

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