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

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

Add new line after 'deny booting'

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