Automatic discovery of TripleO Overcloud hardware

-geting IP addresses from Undercloud nova, allowing
 to poll all Overcloud nodes via SNMP
-adding support of basic auth, user_name and password
 used in TripleO by default

Change-Id: I189dbba9579055c8a1a878a769760a72e9174c6d
This commit is contained in:
Ladislav Smola 2014-05-06 15:34:12 +02:00
parent 1f87c36a67
commit 670736a2b6
6 changed files with 93 additions and 15 deletions

View File

@ -0,0 +1,62 @@
# -*- encoding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.config import cfg
from ceilometer import nova_client
from ceilometer.openstack.common.gettextutils import _
from ceilometer.openstack.common import log
from ceilometer import plugin
LOG = log.getLogger(__name__)
OPTS = [
cfg.StrOpt('url_scheme',
default='snmp://',
help='URL scheme to use for hardware nodes'),
cfg.StrOpt('readonly_user_name',
default='ro_snmp_user',
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'),
]
cfg.CONF.register_opts(OPTS, group='hardware')
class NodesDiscoveryTripleO(plugin.DiscoveryBase):
def __init__(self):
super(NodesDiscoveryTripleO, self).__init__()
self.nova_cli = nova_client.Client()
def discover(self, param=None):
"""Discover resources to monitor."""
instances = self.nova_cli.instance_get_all()
ip_addresses = []
for instance in instances:
try:
ip_address = instance.addresses['ctlplane'][0]['addr']
final_address = (
cfg.CONF.hardware.url_scheme +
cfg.CONF.hardware.readonly_user_name + ':' +
cfg.CONF.hardware.readonly_user_password + '@' +
ip_address)
ip_addresses.append(final_address)
except KeyError:
LOG.error(_("Couldn't obtain IP address of"
"instance %s") % instance.id)
return ip_addresses

View File

@ -18,7 +18,6 @@
"""Inspector for collecting data over SNMP"""
from pysnmp.entity.rfc3413.oneliner import cmdgen
from six.moves.urllib import parse as urlparse
from ceilometer.hardware.inspector import base
@ -73,9 +72,8 @@ class SNMPInspector(base.Inspector):
_interface_received_oid = "1.3.6.1.2.1.2.2.1.10"
_interface_transmitted_oid = "1.3.6.1.2.1.2.2.1.16"
_interface_error_oid = "1.3.6.1.2.1.2.2.1.20"
# Default port and security name
# Default port
_port = 161
_security_name = 'public'
def __init__(self):
super(SNMPInspector, self).__init__()
@ -88,7 +86,8 @@ class SNMPInspector(base.Inspector):
else:
func = self._cmdGen.nextCmd
ret_func = lambda x: x
ret = func(cmdgen.CommunityData(self._get_security_name(host)),
ret = func(self._get_auth_strategy(host),
cmdgen.UdpTransportTarget((host.hostname,
host.port or self._port)),
oid)
@ -100,6 +99,15 @@ class SNMPInspector(base.Inspector):
else:
return ret_func(data)
def _get_auth_strategy(self, host):
if host.password:
auth_strategy = cmdgen.UsmUserData(host.username,
authKey=host.password)
else:
auth_strategy = cmdgen.CommunityData(host.username or 'public')
return auth_strategy
def _get_value_from_oid(self, oid, host):
return self._get_or_walk_oid(oid, host, True)
@ -186,10 +194,6 @@ class SNMPInspector(base.Inspector):
error=int(error))
yield (interface, stats)
def _get_security_name(self, host):
options = urlparse.parse_qs(host.query)
return options.get('security_name', [self._security_name])[-1]
def _get_ip_for_interface(self, host, interface_id):
ip_addresses = self._walk_oid(self._interface_ip_oid, host)
for ip in ip_addresses:

View File

@ -131,6 +131,14 @@ class Client(object):
detailed=True,
search_opts=search_opts))
@logged
def instance_get_all(self):
"""Returns list of all instances."""
search_opts = {'all_tenants': True}
return self.nova_client.servers.list(
detailed=True,
search_opts=search_opts)
@logged
def floating_ip_get_all(self):
"""Returns all floating ips."""

View File

@ -214,13 +214,6 @@ class TestSNMPInspector(Base, test_base.BaseTestCase):
self.useFixture(mockpatch.PatchObject(
self.inspector._cmdGen, 'nextCmd', new=faux_nextCmd))
def test_get_security_name(self):
self.assertEqual(self.inspector._get_security_name(self.host),
self.inspector._security_name)
host2 = network_utils.urlsplit("snmp://foo:80?security_name=fake")
self.assertEqual(self.inspector._get_security_name(host2),
'fake')
def test_get_cmd_error(self):
self.useFixture(mockpatch.PatchObject(
self.inspector, '_memory_total_oid', new='failure'))

View File

@ -96,6 +96,16 @@ class TestNovaClient(test.BaseTestCase):
self.assertEqual(11, instances[0].kernel_id)
self.assertEqual(21, instances[0].ramdisk_id)
def test_instance_get_all(self):
with mock.patch.object(self.nv.nova_client.servers, 'list',
side_effect=self.fake_servers_list):
instances = self.nv.instance_get_all()
self.assertEqual(2, len(instances))
self.assertEqual(42, instances[0].id)
self.assertEqual(1, instances[0].flavor['id'])
self.assertEqual(1, instances[0].image['id'])
@staticmethod
def fake_servers_list_unknown_flavor(*args, **kwargs):
a = mock.MagicMock()

View File

@ -81,6 +81,7 @@ ceilometer.discover =
ipsec_connections = ceilometer.network.services.discovery:IPSecConnectionsDiscovery
fw_services = ceilometer.network.services.discovery:FirewallDiscovery
fw_policy = ceilometer.network.services.discovery:FirewallPolicyDiscovery
tripleo_overcloud_nodes = ceilometer.hardware.discovery:NodesDiscoveryTripleO
ceilometer.poll.compute =
disk.read.requests = ceilometer.compute.pollsters.disk:ReadRequestsPollster