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

Last change on this file since 2337 was 2337, checked in by nanardon, 4 years ago

Forgotten encode()

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