[Maintain-dev] [JIRA] Commented: (MNT-1361) VLAN to dhcp's shared-network

James O'Dell (JIRA) jira at osuosl.org
Mon Aug 8 15:43:07 PDT 2005


     [ http://bugs.osuosl.org/browse/MNT-1361?page=comments#action_10612 ]
     
James O'Dell commented on MNT-1361:
-----------------------------------



#Maintain.pm


# Copyright (c) 2004 Oregon State University - Open Source Lab
# All rights reserved.
 
# $Id: Maintain.pm 3564 2005-02-04 18:10:26Z philips $
# $HeadURL: https://svn.osuosl.org/public/maintain2/tags/release-2.4.2-RC2/lib/perl/Local/Maintain/Maintain.pm $ 

# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

#		my $one = $1;
#		my $two = $2;
#		$one =~ s/\s*$//;
#		$two =~ s/\s*$//;

package Local::Maintain;

use DBI;
use Socket;
use Data::Dumper;
use File::Copy;
use File::Basename;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %maint);

require Exporter;

@ISA = qw(Exporter AutoLoader);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw(

);
my $TRUE    = 1;
my $FALSE   = 0;

$VERSION = '';

my %maint = ();

# Autoload methods go after =cut, and are processed by the autosplit program.

#####################################################################
# Main subs
#####################################################################

sub new {
	my ($class, $path) = @_;

	if ( !$path ) {
		$path = '/data/maintain';
	}

        # let's check if there is a secrets file
	# let's build our object from the config file:
	open(CONFIG, "< $path/config/maintain.cfg")
		or die "Could not find $path/config/maintain.cfg.  Is it readable by me?\n";

	my %config = ();

	while (<CONFIG>) {
		next if ($_ =~ /^#.*/);	

		if ( $_ =~ /(.*?)\s+=\s+(.*)/ ) {
			# Strip off trailing spaces.
			my $one = $1;
			my $two = $2;
			$one =~ s/\s*$//;
			$two =~ s/\s*$//;

			$config{$one} = $two;
		}
	}

	my $name = $config{'mysql_db'};

	my $self = 
		{
			_name         => $config{'local_db'},
			_database     => $config{'local_db'},
			_sth_cache    => {},
			_domains      => {},
			_dhcp_options => {},
			_connect      => {
				dbd      => 'mysql',
				host     => $config{'local_host'},
				port     => '3306',
				username => $config{'local_username'},
				password => $config{'local_pass'} 
			},
		_dbh       => '',
		_path   => $path,
		_config => \%config,
		_debug	=> $FALSE
	};

	$VERSION = $config{'VERSION'};

	$Local::Maintain::maint{$name} = bless ($self, $class);

	$self->{_dbh} = $self->dbh( $name );

	return $self;
}

# Let's whack the statement handles and db connections
sub DESTROY {
	my ($self) = @_;

	foreach my $sth (values %{$self->{_sth_cache}}) {
		$sth->finish();
	}
  
	$self->{_dbh}->disconnect();
}


sub get {
	my ($class, $name) = @_;

	if( not $Local::Maintain::maint{$name} ) {
		$Local::Maintain::maint{$name} = Local::Maintain->new($name);
	}
	return bless $Local::Maintain::maint{$name}, $class;
}


# Enable or disable debugging -> the default is disable
sub debug {
	my ($self) = @_;

	# if debugging is on, disable it, if it isn't, enable it
	if ( $self->{_debug} ) {
		$self->{_debug} = $FALSE;
	}
	else {
		$self->{_debug} = $TRUE;
	}
}

# Get a database handle for the specified database
sub dbh {
  my ($self, $database) = @_;

  my $dbh = '';

  if ( $self->{_dbh} ) {
    return $self->{_dbh};
  }
  else {
    my $connect_string = [ sprintf("dbi:%s:database=%s;host=%s;port=%s", 
                   $self->{_connect}{dbd}, 
                   $self->{_database},
                   $self->{_connect}{host},
                   $self->{_connect}{port}),
                   $self->{_connect}{username},
                   $self->{_connect}{password} ];

    $dbh = DBI->connect( @{$connect_string}, 
		      {PrintError => 0,
		       RaiseError => 0,
		       AutoCommit => 1});

    if ( !$dbh ) {
    	die("Failed to connect to database.  Exiting.\n");
    }
  }

  return $dbh;
}

# populate a hash named $self->{_domains} for use
#   by the get_all_hosts and dhcp build subs.
sub populate_domains {
	my ($self) = @_;

	my $dbh = $self->{_dbh};

	# setup a domain hash so we don't do so many lookups of the
	#   domains ... this should cut down on build time greatly
	my $sql = qq{SELECT * FROM domain};
	my $domain_sth = $dbh->prepare($sql);
	$domain_sth->execute();

	while ( my $obj = $domain_sth->fetchrow_hashref() ) {
		$self->{_domains}->{$obj->{id}} = $obj->{name};
	}
}

# Much like populate_domains we want to get all of the key/value
#   pairs for dhcp options before we do a lot of things.  If we
#   pre-populate this here its a lot fewer selects we need to do
#   later.
#
sub populate_dhcp_options {
	my ($self) = @_;
        
	my $dbh = $self->{_dbh};

	my $sql = qq{SELECT * FROM dhcp_options ORDER BY id};
	my $dhcp_option_sth = $self->{_dbh}->prepare($sql);
	$dhcp_option_sth->execute();

	while ( my $obj = $dhcp_option_sth->fetchrow_hashref() ) {
		$self->{_dhcp_options}->{$obj->{id}} = $obj;
	}
}

sub get_table {
  my ($self, $name) = @_;

  my ($sql, $sth);

  my $dbh = $self->dbh();

  if ( not $self->{_sth_cache}{show_table}{$name} ) {
    $sql = qq{SELECT * FROM $name};
    $sth = $dbh->prepare($sql);
    $self->{_sth_cache}{show_table}{$name} = $sth;
  }

  $sth = $self->{_sth_cache}{show_table}{$name};
  $sth->execute();

  my @table = ();

  while ( my $hash = $sth->fetchrow_hashref() ) {
    push(@table, $hash); 
  }

  return (@table);
}

# For a given subnet hashref get the dynamic ranges that are
#   associated with it and return as an array of hashrefs
sub get_dynamic_ranges {
	my ($self, $subnet) = @_;

	my $dbh = $self->dbh();

	my $subnet_id = $subnet->{id};

	my $sql = qq{SELECT * FROM ranges WHERE subnet=? AND (type='dynamic' OR type='ddns') AND enabled=1};
	my $sth = $dbh->prepare($sql);
	$sth->execute($subnet->{id});

	$sql = qq{SELECT zone FROM zone_range WHERE range=?};
	my $zone_sth = $dbh->prepare($sql);

	my %range = ();

	while ( my $r = $sth->fetchrow_hashref() ) {
		$zone_sth->execute($r->{id});
		$range{$r->{start}}{$r->{end}}{id}        = $r->{id};
		$range{$r->{start}}{$r->{end}}{type}      = $r->{type};
		$range{$r->{start}}{$r->{end}}{allow_all_hosts}      = $r->{allow_all_hosts};
		while ( my $zone = $zone_sth->fetchrow() ) {
			push(@{$range{$r->{start}}{$r->{end}}{zone}}, $zone );
		}
	} 

	return \%range;
}

# For a given type and id return the options for that object
#   For use with returning both local workgroup and subnet options
#
sub get_workgroup_options {
        my ($self, $id, $type) = @_;

	# an arrayref
        #my $options = $self->get_global_options($type);

        my $sql = "SELECT * FROM object_option WHERE type='$type' AND ( scope='$type' OR scope='host' ) AND object_id=?";
        my $sth = $self->{_dbh}->prepare($sql);
        $sth->execute($id);

	my %options = ();

        while ( my $option = $sth->fetchrow_hashref() ) {
		# populate_dhcp_options has to have been run for this
		#  call to work
                $options{$option->{dhcp_option}} = $option;
        }

        return \%options;
}

# For a given type return the global options associated with it
#   For use with returning the global options for workgrup and subnet
#
sub get_global_options {
        my ($self, $type) = @_;

        my @globals = ();

        my $sql = "SELECT * FROM object_option WHERE type='global'";
        my $sth = $self->{_dbh}->prepare($sql);
        $sth->execute();

        while ( my $option = $sth->fetchrow_hashref() ) {
		$option->{dhcp_option} = $self->{_dhcp_options}->{$option->{dhcp_option}->{name}};
                push(@globals, $option);
        }

        return \@globals;
}

# Get the dhcp options for this host.  This allows us to have per
#   host dhcp options in group {}; definitions in dhcpd.conf
#
sub get_host_options {
	my ($self, $host) = @_;

	my %options = ();

        my $sql = qq{SELECT * FROM object_option WHERE type='host' AND object_id=?};
        my $sth = $self->{_dbh}->prepare($sql);
        $sth->execute($host->{id});

        while ( my $option = $sth->fetchrow_hashref() ) {
		$option->{value} = $self->convert_dhcp_option($host, $option->{value});
                $options{$option->{dhcp_option}} = $option;
        }
                
        return \%options; 
}

# Take the dhcp options for this host and make any conversions nessacary
# then return them, allows for host specific dhcp options in dhcpd.conf
sub parse_host_options {
	my ($self, $host, $options) = @_;


	my %final_options = ();
	my $option = ();
	my $key = ();

	foreach $key ( keys %{$options} ) {
		$option->{value} = $self->convert_dhcp_option($host, $options->{$key});
		$final_options{$key} = $option;
	}

	return \%final_options;

}

# We might want to substitute in some information in for
#  a given option.  For example, a hostname for printer
#  definitions or a MAC address for use with IP phones.
#  We allow workgroups to have "host" values which you can have
#  things like %h, %m and %i where the value for the host is
#  substituted in.
#
sub convert_dhcp_option {
	my ($self, $host, $value) = @_;

	# Let's setup the small hwa
	my $hwa = $host->{ha};
	$hwa =~ s/\://g;
	$hwa = substr($hwa,0,6);

	$value =~ s/%h/$host->{name}/;		
	$value =~ s/%i/$host->{ip}/;		
	$value =~ s/%m/$host->{ha}/;		
	$value =~ s/%6m/$hwa/;

	return $value;
}

#####################################################################
# End Main subs
#####################################################################


#####################################################################
# Subnet subs
#####################################################################

# Use subnet ID here to get all of the info
# Modified
sub get_subnet_by_id {
	my ($self, $id) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_subnet_by_id_select} ) {
		my $sql = qq{SELECT name,inet_ntoa(subnet),inet_ntoa(netmask),vlan FROM subnet WHERE id=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_subnet_by_id_select} = $sth;
	}

	my $sth = $self->{_sth_cache}{get_subnet_by_id_select};
	$sth->execute($id);

	# we have to return like this because otherwise the variables
	#   are named with the inet_ntoa which confuses referencing
	my %info = ();
	my ($name,$subnet,$netmask,$vlan) = $sth->fetchrow();
	$info{id}      = $id;
	$info{name}    = $name;
	$info{subnet}  = $subnet;
	$info{netmask} = $netmask;
	$info{vlan}    = $vlan;
	
	return (\%info);
}

# New
sub get_vlan_count_by_vlan {
	my ($self, $vlan) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_vlan_count_by_vlan_select} ) {
		my $sql = qq{SELECT * FROM subnet WHERE vlan=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_vlan_count_by_vlan_select} = $sth;
	}

	my $sth = $self->{_sth_cache}{get_vlan_count_by_vlan_select};
	$sth->execute($vlan);

	my $count = 0;

        while ( my ($host) = $sth->fetchrow() ) {
	        $count = $count + 1;
	}
	
	return $count;
}

#####################################################################
#####################################################################
# End Subnet
#####################################################################


#####################################################################
# IP subs
#####################################################################

# Make sure this IP address is in one of our IP blocks
#
sub check_ip {
	my ($self, $ip) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{check_ip} ) {
		my $sql = qq{SELECT * FROM ip_blocks WHERE start<=? AND end>=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{check_ip} = $sth;
	}

	my $sth = $self->{_sth_cache}{check_ip};
	$sth->execute($ip, $ip);

	return ($sth->fetchrow());
}

#####################################################################
# End IP
#####################################################################


#####################################################################
# Host/Record subs
#####################################################################

# Return a hashref of the host with the given hardware address and
#   zone_id.
#
sub get_host_from_ha {
	my ($self, $ha, $zone) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_host_from_ha} ) {
		my $sql = qq{SELECT * FROM host WHERE ha=? AND zone=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_host_from_ha} = $sth;
	}

	my $sth = $self->{_sth_cache}{get_host_from_ha};
	$sth->execute($ha, $zone);
	return ($sth->fetchrow_hashref);
}

# Return a hashref of the host with the given IP address and
#   zone_id.
#
sub get_host_from_ip {
        my ($self, $ip, $zone) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_host_from_ip_select} or
		not $self->{_sth_cache}{get_host_from_ip_host_select} ) {
                my $sql = qq{SELECT * FROM ip WHERE address=?};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_host_from_ip_select} = $sth;

		$sql = qq{SELECT * FROM host WHERE ip=? AND zone=?};
                $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_host_from_ip_host_select} = $sth;
        }

	# So this is a two-step process only because 2 selects are much
	#   faster than one join ... by like a factor of 30.
        my $sth = $self->{_sth_cache}{get_host_from_ip_select};
        $sth->execute($ip);
        my $ip_obj = $sth->fetchrow_hashref;

	$sth = $self->{_sth_cache}{get_host_from_ip_host_select};
	$sth->execute($ip_obj->{id}, $zone);
	return ( $sth->fetchrow_hashref() );
}


# Return a hashref of the host with the given IP address and
#   zone_id.
#
sub get_host_from_serial {
        my ($self, $serial, $zone) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_host_from_serial} ) {
                my $sql = qq{SELECT * FROM host WHERE serial=? AND zone=?};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_host_from_serial} = $sth;
        }

	my $sth = $self->{_sth_cache}{get_host_from_serial};
	$sth->execute($serial, $zone);
	return ( $sth->fetchrow_hashref() );
}


# Insert a host based on the given information.
#
sub insert_host {
	my ($self, $ip, $name, $domain, $tc, $zone) = @_;

	my $dbh = $self->dbh();
	my $ip_addr = unpack N => inet_aton($ip);

	# if we find the IP then lets insert it accordingly
	if ( (my $ip_id = $self->get_ip($ip_addr)) or ( ! $ip) ) {
		#print "$ip : $ip_addr : $ip_id\n";
		
		if ( ! $ip ) {
			$ip_id = 0;
		}
		
		if ( not $self->{_sth_cache}{insert_host_insert} or
			not $self->{_sth_cache}{insert_host_select} ) {
			my $sql = qq{INSERT INTO host (ip,name,domain,workgroup,zone) VALUES (?,?,?,?,?)};
			my $sth = $dbh->prepare($sql);
			$self->{_sth_cache}{insert_host_insert} = $sth;

			$sql = qq{SELECT id FROM host WHERE ip=? AND name=? AND domain=?};
			$sth = $dbh->prepare($sql);
			$self->{_sth_cache}{insert_host_select} = $sth;
		}

		my $sth = $self->{_sth_cache}{insert_host_select};
		$sth->execute($ip_id, $name, $domain);

		if ( my $id = $sth->fetchrow() ) {
			return $id;
		}
		else {
			$sth = $self->{_sth_cache}{insert_host_insert};
			$sth->execute($ip_id, $name, $domain, $tc, $zone);
		}
		return ($dbh->{mysql_insertid});
	}
}

sub insert_full_host {
	my ($self, $name, $domain, $ip, $ha, $os, $type, $other_id, $workgroup, $zone) = @_;

	my $dbh = $self->dbh();
        my $ip_addr = 0;

	if ( $ip ) {
		$ip_addr = unpack N => inet_aton($ip);
	}

	if ( not $self->{_sth_cache}{insert_full_host_insert} or
        	not $self->{_sth_cache}{insert_full_host_select} ) {
        	my $sql = qq{INSERT INTO host (name,domain,ip,ha,os,type,other_id,workgroup,zone) VALUES (?,?,?,?,?,?,?,?,?)};
        	my $sth = $dbh->prepare($sql);
        	$self->{_sth_cache}{insert_full_host_insert} = $sth;

        	$sql = qq{SELECT id FROM host WHERE ip=? AND name=? AND domain=?};
        	$sth = $dbh->prepare($sql);
        	$self->{_sth_cache}{insert_full_host_select} = $sth;
        }

        my $sth = $self->{_sth_cache}{insert_full_host_select};
        $sth->execute($ip_addr, $name, $domain);

        if ( my $id = $sth->fetchrow() ) {
		print "ERROR: this is a duplicate host: $name.$ip -> $zone = host: $id\n";
        	return $id;
        }
        else {
        	$sth = $self->{_sth_cache}{insert_full_host_insert};
        	$sth->execute($name,$domain,$ip_addr,$ha,$os,$type,$other_id,$workgroup,$zone);
        }

        return ($dbh->{mysql_insertid});
}

sub insert_simple_host {
	my ($self, $name, $domain_name, $ip, $workgroup_name, $zone_name) = @_;

	my $domain = $self->get_domain_from_name($domain_name);
	my $workgroup = $self->get_workgroup_by_name($workgroup_name);
	my $zone = $self->get_zone_by_name($zone_name);

	$self->insert_full_host($name, $domain->{id}, $ip, '000000000000', '', '', '', $workgroup->{id}, $zone->{id});
}

# Insert a cname record
#
sub insert_cname {
	my ($self, $name, $domain_name, $server, $zone_name) = @_;

	my $dbh = $self->dbh();

	my $domain = $self->get_domain_from_name($domain_name);
	my $zone = $self->get_zone_by_name($zone_name);

	if ( not $self->{_sth_cache}{insert_cname} ) {	
		my $sql = qq{INSERT INTO zone_cname (name,domain,server,zone) VALUES (?,?,?,?)};
		my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{insert_cname} = $sth;
	}

	my $sth = $self->{_sth_cache}{insert_cname};
        $sth->execute($name, $domain->{id}, $server, $zone->{id});

	return ($dbh->{mysql_insertid});
}

# Insert an mx record
#
sub insert_mx {
	my ($self, $name, $domain_name, $server, $priority, $zone_name) = @_;

	my $domain = $self->get_domain_from_name($domain_name);
        my $zone = $self->get_zone_by_name($zone_name);

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{insert_mx} ) {           
                my $sql = qq{INSERT INTO zone_mx (name,domain,server,priority,zone) VALUES (?,?,?,?,?)};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{insert_mx} = $sth;
        }

        my $sth = $self->{_sth_cache}{insert_mx};
        $sth->execute($name, $domain->{id}, $server, $priority, $zone->{id});

        return ($dbh->{mysql_insertid});
}

sub insert_ns {
        my ($self, $fqdn, $ns) = @_;

        my $domain = $self->get_domain_from_name($fqdn);

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{insert_ns} ) {
                my $sql = qq{INSERT INTO nameserver (name,domain) VALUES (?,?)};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{insert_ns} = $sth;
        }

        my $sth = $self->{_sth_cache}{insert_ns};
        $sth->execute($ns, $domain->{id});

        return ($dbh->{mysql_insertid});
}

sub insert_pointer {
	my ($self, $ip, $hostname, $type, $zone_name) = @_;

        my $zone = $self->get_zone_by_name($zone_name);

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{insert_pointer} ) {
                my $sql = qq{INSERT INTO pointer (ip,hostname,type,zone) VALUES (inet_aton(?),?,?,?)};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{insert_pointer} = $sth;
        }

        my $sth = $self->{_sth_cache}{insert_pointer};
        $sth->execute($ip, $hostname, $type, $zone->{id});

        return ($dbh->{mysql_insertid});
}

# Update the last_seen entry for the host with the given MAC address.
#   We do this slightly different than other subs because we want to return
#   whether or not the MAC was in the database.  This function will tell you
#   how many rows it updated so anything > 0 means this is a known MAC.
#
sub update_host_last_seen {
	my ($self, $ha, $last_seen) = @_;

	my $sql = qq{UPDATE host SET last_seen=$last_seen WHERE ha='$ha'};
	return $self->{_dbh}->do($sql);
}

# Get all of the hosts in the host table
#   $self->populate_domains has to have been called before this function will work
#
sub get_all_hosts {
	my ($self) = @_;

	my $dbh = $self->dbh();
	my $sql = qq{SELECT * FROM host WHERE ip!=0 AND enabled=1 ORDER BY ip};
	my $sth = $dbh->prepare($sql);
	$sth->execute();

	# now get all of the hosts and populate this array
	my @hosts = ();

	while ( my $host = $sth->fetchrow_hashref ) {
		$host->{ip}     = inet_ntoa(pack N => $host->{ip}); 
		#$host->{subnet} = $ip_info->{subnet};
		$host->{domain} = $self->{_domains}->{$host->{domain}};
		#print "host: $ip_info, $host->{name}, $host->{domain} $host->{ip}\n";
		push(@hosts, $host);
	}

	$sql = qq{SELECT * FROM ranges WHERE type='dynamic' AND enabled=1 ORDER by start};
	$sth = $dbh->prepare($sql);
        $sth->execute();

	$sql = qq{SELECT * FROM zone_range WHERE range=?};
	my $range_sth = $dbh->prepare($sql);

	my $ip_string = '';

	while ( my $dyn = $sth->fetchrow_hashref ) {
		$range_sth->execute($dyn->{id});
		my $range = $range_sth->fetchrow_hashref();

		while ( $dyn->{start} <= $dyn->{end} ) {
                        my %host = ();
                        $host{ip}   = inet_ntoa(pack N => $dyn->{start});

                        ($ip_string = $host{ip}) =~ s/\./-/g;

                        $host{name} = $ip_string;
			$host{domain} = $self->{_domains}->{$range->{default_domain}};
			push(@hosts, \%host);
                        $dyn->{start}++;
		}
	}

	return (\@hosts);
}

# Get all of the hosts for the vmps build
sub get_all_hosts_vmps {
	my ($self) = @_;

	# let's populate the domain info
	$self->populate_domains();

	my $dbh = $self->dbh();
	my $sql = qq{SELECT distinct(host.ha),host.ip,host.dynamic_range FROM host WHERE ha!='000000000000' AND enabled=1 ORDER BY ip};
	my $sth = $dbh->prepare($sql);
	$sth->execute();

	$sql = qq{SELECT subnet.* FROM ranges,subnet WHERE ranges.subnet=subnet.id AND ranges.id=?};
	my $dyn_subnet_sth = $dbh->prepare($sql);

	$sql = qq{SELECT * FROM subnet WHERE subnet < ? ORDER BY subnet DESC LIMIT 1};
	my $sta_subnet_sth = $dbh->prepare($sql);

	my @hosts = ();
	my %range_cache = ();
	my %host_cache = ();

	while ( my $host = $sth->fetchrow_hashref ) {
		# two cases: dynamic & static
		my $subnet = undef;

		if ( $host->{ip} == 0 && $range_cache{$host->{dynamic_range}}{subnet} == 0) { 
			$dyn_subnet_sth->execute($host->{dynamic_range});
	               	$subnet = $dyn_subnet_sth->fetchrow_hashref();
			$range_cache{$host->{dynamic_range}}{subnet} = $subnet->{id};
			$range_cache{$host->{dynamic_range}}{vlan} = $subnet->{vlan};
			$host->{subnet} = $subnet->{id};
			$host->{vlan}   = $subnet->{vlan};	
		}
		elsif ( $host->{ip} == 0 ) {
			$host->{subnet} = $range_cache{$host->{dynamic_range}}{subnet};
			$host->{vlan}   = $range_cache{$host->{dynamic_range}}{vlan};
		}
		else {	
			$sta_subnet_sth->execute($host->{ip});
			$subnet = $sta_subnet_sth->fetchrow_hashref();
			$host->{ip}     = inet_ntoa(pack N => $host->{ip}); 
			$host->{vlan}   = $subnet->{vlan};
		}

		# this is created by using $self->populate_domains()
		$host->{domain} = $self->{_domains}->{$host->{domain}};

		if ( $host->{vlan} != 0 && $host_cache{$host->{ha}}{vlan} != $host->{vlan} ) {
			push(@hosts, $host);
		}
		
		# Total Crap fix
		$host_cache{$host->{ha}}{vlan} = $host->{vlan};

	}
	return (\@hosts);
}

#####################################################################
# End Host/Record subs
#####################################################################


#####################################################################
# Zone subs
#####################################################################

# Given a domain_id and a zone_id, does the domain_id exist in
#   zone_domain with the given zone_id?
#
sub is_in_zone_domain {
	my ($self, $domain_id, $zone_id) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{is_in_zone_domain} ) {
		my $sql = qq{SELECT id FROM zone_domain WHERE domain=? AND zone=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{is_in_zone_domain} = $sth;
	}

	my $sth = $self->{_sth_cache}{is_in_zone_domain};
	$sth->execute($domain_id, $zone_id);

	my @answer = $sth->fetchrow();

	return $answer[0];
}

#
# Get the zone information for a specific zone
sub get_zone {
	my ($self, $zone) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_zone} ) {
		my $sql = qq{SELECT * FROM zone WHERE id=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_zone} = $sth;
	}

	my $sth = $self->{_sth_cache}{get_zone};
	$sth->execute($zone);

	return $sth->fetchrow_hashref;
}

# Get a zone by its name instead of ID
#
sub get_zone_by_name {
        my ($self, $zone) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_zone_by_name} ) {
                my $sql = qq{SELECT * FROM zone WHERE name=?};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_zone_by_name} = $sth;
        }

        my $sth = $self->{_sth_cache}{get_zone_by_name};
        $sth->execute($zone);

        return $sth->fetchrow_hashref;
}

# Ranges are now split across 'ranges' and 'zone_ranges' so we
#   have to do some fancy footwork there to get the data back.
#
sub get_range {
	my ($self, $range_id) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{select_get_range} ) {
		my $sql = qq{SELECT ranges.*,zone_range.zone,
				zone_range.default_domain,zone_range.comment
				FROM ranges,zone_range 
				WHERE ranges.id = zone_range.range AND
				ranges.id = ?};
		$self->{_sth_cache}{select_get_range} = $dbh->prepare($sql);
	}

	my $sth = $self->{_sth_cache}{select_get_range};
	$sth->execute($range_id);

	my $range = $sth->fetchrow_hashref();

	# keep these around because we might want the int representation
	$range->{start_int} = $range->{start};
	$range->{end_int} = $range->{end};

	$range->{start} = inet_ntoa(pack N => $range->{start});
	$range->{end}   = inet_ntoa(pack N => $range->{end});

	return $range;
}

# given an IP address return what zone_range its in
#   Don't forget -- we have to update this IP to its integer representation
sub get_zone_range {
	my ($self, $ip) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_zone_range} ) {
		my $sql = qq{SELECT id FROM ranges WHERE start<=inet_aton(?) AND end>=inet_aton(?)};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_zone_range} = $sth;
	}
	
	my $sth = $self->{_sth_cache}{get_zone_range};
	$sth->execute($ip, $ip);

	my $zone_range = 0;

	if ( my $range_id = $sth->fetchrow() ) {
		$zone_range = $self->get_range($range_id);	
	}

	# if it exists it will return the proper zone_range ID
	return $zone_range;
}

#####################################################################
# End Zone subs
#####################################################################


#####################################################################
# Domain subs
#####################################################################

# Given a domain id return the domain name
sub get_domain {
	my ($self, $domain) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT name FROM domain WHERE id=?};
	my $sth = $dbh->prepare($sql);
	$sth->execute($domain);

	return ($sth->fetchrow());
}

# Given a domain name return info about it
sub get_domain_from_name {
	my ($self, $domain) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT * FROM domain WHERE name=?};
	my $sth = $dbh->prepare($sql);
	$sth->execute($domain);

	return ($sth->fetchrow_hashref());
}

#####################################################################
# End Domain
#####################################################################


#####################################################################
# Workgroup Mojo
#####################################################################

# Use subnet ID here to get all of the info
sub get_workgroup_by_id {
	my ($self, $id) = @_;

	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_workgroup_by_id_select} ) {
		my $sql = qq{SELECT id,name FROM workgroup WHERE id=?};
		my $sth = $dbh->prepare($sql);
		$self->{_sth_cache}{get_workgroup_by_id_select} = $sth;
	}

	my $sth = $self->{_sth_cache}{get_workgroup_by_id_select};
	$sth->execute($id);

	return ($sth->fetchrow_hashref);
}

# Get complete workgroup information given the name of the workgroup
sub get_workgroup_by_name {
        my ($self, $name) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_workgroup_by_name_select} ) {
                my $sql = qq{SELECT * FROM workgroup WHERE name=?};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_workgroup_by_name_select} = $sth;
        }

        my $sth = $self->{_sth_cache}{get_workgroup_by_name_select};
        $sth->execute($name);

        return ($sth->fetchrow_hashref);
}

# When we insert new hosts we want to know which default workgroup
#   they should be in ... this sub will tell us that
#
sub get_workgroup_by_ip {
        my ($self, $ip) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_workgroup_by_ip_select} or
		not $self->{_sth_cache}{get_workgroup_by_ip_select2} ) {
                my $sql = qq{SELECT id FROM subnet WHERE subnet <= inet_aton(?) ORDER BY subnet DESC LIMIT 1};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_workgroup_by_ip_select} = $sth;

		my $sql = qq{SELECT * FROM workgroup WHERE subnet=? ORDER BY is_default DESC};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_workgroup_by_ip_select2} = $sth;
        }

        my $sth = $self->{_sth_cache}{get_workgroup_by_ip_select};
        $sth->execute($ip);
	my ($id) = $sth->fetchrow();

	$sth = $self->{_sth_cache}{get_workgroup_by_ip_select2};
	$sth->execute($id);

	my $workgroup = $sth->fetchrow_hashref;

        return $workgroup;
}

sub get_workgroup_hosts {
        my ($self, $workgroup) = @_;

        my $dbh = $self->dbh();


        if ( not $self->{_sth_cache}{get_workgroup_hosts_select} or
                not $self->{_sth_cache}{get_workgroup_hosts_select_ip}) {

                my $sql = qq{SELECT * FROM host WHERE workgroup=? 
                                AND ha!='000000000000'
								AND CHAR_LENGTH(`ha`) = 12 AND enabled=1 
								ORDER BY ip};

                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_workgroup_hosts_select} = $sth;
        }

        my $sth = $self->{_sth_cache}{get_workgroup_hosts_select};
        $sth->execute($workgroup->{id});
        my @hosts = ();

        while ( my $host = $sth->fetchrow_hashref ) {
                if ( $host->{ip} ) {
                        $host->{ip} = inet_ntoa(pack N => $host->{ip});
                }

                $host->{domain} = $self->{_domains}->{$host->{domain}};

                if ( $host->{ha} ) {
                        print "ha: $host->{ha}\n" if $self->{_debug};
                        $host->{ha} = $self->convert_ha($host->{ha});
                        push(@hosts, $host);
                }
        }

        return (\@hosts);
}

# Just turn the 12 character hardware address into the colon-delimited
#   version.
sub convert_ha {
	my ($self, $ha) = @_;

	$ha =~ /(\w{2,2})(\w{2,2})(\w{2,2})(\w{2,2})(\w{2,2})(\w{2,2})/;
	return ($1.":".$2.":".$3.":".$4.":".$5.":".$6);
}

sub get_workgroup_dynamic_hosts {
        my ($self, $id) = @_;

        my $dbh = $self->dbh();

        if ( not $self->{_sth_cache}{get_workgroup_dynamic_hosts_select} ) {
                my $sql = qq{SELECT * FROM host WHERE workgroup=? 
                                AND ha!='000000000000'
								AND CHAR_LENGTH(`ha`) = 12 
								AND ip=0};
                my $sth = $dbh->prepare($sql);
                $self->{_sth_cache}{get_workgroup_dynamic_hosts_select} = $sth;
        }

        my $sth = $self->{_sth_cache}{get_workgroup_dynamic_hosts_select};
        $sth->execute($id);

        my @hosts = ();

        while ( my $host = $sth->fetchrow_hashref ) {
                $host->{ip}     = inet_ntoa(pack N => $host->{ip});
                $host->{domain} = $self->get_domain($host->{domain});
                $host->{ha} = $self->convert_ha($host->{ha});
                push(@hosts, $host);
        }

        return (\@hosts);
}

#####################################################################
# End Workgroup
#####################################################################

#####################################################################
## VLAN Mojo
######################################################################

# Get a vlan hashref based on the subnet id
#
sub get_vlan {
	my ($self, $vlan) = @_;
	my $dbh = $self->dbh();

	if ( not $self->{_sth_cache}{get_vlan} ) {
		my $sql = qq{SELECT * FROM vlan WHERE id=?};
		$self->{_sth_cache}{get_vlan} = $dbh->prepare($sql);
	}

	my $sth = $self->{_sth_cache}{get_vlan};
	$sth->execute($vlan);
	return $sth->fetchrow_hashref();
}

#####################################################################
# end VLAN
#####################################################################

#####################################################################
# Staging
######################################################################

# Insert a host into the database with all of the given values.
#   This is much more verbose that insert_host because we have
#   to insert _everything_ for this host that is handed to us
#   from both the staging and staging_permissions database
#
sub insert_host_staging {
	my ($self, $host, $perms) = @_;

	my $dbh = $self->dbh();
	
	if ( ! $self->{_sth_cache}{insert_host_staging} ||
		! $self->{_sth_cache}{insert_host_dynamic} ) {
		my $sql = qq{INSERT INTO host (ip,name,domain,ha,type,os,location,serial,other_id,workgroup,zone)
				VALUES (?,?,?,?,?,?,?,?,?,?,?)};
		$self->{_sth_cache}{insert_host_staging} = $dbh->prepare($sql);

		$sql = qq{INSERT INTO host_range (range,host) VALUES (?,?)};
		$self->{_sth_cache}{insert_host_dynamic} = $dbh->prepare($sql);
	}

	my $sth = $self->{_sth_cache}{insert_host_staging};
	$sth->execute($host->{ip},
			$host->{name},
			$host->{domain_id},
			$host->{ha},
			$host->{type},
			$host->{os},
			$host->{location},
			$host->{serial},
			$host->{other_id},
			$host->{workgroup},
			$perms->{zone});

	my $host_id = $dbh->{'mysql_insertid'};

	# this is a dynamic host, it must be inserted in the host_range
	#   table as well as the host table
	if ( $host->{ip_id} == 0 ) {
		$sth = $self->{_sth_cache}{insert_host_dynamic};
		$sth->execute($host_id, $host->{range_id});
	}
}

# Update a host with the given $host->{id} that already exists in
#   the production database.
#
sub update_host_staging {
	my ($self, $host, $perms) = @_;

        my $dbh = $self->dbh();

        if ( ! $self->{_sth_cache}{update_host_staging} ||
		! $self->{_sth_cache}{update_host_dynamic} ) {
                my $sql = qq{UPDATE host SET ip=?,name=?,domain=?,ha=?,type=?,os=?,location=?,serial=?,
				other_id=?,workgroup=? WHERE id=? AND zone=?};
                $self->{_sth_cache}{update_host_staging} = $dbh->prepare($sql);

		$sql = qq{UPDATE host_range SET range=? WHERE host=?};
		$self->{_sth_cache}{update_host_dynamic} = $dbh->prepare($sql);
        }

        my $sth = $self->{_sth_cache}{update_host_staging};
        $sth->execute($host->{ip},
                        $host->{name},
                        $host->{domain_id},
                        $host->{ha},
                        $host->{type},
                        $host->{os},
                        $host->{location},
                        $host->{serial},
                        $host->{other_id},
                        $host->{workgroup},
                        $host->{id},
			$perms->{zone});

	# deal with dynamic hosts appropriately
	if ( $host->{ip_id} == 0 ) {
		$sth = $self->{_sth_cache}{update_host_dynamic};
		$sth->execute($host->{id}, $host->{range_id});
	}
}

# Given a hardware address and a zone delete the host associated with that
#
sub delete_host_staging {
	my ($self, $host_id) = @_;

        my $dbh = $self->dbh();

        if ( ! $self->{_sth_cache}{delete_host_staging} || 
		! $self->{_sth_cache}{delete_host_dynamic} ) {

		my $sql = qq{DELETE FROM host WHERE id=?};
		$self->{_sth_cache}{delete_host_staging} = $dbh->prepare($sql);

		$sql = qq{DELETE FROM host_range WHERE host=?};
		$self->{_sth_cache}{delete_host_dynamic} = $dbh->prepare($sql);
	}

	my $sth = $self->{_sth_cache}{delete_host_staging};
	$sth->execute($host_id);

	$sth = $self->{_sth_cache}{delete_host_dynamic};
	$sth->execute($host_id);
}

#####################################################################
# End Staging
######################################################################


#####################################################################
# Output functions for DHCP config
#####################################################################

# This will print a given subnet out in dhcpd.conf format
#
#   Also the place where shared-network will be worked in
#
sub print_dhcp_subnet {
        my ($self, $subnet, $netmask, $ranges, $info, $fh) = @_;

        print $fh "subnet $subnet netmask $netmask {\n";

	$self->print_dhcp_options($info, "\t", $fh);

	my %used = ();

	if ( $ranges ) {
		while ( my ($start, $info) = each(%{$ranges}) ) {
			while ( my ($end, $data) = each(%{$info}) ) { 
				$start = inet_ntoa(pack N => $start);
				$end   = inet_ntoa(pack N => $end);

				# since dynamic ranges can exist from zone-to-zone
				#   we have to check to make sure we aren't printing
				#   this guy twice.
				if ( not $used{$start} and not $used{$end} ) {
					print $fh "\tpool {\n";
					print $fh qq{\t\tfailover peer "dhcp";\n};

					foreach my $zone (@{$data->{zone}}) {
						my $z = $self->get_zone($zone);
						print $fh qq{\t\tallow members of "$z->{name}:$start:$end";\n};
					}

					print $fh "\t\trange $start $end;\n";
					
					# This pool allows all hosts registered in maintain to get
					# an ip from it.  Looks at the allow_all_hosts column of
					# ranges in the db.
					if ($data->{allow_all_hosts}) {
						print $fh qq{\t\tallow known clients;\n};
					}

					print $fh "\t}\n";	
				}

				$used{$start} = 1;
				$used{$end}   = 1;
			}
		}
	}

        print $fh "}\n";
}

# This will print out group {} declarations for dhcpd.conf.
#   Will also pull in information from $self about hosts associated
#   with workgroup.
#
sub print_dhcp_workgroup {

	my %ref_h_options = ();

    my ($self, $workgroup, $type, $info, $fh, $ref_h_options) = @_;

    # now print this sucker out
    print $fh "group { \# $workgroup->{name}\n";

	# we need to break out these options based on type
	#   We need an array of host options and one of group
	my %host_options  = ();
	my %group_options = ();

	while ( my ($k,$v) = each(%{$info}) ) {
		if ( $v->{scope} eq 'host' ) {
			$host_options{$k} = $v;
		}
		else {
			$group_options{$k} = $v;
		}
	}

	# print the options accordingly
	$self->print_dhcp_options(\%group_options, "\t", $fh);

	# now get the hosts with fixed addresses for this workgroup and 
	#   print them out all purdy

	my $hosts = $self->get_workgroup_hosts($workgroup);

	foreach my $host (@{$hosts}) {
		# this should not be added if there isn't a hardware
		#   address associated with it
		print $fh "\thost $host->{name}.$host->{domain} {\n";

 		# is this a dynamic host?
		if ( ($host->{ip}) and ($type eq 'default') ) {
			print $fh "\t\tfixed-address $host->{ip};\n";
		}

		print $fh "\t\thardware ethernet $host->{ha};\n";

		if ( !$host->{skip_options} ) {
			# Now we are going to use it differently, take the passed
			# h_options and push it to new function parse_host_options
			my $options = $self->parse_host_options($host,$ref_h_options->{$host->{id}});

			# convert any %h, %m or %i that could be present in options
			while ( my ($k,$v) = each(%host_options) ) {
				$host_options{$k}->{sub_value} = $self->convert_dhcp_option($host, $v->{value});
			}

			$self->print_dhcp_options($options, "\t\t", $fh);
			$self->print_dhcp_options(\%host_options, "\t\t", $fh);
		}

		print $fh "\t}\n";

	}

	print $fh "}\n";

}

# we use this for printing both subnet and workgroup options
#   so we might as do this in one place.  Bear in mind that some
#   of these options don't need the 'option' keyword in the front
#   of them so we print them here to take care of that.
#
sub print_dhcp_options {
	my ($self, $options, $offset, $fh) = @_;

	while ( my ($k,$v) = each(%{$options}) ) {
		my $option_name = $self->{_dhcp_options}->{$k}->{name};
		my $value = $v->{value};

		if ( $v->{sub_value} ) {
			$value = $v->{sub_value};
		}

		if ( $self->{_dhcp_options}->{$k}->{type} eq 'option' ) {
			print $fh $offset . "option $option_name $value;\n";
		}
		elsif ( $self->{_dhcp_options}->{$k}->{type} eq 'quoted-option' ) {
			print $fh $offset . qq{option $option_name "$value";\n};
		}
		elsif ( $self->{_dhcp_options}->{$k}->{type} eq 'quoted' ) {
			print $fh $offset . qq{$option_name "$value";\n};
		}
		else {
			print $fh $offset . "$option_name $value;\n";
		}
        }
}

# Get the complete option by name
#
sub get_dhcp_option_by_name {
	my ($self, $option_name) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT * FROM dhcp_options WHERE name=?};
	my $sth = $dbh->prepare($sql);
	$sth->execute($option_name);

	return ($sth->fetchrow_hashref());
}

sub insert_workgroup_option_local {
	my ($self, $workgroup_name, $option_name, $value, $type) = @_;

	my $dbh = $self->dbh();

	my $workgroup = $self->get_workgroup_by_name($workgroup_name);
	my $option = $self->get_dhcp_option_by_name($option_name);

	my $sql = qq{INSERT INTO workgroup_option_local (workgroup,dhcp_option,value,type) VALUES (?,?,?,?)};
	my $sth = $dbh->prepare($sql);

	$sth->execute($workgroup->{id}, $option->{id}, $value, $type);
}

sub insert_zone_workgroup {
	my ($self, $workgroup_name, $zone_name) = @_;

        my $zone = $self->get_zone_by_name($zone_name);
	my $workgroup = $self->get_workgroup_by_name($workgroup_name);

	my $dbh = $self->dbh();

	my $sql = qq{INSERT INTO zone_workgroup (workgroup,zone) VALUES (?,?)};
	my $sth = $dbh->prepare($sql);
	$sth->execute($workgroup->{id}, $zone->{id});
}

#####################################################################
# End DHCP Output functions
#####################################################################


#####################################################################
# Output functions for DJBDNS config
#####################################################################

sub print_mx {
	my ($self, $fh) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT DISTINCT zone FROM zone_mx ORDER BY zone};
	my $zone_sth = $dbh->prepare($sql);
	$zone_sth->execute();

	$sql = qq{SELECT * FROM zone_mx WHERE zone=?};
	my $sth = $dbh->prepare($sql);

	print $fh "# print_mx: Listing of all of the MX records for the domains\n";
	print $fh "#   we administrate\n";

	while ( my ($zone) = $zone_sth->fetchrow() ) {
		$sth->execute($zone);
		my $z = $self->get_zone($zone);

		print $fh "# MX for $z->{name}\n";

		while ( my $mx = $sth->fetchrow_hashref() ) {
                        my $domain_name = $self->{_domains}->{$mx->{domain}};

                        if ( ! $mx->{ttl} ) {
                                if ( $self->{'_config'}->{'default_mx_ttl'} < 300 ) {
                                        $mx->{ttl} = 300;
                                }
                                else {
                                        $mx->{ttl} = $self->{'_config'}->{'default_mx_ttl'};
                                }
                        }

			print $fh "\@$mx->{name}.$domain_name\::$mx->{server}:$mx->{priority}:$mx->{ttl}\n";
		}

		print $fh "\n";
	}

	print $fh "\n\n";
}

sub print_cname {
	my ($self, $fh) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT DISTINCT zone FROM zone_cname ORDER BY zone};
	my $zone_sth = $dbh->prepare($sql);
	$zone_sth->execute();

	$sql = qq{SELECT * FROM zone_cname WHERE zone=?};
	my $sth = $dbh->prepare($sql);

	print $fh "# print_cname: Listing of all of the CNAME records for the domains\n";
	print $fh "#   we administrate\n";
	
	while ( my ($zone) = $zone_sth->fetchrow() ) {
		$sth->execute($zone);
		my $z = $self->get_zone($zone);

		print $fh "# CNAME's for $z->{name}\n";

		while ( my $cname = $sth->fetchrow_hashref() ) {
			my $domain_name = $self->{_domains}->{$cname->{domain}};

			if ( ! $cname->{ttl} ) {
				if ( $self->{'_config'}->{'default_cname_ttl'} < 300 ) {
                                	$cname->{ttl} = 300;
                        	}
                        	else {
                                	$cname->{ttl} = $self->{'_config'}->{'default_cname_ttl'};
       	                	}
			}

			print $fh "C$cname->{name}.$domain_name\:$cname->{server}:$cname->{ttl}\n";
		}

		print $fh "\n";
	}

	print $fh "\n\n";
}

sub print_host {
	my ($self, $fh) = @_;

	my $hosts = $self->get_all_hosts();

	print $fh "# print_host: These are all of the hosts we have record of\n";
	print $fh "#\n";

	foreach my $host (@{$hosts}) {
		if ( ! $host->{ttl} ) {

			# this should never be lower than 300
			if ( $self->{'_config'}->{'default_host_ttl'} < 300 ) {
				$host->{ttl} = 300;
			}
			else {
				$host->{ttl} = $self->{'_config'}->{'default_host_ttl'};
			}

		}

		print $fh "=$host->{name}.$host->{domain}:$host->{ip}:$host->{ttl}\n";
	}

	print $fh "\n\n";
}


# This will grab all pointers and print them out as either reverse
#  or forward depending on 'type'
sub print_ptr {
	my ($self, $fh) = @_;

	my $dbh = $self->dbh();

        my $sql = qq{SELECT * FROM pointer WHERE enabled=1 ORDER BY type};
        my $ptr_sth = $dbh->prepare($sql);
        $ptr_sth->execute();

        print $fh "# print_ptr: These are all forward/reverse pointers we know of\n";
        print $fh "#\n";

	while ( my $ptr = $ptr_sth->fetchrow_hashref() ) {
		my $ip_addr = inet_ntoa(pack N => $ptr->{ip});

                if ( ! $ptr->{ttl} ) {

                        # this should never be lower than 300
                        if ( $self->{'_config'}->{'default_host_ttl'} < 300 ) {
                	        $ptr->{ttl} = 300;
                        }
                        else {
                       		$ptr->{ttl} = $self->{'_config'}->{'default_host_ttl'};
                        }
                }

		if ( $ptr->{'type'} eq 'forward' ) {
			print $fh "+$ptr->{hostname}:$ip_addr:$ptr->{ttl}\n";
		}
		elsif ( $ptr->{'type'} eq 'reverse' ) {
			my ($one,$two,$three,$four) = split(/\./, $ip_addr);
			print $fh "^$four.$three.$two.$one.in-addr.arpa:$ptr->{hostname}:$ptr->{ttl}\n";
		}
        }

        print $fh "\n\n";
}

# We only print the NS records for things that don't have SOA records.
# #   These are usually things we delegate off to other machines/hosts.
# #
sub print_ns {
	my ($self, $fh) = @_;

	my $dbh = $self->dbh();
	my $sql = qq{SELECT nameserver.name,nameserver.domain FROM nameserver 
			LEFT JOIN soa ON
			nameserver.domain=soa.domain WHERE soa.id is NULL};
	my $sth = $dbh->prepare($sql);
	$sth->execute();
	print $fh "# print_ns: The following are NS records of domains we\n";
	print $fh "#   delegate to other servers\n";

	while ( my ($server,$domain) = $sth->fetchrow() ) {
		my $domain_name = $self->get_domain($domain);
		print $fh ".$domain_name\::$server:86400::\n";
	}

	print $fh "\n\n";
}

# This will grab all of the SOA's and print them accordingly to
#   the tinydns data format
#
sub print_soa {
	my 	($self, $fh) = @_;

	my $dbh = $self->dbh();

	my $sql = qq{SELECT * FROM soa};
	my $sth = $dbh->prepare($sql);
	$sth->execute();

	# now print the name server records for these this domain
	$sql = qq{SELECT * FROM nameserver WHERE domain=?};
	my $ns_sth = $dbh->prepare($sql);

	print $fh "# print_soa: These are the soa records for all of the\n";
	print $fh "#   domains we administer\n";

	while ( my $soa = $sth->fetchrow_hashref() ) {
		my $domain = $self->get_domain($soa->{domain});

		if ( $soa->{delegated} eq 'true' ) {
			print $fh "# Delegation of $domain\n";
		}
		else {
			print $fh "# soa & ns records for $domain\n";
			print $fh "Z$domain:$soa->{primary_master}:$soa->{hostmaster}::$soa->{refresh}:$soa->{retry}:$soa->{expire}:$soa->{ttl}:$soa->{ttl}::\n";
		}

		# now print the name server records for these this domain
		$ns_sth->execute($soa->{domain});

		while ( my $ns = $ns_sth->fetchrow_hashref() ) {
			# eww ... otherwise perl thinks its dereferencing something ugly
			if ( $soa->{delegated} eq 'true' ) {
				print $fh "\&$domain\:$ns->{name}\n";
			}
			# print a delegation out
			else {
				print $fh ".$domain\:\:$ns->{name}:86400::\n";
			}

			# if they have a glue component then use it
			if ( $ns->{glue} ) {
				my  $glue_ip = inet_ntoa(pack N => $ns->{glue});
				print $fh "+$ns->{name}\:\:$glue_ip\n";
			}
		}

		print $fh "\n";
	}

	print $fh "\n\n";
}

#####################################################################
# End TINYDNS Output functions
#####################################################################

#####################################################################
# Start BIND Output functions
#####################################################################

sub build_bind_config {
        my ($self, $fh) = @_;

        my $dbh = $self->dbh();

        my $sql = qq{SELECT * FROM soa};
        my $sth = $dbh->prepare($sql);
        $sth->execute();

        while ( my $soa = $sth->fetchrow_hashref() ) {
        	my $domain = $self->get_domain($soa->{domain});
		
		next if $domain eq '';

		print $fh qq{zone "$domain" \{\n};
		print $fh qq{\ttype slave;\n};
        	print $fh qq{\tfile "/etc/bind/zones/$domain";\n};
        	print $fh qq{\tmasters \{\n};
		print $fh "\t\t" . $self->{'_config'}->{'zone_master'} . ";\n\t};\n";

		if ( $domain =~ /orst.edu|oregonstate.edu|193.128.in-addr.arpa/ ) {
        		print $fh qq{\talso-notify \{\n};
			print $fh "\t\t128.193.54.1;\n\t};\n";
		}

		print $fh "};\n\n";
	}
}

#####################################################################
# End BIND Output functions
#####################################################################

#####################################################################
# Start DNSCACHE Output functions
#####################################################################

sub build_dnscache_config {
		# fdir is the output dir
		# tdir is the template dir
        my ($self, $fdir, $tdir) = @_;

        my $dbh = $self->dbh();

        my $sql = qq{SELECT * FROM soa};
        my $sth = $dbh->prepare($sql);
        $sth->execute();

		# See if the dnscache dir is there or not
		# if it is remove all current entries
		# if it isn't make the directory
		if (-d $fdir) {
			unlink(glob($fdir . '/*'));
		} else {
			mkdir($fdir);
		}

		#
		# Find the domains that we are a SOA for and create a server entry
		# that points to the internal interface
		#
        while ( my $soa = $sth->fetchrow_hashref() ) {
        	my $domain = $self->get_domain($soa->{domain});
		
			next if $domain eq '';

			open(SERVER, "+>". $fdir . $domain);
			print SERVER "127.0.0.1";
			close(SERVER);
		}
	
		#
		# Copy the templates over
		#
		foreach my $file (glob("$tdir/*")) {
			copy($file,$fdir.'/');
		}
}

#####################################################################
# End DNSCACHE Output functions
#####################################################################



1;
__END__
# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

Local::Maintain - Website architecture support module

=head1 SYNOPSIS

  use Local::Maintain;
  new Local::Maintain '';
  
later...

  $v = get Local::Maintain '';
  $v->news();

=head1 DESCRIPTION

This module consolidates all of the methods that are required for 

=head1 METHODS



=over

=item new

 $v = Local::Maintain->new( '', '' )

Create a new Local::Maintain object instance and store this
away so that it can be retrieved later with the B<get> method.

The database is optional and defaults to a lower case version
of the vertical name.

=item get

 $v = Local::Maintain->get('');

Return the object instance associated with the vertical name.
Classes derived from Local::Maintain may return $v blessed
into the derived class name.

For example, when called from Local::Maintain::XML, $v will be
blessed into the Local::Maintain::XML package.

The get does not test the database connection (unlike eSite) that
is left to the B<dbh> method.

=item name

 $name = $v->name;

Return the name that the vertical object instance was created with.

=item dbh

 $dbh = $v->dbh( dbd      => 'mysql',
                 databse  => 'vertical'
                 host     => 'localhost',
                 port     => '3306',
                 username => 'user',
                 password => 'password' );

Get the database handle for this vertical instance. This method checks
the integrity of the database handle and attempts to reconnect to the
database if possible.

A modified form of the DBI DSN is used to store the database handle.
This DSN does not include the information about the database being connected
to only the driver, host, and port. This allows multiple databases
running on the same server to share a connection.

All parameters are optional and have the follwing defaults:

  dbd       mysql
  host      localhost
  port      3306
  username  vertical
  password  *****

The database that is switched to as part of this call defaults to
the object instance database provided when the instance was created.
A different database can be specified with a B<database> parameter.

=back

=head1 AUTHOR

Owen Stenseth

=head1 SEE ALSO

perl(1).

=cut


> VLAN to dhcp's shared-network
> -----------------------------
>
>          Key: MNT-1361
>          URL: http://bugs.osuosl.org/browse/MNT-1361
>      Project: Maintain
>         Type: Improvement
>   Components: Build Scripts
>     Versions: 2.4.0
>     Reporter: James O'Dell
>     Assignee: Danny Robert

>
>
> Shouldn't Maintain realize that if there are 2 or more subnets are on the same VLAN
> that the DHCP server should be configured with a "shared-network" directive?
> for example
> VLAN1
>    -> 10.10.0.0/16
>    -> 192.168.1.0/24
> #dhcpd.conf.data
> shared-network VLAN1 {
>    subnet 10.10.0.0 netmask 255.255.0.0 {
>       ...
>       match hardware;
>    }
>    subnet 192.168.1.0 {
>       ...
>       allow unknown hosts;
>    }
> }
>   
> I noticed the commented out "shared-network" in dhcpd.conf.footer.
> If this the only alternative?

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://bugs.osuosl.org/secure/Administrators.jspa
-
If you want more information on JIRA, or have a bug to report see:
   http://www.atlassian.com/software/jira



More information about the Maintain-dev mailing list