ValueError exception when SNMP returns NoSuchObject

When an SNMP server doesn't implement a particular variable, pysnmp gives
us back a NoSuchObject object.  We can check for that when we fail to
convert the value to the type we're expecting it to be and safely return
None instead.

Change-Id: Ideb7ab68a0d3c6f0d133fafe020309c19cbdd7c7
Closes-Bug: #1611515
This commit is contained in:
Danek Duvall 2016-08-09 14:15:16 -07:00
parent 8e870b1828
commit 7348c156a8
2 changed files with 44 additions and 8 deletions

View File

@ -17,13 +17,18 @@
import copy
from oslo_log import log
from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp.proto import rfc1905
import six
import six.moves.urllib.parse as urlparse
from ceilometer.hardware.inspector import base
LOG = log.getLogger(__name__)
class SNMPException(Exception):
pass
@ -187,18 +192,24 @@ class SNMPInspector(base.Inspector):
return matched
@staticmethod
def get_oid_value(oid_cache, oid_def, suffix=''):
def get_oid_value(oid_cache, oid_def, suffix='', host=None):
oid, converter = oid_def
value = oid_cache[oid + suffix]
if converter:
value = converter(value)
try:
value = converter(value)
except ValueError:
if isinstance(value, rfc1905.NoSuchObject):
LOG.debug("OID %s%s has no value" % (
oid, " on %s" % host.hostname if host else ""))
return None
return value
@classmethod
def construct_metadata(cls, oid_cache, meta_defs, suffix=''):
def construct_metadata(cls, oid_cache, meta_defs, suffix='', host=None):
metadata = {}
for key, oid_def in six.iteritems(meta_defs):
metadata[key] = cls.get_oid_value(oid_cache, oid_def, suffix)
metadata[key] = cls.get_oid_value(oid_cache, oid_def, suffix, host)
return metadata
@classmethod
@ -243,11 +254,11 @@ class SNMPInspector(base.Inspector):
suffix = oid[len(meter_def['metric_oid'][0]):]
value = self.get_oid_value(oid_cache,
meter_def['metric_oid'],
suffix)
suffix, host)
# get the metadata for this sample value
metadata = self.construct_metadata(oid_cache,
meter_def['metadata'],
suffix)
suffix, host)
extra_metadata = copy.deepcopy(input_extra_metadata) or {}
# call post_op for special cases
if meter_def['post_op']:

View File

@ -17,6 +17,7 @@
import mock
from oslo_utils import netutils
from oslotest import mockpatch
from pysnmp.proto.rfc1905 import noSuchObject
from ceilometer.hardware.inspector import snmp
from ceilometer.tests import base as test_base
@ -34,8 +35,14 @@ class FakeObjectName(object):
class FakeCommandGenerator(object):
def getCmd(self, authData, transportTarget, *oids, **kwargs):
varBinds = [(FakeObjectName(oid),
int(oid.split('.')[-1])) for oid in oids]
emptyOID = '1.3.6.1.4.1.2021.4.14.0'
varBinds = [
(FakeObjectName(oid), int(oid.split('.')[-1]))
for oid in oids
if oid != emptyOID
]
if emptyOID in oids:
varBinds += [(FakeObjectName(emptyOID), noSuchObject)]
return (None, None, 0, varBinds)
def bulkCmd(authData, transportTarget, nonRepeaters, maxRepetitions,
@ -65,6 +72,12 @@ class TestSNMPInspector(test_base.BaseTestCase):
},
'post_op': None,
},
'test_nosuch': {
'matching_type': snmp.EXACT,
'metric_oid': ('1.3.6.1.4.1.2021.4.14.0', int),
'metadata': {},
'post_op': None,
},
}
def setUp(self):
@ -99,6 +112,18 @@ class TestSNMPInspector(test_base.BaseTestCase):
extra.update(project_id=2)
return value
def test_inspect_no_such_object(self):
cache = {}
try:
# inspect_generic() is a generator, so we explicitly need to
# iterate through it in order to trigger the exception.
list(self.inspector.inspect_generic(self.host,
cache,
{},
self.mapping['test_nosuch']))
except ValueError:
self.fail("got ValueError when interpreting NoSuchObject return")
def test_inspect_generic_exact(self):
self.inspector._fake_post_op = self._fake_post_op
cache = {}