Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PySNMP >6 asyncio API #1497

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions labgrid/driver/power/eaton.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions labgrid/driver/power/poe_mib.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ 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)
output_status_oid = "{}.{}".format(OID, index)

value = _snmp.get(output_status_oid)

_snmp.cleanup()
if value == 1: # On
return True
if value == 2: # Off
Expand Down
2 changes: 2 additions & 0 deletions labgrid/driver/power/raritan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions labgrid/driver/powerdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions labgrid/remote/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ def broken(self, reason):
self.data["acquired"] = "<broken>"
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):
Expand Down
45 changes: 26 additions & 19 deletions labgrid/resource/ethernetport.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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'),
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -223,14 +230,16 @@ 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
new = current - set(self.switches)
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)
Expand All @@ -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)))

Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion labgrid/resource/udev.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
37 changes: 29 additions & 8 deletions labgrid/util/snmp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from pysnmp import hlapi
import asyncio

import pysnmp.hlapi.v3arch.asyncio as hlapi
from ..driver.exception import ExecutionError


Expand All @@ -8,24 +10,43 @@ 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]

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()
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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
Expand Down Expand Up @@ -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",
Expand Down
Loading