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:
parent
5cebb31c09
commit
dc254e2f78
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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')
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
fixes:
|
||||
- >
|
||||
[`bug 1597618 <https://bugs.launchpad.net/ceilometer/+bug/1597618>`_]
|
||||
Add the full support of snmp v3 user security model.
|
Loading…
Reference in New Issue
Block a user