Skip to content

Commit

Permalink
Merge pull request openstack-charmers#64 from gnuoy/aodh
Browse files Browse the repository at this point in the history
Add support for aodh tests
  • Loading branch information
fnordahl committed Sep 26, 2019
2 parents c82f121 + 3752f2c commit 959d813
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 1 deletion.
3 changes: 2 additions & 1 deletion setup.py
Expand Up @@ -34,6 +34,7 @@
'PyYAML',
'tenacity',
'oslo.config',
'aodhclient',
'python-glanceclient',
'python-keystoneclient',
'python-novaclient',
Expand Down Expand Up @@ -102,4 +103,4 @@ def run_tests(self):
'testing': tests_require,
},
tests_require=tests_require,
)
)
15 changes: 15 additions & 0 deletions zaza/openstack/charm_tests/aodh/__init__.py
@@ -0,0 +1,15 @@
# Copyright 2019 Canonical Ltd.
#
# 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.

"""Collection of code for setting up and testing aodh."""
135 changes: 135 additions & 0 deletions zaza/openstack/charm_tests/aodh/tests.py
@@ -0,0 +1,135 @@
#!/usr/bin/env python3

# Copyright 2019 Canonical Ltd.
#
# 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.

"""Encapsulate Aodh testing."""

import logging

import zaza.model
import zaza.openstack.charm_tests.test_utils as test_utils
import zaza.openstack.utilities.openstack as openstack_utils
import zaza.openstack.configure.telemetry as telemetry_utils


class AodhTest(test_utils.OpenStackBaseTest):
"""Encapsulate Aodh tests."""

RESOURCE_PREFIX = 'zaza-aodhtests'

@classmethod
def setUpClass(cls):
"""Run class setup for running tests."""
super(AodhTest, cls).setUpClass()
cls.xenial_ocata = openstack_utils.get_os_release('xenial_ocata')
cls.xenial_newton = openstack_utils.get_os_release('xenial_newton')
cls.bionic_stein = openstack_utils.get_os_release('bionic_stein')
cls.release = openstack_utils.get_os_release()
cls.keystone_session = openstack_utils.get_overcloud_keystone_session()
cls.model_name = zaza.model.get_juju_model()
cls.aodh_client = openstack_utils.get_aodh_session_client(
cls.keystone_session)

@classmethod
def tearDown(cls):
"""Remove test resources."""
logging.info('Running teardown')
cache_wait = False
for alarm in cls.aodh_client.alarm.list():
if alarm['name'].startswith(cls.RESOURCE_PREFIX):
cache_wait = True
logging.info('Removing Alarm {}'.format(alarm['name']))
telemetry_utils.delete_alarm(
cls.aodh_client,
alarm['name'],
cache_wait=False)
if cache_wait:
logging.info('Waiting for alarm cache to clear')
telemetry_utils.alarm_cache_wait()

@property
def services(self):
"""Return a list of the service that should be running."""
if self.release >= self.xenial_ocata:
services = [
'apache2',
'aodh-evaluator: AlarmEvaluationService worker(0)',
'aodh-notifier: AlarmNotifierService worker(0)',
('aodh-listener: EventAlarmEvaluationService'
' worker(0)')]
elif self.release >= self.xenial_newton:
services = [
('/usr/bin/python /usr/bin/aodh-api --port 8032 -- '
'--config-file=/etc/aodh/aodh.conf '
'--log-file=/var/log/aodh/aodh-api.log'),
'aodh-evaluator - AlarmEvaluationService(0)',
'aodh-notifier - AlarmNotifierService(0)',
'aodh-listener - EventAlarmEvaluationService(0)']
else:
services = [
'aodh-api',
'aodh-evaluator',
'aodh-notifier',
'aodh-listener']
return services

def test_100_test_api(self):
"""Check api by creating an alarm."""
alarm_name = '{}_test_api_alarm'.format(self.RESOURCE_PREFIX)
logging.info('Creating alarm {}'.format(alarm_name))
alarm = telemetry_utils.create_server_power_off_alarm(
self.aodh_client,
alarm_name,
'some-uuid')
alarm_state = telemetry_utils.get_alarm_state(
self.aodh_client,
alarm['alarm_id'])
logging.info('alarm_state: {}'.format(alarm_state))
# Until data is collected alarm come up in an 'insufficient data'
# state.
self.assertEqual(alarm_state, 'insufficient data')

def test_900_restart_on_config_change(self):
"""Checking restart happens on config change.
Change disk format and assert then change propagates to the correct
file and that services are restarted as a result
"""
# Expected default and alternate values
set_default = {'debug': 'False'}
set_alternate = {'debug': 'True'}

# Config file affected by juju set config change
conf_file = '/etc/aodh/aodh.conf'

# Make config change, check for service restarts
self.restart_on_changed(
conf_file,
set_default,
set_alternate,
{'DEFAULT': {'debug': ['False']}},
{'DEFAULT': {'debug': ['True']}},
self.services)

def test_901_pause_resume(self):
"""Run pause and resume tests.
Pause service and check services are stopped then resume and check
they are started
"""
with self.pause_resume(
self.services,
pgrep_full=False):
logging.info("Testing pause resume")
121 changes: 121 additions & 0 deletions zaza/openstack/configure/telemetry.py
@@ -0,0 +1,121 @@
# Copyright 2019 Canonical Ltd.
#
# 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.

"""Configure and manage masakari.
Functions for managing masakari resources and simulating compute node loss
and recovery.
"""

import time

import zaza.model


def ceilometer_upgrade(application_name=None, model_name=None):
"""Run ceilometer upgrade action.
:param application_name: Name of application to run action against.
:type application_name: str
:param model_name: Name of model application_name resides in.
:type model_name: str
"""
zaza.model.run_action_on_leader(
application_name,
'ceilometer-upgrade',
model_name=model_name,
action_params={})


def get_alarm(aodh_client, alarm_name):
"""Return the alarm with the given name.
:param aodh_client: Authenticated aodh v2 client
:type aodh_client: aodhclient.v2.client.Client
:param alarm_name: Name of alarm to search for
:type alarm_name: str
:returns: Returns a dict of alarm data.
:rtype: {} or None
"""
for alarm in aodh_client.alarm.list():
if alarm['name'] == alarm_name:
return alarm
return None


def alarm_cache_wait():
"""Wait for alarm cache to clear."""
# AODH has an alarm cache (see event_alarm_cache_ttl in aodh.conf). This
# means deleted alarms can persist and fire. The default is 60s and is
# currently not configrable via the charm so 61s is a safe assumption.
time.sleep(61)


def delete_alarm(aodh_client, alarm_name, cache_wait=False):
"""Delete alarm with given name.
:param aodh_client: Authenticated aodh v2 client
:type aodh_client: aodhclient.v2.client.Client
:param alarm_name: Name of alarm to delete
:type alarm_name: str
:param cache_wait: Whether to wait for cache to clear after deletion.
:type cache_wait: bool
"""
alarm = get_alarm(aodh_client, alarm_name)
if alarm:
aodh_client.alarm.delete(alarm['alarm_id'])
if cache_wait:
alarm_cache_wait()


def get_alarm_state(aodh_client, alarm_id):
"""Return the state of the alarm with the given name.
:param aodh_client: Authenticated aodh v2 client
:type aodh_client: aodhclient.v2.client.Client
:param alarm_id: ID of provided alarm
:param alarm_id: str
:returns: State of given alarm
:rtype: str
"""
alarm = aodh_client.alarm.get(alarm_id)
return alarm['state']


def create_server_power_off_alarm(aodh_client, alarm_name, server_uuid):
"""Create an alarm which triggers when an instance powers off.
:param aodh_client: Authenticated aodh v2 client
:type aodh_client: aodhclient.v2.client.Client
:param alarm_name: Name of alarm to delete
:type alarm_name: str
:param server_uuid: UUID of server to monitor
:type server_uuid: str
:returns: Dict of alarm data
:rtype: {}
"""
alarm_def = {
'type': 'event',
'name': alarm_name,
'description': 'Instance powered OFF',
'alarm_actions': ['log://'],
'ok_actions': ['log://'],
'insufficient_data_actions': ['log://'],
'event_rule': {
'event_type': 'compute.instance.power_off.*',
'query': [{'field': 'traits.instance_id',
'op': 'eq',
'type': 'string',
'value': server_uuid}]}}
return aodh_client.alarm.create(alarm_def)
12 changes: 12 additions & 0 deletions zaza/openstack/utilities/openstack.py
Expand Up @@ -25,6 +25,7 @@

from openstack import connection

from aodhclient.v2 import client as aodh_client
from cinderclient import client as cinderclient
from glanceclient import Client as GlanceClient

Expand Down Expand Up @@ -289,6 +290,17 @@ def get_masakari_session_client(session, interface='internal',
return conn.instance_ha


def get_aodh_session_client(session):
"""Return aodh client authenticated by keystone session.
:param session: Keystone session object
:type session: keystoneauth1.session.Session object
:returns: Authenticated aodh client
:rtype: openstack.instance_ha.v1._proxy.Proxy
"""
return aodh_client.Client(session=session)


def get_keystone_scope():
"""Return Keystone scope based on OpenStack release of the overcloud.
Expand Down

0 comments on commit 959d813

Please sign in to comment.