def test_assert_task_lock(self): mock_repo = mock.MagicMock() mock_repo.get.return_value.extra_properties = { 'os_glance_import_task': TASK_ID1 } wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) wrapper.assert_task_lock() # Try again with a different task ID and it should fail wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, 'foo') self.assertRaises(exception.TaskAbortedError, wrapper.assert_task_lock)
def test_revert_updates_status_keys(self): img_repo = mock.MagicMock() task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", "store1", True, True) extra_properties = { "os_glance_importing_to_stores": "store1,store2", "os_glance_import_task": TASK_ID1 } image = self.img_factory.new_image(image_id=UUID1, extra_properties=extra_properties) img_repo.get.return_value = image fail_key = 'os_glance_failed_import' pend_key = 'os_glance_importing_to_stores' image_import.revert(None) self.assertEqual('store2', image.extra_properties[pend_key]) try: raise Exception('foo') except Exception: fake_exc_info = sys.exc_info() extra_properties = {"os_glance_importing_to_stores": "store1,store2"} image_import.revert(taskflow.types.failure.Failure(fake_exc_info)) self.assertEqual('store2', image.extra_properties[pend_key]) self.assertEqual('store1', image.extra_properties[fail_key])
def setUp(self): super(TestWebDownloadTask, self).setUp() self.config(node_staging_uri='/tmp/staging') self.image_repo = mock.MagicMock() self.image_id = mock.MagicMock() self.uri = mock.MagicMock() self.task_factory = domain.TaskFactory() task_input = { "import_req": { 'method': { 'name': 'web_download', 'uri': 'http://cloud.foo/image.qcow2' } } } task_ttl = CONF.task.task_time_to_live self.task_type = 'import' request_id = 'fake_request_id' user_id = 'fake_user' self.task = self.task_factory.new_task(self.task_type, TENANT1, self.image_id, user_id, request_id, task_time_to_live=task_ttl, task_input=task_input) self.task_id = self.task.task_id self.action_wrapper = api_image_import.ImportActionWrapper( self.image_repo, self.image_id, self.task_id) self.image_repo.get.return_value = mock.MagicMock( extra_properties={'os_glance_import_task': self.task_id})
def test_drop_lock_for_task(self): mock_repo = mock.MagicMock() mock_repo.get.return_value.extra_properties = { 'os_glance_import_task': TASK_ID1} wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) wrapper.drop_lock_for_task() mock_repo.delete_property_atomic.assert_called_once_with( mock_repo.get.return_value, 'os_glance_import_task', TASK_ID1)
def test_image_size(self): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.image_id = IMAGE_ID1 mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_image.size = 123 wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) with wrapper as action: self.assertEqual(123, action.image_size)
def test_set_image_attribute_disallowed(self): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_image.status = 'bar' wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) with wrapper as action: self.assertRaises(AttributeError, action.set_image_attribute, id='foo')
def setUp(self): super(TestConvertImageTask, self).setUp() glance_store.register_opts(CONF) self.config(default_store='file', stores=['file', 'http'], filesystem_store_datadir=self.test_dir, group="glance_store") self.config(output_format='qcow2', group='image_conversion') glance_store.create_stores(CONF) self.work_dir = os.path.join(self.test_dir, 'work_dir') utils.safe_mkdirs(self.work_dir) self.config(work_dir=self.work_dir, group='task') self.context = mock.MagicMock() self.img_repo = mock.MagicMock() self.task_repo = mock.MagicMock() self.image_id = UUID1 self.gateway = gateway.Gateway() self.task_factory = domain.TaskFactory() self.img_factory = self.gateway.get_image_factory(self.context) self.image = self.img_factory.new_image(image_id=self.image_id, disk_format='raw', container_format='bare') task_input = { "import_from": "http://cloud.foo/image.raw", "import_from_format": "raw", "image_properties": { 'disk_format': 'raw', 'container_format': 'bare' } } task_ttl = CONF.task.task_time_to_live self.task_type = 'import' request_id = 'fake_request_id' user_id = 'fake_user' self.task = self.task_factory.new_task(self.task_type, TENANT1, self.image_id, user_id, request_id, task_time_to_live=task_ttl, task_input=task_input) self.image.extra_properties = { 'os_glance_import_task': self.task.task_id } self.wrapper = import_flow.ImportActionWrapper(self.img_repo, self.image_id, self.task.task_id)
def test_status_callback_limits_rate(self, mock_now, mock_log): img_repo = mock.MagicMock() task_repo = mock.MagicMock() task_repo.get.return_value.status = 'processing' wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", None, False, True) expected_calls = [] log_call = mock.call('Image import %(image_id)s copied %(copied)i MiB', { 'image_id': IMAGE_ID1, 'copied': 0 }) action = mock.MagicMock(image_id=IMAGE_ID1) mock_now.return_value = 1000 image_import._status_callback(action, 32, 32) # First call will emit immediately because we only ran __init__ # which sets the last status to zero expected_calls.append(log_call) mock_log.assert_has_calls(expected_calls) image_import._status_callback(action, 32, 64) # Second call will not emit any other logs because no time # has passed mock_log.assert_has_calls(expected_calls) mock_now.return_value += 190 image_import._status_callback(action, 32, 96) # Third call will not emit any other logs because not enough # time has passed mock_log.assert_has_calls(expected_calls) mock_now.return_value += 300 image_import._status_callback(action, 32, 128) # Fourth call will emit because we crossed five minutes expected_calls.append(log_call) mock_log.assert_has_calls(expected_calls) mock_now.return_value += 150 image_import._status_callback(action, 32, 128) # Fifth call will not emit any other logs because not enough # time has passed mock_log.assert_has_calls(expected_calls) mock_now.return_value += 3600 image_import._status_callback(action, 32, 128) # Sixth call will emit because we crossed five minutes expected_calls.append(log_call) mock_log.assert_has_calls(expected_calls)
def test_execute_confirms_lock(self, mock_log): self.img_repo.get.return_value.extra_properties = { 'os_glance_import_task': TASK_ID1} wrapper = import_flow.ImportActionWrapper(self.img_repo, IMAGE_ID1, TASK_ID1) imagelock = import_flow._ImageLock(TASK_ID1, TASK_TYPE, wrapper) imagelock.execute() mock_log.debug.assert_called_once_with('Image %(image)s import task ' '%(task)s lock confirmed', {'image': IMAGE_ID1, 'task': TASK_ID1})
def test_revert_drops_lock(self, mock_log): wrapper = import_flow.ImportActionWrapper(self.img_repo, IMAGE_ID1, TASK_ID1) imagelock = import_flow._ImageLock(TASK_ID1, TASK_TYPE, wrapper) with mock.patch.object(wrapper, 'drop_lock_for_task') as mock_drop: imagelock.revert(None) mock_drop.assert_called_once_with() mock_log.debug.assert_called_once_with('Image %(image)s import task ' '%(task)s dropped its lock ' 'after failure', {'image': IMAGE_ID1, 'task': TASK_ID1})
def test_revert_drops_lock_missing(self, mock_log): wrapper = import_flow.ImportActionWrapper(self.img_repo, IMAGE_ID1, TASK_ID1) imagelock = import_flow._ImageLock(TASK_ID1, TASK_TYPE, wrapper) with mock.patch.object(wrapper, 'drop_lock_for_task') as mock_drop: mock_drop.side_effect = exception.NotFound() imagelock.revert(None) mock_log.warning.assert_called_once_with('Image %(image)s import task ' '%(task)s lost its lock ' 'during execution!', {'image': IMAGE_ID1, 'task': TASK_ID1})
def test_set_image_attribute(self): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_image.status = 'bar' wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) with wrapper as action: action.set_image_attribute(status='foo', virtual_size=123) mock_repo.save.assert_called_once_with(mock_image, 'bar') self.assertEqual('foo', mock_image.status) self.assertEqual(123, mock_image.virtual_size)
def test_raises_when_image_deleted(self): img_repo = mock.MagicMock() task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", "store1", False, True) image = self.img_factory.new_image(image_id=UUID1) image.status = "deleted" img_repo.get.return_value = image self.assertRaises(exception.ImportTaskError, image_import.execute)
def test_wrapper_success(self): mock_repo = mock.MagicMock() mock_repo.get.return_value.extra_properties = { 'os_glance_import_task': TASK_ID1 } wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) with wrapper as action: self.assertIsInstance(action, import_flow._ImportActions) mock_repo.get.assert_has_calls( [mock.call(IMAGE_ID1), mock.call(IMAGE_ID1)]) mock_repo.save.assert_called_once_with( mock_repo.get.return_value, mock_repo.get.return_value.status)
def test_verify_active_status(self): fake_img = mock.MagicMock( status='active', extra_properties={'os_glance_import_task': TASK_ID1}) mock_repo = mock.MagicMock() mock_repo.get.return_value = fake_img wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) task = import_flow._VerifyImageState(TASK_ID1, TASK_TYPE, wrapper, 'anything!') task.execute() fake_img.status = 'importing' self.assertRaises(import_flow._NoStoresSucceeded, task.execute)
def test_image_locations(self): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.image_id = IMAGE_ID1 mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_image.locations = {'some': {'complex': ['structure']}} wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) with wrapper as action: self.assertEqual({'some': {'complex': ['structure']}}, action.image_locations) # Mutate our copy action.image_locations['foo'] = 'bar' # Make sure we did not mutate the image itself self.assertEqual({'some': {'complex': ['structure']}}, mock_image.locations)
def test_wrapper_logs_status(self, mock_log): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) mock_image.status = 'foo' with wrapper as action: action.set_image_attribute(status='bar') mock_log.debug.assert_called_once_with( 'Image %(image_id)s status changing from ' '%(old_status)s to %(new_status)s', {'image_id': IMAGE_ID1, 'old_status': 'foo', 'new_status': 'bar'}) self.assertEqual('bar', mock_image.status)
def test_raises_when_all_stores_must_succeed(self, mock_import): img_repo = mock.MagicMock() task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", "store1", True, True) extra_properties = {'os_glance_import_task': TASK_ID1} image = self.img_factory.new_image(image_id=UUID1, extra_properties=extra_properties) img_repo.get.return_value = image mock_import.set_image_data.side_effect = \ cursive_exception.SignatureVerificationError( "Signature verification failed") self.assertRaises(cursive_exception.SignatureVerificationError, image_import.execute)
def test_remove_store_from_property(self, mock_import): img_repo = mock.MagicMock() task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", "store1", True, True) extra_properties = {"os_glance_importing_to_stores": "store1,store2", "os_glance_import_task": TASK_ID1} image = self.img_factory.new_image(image_id=UUID1, extra_properties=extra_properties) img_repo.get.return_value = image image_import.execute() self.assertEqual( image.extra_properties['os_glance_importing_to_stores'], "store2")
def test_check_task_lock(self, mock_log): mock_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) image = mock.MagicMock(image_id=IMAGE_ID1) image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_repo.get.return_value = image self._grab_image(wrapper) mock_log.error.assert_not_called() image.extra_properties['os_glance_import_task'] = 'somethingelse' self.assertRaises(exception.TaskAbortedError, self._grab_image, wrapper) mock_log.error.assert_called_once_with( 'Image %(image)s import task %(task)s attempted to take action on ' 'image, but other task %(other)s holds the lock; Aborting.', {'image': image.image_id, 'task': TASK_ID1, 'other': 'somethingelse'})
def setUp(self): super(TestCopyImageTask, self).setUp() self.db = unit_test_utils.FakeDB(initialize=False) self._create_images() self.image_repo = mock.MagicMock() self.task_repo = mock.MagicMock() self.image_id = UUID1 self.staging_store = mock.MagicMock() self.task_factory = domain.TaskFactory() task_input = { "import_req": { 'method': { 'name': 'copy-image', }, 'stores': ['fast'] } } task_ttl = CONF.task.task_time_to_live self.task_type = 'import' request_id = 'fake_request_id' user_id = 'fake_user' self.task = self.task_factory.new_task(self.task_type, TENANT1, self.image_id, user_id, request_id, task_time_to_live=task_ttl, task_input=task_input) self.task_id = self.task.task_id self.action_wrapper = api_image_import.ImportActionWrapper( self.image_repo, self.image_id, self.task_id) self.image_repo.get.return_value = mock.MagicMock( extra_properties={'os_glance_import_task': self.task_id}) stores = {'cheap': 'file', 'fast': 'file'} self.config(enabled_backends=stores) store_api.register_store_opts(CONF, reserved_stores=RESERVED_STORES) self.config(default_backend='fast', group='glance_store') store_api.create_multi_stores(CONF, reserved_stores=RESERVED_STORES)
def test_wrapper_failure(self): mock_repo = mock.MagicMock() mock_repo.get.return_value.extra_properties = { 'os_glance_import_task': TASK_ID1} wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) class SpecificError(Exception): pass try: with wrapper: raise SpecificError('some failure') except SpecificError: # NOTE(danms): Make sure we only caught the test exception # and aren't hiding anything else pass mock_repo.get.assert_called_once_with(IMAGE_ID1) mock_repo.save.assert_not_called()
def test_execute_body_without_store(self): image = mock.MagicMock() img_repo = mock.MagicMock() img_repo.get.return_value = image task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", None, False, True) action = mock.MagicMock() image_import._execute(action, mock.sentinel.path) action.set_image_data.assert_called_once_with( mock.sentinel.path, TASK_ID1, backend=None, set_active=True, callback=image_import._status_callback) action.remove_importing_stores.assert_not_called()
def test_execute_body_with_store_no_path(self): image = mock.MagicMock() img_repo = mock.MagicMock() img_repo.get.return_value = image task_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(img_repo, IMAGE_ID1, TASK_ID1) image_import = import_flow._ImportToStore(TASK_ID1, TASK_TYPE, task_repo, wrapper, "http://url", "store1", False, True) action = mock.MagicMock() image_import._execute(action, None) action.set_image_data.assert_called_once_with( 'http://url', TASK_ID1, backend='store1', set_active=True, callback=image_import._status_callback) action.remove_importing_stores(['store1'])
def test_set_image_extra_properties(self, mock_log): mock_repo = mock.MagicMock() mock_image = mock_repo.get.return_value mock_image.image_id = IMAGE_ID1 mock_image.extra_properties = {'os_glance_import_task': TASK_ID1} mock_image.status = 'bar' wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) # One banned property with wrapper as action: action.set_image_extra_properties({'os_glance_foo': 'bar'}) self.assertEqual({'os_glance_import_task': TASK_ID1}, mock_image.extra_properties) mock_log.warning.assert_called() mock_log.warning.reset_mock() # Two banned properties with wrapper as action: action.set_image_extra_properties({'os_glance_foo': 'bar', 'os_glance_baz': 'bat'}) self.assertEqual({'os_glance_import_task': TASK_ID1}, mock_image.extra_properties) mock_log.warning.assert_called() mock_log.warning.reset_mock() # One banned and one allowed property with wrapper as action: action.set_image_extra_properties({'foo': 'bar', 'os_glance_foo': 'baz'}) self.assertEqual({'foo': 'bar', 'os_glance_import_task': TASK_ID1}, mock_image.extra_properties) mock_log.warning.assert_called_once_with( 'Dropping %(key)s=%(val)s during metadata injection for %(image)s', {'key': 'os_glance_foo', 'val': 'baz', 'image': IMAGE_ID1})
def test_image_id_property(self): mock_repo = mock.MagicMock() wrapper = import_flow.ImportActionWrapper(mock_repo, IMAGE_ID1, TASK_ID1) self.assertEqual(IMAGE_ID1, wrapper.image_id)
def test_execute_confirms_lock_not_held(self, mock_log): wrapper = import_flow.ImportActionWrapper(self.img_repo, IMAGE_ID1, TASK_ID1) imagelock = import_flow._ImageLock(TASK_ID1, TASK_TYPE, wrapper) self.assertRaises(exception.TaskAbortedError, imagelock.execute)