Skip to content

Commit

Permalink
Merge pull request #3250 from Uninett/feature/portadmin-select-generi…
Browse files Browse the repository at this point in the history
…c-handler-for-ciscosmb

Select generic `SNMPHandler` for Cisco Small Business switches in PortAdmin
  • Loading branch information
lunkwill42 authored Dec 4, 2024
2 parents c57c9a1 + b6c327d commit 769d1d2
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.d/3249.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for setting access port VLANs for Cisco Small Business switches in PortAdmin (by reverting to non-Cisco-specific handler routines for this subgroup of products)
7 changes: 7 additions & 0 deletions python/nav/portadmin/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,16 @@ class ManagementHandler:
a class.
"""

VENDOR = None

def __init__(self, netbox: manage.Netbox, **kwargs):
self.netbox = netbox

@classmethod
def can_handle(cls, netbox: manage.Netbox) -> bool:
"""Returns True if this handler can handle the given netbox"""
return netbox.type and netbox.type.get_enterprise_id() == cls.VENDOR

def set_interface_description(self, interface: manage.Interface, description: str):
"""Configures a single interface's description, AKA the ifalias value"""
raise NotImplementedError
Expand Down
9 changes: 5 additions & 4 deletions python/nav/portadmin/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
from nav.portadmin.snmp.hp import HP
from nav.portadmin.napalm.juniper import Juniper

VENDOR_MAP = {cls.VENDOR: cls for cls in (Cisco, Dell, H3C, HP, Juniper)}
SUPPORTED_HANDLERS = (Cisco, Dell, H3C, HP, Juniper)
FALLBACK_HANDLER = SNMPHandler


class ManagementFactory(object):
Expand All @@ -37,9 +38,9 @@ def get_instance(cls, netbox: manage.Netbox, **kwargs) -> ManagementHandler:
if not netbox.type:
raise NoNetboxTypeError()

vendor_id = netbox.type.get_enterprise_id()
handler = VENDOR_MAP.get(vendor_id, SNMPHandler)
return handler(netbox, **kwargs)
matched_handlers = (h for h in SUPPORTED_HANDLERS if h.can_handle(netbox))
chosen_handler = next(matched_handlers, FALLBACK_HANDLER)
return chosen_handler(netbox, **kwargs)

def __init__(self):
pass
2 changes: 0 additions & 2 deletions python/nav/portadmin/snmp/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ class InvalidManagementProfileError(ManagementError):
class SNMPHandler(ManagementHandler):
"""Implements PortAdmin management functions for SNMP-enabled switches"""

VENDOR = None

QBRIDGENODES = get_mib('Q-BRIDGE-MIB')['nodes']

SYSOBJECTID = '.1.3.6.1.2.1.1.2.0'
Expand Down
13 changes: 13 additions & 0 deletions python/nav/portadmin/snmp/cisco.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
class Cisco(SNMPHandler):
"""A specialized class for handling ports in CISCO switches."""

# Cisco sysObjectIDs under this tree are not normal Cisco products and should
# probably not be handled by this handler
OTHER_ENTERPRISES = OID('.1.3.6.1.4.1.9.6')

VENDOR = VENDOR_ID_CISCOSYSTEMS

VTPNODES = get_mib('CISCO-VTP-MIB')['nodes']
Expand Down Expand Up @@ -85,6 +89,15 @@ def __init__(self, netbox, **kwargs):
self.voice_vlan_oid = '1.3.6.1.4.1.9.9.68.1.5.1.1.1'
self.cdp_oid = '1.3.6.1.4.1.9.9.23.1.1.1.1.2'

@classmethod
def can_handle(cls, netbox: manage.Netbox) -> bool:
"""Returns True if this handler can handle this netbox"""
if netbox.type and cls.OTHER_ENTERPRISES.is_a_prefix_of(
netbox.type.sysobjectid
):
return False
return super().can_handle(netbox)

@translate_protocol_errors
def get_interface_native_vlan(self, interface):
return self._query_netbox(self.vlan_oid, interface.ifindex)
Expand Down
8 changes: 8 additions & 0 deletions tests/unittests/portadmin/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def netbox_hp(profile):

netbox_type = Mock()
netbox_type.vendor = vendor
netbox_type.sysobjectid = '1.3.6.1.4.1.11.2.3.7.11.45'
netbox_type.get_enterprise_id.return_value = VENDOR_ID_HEWLETT_PACKARD

netbox = Mock()
Expand All @@ -40,6 +41,7 @@ def netbox_cisco(profile):

netbox_type = Mock()
netbox_type.vendor = vendor
netbox_type.sysobjectid = '1.3.6.1.4.1.9.1.278'
netbox_type.get_enterprise_id.return_value = VENDOR_ID_CISCOSYSTEMS

netbox = Mock()
Expand All @@ -50,6 +52,12 @@ def netbox_cisco(profile):
return netbox


@pytest.fixture
def netbox_cisco_smb(netbox_cisco):
netbox_cisco.type.sysobjectid = '1.3.6.1.4.1.9.6.1.1004.10.1'
return netbox_cisco


@pytest.fixture
def handler_hp(netbox_hp):
return ManagementFactory.get_instance(netbox_hp)
Expand Down
8 changes: 8 additions & 0 deletions tests/unittests/portadmin/portadmin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from nav.oids import OID
from nav.portadmin.management import ManagementFactory
from nav.portadmin.snmp.base import SNMPHandler
from nav.portadmin.snmp.hp import HP
from nav.portadmin.snmp.cisco import Cisco

Expand All @@ -17,6 +18,13 @@ def test_get_cisco(self, netbox_cisco):
assert handler is not None, "Could not get handler-object"
assert isinstance(handler, Cisco), "Wrong handler-type"

def test_when_device_is_cisco_smb_switch_it_should_return_generic_snmp_handler(
self, netbox_cisco_smb
):
handler = ManagementFactory.get_instance(netbox_cisco_smb)
assert handler is not None, "Could not get handler-object"
assert type(handler) is SNMPHandler, "Wrong handler-type"


class TestPortadminResponseHP:
def test_get_vlan_hp(self, handler_hp):
Expand Down

0 comments on commit 769d1d2

Please sign in to comment.