Tempest tests for Image_Synchronization.
Added Tempest testcases for Kingbird image_sync. Added test_image_sync.py to resource_sync directory which contains tempest testcases for image_sync. Depends-on: I7fbeec5376dc3a2333a7cc9ad51108feb79a7b03 Implements: blueprint image-synchronization Change-Id: I58b54eedd2956990991dc15a37cf0ff246341acb
This commit is contained in:
parent
3c2bc63a9f
commit
47e1cd48a2
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2016 Ericsson AB
|
||||
# Copyright 2016 Ericsson AB.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -28,6 +28,8 @@ DEFAULT_QUOTAS = {
|
|||
|
||||
KEYPAIR_RESOURCE_TYPE = "keypair"
|
||||
|
||||
IMAGE_RESOURCE_TYPE = "image"
|
||||
|
||||
JOB_SUCCESS = "SUCCESS"
|
||||
|
||||
JOB_PROGRESS = "IN_PROGRESS"
|
||||
|
|
|
@ -13,21 +13,27 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
from glanceclient import Client as gl_client
|
||||
from keystoneclient.auth.identity import v3
|
||||
from keystoneclient import session
|
||||
from keystoneclient.v3 import client as ks_client
|
||||
from kingbirdclient.api.v1 import client as kb_client
|
||||
from novaclient import client as nv_client
|
||||
import six
|
||||
import tempest.test
|
||||
|
||||
from tempest.api.compute import base as kp_base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib.common.utils import test_utils
|
||||
|
||||
from kingbird.tests.tempest.scenario import consts
|
||||
from novaclient import client as nv_client
|
||||
|
||||
CONF = config.CONF
|
||||
KINGBIRD_URL = CONF.kingbird.endpoint_url + CONF.kingbird.api_version
|
||||
NOVA_API_VERSION = "2.37"
|
||||
GLANCE_API_VERSION = "2"
|
||||
|
||||
|
||||
class BaseKingbirdClass(object):
|
||||
|
@ -56,6 +62,24 @@ class BaseKingbirdClass(object):
|
|||
result['target'] = target_regions
|
||||
return result
|
||||
|
||||
def _image_sync_job_create(self, force, **kwargs):
|
||||
result = dict()
|
||||
images = self._create_images(**kwargs)
|
||||
admin_client = self._get_admin_keystone()
|
||||
regions = self._get_regions(admin_client)
|
||||
target_regions = regions
|
||||
target_regions.remove(self.client.region)
|
||||
# Now sync the created images in other regions.
|
||||
create_response = self._sync_job_create(
|
||||
consts.IMAGE_RESOURCE_TYPE, target_regions, images.keys(),
|
||||
force=force)
|
||||
job_id = create_response.get('job_status').get('id')
|
||||
result['job_id'] = job_id
|
||||
result['admin'] = admin_client
|
||||
result['target'] = target_regions
|
||||
result['images'] = images
|
||||
return result
|
||||
|
||||
def _check_job_status(self):
|
||||
# Wait until the status of the job is not "IN_PROGRESS"
|
||||
job_list_resp = self.get_sync_job_list()
|
||||
|
@ -192,3 +216,179 @@ class BaseKBKeypairTest(kp_base.BaseV2ComputeTest):
|
|||
project_id=self.client.tenant_id)
|
||||
self.addCleanup(self._delete_keypair, keypair_name, **delete_params)
|
||||
return body
|
||||
|
||||
|
||||
class BaseKBImageTest(tempest.test.BaseTestCase):
|
||||
"""Base test class for Image API tests."""
|
||||
|
||||
credentials = ['primary']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseKBImageTest, cls).skip_checks()
|
||||
if not CONF.service_available.glance:
|
||||
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
|
||||
raise cls.skipException(skip_msg)
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
cls.set_network_resources()
|
||||
super(BaseKBImageTest, cls).setup_credentials()
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseKBImageTest, cls).setup_clients()
|
||||
cls.client = cls.os.image_client_v2
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseKBImageTest, cls).resource_setup()
|
||||
cls.created_images = []
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
for image_id in cls.created_images:
|
||||
test_utils.call_and_ignore_notfound_exc(
|
||||
cls.client.delete_image, image_id)
|
||||
|
||||
for image_id in cls.created_images:
|
||||
cls.client.wait_for_resource_deletion(image_id)
|
||||
super(BaseKBImageTest, cls).resource_cleanup()
|
||||
|
||||
@classmethod
|
||||
def create_and_upload_image(cls, data=None, **kwargs):
|
||||
"""Wrapper that returns a test image."""
|
||||
if 'name' not in kwargs:
|
||||
name = data_utils.rand_name("kb-image")
|
||||
kwargs['name'] = name
|
||||
|
||||
params = cls._get_create_params(**kwargs)
|
||||
if data:
|
||||
# NOTE: On glance v1 API, the data should be passed on
|
||||
# a header. Then here handles the data separately.
|
||||
params['data'] = data
|
||||
|
||||
image = cls.client.create_image(**params)
|
||||
# Image objects returned by the v1 client have the image
|
||||
# data inside a dict that is keyed against 'image'.
|
||||
if 'image' in image:
|
||||
image = image['image']
|
||||
cls.created_images.append(image['id'])
|
||||
# Upload image to glance artifactory.
|
||||
file_content = data_utils.random_bytes()
|
||||
image_file = six.BytesIO(file_content)
|
||||
cls.client.store_image_file(image['id'], image_file)
|
||||
cls.kingbird_client = kb_client.Client(
|
||||
kingbird_url=KINGBIRD_URL, auth_token=cls.client.token,
|
||||
project_id=cls.client.tenant_id)
|
||||
return image
|
||||
|
||||
@classmethod
|
||||
def _get_create_params(cls, **kwargs):
|
||||
return kwargs
|
||||
|
||||
def _get_endpoint_from_region(self, keystone_admin, region):
|
||||
services_list = keystone_admin.services.list()
|
||||
endpoints_list = keystone_admin.endpoints.list()
|
||||
service_id = [service.id for service in
|
||||
services_list if service.type == 'image'][0]
|
||||
|
||||
glance_endpoint = [endpoint.url for endpoint in
|
||||
endpoints_list
|
||||
if endpoint.service_id == service_id
|
||||
and endpoint.region == region and
|
||||
endpoint.interface == 'public'][0]
|
||||
return glance_endpoint
|
||||
|
||||
def _create_images(self, **kwargs):
|
||||
images = dict()
|
||||
for _ in range(2):
|
||||
image = self.create_and_upload_image(**kwargs)
|
||||
images[image['id']] = image['name']
|
||||
return images
|
||||
|
||||
def _sync_ami_image(self, force, ami_id):
|
||||
result = dict()
|
||||
admin_client = self._get_admin_keystone()
|
||||
regions = self._get_regions(admin_client)
|
||||
target_regions = regions
|
||||
target_regions.remove(self.client.region)
|
||||
# Now sync the created images in other regions.
|
||||
create_response = self._sync_job_create(
|
||||
consts.IMAGE_RESOURCE_TYPE, target_regions, [ami_id],
|
||||
force=force)
|
||||
job_id = create_response.get('job_status').get('id')
|
||||
result['job_id'] = job_id
|
||||
result['admin'] = admin_client
|
||||
result['target'] = target_regions
|
||||
result['images'] = ami_id
|
||||
return result
|
||||
|
||||
def _check_image_properties_in_target_region(self, image, **kwargs):
|
||||
if 'architecture' in kwargs:
|
||||
self.assertEqual(image.architecture,
|
||||
kwargs['architecture'])
|
||||
if 'hypervisor_type' in kwargs:
|
||||
self.assertEqual(image.hypervisor_type,
|
||||
kwargs['hypervisor_type'])
|
||||
if 'ramdisk_id' in kwargs:
|
||||
self.assertIsNotNone(image.ramdisk_id)
|
||||
if 'kernel_id' in kwargs:
|
||||
self.assertIsNotNone(image.kernel_id)
|
||||
|
||||
self.assertEqual(image.container_format,
|
||||
kwargs['container_format'])
|
||||
self.assertEqual(image.disk_format,
|
||||
kwargs['disk_format'])
|
||||
self.assertEqual(image.visibility,
|
||||
kwargs['visibility'])
|
||||
|
||||
def _check_images_delete_target_region(self, keystone_admin,
|
||||
target_regions, images,
|
||||
force, **kwargs):
|
||||
for region in target_regions:
|
||||
for image in images:
|
||||
region_endpoint = self._get_endpoint_from_region(
|
||||
keystone_admin, region)
|
||||
glance_client = gl_client(GLANCE_API_VERSION,
|
||||
session=self.sess,
|
||||
endpoint=region_endpoint)
|
||||
target_region_images = glance_client.images.list()
|
||||
for target_image in target_region_images:
|
||||
if target_image['name'] == images[image]:
|
||||
region_image = glance_client.images.\
|
||||
get(target_image['id'])
|
||||
if ast.literal_eval(force):
|
||||
self.assertNotEqual(region_image.id, image)
|
||||
else:
|
||||
self.assertEqual(region_image.id, image)
|
||||
self._check_image_properties_in_target_region(
|
||||
region_image, **kwargs)
|
||||
glance_client.images.delete(target_image['id'])
|
||||
|
||||
def _check_and_delete_dependent_images_target_region(self, keystone_admin,
|
||||
target_regions, image,
|
||||
force, **kwargs):
|
||||
for region in target_regions:
|
||||
region_endpoint = self._get_endpoint_from_region(keystone_admin,
|
||||
region)
|
||||
glance_client = gl_client(GLANCE_API_VERSION, session=self.sess,
|
||||
endpoint=region_endpoint)
|
||||
target_region_images = glance_client.images.list()
|
||||
for target_image in target_region_images:
|
||||
if target_image['name'] == image['name']:
|
||||
region_image = glance_client.images.\
|
||||
get(target_image['id'])
|
||||
self._check_image_properties_in_target_region(
|
||||
region_image, **kwargs)
|
||||
if ast.literal_eval(force):
|
||||
self.assertNotEqual(region_image.id, image['id'])
|
||||
else:
|
||||
self.assertEqual(region_image.id, image['id'])
|
||||
source_aki_image = glance_client.images.get(
|
||||
region_image.kernel_id)
|
||||
glance_client.images.delete(source_aki_image.id)
|
||||
source_ari_image = glance_client.images.get(
|
||||
region_image.ramdisk_id)
|
||||
glance_client.images.delete(source_ari_image.id)
|
||||
glance_client.images.delete(target_image['id'])
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
# Copyright 2017 Ericsson AB.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 kingbirdclient
|
||||
from tempest import config
|
||||
from tempest.lib import decorators
|
||||
|
||||
from kingbird.tests.tempest.scenario import consts
|
||||
from kingbird.tests.tempest.scenario.resource_management.sync_tests \
|
||||
import base
|
||||
from kingbird.tests import utils
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
FORCE = "True"
|
||||
DEFAULT_FORCE = "False"
|
||||
|
||||
|
||||
class KingbirdImageSyncTest(base.BaseKBImageTest, base.BaseKingbirdClass):
|
||||
"""Here we test the basic operations of images."""
|
||||
|
||||
@decorators.idempotent_id('f06e43f5-b4e5-40af-92f5-1d280e398d9b')
|
||||
def test_qcow2_force_image_sync(self):
|
||||
"""Here we test these functionalities.
|
||||
|
||||
Register image, upload the image file, get image and check
|
||||
if image is created in target regions.
|
||||
"""
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private'
|
||||
}
|
||||
job_details = self._image_sync_job_create(FORCE, **create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], FORCE, **create_params)
|
||||
# Clean_up the database entries and resources
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('791c9c00-d409-4cae-a4b0-306f4dc15abb')
|
||||
def test_qcow2_force_image_sync_with_extra_specs(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
"architecture": 'arm',
|
||||
"hypervisor_type": 'qemu'
|
||||
}
|
||||
job_details = self._image_sync_job_create(FORCE, **create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], FORCE, **create_params)
|
||||
# Clean_up the database entries and resources
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('d93d8da5-05dc-4e34-b1af-910d65aad92b')
|
||||
def test_qcow2_image_sync_without_force(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
"architecture": 'arm',
|
||||
"hypervisor_type": 'qemu'
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
# Clean_up the database entries and resources
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('9f51e13d-6958-46f3-a193-3352c5f157dc')
|
||||
def test_get_kingbird_image_sync_list(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
job_list_resp = self.get_sync_job_list()
|
||||
self.assertEqual(job_list_resp['job_set'][0]['id'],
|
||||
job_details['job_id'])
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
# Clean_up the database entries and resources.
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('dfc13300-9d88-4266-bff7-3ea3e56521a7')
|
||||
def test_get_image_sync_job_details(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
job_list_resp = self.get_sync_job_detail(job_details['job_id'])
|
||||
for image_id in job_details['images']:
|
||||
for j in range(len(job_list_resp.get('job_set'))):
|
||||
if image_id in job_list_resp.get('job_set')[j].values():
|
||||
self.assertEqual(
|
||||
job_list_resp.get('job_set')[j].get('resource'),
|
||||
image_id)
|
||||
self.assertEqual(
|
||||
job_list_resp.get('job_set')[0].get('resource_type'),
|
||||
consts.IMAGE_RESOURCE_TYPE)
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
# Clean_up the database entries and resources.
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('65bcfd9e-b9e3-42f1-a77a-3efe50e1b619')
|
||||
def test_get_active_jobs_image_sync(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
active_job = self.get_sync_job_list(consts.JOB_ACTIVE)
|
||||
status = active_job.get('job_set')[0].get('sync_status')
|
||||
self.assertEqual(status, consts.JOB_PROGRESS)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
# Clean_up the database entries
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('24de7f1d-afde-41eb-8eda-abf92dfee144')
|
||||
def test_delete_active_jobs_image_sync(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
self.assertRaisesRegexp(kingbirdclient.exceptions.APIException,
|
||||
"406 *",
|
||||
self.delete_db_entries,
|
||||
job_details['job_id'])
|
||||
# Actual result when we try and delete an active_job
|
||||
# Clean_up the database entries
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
# Clean_up the database entries
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
|
||||
@decorators.idempotent_id('8f463390-0773-4e6b-a152-3a077480d4da')
|
||||
def test_delete_already_deleted_job(self):
|
||||
create_params = {
|
||||
"container_format": CONF.image.container_formats[3],
|
||||
"disk_format": CONF.image.disk_formats[6],
|
||||
"visibility": 'private',
|
||||
}
|
||||
job_details = self._image_sync_job_create(DEFAULT_FORCE,
|
||||
**create_params)
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Clean_up the database entries
|
||||
self.delete_db_entries(job_details['job_id'])
|
||||
self.assertRaisesRegexp(kingbirdclient.exceptions.APIException,
|
||||
"404 *",
|
||||
self.delete_db_entries, job_details['job_id'])
|
||||
# Check for resources in target_regions.
|
||||
self._check_images_delete_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
job_details['images'], DEFAULT_FORCE, **create_params)
|
||||
|
||||
def test_sync_ami_image_with_dependent_images(self):
|
||||
ari_create_params = {
|
||||
"container_format": CONF.image.container_formats[1],
|
||||
"disk_format": CONF.image.disk_formats[1],
|
||||
"visibility": 'private',
|
||||
}
|
||||
ari_image = self.create_and_upload_image(**ari_create_params)
|
||||
aki_create_params = {
|
||||
"container_format": CONF.image.container_formats[2],
|
||||
"disk_format": CONF.image.disk_formats[2],
|
||||
"visibility": 'private',
|
||||
}
|
||||
aki_image = self.create_and_upload_image(**aki_create_params)
|
||||
ami_create_params = {
|
||||
"container_format": CONF.image.container_formats[0],
|
||||
"disk_format": CONF.image.disk_formats[0],
|
||||
"visibility": 'private',
|
||||
"ramdisk_id": ari_image['id'],
|
||||
"kernel_id": aki_image['id']
|
||||
}
|
||||
ami_image = self.create_and_upload_image(**ami_create_params)
|
||||
job_details = self._sync_ami_image(DEFAULT_FORCE, ami_image['id'])
|
||||
# Clean_up the database entries
|
||||
utils.wait_until_true(
|
||||
lambda: self._check_job_status(),
|
||||
exception=RuntimeError("Timed out waiting for job %s " %
|
||||
job_details['job_id']))
|
||||
# Check for resources in target_regions.
|
||||
self._check_and_delete_dependent_images_target_region(
|
||||
job_details['admin'], job_details['target'],
|
||||
ami_image, DEFAULT_FORCE, **ami_create_params)
|
||||
self.delete_db_entries(job_details['job_id'])
|
Loading…
Reference in New Issue