diff --git a/lower-constraints.txt b/lower-constraints.txt index a770a01..1a86d9e 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -5,7 +5,7 @@ doc8==0.6.0 fixtures==3.0.0 hacking==1.1.0 keystoneauth1===3.18.0 -mistral-lib==1.4.0 +mistral-lib==2.3.0 oslo.log==3.36.0 oslotest==3.2.0 pbr==2.0.0 diff --git a/mistral_extra/actions/openstack/action_generator/base.py b/mistral_extra/actions/openstack/action_generator/base.py index 758e55e..56bbce9 100644 --- a/mistral_extra/actions/openstack/action_generator/base.py +++ b/mistral_extra/actions/openstack/action_generator/base.py @@ -33,14 +33,17 @@ def get_mapping(): for key, value in map_part.items(): if isinstance(value, dict): delete_comment(value) + if '_comment' in map_part: del map_part['_comment'] + package = version.version_info.package if os.path.isabs(CONF.openstack_actions_mapping_path): mapping_file_path = CONF.openstack_actions_mapping_path else: path = CONF.openstack_actions_mapping_path + mapping_file_path = pkg.resource_filename(package, path) LOG.info( @@ -99,11 +102,13 @@ class OpenStackActionGenerator(action_generator.ActionGenerator): return ", ".join(added) inputs = [i.strip() for i in origin_inputs.split(',')] + kwarg_index = None for index, input in enumerate(inputs): if "=" in input: kwarg_index = index + if "**" in input: kwarg_index = index - 1 @@ -144,14 +149,17 @@ class OpenStackActionGenerator(action_generator.ActionGenerator): except Exception: LOG.exception( "Failed to create action: %s.%s", - cls.action_namespace, action_name + cls.action_namespace, + action_name ) + continue arg_list = i_u.get_arg_list_as_str(client_method) # Support specifying region for OpenStack actions. modules = CONF.openstack_actions.modules_support_region + if cls.action_namespace in modules: arg_list = cls.prepare_action_inputs( arg_list, diff --git a/mistral_extra/actions/openstack/actions.py b/mistral_extra/actions/openstack/actions.py index 8fe769c..af52ee1 100644 --- a/mistral_extra/actions/openstack/actions.py +++ b/mistral_extra/actions/openstack/actions.py @@ -187,7 +187,6 @@ class HeatAction(base.OpenStackAction): return heatclient.Client def _create_client(self, context): - LOG.debug("Heat action security context: %s", context) heat_endpoint = self.get_service_endpoint() diff --git a/mistral_extra/actions/openstack/base.py b/mistral_extra/actions/openstack/base.py index bee79dc..463356e 100644 --- a/mistral_extra/actions/openstack/base.py +++ b/mistral_extra/actions/openstack/base.py @@ -22,6 +22,9 @@ from mistral_extra.actions.openstack.utils import exceptions as exc from mistral_extra.actions.openstack.utils import keystone as \ keystone_utils from mistral_lib import actions +from mistral_lib.actions import base as ml_actions_base +from mistral_lib import serialization + LOG = log.getLogger(__name__) @@ -39,6 +42,8 @@ class OpenStackAction(actions.Action): _client_class = None def __init__(self, **kwargs): + super(OpenStackAction, self).__init__() + self._kwargs_for_run = kwargs self.action_region = self._kwargs_for_run.pop('action_region', None) @@ -135,3 +140,24 @@ class OpenStackAction(actions.Action): return dict( zip(self._kwargs_for_run, ['test'] * len(self._kwargs_for_run)) ) + + @classmethod + def get_serialization_key(cls): + return "%s.%s" % (OpenStackAction.__module__, OpenStackAction.__name__) + + +class OpenStackActionSerializer(ml_actions_base.ActionSerializer): + def serialize_to_dict(self, entity): + res = super(OpenStackActionSerializer, self).serialize_to_dict(entity) + + # Since all OpenStack actions are dynamically generated with the + # function type() we need to take the base class of the action + # class (i.e. NovaAction) and write it to the result dict. + base_cls = type(entity).__bases__[0] + + res['cls'] = '%s.%s' % (base_cls.__module__, base_cls.__name__) + + return res + + +serialization.register_serializer(OpenStackAction, OpenStackActionSerializer()) diff --git a/mistral_extra/tests/unit/actions/openstack/test_openstack_action_serialization.py b/mistral_extra/tests/unit/actions/openstack/test_openstack_action_serialization.py new file mode 100644 index 0000000..4ae19d2 --- /dev/null +++ b/mistral_extra/tests/unit/actions/openstack/test_openstack_action_serialization.py @@ -0,0 +1,67 @@ +# Copyright 2020 Nokia Software. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +from mistral_extra.actions.openstack import actions +from mistral_extra.actions.openstack import base as actions_base + +from oslotest import base + + +class OpenStackActionSerializationTest(base.BaseTestCase): + @mock.patch.object(actions.NovaAction, '_get_client') + def test_nova_action_serialization(self, mocked): + mock_ctx = mock.Mock() + + method_name = "servers.get" + + # Create a dynamic action class. + action_class = type( + str(method_name), + (actions.NovaAction,), + {'client_method_name': method_name} + ) + + params = {'server': '1234-abcd'} + + # Just in case let's just make sure the action is functioning. + action = action_class(**params) + + action.run(mock_ctx) + + self.assertTrue(mocked().servers.get.called) + + mocked().servers.get.assert_called_once_with(server="1234-abcd") + + # Now let's serialize the action. + serializer = actions_base.OpenStackActionSerializer() + + serialized_action = serializer.serialize(action) + + self.assertIsNotNone(serialized_action) + + deserialized_action = serializer.deserialize(serialized_action) + + # Make sure that the action class is dynamic and it's still + # functioning. + self.assertNotEqual(actions.NovaAction, type(deserialized_action)) + + deserialized_action.run(mock_ctx) + + self.assertTrue(mocked().servers.get.called) + + mocked().servers.get.assert_called_with(server="1234-abcd") + + self.assertEqual(2, mocked().servers.get.call_count) diff --git a/requirements.txt b/requirements.txt index a6ce566..1359a2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD oslo.log>=3.36.0 # Apache-2.0 -mistral-lib>=1.4.0 # Apache-2.0 +mistral-lib>=2.3.0 # Apache-2.0 aodhclient>=0.9.0 # Apache-2.0 gnocchiclient>=3.3.1 # Apache-2.0 python-barbicanclient>=4.5.2 # Apache-2.0