#!/usr/bin/perl

use lib '/home/tracker';
use DBI;
use Net::SNMP;
use Socket;
use tracker;
use IO::Handle;
autoflush STDOUT 1;
autoflush STDERR 1;

%oids = (
	sysObjectID =>			'1.3.6.1.2.1.1.2.0',
	sysName =>			'1.3.6.1.2.1.1.5.0',
	IParp =>			'1.3.6.1.2.1.4.22.1.2',
	dot1dBridge =>			'1.3.6.1.2.1.17',
	dot1dTpFdbAddress =>		'1.3.6.1.2.1.17.4.3.1.1',
	dot1dTpFdbPort =>		'1.3.6.1.2.1.17.4.3.1.2',
	dot1dBasePortIfIndex =>		'1.3.6.1.2.1.17.1.4.1.2',
	ifMIB =>			'1.3.6.1.2.1.31',
	ifName =>			'1.3.6.1.2.1.31.1.1.1.1',
	entLogicalTable =>		'1.3.6.1.2.1.47.1.2.1',
	entLogicalEntry =>		'1.3.6.1.2.1.47.1.2.1.1',
	entLogicalIndex =>		'1.3.6.1.2.1.47.1.2.1.1.1',
	entLogicalDescr =>		'1.3.6.1.2.1.47.1.2.1.1.2',
	entLogicalType =>		'1.3.6.1.2.1.47.1.2.1.1.3',
	entLogicalCommunity =>		'1.3.6.1.2.1.47.1.2.1.1.4',
	cdpMIB =>			'1.3.6.1.4.1.9.9.23',
	cdpCacheEntry =>		'1.3.6.1.4.1.9.9.23.1.2.1.1',
	ciscoVtp =>			'1.3.6.1.4.1.9.9.46',
	vtpVlanName =>			'1.3.6.1.4.1.9.9.46.1.3.1.1.4',
	vtpTrunkStatus =>		'1.3.6.1.4.1.9.9.46.1.6.1.1.14',
	vlanMembership =>		'1.3.6.1.4.1.9.9.68',
	vmVlan =>			'1.3.6.1.4.1.9.9.68.1.2.2.1.2',
	);


@CdpCacheEntryAttributes = ( qw(
undefined
IfIndex
DeviceIndex
AddressType
Address 
Version
DeviceId
DevicePort
Platform
Capabilities 
VTPMgmtDomain
NativeVLAN
Duplex
));


#connect to the database
unless ($dbconn = tracker::conndb() )
	{ die "Unable to connect to Database" }


#We have lists:active, discovered, ignored, inactive
#active, and discovered devices should be used for seeding.
#inactive devices aren't added to any list.
#Ignored devices populate the ignore list

#Get the configuration variables from the database
my $sth = $dbconn->prepare("SELECT name, value FROM config ORDER BY name;");
$sth->execute or die "Database select failed:" .  $dbconn->errstr;
while (@row = $sth->fetchrow_array)
	{
	$config{$row[0]} = $row[1];
	if ( $row[0] =~ /^snmp_string/ )
		{ push @snmp_strings, $row[1] }
	}

#Use the devices in tracker already as starting points
$sth = $dbconn->prepare("SELECT name, address, snmp_ro_string, " . 
	"cached_snmp_string, object_id, cdp_id, status, ignore_ports FROM devices;");
$sth->execute or die "Database select failed:" .  $dbconn->errstr;

while (@row = $sth->fetchrow_array)
	{
	my $hashref = {
		name => $row[0],
		address => $row[1],
		snmp_ro_string => $row[2],
		cached_snmp_string => $row[3],
		object_id => $row[4],
		cdpId => $row[5],
		status => $row[6],
		ignore_ports => $row[7]
		};
		
	#keep all of the devices in the DB on a list, so we can compare when we are done
	push @started_with, $hashref;
	#if the device is not set to ignore, use it to seed from
	if ($hashref->{status} != 3)
		{ push @seed, $hashref }
	#ignore everything else
	else { push @ignored, $hashref }
	}

&DEBUG("****** Starting Phase 1 *****************************\n");
#pre load cdp info for device from the seed
while (@seed)
	{
	my $device_entry = pop(@seed);
	my ($community, $device, %neighboordata, @strings, $snmp_string, $oid);
	#take the name out of the hash and put it in an (easier to type) scalar variable
	my $name = $device_entry->{"name"};
	my $address = $device_entry->{"address"};
	&DEBUG("\n\ttrying $name ($address)\n");
	
	#contruct a list of snmp_strings, in the order we'll try them
	#first try the cached snmp_string, if there is one
	if ($device_entry->{cached_snmp_string}) { push @strings, $device_entry->{cached_snmp_string} }
	#if there is a hard set string that is not the same as the cached one, add it
	if ( $device_entry->{snmp_ro_string} && ($device_entry->{snmp_ro_string} ne $device_entry->{cached_snmp_string}) )
		{ push @strings, $device_entry->{snmp_ro_string} }
	#add any global strings that aren't already on the list
	foreach $snmp_string (@snmp_strings)
		{
		if (!on_list(\@strings, $snmp_string) )
			{ push @strings, $snmp_string }
		}
	#try each string in turn
	foreach $snmp_string (@strings)
		{
		if (can_snmp($address, $snmp_string))
			{
			$community = $snmp_string;
			last
			}
		}
	unless ($community)
		{ next }
	

	
	&DEBUG("\tusing $name $address $community\n");
	if ($community ne $device_entry->{cached_snmp_string})
		{
		&DEBUG("\t\tWhich differs from cached string\n");
		$device_entry->{cached_snmp_string} = $community;
		$device_entry->{needsUpdate} = 1
		}
		

	
	my $snmp = Net::SNMP->session( -hostname => $address, -community => $community, version => '1', -translate => 0);
	my ($cdpCache) = $snmp->get_table($oids{cdpCacheEntry});
	foreach $oid (keys(%$cdpCache))
		{
		#pull the name of the attribute, the ifIndex and a cdp neighboor index out of the name of the value we got
		($Attribute,$IfIndex,$DevIndex) = ( $oid =~ /^\Q$oids{cdpCacheEntry}.\E(\d*)\.(\d*)\.(\d*)/ );
		#put the corresponding value into the hash
		$neighboordata{"$IfIndex.$DevIndex"}->{$CdpCacheEntryAttributes[$Attribute]} = $cdpCache->{$oid};
		}
	$snmp->close;
	&DEBUG("\tFound " . scalar(keys(%neighboordata)) . "neihgboors\n");
	#iterate through all the neigboors we have found	
	foreach $neigh (keys(%neighboordata))
		{
		#some local vars
		my ($ipaddr, $ipaddr_packed, $hashref, $community2, $device_entry2, $sysName, $sysName2, $cdpId2, $FQDN, $address, $name);
		
		my $cdpId = $neighboordata{$neigh}->{"DeviceId"};
		&DEBUG("\t\t$cdpId ");
		#if the device is on one of the lists, just move on
		if (device_on_list(\@discovered,$cdpId) || device_on_list(\@ignored,$cdpId))
			{ &DEBUG("Found already\n"); next }
		
		#and the type of the address is 1 (IP)	
		if ( ($neighboordata{$neigh}->{"AddressType"} == 1))
			{ $ipaddr_packed = $neighboordata{$neigh}->{"Address"} }
		#else don't bother, 
		else 
			{&DEBUG("non IP network address\n"); next }
		
		#address is IP, convert it to dotted quad if its the right length
		if ((length($ipaddr_packed) == 4))
			{ $ipaddr = inet_ntoa($ipaddr_packed) }
		#IP addresses are 4 octets, so this shouldn't happen.  I have seen it happen though
		else 
			{ &DEBUG("IP address not 4 octets\n"); next }
		
		if ($ipaddr == '0.0.0.0')
			{ &DEBUG("Invalid IP address\n"); next }

		
		#try to find an snmp community string that works for this device
		#first try the global default snmp_strings from the tracker config
		foreach $snmp_string (@snmp_strings)
			{
			if ( can_snmp($ipaddr, $snmp_string) )
				{
				$community2 = $snmp_string;
				last
				}
			} 
		unless ($community2)
		#That didn't work, try some other stuff
			{
			my (@strings, $snmp_string);
			#Is there a device in the DB with this cdpid?
			if ($device_entry2 = device_on_list(\@started_with, $cdpId) )
				{
				#if there is , try its hard set and cached snmp strings.
				if ($device_entry2->{snmp_ro_string} && !on_list(\@snmp_strings, $device_entry2->{cached_snmp_string}))
					{ push @strings, $device_entry2->{snmp_ro_string} }
				if (	$device_entry2->{cached_snmp_string} &&
					($device_entry2->{cached_snmp_string} ne $device_entry2->{cached_snmp_string}) &&
					!on_list(\@snmp_strings, $device_entry2->{cached_snmp_string})
					)
					{ push @strings, $device_entry2->{cached_snmp_string} }
				}
			#also try cached and hard set snmp strings from the device we are discovering from
			unless ( on_list(\@snmp_strings, $community) ||
				on_list(\@strings, $community)
				)
				{ push @strings, $community }
			if ( 	$device_entry->{snmp_ro_string} &&
				(!on_list(\@snmp_strings, $device_entry->{snmp_ro_string}) &&
				!on_list(\@strings, $device_entry->{snmp_ro_string} ) )
				)
				{ push @strings, $device_entry->{snmp_ro_string} }
			foreach $snmp_string (@strings)
				{
				if ( can_snmp($ipaddr, $snmp_string ) )
					{
					$community2 = $snmp_string;
					last
					}
				}
			}
		unless ($community2)
			{ &DEBUG(" [Failed to find working community string\@$ipaddr]\n"); next }
		&DEBUG(" [Using $community2\@$ipaddr] ");
		#get the sys.sysName variable from the address we got in the cdp advertisement
		($sysName) = lc(quick_get($oids{sysName}, $ipaddr, $community2 ));
		#try to get the FQDN based on the sysname it told us 
		if (($FQDN) = gethostbyname($sysName))
			{
			#get the sys.sysName from the FQDN we got
			($sysName2) = lc(quick_get($oids{sysName}, $FQDN, $community2))
			}
		#if the sysname resolved in DNS to a valid entry for this device
		#add it to the database using that DNS entry for the name and address
		if ($sysName)
			{
			if ($sysName eq $sysName2)
				{ $name = $address = lc($FQDN) }
			else
				{
				$name = lc($sysName);
				$address = $ipaddr;
				}
			}
		else
			{
			if (($name) = gethostbyaddr($ipaddr_packed, AF_INET))
				{ $address = $ipaddr }
			else
				{
				$name = 'ip ' . $ipaddr;
				$address = $ipaddr;
				}
			}
		&DEBUG(" [name=$name address=$address] ");
		
		#It is possible to discover the same device twice with two different cdp ID's
		#if this device (by name) is already on the discvered list, skip over it 
		if ( name_on_list(\@discovered, $FQDN) || name_on_list(\@ignored, $FQDN) )
			{ &DEBUG(" [already known by name, ignoring] "); next }
			
		#before we create a new device_entry for this newly discovered device we check the list
		#of devices we got from the DB.  If it was from the DB we re-use the original entry, because
		#it may have more information.
		if ( $hashref = name_on_list(\@started_with, $FQDN) )
			{
			&DEBUG(" [on started_from list] ");
			if ($hashref->{status} != 3)
				{
				my $i;
				#look for it on the seed list, and splice it out if it is there
				for ($i = 0; $i < scalar(@seed); $i++)
					{
					if ($seed[$i] == $hashref)
						{ &DEBUG(" [removed from seed list] ");splice @seed, $i, 1 }
					}
				unless ($cdpId eq $hashref->{cdpId})
					{
					# if the cdpid is different, update it and flag that the record should be updated in the DB
					&DEBUG(" [CdpId's differ] ");
					$hashref->{cdpId} = $cdpId;
					$hashref->{needsUpdate} = 1;
					}
				unless ($community2 eq $hashref->{cached_snmp_string})
					{
					&DEBUG(" [snmp strings differ] ");
					$hashref->{cached_snmp_string} = $community2;
					$hashref->{needsUpdate} = 1;
					}
				unless ($address eq $hashref->{address})
					{
					&DEBUG(" [addresses differ] ");
					$hashref->{address} = $address;
					$hashref->{needsUpdate} = 1;
					}
				push @discovered, $hashref;
				}
			}
		else
			{
			#contruct a refernece to an anonymous hash with the values representing this discovered device
			#the important one is the name, which is the FQDN or ip address that will be used to manage this device			
			my $hashref = 
				{
				name => $name,
				address => $address,
				cdpId => $cdpId,
				foundFrom => $device,
				foundFromPort => $neighboordata{$neigh}->{"DevicePort"},
				cached_snmp_string => $community2
				};
			
			push @discovered, $hashref;
			}
		&DEBUG("\n");
		}
	}

&DEBUG("****** Starting Phase 2 *****************************\n");
while (@discovered)
	{
	my ($community, $device, %neighboordata);
	#take a device off the discovered list to talk to
	my $device_entry = pop(@discovered);
	#take the name out of the hash and put it in an (easier to type) scalar variable
	my $device = $device_entry->{"name"};
	my $address = $device_entry->{"address"};
	&DEBUG("\ttrying $device\n");
	

	my $snmp = Net::SNMP->session( -hostname => $address, -community => $device_entry->{cached_snmp_string},
		version => '1', -translate => 0);
	my ($cdpCache) = $snmp->get_table($oids{cdpCacheEntry});
	foreach $oid (keys(%$cdpCache))
		{
		#pull the name of the attribute, the ifIndex and a cdp neighboor index out of the name of the value we got
		($Attribute,$IfIndex,$DevIndex) = ( $oid =~ /^\Q$oids{cdpCacheEntry}.\E(\d*)\.(\d*)\.(\d*)/ );
		#put the corresponding value into the hash
		$neighboordata{"$IfIndex.$DevIndex"}->{$CdpCacheEntryAttributes[$Attribute]} = $cdpCache->{$oid};
		}
	$snmp->close;
	
	&DEBUG("\t\tFound " . scalar(keys(%neighboordata)) . "neihgboors\n");
	#iterate through all the neigboors we have found	
	foreach $neigh (keys(%neighboordata))
		{
		#some local vars
		my ($ipaddr, $ipaddr_packed, $hashref, $community2, $device_entry2, $sysName, $sysName2, $cdpId2, $FQDN, $address, $name);
		
		my $cdpId = $neighboordata{$neigh}->{"DeviceId"};
		&DEBUG("\t\t$cdpId ");

		my ($ifIndex) = ( $neigh =~ /^(\d+)\.\d+$/ );
		$device_entry->{ports_with_neighboors}->{$ifIndex} = 1;
		
		#if the device is on one of the lists, just move on
		if (device_on_list(\@discovered,$cdpId) || device_on_list(\@done,$cdpId) || device_on_list(\@ignored,$cdpId))
			{ &DEBUG("[Already found]\n "); next }
		
		#and the type of the address is 1 (IP)	
		if ( ($neighboordata{$neigh}->{"AddressType"} == 1))
			{ $ipaddr_packed = $neighboordata{$neigh}->{"Address"} }
		#else don't bother, 
		else 
			{ &DEBUG("[Non IP network address]\n ");next }
		
		#address is IP, convert it to dotted quad if its the right length
		if ((length($ipaddr_packed) == 4))
			{ $ipaddr = inet_ntoa($ipaddr_packed) }
		#if its not 4 octets its useless, but this oughtn't happen
		else 
			{ &DEBUG("[Bad IP address]\n ");next }
			
		if ($ipaddr == '0.0.0.0')
			{ &DEBUG("Invalid IP address\n"); next }
		&DEBUG(" [neigboor IP=$ipaddr] ");
		foreach $snmp_string (@snmp_strings)
			{
			if ( can_snmp($ipaddr, $snmp_string ) )
				{
				$community2 = $snmp_string;
				last
				}
			} 
		unless ($community2)
		#That didn't work, try some other stuff
			{
			my (@strings, $snmp_string);
			#Is there a device in the DB with this cdpid?
			if ($device_entry2 = device_on_list(\@started_with, $cdpId) )
				{
				#if there is , try its hard set and cached snmp strings.
				if ($device_entry2->{snmp_ro_string} && !on_list(\@snmp_strings, $device_entry2->{cached_snmp_string}))
					{ push @strings, $device_entry2->{snmp_ro_string} }
				if (	$device_entry2->{cached_snmp_string} &&
					$device_entry2->{cached_snmp_string} ne $device_entry2->{cached_snmp_string} &&
					!on_list(\@snmp_strings, $device_entry2->{cached_snmp_string})
					)
					{ push @strings, $device_entry2->{cached_snmp_string} }
				}
			#also try cached and hard set snmp strings from the device we are discovering from
			if ( 	!on_list(\@snmp_strings, $device_entry->{cached_snmp_string}) &&
				!on_list(\@strings, $device_entry->{cached_snmp_string})
				)
				{ push @strings, $device_entry->{cached_snmp_string} }
			if ( 	$device_entry->{snmp_ro_string} &&
				!on_list(\@snmp_strings, $device_entry->{snmp_ro_string}) &&
				!on_list(\@strings, $device_entry->{snmp_ro_string})
				)
				{ push @strings, $device_entry->{snmp_ro_string} }
			foreach $snmp_string (@strings)
				{
				if ( can_snmp($ipaddr, $snmp_string ) )
					{
					$community2 = $snmp_string;
					last
					}
				}
			} 
		unless ($community2)
			{ &DEBUG(" [Failed to find working community string\@$ipaddr]\n ");next }
		&DEBUG(" [Using $community2\@$ipaddr] ");

		
		#get the sys.sysName variable from the address we got in the cdp advertisement
		($sysName) = lc(quick_get($oids{sysName}, $ipaddr, $community2 ));
		#try to get the FQDN based on the sysname it told us 
		if (($FQDN) = gethostbyname($sysName))
			{
			#get the sys.sysNamefrom the FQDN we got
			($sysName2) = lc(quick_get($oids{sysName}, $FQDN, $community2))
			}
		#if DNS and the sysname agree, put it in with name and address being the DNS name
		#otherwise use the sysname for the name and the IP address as address
		if (($sysName) && ($sysName eq $sysName2))
			{
			$name = $address = lc($FQDN)
			}
		else
			{
			$name = lc($sysName);
			$address = $ipaddr;
			}
		&DEBUG(" [name=$name address=$address] ");

		#It is possible to discover the same device twice with two different cdp ID's
		#if this device (by name) is already on the discvered list, skip over it 
		if ( name_on_list(\@discovered, $FQDN) || name_on_list(\@ignored, $FQDN) || name_on_list(\@done, $FQDN))
			{ &DEBUG(" [already known by name, ignoring] "); next }
		
		#before we create a new device_entry for this newly discovered device we check the list
		#of devices we got from the DB.  If it was from the DB we re-use the original entry, because
		#it may have more ingormation.
		if ( $hashref = name_on_list(\@started_with, $FQDN) )
			{
			&DEBUG("[Found on started_from list] ");
			if ($hashref->{status} != 3)
				{
				unless ($cdpId eq $hashref->{cdpId})
					{
					# if the cdpid is different, update it and flag that the record should be updated in the DB
					&DEBUG(" [CdpId's differ] ");
					$hashref->{cdpId} = $cdpId;
					$hashref->{needsUpdate} = 1;
					}
				unless ($community2 eq $hashref->{cached_snmp_string})
					{
					&DEBUG(" [snmp strings differ] ");
					$hashref->{cached_snmp_string} = $community2;
					$hashref->{needsUpdate} = 1;
					}
				unless ($address eq $hashref->{address})
					{
					&DEBUG(" [addresses differ] ");
					$hashref->{address} = $address;
					$hashref->{needsUpdate} = 1;
					}
				push @discovered, $hashref;
				}
			}
		else
			{
			#contruct a refernece to an anonymous hash with the values representing this discovered device
			#the important one is the name, which is the FQDN or ip address that will be used to manage this device			
			my $hashref = 
				{
				name => $name,
				address => $address,
				cdpId => $cdpId,
				foundFrom => $device,
				foundFromPort => $neighboordata{$neigh}->{"DevicePort"},
				cached_snmp_string => $community2
				};
			
			push @discovered, $hashref;
			}
		&DEBUG("\n");	
		}
	#we are done with all of this devices neighboors, add it to the done list	
	push(@done,$device_entry)
	}



&DEBUG("***** Final phase ************************\n");
foreach $device_entry (@done)
	{
	&DEBUG($device_entry->{name} . "\n");
	&DEBUG(hashdump($device_entry) . "\n");
	my ($community, $object_id, $status, $error);
        
	if ( $config{'cdp_initial_state'} eq 'dns' )
	    {
	    if ($device_entry->{name} eq $device_entry->{address})
		{
		$status = 1;
		$error = "NULL";
		}
	    else
		{
		$status = 2;
		$error = $dbconn->quote("Device name and DNS name do not match");
		}
	    }
	else
	    {
	    $status = $config{'cdp_initial_state'};
	    }
        
	
	$object_id = quick_get($oids{sysObjectID}, $device_entry->{address}, $device_entry->{cached_snmp_string});
	if ($object_id ne $device_entry->{object_id})
		{
		&DEBUG(" [objectID differs] ");
		$device_entry->{object_id} = $object_id;
		$device_entry->{needsUpdate} = 1
		}
	
	if ( $config{'cdp_ignore_ports'} == 1 )
		{
		my $ignore_ports = join(':', sort(keys( %{$device_entry->{ports_with_neighboors}} )));
		if ($ignore_ports ne $device_entry->{ignore_ports})
			{
			$device_entry->{ignore_ports} = $ignore_ports;
			$device_entry->{needsUpdate} = 1;
			&DEBUG(" [ignore_ports differs] ");
			}
		}
			
	if (!name_on_list(\@started_with, $device_entry->{name}))
		{
		&DEBUG(" [Adding to DB] ");
		my ($type, $description);
		my $select = "SELECT description FROM cisco_products WHERE snmp_oid = " .
			$dbconn->quote(tracker::dbescape($object_id)) . ";";
		my $sth = $dbconn->prepare($select);
		$sth->execute or print("Discovery:Database select failed:" . $dbconn->errstr . "\n");
		
		unless (($description) = $sth->fetchrow_array)
			{
			opendir DIR, $tracker::home_dir . "/cisco_mibs_oid/" or die $!;
			foreach $file (readdir(DIR))
				{
				open FILE, $tracker::home_dir . "/cisco_mibs_oid/" . $file or die $!;
				while (<FILE>)
					{
					if (/\"(.*)\"\s+\"$object_id\"/) #"
						{ $description = $1; last }
					}
				close FILE;
				if ($description)
					{
					my $insert = "INSERT INTO cisco_products (snmp_oid, description) VALUES (" .
						$dbconn->quote($object_id) . ',' . $dbconn->quote($description) . ");";
					my $sth = $dbconn->prepare($insert);
					$sth->execute or print("Discovery:Database insert failed:" . $dbconn->errstr . "\n");
					$dbconn->commit or print("Discovery:Database commit transaction failed:" . $dbconn->errstr . "\n");
					last;
					}
				}
			closedir DIR;
			}
		
		
		print("Discovered $device_entry->{name} $description port $device_entry->{foundFromPort} from $device_entry->{foundFrom}.\n");
		unless (on_list(\@snmp_strings, $device_entry->{cached_snmp_string}))
			{ $device_entry->{snmp_ro_string} = $device_entry->{cached_snmp_string} }
		#Untrusted values (from the resolver and SNMP) get escaped filtered through dbescape in case they
		#contain non-printable binary characters.
		my $insert = "INSERT INTO devices (name, address, snmp_ro_string, cached_snmp_string, " .
			"object_id, cdp_id, ignore_ports, status, error ) VALUES (" .
			$dbconn->quote(tracker::dbescape($device_entry->{name})) . "," .
			$dbconn->quote($device_entry->{address}) . "," .
			$dbconn->quote($device_entry->{snmp_ro_string}) . "," .
			$dbconn->quote($device_entry->{cached_snmp_string}) . "," .
			$dbconn->quote(tracker::dbescape($device_entry->{object_id})) . "," .
			$dbconn->quote(tracker::dbescape($device_entry->{cdpId})) . "," .
			$dbconn->quote($device_entry->{ignore_ports}) .
			", $status, $error);";
		print $insert, "\n";
		$sth = $dbconn->prepare($insert);
		$sth->execute or print("Discovery:Database insert failed:" . $dbconn->errstr . "\n" );
		$dbconn->commit or print("Discovery:Database commit transaction failed:" . $dbconn->errstr . "\n");
		}
	elsif ($device_entry->{needsUpdate})
		{
		my ($ignore_ports);
		&DEBUG(" [Updating existing record] ");
		my $update;
		if ($device_entry->{ignore_ports})
			{ $ignore_ports = $dbconn->quote($device_entry->{ignore_ports}) }
		else
			{ $ignore_ports = 'NULL' }
		$update = "UPDATE devices SET " . 
			"cdp_id = " . $dbconn->quote(tracker::dbescape($device_entry->{cdpId})) . 
			", object_id = " . $dbconn->quote(tracker::dbescape($device_entry->{object_id})) .
			", address = " . $dbconn->quote(tracker::dbescape($device_entry->{address})) .
			", cached_snmp_string = " . $dbconn->quote(tracker::dbescape($device_entry->{cached_snmp_string})) .
			", ignore_ports = $ignore_ports " . 
			" WHERE name = " . $dbconn->quote(tracker::dbescape($device_entry->{name})) . ";";
		print $update, "\n";
		my $sth = $dbconn->prepare($update);
		$sth->execute or print("Discovery:Database update failed:" . $dbconn->errstr . "\n");
		$dbconn->commit or print("Discovery:Database commit transaction failed:" . $dbconn->errstr . "\n");
		}
	&DEBUG("\n");
	}



sub quick_get
	{
	my ($oid, $host, $community) = @_;
	my ($snmp);
	unless ($snmp = Net::SNMP->session( -hostname => $host, -community => $community, -timeout => 1) )
		{ return undef }
	my ($href) = $snmp->get_request($oid);
	$snmp->close;
	return $href->{$oid}
	}	
	
sub can_snmp
	{
	my ($target, $community) = @_;
	if (quick_get($oids{sysName}, $target, $community)) { return 1 } else { return 0 }
	}


sub name_on_list 
	{
	my ($list_ref, $name) = @_;
	my $hash_ref;
	foreach $hash_ref (@$list_ref)
		{
		if ($hash_ref->{name} eq $name) { return $hash_ref }
		}
	return 0
	}

sub device_on_list
	{
	my ($list_ref, $cdpId) = @_;
	my $hash_ref;
	foreach $hash_ref (@$list_ref)
		{
		if ($hash_ref->{cdpId} eq $cdpId) { return $hash_ref }
		}
	return 0
	}

sub on_list
	{
	my ($list_ref, $value) = @_;
	my $val;
	foreach $val (@$list_ref)
		{
		if ($val eq $value) { return 1 }
		}
	return 0
	}
	
sub DEBUG
	{
	#un comment to enable debug output
	#print @_
	}

sub hashdump
	{
	my $href = shift;
	my $key;
	foreach $key (keys(%$href))
		{ print $key . '->' . $href->{$key}, "\n" }
	}

