Fix serialization of OpenStack actions

* In the recent version 2.3.0 of mistral-lib there has been added
  the new serialization mechanism for actions since the communication
  schema between Mistral engine and Mistral remote executor will soon
  require it (once merged). All classes for OpenStack actions are
  eventually dynamically generated based on the static classes like
  NovaAction, HeatAction etc., so to make the serialization work
  correctly we have to take this into account. The newly added class
  OpenStackActionSerializer takes care of that.

Change-Id: I8d7e2db0b17fb8f055f77363667ca4ab2c501b34
This commit is contained in:
Renat Akhmerov 2020-09-09 14:20:20 +07:00
parent b5e08f2276
commit f4272e3e3a
6 changed files with 104 additions and 4 deletions

View File

@ -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

View File

@ -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,

View File

@ -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()

View File

@ -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())

View File

@ -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)

View File

@ -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