def test_flag_update(self): """flag_update=False avoids update even if Subtask returns True.""" txfx = self.useFixture(fx.WrapperTaskFx(self.dwrap)) tx1 = tx.WrapperTask('tx1', self.getter) tx1.add_functor_subtask(lambda x: True, flag_update=False) tx1.execute() self.assertEqual(0, txfx.patchers['update'].mock.call_count) # But if there's another Subtask that returns True without # flag_update=False, it does trigger an update. tx1.add_functor_subtask(lambda x: True) tx1.execute() self.assertEqual(1, txfx.patchers['update'].mock.call_count)
def test_logspec(self): txfx = self.useFixture(fx.WrapperTaskFx(self.dwrap)) tx1 = tx.WrapperTask('tx1', self.getter) mock_log = mock.Mock() mock_log.side_effect = lambda *args: txfx.log('log') def functor(wrp): txfx.log('functor') # "False" logspec ignored tx1.add_functor_subtask(functor, logspec=[]) # logspec must have at least two args self.assertRaises(ValueError, tx1.add_functor_subtask, functor, logspec=[1]) # First arg must be callable self.assertRaises(ValueError, tx1.add_functor_subtask, functor, logspec=[1, 2]) # Valid call with just a string tx1.add_functor_subtask(functor, logspec=[mock_log, "string"]) # Valid call with a format string and args tx1.add_functor_subtask(functor, logspec=[mock_log, "one %s two %s", 1, 2]) # Valid call with named args tx1.add_functor_subtask(functor, logspec=[ mock_log, "three %(three)s four %(four)s", { 'three': 3, 'four': 4 } ]) tx1.execute() self.assertEqual([ 'lock', 'get', 'functor', 'log', 'functor', 'log', 'functor', 'log', 'functor', 'unlock' ], txfx.get_log()) mock_log.assert_has_calls([ mock.call("string"), mock.call("one %s two %s", 1, 2), mock.call("three %(three)s four %(four)s", { 'three': 3, 'four': 4 }) ])
def _attach_vopt(self, instance, lpar_uuid, vopt, stg_ftsk=None): """Will attach the vopt to the VIOS. If the stg_ftsk is provided, adds the mapping to the stg_ftsk, but won't attach until the stg_ftsk is independently executed. :param instance: The VM instance from OpenStack. :param lpar_uuid: The UUID of the client LPAR :param vopt: The virtual optical device to add. :param stg_ftsk: (Optional) If provided, the tasks to create the storage mappings to connect the Media to the VM will be deferred on to the FeedTask passed in. The execute can be done all in one method (batched together). If None (the default), the media will be attached immediately. """ # If no transaction manager, build locally so that we can run # immediately if stg_ftsk is None: wtsk = pvm_tx.WrapperTask( 'media_attach', pvm_vios.VIOS.getter(self.adapter, entry_uuid=self.vios_uuid, xag=[pvm_const.XAG.VIO_SMAP])) else: wtsk = stg_ftsk.wrapper_tasks[self.vios_uuid] # Define the function to build and add the mapping def add_func(vios_w): LOG.info(_LI("Adding cfg drive mapping for instance %(inst)s for " "Virtual I/O Server %(vios)s"), { 'inst': instance.name, 'vios': vios_w.name }, instance=instance) mapping = tsk_map.build_vscsi_mapping(self.host_uuid, vios_w, lpar_uuid, vopt) return tsk_map.add_map(vios_w, mapping) wtsk.add_functor_subtask(add_func) # If built locally, then execute if stg_ftsk is None: wtsk.execute()
def test_wrapper_task2(self): # Now: # o Fake like update forces retry # o Test add_functor_subtask, including chaining # o Ensure GET is deferred when .wrapper() is not called ahead of time. # o Make sure subtask args are getting to the subtask. txfx = fx.WrapperTaskFx(self.dwrap) def _update_retries_twice(timeout=-1): self.assertEqual(123, timeout) return self.retry_twice(self.dwrap, self.tracker, txfx) txfx.patchers['update'].side_effect = _update_retries_twice self.useFixture(txfx) def functor(wrapper, arg1, arg2, kwarg3=None, kwarg4=None): txfx.log('functor') # Make sure args are getting here self.assertEqual(['arg', 1], arg1) self.assertEqual('arg2', arg2) self.assertIsNone(kwarg3) self.assertEqual('kwarg4', kwarg4) return wrapper, True # Instantiate-add-execute chain tx.WrapperTask( 'tx2', self.getter, update_timeout=123).add_functor_subtask(functor, ['arg', 1], 'arg2', kwarg4='kwarg4').execute() # Check the overall order. Update should have been called thrice (two # retries) self.assertEqual(3, txfx.patchers['update'].mock.call_count) self.assertEqual([ 'lock', 'get', 'functor', 'update 1', 'refresh', 'functor', 'update 2', 'refresh', 'functor', 'update 3', 'unlock' ], txfx.get_log())
def test_subtask_provides(self): self.useFixture(fx.WrapperTaskFx(self.dwrap)) test_case = self class ChainSubtask(tx.Subtask): def __init__(self, val, *args, **kwargs): self.val = val super(ChainSubtask, self).__init__(*args, **kwargs) def execute(self, *args, **kwargs): test_case.assertEqual(test_case.dwrap, args[0]) # If execute accepts **kwargs, 'provided' is provided. test_case.assertIn('provided', kwargs) test_case.assertEqual(kwargs['expected_provided'], kwargs['provided']) return self.val class ChainSubtask2(tx.Subtask): def execute(self, wrp, provided, expected_provided): test_case.assertEqual(test_case.dwrap, wrp) # Able to get 'provided' as a named parameter test_case.assertEqual(expected_provided, provided) wtsk = tx.WrapperTask('name', self.getter) wtsk.add_subtask(ChainSubtask(1, provides='one', expected_provided={})) # Can't add another Subtask with the same 'provides' self.assertRaises(ValueError, wtsk.add_subtask, ChainSubtask(2, provides='one')) # Next subtask should see the result from the first. wtsk.add_subtask( ChainSubtask(2, provides='two', expected_provided={'one': 1})) # Add one that doesn't provide. Its return shouldn't show up in # 'provided'. wtsk.add_subtask( ChainSubtask(3, expected_provided={ 'one': 1, 'two': 2 })) # 'provided' works implicitly when it's a named parameter on execute wtsk.add_subtask(ChainSubtask2(expected_provided={'one': 1, 'two': 2})) # Even when execute doesn't return anything, we 'provide' that None wtsk.add_subtask( ChainSubtask2(provides='four', expected_provided={ 'one': 1, 'two': 2 })) # Make sure the same stuff works for functors def ret_val_kwargs(*args, **kwargs): self.assertEqual(self.dwrap, args[0]) self.assertIn('provided', kwargs) self.assertEqual(kwargs['expected_provided'], kwargs['provided']) return args[1] def ret_val_explicit(wrp, val, provided, expected_provided): self.assertEqual(self.dwrap, wrp) self.assertEqual(expected_provided, provided) return val self.assertRaises(ValueError, wtsk.add_functor_subtask, int, provides='one') wtsk.add_functor_subtask(ret_val_kwargs, 5, provides='five', expected_provided={ 'one': 1, 'two': 2, 'four': None }) wtsk.add_functor_subtask(ret_val_kwargs, 6, expected_provided={ 'one': 1, 'two': 2, 'four': None, 'five': 5 }) wtsk.add_functor_subtask(ret_val_explicit, 7, provides='seven', expected_provided={ 'one': 1, 'two': 2, 'four': None, 'five': 5 }) wtsk.add_functor_subtask(ret_val_explicit, 8, expected_provided={ 'one': 1, 'two': 2, 'four': None, 'five': 5, 'seven': 7 }) # Execute the WrapperTask, verifying assertions in ChainSubtask[2] and # ret_val_{kwargs|explicit) wrapper, subtask_rets = wtsk.execute() self.assertEqual(self.dwrap, wrapper) # Verify final form of subtask_rets returned from WrapperTask.execute() self.assertEqual( { 'one': 1, 'two': 2, 'four': None, 'five': 5, 'seven': 7 }, subtask_rets)
def test_wrapper_task1(self): txfx = self.useFixture(fx.WrapperTaskFx(self.dwrap)) # Must supply a wrapper or getter to instantiate self.assertRaises(ValueError, tx.WrapperTask, 'foo', 'bar') # Create a valid WrapperTask tx1 = tx.WrapperTask('tx1', self.getter) self.assertEqual('tx1', tx1.name) self.assertIn('wrapper_getter_uuid', tx1.provides) self.assertIn('subtask_rets_getter_uuid', tx1.provides) # Nothing has been run yet self.assertEqual([], txfx.get_log()) # Try running with no subtasks self.assertRaises(ex.WrapperTaskNoSubtasks, tx1.execute) # Try adding something that isn't a Subtask self.assertRaises(ValueError, tx1.add_subtask, 'Not a Subtask') # Error paths don't run anything. self.assertEqual([], txfx.get_log()) # Add a subtask that doesn't change anything tx1.add_subtask( self.LparNameAndMem('z3-9-5-126-127-00000001', logger=txfx)) # Adding a subtask does not run anything self.assertEqual([], txfx.get_log()) # Get the wrapper - this should invoke GET, but *not* under lock self.assertEqual(self.dwrap, tx1.wrapper) self.assertEqual(['get'], txfx.get_log()) # Run the transaction lwrap, subtask_rets = tx1.execute() # The name should be unchanged self.assertEqual('z3-9-5-126-127-00000001', lwrap.name) # And update should not have been called, which should be reflected in # the log. Note that 'get' is NOT called a second time. self.assertEqual([ 'get', 'lock', 'LparNameAndMem_z3-9-5-126-127-00000001', 'unlock' ], txfx.get_log()) self.assertEqual({}, subtask_rets) txfx.reset_log() # These subtasks do change the name. tx1.add_subtask(self.LparNameAndMem('new_name', logger=txfx)) tx1.add_subtask(self.LparNameAndMem('newer_name', logger=txfx)) # But this one doesn't. We're making sure the last 'no update needed' # doesn't make the overall update_needed status False. tx1.add_subtask(self.LparNameAndMem('newer_name', logger=txfx)) # Get the wrapper - this should *not* reinvoke GET self.assertEqual(self.dwrap, tx1.wrapper) self.assertEqual([], txfx.get_log()) # Now execute the transaction lwrap, subtask_rets = tx1.execute() # The last change should be the one that stuck self.assertEqual('newer_name', lwrap.name) # Check the overall order. Update was called. self.assertEqual([ 'lock', 'LparNameAndMem_z3-9-5-126-127-00000001', 'LparNameAndMem_new_name', 'LparNameAndMem_newer_name', 'LparNameAndMem_newer_name', 'update', 'unlock' ], txfx.get_log()) self.assertEqual({}, subtask_rets) # Test 'cloning' the subtask list txfx.reset_log() tx2 = tx.WrapperTask('tx2', self.getter, subtasks=tx1.subtasks) # Add another one to make sure it goes at the end tx2.add_subtask(self.LparNameAndMem('newest_name', logger=txfx)) # Add one to the original transaction to make sure it doesn't affect # this one. tx1.add_subtask(self.LparNameAndMem('bogus_name', logger=txfx)) lwrap, subtask_rets = tx2.execute() # The last change should be the one that stuck self.assertEqual('newest_name', lwrap.name) # Check the overall order. This one GETs under lock. Update called. self.assertEqual([ 'lock', 'get', 'LparNameAndMem_z3-9-5-126-127-00000001', 'LparNameAndMem_new_name', 'LparNameAndMem_newer_name', 'LparNameAndMem_newer_name', 'LparNameAndMem_newest_name', 'update', 'unlock' ], txfx.get_log()) self.assertEqual({}, subtask_rets)
def test_wrapper_task_allow_empty(self): """Test the allow_empty=True condition.""" # No mocks - no REST calls should be run. tx1 = tx.WrapperTask('tx1', self.getter, allow_empty=True) # Does not raise, returns None self.assertIsNone(tx1.execute())