package LATMOS::Accounts::Task::Buildnet; use strict; use warnings; use base qw(LATMOS::Accounts::Task); use LATMOS::Accounts::Log; use LATMOS::Accounts::Utils; use FindBin qw($Bin); use POSIX qw(strftime); use Net::IP; use File::Path; use File::Temp qw(tempfile); use Net::IPv4Addr; use Net::IPv6Addr; =head1 NAME LATMOS::Accounts::Task::Buildnet - Task to generate network configuration files =head1 DESCRIPTION This contains functions to generate network config file from C and C object. These config file can be: =over 4 =item DNS zone files A standard DNS zone generated from a header and entries found in bases =item DNS reverse zone files A reverse DNS zone genarated from a header and entries found in bases =item A DHCP host list A file well formated host list to be included in dhcpd config file. =back =cut sub order { 2 } # Always try because depend also on files: sub runDelay { 10 * 60 } sub init { my ($self) = @_; my $LA = LATMOS::Accounts->new($self->{config}, noacl => 1); $self->{_base} = $LA->base(); $self->{_base} && $self->{_base} or die "Cannot load base"; $self->{_base}->type eq 'sql' or die "This module work only with SQL base type\n"; $self->{_la} = $LA; } sub run { my ($self) = @_; if (my $cmd = $self->_la->val('_network_', 'pre')) { exec_command( $cmd, { TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''), OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''), DIRECTORY => $self->_la->val('_network_', 'output_dir', ''), HOOK_TYPE => 'PRE', }, ); } foreach my $zone ($self->_base->search_objects('netzone', 'exported=*')) { my $ozone = $self->_base->get_object('netzone', $zone) or next; # check file need regeneration: $self->_check_zone_need_update($ozone) or do { la_log(LA_DEBUG, "No need to rebuild %s", $ozone->id); next; }; $self->gen_zone($zone) or do { $self->_base->rollback; return; }; $self->_base->commit; } if (my $cmd = $self->_la->val('_network_', 'post')) { exec_command( $cmd, { TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''), OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''), DIRECTORY => $self->_la->val('_network_', 'output_dir', ''), HOOK_TYPE => 'POST', }, ); } 1; } sub _la { $_[0]->{_la} } sub _base { my ($self) = @_; return $self->{_base} if ($self->{_base}); my $base = $self->SUPER::base; return $self->{_base} = $base } sub _bnet_state { my ($self) = @_; return $self->{_bnet_state} if ($self->{_bnet_state}); # where trace goes: my $state_file = $self->_la->val('_default_', 'state_dir', '/'); $state_file .= '/buildnet_state.ini'; la_log(LA_DEBUG, "Status file is %s", $state_file); if ($state_file && ! -w $state_file) { # don't exists, we have to create it open(my $handle, '>', $state_file) or do { la_log(LA_ERR, "Cannot open build net status file %s", $state_file); return; }; print $handle "[_default_]\n"; close($handle); } $self->{_bnet_state} = Config::IniFiles->new( -file => $state_file ); } sub _write_state_file { la_log(LA_DEBUG, "Writting status file"); $_[0]->_bnet_state->RewriteConfig; } sub _template_file { my ($self, $ozone) = @_; my $template = join('/', grep { $_ } $self->_la->val('_network_', 'template_dir'), $ozone->get_attributes('templateD')); la_log(LA_DEBUG, "Template for %s is %s", $ozone->id, $template); $template; } sub _output_file { my ($self, $ozone) = @_; my $path = join( '/', $self->_la->val('_network_', 'output_dir', ($self->_la->val('_default_', 'state_dir'), $ozone->get_attributes('type')) ) ); if (! -d $path) { la_log(LA_INFO, 'Creating directory %s', $path); mkpath($path) or return; } my $output = join('/', $path, $ozone->get_attributes('outputD')); la_log(LA_DEBUG, 'output file for %s is %s', $ozone->id, $output); $output; } =head2 get_zone_rev ($zone) Return next zone revision (DNS). This revision is formated from date + incremental serial number. If day change, serial start to 1. If serial goes goes over 99, head build from date is increment. The code ensure returned number is always highter that current one. =cut sub get_zone_rev { my ($self, $ozone) = @_; my $date = strftime('%Y%m%d01', localtime); my $oldrev = $ozone->get_attributes('zoneRevision') || 0; my $rev; if ($oldrev >= $date) { # same date, increment subrev $rev = $oldrev + 1; } else { # date has changed, subrev is 1 $rev = $date; } la_log(LA_DEBUG, 'new dns revision for %s is %s', $ozone->id, $rev); $ozone->set_c_fields(zoneRevision => $rev) or do { return; }; $rev } sub _check_zone_need_update { my ($self, $ozone) = @_; # If env var is set, do it anyway if ($ENV{LA_BNET_FORCE}) { return 1 } if ($ozone->get_attributes('rev') > $self->_bnet_state->val($ozone->id, 'dbrev', 0)) { return 1; } return 1 if (! -f $self->_output_file($ozone)); if ($ozone->get_attributes('templateD')) { my $template = $self->_template_file($ozone); my $output = $self->_output_file($ozone); my @tstat = stat($template) or return; my @ostat = stat($output); if (($ostat[9] || 0) <= ($tstat[9] || 0)) { return 1; } } return; } sub _set_last_build { my ($self, $ozone) = @_; my $lctime = scalar(localtime); la_log(LA_DEBUG, 'Update last build for zone %s (%s)', $ozone->id, $lctime); $ozone->set_c_fields('lastBuild' => $lctime); } sub _pre_zone { my ($self, $ozone) = @_; if (!$ozone->get_attributes('templateD')) { la_log(LA_ERR, "No template file for zone %s, aborting", $ozone->id); return; } my $textzone = $self->_comment_zone($ozone); my $tzone = $self->_read_template($ozone); if (defined($tzone)) { $textzone .= $tzone; } else { return; } $self->_set_last_build($ozone); return $textzone; } =head2 gen_zone($zone, $header) Generate zone file C<$zone> with header C<$header> =cut sub gen_zone { my ($self, $zone) = @_; my $ozone = $self->_base->get_object('netzone', $zone) or return; la_log(LA_DEBUG, "Start building zone %s (%s)", $zone, $ozone->get_attributes('type')); my $header = $self->_pre_zone($ozone); my $type = $ozone->get_attributes('type'); my $res = $type eq 'dns' ? $self->_gen_dns_zone($ozone, $header) : $type eq 'reverse' ? $self->_gen_reverse_zone($ozone, $header) : $type eq 'dhcp' ? $self->_gen_dhcp_zone($ozone, $header) : $type eq 'puppet' ? $self->_gen_puppet_zone($ozone, $header) : $type eq 'radius' ? $self->_gen_radius_zone($ozone, $header) : undef; if (!defined($res)) { } my $textzone = $header . "\n" . $self->_comment($ozone, "Comming from database:\n") . $res . $self->_comment($ozone, "End of data from database\n"); if ($type =~ /^(dns|reverse)$/) { if (!$self->_checkzone_output($ozone, $textzone)) { la_log(LA_ERR, "Output of DNS zone %s not ok, not updating this zone", $ozone->id); return; } } if (open(my $handle, '>', $self->_output_file($ozone))) { print $handle $textzone; close($handle); la_log(LA_INFO, "zone %s written into %s", $ozone->id, $self->_output_file($ozone)); } else { la_log(LA_ERR, "Can't open output file for zone %s", $ozone->id); return; } if (my $cmd = $self->_la->val('_network_', 'post_file', $self->_la->val('_network_', 'post_zone'))) { exec_command( $cmd, { TEMPLATE_DIR => $self->_la->val('_network_', 'template_dir', ''), OUTPUT_DIR => $self->_la->val('_network_', 'output_dir', ''), DIRECTORY => $self->_la->val('_network_', 'output_dir', ''), TEMPLATE_FILE => $ozone->get_attributes('templateD'), OUTPUT_FILE => $ozone->get_attributes('outputD'), HOOK_TYPE => 'POSTFILE', }, ); } $self->_bnet_state->newval($ozone->id, 'dbrev', $ozone->get_attributes('rev')); la_log LA_DEBUG, "Zone rev build point is %d for %s", $ozone->get_attributes('rev'), $ozone->id; $self->_bnet_state->SetParameterComment( $ozone->id, 'dbrev', scalar(localtime)); $self->_write_state_file; 1; } sub _checkzone_output { my ($self, $ozone, $output) = @_; if (!$self->_la->val('_network_', 'checkzone')) { return 1; } my ($fh, $filename) = tempfile(); print $fh $output; close($fh); my $named_checkzone = $self->_la->val('_network_', 'named-checkzone', '/usr/sbin/named-checkzone'); my $msg; my $res = exec_command(sprintf( "%s -k fail '%s' '%s'", $named_checkzone, $ozone->id, $filename, ), undef, $msg); if (!$res) { la_log(LA_ERR, "Error on zone %s: ", $ozone->id); la_log(LA_ERR, " msg: $_") foreach (split(/\n/, $msg)); } else { unlink($filename); } $res } sub _comment { my ($self, $ozone, $message, @args) = @_; my $com_prefix = $ozone->get_attributes('type') =~ /^(dhcp|puppet|radius)$/ ? '#' : ';'; if ($message) { return(sprintf("$com_prefix $message", @args)); } else { $com_prefix } } sub _comment_zone { my ($self, $ozone) = @_; my @output = (); my $com_prefix = $ozone->get_attributes('type') =~ /^(dhcp|puppet|radius)$/ ? '# ' : '; '; push @output, sprintf('Zone %s, type %s', $ozone->id, $ozone->get_attributes('type')); push @output, $ozone->get_attributes('description') if ($ozone->get_attributes('description')); push @output, sprintf('Generated by %s', q$Id: BuildNet.pm 6283 2011-05-20 10:16:51Z nanardon $ ); push @output, sprintf(' the %s', scalar(localtime) ); push @output, sprintf('Network: %s', join(', ', $ozone->get_attributes('net'))) if ($ozone->get_attributes('net')); push @output, sprintf('Exclude Network: %s', join(', ', $ozone->get_attributes('netExclude'))) if ($ozone->get_attributes('netExclude')); if ($ozone->get_attributes('type') =~ /^(dhcp|radius)$/) { my @dynFrom = grep { $_ } $ozone->get_attributes('dynFrom'); push(@output, sprintf('This zone include host from zone: %s', join(', ', sort @dynFrom))) if (@dynFrom); push(@output, 'This zone include dynamic IP address') if ($ozone->get_attributes('allow_dyn')); } return to_ascii(join('', map { $com_prefix . $_ . "\n" } @output) . "\n"); } sub _comment_nethost { my ($self, $nethost) = @_; my $displayeduser; if (my $owner = $nethost->get_attributes('user')) { if (my $user = $self->_base->get_object('user', $owner)) { $displayeduser = $user->get_attributes('displayName'); } } if ((!$displayeduser) && (my $owner = $nethost->get_attributes('owner'))) { if (my $user = $self->_base->get_object('user', $owner)) { $displayeduser = $user->get_attributes('displayName'); } } my @desc = ($displayeduser, $nethost->get_attributes('description')); return to_ascii(join(', ', grep { $_ } @desc) || ''); } # return undef on fatal error, depending zone type sub _read_template { my ($self, $ozone) = @_; my $revision = $self->get_zone_rev($ozone) or return; if (open(my $handle, '<', $self->_template_file($ozone))) { my $textzone = ''; while (my $line = <$handle>) { $line =~ s/(\d+\s*;\s*)?\@REVISION@/$revision/; $textzone .= $line; } close($handle); return $textzone; } else { if ($ozone->get_attributes('type') =~ /^(dns|reverse)$/) { la_log(LA_ERR, "Can't open template file for zone %s: %s", $ozone->id, $!); return; } else { return ''; } } } sub _gen_dns_zone { my ($self, $ozone) = @_; my $dbzone = ''; if ($ozone->get_attributes('net')) { my $findhost = $self->_base->db->prepare_cached(q{ select name, value::inet as value from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= any(?) and exported = true except select name, value::inet from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= any(?) order by value, name }); $findhost->execute( [ $ozone->get_attributes('net') ], [ $ozone->get_attributes('netExclude') ], ) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; my %lists; my %names; # Storing all name in %names to check later if CNAME does not conflict while (my $res = $findhost->fetchrow_hashref) { $lists{$res->{name}} ||= {}; push(@{$lists{$res->{name}}{ip}}, $res->{value}); my $host_o = $self->_base->get_object('nethost', $res->{name}); foreach (grep { $_ } $host_o->get_attributes('otherName')) { $names{$_} = 1; } } foreach my $res (sort keys %lists) { my $host_o = $self->_base->get_object('nethost', $res) or do { la_log LA_ERR, "Cannot fetch host %s", $res->{name}; return; }; my $desc = $self->_comment_nethost($host_o); $dbzone .= $desc ? '; ' . $desc . "\n" : ''; foreach my $ip (@{$lists{$res}{ip}}) { $dbzone .= sprintf( "%-30s IN %-4s %s\n", $res, ($ip =~ /:/ ? 'AAAA' : 'A'), $ip ); } foreach (grep { $_ } $host_o->get_attributes('otherName')) { foreach my $ip (@{$lists{$res}{ip}}) { $dbzone .= sprintf( "%-30s IN %-4s %s\n", $_, ($ip =~ /:/ ? 'AAAA' : 'A'), $ip ); } } foreach (grep { $_ } $host_o->get_attributes('cname')) { # It is deny to have both: # foo IN A # foo IN CNAME if ($names{$_}) { my $msg .= sprintf( 'Cname %s to %s exclude because %s is already an A record', $_, $res, $_ ); la_log(LA_ERR, sprintf("$msg (zone %s)", $ozone->id)); $dbzone .= "; $msg\n"; } else { $dbzone .= sprintf("%-30s IN CNAME %s\n", $_, $res,); } } # SSH finger print foreach my $name (grep { $_ } ($res, $host_o->get_attributes('otherName'))) { foreach my $sshfp (grep { $_ } $host_o->get_attributes('sshfp')) { $dbzone .= sprintf( "%-30s IN SSHFP %s\n", $name, $sshfp, ); } } } } return $dbzone; } sub _gen_reverse_zone { my ($self, $ozone) = @_; my $domain = $ozone->get_attributes('domain') || ''; my $dbzone = ''; if ($ozone->get_attributes('net')) { my $findhost = $self->_base->db->prepare_cached(q{ select * from ( select * from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= ? and exported = true except select * from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= any(?) ) as q order by value::inet }); $findhost->execute( $ozone->get_attributes('net'), [ $ozone->get_attributes('netExclude') ], ) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; # reverse is complicated: my ($net) = $ozone->get_attributes('net') or do { $self->base->log('Cannot fetch attribute "net" for zone %s', $ozone->id); return; }; my $netip = Net::IP->new($net) or do { $self->base->log(LA_ERR, "Cannot build reverse zone %s: wrong net %s", $ozone->id, $net); return; }; my $mask = $netip->prefixlen; while (my $res = $findhost->fetchrow_hashref) { my $host_o = $self->_base->get_object('nethost', $res->{name}) or do { la_log LA_ERR, "Cannot fetch host %s", $res->{name}; return; }; my $desc = $self->_comment_nethost($host_o); my $reverse = $host_o->get_attributes('reverse') || ($res->{name} . '.'); $dbzone .= $desc ? '; ' . $desc . "\n" : ''; my $revip; my $fmt; if ($res->{value} =~ /:/) { # IPv6 my $m = $mask/4; $revip = Net::IPv6Addr->new($res->{value})->to_string_ip6_int; $revip =~ s/\.([0-9,a-f]\.?){$m}\.IP6\.INT\.$//i; $fmt = "%-72s IN PTR %s%s\n"; } else { # ipv4 if ($mask > 24) { $self->base->log(LA_ERR, 'Mask for zone %s cannot be %d', $ozone->id, $mask); return; } my @ippart = split(/\./, $res->{value}); splice(@ippart, 0, $mask/8); # get rid of start of ip my @nippart; while (@ippart) { unshift(@nippart, shift(@ippart)) } $revip = join('.', @nippart); $fmt = "%-12s IN PTR %s%s\n"; } $dbzone .= sprintf($fmt, $revip, $reverse =~ /\.$/ ? ($reverse, ($domain ? "$domain." : '.')) : ($reverse, '.')); } } return $dbzone; } sub _gen_dhcp_zone { my ($self, $ozone) = @_; my $outzone = $ozone; my $output = ''; my @net; if ($outzone->get_attributes('net')) { @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do { la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id, join(', ', $outzone->get_attributes('net'))); next; }; } { my $find = $self->_base->db->prepare(q{ select * from nethost where exported = true and ikey in( select okey from nethost_attributes where attr = 'macaddr' intersect ( select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = $1 except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'netExclude' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = $1 ) ) order by name }); $find->execute($ozone->id) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; while (my $res = $find->fetchrow_hashref) { my $nethost = $res->{name}; my $obj = $self->_base->get_object('nethost', $nethost) or do { la_log LA_ERR, "Cannot fetch host %s", $res->{name}; return; }; my $retainip; if (@net) { foreach my $inet (@net) { ($retainip) = grep { $_ && $inet->overlaps(Net::IP->new($_)) } $obj->get_attributes('ip') and last; } } $obj->get_attributes('noDynamic') && !$retainip and next; my $desc = $self->_comment_nethost($obj); foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) { $output .= $desc ? '# ' . $desc . "\n" : ''; my $fmac = $mac; $fmac =~ s/://g; $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac)); $output .= sprintf(" hardware ethernet %s;\n", $mac); $output .= sprintf(" fixed-address %s;\n", $retainip) if ($retainip); $output .= "}\n\n"; } } } if ($ozone->get_attributes('allow_dyn')) { $output .= "\n# Host without IP:\n"; my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom'); my $find = $self->_base->db->prepare(q{ select * from nethost where exported = true and ikey in( select okey from nethost_attributes where attr = 'macaddr' } . (@dynfrom ? q{ intersect ( select ikey from nethost where ikey not in (select okey from nethost_attributes_ips) union ( select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = any(?) except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'netExclude' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = any(?) ) )} : '') . q{ except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = ? ) order by name }); $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; while (my $res = $find->fetchrow_hashref) { my $nethost = $res->{name}; my $obj = $self->_base->get_object('nethost', $nethost); $obj->get_attributes('noDynamic') and next; my $desc = $self->_comment_nethost($obj); foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) { $output .= $desc ? '# ' . $desc . "\n" : ''; my $fmac = $mac; $fmac =~ s/://g; $output .= sprintf("host %s-%s {\n", $nethost, lc($fmac)); $output .= sprintf(" hardware ethernet %s;\n", $mac); $output .= "}\n\n"; } } } $output } sub _gen_radius_zone { my ($self, $ozone) = @_; my $outzone = $ozone; my $output = ''; my @net; if ($outzone->get_attributes('net')) { @net = (map { Net::IP->new($_) } $outzone->get_attributes('net')) or do { la_log(LA_DEBUG, 'Cannot get Net::IP for zone %s (ip: %s)', $outzone->id, join(', ', $outzone->get_attributes('net'))); next; }; } my %hosts; { my $find = $self->_base->db->prepare(q{ select * from nethost where exported = true and ikey in( select okey from nethost_attributes where attr = 'macaddr' intersect ( select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = $1 except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'netExclude' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = $1 ) ) order by name }); $find->execute($ozone->id) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; while (my $res = $find->fetchrow_hashref) { $hosts{ $res->{name} } = 1; } } if ($ozone->get_attributes('allow_dyn')) { my @dynfrom = grep { $_ } $ozone->get_attributes('dynFrom'); my $find = $self->_base->db->prepare(q{ select * from nethost where exported = true and ikey in( select okey from nethost_attributes where attr = 'macaddr' } . (@dynfrom ? q{ intersect ( select ikey from nethost where ikey not in (select okey from nethost_attributes_ips) union ( select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = any(?) except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'netExclude' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = any(?) ) )} : '') . q{ except select nethost_attributes_ips.okey from nethost_attributes_ips join netzone_attributes on netzone_attributes.attr = 'net' and netzone_attributes.value::inet >>= nethost_attributes_ips.value::inet join netzone on netzone.ikey = netzone_attributes.okey where netzone.name = ? ) order by name }); $find->execute((@dynfrom ? ([ @dynfrom ], [ @dynfrom ]) : ()), $ozone->id) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; while (my $res = $find->fetchrow_hashref) { $hosts{ $res->{name} } = 1; } } my @parameters = grep { $_ } $outzone->get_attributes('hostParams'); foreach my $nethost (sort keys %hosts) { my $obj = $self->_base->get_object('nethost', $nethost); $obj->get_attributes('noDynamic') and next; $output .= "# Host: $nethost\n"; my $desc = $self->_comment_nethost($obj); $output .= $desc ? '# ' . $desc . "\n" : ''; foreach my $mac (sort grep { $_ } $obj->get_attributes('macaddr')) { my $fmac = $mac; $fmac =~ s/://g; $output .= sprintf("%s Auth-Type := EAP, User-Password == \"%s\"\n", lc($fmac), lc($fmac)); $output .= join (",\n", map { " $_" } @parameters) . "\n" if(@parameters); $output .= "\n"; } $output .= "\n"; } $output; } sub _gen_puppet_zone { my ($self, $ozone) = @_; my $output = ''; if ($ozone->get_attributes('net')) { my $findhost = $self->_base->db->prepare_cached(q{ select * from ( select * from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= ? and exported = true except select * from nethost join nethost_attributes_ips on nethost.ikey = nethost_attributes_ips.okey where value::inet <<= any(?) ) as q order by value::inet }); $findhost->execute( $ozone->get_attributes('net'), [ $ozone->get_attributes('netExclude') ], ) or do { la_log LA_ERR, "Cannot fetch host list: %s", $self->_base->db->errstr; return; }; my @puppetclasses = $ozone->get_attributes('puppetClass'); while (my $res = $findhost->fetchrow_hashref) { my $nethost = $res->{name}; my $obj = $self->_base->get_object('nethost', $nethost) or do { la_log LA_ERR, "Cannot fetch host %s", $res->{name}; return; }; # merging classes from host and zone my %classes = map { $_ => 1 } grep { $_ } ( $obj->get_attributes('puppetClass'), ($obj->get_attributes('noInheritPuppet') ? () : @puppetclasses), ); my $desc = $self->_comment_nethost($obj); $output .= sprintf("node '%s' {\n", $obj->id); $output .= $desc ? sprintf(" # %s\n", $desc) : ''; $output .= join('', map { " class '$_'\n" } sort keys %classes); $output .= "}\n\n"; } } $output } sub reset_savepoint { my ($self) = @_; foreach my $zone ($self->_base->search_objects('netzone')) { my $ozone = $self->_base->get_object('netzone', $zone) or next; $self->_bnet_state->newval($ozone->id, 'dbrev', 0); la_log LA_DEBUG, "Zone savepoint reset for %s", $ozone->id; $self->_bnet_state->SetParameterComment( $ozone->id, 'dbrev', 'Reset savepoint the ' . scalar(localtime)); } $self->_write_state_file; } 1; __END__ =head1 AUTHOR Olivier Thauvin, Eolivier.thauvin@latmos.ipsl.frE =head1 COPYRIGHT AND LICENSE Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available. =cut