def test_create_backup(self): # Prioritize glance v2 over v1 for deleting/waiting for image status. if CONF.image_feature_enabled.api_v2: glance_admin_client = self.os_admin.image_client_v2 elif CONF.image_feature_enabled.api_v1: glance_admin_client = self.os_admin.image_client else: raise lib_exc.InvalidConfiguration( 'Either api_v1 or api_v2 must be True in ' '[image-feature-enabled].') backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup') self.rbac_utils.switch_role(self, toggle_rbac_role=True) resp = self.servers_client.create_backup(self.server_id, backup_type='daily', rotation=1, name=backup_name).response # Prior to microversion 2.45, image ID must be parsed from location # header. With microversion 2.45+, image_id is returned. if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "2.45", resp, "lt"): image_id = resp['image_id'] else: image_id = data_utils.parse_image_id(resp['location']) # Use admin credentials to wait since waiting involves show, which is # a different policy. self.addCleanup(test_utils.call_and_ignore_notfound_exc, glance_admin_client.delete_image, image_id) waiters.wait_for_image_status(glance_admin_client, image_id, 'active')
def test_create_second_image_when_first_image_is_being_saved(self): """Test creating another server image when first image is being saved Creating another server image when first image is being saved is not allowed. """ try: # Create first snapshot image = self.create_image_from_server(self.server_id) self.addCleanup(self._reset_server) # Create second snapshot self.assertRaises(lib_exc.Conflict, self.create_image_from_server, self.server_id) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id( image.response['location']) self.client.delete_image(image_id) except lib_exc.TimeoutException as ex: # Test cannot capture the image saving state. # If timeout is reached, we don't need to check state, # since, it wouldn't be a 'SAVING' state atleast and apart from # it, this testcase doesn't have scope for other state transition # Hence, skip the test. raise self.skipException("This test is skipped because " + str(ex))
def test_compare_versions_header_not_present(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = '2.1' test_response = {} self.assertFalse( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "eq"))
def test_compare_versions_greater_than(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = '2.1' test_response = {microversion_header_name: '2.2'} self.assertFalse( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "gt"))
def test_compare_versions_with_name_in_microversion(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = 'volume 3.1' test_response = {microversion_header_name: 'volume 3.1'} self.assertTrue( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "eq"))
def test_compare_versions_header_not_present(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = '2.1' test_response = {} self.assertFalse( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "eq"))
def test_compare_versions_with_name_in_microversion(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = 'volume 3.1' test_response = {microversion_header_name: 'volume 3.1'} self.assertTrue( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "eq"))
def test_compare_versions_greater_than(self): microversion_header_name = 'x-openstack-xyz-api-version' request_microversion = '2.1' test_response = {microversion_header_name: '2.2'} self.assertFalse( api_version_utils.compare_version_header_to_response( microversion_header_name, request_microversion, test_response, "gt"))
def test_delete_image_that_is_not_yet_active(self): """Test deleting a non-active server image should fail""" image = self.create_image_from_server(self.server_id) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) self.addCleanup(self._reset_server) # Do not wait, attempt to delete the image, ensure it's successful self.client.delete_image(image_id) self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
def test_create_image_specify_multibyte_character_image_name(self): # prefix character is: # http://unicode.org/cldr/utility/character.jsp?a=20A1 # We use a string with 3 byte utf-8 character due to nova/glance which # will return 400(Bad Request) if we attempt to send a name which has # 4 byte utf-8 character. utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8')) body = self.client.create_image(self.server_id, name=utf8_name) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", body.response, "lt"): image_id = body['image_id'] else: image_id = data_utils.parse_image_id(body.response['location']) self.addCleanup(self.client.delete_image, image_id)
def test_create_image_specify_multibyte_character_image_name(self): # prefix character is: # http://unicode.org/cldr/utility/character.jsp?a=20A1 # We use a string with 3 byte utf-8 character due to nova/glance which # will return 400(Bad Request) if we attempt to send a name which has # 4 byte utf-8 character. utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8')) body = self.client.create_image(self.server_id, name=utf8_name) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", body.response, "lt"): image_id = body['image_id'] else: image_id = data_utils.parse_image_id(body.response['location']) self.addCleanup(self.client.delete_image, image_id)
def test_delete_image_that_is_not_yet_active(self): # Return an error while trying to delete an image what is creating image = self.create_image_from_server(self.server_id) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) self.addCleanup(self._reset_server) # Do not wait, attempt to delete the image, ensure it's successful self.client.delete_image(image_id) self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
def create_image_from_server(cls, server_id, **kwargs): """Wrapper utility that returns an image created from the server.""" name = kwargs.pop('name', data_utils.rand_name(cls.__name__ + "-image")) wait_until = kwargs.pop('wait_until', None) wait_for_server = kwargs.pop('wait_for_server', True) image = cls.compute_images_client.create_image(server_id, name=name, **kwargs) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, cls.compute_images_client.delete_image, image_id) if wait_until is not None: try: waiters.wait_for_image_status(cls.compute_images_client, image_id, wait_until) except lib_exc.NotFound: if wait_until.upper() == 'ACTIVE': # If the image is not found after create_image returned # that means the snapshot failed in nova-compute and nova # deleted the image. There should be a compute fault # recorded with the server in that case, so get the server # and dump some details. server = ( cls.servers_client.show_server(server_id)['server']) if 'fault' in server: raise exceptions.SnapshotNotFoundException( server['fault'], image_id=image_id) else: raise exceptions.SnapshotNotFoundException( image_id=image_id) else: raise image = cls.compute_images_client.show_image(image_id)['image'] if wait_until.upper() == 'ACTIVE': if wait_for_server: waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE') return image
def test_create_second_image_when_first_image_is_being_saved(self): # Disallow creating another image when first image is being saved # Create first snapshot image = self.create_image_from_server(self.server_id) self.addCleanup(self._reset_server) # Create second snapshot self.assertRaises(lib_exc.Conflict, self.create_image_from_server, self.server_id) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) self.client.delete_image(image_id)
def test_create_second_image_when_first_image_is_being_saved(self): # Disallow creating another image when first image is being saved # Create first snapshot image = self.create_image_from_server(self.server_id) self.addCleanup(self._reset_server) # Create second snapshot self.assertRaises(lib_exc.Conflict, self.create_image_from_server, self.server_id) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) self.client.delete_image(image_id)
def create_image_from_server(cls, server_id, **kwargs): """Wrapper utility that returns an image created from the server. If compute microversion >= 2.36, the returned image response will be from the image service API rather than the compute image proxy API. """ name = kwargs.pop('name', data_utils.rand_name(cls.__name__ + "-image")) wait_until = kwargs.pop('wait_until', None) wait_for_server = kwargs.pop('wait_for_server', True) image = cls.compute_images_client.create_image(server_id, name=name, **kwargs) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) # The compute image proxy APIs were deprecated in 2.35 so # use the images client directly if the API microversion being # used is >=2.36. if not cls.is_requested_microversion_compatible('2.35'): client = cls.images_client else: client = cls.compute_images_client cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_image, image_id) if wait_until is not None: try: wait_until = wait_until.upper() if not cls.is_requested_microversion_compatible('2.35'): wait_until = wait_until.lower() waiters.wait_for_image_status(client, image_id, wait_until) except lib_exc.NotFound: if wait_until.upper() == 'ACTIVE': # If the image is not found after create_image returned # that means the snapshot failed in nova-compute and nova # deleted the image. There should be a compute fault # recorded with the server in that case, so get the server # and dump some details. server = ( cls.servers_client.show_server(server_id)['server']) if 'fault' in server: raise exceptions.SnapshotNotFoundException( server['fault'], image_id=image_id) else: raise exceptions.SnapshotNotFoundException( image_id=image_id) else: raise image = client.show_image(image_id) # Compute image client returns response wrapped in 'image' element # which is not the case with Glance image client. if 'image' in image: image = image['image'] if wait_until.upper() == 'ACTIVE': if wait_for_server: waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE') return image
def create_image_from_server(cls, server_id, **kwargs): """Wrapper utility that returns an image created from the server. If compute microversion >= 2.36, the returned image response will be from the image service API rather than the compute image proxy API. """ name = kwargs.pop('name', data_utils.rand_name(cls.__name__ + "-image")) wait_until = kwargs.pop('wait_until', None) wait_for_server = kwargs.pop('wait_for_server', True) image = cls.compute_images_client.create_image(server_id, name=name, **kwargs) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", image.response, "lt"): image_id = image['image_id'] else: image_id = data_utils.parse_image_id(image.response['location']) # The compute image proxy APIs were deprecated in 2.35 so # use the images client directly if the API microversion being # used is >=2.36. if not cls.is_requested_microversion_compatible('2.35'): client = cls.images_client else: client = cls.compute_images_client cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_image, image_id) if wait_until is not None: try: wait_until = wait_until.upper() if not cls.is_requested_microversion_compatible('2.35'): wait_until = wait_until.lower() waiters.wait_for_image_status(client, image_id, wait_until) except lib_exc.NotFound: if wait_until.upper() == 'ACTIVE': # If the image is not found after create_image returned # that means the snapshot failed in nova-compute and nova # deleted the image. There should be a compute fault # recorded with the server in that case, so get the server # and dump some details. server = ( cls.servers_client.show_server(server_id)['server']) if 'fault' in server: raise exceptions.SnapshotNotFoundException( server['fault'], image_id=image_id) else: raise exceptions.SnapshotNotFoundException( image_id=image_id) else: raise image = client.show_image(image_id) # Compute image client returns response wrapped in 'image' element # which is not the case with Glance image client. if 'image' in image: image = image['image'] if wait_until.upper() == 'ACTIVE': if wait_for_server: waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE') return image
def test_create_backup(self): # Positive test:create backup successfully and rotate backups correctly # create the first and the second backup # Check if glance v1 is available to determine which client to use. We # prefer glance v1 for the compute API tests since the compute image # API proxy was written for glance v1. if CONF.image_feature_enabled.api_v1: glance_client = self.os_primary.image_client elif CONF.image_feature_enabled.api_v2: glance_client = self.os_primary.image_client_v2 else: raise lib_exc.InvalidConfiguration( 'Either api_v1 or api_v2 must be True in ' '[image-feature-enabled].') backup1 = data_utils.rand_name('backup-1') resp = self.client.create_backup(self.server_id, backup_type='daily', rotation=2, name=backup1) oldest_backup_exist = True # the oldest one should be deleted automatically in this test def _clean_oldest_backup(oldest_backup): if oldest_backup_exist: try: glance_client.delete_image(oldest_backup) except lib_exc.NotFound: pass else: LOG.warning("Deletion of oldest backup %s should not have " "been successful as it should have been " "deleted during rotation.", oldest_backup) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", resp.response, "lt"): image1_id = resp['image_id'] else: image1_id = data_utils.parse_image_id(resp.response['location']) self.addCleanup(_clean_oldest_backup, image1_id) waiters.wait_for_image_status(glance_client, image1_id, 'active') backup2 = data_utils.rand_name('backup-2') waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE') resp = self.client.create_backup(self.server_id, backup_type='daily', rotation=2, name=backup2) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", resp.response, "lt"): image2_id = resp['image_id'] else: image2_id = data_utils.parse_image_id(resp.response['location']) self.addCleanup(glance_client.delete_image, image2_id) waiters.wait_for_image_status(glance_client, image2_id, 'active') # verify they have been created properties = { 'image_type': 'backup', 'backup_type': "daily", 'instance_uuid': self.server_id, } params = { 'status': 'active', 'sort_key': 'created_at', 'sort_dir': 'asc' } if CONF.image_feature_enabled.api_v1: for key, value in properties.items(): params['property-%s' % key] = value image_list = glance_client.list_images( detail=True, **params)['images'] else: # Additional properties are flattened in glance v2. params.update(properties) image_list = glance_client.list_images(params)['images'] self.assertEqual(2, len(image_list)) self.assertEqual((backup1, backup2), (image_list[0]['name'], image_list[1]['name'])) # create the third one, due to the rotation is 2, # the first one will be deleted backup3 = data_utils.rand_name('backup-3') waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE') resp = self.client.create_backup(self.server_id, backup_type='daily', rotation=2, name=backup3) if api_version_utils.compare_version_header_to_response( "OpenStack-API-Version", "compute 2.45", resp.response, "lt"): image3_id = resp['image_id'] else: image3_id = data_utils.parse_image_id(resp.response['location']) self.addCleanup(glance_client.delete_image, image3_id) # the first back up should be deleted waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE') glance_client.wait_for_resource_deletion(image1_id) oldest_backup_exist = False if CONF.image_feature_enabled.api_v1: image_list = glance_client.list_images( detail=True, **params)['images'] else: image_list = glance_client.list_images(params)['images'] self.assertEqual(2, len(image_list), 'Unexpected number of images for ' 'v2:test_create_backup; was the oldest backup not ' 'yet deleted? Image list: %s' % [image['name'] for image in image_list]) self.assertEqual((backup2, backup3), (image_list[0]['name'], image_list[1]['name']))