From 9b662b492ed0b90639683ed499ba12fd4abc26f7 Mon Sep 17 00:00:00 2001 From: Alexander Tivelkov Date: Wed, 26 Mar 2014 14:48:57 +0400 Subject: [PATCH] Added initial version of murano application package parser Partially implements blueprint murano-repository-api-v2 Change-Id: I29b347225b40cf935a89ba6bbe743bb48fb98f78 --- meta/ad.murr | Bin 0 -> 17280 bytes muranoapi/packages/__init__.py | 0 muranoapi/packages/application_package.py | 218 ++++++++++++++++++++++ muranoapi/packages/exceptions.py | 44 +++++ muranoapi/packages/versions/__init__.py | 0 muranoapi/packages/versions/v1.py | 50 +++++ 6 files changed, 312 insertions(+) create mode 100644 meta/ad.murr create mode 100644 muranoapi/packages/__init__.py create mode 100644 muranoapi/packages/application_package.py create mode 100644 muranoapi/packages/exceptions.py create mode 100644 muranoapi/packages/versions/__init__.py create mode 100644 muranoapi/packages/versions/v1.py diff --git a/meta/ad.murr b/meta/ad.murr new file mode 100644 index 0000000000000000000000000000000000000000..7288753c490205d6fba8f989905040b07d954eb9 GIT binary patch literal 17280 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 literal 0 HcmV?d00001 diff --git a/muranoapi/packages/__init__.py b/muranoapi/packages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/muranoapi/packages/application_package.py b/muranoapi/packages/application_package.py new file mode 100644 index 00000000..f2ce95a1 --- /dev/null +++ b/muranoapi/packages/application_package.py @@ -0,0 +1,218 @@ +# Copyright (c) 2014 Mirantis Inc. +# +# 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. +import imghdr +import os +import shutil +import sys +import tarfile +import tempfile +import yaml + +import muranoapi.packages.exceptions as e +import muranoapi.packages.versions.v1 + + +class PackageTypes(object): + Library = 'Library' + Application = 'Application' + ALL = [Library, Application] + + +class ApplicationPackage(object): + def __init__(self, source_directory, yaml_content): + self._source_directory = source_directory + self._full_name = None + self._package_type = None + self._display_name = None + self._description = None + self._author = None + self._tags = None + self._classes = None + self._ui = None + self._logo = None + self._format = yaml_content.get('Format') + self._ui_cache = None + self._raw_ui_cache = None + self._logo_cache = None + self._classes_cache = {} + + @property + def full_name(self): + return self._full_name + + @property + def package_type(self): + return self._package_type + + @property + def display_name(self): + return self._display_name + + @property + def description(self): + return self._description + + @property + def author(self): + return self._author + + @property + def tags(self): + return tuple(self._tags) + + @property + def classes(self): + return tuple(self._classes.keys()) + + @property + def ui(self): + if not self._ui_cache: + self._load_ui(True) + return self._ui_cache + + @property + def raw_ui(self): + if not self._raw_ui_cache: + self._load_ui(False) + return self._raw_ui_cache + + @property + def logo(self): + if not self._logo_cache: + self._load_logo(False) + return self._logo_cache + + def get_class(self, name): + if name not in self._classes_cache: + self._load_class(name) + return self._classes_cache[name] + + def validate(self): + self._classes_cache.clear() + for class_name in self._classes: + self.get_class(class_name) + self._load_ui(True) + self._load_logo(True) + + # Private methods + def _load_ui(self, load_yaml=False): + if self._raw_ui_cache and load_yaml: + self._ui_cache = yaml.load(self._raw_ui_cache) + else: + ui_file = self._ui + full_path = os.path.join(self._source_directory, 'UI', ui_file) + if not os.path.isfile(full_path): + self._raw_ui_cache = None + self._ui_cache = None + return + try: + with open(full_path) as stream: + self._raw_ui_cache = stream.read() + if load_yaml: + self._ui_cache = yaml.load(self._raw_ui_cache) + except Exception as ex: + trace = sys.exc_info()[2] + raise e.PackageUILoadError(str(ex)), None, trace + + def _load_logo(self, validate=False): + logo_file = self._logo or 'logo.png' + full_path = os.path.join(self._source_directory, logo_file) + if not os.path.isfile(full_path) and logo_file == 'logo.png': + self._logo_cache = None + return + try: + if validate: + if imghdr.what(full_path) != 'png': + raise e.PackageLoadError("Logo is not in PNG format") + with open(full_path) as stream: + self._logo_cache = stream.read() + except Exception as ex: + trace = sys.exc_info()[2] + raise e.PackageLoadError( + "Unable to load logo: " + str(ex)), None, trace + + def _load_class(self, name): + if name not in self._classes: + raise e.PackageClassLoadError(name, 'Class not defined ' + 'in this package') + def_file = self._classes[name] + full_path = os.path.join(self._source_directory, 'Classes', def_file) + if not os.path.isfile(full_path): + raise e.PackageClassLoadError(name, 'File with class ' + 'definition not found') + try: + with open(full_path) as stream: + self._classes_cache[name] = yaml.load(stream) + except Exception as ex: + trace = sys.exc_info()[2] + msg = 'Unable to load class definition due to "{0}"'.format( + str(ex)) + raise e.PackageClassLoadError(name, msg), None, trace + + +def load_from_dir(source_directory, filename='manifest.yaml', preload=False): + formats = {'1.0': muranoapi.packages.versions.v1} + + if not os.path.isdir(source_directory) or not os.path.exists( + source_directory): + raise e.PackageLoadError('Invalid package directory') + full_path = os.path.join(source_directory, filename) + if not os.path.isfile(full_path): + raise e.PackageLoadError('Unable to find package manifest') + + try: + with open(full_path) as stream: + content = yaml.load(stream) + except Exception as ex: + trace = sys.exc_info()[2] + raise e.PackageLoadError( + "Unable to load due to '{0}'".format(str(ex))), None, trace + if content: + p_format = str(content.get('Format')) + if not p_format or p_format not in formats: + raise e.PackageFormatError( + 'Unknown or missing format version') + package = ApplicationPackage(source_directory, content) + formats[p_format].load(package, content) + if preload: + package.validate() + return package + + +def load_from_file(archive_path, target_dir=None, drop_dir=False): + if not os.path.isfile(archive_path): + raise e.PackageLoadError('Unable to find package file') + created = False + if not target_dir: + target_dir = tempfile.mkdtemp() + created = True + elif not os.path.exists(target_dir): + os.mkdir(target_dir) + created = True + else: + if os.listdir(target_dir): + raise e.PackageLoadError('Target directory is not empty') + + try: + package = tarfile.open(archive_path) + package.extractall(path=target_dir) + return load_from_dir(target_dir, preload=True) + finally: + if drop_dir: + if created: + shutil.rmtree(target_dir) + else: + for f in os.listdir(target_dir): + os.unlink(os.path.join(target_dir, f)) diff --git a/muranoapi/packages/exceptions.py b/muranoapi/packages/exceptions.py new file mode 100644 index 00000000..4e2c47f7 --- /dev/null +++ b/muranoapi/packages/exceptions.py @@ -0,0 +1,44 @@ +# Copyright (c) 2014 Mirantis Inc. +# +# 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. + +import muranoapi.openstack.common.exception as e + + +class PackageClassLoadError(e.Error): + def __init__(self, class_name, message=None): + msg = 'Unable to load class "{0}" from package'.format(class_name) + if message: + msg += ": " + message + super(PackageClassLoadError, self).__init__(msg) + + +class PackageUILoadError(e.Error): + def __init__(self, message=None): + msg = 'Unable to load ui definition from package' + if message: + msg += ": " + message + super(PackageUILoadError, self).__init__(msg) + + +class PackageLoadError(e.Error): + pass + + +class PackageFormatError(PackageLoadError): + def __init__(self, message=None): + msg = 'Incorrect package format' + if message: + msg += ': ' + message + super(PackageFormatError, self).__init__(msg) diff --git a/muranoapi/packages/versions/__init__.py b/muranoapi/packages/versions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/muranoapi/packages/versions/v1.py b/muranoapi/packages/versions/v1.py new file mode 100644 index 00000000..ce64a9dd --- /dev/null +++ b/muranoapi/packages/versions/v1.py @@ -0,0 +1,50 @@ +# Copyright (c) 2014 Mirantis Inc. +# +# 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. + +import re + +import muranoapi.packages.application_package +import muranoapi.packages.exceptions as e + + +# noinspection PyProtectedMember +def load(package, yaml_content): + package._full_name = yaml_content.get('FullName') + if not package._full_name: + raise muranoapi.packages.exceptions.PackageFormatError( + 'FullName not specified') + _check_full_name(package._full_name) + package._package_type = yaml_content.get('Type') + if not package._package_type or package._package_type not in \ + muranoapi.packages.application_package.PackageTypes.ALL: + raise e.PackageFormatError('Invalid Package Type') + package._display_name = yaml_content.get('Name', package._full_name) + package._description = yaml_content.get('Description') + package._author = yaml_content.get('Author') + package._classes = yaml_content.get('Classes') + package._ui = yaml_content.get('UI', 'ui.yaml') + package._logo = yaml_content.get('Logo') + + +def _check_full_name(full_name): + error = muranoapi.packages.exceptions.PackageFormatError( + 'Invalid FullName') + if re.match(r'^[\w\.]+$', full_name): + if full_name.startswith('.') or full_name.endswith('.'): + raise error + if '..' in full_name: + raise error + else: + raise error