class ListVersions(ImagesFixture): @data_driven_test(ImagesDatasetListGenerator.Versions()) def ddtest_list_versions(self, url_addition): """ @summary: List all versions @param url_addition: Parameter being passed to the list versions request @type url_addition: Dictionary 1) Retrieve the versions data file 2) List all versions passing in each url addition 3) Verify that the response code is 300 4) Verify that the number of versions received matches the number of versions in the versions data file 5) For each version returned, verify that the data matches the versions data file """ errors = [] versions_data = self.images.behaviors.get_comparison_data( self.images.config.versions_data) resp = self.images.client.list_versions(url_addition) self.assertEqual( resp.status_code, 300, Messages.STATUS_CODE_MSG.format(300, resp.status_code)) listed_versions = resp.entity self.assertEqual(len(listed_versions), len(versions_data)) for version in listed_versions: version_data = versions_data[version.id_] if version.links[0].href != version_data.get('href'): errors.append(self.error_msg.format( 'href', version_data.get('href'), version.links[0].href)) if version.links[0].rel != version_data.get('rel'): errors.append(self.error_msg.format( 'rel', version_data.get('rel'), version.links[0].rel)) if version.status.lower() != version_data.get('status').lower(): errors.append(self.error_msg.format( 'status', version_data.get('status').lower(), version.status.lower())) # Allows the full error list to be returned if it is larger than normal self.assertEqual.im_class.maxDiff = None self.assertListEqual( errors, [], msg=('Unexpected errors received. Expected: No errors ' 'Received: {0}').format(errors))
class VersionsSmoke(ImagesFixture): @data_driven_test(ImagesDatasetListGenerator.Versions()) def ddtest_list_versions(self, url_addition): """ @summary: List all versions @param url_addition: Paremter being passed to the list versions request @type url_addition: Dictonary 1) List all versions passing in each url addition 2) Verify that the response code is 300 """ resp = self.images.client.list_versions(url_addition) self.assertEqual( resp.status_code, 300, Messages.STATUS_CODE_MSG.format(300, resp.status_code))
class ImageOperationsSmoke(ImagesFixture): @classmethod def setUpClass(cls): super(ImageOperationsSmoke, cls).setUpClass() # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('image_operations_smoke')}, count=4) cls.image = created_images.pop() cls.alt_image = created_images.pop() cls.activated_image = created_images.pop() cls.deactivated_image = created_images.pop() cls.images_admin.client.deactivate_image(cls.deactivated_image.id_) @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(ImageOperationsSmoke, cls).tearDownClass() def test_register_image(self): """ @summary: Register an image 1) Register an image 2) Verify that the response code is 201 3) Add the image to the resource pool for deletion """ resp = self.images.client.register_image( name=rand_name('image_operations_smoke')) self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) image = resp.entity self.resources.add(image.id_, self.images.client.delete_image) @data_driven_test(ImagesDatasetListGenerator.ListImagesSmoke()) def ddtest_list_images(self, params): """ @summary: List a subset of images passing in valid query parameters @param params: Parameter being passed to the list images request @type params: Dictionary 1) List subset of images passing in a query parameter 2) Verify the response status code is 200 """ resp = self.images.client.list_images(params) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) @data_driven_test(ImagesDatasetListGenerator.ListImagesInvalidParameters()) def ddtest_invalid_list_images(self, prop): """ @summary: Attempt to list a subset of images passing in invalid query parameters @param params: Parameter being passed to the list images request @type params: Dictionary 1) Attempt to list a subset of images passing in an invalid query parameter 2) If the invalid query parameter is created_at or updated_at, verify the response status code is 400 3) If the invalid query parameter is not created_at or updated_at, verify the response status code is 200 """ unacceptable_props = ['created_at', 'updated_at'] # Most invalid parameters should be ignored, should return 200 response resp = self.images.client.list_images(prop) # Prop will always only have a single key try: if prop.keys()[0] in unacceptable_props: self.assertEqual( resp.status_code, 400, Messages.STATUS_CODE_MSG.format(400, resp.status_code)) else: self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) except IndexError, error: self.fail(msg='No image property passed in. ' 'Error: {0}'.format(error))
class ListImages(ImagesIntegrationFixture): @classmethod def setUpClass(cls): super(ListImages, cls).setUpClass() cls.alt_one_member = cls.images_alt_one.auth.tenant_id # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('list_images')}, count=4) cls.created_image = created_images.pop() cls.images.client.create_image_member(cls.created_image.id_, cls.alt_one_member) resp = cls.images.client.get_image_member(cls.created_image.id_, cls.alt_one_member) cls.created_image_member = resp.entity cls.shared_image = created_images.pop() cls.images.client.create_image_member(cls.shared_image.id_, cls.alt_one_member) image = created_images.pop() cls.images_admin.client.deactivate_image(image.id_) cls.deactivated_imported_image = (cls.images.client.get_image_details( image.id_)).entity alt_image = created_images.pop() cls.images_admin.client.deactivate_image(alt_image.id_) cls.images_admin.client.reactivate_image(alt_image.id_) cls.reactivated_imported_image = (cls.images.client.get_image_details( alt_image.id_)).entity created_server = cls.compute.servers.behaviors.create_active_server( image_ref=cls.images.config.primary_image).entity cls.resources.add(created_server.id, cls.compute.servers.client.delete_server) snapshot_image = cls.compute.images.behaviors.create_active_image( created_server.id).entity cls.resources.add(snapshot_image.id, cls.images.client.delete_image) cls.images_admin.client.deactivate_image(snapshot_image.id) cls.deactivated_snapshot_image = (cls.images.client.get_image_details( snapshot_image.id).entity) alt_snapshot_image = cls.compute.images.behaviors.create_active_image( created_server.id).entity cls.resources.add(alt_snapshot_image.id, cls.images.client.delete_image) cls.images_admin.client.deactivate_image(alt_snapshot_image.id) cls.images_admin.client.reactivate_image(alt_snapshot_image.id) cls.reactivated_snapshot_image = (cls.images.client.get_image_details( alt_snapshot_image.id).entity) @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(ListImages, cls).tearDownClass() def test_compare_list_images_between_glance_and_nova(self): """ @summary: Compare the list of images returned from the glance api and the nova api 1) List images with a limit set to 100 and image_type set to base through the glance api 2) Verify that the response is ok 3) Verify that the images returned is not none 4) List images with a limit set to 100 and image_type set to base through the nova api 5) Verify that the response is ok 6) Verify that the images returned is not none 7) Verify that the length of the list of images is the same through the glance api and the nova api 8) Verify that each image name in the list of images is the same through the glance api and the nova api """ resp = self.images.client.list_images(params={ 'limit': 100, 'image_type': ImageType.BASE }) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) glance_image_names = [image.name for image in resp.entity] self.assertIsNotNone(glance_image_names, msg=('Unexpected images received.' 'Expected: At least one image received ' 'Received: No images received')) resp = self.compute.images.client.list_images_with_detail( limit=100, image_type='base') self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) nova_image_names = [image.name for image in resp.entity] self.assertIsNotNone(nova_image_names, msg=('Unexpected images received.' 'Expected: At least one image received ' 'Received: No images received')) self.assertEqual( len(nova_image_names), len(glance_image_names), msg=('Unexpected images received. Expected: Number of Nova images ' '({0}) to match number of Glance images ({1}) Received: ' 'Number of images do not ' 'match'.format(len(nova_image_names), len(glance_image_names)))) images_diff = list(set(glance_image_names) - set(nova_image_names)) self.assertEqual(images_diff, [], msg=('Unexpected images listed in Glance that are ' 'not listed in Nova: {0}').format(images_diff)) def test_image_visibility_of_shared_images(self): """ @summary: Image visibility of shared images 1) Using alt_one_member, list all images 2) Verify that shared_image is not present 3) Using alt_one_member, list all images passing visibility as shared and member status as all 4) Verify that shared_image is now present 5) Using alt_one_member, update image member status to rejected 6) Verify that the response is ok 7) Using alt_one_member, list all images passing visibility as shared and member status as all 8) Verify that shared_image is still present """ listed_images = self.images_alt_one.behaviors.list_all_images() self.assertNotIn( self.shared_image, listed_images, msg=('Unexpected image received. Expected: {0} to not be in list ' 'of images Received: {1}').format(self.shared_image, listed_images)) listed_images = self.images_alt_one.behaviors.list_all_images( visibility=ImageVisibility.SHARED, member_status=ImageMemberStatus.ALL) self.assertIn( self.shared_image, listed_images, msg=('Expected image not received. Expected: {0} to be in list of ' 'images Received: {1}').format(self.shared_image, listed_images)) resp = self.images_alt_one.client.update_image_member( self.shared_image.id_, self.alt_one_member, ImageMemberStatus.REJECTED) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = self.images_alt_one.behaviors.list_all_images( visibility=ImageVisibility.SHARED, member_status=ImageMemberStatus.ALL) self.assertIn( self.shared_image, listed_images, msg=('Expected image not received. Expected: {0} to be in list of ' 'images Received: {1}').format(self.shared_image, listed_images)) @data_driven_test(ImagesDatasetListGenerator.ListImagesFilters()) def ddtest_filter_images_list(self, params): """ @summary: List all images that match a filter, passing in a specific query parameter @param param: Parameter being passed to the list images request @type param: Dictionary 1) List all images passing in a query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for the given filter that is acceptable """ api_args = {} for param in params: if param == 'id_': api_args.update({'id': getattr(self.created_image, param)}) elif param == 'created_at' or param == 'updated_at': # Params will always only have a single value api_args.update({param: 'lte:{0}'.format(params[param])}) else: api_args.update({param: getattr(self.created_image, param)}) listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not 0 ' 'Received: {0}').format(len(listed_images))) for image in listed_images: for param in params: received = getattr(image, param) expected = getattr(self.created_image, param) if param == 'created_at' or param == 'updated_at': # Params will always only have a single value received = datetime.datetime.strptime( str(received), '%Y-%m-%d %H:%M:%S+00:00') expected = datetime.datetime.strptime( params[param], '%Y-%m-%dT%H:%M:%SZ') self.assertLessEqual( received, expected, msg=('Unexpected property value for image {0} ' 'received. Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) else: self.assertEqual( received, expected, msg=('Unexpected property value for image {0} ' 'received. Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_additional_property(self): """ @summary: List all images that match a filter, passing in an additional property as the query parameter 1) List all images passing in an additional property as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for the additional property that matches the additional property that is being used as the filter """ prop = self.images.config.additional_property api_args = ({prop: self.created_image.additional_properties.get(prop)}) listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not 0 ' 'Received: {0}').format(len(listed_images))) for image in listed_images: received = image.additional_properties.get(prop) expected = (self.created_image.additional_properties.get(prop)) self.assertEqual(received, expected, msg=('Unexpected property for image {0} received.' 'Expected: {1} ' 'Received: {2}').format( image.id_, expected, received)) def test_filter_images_list_passing_member_status_pending(self): """ @summary: List all images that match in member_status of pending and visibility of shared as the query parameters 1) List all images passing in member_status of pending and visibility of shared as the query parameters 2) Verify that the list of images returned is not empty 3) Verify that each image returned list image members 4) Verify that each image returned contains a value for member_status that matches the member_status that is being used as the filter """ api_args = ({ 'member_status': getattr(self.created_image_member, 'status'), 'visibility': ImageVisibility.SHARED }) listed_images = self.images_alt_one.behaviors.list_all_images( **api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: image_member_statuses = [] resp = self.images.client.list_image_members(image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) list_image_members = resp.entity for member in list_image_members: image_member_statuses.append(member.status) received_image_member_status = (getattr( self.created_image_member, 'status')) self.assertIn(getattr(self.created_image_member, 'status'), image_member_statuses, msg=('Unexpected image member status for image' '{0} received. Expected: {1} in list of' 'image member statuses Received: ' '{2}').format(image.id_, received_image_member_status, image_member_statuses)) def test_filter_images_list_passing_size_max(self): """ @summary: List all images that match a filter, passing in size_max as the query parameter 1) List all images passing in size_max as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is less than or equal to the size_max that is being used as the filter """ api_args = {'size_max': self.images.config.size_max} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_max self.assertLessEqual( received, expected, msg=('Unexpected size_max for image {0} received.' 'Expected: <= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_size_max_and_size_min(self): """ @summary: List all images that match a filter, passing in both size_max and size_min as the query parameters 1) List all images passing in size_max and size_min as the query parameters 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is less than or equal to the size_max that is being used as the filter and greater than or equal to the size_min that is being used as the filter """ api_args = { 'size_max': self.images.config.size_max, 'size_min': self.images.config.size_min } listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_max self.assertLessEqual( received, expected, msg=('Unexpected size_max for image {0} received.' 'Expected: <= {1} ' 'Received: {2}').format(image.id_, expected, received)) expected = getattr(self.images.config, 'size_min') self.assertGreaterEqual( received, expected, msg=('Unexpected size_min for image {0} received.' 'Expected: >= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_size_min(self): """ @summary: List all images that match a filter, passing in size_min as the query parameter 1) List all images passing in size_min as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is greater than or equal to the size_min that is being used as the filter """ api_args = {'size_min': self.images.config.size_min} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_min self.assertGreaterEqual( received, expected, msg=('Unexpected size_min for image {0} received.' 'Expected: >= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_tag(self): """ @summary: List all images that match a filter, passing in tag as the query parameter 1) List all images passing in tag as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for tags that matches the tag that is being used as the filter """ api_args = {'tag': getattr(self.created_image, 'tags')} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'tags') expected = getattr(self.created_image, 'tags') self.assertEqual( received, expected, msg=('Unexpected tags for image {0} received. Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_list_images_using_limit(self): """ @summary: List images using limit 1) List images passing in 1 as the limit 2) Verify that the response code is ok 4) Verify that the number of images returned is 1 """ resp = self.images.client.list_images(params={'limit': 1}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = resp.entity self.assertEqual( len(listed_images), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(listed_images))) def test_list_images_using_limit_zero(self): """ @summary: List images using limit of zero 1) List images passing in 0 as the limit 2) Verify that the response code is ok 3) Verify that the number of images returned is 0 """ resp = self.images.client.list_images(params={'limit': 0}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = resp.entity self.assertEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(0, len(listed_images))) def test_list_images_using_marker_pagination(self): """ @summary: List images using marker pagination 1) List images passing in 1 as the limit, the created image's name as the name, and the user as the owner 2) Verify that the response code is ok 3) Verify that the returned list only contains 1 image 4) List images again passing in the listed image id as the marker as well as 1 as the limit, the created image's name as the name, and the user as the owner 5) Verify that the response code is ok 6) Verify that the returned list only contains 1 image 7) Verify that the newly returned image is not the same as the previously returned image """ resp = self.images.client.list_images( params={ 'limit': 1, 'name': self.created_image.name, 'owner': self.images.auth.tenant_id }) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_image = resp.entity self.assertEqual( len(listed_image), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(listed_image))) marker = listed_image[0].id_ resp = self.images.client.list_images( params={ 'limit': 1, 'marker': marker, 'name': self.created_image.name, 'owner': self.images.auth.tenant_id }) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) next_listed_image = resp.entity self.assertEqual( len(next_listed_image), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(next_listed_image))) self.assertNotEqual( listed_image[0], next_listed_image[0], msg=('Unexpected images received. Expected: Images to not match ' 'Received: Images match')) @data_driven_test(ImagesDatasetListGenerator.ListImagesSort()) def ddtest_sort_images_list(self, params): """ @summary: List all base images, sorting the list by passing in a query parameter as the sort_key and a direction as the sort_dir @param params: Parameter being passed to the list images request @type params: Dictionary 1) List all base images passing in a query parameter as the sort_key and a direction as the sort_dir 2) Verify that the list of images returned is not empty 3) Verify that each image returned is in order based on the sort_key and sort_dir """ sort_dir = None sort_key = None not_str_list = [ 'created_at', 'min_disk', 'min_ram', 'owner', 'size', 'updated_at', 'user_id' ] # Only two key-value pairs are passed in, sort_dir and sort_key for key in params.keys(): if key == 'sort_dir': sort_dir = params[key] elif key == 'sort_key': sort_key = params[key] params.update({'image_type': ImageType.BASE}) listed_images = self.images.behaviors.list_all_images(**params) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) if sort_key == 'id': sort_key = 'id_' for current, next_ in zip(listed_images[0::2], listed_images[1::2]): current_item = getattr(current, sort_key) next_item = getattr(next_, sort_key) if sort_key not in not_str_list: if current_item is not None: current_item = current_item.lower() if next_item is not None: next_item = next_item.lower() if sort_dir.lower() == 'asc': self.assertLessEqual( current_item, next_item, msg=('Unexpected items for images {0} and {1} received.' 'Expected: Less than or equal to {2} ' 'Received: {3}').format(next_.id_, current.id_, next_item, current_item)) else: self.assertGreaterEqual( current_item, next_item, msg=('Unexpected items for images {0} and {1} received.' 'Expected: Greater than or equal to {2} ' 'Received: {3}').format(next_.id_, current.id_, next_item, current_item)) def test_sort_images_list_using_multiple_sort_keys_only(self): """ @summary: List all images, sorting the list by passing in multiple query parameters as the sort_keys only 1) List all images passing in multiple query parameters as the sort_keys only via wrapper test method 2) Verify that each image returned is in order based on the first sort_key (descending by default) 3) If the two images being compared have the same value for the first sort_key, verify that each image returned in then in order based on the second sort_key (descending by default) """ listed_images = self._list_images_with_sort_keys_dirs( ['created_at', 'size']) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): self.assertGreaterEqual( current.created_at, next_.created_at, msg=('Unexpected order of images {0} and {1} received. ' 'Expected: Greater than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_.created_at, current.created_at)) # If created_at dates are equal, verify sizes are in expected order if current.created_at == next_.created_at: self.assertGreaterEqual( current.size, next_.size, msg=('Unexpected order of images {0} and {1} received.' 'Expected: Greater than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_.size, current.size)) def test_sort_images_list_using_multiple_sort_keys_and_sort_dirs(self): """ @summary: List all images, sorting the list by passing in multiple query parameters as the sort_keys and multiple directions as the sort_dirs 1) List all images passing in multiple query parameters as the sort_keys and multiple directions as the sort_dirs via wrapper test method 2) Verify that each image returned is in order based on the first sort_key and sort_dir 3) Verify that each image returned in then in order based on the second sort_key and sort_dir """ listed_images = self._list_images_with_sort_keys_dirs( ['name', 'size'], [SortDirection.ASCENDING, SortDirection.DESCENDING]) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): current_name = current.name next_name = next_.name if current_name is not None: current_name = current_name.lower() if next_name is not None: next_name = next_name.lower() self.assertLessEqual( next_name, next_name, msg=('Unexpected order of images {0} and {1} received.' 'Expected: Less than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_name, current_name)) if current_name == next_name: self.assertGreaterEqual( current.size, next_.size, msg=('Unexpected order of images {0} and {1} received.' 'Expected: Greater than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_.size, current.size)) def test_sort_images_list_using_multi_sort_keys_and_single_sort_dir(self): """ @summary: List all images, sorting the list by passing in multiple query parameters as the sort_keys and a single direction as the sort_dir 1) List all images passing in multiple query parameters as the sort_keys and a single direction as the sort_dir via wrapper test method 2) Verify that each image returned is in order based on the first sort_key and sort_dir 3) Verify that each image returned in then in order based on the second sort_key and the same sort_dir """ listed_images = self._list_images_with_sort_keys_dirs( ['created_at', 'size'], [SortDirection.ASCENDING]) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): self.assertLessEqual( current.created_at, next_.created_at, msg=('Unexpected order of images {0} and {1} received.' 'Expected: Less than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_.created_at, current.created_at)) if current.created_at == next_.created_at: self.assertLessEqual( current.size, next_.size, msg=('Unexpected order of images {0} and {1} received.' 'Expected: Less than or equal to {2} Received: ' '{3}').format(next_.id_, current.id_, next_.size, current.size)) def test_sort_images_list_using_multiple_sort_dirs_only(self): """ @summary: List all images, sorting the list by passing in multiple query parameters as the sort_dirs only 1) List all images passing in multiple query parameters as the sort_dirs only 2) Verify that the response code is 400 """ sort_dirs = [SortDirection.ASCENDING, SortDirection.DESCENDING] sort_pairs = ['sort_dir={0}'.format(key) for key in sort_dirs] url_addition = '&'.join(sort_pairs) resp = self.images.client.list_images(url_addition=url_addition) self.assertEqual( resp.status_code, 400, Messages.STATUS_CODE_MSG.format(400, resp.status_code)) def test_sort_images_list_using_single_sort_key_and_multi_sort_dirs(self): """ @summary: List all images, sorting the list by passing in a single query parameter as the sort_key and multiple query parameters as the sort_dirs 1) List all images passing in a single query parameter as the sort_key and multiple query parameters as the sort_dirs 2) Verify that the response code is 400 """ sort_key = 'name' sort_dirs = [SortDirection.ASCENDING, SortDirection.DESCENDING] sort_pairs = ['sort_dir={0}'.format(key) for key in sort_dirs] sort_pairs.append('sort_key={0}'.format(sort_key)) url_addition = '&'.join(sort_pairs) resp = self.images.client.list_images(url_addition=url_addition) self.assertEqual( resp.status_code, 400, Messages.STATUS_CODE_MSG.format(400, resp.status_code)) def test_list_deactivated_images(self): """ @summary: List all images with no additional query parameters, paginating through the results as needed, and verify that the deactivated images are listed 1) List all images via wrapper test method 2) Verify that there are no errors """ errors = self._verify_listed_images( [self.deactivated_imported_image, self.deactivated_snapshot_image]) self.assertEqual( errors, [], msg=('Unexpected errors received. Expected: No errors ' 'Received: {0}').format(errors)) def test_list_reactivated_images(self): """ @summary: List all images with no additional query parameters, paginating through the results as needed, and verify that the reactivated images are listed 1) List all images via wrapper test method 2) Verify that there are no errors """ errors = self._verify_listed_images( [self.reactivated_imported_image, self.reactivated_snapshot_image]) self.assertEqual( errors, [], msg=('Unexpected errors received. Expected: No errors ' 'Received: {0}').format(errors)) def _verify_listed_images(self, expected_images): """ @summary: List all images with no additional query parameters, paginating through the results as needed, and verify that the expected images are listed @param expected_images: Images to expect @type expected_images: List @return: Errors @rtype: List 1) List all images not passing in any additional query parameter, paginating through the results as needed 2) Verify that the list is not empty 3) Verify that the expected images are in the returned list of images 4) Return errors """ listed_images = self.images.behaviors.list_all_images() self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) return [('Expected image not received. Expected: {0} in list of ' 'images Received: {1}').format(image.id_, listed_images) for image in expected_images if image not in listed_images] def _list_images_with_sort_keys_dirs(self, sort_keys, sort_dirs=None): """ @summary: List all images, sorting the list by passing in query parameters for the sort_keys and sort_dirs and return the list of images @param sort_keys: Keys to sort an image list by @type sort_keys: List @param sort_dirs: Directions to sort an image list by @type sort_dirs: Object @return: Listed images @rtype: List 1) List all images passing in query parameters for the sort_keys and sort_dirs 2) Verify that the list of images returned is not empty 3) Return the list of images """ sort_pairs = [ 'sort_key={0}'.format(sort_key) for sort_key in sort_keys ] if sort_dirs is not None: [ sort_pairs.append('sort_dir={0}'.format(sort_dir)) for sort_dir in sort_dirs ] url_addition = '&'.join(sort_pairs) listed_images = self.images.behaviors.list_all_images(url_addition) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) return listed_images
class RegisterImage(ImagesFixture): @classmethod def tearDownClass(cls): cls.resources.release() super(RegisterImage, cls).tearDownClass() def test_register_image_without_passing_properties(self): """ @summary: Register image without passing any properties 1) Register image without passing any properties 2) Verify that the response code is 201 3) Add the image to the resource pool for deletion 4) Verify that the response contains the properties and values as expected """ errors = [] id_regex = re.compile(ImageProperties.ID_REGEX) resp = self.images.client.register_image() image_created_at_time_in_sec = calendar.timegm(time.gmtime()) self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) reg_image = resp.entity self.resources.add(reg_image.id_, self.images.client.delete_image) created_at_delta = self.images.behaviors.get_time_delta( image_created_at_time_in_sec, reg_image.created_at) updated_at_delta = self.images.behaviors.get_time_delta( image_created_at_time_in_sec, reg_image.updated_at) if reg_image.auto_disk_config is not None: errors.append( Messages.PROPERTY_MSG.format('auto_disk_config', 'None', reg_image.auto_disk_config)) if reg_image.checksum is not None: errors.append( Messages.PROPERTY_MSG.format('checksum', 'None', reg_image.checksum)) if reg_image.container_format is not None: errors.append( Messages.PROPERTY_MSG.format('container_format', 'None', reg_image.container_format)) if created_at_delta > self.images.config.max_created_at_delta: errors.append( Messages.PROPERTY_MSG.format( 'created_at delta', self.images.config.max_created_at_delta, created_at_delta)) if reg_image.disk_format is not None: errors.append( Messages.PROPERTY_MSG.format('disk_format', 'None', reg_image.disk_format)) if reg_image.file_ != '/v2/images/{0}/file'.format(reg_image.id_): errors.append( Messages.PROPERTY_MSG.format( 'file_', '/v2/images/{0}/file'.format(reg_image.id_), reg_image.file_)) if id_regex.match(reg_image.id_) is None: errors.append( Messages.PROPERTY_MSG.format('id_', 'not None', id_regex)) if reg_image.image_type is not None: errors.append( Messages.PROPERTY_MSG.format('image_type', 'None', reg_image.image_type)) if reg_image.min_disk != 0: errors.append( Messages.PROPERTY_MSG.format('min_disk', 0, reg_image.min_disk)) if reg_image.min_ram != 0: errors.append( Messages.PROPERTY_MSG.format('min_ram', 0, reg_image.min_ram)) if reg_image.name is not None: errors.append( Messages.PROPERTY_MSG.format('name', 'None', reg_image.name)) if reg_image.os_type is not None: errors.append( Messages.PROPERTY_MSG.format('os_type', 'None', reg_image.os_type)) if reg_image.owner != self.images.auth.tenant_id: errors.append( Messages.PROPERTY_MSG.format('owner', self.images.auth.tenant_id, reg_image.owner)) if reg_image.protected is not False: errors.append( Messages.PROPERTY_MSG.format('protected', False, reg_image.protected)) if reg_image.schema != Schemas.IMAGE_SCHEMA: errors.append( Messages.PROPERTY_MSG.format('schema', Schemas.IMAGE_SCHEMA, reg_image.schema)) if reg_image.self_ != '/v2/images/{0}'.format(reg_image.id_): errors.append( Messages.PROPERTY_MSG.format( 'self', '/v2/images/{0}'.format(reg_image.id_), reg_image.self_)) if reg_image.size is not None: errors.append( Messages.PROPERTY_MSG.format('size', 'None', reg_image.size)) if reg_image.status != ImageStatus.QUEUED: errors.append( Messages.PROPERTY_MSG.format('status', ImageStatus.QUEUED, reg_image.status)) if reg_image.tags != []: errors.append( Messages.PROPERTY_MSG.format('tags', [], reg_image.tags)) if updated_at_delta > self.images.config.max_updated_at_delta: errors.append( Messages.PROPERTY_MSG.format( 'updated_at delta', self.images.config.max_updated_at_delta, updated_at_delta)) if reg_image.user_id is not None: errors.append( Messages.PROPERTY_MSG.format('user_id', 'None', reg_image.user_id)) if reg_image.visibility != ImageVisibility.PRIVATE: errors.append( Messages.PROPERTY_MSG.format('visibility', ImageVisibility.PRIVATE, reg_image.visibility)) if reg_image.additional_properties != {}: errors.append( Messages.PROPERTY_MSG.format('additional_properties', {}, reg_image.additional_properties)) self.assertEqual(errors, [], msg=('Unexpected error received. Expected: No errors ' 'Received: {0}').format(errors)) @unittest.skip('Redmine bug #11436') def test_register_image_passing_all_allowed_properties(self): """ @summary: Register image passing all allowed properties 1) Register image passing all allowed properties 2) Verify that the response code is 201 3) Add the image to the resource pool for deletion 4) Verify that the response contains values for all allowed properties as expected """ errors = [] id_regex = re.compile(ImageProperties.ID_REGEX) auto_disk_config = 'False' container_format = ImageContainerFormat.AKI disk_format = ImageDiskFormat.ISO id_ = '00000000-0000-0000-0000-000000000000' image_type = ImageType.IMPORT min_disk = images.config.min_disk min_ram = images.config.min_ram name = rand_name('register_image') os_type = ImageOSType.LINUX protected = False tags = [rand_name('tag1')] user_id = random_string() additional_properties = { self.images.config.additional_property: self.images.config.additional_property_value } resp = self.images.client.register_image( auto_disk_config=auto_disk_config, container_format=container_format, disk_format=disk_format, id_=id_, image_type=image_type, min_disk=min_disk, min_ram=min_ram, name=name, os_type=os_type, protected=protected, tags=tags, user_id=user_id, additional_properties=additional_properties) self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) reg_image = resp.entity self.resources.add(reg_image.id_, self.images.client.delete_image) if reg_image.auto_disk_config != auto_disk_config: errors.append( Messages.PROPERTY_MSG.format('auto_disk_config', auto_disk_config, reg_image.auto_disk_config)) if reg_image.container_format != container_format: errors.append( Messages.PROPERTY_MSG.format('container_format', container_format, reg_image.container_format)) if reg_image.disk_format != disk_format: errors.append( Messages.PROPERTY_MSG.format('disk_format', disk_format, reg_image.disk_format)) if id_regex.match(reg_image.id_) is None: errors.append( Messages.PROPERTY_MSG.format('id_', 'not None', id_regex)) if reg_image.image_type is not None: errors.append( Messages.PROPERTY_MSG.format('image_type', 'None', reg_image.image_type)) if reg_image.min_disk != min_disk: errors.append( Messages.PROPERTY_MSG.format('min_disk', min_disk, reg_image.min_disk)) if reg_image.min_ram != min_ram: errors.append( Messages.PROPERTY_MSG.format('min_ram', min_ram, reg_image.min_ram)) if reg_image.name != name: errors.append( Messages.PROPERTY_MSG.format('name', name, reg_image.name)) if reg_image.os_type is not None: errors.append( Messages.PROPERTY_MSG.format('os_type', 'None', reg_image.os_type)) if reg_image.protected != protected: errors.append( Messages.PROPERTY_MSG.format('protected', protected, reg_image.protected)) if reg_image.tags != tags: errors.append( Messages.PROPERTY_MSG.format('tags', tags, reg_image.tags)) if reg_image.user_id is not None: errors.append( Messages.PROPERTY_MSG.format('user_id', 'None', reg_image.user_id)) if reg_image.additional_properties != additional_properties: errors.append( Messages.PROPERTY_MSG.format('additional_properties', additional_properties, reg_image.additional_properties)) self.assertEqual(errors, [], msg=('Unexpected error received. Expected: No errors ' 'Received: {0}').format(errors)) @data_driven_test( ImagesDatasetListGenerator.UpdateRegisterImageRestricted()) def ddtest_register_image_passing_restricted_property(self, prop): """ @summary: Register image passing restricted property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Register image passing restricted property 2) Verify that the response code is 403 """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() # This is a temporary workaround for skips in ddtests failure_prop_list = [ 'checksum', 'created_at', 'id', 'image_type', 'location', 'os_type', 'owner', 'schema', 'size', 'status', 'updated_at', 'user_id', 'visibility' ] if prop_key in failure_prop_list: sys.stderr.write('skipped \'Redmine bug #11436\' ... ') return if prop_key == 'file' or prop_key == 'self': prop = {'{0}_'.format(prop_key): prop_val} resp = self.images.client.register_image( name=rand_name('register_image'), **prop) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) def test_register_two_images_passing_same_name(self): """ @summary: Register two images passing the same name for both 1) Register image passing only name 2) Verify that the response code is 201 3) Add the image to the resource pool for deletion 4) Register another image passing only the same name 5) Verify that the response code is 201 6) Add the image to the resource pool for deletion 7) Verify that the image ids are not the equal """ name = rand_name('register_image') resp = self.images.client.register_image(name=name) self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) reg_image = resp.entity self.resources.add(reg_image.id_, self.images.client.delete_image) resp = self.images.client.register_image(name=name) self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) alt_reg_image = resp.entity self.resources.add(alt_reg_image.id_, self.images.client.delete_image) self.assertNotEqual( reg_image.id_, alt_reg_image.id_, msg=('Unexpected image ids received. Expected: Image {0} to be ' 'different from image {1} ' 'Received: The same image ids'.format(reg_image.id_, alt_reg_image.id_))) @data_driven_test(ImagesDatasetListGenerator.RegisterImageInvalidValues()) def ddtest_register_image_passing_invalid_values(self, prop): """ @summary: Register image passing invalid value for image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Register image passing invalid value for image property 2) Verify that the response code is 400 """ # This is a temporary workaround for skips in ddtests if 'auto_disk_config' in prop: sys.stderr.write('skipped \'Redmine bug #11438\' ... ') return if 'name' in prop: resp = self.images.client.register_image(**prop) else: resp = self.images.client.register_image( name=rand_name('register_image'), **prop) self.assertEqual( resp.status_code, 400, Messages.STATUS_CODE_MSG.format(400, resp.status_code)) def test_register_public_image(self): """ @summary: Register a public image by setting the visibility property to public 1) Register image setting the visibility property to public 2) If the allow_public_images_crud property is true, verify that the response code is 201, else verify that the response code is 403 3) If the allow_public_images_crud property is true, add the image for deletion. List all images accounting for pagination and verify that the image is present. """ name = rand_name('register_image') resp = self.images.client.register_image( name=name, visibility=ImageVisibility.PUBLIC) if self.images.config.allow_public_images_crud: self.assertEqual( resp.status_code, 201, Messages.STATUS_CODE_MSG.format(201, resp.status_code)) reg_image = resp.entity self.resources.add(reg_image.id_, self.images.client.delete_image) list_images = self.images.behaviors.list_all_images() image_names = [image.name for image in list_images] self.assertIn( name, image_names, msg=('Unexpected image name received. Expected: {0} in {1} ' 'Received: Was not present').format(name, image_names)) else: self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code))
class UpdateRegisteredImage(ImagesFixture): @classmethod def setUpClass(cls): super(UpdateRegisteredImage, cls).setUpClass() cls.reg_image = cls.images.behaviors.register_new_image( name=rand_name('update_registered_image')) @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(UpdateRegisteredImage, cls).tearDownClass() @data_driven_test( ImagesDatasetListGenerator.UpdateImageAllowed( image_status=ImageStatus.QUEUED)) def ddtest_update_reg_image_replace_allowed_property(self, prop): """ @summary: Update image replacing allowed image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image replacing allowed image property's value 2) Verify that the response code is 200 3) Verify that the update image response shows that the property's value has been updated as expected 4) Get image details for the image 5) Verify that the response is ok 6) Verify that the get image details response shows that the property's value has been updated as expected 7) Verify that the image's updated_at time has increased """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() resp = self.images.client.update_image(self.reg_image.id_, replace={prop_key: prop_val}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) updated_image = resp.entity self.assertEqual( getattr(updated_image, prop_key), prop_val, msg=('Unexpected updated image value received. Expected: {0} ' 'Received: {1}').format(prop_val, getattr(updated_image, prop_key))) resp = self.images.client.get_image_details(self.reg_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( getattr(get_image, prop_key), prop_val, msg=('Unexpected get image details value received. Expected: {0} ' 'Received: {1}').format(prop_val, getattr(get_image, prop_key))) self.assertGreaterEqual(updated_image.updated_at, self.reg_image.updated_at, msg=('Unexpected updated_at value received. ' 'Expected: Greater than {0} ' 'Received: {1}').format( self.reg_image.updated_at, updated_image.updated_at))
class ImageOperationsSmoke(ImagesFixture): @classmethod def setUpClass(cls): super(ImageOperationsSmoke, cls).setUpClass() # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('image_operations_smoke')}, count=2) cls.image = created_images.pop() cls.alt_image = created_images.pop() @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(ImageOperationsSmoke, cls).tearDownClass() @data_driven_test(ImagesDatasetListGenerator.ListImagesSmoke()) def ddtest_list_images(self, params): """ @summary: List a subset of images passing in valid query parameters @param params: Parameter being passed to the list images request @type params: Dictionary 1) List subset of images passing in a query parameter 2) Verify the response status code is 200 """ resp = self.images.client.list_images(params) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) @data_driven_test( ImagesDatasetListGenerator.ListImagesInvalidParameters()) def ddtest_invalid_list_images(self, params): """ @summary: Attempt to list a subset of images passing in invalid query parameters @param params: Parameter being passed to the list images request @type params: Dictionary 1) Attempt to list a subset of images passing in an invalid query parameter 2) Verify the response status code is 200 """ # Invalid parameters should be ignored, the response code should be 200 resp = self.images.client.list_images(params) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_get_image_details(self): """ @summary: Get the details of an image 1) Get the details of an image passing in an image id 2) Verify the response status code is 200 """ resp = self.images.client.get_image_details(self.image.id_) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_update_image(self): """ @summary: Update an image 1) Update an image replacing a single property 2) Verify that the response code is 200 """ updated_name = rand_name('image_operations_smoke_updated') resp = self.images.client.update_image( self.image.id_, replace={'name': updated_name}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_delete_image(self): """ @summary: Delete an image 1) Delete an image 2) Verify that the response code is 204 """ resp = self.images.client.delete_image(self.alt_image.id_) self.assertEqual( resp.status_code, 204, Messages.STATUS_CODE_MSG.format(204, resp.status_code))
class ImageSharingOperationsSmoke(ImagesFixture): @classmethod def setUpClass(cls): super(ImageSharingOperationsSmoke, cls).setUpClass() cls.member_id = cls.images_alt_one.auth.tenant_id # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={ 'name': rand_name('image_sharing_operations_smoke') }, count=4) cls.created_image = created_images.pop() cls.alt_created_image = created_images.pop() cls.images.client.create_image_member(cls.alt_created_image.id_, cls.member_id) cls.delete_member_image = created_images.pop() cls.images.client.create_image_member(cls.delete_member_image.id_, cls.member_id) cls.update_member_image = created_images.pop() cls.images.client.create_image_member(cls.update_member_image.id_, cls.member_id) @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(ImageSharingOperationsSmoke, cls).tearDownClass() @data_driven_test(ImagesDatasetListGenerator.ListImageMembersStatuses()) def ddtest_list_image_members(self, params): """ @summary: List all image members @param params: Parameter being passed to the list image members request @type params: Dictionary 1) List all image members passing in a query parameter 2) Verify the response status code is 200 """ resp = self.images.client.list_image_members(self.created_image.id_, params) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_get_image_member(self): """ @summary: Get an image member of an image 1) Get an image member of an image passing in the image id and member id 4) Verify the response status code is 200 """ resp = self.images.client.get_image_member(self.alt_created_image.id_, self.member_id) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_create_image_member(self): """ @summary: Create an image member 1) As user A, create an image member for an image passing the image id and user B's tenant id as member id 2) Verify the response status code is 200 """ resp = self.images.client.create_image_member(self.created_image.id_, self.member_id) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) def test_delete_image_member(self): """ @summary: Delete an image member 1) As user A, delete an image member for an image passing the image id and user B's tenant id as member id 2) Verify the response status code is 204 """ resp = self.images.client.delete_image_member( self.delete_member_image.id_, self.member_id) self.assertEqual( resp.status_code, 204, Messages.STATUS_CODE_MSG.format(204, resp.status_code)) def test_update_image_member(self): """ @summary: Update an image member 1) As user B, update an image member of an image passing in the image id, member id, and 'accepted' as the status 2) Verify the response status code is 200 """ resp = self.images_alt_one.client.update_image_member( self.update_member_image.id_, self.member_id, ImageMemberStatus.ACCEPTED) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code))
class UpdateImage(ImagesFixture): @classmethod def setUpClass(cls): super(UpdateImage, cls).setUpClass() # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('update_image')}, count=3) cls.created_image = created_images.pop() cls.alt_created_image = created_images.pop() cls.quota_image = created_images.pop() @classmethod def tearDownClass(cls): cls.images.client.update_image(cls.created_image.id_, replace={'protected': False}) cls.images.behaviors.resources.release() super(UpdateImage, cls).tearDownClass() @data_driven_test(ImagesDatasetListGenerator.UpdateImageAllowed()) def ddtest_update_image_replace_allowed_property(self, prop): """ @summary: Update image replacing allowed image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image replacing allowed image property's value 2) Verify that the response code is 200 3) Verify that the update image response shows that the property's value has been updated as expected 4) Get image details for the image 5) Verify that the response is ok 6) Verify that the get image details response shows that the property's value has been updated as expected 7) Verify that the image's updated_at time has increased """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() resp = self.images.client.update_image(self.created_image.id_, replace={prop_key: prop_val}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) updated_image = resp.entity self.assertEqual( getattr(updated_image, prop_key), prop_val, msg=('Unexpected updated image value received. Expected: {0} ' 'Received: {1}').format(prop_val, getattr(updated_image, prop_key))) resp = self.images.client.get_image_details(self.created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( getattr(get_image, prop_key), prop_val, msg=('Unexpected get image details value received. Expected: {0} ' 'Received: {1}').format(prop_val, getattr(get_image, prop_key))) self.assertGreaterEqual(updated_image.updated_at, self.created_image.updated_at, msg=('Unexpected updated_at value received. ' 'Expected: Greater than {0} ' 'Received: {1}').format( self.created_image.updated_at, updated_image.updated_at)) @data_driven_test(ImagesDatasetListGenerator.UpdateImageInaccessible()) def ddtest_update_image_replace_inaccessible_property(self, prop): """ @summary: Update image replacing inaccessible image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image replacing inaccessible image property's value 2) Verify that the response code is 403 """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() resp = self.images.client.update_image(self.alt_created_image.id_, replace={prop_key: prop_val}) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) @data_driven_test( ImagesDatasetListGenerator.UpdateRegisterImageRestricted()) def ddtest_update_image_replace_restricted_property(self, prop): """ @summary: Update image replacing restricted image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image replacing restricted image property's value 2) Verify that the response code is 403 3) Get image details for the image 4) Verify that the response is ok 5) Verify that the get image details response shows that the property's value has not been updated """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() # This is a temporary workaround for skips in ddtests if prop_key == 'id': sys.stderr.write('skipped \'Redmine bug #11398\' ... ') return resp = self.images.client.update_image(self.alt_created_image.id_, replace={prop_key: prop_val}) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) resp = self.images.client.get_image_details(self.alt_created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity prop_key = self._check_prop_key(prop_key) self.assertEqual( getattr(get_image, prop_key), getattr(self.alt_created_image, prop_key), msg=('Unexpected get image details value received. Expected: {0} ' 'Received: {1}').format( getattr(self.alt_created_image, prop_key), getattr(get_image, prop_key))) @data_driven_test( ImagesDatasetListGenerator.UpdateRegisterImageRestricted()) def ddtest_update_image_add_restricted_property(self, prop): """ @summary: Update image adding restricted image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image adding restricted image property 2) Verify that the response code is 403 3) Get image details for the image 4) Verify that the response is ok 5) Verify that the get image details response shows that the property has been not been added """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() # This is a temporary workaround for skips in ddtests if prop_key == 'id': sys.stderr.write('skipped \'Redmine bug #11398\' ... ') return resp = self.images.client.update_image(self.alt_created_image.id_, add={prop_key: prop_val}) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) resp = self.images.client.get_image_details(self.alt_created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity prop_key = self._check_prop_key(prop_key) self.assertEqual( getattr(get_image, prop_key), getattr(self.alt_created_image, prop_key), msg=('Unexpected get image details value received. Expected: {0} ' 'Received: {1}').format( getattr(self.alt_created_image, prop_key), getattr(get_image, prop_key))) @data_driven_test( ImagesDatasetListGenerator.UpdateRegisterImageRestricted()) def ddtest_update_image_remove_restricted_property(self, prop): """ @summary: Update image removing restricted image property @param prop: Key-value pair containing the image property to validate @type prop: Dictionary 1) Update image removing restricted image property 2) Verify that the response code is 403 3) Get image details for the image 4) Verify that the response is ok 5) Verify that the get image details response shows that the property has been not been removed """ # Each prop passed in only has one key-value pair prop_key, prop_val = prop.popitem() # This is a temporary workaround for skips in ddtests failure_prop_list = ['id', 'file', 'schema', 'self'] if prop_key in failure_prop_list: sys.stderr.write('skipped \'Launchpad bug #1438826\' ... ') return resp = self.images.client.update_image(self.alt_created_image.id_, remove={prop_key: prop_val}) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) resp = self.images.client.get_image_details(self.alt_created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity prop_key = self._check_prop_key(prop_key) self.assertEqual( getattr(get_image, prop_key), getattr(self.alt_created_image, prop_key), msg=('Unexpected get image details value received. Expected: {0} ' 'Received: {1}').format( getattr(self.alt_created_image, prop_key), getattr(get_image, prop_key))) def test_update_image_add_new_property(self): """ @summary: Update image by adding a new image property 1) Update image adding a new image property 2) Verify that the response code is 200 3) Verify that the update image response shows that the new property has been added as expected 4) Get image details for the image 5) Verify that the response is ok 6) Verify that the get image details response shows that the new property has been added as expected """ new_prop = 'new_property' new_prop_value = rand_name('new_property_value') resp = self.images.client.update_image(self.created_image.id_, add={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) updated_image = resp.entity added_prop_value = (updated_image.additional_properties.get( new_prop, None)) self.assertEqual( added_prop_value, new_prop_value, msg=('Unexpected new image property value received. Expected: {0} ' 'Received: {1}').format(new_prop_value, added_prop_value)) resp = self.images.client.get_image_details(self.created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity get_added_prop_value = (get_image.additional_properties.get( new_prop, None)) self.assertEqual( get_added_prop_value, new_prop_value, msg=('Unexpected new image property value received. Expected: {0} ' 'Received: {1}').format(new_prop_value, get_added_prop_value)) def test_update_image_remove_property(self): """ @summary: Update image by removing an image property 1) Update image adding a new image property 2) Verify that the response code is 200 3) Update the image removing the new image property 4) Verify that the response code is 200 5) Verify that the update image response shows that the new property has been removed as expected 6) Get image details for the image 7) Verify that the response code is ok 8) Verify that the get image response shows that the new property has been removed as expected """ new_prop = 'alt_new_property' new_prop_value = rand_name('alt_new_property_value') resp = self.images.client.update_image(self.created_image.id_, add={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) response = self.images.client.update_image( self.created_image.id_, remove={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) updated_image = response.entity self.assertNotIn( new_prop, updated_image.additional_properties, msg=('Unexpected removed image property received. Expected: {0} ' 'to not be present ' 'Received: {1}').format(new_prop, updated_image.additional_properties)) resp = self.images.client.get_image_details(self.created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertNotIn( new_prop, get_image.additional_properties, msg=('Unexpected removed image property received. Expected: {0} ' 'to not be present ' 'Received: {1}').format(new_prop, get_image.additional_properties)) def test_update_image_replace_additional_property(self): """ @summary: Update image by replacing an additional image property 1) Update image adding a new image property 2) Verify that the response code is 200 3) Update the image replacing the new image property's value 4) Verify that the response code is 200 5) Verify that the update image response shows that the new property's value has been updated as expected 6) Get image details for the image 7) Verify that the response code is ok 8) Verify that the get image response shows that the new property's value has been updated as expected """ new_prop = 'alt_two_new_property' new_prop_value = rand_name('alt_two_new_property_value') updated_prop_value = rand_name('updated_new_property_value') resp = self.images.client.update_image(self.created_image.id_, add={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) resp = self.images.client.update_image( self.created_image.id_, replace={new_prop: updated_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) updated_image = resp.entity replaced_prop_value = (updated_image.additional_properties.get( new_prop, None)) self.assertEqual( replaced_prop_value, updated_prop_value, msg=('Unexpected updated image property value received. ' 'Expected: {0} ' 'Received: {1}').format(updated_prop_value, replaced_prop_value)) resp = self.images.client.get_image_details(self.created_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity get_replaced_prop_value = (get_image.additional_properties.get( new_prop, None)) self.assertEqual( get_replaced_prop_value, updated_prop_value, msg=('Unexpected updated image property value received.' 'Expected: {0} ' 'Received: {1}').format(new_prop_value, get_replaced_prop_value)) def test_update_image_property_quota_limit(self): """ @summary: Validate image properties quota limit 1) While the number of image properties is not equal to the image properties quota, update image adding a new image property 2) Verify that the response code is 200 3) When the number of image properties is equal to the image properties quota, update image adding another new image property 4) Verify that the response code is 413 5) Get image details 6) Verify that the response is ok 7) Verify that the number of image properties matches the image properties quota """ number_of_image_properties = 0 additional_props = [ 'auto_disk_config', 'image_type', 'os_type', 'user_id' ] quota_limit = self.images.config.image_properties_limit # Decrease quota limit for every property that image already contains for prop in additional_props: if hasattr(self.quota_image, prop): quota_limit -= 1 while number_of_image_properties != quota_limit: new_prop = rand_name('prop') new_prop_value = rand_name('prop_value') resp = self.images.client.update_image( self.quota_image.id_, add={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 200, Messages.STATUS_CODE_MSG.format(200, resp.status_code)) resp = self.images.client.get_image_details(self.quota_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity number_of_image_properties = len( getattr(get_image, 'additional_properties')) new_prop = rand_name('prop') new_prop_value = rand_name('prop_value') resp = self.images.client.update_image(self.quota_image.id_, add={new_prop: new_prop_value}) self.assertEqual( resp.status_code, 413, Messages.STATUS_CODE_MSG.format(413, resp.status_code)) resp = self.images.client.get_image_details(self.quota_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual(len(getattr(get_image, 'additional_properties')), quota_limit, msg='Unexpected number of image properties returned.' 'Expected: {0} ' 'Received: {1}'.format( quota_limit, len(getattr(get_image, 'additional_properties')))) def test_update_image_using_blank_image_id(self): """ @summary: Update image using a blank image id 1) Update image using blank image id 2) Verify that the response code is 404 """ resp = self.images.client.update_image( image_id='', add={'new_prop': rand_name('new_prop_value')}) self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code)) def test_update_image_using_invalid_image_id(self): """ @summary: Update image using an invalid image id 1) Update image using invalid image id 2) Verify that the response code is 404 """ resp = self.images.client.update_image( image_id='invalid', add={'new_prop': rand_name('new_prop_value')}) self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code)) def _check_prop_key(self, prop_key): """ @summary: Check if prop_key needs an underscore added @param prop_key: Image property to check @type prop_key: String @return: Prop_key @rtype: String """ keys_need_underscore = ['file', 'id', 'self'] if prop_key in keys_need_underscore: prop_key = '{0}_'.format(prop_key) if prop_key == 'location': prop_key = 'file_' return prop_key
class ListImages(ImagesIntergrationFixture): @classmethod def setUpClass(cls): super(ListImages, cls).setUpClass() cls.alt_one_member = cls.images_alt_one.auth.tenant_id # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('list_images')}, count=2) cls.created_image = created_images.pop() cls.images.client.create_image_member( cls.created_image.id_, cls.alt_one_member) resp = cls.images.client.get_image_member( cls.created_image.id_, cls.alt_one_member) cls.created_image_member = resp.entity cls.shared_image = created_images.pop() cls.images.client.create_image_member( cls.shared_image.id_, cls.alt_one_member) @classmethod def tearDownClass(cls): cls.images.behaviors.resources.release() super(ListImages, cls).tearDownClass() def test_compare_list_images_between_glance_and_nova(self): """ @summary: Compare the list of images returned from the glance api and the nova api 1) List images with a limit set to 100 and visiblity set to public through the glance api 2) Verify that the response is ok 3) Verify that the images returned is not none 4) List images with a limit set to 100 and image_type set to base through the nova api 5) Verify that the response is ok 6) Verify that the images returned is not none 7) Verify that the length of the list of images is the same through the glance api and the nova api 8) Verify that each image name in the list of images is the same through the glance api and the nova api """ resp = self.images.client.list_images( params={'limit': 100, 'visibility': ImageVisibility.PUBLIC}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) glance_image_names = [image.name for image in resp.entity] self.assertIsNotNone( glance_image_names, msg=('Unexpected images received.' 'Expected: At least one image received ' 'Received: No images received')) resp = self.compute.images.client.list_images_with_detail( limit=100, image_type='base') self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) nova_image_names = [image.name for image in resp.entity] self.assertIsNotNone( nova_image_names, msg=('Unexpected images received.' 'Expected: At least one image received ' 'Received: No images received')) self.assertEqual( len(glance_image_names), len(nova_image_names), msg=('Unexpected images received. Expected: Number of images to ' 'match Received: Number of images do not match')) for image_name in glance_image_names: self.assertIn( image_name, nova_image_names, msg=('Unexpected images received. Expected: {0} in list of ' 'images ' 'Received: {1}').format(image_name, nova_image_names)) def test_list_all_images(self): """ @summary: List all images with no additional query parameters, paginating through the results as needed, and verify that the created image is listed 1) List all images not passing in any additional query parameter, paginating through the results as needed 2) Verify that the list is not empty 3) Verify that the created image is in the returned list of images 4) Verify that each image returned has a status of active """ listed_images = self.images.behaviors.list_all_images() self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) self.assertIn( self.created_image, listed_images, msg=('Unexpected images received. Expected: {0} in list of ' 'images ' 'Received: {1}').format(self.created_image, listed_images)) # TODO: Add additional assertions to verify all images are as expected for image in listed_images: self.assertEqual( image.status, ImageStatus.ACTIVE, msg=('Unexpected status for image {0} received. Expected: {1} ' 'Received: {2}').format(image.id_, ImageStatus.ACTIVE, image.status)) def test_image_visibility_of_shared_images(self): """ @summary: Image visibility of shared images 1) Using alt_one_member, list all images 2) Verify that shared_image is not present 3) Using alt_one_member, list all images passing visibility as shared and member status as all 4) Verify that shared_image is now present 5) Using alt_one_member, update image member status to rejected 6) Verify that the response is ok 7) Using alt_one_member, list all images passing visibility as shared and member status as all 8) Verify that shared_image is still present """ listed_images = self.images_alt_one.behaviors.list_all_images() self.assertNotIn( self.shared_image, listed_images, msg=('Unexpected image received. Expected: {0} to not be in list ' 'of images Received: {1}').format(self.shared_image, listed_images)) listed_images = self.images_alt_one.behaviors.list_all_images( visibility=ImageVisibility.SHARED, member_status=ImageMemberStatus.ALL) self.assertIn( self.shared_image, listed_images, msg=('Expected image not received. Expected: {0} to be in list of ' 'images Received: {1}').format(self.shared_image, listed_images)) resp = self.images_alt_one.client.update_image_member( self.shared_image.id_, self.alt_one_member, ImageMemberStatus.REJECTED) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = self.images_alt_one.behaviors.list_all_images( visibility=ImageVisibility.SHARED, member_status=ImageMemberStatus.ALL) self.assertIn( self.shared_image, listed_images, msg=('Expected image not received. Expected: {0} to be in list of ' 'images Received: {1}').format(self.shared_image, listed_images)) @data_driven_test( ImagesDatasetListGenerator.ListImagesFilters()) def ddtest_filter_images_list(self, params): """ @summary: List all images that match a filter, passing in a specific query parameter @param param: Parameter being passed to the list images request @type param: Dictionary 1) List all images passing in a query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for the given filter that is acceptable """ # This is a temporary workaround for skips in ddtests if 'id_' in params or 'created_at' in params: sys.stderr.write('skipped \'Redmine bug #11168\' ... ') return api_args = {} for param in params: api_args.update({param: getattr(self.created_image, param)}) listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not 0 ' 'Received: {0}').format(len(listed_images))) for image in listed_images: for param in params: received = getattr(image, param) expected = getattr(self.created_image, param) self.assertEqual( received, expected, msg=('Unexpected property value for image {0} received.' 'Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_additional_property(self): """ @summary: List all images that match a filter, passing in an additional property as the query parameter 1) List all images passing in an additional property as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for the additional property that matches the additional property that is being used as the filter """ prop = self.images.config.additional_property api_args = ({prop: self.created_image.additional_properties.get(prop)}) listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not 0 ' 'Received: {0}').format(len(listed_images))) for image in listed_images: received = image.additional_properties.get(prop) expected = (self.created_image.additional_properties.get(prop)) self.assertEqual( received, expected, msg=('Unexpected property for image {0} received.' 'Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) @unittest.skip('Redmine bug #11270') def test_filter_images_list_passing_member_status(self): """ @summary: List all images that match a filter, passing in member_status as the query parameter 1) List all images passing in member_status as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned list image members 4) Verify that each image returned contains a value for member_status that matches the member_status that is being used as the filter """ api_args = ({'member_status': getattr(self.created_image_member, 'status'), 'visibility': getattr(self.created_image, 'visibility')}) listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: image_member_statuses = [] resp = self.images.client.list_image_members(image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) list_image_members = resp.entity for member in list_image_members: image_member_statuses.append(member.status) received_image_member_status = ( getattr(self.created_image_member, 'status')) self.assertIn(getattr(self.created_image_member, 'status'), image_member_statuses, msg=('Unexpected image member status for image' '{0} received. Expected: {1} in list of' 'image member statuses Received: ' '{2}').format(image.id_, received_image_member_status, image_member_statuses)) def test_filter_images_list_passing_size_max(self): """ @summary: List all images that match a filter, passing in size_max as the query parameter 1) List all images passing in size_max as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is less than or equal to the size_max that is being used as the filter """ api_args = {'size_max': self.images.config.size_max} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_max self.assertLessEqual( received, expected, msg=('Unexpected size_max for image {0} received.' 'Expected: <= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_size_max_and_size_min(self): """ @summary: List all images that match a filter, passing in both size_max and size_min as the query parameters 1) List all images passing in size_max and size_min as the query parameters 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is less than or equal to the size_max that is being used as the filter and greater than or equal to the size_min that is being used as the filter """ api_args = {'size_max': self.images.config.size_max, 'size_min': self.images.config.size_min} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_max self.assertLessEqual( received, expected, msg=('Unexpected size_max for image {0} received.' 'Expected: <= {1} ' 'Received: {2}').format(image.id_, expected, received)) expected = getattr(self.images.config, 'size_min') self.assertGreaterEqual( received, expected, msg=('Unexpected size_min for image {0} received.' 'Expected: >= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_size_min(self): """ @summary: List all images that match a filter, passing in size_min as the query parameter 1) List all images passing in size_min as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for size that is greater than or equal to the size_min that is being used as the filter """ api_args = {'size_min': self.images.config.size_min} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'size') expected = self.images.config.size_min self.assertGreaterEqual( received, expected, msg=('Unexpected size_min for image {0} received.' 'Expected: >= {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_filter_images_list_passing_tag(self): """ @summary: List all images that match a filter, passing in tag as the query parameter 1) List all images passing in tag as the query parameter 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for tags that matches the tag that is being used as the filter """ api_args = {'tag': getattr(self.created_image, 'tags')} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for image in listed_images: received = getattr(image, 'tags') expected = getattr(self.created_image, 'tags') self.assertEqual( received, expected, msg=('Unexpected tags for image {0} received. Expected: {1} ' 'Received: {2}').format(image.id_, expected, received)) def test_list_images_using_limit(self): """ @summary: List images using limit 1) List images passing in 1 as the limit 2) Verify that the response code is ok 4) Verify that the number of images returned is 1 """ resp = self.images.client.list_images(params={'limit': 1}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = resp.entity self.assertEqual( len(listed_images), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(listed_images))) def test_list_images_using_limit_zero(self): """ @summary: List images using limit of zero 1) List images passing in 0 as the limit 2) Verify that the response code is ok 3) Verify that the number of images returned is 0 """ resp = self.images.client.list_images(params={'limit': 0}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_images = resp.entity self.assertEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(0, len(listed_images))) def test_list_images_using_marker_pagination(self): """ @summary: List images using marker pagination 1) List images passing in 1 as the limit, the created image's name as the name, and the user as the owner 2) Verify that the response code is ok 3) Verify that the returned list only contains 1 image 4) List images again passing in the listed image id as the marker as well as 1 as the limit, the created image's name as the name, and the user as the owner 5) Verify that the response code is ok 6) Verify that the returned list only contains 1 image 7) Verify that the newly returned image is not the same as the previously returned image """ resp = self.images.client.list_images( params={'limit': 1, 'name': self.created_image.name, 'owner': self.images.auth.tenant_id}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) listed_image = resp.entity self.assertEqual( len(listed_image), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(listed_image))) marker = listed_image[0].id_ resp = self.images.client.list_images( params={'limit': 1, 'marker': marker, 'name': self.created_image.name, 'owner': self.images.auth.tenant_id}) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) next_listed_image = resp.entity self.assertEqual( len(next_listed_image), 1, msg=('Unexpected number of images received. Expected: {0} ' 'Received: {1}').format(1, len(next_listed_image))) self.assertNotEqual( listed_image[0], next_listed_image[0], msg=('Unexpected images received. Expected: Images to not match ' 'Received: Images match')) @data_driven_test( ImagesDatasetListGenerator.ListImagesSort()) def ddtest_sort_images_list(self, params): """ @summary: List all images, sorting the list by passing in a query parameter as the sort_key and a direction as the sort_dir @param params: Parameter being passed to the list images request @type params: Dictionary 1) List all images passing in a query parameter as the sort_key and a direction as the sort_dir 2) Verify that the list of images returned is not empty 3) Verify that each image returned is in order based on the sort_key and sort_dir """ sort_dir = None sort_key = None not_str_list = ['created_at', 'min_disk', 'min_ram', 'owner', 'size', 'updated_at', 'user_id'] # Only two key-value pairs are passed in, sort_dir and sort_key for key in params.keys(): if key == 'sort_dir': sort_dir = params[key] elif key == 'sort_key': sort_key = params[key] # This is a temporary workaround for skips in ddtests additional_property = self.images.config.additional_property expected_failure_properties = [ additional_property, 'auto_disk_config', 'id', 'image_type', 'os_type', 'protected', 'tags', 'user_id', 'visibility'] if sort_key in expected_failure_properties: sys.stderr.write('skipped \'Redmine bugs #11260 and #11262\' ... ') return listed_images = self.images.behaviors.list_all_images(**params) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): current_item = getattr(current, sort_key) next_item = getattr(next_, sort_key) if sort_key not in not_str_list: if current_item is not None: current_item = current_item.lower() if next_item is not None: next_item = next_item.lower() if sort_dir.lower() == 'asc': self.assertLessEqual( current_item, next_item, msg=('Unexpected items for images {0} and {1} received.' 'Expected: Less than or equal to {2} ' 'Received: {3}').format(next_.id_, current.id_, next_item, current_item)) else: self.assertGreaterEqual( current_item, next_item, msg=('Unexpected items for images {0} and {1} received.' 'Expected: Greater than or equal to {0} ' 'Received: {1}').format(next_.id_, current.id_, next_item, current_item)) @unittest.skip('Redmine bug #11261') def test_sort_images_list_passing_id_asc(self): """ @summary: List all images, sorting the list by passing in id as the sort_key and asc as the sort_dir 1) List all images passing in id as the sort_key and asc as the sort_dir 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for id that is less than or equal to the value for id of the next image """ sort_key_var = 'id_' api_args = {'sort_key': 'id', 'sort_dir': SortDirection.ASCENDING} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): current_item = getattr(current, sort_key_var).replace('-', '') next_item = getattr(next_, sort_key_var).replace('-', '') self.assertLessEqual( current_item, next_item, msg=('Unexpected item received. Expected: Less than or equal ' 'to {0} Received: {1}').format(next_item, current_item)) @unittest.skip('Redmine bug #11261') def test_sort_images_list_passing_id_desc(self): """ @summary: List all images, sorting the list by passing in id as the sort_key and desc as the sort_dir 1) List all images passing in id as the sort_key and desc as the sort_dir 2) Verify that the list of images returned is not empty 3) Verify that each image returned contains a value for id that is greater than or equal to the value for id of the next image """ sort_key_var = 'id_' api_args = {'sort_key': 'id', 'sort_dir': SortDirection.DESCENDING} listed_images = self.images.behaviors.list_all_images(**api_args) self.assertNotEqual( len(listed_images), 0, msg=('Unexpected number of images received. Expected: Not {0} ' 'Received: {1}').format(0, len(listed_images))) for current, next_ in zip(listed_images[0::2], listed_images[1::2]): current_item = getattr(current, sort_key_var).replace('-', '') next_item = getattr(next_, sort_key_var).replace('-', '') self.assertGreaterEqual( current_item, next_item, msg=('Unexpected item received. Expected: Greater than or ' 'equal to {0} Received: {1}').format(next_item, current_item))
class DeactivateImage(ImagesIntegrationFixture): @classmethod def setUpClass(cls): super(DeactivateImage, cls).setUpClass() # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('deactivate_image')}, count=3) cls.deleted_image = created_images.pop() cls.images.client.delete_image(cls.deleted_image.id_) cls.protected_image = created_images.pop() cls.images.client.update_image(cls.protected_image.id_, replace={'protected': True}) cls.created_image = created_images.pop() created_server = cls.compute.servers.behaviors.create_active_server( image_ref=cls.images.config.primary_image).entity cls.resources.add(created_server.id, cls.compute.servers.client.delete_server) cls.created_snapshot = ( cls.compute.images.behaviors.create_active_image( created_server.id).entity) cls.resources.add(cls.created_snapshot.id, cls.images.client.delete_image) @classmethod def tearDownClass(cls): cls.images.client.update_image(cls.protected_image.id_, replace={'protected': False}) cls.images.behaviors.resources.release() super(DeactivateImage, cls).tearDownClass() @data_driven_test(ImagesDatasetListGenerator.DeactivateImageTypes()) def ddtest_deactivate_image(self, image): """ @summary: Deactivate an image 1) Deactivate an image via wrapper test method 2) Verify that the image's status is deactivated """ get_image = self._deactivate_image(image.id_) self.assertEqual(get_image.status, ImageStatus.DEACTIVATED, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(image.id_, ImageStatus.DEACTIVATED, get_image.status)) def test_deactivate_protected_image(self): """ @summary: Deactivate a protected image 1) Deactivate a protected image via wrapper test method 2) Verify that the image's status is deactivated """ get_image = self._deactivate_image(self.protected_image.id_) self.assertEqual(get_image.status, ImageStatus.DEACTIVATED, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.protected_image.id_, ImageStatus.DEACTIVATED, get_image.status)) def test_deactivate_snapshot_image(self): """ @summary: Deactivate a snapshot image 1) Deactivate a snapshot image via wrapper test method 2) Verify that the image's status is deactivated """ get_image = self._deactivate_image(self.created_snapshot.id) self.assertEqual(get_image.status, ImageStatus.DEACTIVATED, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.created_snapshot.id, ImageStatus.DEACTIVATED, get_image.status)) def test_deactivate_deleted_image(self): """ @summary: Deactivate a deleted image 1) Deactivate a deleted image 2) Verify that the response code is 404 """ resp = self.images_admin.client.deactivate_image( self.deleted_image.id_) self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code)) def test_deactivate_image_using_non_admin_forbidden(self): """ @summary: Deactivate an image as non-admin 1) Deactivate an image as non-admin via wrapper test method 2) Verify that the image's status is still active """ get_image = self._deactivate_image(self.created_image.id_, self.images.client, 403) self.assertEqual(get_image.status, ImageStatus.ACTIVE, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.created_image.id_, ImageStatus.ACTIVE, get_image.status)) def test_deactivate_image_using_invalid_image_id(self): """ @summary: Deactivate an image using an invalid image id 1) Deactivate an image using an invalid image id 2) Verify that the response code is 404 """ resp = self.images_admin.client.deactivate_image(image_id='invalid') self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code)) def _deactivate_image(self, image_id, images_client=None, response_code=None): """ @summary: Deactivate image and return the get image details response @param image_id: Image id to deactivate @type image_id: Uuid @param images_client: Images client to user @type images_client: Object @param response_code: Response status code @type response_code: Integer @return: Get image details response @rtype: Object 1) Deactivate an image as specified user 2) Verify that the response code is as expected 3) Get image details passing in the image id 4) Verify that the response is ok 5) Return the get image details response """ if images_client is None: images_client = self.images_admin.client if response_code is None: response_code = 204 resp = images_client.deactivate_image(image_id) self.assertEqual( resp.status_code, response_code, Messages.STATUS_CODE_MSG.format(response_code, resp.status_code)) resp = self.images.client.get_image_details(image_id) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) return resp.entity
class ReactivateImage(ImagesIntegrationFixture): @classmethod def setUpClass(cls): super(ReactivateImage, cls).setUpClass() # Count set to number of images required for this module created_images = cls.images.behaviors.create_images_via_task( image_properties={'name': rand_name('deactivate_image')}, count=3) cls.deleted_image = created_images.pop() cls.images.client.delete_image(cls.deleted_image.id_) cls.protected_image = created_images.pop() cls.images_admin.client.deactivate_image(cls.protected_image.id_) cls.images.client.update_image( cls.protected_image.id_, replace={'protected': True}) cls.deactivated_image = created_images.pop() cls.images_admin.client.deactivate_image(cls.deactivated_image.id_) created_server = cls.compute.servers.behaviors.create_active_server( image_ref=cls.images.config.primary_image).entity cls.resources.add( created_server.id, cls.compute.servers.client.delete_server) cls.created_snapshot = ( cls.compute.images.behaviors.create_active_image( created_server.id).entity) cls.resources.add( cls.created_snapshot.id, cls.images.client.delete_image) cls.images_admin.client.deactivate_image(cls.created_snapshot.id) @classmethod def tearDownClass(cls): cls.images.client.update_image( cls.protected_image.id_, replace={'protected': False}) cls.images.behaviors.resources.release() super(ReactivateImage, cls).tearDownClass() @data_driven_test( ImagesDatasetListGenerator.ReactivateImageTypes()) def ddtest_reactivate_image(self, image): """ @summary: Reactivate an image 1) Reactivate an image 2) Verify that the response code is 204 3) Get image details passing in the image id 4) Verify that the response is ok 5) Verify that the returned image's status is active """ resp = self.images_admin.client.reactivate_image(image.id_) self.assertEqual( resp.status_code, 204, Messages.STATUS_CODE_MSG.format(204, resp.status_code)) resp = self.images.client.get_image_details(image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( get_image.status, ImageStatus.ACTIVE, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(image.id_, ImageStatus.ACTIVE, get_image.status)) def test_reactivate_protected_image(self): """ @summary: Reactivate a protected image 1) Reactivate a protected image 2) Verify that the response code is 204 3) Get image details passing in the image id 4) Verify that the response is ok 5) Verify that the returned image's status is active """ resp = self.images_admin.client.reactivate_image( self.protected_image.id_) self.assertEqual( resp.status_code, 204, Messages.STATUS_CODE_MSG.format(204, resp.status_code)) resp = self.images.client.get_image_details(self.protected_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( get_image.status, ImageStatus.ACTIVE, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.protected_image.id_, ImageStatus.ACTIVE, get_image.status)) def test_reactivate_snapshot_image(self): """ @summary: Reactivate a snapshot image 1) Reactivate a snapshot image 2) Verify that the response code is 204 3) Get image details passing in the image id 4) Verify that the response is ok 5) Verify that the returned image's status is active """ resp = self.images_admin.client.reactivate_image( self.created_snapshot.id) self.assertEqual( resp.status_code, 204, Messages.STATUS_CODE_MSG.format(204, resp.status_code)) resp = self.images.client.get_image_details(self.created_snapshot.id) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( get_image.status, ImageStatus.ACTIVE, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.created_snapshot.id, ImageStatus.ACTIVE, get_image.status)) def test_reactivate_deleted_image(self): """ @summary: Reactivate a deleted image 1) Reactivate a deleted image 2) Verify that the response code is 404 """ resp = self.images_admin.client.reactivate_image( self.deleted_image.id_) self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code)) def test_reactivate_image_using_non_admin_forbidden(self): """ @summary: Reactivate an image as non-admin 1) Reactivate an image as non-admin 2) Verify that the response code is 403 3) Get image details passing in the image id 4) Verify that the response is ok 5) Verify that the returned image's status is still deactivated """ resp = self.images.client.reactivate_image( self.deactivated_image.id_) self.assertEqual( resp.status_code, 403, Messages.STATUS_CODE_MSG.format(403, resp.status_code)) resp = self.images.client.get_image_details( self.deactivated_image.id_) self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code)) get_image = resp.entity self.assertEqual( get_image.status, ImageStatus.DEACTIVATED, msg=('Unexpected status for image {0}. ' 'Expected: {1} Received: ' '{2}').format(self.deactivated_image.id_, ImageStatus.DEACTIVATED, get_image.status)) def test_reactivate_image_using_invalid_image_id(self): """ @summary: Reactivate an image using an invalid image id 1) Reactivate an image using an invalid image id 2) Verify that the response code is 404 """ resp = self.images_admin.client.deactivate_image(image_id='invalid') self.assertEqual( resp.status_code, 404, Messages.STATUS_CODE_MSG.format(404, resp.status_code))