Skip to content

Commit

Permalink
Automatic discovery of TripleO Overcloud hardware
Browse files Browse the repository at this point in the history
-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
  • Loading branch information
Ladas committed Jul 28, 2014
1 parent 1f87c36 commit 670736a
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 15 deletions.
62 changes: 62 additions & 0 deletions ceilometer/hardware/discovery.py
Original file line number Diff line number Diff line change
@@ -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
20 changes: 12 additions & 8 deletions ceilometer/hardware/inspector/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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__()
Expand All @@ -88,7 +86,8 @@ def _get_or_walk_oid(self, oid, host, get=True):
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)
Expand All @@ -100,6 +99,15 @@ def _get_or_walk_oid(self, oid, host, get=True):
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)

Expand Down Expand Up @@ -186,10 +194,6 @@ def inspect_network(self, host):
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:
Expand Down
8 changes: 8 additions & 0 deletions ceilometer/nova_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ def instance_get_all_by_host(self, hostname):
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."""
Expand Down
7 changes: 0 additions & 7 deletions ceilometer/tests/hardware/inspector/test_snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,6 @@ def setUp(self):
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'))
Expand Down
10 changes: 10 additions & 0 deletions ceilometer/tests/test_novaclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ def test_instance_get_all_by_host(self):
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()
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 670736a

Please sign in to comment.