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

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

Add comment about zone Id

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