diff --git a/doc/source/contributor/vendor-passthru.rst b/doc/source/contributor/vendor-passthru.rst index 538b668f19..372fcf2ea5 100644 --- a/doc/source/contributor/vendor-passthru.rst +++ b/doc/source/contributor/vendor-passthru.rst @@ -82,7 +82,7 @@ the node vendor passthru endpoint: if 'raw_bytes' not in kwargs: raise MissingParameterValue() - @base.driver_passthru(['GET'], async=False) + @base.driver_passthru(['GET'], async_call=False) def authentication_types(self, context, **kwargs): return {"types": ["NONE", "MD5", "MD2"]} @@ -122,9 +122,11 @@ Both decorators accept these parameters: .. _VendorInterface: ../api/ironic.drivers.base.html#ironic.drivers.base.VendorInterface -* async: A boolean value to determine whether this method should run +* async_call: A boolean value to determine whether this method should run asynchronously or synchronously. Defaults to True (Asynchronously). + .. note:: This parameter was previously called "async". + The node vendor passthru decorator (`@passthru`) also accepts the following parameter: diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index d1c4b9bde7..b32216c7d4 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -619,8 +619,9 @@ VendorMetadata = collections.namedtuple('VendorMetadata', ['method', 'metadata']) -def _passthru(http_methods, method=None, async=True, driver_passthru=False, - description=None, attach=False, require_exclusive_lock=True): +def _passthru(http_methods, method=None, async=None, async_call=None, + driver_passthru=False, description=None, + attach=False, require_exclusive_lock=True): """A decorator for registering a function as a passthru function. Decorator ensures function is ready to catch any ironic exceptions @@ -634,7 +635,8 @@ def _passthru(http_methods, method=None, async=True, driver_passthru=False, :param http_methods: A list of supported HTTP methods by the vendor function. :param method: an arbitrary string describing the action to be taken. - :param async: Boolean value. If True invoke the passthru function + :param async: Deprecated, please use async_call instead. + :param async_call: Boolean value. If True invoke the passthru function asynchronously; if False, synchronously. If a passthru function touches the BMC we strongly recommend it to run asynchronously. Defaults to True. @@ -653,6 +655,22 @@ def _passthru(http_methods, method=None, async=True, driver_passthru=False, for a synchronous passthru method. If False, don't lock the node. Defaults to True. """ + if async_call is None: + if async is not None: + LOG.warning( + 'async parameter is deprecated, please use async_call instead.' + 'deprecated parameter will be removed in the next cycle.' + ) + async_call = async + else: + async_call = True + else: + if async is not None: + raise TypeError( + "async_call and async parameters couldn't be used together, " + "use async_call instead of async" + ) + def handle_passthru(func): api_method = method if api_method is None: @@ -661,7 +679,7 @@ def _passthru(http_methods, method=None, async=True, driver_passthru=False, supported_ = [i.upper() for i in http_methods] description_ = description or '' metadata = VendorMetadata(api_method, {'http_methods': supported_, - 'async': async, + 'async': async_call, 'description': description_, 'attach': attach}) if driver_passthru: @@ -687,17 +705,19 @@ def _passthru(http_methods, method=None, async=True, driver_passthru=False, return handle_passthru -def passthru(http_methods, method=None, async=True, description=None, - attach=False, require_exclusive_lock=True): - return _passthru(http_methods, method, async, driver_passthru=False, +def passthru(http_methods, method=None, async=None, description=None, + attach=False, require_exclusive_lock=True, async_call=None): + return _passthru(http_methods, method, async, async_call, + driver_passthru=False, description=description, attach=attach, require_exclusive_lock=require_exclusive_lock) -def driver_passthru(http_methods, method=None, async=True, description=None, - attach=False): - return _passthru(http_methods, method, async, driver_passthru=True, - description=description, attach=attach) +def driver_passthru(http_methods, method=None, async=None, description=None, + attach=False, async_call=None): + return _passthru(http_methods, method, async, async_call, + driver_passthru=True, description=description, + attach=attach) class VendorInterface(BaseInterface): diff --git a/ironic/drivers/modules/drac/vendor_passthru.py b/ironic/drivers/modules/drac/vendor_passthru.py index be45ef61ca..0637468970 100644 --- a/ironic/drivers/modules/drac/vendor_passthru.py +++ b/ironic/drivers/modules/drac/vendor_passthru.py @@ -50,7 +50,7 @@ class DracVendorPassthru(base.VendorInterface): return drac_common.parse_driver_info(task.node) @METRICS.timer('DracVendorPassthru.get_bios_config') - @base.passthru(['GET'], async=False, + @base.passthru(['GET'], async_call=False, description=_("Returns a dictionary containing the BIOS " "settings from a node.")) def get_bios_config(self, task, **kwargs): @@ -70,7 +70,7 @@ class DracVendorPassthru(base.VendorInterface): return bios_attrs @METRICS.timer('DracVendorPassthru.set_bios_config') - @base.passthru(['POST'], async=False, + @base.passthru(['POST'], async_call=False, description=_("Change the BIOS configuration on a node. " "Required argument : a dictionary of " "{'AttributeName': 'NewValue'}. Returns " @@ -94,7 +94,7 @@ class DracVendorPassthru(base.VendorInterface): return drac_bios.set_config(task, **kwargs) @METRICS.timer('DracVendorPassthru.commit_bios_config') - @base.passthru(['POST'], async=False, + @base.passthru(['POST'], async_call=False, description=_("Commit a BIOS configuration job submitted " "through set_bios_config(). Required " "argument: 'reboot' - indicates whether a " @@ -126,7 +126,7 @@ class DracVendorPassthru(base.VendorInterface): return {'job_id': job_id, 'reboot_required': not reboot} @METRICS.timer('DracVendorPassthru.abandon_bios_config') - @base.passthru(['DELETE'], async=False, + @base.passthru(['DELETE'], async_call=False, description=_("Abandon a BIOS configuration job previously " "submitted through set_bios_config().")) @task_manager.require_exclusive_lock @@ -142,7 +142,7 @@ class DracVendorPassthru(base.VendorInterface): """ drac_bios.abandon_config(task) - @base.passthru(['GET'], async=False, + @base.passthru(['GET'], async_call=False, description=_('Returns a dictionary containing the key ' '"unfinished_jobs"; its value is a list of ' 'dictionaries. Each dictionary represents ' diff --git a/ironic/drivers/modules/fake.py b/ironic/drivers/modules/fake.py index dc37ef1e42..3fef1fe552 100644 --- a/ironic/drivers/modules/fake.py +++ b/ironic/drivers/modules/fake.py @@ -154,7 +154,7 @@ class FakeVendorB(base.VendorInterface): def second_method(self, task, http_method, bar): return True if bar == 'kazoo' else False - @base.passthru(['POST'], async=False, + @base.passthru(['POST'], async_call=False, description=_("Test if the value of bar is meow")) def third_method_sync(self, task, http_method, bar): return True if bar == 'meow' else False diff --git a/ironic/tests/unit/api/controllers/v1/test_utils.py b/ironic/tests/unit/api/controllers/v1/test_utils.py index cc962a6bac..c47f7eedf4 100644 --- a/ironic/tests/unit/api/controllers/v1/test_utils.py +++ b/ironic/tests/unit/api/controllers/v1/test_utils.py @@ -617,9 +617,13 @@ class TestVendorPassthru(base.TestCase): @mock.patch.object(pecan, 'request', spec_set=['method', 'context', 'rpcapi']) - def _vendor_passthru(self, mock_request, async=True, + def _vendor_passthru(self, mock_request, async_call=True, driver_passthru=False): - return_value = {'return': 'SpongeBob', 'async': async, 'attach': False} + return_value = { + 'return': 'SpongeBob', + 'async': async_call, + 'attach': False + } mock_request.method = 'post' mock_request.context = 'fake-context' @@ -640,20 +644,20 @@ class TestVendorPassthru(base.TestCase): self.assertIsInstance(response, wsme.api.Response) self.assertEqual('SpongeBob', response.obj) self.assertEqual(response.return_type, wsme.types.Unset) - sc = http_client.ACCEPTED if async else http_client.OK + sc = http_client.ACCEPTED if async_call else http_client.OK self.assertEqual(sc, response.status_code) def test_vendor_passthru_async(self): self._vendor_passthru() def test_vendor_passthru_sync(self): - self._vendor_passthru(async=False) + self._vendor_passthru(async_call=False) def test_driver_vendor_passthru_async(self): self._vendor_passthru(driver_passthru=True) def test_driver_vendor_passthru_sync(self): - self._vendor_passthru(async=False, driver_passthru=True) + self._vendor_passthru(async_call=False, driver_passthru=True) @mock.patch.object(pecan, 'response', spec_set=['app_iter']) @mock.patch.object(pecan, 'request', diff --git a/ironic/tests/unit/drivers/test_base.py b/ironic/tests/unit/drivers/test_base.py index 892da1c8f9..224fc67609 100644 --- a/ironic/tests/unit/drivers/test_base.py +++ b/ironic/tests/unit/drivers/test_base.py @@ -102,6 +102,16 @@ class PassthruDecoratorTestCase(base.TestCase): self.assertNotEqual(inst1.driver_routes['driver_noexception']['func'], inst2.driver_routes['driver_noexception']['func']) + @mock.patch.object(driver_base.LOG, 'warning') + def test_old_async_warning(self, mock_log_warning): + driver_base.passthru(['POST'], async=True) + mock_log_warning.assert_called_once() + + @mock.patch.object(driver_base.LOG, 'warning') + def test_new_async_call_without_warning(self, mock_log_warning): + driver_base.passthru(['POST'], async_call=True) + mock_log_warning.assert_not_called() + class CleanStepDecoratorTestCase(base.TestCase): diff --git a/releasenotes/notes/async-deprecate-b3d81d7968ea47e5.yaml b/releasenotes/notes/async-deprecate-b3d81d7968ea47e5.yaml new file mode 100644 index 0000000000..4deb448f89 --- /dev/null +++ b/releasenotes/notes/async-deprecate-b3d81d7968ea47e5.yaml @@ -0,0 +1,4 @@ +--- +other: + async parameter of passthru and driver_passthru decorators is deprecated + and will be removed in next cycle. async_call should be used instead.