diff --git a/mistral/actions/openstack/action_generator/base.py b/mistral/actions/openstack/action_generator/base.py index 49b41476..0995a017 100644 --- a/mistral/actions/openstack/action_generator/base.py +++ b/mistral/actions/openstack/action_generator/base.py @@ -23,12 +23,8 @@ from mistral.actions import action_generator from mistral.utils import inspect_utils as i_u from mistral import version -os_actions_mapping_path = cfg.StrOpt('openstack_actions_mapping_path', - default='actions/openstack/mapping.json') - - CONF = cfg.CONF -CONF.register_opt(os_actions_mapping_path) + LOG = logging.getLogger(__name__) @@ -84,7 +80,7 @@ class OpenStackActionGenerator(action_generator.ActionGenerator): @classmethod def create_actions(cls): mapping = get_mapping() - method_dict = mapping[cls.action_namespace] + method_dict = mapping.get(cls.action_namespace, {}) action_classes = [] diff --git a/mistral/config.py b/mistral/config.py index 4ed2f536..21c4d3eb 100644 --- a/mistral/config.py +++ b/mistral/config.py @@ -259,6 +259,18 @@ keycloak_oidc_opts = [ ) ] +# note: this command line option is used only from sync_db and +# mistral-db-manage +os_actions_mapping_path = cfg.StrOpt( + 'openstack_actions_mapping_path', + short='m', + metavar='MAPPING_PATH', + default='actions/openstack/mapping.json', + help='Path to openstack action mapping json file.' + 'It could be relative to mistral package ' + 'directory or absolute.' +) + CONF = cfg.CONF API_GROUP = 'api' diff --git a/mistral/db/sqlalchemy/migration/cli.py b/mistral/db/sqlalchemy/migration/cli.py index e7f1e3cb..172c0475 100644 --- a/mistral/db/sqlalchemy/migration/cli.py +++ b/mistral/db/sqlalchemy/migration/cli.py @@ -23,6 +23,7 @@ from oslo_utils import importutils import six import sys +from mistral import config from mistral.services import action_manager from mistral.services import workflows @@ -113,6 +114,7 @@ command_opt = cfg.SubCommandOpt('command', handler=add_command_parsers) CONF.register_cli_opt(command_opt) +CONF.register_cli_opt(config.os_actions_mapping_path) def main(): diff --git a/mistral/tests/resources/openstack/test_mapping.json b/mistral/tests/resources/openstack/test_mapping.json new file mode 100644 index 00000000..b09f0436 --- /dev/null +++ b/mistral/tests/resources/openstack/test_mapping.json @@ -0,0 +1,6 @@ +{ + "_comment": "Mapping OpenStack action namespaces to all its actions. Each action name is mapped to python-client method name in this namespace.", + "nova": { + "servers_get": "servers.get" + } +} diff --git a/mistral/tests/unit/actions/openstack/test_generator.py b/mistral/tests/unit/actions/openstack/test_generator.py index 81b60f73..78a10080 100644 --- a/mistral/tests/unit/actions/openstack/test_generator.py +++ b/mistral/tests/unit/actions/openstack/test_generator.py @@ -10,11 +10,23 @@ # 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. +import contextlib +import os + +from oslo_config import cfg from mistral.actions import generator_factory from mistral.actions.openstack import actions +from mistral import config + from mistral.tests.unit import base +ABSOLUTE_TEST_MAPPING_PATH = os.path.realpath( + os.path.join(os.path.dirname(__file__), + "../../../resources/openstack/test_mapping.json") +) + +RELATIVE_TEST_MAPPING_PATH = "tests/resources/openstack/test_mapping.json" MODULE_MAPPING = { 'nova': ['nova.servers_get', actions.NovaAction], @@ -44,6 +56,10 @@ MODULE_MAPPING = { EXTRA_MODULES = ['neutron', 'swift', 'zaqar', 'tacker'] +CONF = cfg.CONF +CONF.register_opt(config.os_actions_mapping_path) + + class GeneratorTest(base.BaseTest): def test_generator(self): for generator_cls in generator_factory.all_generators(): @@ -65,3 +81,38 @@ class GeneratorTest(base.BaseTest): self.assertTrue(issubclass(action['class'], action_cls)) self.assertEqual(method_name, action['class'].client_method_name) + + def test_missing_module_from_mapping(self): + with _patch_openstack_action_mapping_path(RELATIVE_TEST_MAPPING_PATH): + for generator_cls in generator_factory.all_generators(): + action_classes = generator_cls.create_actions() + action_names = [action['name'] for action in action_classes] + + cls = MODULE_MAPPING.get(generator_cls.action_namespace)[1] + if cls == actions.NovaAction: + self.assertEqual(['nova.servers_get'], action_names) + else: + self.assertEqual([], action_names) + + def test_absolute_mapping_path(self): + with _patch_openstack_action_mapping_path(ABSOLUTE_TEST_MAPPING_PATH): + self.assertTrue(os.path.isabs(ABSOLUTE_TEST_MAPPING_PATH), + "Mapping path is relative: %s" % + ABSOLUTE_TEST_MAPPING_PATH) + for generator_cls in generator_factory.all_generators(): + action_classes = generator_cls.create_actions() + action_names = [action['name'] for action in action_classes] + + cls = MODULE_MAPPING.get(generator_cls.action_namespace)[1] + if cls == actions.NovaAction: + self.assertEqual(['nova.servers_get'], action_names) + else: + self.assertEqual([], action_names) + + +@contextlib.contextmanager +def _patch_openstack_action_mapping_path(path): + original_path = CONF.openstack_actions_mapping_path + CONF.set_default("openstack_actions_mapping_path", path) + yield + CONF.set_default("openstack_actions_mapping_path", original_path) diff --git a/releasenotes/notes/external_openstack_action_mapping_support-5cec5d9d5192feb7.yaml b/releasenotes/notes/external_openstack_action_mapping_support-5cec5d9d5192feb7.yaml new file mode 100644 index 00000000..fe99c0a8 --- /dev/null +++ b/releasenotes/notes/external_openstack_action_mapping_support-5cec5d9d5192feb7.yaml @@ -0,0 +1,9 @@ +--- +features: + - External OpenStack action mapping file could be specified at sync_db.sh or + mistral-db-mange script. + For more details see 'sync_db.sh --help' or 'mistral-db-manage --help'. + + - From now it is optional to list openstack modules in mapping file which + you would not include into supported action set. + diff --git a/tools/sync_db.py b/tools/sync_db.py index 1f84db02..3e01a4d4 100644 --- a/tools/sync_db.py +++ b/tools/sync_db.py @@ -23,7 +23,6 @@ from mistral.db.v2 import api as db_api from mistral.services import action_manager from mistral.services import workflows - CONF = cfg.CONF @@ -36,6 +35,8 @@ def main(): for group, opts in keystonemw_opts.list_auth_token_opts(): CONF.register_opts(opts, group=group) + CONF.register_cli_opt(config.os_actions_mapping_path) + config.parse_args() if len(CONF.config_file) == 0: