Skip to content

Commit

Permalink
Porting Cinder Backup tests to Zaza
Browse files Browse the repository at this point in the history
Porting the Amulet tests from Cinder Backup to the Zaza framework.

The Amulet tests can be found here:
https://opendev.org/openstack/charm-cinder-backup/src/commit/4273738b94b22cca187487af2bad982be49fdd69/tests/basic_deployment.py
  • Loading branch information
natalytvinova committed Oct 1, 2019
1 parent 17f18e3 commit 6963420
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 1 deletion.
17 changes: 17 additions & 0 deletions zaza/openstack/charm_tests/cinder_backup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/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.

"""Collection of code for setting up and testing cinder-backup."""
182 changes: 182 additions & 0 deletions zaza/openstack/charm_tests/cinder_backup/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#!/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 cinder-backup testing."""
import time
import logging

import zaza.openstack.charm_tests.test_utils as test_utils
import zaza.openstack.utilities.ceph as ceph_utils
import zaza.openstack.utilities.openstack as openstack_utils


class CinderBackupTest(test_utils.OpenStackBaseTest):
"""Encapsulate Cinder Backup tests."""

RESOURCE_PREFIX = 'zaza-cinderbackuptests'

@classmethod
def setUpClass(cls):
"""Run class setup for running Cinder Backup tests."""
super(CinderBackupTest, cls).setUpClass()
cls.cinder_client = openstack_utils.get_cinder_session_client(
cls.keystone_session)

@property
def services(self):
"""Return a list services for Openstack Release."""
current_release = openstack_utils.get_os_release()
services = ['cinder-scheduler', 'cinder-volume']
if (current_release >=
openstack_utils.get_os_release('xenial_ocata')):
services.append('apache2')
else:
services.append('cinder-api')
return services

def test_100_volume_create_extend_delete(self):
"""Test creating, extending a volume."""
vol_new = openstack_utils.create_volume(
self.cinder_client,
name='{}-100-vol'.format(self.RESOURCE_PREFIX),
size=1)
self.cinder_client.volumes.extend(
vol_new.id,
'2')
openstack_utils.resource_reaches_status(
self.cinder_client.volumes,
vol_new.id,
expected_status="available",
msg="Volume status wait")

def test_410_cinder_vol_create_backup_delete_restore_pool_inspect(self):
"""Create, backup, delete, restore a ceph-backed cinder volume.
Create, backup, delete, restore a ceph-backed cinder volume, and
inspect ceph cinder pool object count as the volume is created
and deleted.
"""
unit_name = 'ceph-mon/0'
obj_count_samples = []
pool_size_samples = []
pools = ceph_utils.get_ceph_pools(unit_name)
expected_pool = 'cinder-ceph'
cinder_ceph_pool = pools[expected_pool]

# Check ceph cinder pool object count, disk space usage and pool name
logging.info('Checking ceph cinder pool original samples...')
pool_name, obj_count, kb_used = ceph_utils.get_ceph_pool_sample(
unit_name, cinder_ceph_pool)

obj_count_samples.append(obj_count)
pool_size_samples.append(kb_used)

self.assertEqual(pool_name, expected_pool)

# Create ceph-backed cinder volume
cinder_vol = openstack_utils.create_volume(
self.cinder_client,
name='{}-410-vol'.format(self.RESOURCE_PREFIX),
size=1)
# Backup the volume
vol_backup = openstack_utils.create_volume_backup(
self.cinder_client,
cinder_vol.id,
name='{}-410-backup-vol'.format(self.RESOURCE_PREFIX))
# Delete the volume
openstack_utils.delete_volume(self.cinder_client, cinder_vol.id)
# Restore the volume
self.cinder_client.restores.restore(vol_backup.id)
openstack_utils.resource_reaches_status(
self.cinder_client.backups,
vol_backup.id,
expected_status='available',
msg='Backup status wait')
# Delete the backup
openstack_utils.delete_volume_backup(self.cinder_client, vol_backup.id)

# Re-check ceph cinder pool object count and disk usage
time.sleep(10)
logging.info('Checking ceph cinder pool samples '
'after volume create...')
pool_name, obj_count, kb_used = ceph_utils.get_ceph_pool_sample(
unit_name, cinder_ceph_pool, self.model_name)

obj_count_samples.append(obj_count)
pool_size_samples.append(kb_used)

name = '{}-410-vol'.format(self.RESOURCE_PREFIX)
vols = self.cinder_client.volumes.list()
try:
cinder_vols = [v for v in vols if v.name == name]
except AttributeError:
cinder_vols = [v for v in vols if v.display_name == name]
if not cinder_vols:
# NOTE(hopem): it appears that at some point cinder-backup stopped
# restoring volume metadata properly so revert to default name if
# original is not found
name = "restore_backup_{}".format(vol_backup.id)
try:
cinder_vols = [v for v in vols if v.name == name]
except AttributeError:
cinder_vols = [v for v in vols if v.display_name == name]

self.assertTrue(cinder_vols)

cinder_vol = cinder_vols[0]

# Delete restored cinder volume
openstack_utils.delete_volume(self.cinder_client, cinder_vol.id)

# Final check, ceph cinder pool object count and disk usage
time.sleep(10)
logging.info('Checking ceph cinder pool after volume delete...')
pool_name, obj_count, kb_used = ceph_utils.get_ceph_pool_sample(
unit_name, cinder_ceph_pool, self.model_name)

obj_count_samples.append(obj_count)
pool_size_samples.append(kb_used)

# Validate ceph cinder pool object count samples over time
original, created, deleted = range(3)
self.assertFalse(obj_count_samples[created] <=
obj_count_samples[original])
self.assertFalse(obj_count_samples[deleted] >=
obj_count_samples[created])

# Luminous (pike) ceph seems more efficient at disk usage so we cannot
# grantee the ordering of kb_used
if (openstack_utils.get_os_release() <
openstack_utils.get_os_release('xenial_mitaka')):
self.assertFalse(pool_size_samples[created] <=
pool_size_samples[original])
self.assertFalse(pool_size_samples[deleted] >=
pool_size_samples[created])

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
"""
services = ['cinder-scheduler', 'cinder-volume']
if (openstack_utils.get_os_release() >=
openstack_utils.get_os_release('xenial_ocata')):
services.append('apache2')
else:
services.append('cinder-api')
with self.pause_resume(services):
logging.info("Testing pause resume")
50 changes: 49 additions & 1 deletion zaza/openstack/utilities/ceph.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Module containing Ceph related utilities."""

import json
import logging

import zaza.openstack.utilities.openstack as openstack_utils
Expand Down Expand Up @@ -97,6 +97,54 @@ def get_ceph_pools(unit_name, model_name=None):
return pools


def get_ceph_df(unit_name, model_name=None):
"""Return dict of ceph df json output, including ceph pool state.
:param unit_name: Name of the unit to get ceph df
:type unit_name: string
:param model_name: Name of model to operate in
:type model_name: str
:returns: Dict of ceph df output
:rtype: dict
:raise: zaza.model.CommandRunFailed
"""
cmd = 'sudo ceph df --format=json'
result = zaza_model.run_on_unit(unit_name, cmd, model_name=model_name)
if result.get('Code') != '0':
raise zaza_model.CommandRunFailed(cmd, result)
return json.loads(result.get('Stdout'))


def get_ceph_pool_sample(unit_name, pool_id=0, model_name=None):
"""Return list of ceph pool attributes.
Take a sample of attributes of a ceph pool, returning ceph
pool name, object count and disk space used for the specified
pool ID number.
:param unit_name: Name of the unit to get the pool sample
:type unit_name: string
:param pool_id: Ceph pool ID
:type pool_id: int
:param model_name: Name of model to operate in
:type model_name: str
:returns: List of pool name, object count, kb disk space used
:rtype: list
:raises: zaza.model.CommandRunFailed
"""
df = get_ceph_df(unit_name, model_name)
for pool in df['pools']:
if pool['id'] == pool_id:
pool_name = pool['name']
obj_count = pool['stats']['objects']
kb_used = pool['stats']['kb_used']

logging.debug('Ceph {} pool (ID {}): {} objects, '
'{} kb used'.format(pool_name, pool_id,
obj_count, kb_used))
return pool_name, obj_count, kb_used


def get_rbd_hash(unit_name, pool, image, model_name=None):
"""Get SHA512 hash of RBD image.
Expand Down

0 comments on commit 6963420

Please sign in to comment.