Merge "Add support for gnocchi"
This commit is contained in:
commit
58b7465717
|
@ -74,7 +74,7 @@ options:
|
|||
api-workers:
|
||||
type: int
|
||||
default: 1
|
||||
description: |
|
||||
description: |
|
||||
Number of workers for Ceilometer API server. (>= Kilo).
|
||||
# Monitoring config
|
||||
nagios_context:
|
||||
|
@ -222,3 +222,9 @@ options:
|
|||
description: |
|
||||
Connect timeout configuration in ms for haproxy, used in HA
|
||||
configurations. If not provided, default value of 5000ms is used.
|
||||
gnocchi-archive-policy:
|
||||
type: string
|
||||
default: low
|
||||
description: |
|
||||
Archive retention policy to use when Ceilometer is deployed with
|
||||
Gnocchi for resource, metric and measures storage.
|
||||
|
|
|
@ -70,6 +70,7 @@ from ceilometer_utils import (
|
|||
set_shared_secret,
|
||||
assess_status,
|
||||
reload_systemd,
|
||||
ceilometer_upgrade,
|
||||
)
|
||||
from ceilometer_contexts import CEILOMETER_PORT
|
||||
from charmhelpers.contrib.openstack.ip import (
|
||||
|
@ -134,32 +135,36 @@ def db_joined():
|
|||
relation_set(ceilometer_database=CEILOMETER_DB)
|
||||
|
||||
|
||||
@hooks.hook("metric-service-relation-joined")
|
||||
def metric_service_joined():
|
||||
# NOTE(jamespage): gnocchiclient is required to support
|
||||
# the gnocchi event dispatcher
|
||||
apt_install(filter_installed_packages(['python-gnocchiclient']),
|
||||
fatal=True)
|
||||
|
||||
|
||||
@hooks.hook("amqp-relation-changed",
|
||||
"amqp-relation-departed",
|
||||
"shared-db-relation-changed",
|
||||
"shared-db-relation-departed")
|
||||
"shared-db-relation-departed",
|
||||
"identity-service-relation-changed",
|
||||
"identity-service-relation-departed",
|
||||
"metric-service-relation-changed",
|
||||
"metric-service-relation-departed")
|
||||
@restart_on_change(restart_map())
|
||||
def any_changed():
|
||||
CONFIGS.write_all()
|
||||
configure_https()
|
||||
for rid in relation_ids('identity-service'):
|
||||
keystone_joined(relid=rid)
|
||||
ceilometer_joined()
|
||||
|
||||
|
||||
@hooks.hook("identity-service-relation-changed")
|
||||
@restart_on_change(restart_map())
|
||||
def identity_service_relation_changed():
|
||||
CONFIGS.write_all()
|
||||
configure_https()
|
||||
keystone_joined()
|
||||
ceilometer_joined()
|
||||
|
||||
|
||||
@hooks.hook("amqp-relation-departed")
|
||||
@restart_on_change(restart_map())
|
||||
def amqp_departed():
|
||||
if 'amqp' not in CONFIGS.complete_contexts():
|
||||
log('amqp relation incomplete. Peer not ready?')
|
||||
return
|
||||
CONFIGS.write_all()
|
||||
# NOTE(jamespage): ceilometer@ocata requires both gnocchi
|
||||
# and mongodb to be configured to successfully
|
||||
# upgrade the underlying data stores.
|
||||
if ('metric-service' in CONFIGS.complete_contexts() and
|
||||
'identity-service' in CONFIGS.complete_contexts() and
|
||||
'mongodb' in CONFIGS.complete_contexts()):
|
||||
ceilometer_upgrade()
|
||||
|
||||
|
||||
def configure_https():
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ceilometer_hooks.py
|
|
@ -0,0 +1 @@
|
|||
ceilometer_hooks.py
|
|
@ -0,0 +1 @@
|
|||
ceilometer_hooks.py
|
|
@ -0,0 +1 @@
|
|||
ceilometer_hooks.py
|
|
@ -145,3 +145,17 @@ class ApacheSSLContext(SSLContext):
|
|||
|
||||
external_ports = [CEILOMETER_PORT]
|
||||
service_namespace = "ceilometer"
|
||||
|
||||
|
||||
class MetricServiceContext(OSContextGenerator):
|
||||
interfaces = ['metric-service']
|
||||
|
||||
def __call__(self):
|
||||
|
||||
for relid in relation_ids('metric-service'):
|
||||
for unit in related_units(relid):
|
||||
gnocchi_url = relation_get('gnocchi_url', unit=unit, rid=relid)
|
||||
if gnocchi_url:
|
||||
return {'gnocchi_url': gnocchi_url,
|
||||
'archive_policy': config('gnocchi-archive-policy')}
|
||||
return {}
|
||||
|
|
|
@ -28,6 +28,7 @@ from ceilometer_contexts import (
|
|||
MongoDBContext,
|
||||
CeilometerContext,
|
||||
HAProxyContext,
|
||||
MetricServiceContext,
|
||||
CEILOMETER_PORT,
|
||||
)
|
||||
from charmhelpers.contrib.openstack.utils import (
|
||||
|
@ -43,9 +44,10 @@ from charmhelpers.contrib.openstack.utils import (
|
|||
enable_memcache,
|
||||
CompareOpenStackReleases,
|
||||
)
|
||||
from charmhelpers.core.hookenv import config, log
|
||||
from charmhelpers.core.hookenv import config, log, is_leader
|
||||
from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
|
||||
from charmhelpers.core.host import init_is_systemd
|
||||
from charmhelpers.core.decorators import retry_on_exception
|
||||
from copy import deepcopy
|
||||
|
||||
HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
|
||||
|
@ -119,7 +121,8 @@ CONFIG_FILES = OrderedDict([
|
|||
CeilometerContext(),
|
||||
context.SyslogContext(),
|
||||
HAProxyContext(),
|
||||
context.MemcacheContext()],
|
||||
context.MemcacheContext(),
|
||||
MetricServiceContext()],
|
||||
'services': CEILOMETER_BASE_SERVICES
|
||||
}),
|
||||
(CEILOMETER_API_SYSTEMD_CONF, {
|
||||
|
@ -363,6 +366,18 @@ def assess_status(configs):
|
|||
os_application_version_set(VERSION_PACKAGE)
|
||||
|
||||
|
||||
def resolve_required_interfaces():
|
||||
"""Helper function to build a map of required interfaces based on the
|
||||
OpenStack release being deployed.
|
||||
|
||||
@returns dict - a dictionary keyed by high-level type of interfaces names
|
||||
"""
|
||||
required_ints = deepcopy(REQUIRED_INTERFACES)
|
||||
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'mitaka':
|
||||
required_ints['database'].append('metric-service')
|
||||
return required_ints
|
||||
|
||||
|
||||
def assess_status_func(configs):
|
||||
"""Helper function to create the function that will assess_status() for
|
||||
the unit.
|
||||
|
@ -375,7 +390,7 @@ def assess_status_func(configs):
|
|||
@return f() -> None : a function that assesses the unit's workload status
|
||||
"""
|
||||
return make_assess_status_func(
|
||||
configs, REQUIRED_INTERFACES,
|
||||
configs, resolve_required_interfaces(),
|
||||
services=services(), ports=determine_ports())
|
||||
|
||||
|
||||
|
@ -437,3 +452,16 @@ def disable_package_apache_site():
|
|||
"""
|
||||
if os.path.exists(PACKAGE_CEILOMETER_API_CONF):
|
||||
subprocess.check_call(['a2dissite', 'ceilometer-api'])
|
||||
|
||||
|
||||
@retry_on_exception(5, exc_type=subprocess.CalledProcessError)
|
||||
def ceilometer_upgrade():
|
||||
"""Execute ceilometer-upgrade command, with retry on failure if gnocchi
|
||||
API is not ready for requests"""
|
||||
if is_leader():
|
||||
if (CompareOpenStackReleases(os_release('ceilometer-common')) >=
|
||||
'newton'):
|
||||
cmd = ['ceilometer-upgrade']
|
||||
else:
|
||||
cmd = ['ceilometer-dbsync']
|
||||
subprocess.check_call(cmd)
|
||||
|
|
|
@ -39,6 +39,8 @@ requires:
|
|||
ha:
|
||||
interface: hacluster
|
||||
scope: container
|
||||
metric-service:
|
||||
interface: gnocchi
|
||||
peers:
|
||||
cluster:
|
||||
interface: ceilometer-ha
|
||||
|
|
|
@ -10,6 +10,11 @@ verbose = {{ verbose }}
|
|||
use_syslog = {{ use_syslog }}
|
||||
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline_alarm.yaml
|
||||
|
||||
{% if gnocchi_url -%}
|
||||
meter_dispatchers = gnocchi
|
||||
event_dispatchers = gnocchi
|
||||
{%- endif %}
|
||||
|
||||
[api]
|
||||
port = {{ port }}
|
||||
workers = {{ api_workers }}
|
||||
|
@ -30,6 +35,7 @@ user_domain_name = default
|
|||
auth_type = password
|
||||
{% endif -%}
|
||||
|
||||
{% if db_host or db_mongo_servers -%}
|
||||
[database]
|
||||
{% if db_replset: -%}
|
||||
connection = mongodb://{{ db_mongo_servers }}/{{ db_name }}?readPreference=primaryPreferred&replicaSet={{ db_replset }}
|
||||
|
@ -39,10 +45,18 @@ connection = mongodb://{{ db_host }}:{{ db_port }}/{{ db_name }}
|
|||
{% endif %}
|
||||
metering_time_to_live = {{ metering_time_to_live }}
|
||||
event_time_to_live = {{ event_time_to_live }}
|
||||
{%- endif %}
|
||||
|
||||
[publisher]
|
||||
telemetry_secret = {{ metering_secret }}
|
||||
|
||||
{% if gnocchi_url -%}
|
||||
[dispatcher_gnocchi]
|
||||
filter_service_activity = False
|
||||
archive_policy = {{ archive_policy }}
|
||||
url = {{ gnocchi_url }}
|
||||
{%- endif %}
|
||||
|
||||
{% include "section-keystone-authtoken-mitaka" %}
|
||||
|
||||
{% include "section-rabbitmq-oslo" %}
|
||||
|
|
|
@ -438,44 +438,6 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_209_nova_compute_ceilometer_agent_relation(self):
|
||||
"""Verify the nova-compute to ceilometer relation data"""
|
||||
u.log.debug('Checking nova-compute:ceilometer relation data...')
|
||||
unit = self.nova_sentry
|
||||
relation = ['nova-ceilometer', 'ceilometer-agent:nova-ceilometer']
|
||||
expected = {'private-address': u.valid_ip}
|
||||
|
||||
ret = u.validate_relation_data(unit, relation, expected)
|
||||
if ret:
|
||||
message = u.relation_error('ceilometer-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_210_ceilometer_agent_nova_compute_relation(self):
|
||||
"""Verify the ceilometer to nova-compute relation data"""
|
||||
u.log.debug('Checking ceilometer:nova-compute relation data...')
|
||||
unit = self.ceil_agent_sentry
|
||||
relation = ['nova-ceilometer', 'nova-compute:nova-ceilometer']
|
||||
sub = ('{"nova": {"/etc/nova/nova.conf": {"sections": {"DEFAULT": '
|
||||
'[["instance_usage_audit", "True"], '
|
||||
'["instance_usage_audit_period", "hour"], '
|
||||
'["notify_on_state_change", "vm_and_task_state"], '
|
||||
'["notification_driver", "ceilometer.compute.nova_notifier"], '
|
||||
'["notification_driver", '
|
||||
'"nova.openstack.common.notifier.rpc_notifier"]]}}}}')
|
||||
expected = {
|
||||
'subordinate_configuration': sub,
|
||||
'private-address': u.valid_ip
|
||||
}
|
||||
|
||||
ret = u.validate_relation_data(unit, relation, expected)
|
||||
if ret:
|
||||
message = u.relation_error('ceilometer-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_300_ceilometer_config(self):
|
||||
"""Verify the data in the ceilometer config file."""
|
||||
u.log.debug('Checking ceilometer config file data...')
|
||||
|
@ -549,49 +511,6 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_301_nova_config(self):
|
||||
"""Verify data in the nova compute nova config file"""
|
||||
u.log.debug('Checking nova compute config file...')
|
||||
unit = self.nova_sentry
|
||||
conf = '/etc/nova/nova.conf'
|
||||
expected = {
|
||||
'DEFAULT': {
|
||||
'verbose': 'False',
|
||||
'debug': 'False',
|
||||
'use_syslog': 'False',
|
||||
'my_ip': u.valid_ip,
|
||||
}
|
||||
}
|
||||
|
||||
# NOTE(beisner): notification_driver is not checked like the
|
||||
# others, as configparser does not support duplicate config
|
||||
# options, and dicts cant have duplicate keys.
|
||||
# Ex. from conf file:
|
||||
# notification_driver = ceilometer.compute.nova_notifier
|
||||
# notification_driver = nova.openstack.common.notifier.rpc_notifier
|
||||
for section, pairs in expected.iteritems():
|
||||
ret = u.validate_config_data(unit, conf, section, pairs)
|
||||
if ret:
|
||||
message = "ceilometer config error: {}".format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
# Check notification_driver existence via simple grep cmd
|
||||
lines = [('notification_driver = '
|
||||
'ceilometer.compute.nova_notifier'),
|
||||
('notification_driver = '
|
||||
'nova.openstack.common.notifier.rpc_notifier')]
|
||||
|
||||
sentry_units = [unit]
|
||||
cmds = []
|
||||
for line in lines:
|
||||
cmds.append('grep "{}" {}'.format(line, conf))
|
||||
|
||||
ret = u.check_commands_on_units(cmds, sentry_units)
|
||||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
||||
u.log.debug('OK')
|
||||
|
||||
def test_400_api_connection(self):
|
||||
"""Simple api calls to check service is up and responding"""
|
||||
u.log.debug('Checking api functionality...')
|
||||
|
|
|
@ -135,12 +135,38 @@ class CeilometerHooksTest(CharmTestCase):
|
|||
self.relation_set.assert_called_with(
|
||||
ceilometer_database='ceilometer')
|
||||
|
||||
@patch.object(hooks, 'ceilometer_upgrade')
|
||||
@patch.object(hooks, 'keystone_joined')
|
||||
@patch('charmhelpers.core.hookenv.config')
|
||||
@patch.object(hooks, 'ceilometer_joined')
|
||||
def test_any_changed(self, joined, mock_config):
|
||||
def test_any_changed_with_metrics(self, ceilometer_joined, mock_config,
|
||||
keystone_joined, ceilometer_upgrade):
|
||||
self.CONFIGS.complete_contexts.return_value = [
|
||||
'metric-service',
|
||||
'identity-service',
|
||||
'mongodb'
|
||||
]
|
||||
self.relation_ids.return_value = ['identity-service:1']
|
||||
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
|
||||
self.CONFIGS.write_all.assert_called_once()
|
||||
ceilometer_joined.assert_called_once()
|
||||
keystone_joined.assert_called_with(relid='identity-service:1')
|
||||
ceilometer_upgrade.assert_called_once()
|
||||
self.configure_https.assert_called_once()
|
||||
|
||||
@patch.object(hooks, 'ceilometer_upgrade')
|
||||
@patch.object(hooks, 'keystone_joined')
|
||||
@patch('charmhelpers.core.hookenv.config')
|
||||
@patch.object(hooks, 'ceilometer_joined')
|
||||
def test_any_changed(self, ceilometer_joined, mock_config,
|
||||
keystone_joined, ceilometer_upgrade):
|
||||
self.relation_ids.return_value = ['identity-service:1']
|
||||
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
|
||||
self.assertTrue(self.CONFIGS.write_all.called)
|
||||
self.assertTrue(joined.called)
|
||||
self.assertTrue(ceilometer_joined.called)
|
||||
keystone_joined.assert_called_with(relid='identity-service:1')
|
||||
ceilometer_upgrade.assert_not_called()
|
||||
self.configure_https.assert_called_once()
|
||||
|
||||
@patch('charmhelpers.core.hookenv.config')
|
||||
@patch.object(hooks, 'install')
|
||||
|
@ -150,14 +176,17 @@ class CeilometerHooksTest(CharmTestCase):
|
|||
self.assertTrue(changed.called)
|
||||
self.assertTrue(install.called)
|
||||
|
||||
@patch.object(hooks, 'any_changed')
|
||||
@patch('charmhelpers.core.hookenv.config')
|
||||
@patch.object(hooks, 'cluster_joined')
|
||||
def test_upgrade_charm_with_cluster(self, cluster_joined, mock_config):
|
||||
def test_upgrade_charm_with_cluster(self, cluster_joined, mock_config,
|
||||
any_changed):
|
||||
self.relation_ids.return_value = ['ceilometer/0',
|
||||
'ceilometer/1',
|
||||
'ceilometer/2']
|
||||
hooks.hooks.execute(['hooks/upgrade-charm'])
|
||||
self.assertEquals(cluster_joined.call_count, 3)
|
||||
any_changed.assert_called_once()
|
||||
|
||||
@patch.object(hooks, 'install_event_pipeline_setting')
|
||||
@patch('charmhelpers.core.hookenv.config')
|
||||
|
@ -494,3 +523,12 @@ class CeilometerHooksTest(CharmTestCase):
|
|||
self.relation_ids.return_value = ['identity-service/0']
|
||||
hooks.hooks.execute(['hooks/ha-relation-changed'])
|
||||
self.assertEquals(mock_keystone_joined.call_count, 1)
|
||||
|
||||
def test_metric_service_joined(self):
|
||||
self.filter_installed_packages.return_value = ['python-gnocchiclient']
|
||||
hooks.hooks.execute(['hooks/metric-service-relation-joined'])
|
||||
self.filter_installed_packages.assert_called_with(
|
||||
['python-gnocchiclient']
|
||||
)
|
||||
self.apt_install.assert_called_with(['python-gnocchiclient'],
|
||||
fatal=True)
|
||||
|
|
|
@ -37,6 +37,7 @@ TO_PATCH = [
|
|||
'enable_memcache',
|
||||
'token_cache_pkgs',
|
||||
'os_release',
|
||||
'is_leader',
|
||||
]
|
||||
|
||||
|
||||
|
@ -209,7 +210,7 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||
utils.VERSION_PACKAGE
|
||||
)
|
||||
|
||||
@patch.object(utils, 'REQUIRED_INTERFACES')
|
||||
@patch.object(utils, 'resolve_required_interfaces')
|
||||
@patch.object(utils, 'services')
|
||||
@patch.object(utils, 'determine_ports')
|
||||
@patch.object(utils, 'make_assess_status_func')
|
||||
|
@ -217,12 +218,13 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||
make_assess_status_func,
|
||||
determine_ports,
|
||||
services,
|
||||
REQUIRED_INTERFACES):
|
||||
resolve_required_interfaces):
|
||||
services.return_value = 's1'
|
||||
determine_ports.return_value = 'p1'
|
||||
resolve_required_interfaces.return_value = {'a': ['b']}
|
||||
utils.assess_status_func('test-config')
|
||||
make_assess_status_func.assert_called_once_with(
|
||||
'test-config', REQUIRED_INTERFACES, services='s1', ports='p1')
|
||||
'test-config', {'a': ['b']}, services='s1', ports='p1')
|
||||
|
||||
def test_pause_unit_helper(self):
|
||||
with patch.object(utils, '_pause_resume_helper') as prh:
|
||||
|
@ -243,3 +245,47 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||
utils._pause_resume_helper(f, 'some-config')
|
||||
asf.assert_called_once_with('some-config')
|
||||
f.assert_called_once_with('assessor', services='s1', ports='p1')
|
||||
|
||||
def test_resolve_required_interfaces(self):
|
||||
self.os_release.side_effect = None
|
||||
self.os_release.return_value = 'icehouse'
|
||||
self.assertEqual(
|
||||
utils.resolve_required_interfaces(),
|
||||
{
|
||||
'database': ['mongodb'],
|
||||
'messaging': ['amqp'],
|
||||
'identity': ['identity-service'],
|
||||
}
|
||||
)
|
||||
|
||||
def test_resolve_required_interfaces_mitaka(self):
|
||||
self.os_release.side_effect = None
|
||||
self.os_release.return_value = 'mitaka'
|
||||
self.assertEqual(
|
||||
utils.resolve_required_interfaces(),
|
||||
{
|
||||
'database': ['mongodb', 'metric-service'],
|
||||
'messaging': ['amqp'],
|
||||
'identity': ['identity-service'],
|
||||
}
|
||||
)
|
||||
|
||||
@patch.object(utils, 'subprocess')
|
||||
def test_ceilometer_upgrade(self, mock_subprocess):
|
||||
self.is_leader.return_value = True
|
||||
self.os_release.return_value = 'ocata'
|
||||
utils.ceilometer_upgrade()
|
||||
mock_subprocess.check_call.assert_called_with(['ceilometer-upgrade'])
|
||||
|
||||
@patch.object(utils, 'subprocess')
|
||||
def test_ceilometer_upgrade_mitaka(self, mock_subprocess):
|
||||
self.is_leader.return_value = True
|
||||
self.os_release.return_value = 'mitaka'
|
||||
utils.ceilometer_upgrade()
|
||||
mock_subprocess.check_call.assert_called_with(['ceilometer-dbsync'])
|
||||
|
||||
@patch.object(utils, 'subprocess')
|
||||
def test_ceilometer_upgrade_follower(self, mock_subprocess):
|
||||
self.is_leader.return_value = False
|
||||
utils.ceilometer_upgrade()
|
||||
mock_subprocess.check_call.assert_not_called()
|
||||
|
|
Loading…
Reference in New Issue