From de252f95fec59db217916ee1231132ad14e5a255 Mon Sep 17 00:00:00 2001 From: "Bob.Haddleton" Date: Wed, 30 May 2018 21:43:42 -0500 Subject: [PATCH] Use stevedore Extensions for TOSCA definitions This changes the TOSCA Parser extensions implementation to use a stevedore Extensions namespace (toscaparser.extensions) to allow TOSCA schema extensions to be loaded from other sources without requiring them to be included in the tosca-parser repository. Change-Id: Ia4d9430c398b2911043047d843419ff6edebfe3f --- doc/source/usage.rst | 4 ++ requirements.txt | 1 + setup.cfg | 3 ++ toscaparser/extensions/exttools.py | 51 +++++++------------ .../mec/tosca_simple_profile_for_mec_1_0_0.py | 8 +-- .../nfv/tosca_simple_profile_for_nfv_1_0_0.py | 10 ++-- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/doc/source/usage.rst b/doc/source/usage.rst index 4da77e6b..cd7945a4 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -16,3 +16,7 @@ entry point as:: tosca-parser --template-file=toscaparser/tests/data/tosca_helloworld.yaml The value to the --template-file is required to be a relative or an absolute path. + +Custom template versions can be created and supported outside of TOSCA Parser +using the toscaparser.extensions namespace. See the NFV and MEC extensions +for examples of how to define custom template definitions and versions. diff --git a/requirements.txt b/requirements.txt index c37dad78..58c74f0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ cliff!=2.9.0,>=2.8.0 # Apache-2.0 PyYAML>=3.12 # MIT python-dateutil>=2.5.3 # BSD six>=1.10.0 # MIT +stevedore>=1.20.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 28a188e6..0d0debe5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,9 @@ packages = [entry_points] console_scripts = tosca-parser = toscaparser.shell:main +toscaparser.extensions = + tosca_simple_profile_for_nfv_1_0_0 = toscaparser.extensions.nfv.tosca_simple_profile_for_nfv_1_0_0:NfvProfile_1_0_0 + tosca_simple_profile_for_mec_1_0_0 = toscaparser.extensions.mec.tosca_simple_profile_for_mec_1_0_0:MecProfile_1_0_0 [compile_catalog] directory = toscaparser/locale diff --git a/toscaparser/extensions/exttools.py b/toscaparser/extensions/exttools.py index 96da4b22..d93eb74a 100644 --- a/toscaparser/extensions/exttools.py +++ b/toscaparser/extensions/exttools.py @@ -16,6 +16,8 @@ import importlib import logging import os +from stevedore import extension + from toscaparser.common.exception import ToscaExtAttributeError from toscaparser.common.exception import ToscaExtImportError @@ -32,41 +34,26 @@ class ExtTools(object): '''Dynamically load all the extensions .''' extensions = collections.OrderedDict() - # Use the absolute path of the class path - abs_path = os.path.dirname(os.path.abspath(__file__)) + extns = extension.ExtensionManager(namespace='toscaparser.extensions', + invoke_on_load=True).extensions - extdirs = [e for e in os.listdir(abs_path) if - not e.startswith('tests') and - os.path.isdir(os.path.join(abs_path, e))] + for e in extns: + try: + extinfo = importlib.import_module(e.plugin.__module__) + base_path = os.path.dirname(extinfo.__file__) + version = e.plugin().VERSION + defs_file = base_path + '/' + e.plugin().DEFS_FILE - for e in extdirs: - log.info(e) - extpath = abs_path + '/' + e - # Grab all the extension files in the given path - ext_files = [f for f in os.listdir(extpath) if f.endswith('.py') - and not f.startswith('__init__')] + # Sections is an optional attribute + sections = getattr(e.plugin(), 'SECTIONS', ()) - # For each module, pick out the target translation class - for f in ext_files: - log.info(f) - ext_name = 'toscaparser/extensions/' + e + '/' + f.strip('.py') - ext_name = ext_name.replace('/', '.') - try: - extinfo = importlib.import_module(ext_name) - version = getattr(extinfo, 'VERSION') - defs_file = extpath + '/' + getattr(extinfo, 'DEFS_FILE') - - # Sections is an optional attribute - sections = getattr(extinfo, 'SECTIONS', ()) - - extensions[version] = {'sections': sections, - 'defs_file': defs_file} - except ImportError: - raise ToscaExtImportError(ext_name=ext_name) - except AttributeError: - attrs = ', '.join(REQUIRED_ATTRIBUTES) - raise ToscaExtAttributeError(ext_name=ext_name, - attrs=attrs) + extensions[version] = {'sections': sections, + 'defs_file': defs_file} + except ImportError: + raise ToscaExtImportError(ext_name=e.name) + except AttributeError: + attrs = ', '.join(REQUIRED_ATTRIBUTES) + raise ToscaExtAttributeError(ext_name=e.name, attrs=attrs) return extensions diff --git a/toscaparser/extensions/mec/tosca_simple_profile_for_mec_1_0_0.py b/toscaparser/extensions/mec/tosca_simple_profile_for_mec_1_0_0.py index 11302ba7..2d4934a2 100644 --- a/toscaparser/extensions/mec/tosca_simple_profile_for_mec_1_0_0.py +++ b/toscaparser/extensions/mec/tosca_simple_profile_for_mec_1_0_0.py @@ -12,8 +12,10 @@ # VERSION and DEFS_FILE are required for all extensions -VERSION = 'tosca_simple_profile_for_mec_1_0_0' -DEFS_FILE = "TOSCA_mec_definition_1_0_0.yaml" +class MecProfile_1_0_0(object): + VERSION = 'tosca_simple_profile_for_mec_1_0_0' -SECTIONS = ('metadata') + DEFS_FILE = "TOSCA_mec_definition_1_0_0.yaml" + + SECTIONS = ('metadata') diff --git a/toscaparser/extensions/nfv/tosca_simple_profile_for_nfv_1_0_0.py b/toscaparser/extensions/nfv/tosca_simple_profile_for_nfv_1_0_0.py index 24eabab2..c1cdabd6 100644 --- a/toscaparser/extensions/nfv/tosca_simple_profile_for_nfv_1_0_0.py +++ b/toscaparser/extensions/nfv/tosca_simple_profile_for_nfv_1_0_0.py @@ -9,11 +9,13 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - +# # VERSION and DEFS_FILE are required for all extensions -VERSION = 'tosca_simple_profile_for_nfv_1_0_0' -DEFS_FILE = "TOSCA_nfv_definition_1_0_0.yaml" +class NfvProfile_1_0_0(object): + VERSION = 'tosca_simple_profile_for_nfv_1_0_0' -SECTIONS = ('metadata') + DEFS_FILE = 'TOSCA_nfv_definition_1_0_0.yaml' + + SECTIONS = ('metadata')