Migrate ironic snmp driver to the latest pysnmp API

Since pysnmp 4.3 (which dates back to 2015), library's high-level
SNMP API has been reworked towards better consistency and performance.
Ironic snmp driver still uses the older pysnmp API. This story
suggests migrating ironic snmp driver to the latest (e.g.
post 4.3) pysnmp API.

This bumps up ironic lower constraint on pysnmp.

Change-Id: Id3a03210e8a52dda5e6403e2f9cb040c59ca9573
Story: 2002756
Task: 22612
This commit is contained in:
Ilya Etingof 2018-06-28 22:07:28 +02:00
parent 607070569c
commit 0d03136bcc
6 changed files with 173 additions and 144 deletions

View File

@ -5,7 +5,7 @@
# These are available on pypi # These are available on pypi
proliantutils>=2.5.0 proliantutils>=2.5.0
pysnmp pysnmp>=4.3.0,<5.0.0
python-ironic-inspector-client>=1.5.0 python-ironic-inspector-client>=1.5.0
python-oneviewclient<3.0.0,>=2.5.2 python-oneviewclient<3.0.0,>=2.5.2
python-scciclient>=0.7.1 python-scciclient>=0.7.1

View File

@ -41,24 +41,23 @@ from ironic.drivers import base
pysnmp = importutils.try_import('pysnmp') pysnmp = importutils.try_import('pysnmp')
if pysnmp: if pysnmp:
from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp import error as snmp_error from pysnmp import error as snmp_error
from pysnmp.proto import rfc1902 from pysnmp import hlapi as snmp
snmp_auth_protocols = { snmp_auth_protocols = {
'md5': cmdgen.usmHMACMD5AuthProtocol, 'md5': snmp.usmHMACMD5AuthProtocol,
'sha': cmdgen.usmHMACSHAAuthProtocol, 'sha': snmp.usmHMACSHAAuthProtocol,
'none': cmdgen.usmNoAuthProtocol, 'none': snmp.usmNoAuthProtocol,
} }
# available since pysnmp 4.4.1 # available since pysnmp 4.4.1
try: try:
snmp_auth_protocols.update( snmp_auth_protocols.update(
{ {
'sha224': cmdgen.usmHMAC128SHA224AuthProtocol, 'sha224': snmp.usmHMAC128SHA224AuthProtocol,
'sha256': cmdgen.usmHMAC192SHA256AuthProtocol, 'sha256': snmp.usmHMAC192SHA256AuthProtocol,
'sha384': cmdgen.usmHMAC256SHA384AuthProtocol, 'sha384': snmp.usmHMAC256SHA384AuthProtocol,
'sha512': cmdgen.usmHMAC384SHA512AuthProtocol, 'sha512': snmp.usmHMAC384SHA512AuthProtocol,
} }
) )
@ -67,20 +66,20 @@ if pysnmp:
pass pass
snmp_priv_protocols = { snmp_priv_protocols = {
'des': cmdgen.usmDESPrivProtocol, 'des': snmp.usmDESPrivProtocol,
'3des': cmdgen.usm3DESEDEPrivProtocol, '3des': snmp.usm3DESEDEPrivProtocol,
'aes': cmdgen.usmAesCfb128Protocol, 'aes': snmp.usmAesCfb128Protocol,
'aes192': cmdgen.usmAesCfb192Protocol, 'aes192': snmp.usmAesCfb192Protocol,
'aes256': cmdgen.usmAesCfb256Protocol, 'aes256': snmp.usmAesCfb256Protocol,
'none': cmdgen.usmNoPrivProtocol, 'none': snmp.usmNoPrivProtocol,
} }
# available since pysnmp 4.4.3 # available since pysnmp 4.4.3
try: try:
snmp_priv_protocols.update( snmp_priv_protocols.update(
{ {
'aes192blmt': cmdgen.usmAesBlumenthalCfb192Protocol, 'aes192blmt': snmp.usmAesBlumenthalCfb192Protocol,
'aes256blmt': cmdgen.usmAesBlumenthalCfb256Protocol, 'aes256blmt': snmp.usmAesBlumenthalCfb256Protocol,
} }
) )
@ -89,9 +88,8 @@ if pysnmp:
pass pass
else: else:
cmdgen = None snmp = None
snmp_error = None snmp_error = None
rfc1902 = None
snmp_auth_protocols = { snmp_auth_protocols = {
'none': None 'none': None
@ -198,7 +196,7 @@ class SNMPClient(object):
user=None, auth_proto=None, user=None, auth_proto=None,
auth_key=None, priv_proto=None, auth_key=None, priv_proto=None,
priv_key=None, context_engine_id=None, context_name=None): priv_key=None, context_engine_id=None, context_name=None):
if not cmdgen: if not snmp:
raise exception.DriverLoadError( raise exception.DriverLoadError(
driver=self.__class__.__name__, driver=self.__class__.__name__,
reason=_("Unable to import python-pysnmp library") reason=_("Unable to import python-pysnmp library")
@ -213,13 +211,14 @@ class SNMPClient(object):
self.auth_key = auth_key self.auth_key = auth_key
self.priv_proto = priv_proto self.priv_proto = priv_proto
self.priv_key = priv_key self.priv_key = priv_key
self.context_engine_id = context_engine_id
self.context_name = context_name or ''
else: else:
self.read_community = read_community self.read_community = read_community
self.write_community = write_community self.write_community = write_community
self.cmd_gen = cmdgen.CommandGenerator() self.context_engine_id = context_engine_id
self.context_name = context_name or ''
self.snmp_engine = snmp.SnmpEngine()
def _get_auth(self, write_mode=False): def _get_auth(self, write_mode=False):
"""Return the authorization data for an SNMP request. """Return the authorization data for an SNMP request.
@ -227,23 +226,22 @@ class SNMPClient(object):
:param write_mode: `True` if write class SNMP command is :param write_mode: `True` if write class SNMP command is
executed. Default is `False`. executed. Default is `False`.
:returns: Either :returns: Either
:class:`pysnmp.entity.rfc3413.oneliner.cmdgen.CommunityData` :class:`pysnmp.hlapi.CommunityData`
or :class:`pysnmp.entity.rfc3413.oneliner.cmdgen.UsmUserData` or :class:`pysnmp.hlapi.UsmUserData`
object depending on SNMP version being used. object depending on SNMP version being used.
""" """
if self.version == SNMP_V3: if self.version == SNMP_V3:
return cmdgen.UsmUserData( return snmp.UsmUserData(
self.user, self.user,
authKey=self.auth_key, authKey=self.auth_key,
authProtocol=self.auth_proto, authProtocol=self.auth_proto,
privKey=self.priv_key, privKey=self.priv_key,
privProtocol=self.priv_proto, privProtocol=self.priv_proto
contextEngineId=self.context_engine_id,
contextName=self.context_name
) )
else: else:
mp_model = 1 if self.version == SNMP_V2C else 0 mp_model = 1 if self.version == SNMP_V2C else 0
return cmdgen.CommunityData( return snmp.CommunityData(
self.write_community if write_mode else self.read_community, self.write_community if write_mode else self.read_community,
mpModel=mp_model mpModel=mp_model
) )
@ -252,17 +250,31 @@ class SNMPClient(object):
"""Return the transport target for an SNMP request. """Return the transport target for an SNMP request.
:returns: A :class: :returns: A :class:
`pysnmp.entity.rfc3413.oneliner.cmdgen.UdpTransportTarget` object. `pysnmp.hlapi.UdpTransportTarget` object.
:raises: snmp_error.PySnmpError if the transport address is bad. :raises: :class:`pysnmp.error.PySnmpError` if the transport address
is bad.
""" """
# The transport target accepts timeout and retries parameters, which # The transport target accepts timeout and retries parameters, which
# default to 1 (second) and 5 respectively. These are deemed sensible # default to 1 (second) and 5 respectively. These are deemed sensible
# enough to allow for an unreliable network or slow device. # enough to allow for an unreliable network or slow device.
return cmdgen.UdpTransportTarget( return snmp.UdpTransportTarget(
(self.address, self.port), (self.address, self.port),
timeout=CONF.snmp.udp_transport_timeout, timeout=CONF.snmp.udp_transport_timeout,
retries=CONF.snmp.udp_transport_retries) retries=CONF.snmp.udp_transport_retries)
def _get_context(self):
"""Return the SNMP context for an SNMP request.
:returns: A :class:
`pysnmp.hlapi.ContextData` object.
:raises: :class:`pysnmp.error.PySnmpError` if SNMP context data
is bad.
"""
return snmp.ContextData(
contextEngineId=self.context_engine_id,
contextName=self.context_name
)
def get(self, oid): def get(self, oid):
"""Use PySNMP to perform an SNMP GET operation on a single object. """Use PySNMP to perform an SNMP GET operation on a single object.
@ -271,13 +283,16 @@ class SNMPClient(object):
:returns: The value of the requested object. :returns: The value of the requested object.
""" """
try: try:
results = self.cmd_gen.getCmd(self._get_auth(), snmp_gen = snmp.getCmd(self.snmp_engine,
self._get_transport(), self._get_auth(),
oid) self._get_transport(),
self._get_context(),
snmp.ObjectType(snmp.ObjectIdentity(oid)))
except snmp_error.PySnmpError as e: except snmp_error.PySnmpError as e:
raise exception.SNMPFailure(operation="GET", error=e) raise exception.SNMPFailure(operation="GET", error=e)
error_indication, error_status, error_index, var_binds = results error_indication, error_status, error_index, var_binds = next(snmp_gen)
if error_indication: if error_indication:
# SNMP engine-level error. # SNMP engine-level error.
@ -301,13 +316,17 @@ class SNMPClient(object):
:returns: A list of values of the requested table object. :returns: A list of values of the requested table object.
""" """
try: try:
results = self.cmd_gen.nextCmd(self._get_auth(), snmp_gen = snmp.nextCmd(self.snmp_engine,
self._get_transport(), self._get_auth(),
oid) self._get_transport(),
self._get_context(),
snmp.ObjectType(snmp.ObjectIdentity(oid)))
except snmp_error.PySnmpError as e: except snmp_error.PySnmpError as e:
raise exception.SNMPFailure(operation="GET_NEXT", error=e) raise exception.SNMPFailure(operation="GET_NEXT", error=e)
error_indication, error_status, error_index, var_bind_table = results (error_indication, error_status, error_index,
var_bind_table) = next(snmp_gen)
if error_indication: if error_indication:
# SNMP engine-level error. # SNMP engine-level error.
@ -329,13 +348,17 @@ class SNMPClient(object):
:raises: SNMPFailure if an SNMP request fails. :raises: SNMPFailure if an SNMP request fails.
""" """
try: try:
results = self.cmd_gen.setCmd(self._get_auth(write_mode=True), snmp_gen = snmp.setCmd(self.snmp_engine,
self._get_transport(), self._get_auth(write_mode=True),
(oid, value)) self._get_transport(),
self._get_context(),
snmp.ObjectType(
snmp.ObjectIdentity(oid), value))
except snmp_error.PySnmpError as e: except snmp_error.PySnmpError as e:
raise exception.SNMPFailure(operation="SET", error=e) raise exception.SNMPFailure(operation="SET", error=e)
error_indication, error_status, error_index, var_binds = results error_indication, error_status, error_index, var_binds = next(snmp_gen)
if error_indication: if error_indication:
# SNMP engine-level error. # SNMP engine-level error.
@ -533,11 +556,11 @@ class SNMPDriverSimple(SNMPDriverBase):
return power_state return power_state
def _snmp_power_on(self): def _snmp_power_on(self):
value = rfc1902.Integer(self.value_power_on) value = snmp.Integer(self.value_power_on)
self.client.set(self.oid, value) self.client.set(self.oid, value)
def _snmp_power_off(self): def _snmp_power_off(self):
value = rfc1902.Integer(self.value_power_off) value = snmp.Integer(self.value_power_off)
self.client.set(self.oid, value) self.client.set(self.oid, value)
@ -707,12 +730,12 @@ class SNMPDriverEatonPower(SNMPDriverBase):
def _snmp_power_on(self): def _snmp_power_on(self):
oid = self._snmp_oid(self.oid_poweron) oid = self._snmp_oid(self.oid_poweron)
value = rfc1902.Integer(self.value_power_on) value = snmp.Integer(self.value_power_on)
self.client.set(oid, value) self.client.set(oid, value)
def _snmp_power_off(self): def _snmp_power_off(self):
oid = self._snmp_oid(self.oid_poweroff) oid = self._snmp_oid(self.oid_poweroff)
value = rfc1902.Integer(self.value_power_off) value = snmp.Integer(self.value_power_off)
self.client.set(oid, value) self.client.set(oid, value)

View File

@ -20,8 +20,8 @@ import time
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp import error as snmp_error from pysnmp import error as snmp_error
from pysnmp import hlapi as pysnmp
from ironic.common import exception from ironic.common import exception
from ironic.common import states from ironic.common import states
@ -36,210 +36,218 @@ CONF = cfg.CONF
INFO_DICT = db_utils.get_test_snmp_info() INFO_DICT = db_utils.get_test_snmp_info()
@mock.patch.object(cmdgen, 'CommandGenerator', autospec=True)
class SNMPClientTestCase(base.TestCase): class SNMPClientTestCase(base.TestCase):
def setUp(self): def setUp(self):
super(SNMPClientTestCase, self).setUp() super(SNMPClientTestCase, self).setUp()
self.address = '1.2.3.4' self.address = '1.2.3.4'
self.port = '6700' self.port = '6700'
self.oid = 'oid' self.oid = (1, 3, 6, 1, 1, 1, 0)
self.value = 'value' self.value = 'value'
def test___init__(self, mock_cmdgen): @mock.patch.object(pysnmp, 'SnmpEngine', autospec=True)
def test___init__(self, mock_snmpengine):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1)
mock_cmdgen.assert_called_once_with() mock_snmpengine.assert_called_once_with()
self.assertEqual(self.address, client.address) self.assertEqual(self.address, client.address)
self.assertEqual(self.port, client.port) self.assertEqual(self.port, client.port)
self.assertEqual(snmp.SNMP_V1, client.version) self.assertEqual(snmp.SNMP_V1, client.version)
self.assertIsNone(client.read_community) self.assertIsNone(client.read_community)
self.assertIsNone(client.write_community) self.assertIsNone(client.write_community)
self.assertNotIn('user', client.__dict__) self.assertNotIn('user', client.__dict__)
self.assertEqual(mock_cmdgen.return_value, client.cmd_gen) self.assertEqual(mock_snmpengine.return_value, client.snmp_engine)
@mock.patch.object(cmdgen, 'CommunityData', autospec=True) @mock.patch.object(pysnmp, 'CommunityData', autospec=True)
def test__get_auth_v1_read(self, mock_community, mock_cmdgen): def test__get_auth_v1_read(self, mock_community):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1, client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1,
read_community='public', read_community='public',
write_community='private') write_community='private')
client._get_auth() client._get_auth()
mock_cmdgen.assert_called_once_with()
mock_community.assert_called_once_with(client.read_community, mock_community.assert_called_once_with(client.read_community,
mpModel=0) mpModel=0)
@mock.patch.object(cmdgen, 'CommunityData', autospec=True) @mock.patch.object(pysnmp, 'CommunityData', autospec=True)
def test__get_auth_v1_write(self, mock_community, mock_cmdgen): def test__get_auth_v1_write(self, mock_community):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1, client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1,
read_community='public', read_community='public',
write_community='private') write_community='private')
client._get_auth(write_mode=True) client._get_auth(write_mode=True)
mock_cmdgen.assert_called_once_with()
mock_community.assert_called_once_with(client.write_community, mock_community.assert_called_once_with(client.write_community,
mpModel=0) mpModel=0)
@mock.patch.object(cmdgen, 'UsmUserData', autospec=True) @mock.patch.object(pysnmp, 'UsmUserData', autospec=True)
def test__get_auth_v3(self, mock_user, mock_cmdgen): def test__get_auth_v3(self, mock_user):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
client._get_auth() client._get_auth()
mock_cmdgen.assert_called_once_with()
mock_user.assert_called_once_with( mock_user.assert_called_once_with(
client.user, client.user,
authKey=client.auth_key, authKey=client.auth_key,
authProtocol=client.auth_proto, authProtocol=client.auth_proto,
privKey=client.priv_key, privKey=client.priv_key,
privProtocol=client.priv_proto, privProtocol=client.priv_proto,
contextEngineId=client.context_engine_id,
contextName=client.context_name
) )
@mock.patch.object(cmdgen, 'UdpTransportTarget', autospec=True) @mock.patch.object(pysnmp, 'ContextData', autospec=True)
def test__get_transport(self, mock_transport, mock_cmdgen): def test__get_context(self, mock_context):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V1)
client._get_context()
mock_context.assert_called_once_with(None, '')
@mock.patch.object(pysnmp, 'UdpTransportTarget', autospec=True)
def test__get_transport(self, mock_transport):
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
client._get_transport() client._get_transport()
mock_cmdgen.assert_called_once_with()
mock_transport.assert_called_once_with( mock_transport.assert_called_once_with(
(client.address, client.port), (client.address, client.port),
retries=CONF.snmp.udp_transport_retries, retries=CONF.snmp.udp_transport_retries,
timeout=CONF.snmp.udp_transport_timeout) timeout=CONF.snmp.udp_transport_timeout)
@mock.patch.object(cmdgen, 'UdpTransportTarget', autospec=True) @mock.patch.object(pysnmp, 'UdpTransportTarget', autospec=True)
def test__get_transport_err(self, mock_transport, mock_cmdgen): def test__get_transport_err(self, mock_transport):
mock_transport.side_effect = snmp_error.PySnmpError mock_transport.side_effect = snmp_error.PySnmpError
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(snmp_error.PySnmpError, client._get_transport) self.assertRaises(snmp_error.PySnmpError, client._get_transport)
mock_cmdgen.assert_called_once_with()
mock_transport.assert_called_once_with( mock_transport.assert_called_once_with(
(client.address, client.port), (client.address, client.port),
retries=CONF.snmp.udp_transport_retries, retries=CONF.snmp.udp_transport_retries,
timeout=CONF.snmp.udp_transport_timeout) timeout=CONF.snmp.udp_transport_timeout)
@mock.patch.object(cmdgen, 'UdpTransportTarget', autospec=True) @mock.patch.object(pysnmp, 'UdpTransportTarget', autospec=True)
def test__get_transport_custom_timeout(self, mock_transport, mock_cmdgen): def test__get_transport_custom_timeout(self, mock_transport):
self.config(udp_transport_timeout=2.0, group='snmp') self.config(udp_transport_timeout=2.0, group='snmp')
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
client._get_transport() client._get_transport()
mock_cmdgen.assert_called_once_with()
mock_transport.assert_called_once_with((client.address, client.port), mock_transport.assert_called_once_with((client.address, client.port),
retries=5, timeout=2.0) retries=5, timeout=2.0)
@mock.patch.object(cmdgen, 'UdpTransportTarget', autospec=True) @mock.patch.object(pysnmp, 'UdpTransportTarget', autospec=True)
def test__get_transport_custom_retries(self, mock_transport, mock_cmdgen): def test__get_transport_custom_retries(self, mock_transport):
self.config(udp_transport_retries=10, group='snmp') self.config(udp_transport_retries=10, group='snmp')
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
client._get_transport() client._get_transport()
mock_cmdgen.assert_called_once_with()
mock_transport.assert_called_once_with((client.address, client.port), mock_transport.assert_called_once_with((client.address, client.port),
retries=10, timeout=1.0) retries=10, timeout=1.0)
@mock.patch.object(pysnmp, 'getCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get(self, mock_auth, mock_transport, mock_cmdgen): def test_get(self, mock_auth, mock_context, mock_transport, mock_getcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_getcmd.return_value = iter([("", None, 0, [var_bind])])
mock_cmdgenerator.getCmd.return_value = ("", None, 0, [var_bind])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
val = client.get(self.oid) val = client.get(self.oid)
self.assertEqual(var_bind[1], val) self.assertEqual(var_bind[1], val)
mock_cmdgenerator.getCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_getcmd.call_count)
self.oid)
@mock.patch.object(pysnmp, 'nextCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get_next(self, mock_auth, mock_transport, mock_cmdgen): def test_get_next(self, mock_auth, mock_context, mock_transport,
mock_nextcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_nextcmd.return_value = iter([("", None, 0,
mock_cmdgenerator.nextCmd.return_value = ( [[var_bind, var_bind]])])
"", None, 0, [[var_bind, var_bind]])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
val = client.get_next(self.oid) val = client.get_next(self.oid)
self.assertEqual([self.value, self.value], val) self.assertEqual([self.value, self.value], val)
mock_cmdgenerator.nextCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_nextcmd.call_count)
self.oid)
@mock.patch.object(pysnmp, 'getCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get_err_transport(self, mock_auth, mock_transport, mock_cmdgen): def test_get_err_transport(self, mock_auth, mock_context, mock_transport,
mock_getcmd):
mock_transport.side_effect = snmp_error.PySnmpError mock_transport.side_effect = snmp_error.PySnmpError
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_getcmd.return_value = iter([("engine error", None,
mock_cmdgenerator.getCmd.return_value = ("engine error", None, 0, 0, [var_bind])])
[var_bind])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, client.get, self.oid) self.assertRaises(exception.SNMPFailure, client.get, self.oid)
self.assertFalse(mock_cmdgenerator.getCmd.called) self.assertFalse(mock_getcmd.called)
@mock.patch.object(pysnmp, 'nextCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get_next_err_transport(self, mock_auth, mock_transport, def test_get_next_err_transport(self, mock_auth, mock_context,
mock_cmdgen): mock_transport, mock_nextcmd):
mock_transport.side_effect = snmp_error.PySnmpError mock_transport.side_effect = snmp_error.PySnmpError
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_nextcmd.return_value = iter([("engine error", None, 0,
mock_cmdgenerator.nextCmd.return_value = ("engine error", None, 0, [var_bind])])
[[var_bind, var_bind]])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, client.get_next, self.oid) self.assertRaises(exception.SNMPFailure, client.get_next, self.oid)
self.assertFalse(mock_cmdgenerator.nextCmd.called) self.assertFalse(mock_nextcmd.called)
@mock.patch.object(pysnmp, 'getCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get_err_engine(self, mock_auth, mock_transport, mock_cmdgen): def test_get_err_engine(self, mock_auth, mock_context, mock_transport,
mock_getcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_getcmd.return_value = iter([("engine error", None, 0,
mock_cmdgenerator.getCmd.return_value = ("engine error", None, 0, [var_bind])])
[var_bind])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, client.get, self.oid) self.assertRaises(exception.SNMPFailure, client.get, self.oid)
mock_cmdgenerator.getCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_getcmd.call_count)
self.oid)
@mock.patch.object(pysnmp, 'nextCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_get_next_err_engine(self, mock_auth, mock_transport, mock_cmdgen): def test_get_next_err_engine(self, mock_auth, mock_context,
mock_transport, mock_nextcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_nextcmd.return_value = iter([("engine error", None, 0,
mock_cmdgenerator.nextCmd.return_value = ("engine error", None, 0, [var_bind])])
[[var_bind, var_bind]])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, client.get_next, self.oid) self.assertRaises(exception.SNMPFailure, client.get_next, self.oid)
mock_cmdgenerator.nextCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_nextcmd.call_count)
self.oid)
@mock.patch.object(pysnmp, 'setCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_set(self, mock_auth, mock_transport, mock_cmdgen): def test_set(self, mock_auth, mock_context, mock_transport,
mock_setcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_setcmd.return_value = iter([("", None, 0,
mock_cmdgenerator.setCmd.return_value = ("", None, 0, [var_bind]) [var_bind])])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
client.set(self.oid, self.value) client.set(self.oid, self.value)
mock_cmdgenerator.setCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_setcmd.call_count)
var_bind)
@mock.patch.object(pysnmp, 'setCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_set_err_transport(self, mock_auth, mock_transport, mock_cmdgen): def test_set_err_transport(self, mock_auth, mock_context, mock_transport,
mock_setcmd):
mock_transport.side_effect = snmp_error.PySnmpError mock_transport.side_effect = snmp_error.PySnmpError
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_setcmd.return_value = iter([("engine error", None, 0,
mock_cmdgenerator.setCmd.return_value = ("engine error", None, 0, [var_bind])])
[var_bind])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, self.assertRaises(exception.SNMPFailure, client.set, self.oid,
client.set, self.oid, self.value) self.value)
self.assertFalse(mock_cmdgenerator.setCmd.called) self.assertFalse(mock_setcmd.called)
@mock.patch.object(pysnmp, 'setCmd', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_transport', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_context', autospec=True)
@mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True) @mock.patch.object(snmp.SNMPClient, '_get_auth', autospec=True)
def test_set_err_engine(self, mock_auth, mock_transport, mock_cmdgen): def test_set_err_engine(self, mock_auth, mock_context, mock_transport,
mock_setcmd):
var_bind = (self.oid, self.value) var_bind = (self.oid, self.value)
mock_cmdgenerator = mock_cmdgen.return_value mock_setcmd.return_value = iter([("engine error", None, 0,
mock_cmdgenerator.setCmd.return_value = ("engine error", None, 0, [var_bind])])
[var_bind])
client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3) client = snmp.SNMPClient(self.address, self.port, snmp.SNMP_V3)
self.assertRaises(exception.SNMPFailure, self.assertRaises(exception.SNMPFailure, client.set, self.oid,
client.set, self.oid, self.value) self.value)
mock_cmdgenerator.setCmd.assert_called_once_with(mock.ANY, mock.ANY, self.assertEqual(1, mock_setcmd.call_count)
var_bind)
class SNMPValidateParametersTestCase(db_base.DbTestCase): class SNMPValidateParametersTestCase(db_base.DbTestCase):

View File

@ -71,9 +71,8 @@ PYWSMAN_SPEC = (
# pywsnmp # pywsnmp
PYWSNMP_SPEC = ( PYWSNMP_SPEC = (
'entity', 'hlapi',
'error', 'error',
'proto',
) )
# scciclient # scciclient

View File

@ -117,24 +117,18 @@ if not dracclient:
if 'ironic.drivers.modules.drac' in sys.modules: if 'ironic.drivers.modules.drac' in sys.modules:
six.moves.reload_module(sys.modules['ironic.drivers.modules.drac']) six.moves.reload_module(sys.modules['ironic.drivers.modules.drac'])
# attempt to load the external 'pysnmp' library, which is required by # attempt to load the external 'pysnmp' library, which is required by
# the optional drivers.modules.snmp module # the optional drivers.modules.snmp module
pysnmp = importutils.try_import("pysnmp") pysnmp = importutils.try_import("pysnmp")
if not pysnmp: if not pysnmp:
pysnmp = mock.MagicMock(spec_set=mock_specs.PYWSNMP_SPEC) pysnmp = mock.MagicMock(spec_set=mock_specs.PYWSNMP_SPEC)
sys.modules["pysnmp"] = pysnmp sys.modules["pysnmp"] = pysnmp
sys.modules["pysnmp.entity"] = pysnmp.entity sys.modules["pysnmp.hlapi"] = pysnmp.hlapi
sys.modules["pysnmp.entity.rfc3413"] = pysnmp.entity.rfc3413
sys.modules["pysnmp.entity.rfc3413.oneliner"] = (
pysnmp.entity.rfc3413.oneliner)
sys.modules["pysnmp.entity.rfc3413.oneliner.cmdgen"] = (
pysnmp.entity.rfc3413.oneliner.cmdgen)
sys.modules["pysnmp.error"] = pysnmp.error sys.modules["pysnmp.error"] = pysnmp.error
pysnmp.error.PySnmpError = Exception pysnmp.error.PySnmpError = Exception
sys.modules["pysnmp.proto"] = pysnmp.proto
sys.modules["pysnmp.proto.rfc1902"] = pysnmp.proto.rfc1902
# Patch the RFC1902 integer class with a python int # Patch the RFC1902 integer class with a python int
pysnmp.proto.rfc1902.Integer = int pysnmp.hlapi.Integer = int
# if anything has loaded the snmp driver yet, reload it now that the # if anything has loaded the snmp driver yet, reload it now that the

View File

@ -0,0 +1,5 @@
---
upgrade:
- The minimum required version of pysnmp has been bumped to 4.3. This
pysnmp version introduces simpler, faster and more functional high-level
SNMP API on which ironic `snmp` driver has been migrated.