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

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

Add support for netzone radius

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