diff --git a/labgrid/driver/power/eaton.py b/labgrid/driver/power/eaton.py index f93d8f9f3..8d682fcdc 100644 --- a/labgrid/driver/power/eaton.py +++ b/labgrid/driver/power/eaton.py @@ -14,6 +14,7 @@ def power_set(host, port, index, value): outlet_control_oid = "{}.{}.0.{}".format(OID, cmd_id, index) _snmp.set(outlet_control_oid, 1) + _snmp.cleanup() def power_get(host, port, index): @@ -24,6 +25,7 @@ def power_get(host, port, index): value = _snmp.get(output_status_oid) + _snmp.cleanup() if value == 1: # On return True if value == 0: # Off diff --git a/labgrid/driver/power/poe_mib.py b/labgrid/driver/power/poe_mib.py index 12b159e36..a4d705bf7 100644 --- a/labgrid/driver/power/poe_mib.py +++ b/labgrid/driver/power/poe_mib.py @@ -12,6 +12,7 @@ def power_set(host, port, index, value): oid_value = "1" if value else "2" _snmp.set(outlet_control_oid, oid_value) + _snmp.cleanup() def power_get(host, port, index): _snmp = SimpleSNMP(host, 'private', port=port) @@ -19,6 +20,7 @@ def power_get(host, port, index): value = _snmp.get(output_status_oid) + _snmp.cleanup() if value == 1: # On return True if value == 2: # Off diff --git a/labgrid/driver/power/raritan.py b/labgrid/driver/power/raritan.py index 597e72d0b..0103e4ec7 100644 --- a/labgrid/driver/power/raritan.py +++ b/labgrid/driver/power/raritan.py @@ -17,6 +17,7 @@ def power_set(host, port, index, value): outlet_control_oid = "{}.2.1.{}".format(OID, index) _snmp.set(outlet_control_oid, str(int(value))) + _snmp.cleanup() def power_get(host, port, index): @@ -25,6 +26,7 @@ def power_get(host, port, index): value = _snmp.get(output_status_oid) + _snmp.cleanup() if value == 7: # On return True if value == 8: # Off diff --git a/labgrid/driver/powerdriver.py b/labgrid/driver/powerdriver.py index 80c8377fb..8046a5aeb 100644 --- a/labgrid/driver/powerdriver.py +++ b/labgrid/driver/powerdriver.py @@ -232,6 +232,7 @@ def cycle(self): def get(self): return self.backend.power_get(self._host, self._port, self.port.index) + @target_factory.reg_driver @attr.s(eq=False) class DigitalOutputPowerDriver(Driver, PowerResetMixin, PowerProtocol): diff --git a/labgrid/remote/exporter.py b/labgrid/remote/exporter.py index 86a261c92..8f0f77243 100755 --- a/labgrid/remote/exporter.py +++ b/labgrid/remote/exporter.py @@ -92,10 +92,10 @@ def broken(self, reason): self.data["acquired"] = "" self.logger.error("marked as broken: %s", reason) - def _get_start_params(self): # pylint: disable=no-self-use + def _get_start_params(self): return {} - def _get_params(self): # pylint: disable=no-self-use + def _get_params(self): return {} def _start(self, start_params): diff --git a/labgrid/resource/ethernetport.py b/labgrid/resource/ethernetport.py index 0020f26ba..1129ca8e1 100644 --- a/labgrid/resource/ethernetport.py +++ b/labgrid/resource/ethernetport.py @@ -12,23 +12,27 @@ class SNMPSwitch: """SNMPSwitch describes a switch accessible over SNMP. This class implements functions to query ports and the forwarding database.""" hostname = attr.ib(validator=attr.validators.instance_of(str)) + loop = attr.ib() def __attrs_post_init__(self): + import pysnmp.hlapi.v3arch.asyncio as hlapi + self.logger = logging.getLogger(f"{self}") self.ports = {} self.fdb = {} self.macs_by_port = {} + self.transport = self.loop.run_until_complete(hlapi.UdpTransportTarget.create((self.hostname, 161))) self._autodetect() def _autodetect(self): - from pysnmp import hlapi + import pysnmp.hlapi.v3arch.asyncio as hlapi - for (errorIndication, errorStatus, _, varBindTable) in hlapi.getCmd( + for (errorIndication, errorStatus, _, varBindTable) in self.loop.run_until_complete(hlapi.getCmd( hlapi.SnmpEngine(), hlapi.CommunityData('public'), - hlapi.UdpTransportTarget((self.hostname, 161)), + self.transport, hlapi.ContextData(), - hlapi.ObjectType(hlapi.ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))): + hlapi.ObjectType(hlapi.ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))): if errorIndication: raise Exception(f"snmp error {errorIndication}") elif errorStatus: @@ -51,7 +55,7 @@ def _get_ports(self): Returns: Dict[Dict[]]: ports and their values """ - from pysnmp import hlapi + import pysnmp.hlapi.v3arch.asyncio as hlapi variables = [ (hlapi.ObjectType(hlapi.ObjectIdentity('IF-MIB', 'ifIndex')), 'index'), @@ -64,14 +68,14 @@ def _get_ports(self): ] ports = {} - for (errorIndication, errorStatus, _, varBindTable) in hlapi.bulkCmd( + for (errorIndication, errorStatus, _, varBindTable) in self.loop.run_until_complete(hlapi.bulkCmd( hlapi.SnmpEngine(), hlapi.CommunityData('public'), - hlapi.UdpTransportTarget((self.hostname, 161)), + self.transport, hlapi.ContextData(), 0, 20, *[x[0] for x in variables], - lexicographicMode=False): + lexicographicMode=False)): if errorIndication: raise Exception(f"snmp error {errorIndication}") elif errorStatus: @@ -93,18 +97,18 @@ def _get_fdb_dot1d(self): Returns: Dict[List[str]]: ports and their values """ - from pysnmp import hlapi + import pysnmp.hlapi.v3arch.asyncio as hlapi ports = {} - for (errorIndication, errorStatus, _, varBindTable) in hlapi.bulkCmd( + for (errorIndication, errorStatus, _, varBindTable) in self.loop.run_until_complete(hlapi.bulkCmd( hlapi.SnmpEngine(), hlapi.CommunityData('public'), - hlapi.UdpTransportTarget((self.hostname, 161)), + self.transport, hlapi.ContextData(), 0, 50, hlapi.ObjectType(hlapi.ObjectIdentity('BRIDGE-MIB', 'dot1dTpFdbPort')), - lexicographicMode=False): + lexicographicMode=False)): if errorIndication: raise Exception(f"snmp error {errorIndication}") elif errorStatus: @@ -126,18 +130,18 @@ def _get_fdb_dot1q(self): Returns: Dict[List[str]]: ports and their values """ - from pysnmp import hlapi + import pysnmp.hlapi.v3arch.asyncio as hlapi ports = {} - for (errorIndication, errorStatus, _, varBindTable) in hlapi.bulkCmd( + for (errorIndication, errorStatus, _, varBindTable) in self.loop.run_until_complete(hlapi.bulkCmd( hlapi.SnmpEngine(), hlapi.CommunityData('public'), - hlapi.UdpTransportTarget((self.hostname, 161)), + self.transport, hlapi.ContextData(), 0, 50, hlapi.ObjectType(hlapi.ObjectIdentity('Q-BRIDGE-MIB', 'dot1qTpFdbPort')), - lexicographicMode=False): + lexicographicMode=False)): if errorIndication: raise Exception(f"snmp error {errorIndication}") elif errorStatus: @@ -177,6 +181,9 @@ def update(self): self.logger.debug("updating macs by port") self._update_macs() + def deactivate(self): + self.loop.close() + @attr.s class EthernetPortManager(ResourceManager): @@ -223,6 +230,8 @@ async def poll_neighbour(self): await asyncio.sleep(1.0) + self.loop = asyncio.get_event_loop() + async def poll_switches(self): current = set(resource.switch for resource in self.resources) removed = set(self.switches) - current @@ -230,7 +239,7 @@ async def poll_switches(self): for switch in removed: del self.switches[switch] for switch in new: - self.switches[switch] = SNMPSwitch(switch) + self.switches[switch] = SNMPSwitch(switch, self.loop) for switch in current: self.switches[switch].update() await asyncio.sleep(1.0) @@ -248,7 +257,6 @@ async def poll(self, handler): import traceback traceback.print_exc(file=sys.stderr) - self.loop = asyncio.get_event_loop() self.poll_tasks.append(self.loop.create_task(poll(self, poll_neighbour))) self.poll_tasks.append(self.loop.create_task(poll(self, poll_switches))) @@ -309,7 +317,6 @@ def poll(self): resource.extra = extra self.logger.debug("new information for %s: %s", resource, extra) - @target_factory.reg_resource @attr.s class SNMPEthernetPort(ManagedResource): diff --git a/labgrid/resource/udev.py b/labgrid/resource/udev.py index 54c6a9fd3..22c801a34 100644 --- a/labgrid/resource/udev.py +++ b/labgrid/resource/udev.py @@ -60,7 +60,7 @@ def __attrs_post_init__(self): self.match.setdefault('SUBSYSTEM', 'usb') super().__attrs_post_init__() - def filter_match(self, device): # pylint: disable=unused-argument,no-self-use + def filter_match(self, device): # pylint: disable=unused-argument return True def suggest_match(self, device): diff --git a/labgrid/util/snmp.py b/labgrid/util/snmp.py index 51ddaa6ef..d814996c6 100644 --- a/labgrid/util/snmp.py +++ b/labgrid/util/snmp.py @@ -1,4 +1,6 @@ -from pysnmp import hlapi +import asyncio + +import pysnmp.hlapi.v3arch.asyncio as hlapi from ..driver.exception import ExecutionError @@ -8,17 +10,27 @@ def __init__(self, host, community, port=161): if port is None: port = 161 + self.loop_created = False + + try: + # if called from async code, try to get current's thread loop + self.loop = asyncio.get_running_loop() + except RuntimeError: + self.loop_created = True + # no previous, external or running loop found, create a new one + self.loop = asyncio.new_event_loop() + self.engine = hlapi.SnmpEngine() - self.transport = hlapi.UdpTransportTarget((host, port)) + self.transport = self.loop.run_until_complete(hlapi.UdpTransportTarget.create((host, port))) self.community = hlapi.CommunityData(community, mpModel=0) self.context = hlapi.ContextData() def get(self, oid): - g = hlapi.getCmd(self.engine, self.community, self.transport, + g = self.loop.run_until_complete(hlapi.getCmd(self.engine, self.community, self.transport, self.context, hlapi.ObjectType(hlapi.ObjectIdentity(oid)), - lookupMib=False) + lookupMib=False)) - error_indication, error_status, _, res = next(g) + error_indication, error_status, _, res = g if error_indication or error_status: raise ExecutionError("Failed to get SNMP value.") return res[0][1] @@ -26,6 +38,15 @@ def get(self, oid): def set(self, oid, value): identify = hlapi.ObjectType(hlapi.ObjectIdentity(oid), hlapi.Integer(value)) - g = hlapi.setCmd(self.engine, self.community, self.transport, - self.context, identify, lookupMib=False) - next(g) + g = self.loop.run_until_complete(hlapi.setCmd(self.engine, self.community, self.transport, + self.context, identify, lookupMib=False)) + + error_indication, error_status, _, _ = g + if error_indication or error_status: + raise ExecutionError("Failed to set SNMP value.") + + def cleanup(self): + self.engine.closeDispatcher() + if self.loop_created: + self.loop.run_until_complete(self.loop.shutdown_asyncgens()) + self.loop.close() diff --git a/pyproject.toml b/pyproject.toml index 6eab8df10..048ddeaf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ pyvisa = [ "pyvisa>=1.11.3", "PyVISA-py>=0.5.2", ] -snmp = ["pysnmp-lextudio>=4.4.12, <6"] +snmp = ["pysnmp>6"] vxi11 = ["python-vxi11>=0.9"] xena = ["xenavalkyrie>=3.0.1"] deb = [ @@ -80,7 +80,7 @@ deb = [ "onewire>=0.2", # labgrid[snmp] - "pysnmp-lextudio>=4.4.12, <6", + "pysnmp>6", ] dev = [ # references to other optional dependency groups @@ -114,7 +114,7 @@ dev = [ "PyVISA-py>=0.5.2", # labgrid[snmp] - "pysnmp-lextudio>=4.4.12, <6", + "pysnmp>6", # labgrid[vxi11] "python-vxi11>=0.9",