Added full support of snmp v3 usm model

We used to only support partial of the snmp v3 usm model. This patch
adds the full support.

Change-Id: I3da8b19dea6a5ed3b0625d615ed27856046aa240
Closes-Bug: #1597618
This commit is contained in:
Lianhao Lu 2016-07-07 09:54:37 +08:00
parent 5cebb31c09
commit dc254e2f78
5 changed files with 122 additions and 9 deletions

View File

@ -32,10 +32,26 @@ OPTS = [
help='SNMPd user name of all nodes running in the cloud.'),
cfg.StrOpt('readonly_user_password',
default='password',
help='SNMPd password of all the nodes running in the cloud.',
help='SNMPd v3 authentication password of all the nodes '
'running in the cloud.',
secret=True),
cfg.StrOpt('readonly_user_auth_proto',
choices=['md5', 'sha'],
help='SNMPd v3 authentication algorithm of all the nodes '
'running in the cloud'),
cfg.StrOpt('readonly_user_priv_proto',
choices=['des', 'aes128', '3des', 'aes192', 'aes256'],
help='SNMPd v3 encryption algorithm of all the nodes '
'running in the cloud'),
cfg.StrOpt('readonly_user_priv_password',
help='SNMPd v3 encryption password of all the nodes '
'running in the cloud.',
secret=True),
]
cfg.CONF.register_opts(OPTS, group='hardware')
CONF = cfg.CONF
CONF.register_opts(OPTS, group='hardware')
class NodesDiscoveryTripleO(plugin_base.DiscoveryBase):
@ -49,6 +65,31 @@ class NodesDiscoveryTripleO(plugin_base.DiscoveryBase):
def _address(instance, field):
return instance.addresses['ctlplane'][0].get(field)
@staticmethod
def _make_resource_url(ip):
params = [('readonly_user_auth_proto', 'auth_proto'),
('readonly_user_priv_proto', 'priv_proto'),
('readonly_user_priv_password', 'priv_password')]
hwconf = CONF.hardware
url = hwconf.url_scheme
username = hwconf.readonly_user_name
password = hwconf.readonly_user_password
if username:
url += username
if password:
url += ':' + password
if username or password:
url += '@'
url += ip
query = "&".join(
param + "=" + hwconf.get(conf) for (conf, param) in params
if hwconf.get(conf))
if query:
url += '?' + query
return url
def discover(self, manager, param=None):
"""Discover resources to monitor.
@ -75,11 +116,7 @@ class NodesDiscoveryTripleO(plugin_base.DiscoveryBase):
for instance in self.instances.values():
try:
ip_address = self._address(instance, 'addr')
final_address = (
cfg.CONF.hardware.url_scheme +
cfg.CONF.hardware.readonly_user_name + ':' +
cfg.CONF.hardware.readonly_user_password + '@' +
ip_address)
final_address = self._make_resource_url(ip_address)
resource = {
'resource_id': instance.id,

View File

@ -16,9 +16,10 @@
"""Inspector for collecting data over SNMP"""
import copy
from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp.entity.rfc3413.oneliner import cmdgen
import six
import six.moves.urllib.parse as urlparse
from ceilometer.hardware.inspector import base
@ -56,6 +57,22 @@ def parse_snmp_return(ret, is_bulk=False):
EXACT = 'type_exact'
PREFIX = 'type_prefix'
_auth_proto_mapping = {
'md5': cmdgen.usmHMACMD5AuthProtocol,
'sha': cmdgen.usmHMACSHAAuthProtocol,
}
_priv_proto_mapping = {
'des': cmdgen.usmDESPrivProtocol,
'aes128': cmdgen.usmAesCfb128Protocol,
'3des': cmdgen.usm3DESEDEPrivProtocol,
'aes192': cmdgen.usmAesCfb192Protocol,
'aes256': cmdgen.usmAesCfb256Protocol,
}
_usm_proto_mapping = {
'auth_proto': ('authProtocol', _auth_proto_mapping),
'priv_proto': ('privProtocol', _priv_proto_mapping),
}
class SNMPInspector(base.Inspector):
# Default port
@ -283,9 +300,24 @@ class SNMPInspector(base.Inspector):
@staticmethod
def _get_auth_strategy(host):
options = urlparse.parse_qs(host.query)
kwargs = {}
for key in _usm_proto_mapping:
opt = options.get(key, [None])[-1]
value = _usm_proto_mapping[key][1].get(opt)
if value:
kwargs[_usm_proto_mapping[key][0]] = value
priv_pass = options.get('priv_password', [None])[-1]
if priv_pass:
kwargs['privKey'] = priv_pass
if host.password:
kwargs['authKey'] = host.password
if kwargs:
auth_strategy = cmdgen.UsmUserData(host.username,
authKey=host.password)
**kwargs)
else:
auth_strategy = cmdgen.CommunityData(host.username or 'public')
return auth_strategy

View File

@ -87,11 +87,22 @@ class TestHardwareDiscovery(base.BaseTestCase):
'flavor_id': 'flavor_id',
}
expected_usm = {
'resource_id': 'resource_id',
'resource_url': ''.join(['snmp://ro_snmp_user:password@0.0.0.0',
'?priv_proto=aes192',
'&priv_password=priv_pass']),
'mac_addr': '01-23-45-67-89-ab',
'image_id': 'image_id',
'flavor_id': 'flavor_id',
}
def setUp(self):
super(TestHardwareDiscovery, self).setUp()
self.discovery = hardware.NodesDiscoveryTripleO()
self.discovery.nova_cli = mock.MagicMock()
self.manager = mock.MagicMock()
self.CONF = self.useFixture(fixture_config.Config()).conf
def test_hardware_discovery(self):
self.discovery.nova_cli.instance_get_all.return_value = [
@ -106,3 +117,13 @@ class TestHardwareDiscovery(base.BaseTestCase):
self.discovery.nova_cli.instance_get_all.return_value = [instance]
resources = self.discovery.discover(self.manager)
self.assertEqual(0, len(resources))
def test_hardware_discovery_usm(self):
self.CONF.set_override('readonly_user_priv_proto', 'aes192',
group='hardware')
self.CONF.set_override('readonly_user_priv_password', 'priv_pass',
group='hardware')
self.discovery.nova_cli.instance_get_all.return_value = [
self.MockInstance()]
resources = self.discovery.discover(self.manager)
self.assertEqual(self.expected_usm, resources[0])

View File

@ -14,6 +14,7 @@
# under the License.
"""Tests for ceilometer/hardware/inspector/snmp/inspector.py
"""
import mock
from oslo_utils import netutils
from oslotest import mockpatch
@ -202,3 +203,20 @@ class TestSNMPInspector(test_base.BaseTestCase):
name = rfc1902.ObjectName(oid)
self.assertEqual(oid, str(name))
@mock.patch.object(snmp.cmdgen, 'UsmUserData')
def test_auth_strategy(self, mock_method):
host = ''.join(['snmp://a:b@foo?auth_proto=sha',
'&priv_password=pass&priv_proto=aes256'])
host = netutils.urlsplit(host)
self.inspector._get_auth_strategy(host)
mock_method.assert_called_with(
'a', authKey='b',
authProtocol=snmp.cmdgen.usmHMACSHAAuthProtocol,
privProtocol=snmp.cmdgen.usmAesCfb256Protocol,
privKey='pass')
host2 = 'snmp://a:b@foo?&priv_password=pass'
host2 = netutils.urlsplit(host2)
self.inspector._get_auth_strategy(host2)
mock_method.assert_called_with('a', authKey='b', privKey='pass')

View File

@ -0,0 +1,5 @@
---
fixes:
- >
[`bug 1597618 <https://bugs.launchpad.net/ceilometer/+bug/1597618>`_]
Add the full support of snmp v3 user security model.