Skip to content

Commit

Permalink
Merge 29e7428 into c4b810b
Browse files Browse the repository at this point in the history
  • Loading branch information
andrzej-jankowski committed Aug 14, 2014
2 parents c4b810b + 29e7428 commit 2522dbe
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/ralph/discovery/snmp.py
Expand Up @@ -63,6 +63,18 @@ def snmp_command(
return vars


def snmp_walk(
hostname, community, oid, snmp_version='2c', timeout=1, attempts=3,
priv_protocol=cmdgen.usmDESPrivProtocol
):
transport = cmdgen.UdpTransportTarget((hostname, 161), attempts, timeout)
data = user_data(community, snmp_version, priv_protocol=priv_protocol)
gen = cmdgen.CommandGenerator()
error, status, index, values = gen.nextCmd(data, transport, oid)
if not error:
return values


def snmp_bulk(
hostname, community, oid, snmp_version='2c', timeout=1, attempts=3
):
Expand Down
122 changes: 122 additions & 0 deletions src/ralph/scan/plugins/snmp_lldp.py
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from django.conf import settings

from ralph.discovery.models import ConnectionType
from ralph.scan.errors import Error
from ralph.scan.plugins import get_base_result_template
from ralph.discovery.snmp import snmp_walk


SETTINGS = settings.SCAN_PLUGINS.get(__name__, {})


def _get_device_interfaces_map(ip_address, snmp_community, snmp_version):
items = snmp_walk(
ip_address,
snmp_community,
[int(chunk) for chunk in "1.0.8802.1.1.2.1.3.7.1.3".split(".")],
snmp_version
)
interfaces_map = {}
for item in items:
interfaces_map[unicode(item[0][0]).split(".")[-1]] = unicode(
item[0][1]
)
return interfaces_map


def _get_remote_mac_addresses_map(ip_address, snmp_community, snmp_version):
items = snmp_walk(
ip_address,
snmp_community,
[int(chunk) for chunk in "1.0.8802.1.1.2.1.4.1.1.5".split(".")],
snmp_version
)
mac_addresses_map = {}
for item in items:
mac_addresses_map[unicode(item[0][0]).split(".")[-2]] = "".join(
"{:0>2}".format(str(hex(ord(chunk)))[2:]) for chunk in item[0][1]
).upper()
return mac_addresses_map


def _get_remote_connected_ports_map(ip_address, snmp_community, snmp_version):
items = snmp_walk(
ip_address,
snmp_community,
[int(chunk) for chunk in "1.0.8802.1.1.2.1.4.1.1.7".split(".")],
snmp_version
)
ports_map = {}
for item in items:
ports_map[unicode(item[0][0]).split(".")[-2]] = unicode(item[0][1])
return ports_map


def _snmp_lldp(
ip_address, snmp_community, snmp_version, messages=[]
):
try:
interfaces_map = _get_device_interfaces_map(
ip_address, snmp_community, snmp_version
)
mac_addresses_map = _get_remote_mac_addresses_map(
ip_address, snmp_community, snmp_version
)
ports_map = _get_remote_connected_ports_map(
ip_address, snmp_community, snmp_version
)
except TypeError:
raise Error("No answer.")
except IndexError:
raise Error("Incorrect answer.")
connections = []
for item_id, mac_address in mac_addresses_map.iteritems():
details = {}
if interfaces_map.get(item_id):
details['outbound_port'] = interfaces_map.get(item_id)
if ports_map.get(item_id):
details['inbound_port'] = ports_map.get(item_id)
connection = {
'connection_type': ConnectionType.network.name,
'connected_device_mac_addresses': mac_address
}
if details:
connection['details'] = details
connections.append(connection)
return {
'system_ip_addresses': [ip_address],
'connections': connections
}


def scan_address(ip_address, **kwargs):
snmp_version = kwargs.get('snmp_version', '2c') or '2c'
if snmp_version == '3':
snmp_community = SETTINGS['snmp_v3_auth']
else:
snmp_community = str(kwargs['snmp_community'])
messages = []
result = get_base_result_template('snmp_macs', messages)
try:
info = _snmp_lldp(
ip_address,
snmp_community,
snmp_version,
messages,
)
except Error as e:
messages.append(unicode(e))
result['status'] = "error"
else:
result.update({
'status': 'success',
'device': info,
})
return result
145 changes: 145 additions & 0 deletions src/ralph/scan/tests/plugins/test_snmp_lldp.py
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import mock

from django.test import TestCase

from ralph.scan.plugins.snmp_lldp import (
_get_device_interfaces_map,
_get_remote_connected_ports_map,
_get_remote_mac_addresses_map,
_snmp_lldp,
)


class SnmpLldpPluginTest(TestCase):

def test_get_device_interfaces_map(self):
with mock.patch(
'ralph.scan.plugins.snmp_lldp.snmp_walk',
) as snmp_walk:
snmp_walk.side_effect = [
[
(("1.0.8802.1.1.2.1.3.7.1.3.1", "Gi0/1"), None),
(("1.0.8802.1.1.2.1.3.7.1.3.2", "Gi0/2"), None),
(("1.0.8802.1.1.2.1.3.7.1.3.3", "Gi0/3"), None),
(("1.0.8802.1.1.2.1.3.7.1.3.4", "Gi0/4"), None),
]
]
self.assertEqual(
_get_device_interfaces_map("10.10.10.10", "public", "2c"),
{
"1": "Gi0/1",
"2": "Gi0/2",
"3": "Gi0/3",
"4": "Gi0/4",
}
)

def test_get_remote_mac_addresses_map(self):
with mock.patch(
'ralph.scan.plugins.snmp_lldp.snmp_walk',
) as snmp_walk:
snmp_walk.side_effect = [
[
(
(
"1.0.8802.1.1.2.1.3.7.1.3.6.1",
['\xff', '\xfe', '\xfd', '\xfc', '\xfb', '\x02']
),
None
),
]
]
self.assertEqual(
_get_remote_mac_addresses_map("10.10.10.10", "public", "2c"),
{"6": "FFFEFDFCFB02"}
)

def test_get_remote_connected_ports_map(self):
with mock.patch(
'ralph.scan.plugins.snmp_lldp.snmp_walk',
) as snmp_walk:
snmp_walk.side_effect = [
[
(
(
"1.0.8802.1.1.2.1.4.1.1.7.0.33.1",
"Eth0"
),
None
),
(
(
"1.0.8802.1.1.2.1.4.1.1.7.0.42.2",
"Eth1"
),
None
),
]
]
self.assertEqual(
_get_remote_connected_ports_map("10.10.10.10", "public", "2c"),
{'33': 'Eth0', '42': 'Eth1'},
)

def test_snmp_lldp(self):
with mock.patch(
'ralph.scan.plugins.snmp_lldp._get_device_interfaces_map',
) as get_device_interfaces_map_mock, mock.patch(
'ralph.scan.plugins.snmp_lldp._get_remote_mac_addresses_map',
) as get_remote_mac_addresses_map_mock, mock.patch(
'ralph.scan.plugins.snmp_lldp._get_remote_connected_ports_map',
) as get_remote_connected_ports_map_mock:
get_device_interfaces_map_mock.return_value = {
"1": "Gi0/1",
"2": "Gi0/2",
"3": "Gi0/3",
"4": "Gi0/4",
"5": "Gi0/5",
"6": "Gi0/6",
"7": "Gi0/7",
"8": "Gi0/8",
}
get_remote_mac_addresses_map_mock.return_value = {
"2": "FFFEFDFCFB03",
"3": "FFFEFDFCFB04",
"9": "FFFEFDFCFBFF",
}
get_remote_connected_ports_map_mock.return_value = {
'2': 'Eth0',
'3': 'Eth1',
}
self.assertEqual(
_snmp_lldp("10.10.10.11", "public", "2c"),
{
'connections': [
{
'connected_device_mac_addresses': 'FFFEFDFCFBFF',
'connection_type': 'network',
},
{
'connected_device_mac_addresses': 'FFFEFDFCFB04',
'connection_type': 'network',
'details': {
'inbound_port': 'Eth1',
'outbound_port': 'Gi0/3'
}
},
{
'connected_device_mac_addresses': 'FFFEFDFCFB03',
'connection_type': 'network',
'details': {
'inbound_port': 'Eth0',
'outbound_port': 'Gi0/2'
}
}
],
'system_ip_addresses': ['10.10.10.11']
}
)
9 changes: 9 additions & 0 deletions src/ralph/settings.py
Expand Up @@ -426,6 +426,14 @@
'serial_number': 20,
},
},
'ralph.scan.plugins.snmp_lldp': {
'communities': SNMP_PLUGIN_COMMUNITIES,
'snmp_v3_auth': (SNMP_V3_USER, SNMP_V3_AUTH_KEY, SNMP_V3_PRIV_KEY),
'results_priority': {
'connections': 55,
'system_ip_addresses': 50,
},
},
'ralph.scan.plugins.idrac': {
'user': IDRAC_USER,
'password': IDRAC_PASSWORD,
Expand Down Expand Up @@ -456,6 +464,7 @@
'system_cores_count': 20,
'processors': 15,
'memory': 15,
'connections': 25,
},
},
'ralph.scan.plugins.puppet': {
Expand Down

0 comments on commit 2522dbe

Please sign in to comment.