fuel-ostf/fuel_health/ceilometermanager.py

483 lines
21 KiB
Python

#!/usr/bin/env python
# Copyright 2013 Mirantis, Inc.
#
# 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.
import functools
import logging
import neutronclient.common.exceptions as neutron_exc
from fuel_health.common.utils.data_utils import rand_name
import fuel_health.nmanager
import fuel_health.test
LOG = logging.getLogger(__name__)
def check_compute_nodes():
"""Decorator that checks a compute existence in the environment.
Decorated tests must be skipped if there are no compute nodes.
"""
def decorator(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if (not self.config.compute.compute_nodes and
not self.config.compute.use_vcenter):
self.skipTest('There are no compute nodes in the environment. '
'Test skipped.')
return func(self, *args, **kwargs)
return wrapper
return decorator
class CeilometerBaseTest(fuel_health.nmanager.PlatformServicesBaseClass):
@classmethod
def setUpClass(cls):
super(CeilometerBaseTest, cls).setUpClass()
if cls.manager.clients_initialized:
cls.wait_interval = cls.config.compute.build_interval
cls.wait_timeout = cls.config.compute.build_timeout
cls.objects_for_delete = []
cls.nova_notifications = ['memory', 'vcpus', 'disk.root.size',
'disk.ephemeral.size']
cls.neutron_network_notifications = ['network', 'network.create',
'network.update']
cls.neutron_subnet_notifications = ['subnet', 'subnet.create',
'subnet.update']
cls.neutron_port_notifications = ['port', 'port.create',
'port.update']
cls.neutron_router_notifications = ['router', 'router.create',
'router.update']
cls.neutron_floatingip_notifications = ['ip.floating.create',
'ip.floating.update']
cls.volume_events = [
'volume.create.start', 'volume.create.end',
'volume.delete.start', 'volume.delete.end',
'volume.update.start', 'volume.update.end',
'volume.resize.start', 'volume.resize.end',
'volume.attach.start', 'volume.attach.end',
'volume.detach.start', 'volume.detach.end']
cls.snapshot_events = [
'snapshot.create.start', 'snapshot.create.end',
'snapshot.delete.start', 'snapshot.delete.end']
cls.glance_notifications = ['image.size', 'image.update',
'image.upload', 'image.download',
'image.serve', 'image.delete']
cls.swift_notifications = ['storage.objects.incoming.bytes',
'storage.objects.outgoing.bytes',
'storage.api.request']
cls.heat_notifications = ['stack.create', 'stack.update',
'stack.delete', 'stack.resume',
'stack.suspend']
cls.keystone_user_notifications = [
'identity.user.created', 'identity.user.deleted',
'identity.user.updated']
cls.keystone_role_notifications = [
'identity.role.created', 'identity.role.updated',
'identity.role.deleted']
cls.keystone_role_assignment_notifications = [
'identity.role_assignment.created',
'identity.role_assignment.deleted']
cls.keystone_project_notifications = [
'identity.project.created', 'identity.project.updated',
'identity.project.deleted']
cls.keystone_group_notifications = [
'identity.group.created', 'identity.group.updated',
'identity.group.deleted']
cls.keystone_trust_notifications = [
'identity.trust.created',
'identity.trust.deleted']
cls.sahara_cluster_notifications = [
'cluster.create', 'cluster.update', 'cluster.delete']
def setUp(self):
super(CeilometerBaseTest, self).setUp()
self.check_clients_state()
if not self.ceilometer_client:
self.skipTest('Ceilometer is unavailable.')
def create_server(self, name, **kwargs):
server = self._create_server(self.compute_client, name, **kwargs)
self.addCleanup(
self.delete_resource,
delete_method=lambda: self.compute_client.servers.delete(
server.id),
get_method=lambda: self.compute_client.servers.get(server.id))
return server
def create_alarm(self, **kwargs):
"""This method provides creation of alarm."""
if 'name' in kwargs:
kwargs['name'] = rand_name(kwargs['name'])
alarm = self.ceilometer_client.alarms.create(**kwargs)
self.objects_for_delete.append((self.ceilometer_client.alarms.delete,
alarm.alarm_id))
return alarm
def get_state(self, alarm_id):
"""This method provides getting state."""
return self.ceilometer_client.alarms.get_state(alarm_id=alarm_id)
def verify_state(self, alarm_id, state):
"""This method provides getting state."""
alarm_state_resp = self.get_state(alarm_id)
if not alarm_state_resp == state:
self.fail('State was not setted')
def wait_for_resource_status(self, resource_client, resource_id, status):
self.status_timeout(resource_client, resource_id, status)
def wait_for_alarm_status(self, alarm_id, status=None):
"""The method is a customization of test.status_timeout()."""
def check_status():
try:
alarm_state_resp = self.get_state(alarm_id)
except Exception:
alarm_state_resp = None
if status:
if alarm_state_resp == status:
return True
elif alarm_state_resp == 'alarm' or 'ok':
return True # All good.
LOG.debug("Waiting for state to get alarm status.")
if not fuel_health.test.call_until_true(check_status, 1000, 10):
actual_status = self.get_state(alarm_id)
self.fail(
"Timed out waiting to become alarm status. "
"Expected status:{exp_status}; "
"Actual status:{act_status}".format(
exp_status=status if status else "'alarm' or 'ok'",
act_status=actual_status))
def wait_for_object_sample(self, obj, query, ceilo_obj_type):
"""This method is to wait for sample to add it to database.
query example:
query=[
{'field':'resource',
'op':'eq',
'value':'000e6838-471b-4a14-8da6-655fcff23df1'
}]
"""
kwargs = {"q": query}
if ceilo_obj_type == 'sample':
method = self.ceilometer_client.samples.list
kwargs["meter_name"] = obj
elif ceilo_obj_type == "event":
query.append({'field': 'event_type', 'op': 'eq', 'value': obj})
method = self.ceilometer_client.events.list
def check_status():
try:
body = method(**kwargs)
except Exception:
body = None
if body:
return True
if not fuel_health.test.call_until_true(check_status, 600, 10):
self.fail(
"Timed out waiting for object: {obj} "
"with query:{query}".format(obj=obj, query=query))
def wait_for_statistic_of_metric(self, meter_name, query=None,
period=None):
"""The method is a customization of test.status_timeout()."""
def check_status():
stat_state_resp = self.ceilometer_client.statistics.list(
meter_name, q=query, period=period)
if len(stat_state_resp) > 0:
return True # All good.
LOG.debug("Waiting for while metrics will available.")
if not fuel_health.test.call_until_true(check_status, 600, 10):
self.fail("Timed out waiting to become alarm")
else:
return self.ceilometer_client.statistics.list(meter_name, q=query,
period=period)
def wait_for_ceilo_objects(self, object_list, query, ceilo_obj_type):
for obj in object_list:
self.wait_for_object_sample(obj, query, ceilo_obj_type)
def create_image_sample(self, image_id):
sample = self.ceilometer_client.samples.create(
resource_id=image_id, counter_name='image', counter_type='delta',
counter_unit='image', counter_volume=1,
resource_metadata={'user': 'example_metadata'})
return sample
def get_samples_count(self, meter_name, query):
return self.ceilometer_client.statistics.list(
meter_name=meter_name, q=query)[0].count
def wait_samples_count(self, meter_name, query, count):
def check_count():
new_count = self.get_samples_count(meter_name, query)
return new_count > count
if not fuel_health.test.call_until_true(check_count, 60, 1):
self.fail('Count of samples list isn\'t '
'greater than expected value')
def check_event_type(self, event_type):
event_list = [event.event_type for event
in self.ceilometer_client.event_types.list()]
if event_type not in event_list:
self.fail('"{event_type}" not found in event type list.'.format(
event_type=event_type))
def check_event_message_id(self, events_list, instance_id):
for event in events_list:
try:
if next(x['value'] for x in event.traits
if x['name'] == "instance_id") == instance_id:
return event.message_id
except StopIteration:
self.fail('Trait "instance_id" not found in trait list.')
self.fail('No events found for "{instance_id}" instance.'.format(
instance_id=instance_id))
def check_traits(self, event_type, traits):
trait_desc = [desc.name for desc in
self.ceilometer_client.trait_descriptions.list(
event_type)]
for trait in traits:
if trait not in trait_desc:
self.fail('Trait "{trait}" not found in trait list.'.format(
trait=trait))
def identity_helper(self):
user_pass = rand_name("ceilo-user-pass")
user_name = rand_name("ceilo-user-update")
tenant_name = rand_name("ceilo-tenant-update")
tenant = self.identity_client.tenants.create(rand_name("ceilo-tenant"))
self.objects_for_delete.append((
self.identity_client.tenants.delete, tenant))
self.identity_client.tenants.update(tenant.id, name=tenant_name)
user = self.identity_client.users.create(
rand_name("ceilo-user"), user_pass, tenant.id)
self.objects_for_delete.append((
self.identity_client.users.delete, user))
self.identity_client.users.update(user, name=user_name)
role = self.identity_v3_client.roles.create(rand_name("ceilo-role"))
self.identity_v3_client.roles.update(
role, user=user.id, project=tenant.id)
self.identity_v3_client.roles.grant(
role, user=user.id, project=tenant.id)
self.objects_for_delete.append((
self.identity_client.roles.delete, role))
user_client = self.manager_class()._get_identity_client(
user_name, user_pass, tenant_name, 3)
trust = user_client.trusts.create(
self.identity_v3_client.user_id, user.id, [role.name], tenant.id)
self.objects_for_delete.append((user_client.trusts.delete, trust))
group = self.identity_v3_client.groups.create(rand_name("ceilo-group"))
self.objects_for_delete.append((
self.identity_v3_client.groups.delete, group))
self.identity_v3_client.groups.update(
group, name=rand_name("ceilo-group-update"))
self.identity_v3_client.groups.delete(group)
user_client.trusts.delete(trust)
self.identity_v3_client.roles.revoke(
role, user=user.id, project=tenant.id)
self.identity_client.roles.delete(role)
self.identity_client.users.delete(user)
self.identity_client.tenants.delete(tenant)
return tenant, user, role, group, trust
def neutron_helper(self):
net = self.neutron_client.create_network(
{"network": {"name": rand_name("ceilo-net")}})["network"]
self.addCleanup(self.cleanup_resources,
[(self.neutron_client.delete_network, net["id"])])
self.neutron_client.update_network(
net["id"], {"network": {"name": rand_name("ceilo-net-update")}})
subnet = self.neutron_client.create_subnet(
{"subnet": {"name": rand_name("ceilo-subnet"),
"network_id": net["id"],
"ip_version": 4,
"cidr": "10.0.7.0/24"}})["subnet"]
self.addCleanup(self.cleanup_resources,
[(self.neutron_client.delete_subnet, subnet["id"])])
self.neutron_client.update_subnet(
subnet["id"], {"subnet": {"name": rand_name("ceilo-subnet")}})
port = self.neutron_client.create_port({
"port": {"name": rand_name("ceilo-port"),
"network_id": net["id"]}})['port']
self.addCleanup(self.cleanup_resources,
[(self.neutron_client.delete_port, port["id"])])
self.neutron_client.update_port(
port["id"], {"port": {"name": rand_name("ceilo-port-update")}})
router = self.neutron_client.create_router(
{"router": {"name": rand_name("ceilo-router")}})['router']
self.addCleanup(self.cleanup_resources,
[(self.neutron_client.delete_router, router["id"])])
self.neutron_client.update_router(
router["id"],
{"router": {"name": rand_name("ceilo-router-update")}})
external_network = self.find_external_network()
try:
body = {
"floatingip": {
"floating_network_id": external_network["id"]
}
}
fl_ip = self.neutron_client.create_floatingip(body)["floatingip"]
except neutron_exc.IpAddressGenerationFailureClient:
self.fail('No more IP addresses available on external network.')
self.addCleanup(self.cleanup_resources,
[(self.neutron_client.delete_floatingip, fl_ip["id"])])
self.neutron_client.update_floatingip(
fl_ip["id"], {"floatingip": {"port_id": None}})
self.neutron_client.delete_floatingip(fl_ip["id"])
self.neutron_client.delete_router(router["id"])
self.neutron_client.delete_port(port["id"])
self.neutron_client.delete_subnet(subnet["id"])
self.neutron_client.delete_network(net["id"])
return net, subnet, port, router, fl_ip
def sahara_helper(self, image_id, plugin_name, hadoop_version):
# Find flavor id for sahara instances
flavor_id = next(
flavor.id for flavor in
self.compute_client.flavors.list() if flavor.name == 'm1.small')
private_net_id, floating_ip_pool = self.create_network_resources()
# Create json for node group
node_group = {'name': 'all-in-one',
'flavor_id': flavor_id,
'node_processes': ['nodemanager', 'datanode',
'resourcemanager', 'namenode',
'historyserver'],
'count': 1,
'auto_security_group': True}
if floating_ip_pool:
node_group['floating_ip_pool'] = floating_ip_pool
# Create json for Sahara cluster
cluster_json = {'name': rand_name("ceilo-cluster"),
'plugin_name': plugin_name,
'hadoop_version': hadoop_version,
'default_image_id': image_id,
'cluster_configs': {'HDFS': {'dfs.replication': 1}},
'node_groups': [node_group],
'net_id': private_net_id}
# Create Sahara cluster
cluster = self.sahara_client.clusters.create(**cluster_json)
self.addCleanup(
self.delete_resource,
delete_method=lambda: self.sahara_client.clusters.delete(
cluster.id),
get_method=lambda: self.sahara_client.clusters.get(cluster.id))
# Wait for change cluster state for metric: cluster.update
def check_status():
cluster_state = self.sahara_client.clusters.get(cluster.id).status
return cluster_state in ['Waiting', 'Active', 'Error']
fuel_health.test.call_until_true(check_status, 300, 1)
# Delete cluster
self.sahara_client.clusters.delete(cluster.id)
return cluster
def glance_helper(self):
image = self.glance_client.images.create(
name=rand_name('ostf-ceilo-image'))
self.objects_for_delete.append((self.glance_client.images.delete,
image.id))
self.glance_client.images.update(image.id, data='data',
disk_format='qcow2',
container_format='bare')
self.glance_client.images.upload(image.id, 'upload_data')
self.glance_client.images.data(image.id)
self.glance_client.images.delete(image.id)
return image
def volume_helper(self, instance):
device = '/dev/vdb'
# Create a volume
volume = self.volume_client.volumes.create(
name=rand_name('ost1_test-ceilo-volume'), size=1)
self.addCleanup(
self.delete_resource,
delete_method=lambda: self.volume_client.volumes.delete(volume),
get_method=lambda: self.volume_client.volumes.get(volume.id))
# Wait for "Available" status of the volume
self.wait_for_resource_status(
self.volume_client.volumes, volume.id, 'available')
# Resize the volume
self.volume_client.volumes.extend(volume, 2)
self.wait_for_resource_status(
self.volume_client.volumes, volume.id, 'available')
# Create a volume snapshot
snapshot = self.volume_client.volume_snapshots.create(
volume.id, name=rand_name('ost1_test-'))
self.addCleanup(
self.delete_resource,
delete_method=lambda: self.volume_client.volume_snapshots.delete(
snapshot),
get_method=lambda: self.volume_client.volume_snapshots.get(
snapshot.id))
# Wait for "Available" status of the snapshot
self.wait_for_resource_status(
self.volume_client.volume_snapshots, snapshot.id, 'available')
# Update the volume name
self.volume_client.volumes.update(volume, name="ost1_test-update")
# Attach the volume to the instance
self.volume_client.volumes.attach(volume.id, instance.id, device)
# Detach the volume from the instance
self.volume_client.volumes.detach(volume.id)
# Delete the volume snapshot
self.delete_resource(
delete_method=lambda: self.volume_client.volume_snapshots.delete(
snapshot),
get_method=lambda: self.volume_client.volume_snapshots.get(
snapshot.id))
# Delete the volume
self.delete_resource(
delete_method=lambda: self.volume_client.volumes.delete(volume),
get_method=lambda: self.volume_client.volumes.get(volume.id))
return volume, snapshot
@staticmethod
def cleanup_resources(object_list):
for method, resource in object_list:
try:
method(resource)
except Exception:
LOG.exception("")
@classmethod
def tearDownClass(cls):
if cls.manager.clients_initialized:
cls.cleanup_resources(cls.objects_for_delete)
super(CeilometerBaseTest, cls).tearDownClass()