diff --git a/contrib/devstack/lib/murano b/contrib/devstack/lib/murano index 9a48abf2..493b17e2 100644 --- a/contrib/devstack/lib/murano +++ b/contrib/devstack/lib/murano @@ -135,7 +135,7 @@ function init_murano() { $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE db-sync $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/io.murano - $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/io.murano.windows.ActiveDirectory + $MURANO_BIN_DIR/murano-manage --config-file $MURANO_CONF_FILE import-package $MURANO_DIR/meta/io.murano.windows.ActiveDirectory -c "Microsoft Services" } diff --git a/muranoapi/cmd/manage.py b/muranoapi/cmd/manage.py index f44edb14..4d0bd586 100644 --- a/muranoapi/cmd/manage.py +++ b/muranoapi/cmd/manage.py @@ -17,10 +17,12 @@ """ import sys +import traceback from oslo.config import cfg import muranoapi +from muranoapi.common import consts from muranoapi.db.catalog import api as db_catalog_api from muranoapi.db import session as db_session from muranoapi.openstack.common import log as logging @@ -40,20 +42,38 @@ def do_db_sync(): db_session.db_sync() -def _do_import_package(_dir): +def _do_import_package(_dir, categories): LOG.info("Going to import Murano package from {0}".format(_dir)) pkg = application_package.load_from_dir(_dir) - result = db_catalog_api.package_upload(pkg.__dict__, None) + package = { + 'fully_qualified_name': pkg.full_name, + 'type': pkg.package_type, + 'author': pkg.author, + 'name': pkg.display_name, + 'description': pkg.description, + # note: we explicitly mark all the imported packages as public, + # until a parameter added to control visibility scope of a package + 'is_public': True, + #'tags': pkg.tags, + 'logo': pkg.logo, + 'ui_definition': pkg.raw_ui, + 'class_definition': pkg.classes, + 'archive': pkg.blob, + 'categories': categories or [] + } + + # note(ruhe): the second parameter is tenant_id + # it is a required field in the DB, that's why we pass an empty string + result = db_catalog_api.package_upload(package, '') LOG.info("Finished import of package {0}".format(result.id)) # TODO(ruhe): proper error handling def do_import_package(): """ - Import Murano packages from local directories. + Import Murano package from local directory. """ - for _dir in CONF.command.directories: - _do_import_package(_dir) + _do_import_package(CONF.command.directory, CONF.command.categories) def add_command_parsers(subparsers): @@ -64,10 +84,14 @@ def add_command_parsers(subparsers): parser = subparsers.add_parser('import-package') parser.set_defaults(func=do_import_package) - parser.add_argument('directories', - nargs='+', - help="list of directories with Murano packages " - "separated by space") + parser.add_argument('directory', + help='A directory with Murano package.') + + parser.add_argument('-c', '--categories', + choices=consts.CATEGORIES, + nargs='*', + help='An optional list of categories this package ' + 'to be assigned to.') command_opt = cfg.SubCommandOpt('command', @@ -85,13 +109,16 @@ def main(): default_config_files=default_config_files) logging.setup("murano-api") except RuntimeError as e: - LOG.error("murano-manage command failed: %s" % e) + LOG.error("failed to initialize murano-manage: %s" % e) sys.exit("ERROR: %s" % e) try: CONF.command.func() except Exception as e: - sys.exit("ERROR: %s" % e) + tb = traceback.format_exc() + err_msg = "murano-manage command failed: {0}\n{1}".format(e, tb) + LOG.error(err_msg) + sys.exit(err_msg) if __name__ == '__main__': diff --git a/muranoapi/packages/application_package.py b/muranoapi/packages/application_package.py index e5762942..751b2d95 100644 --- a/muranoapi/packages/application_package.py +++ b/muranoapi/packages/application_package.py @@ -21,6 +21,8 @@ import tempfile import yaml import zipfile +from sqlalchemy.util import byte_buffer + import muranoapi.packages.exceptions as e import muranoapi.packages.versions.v1 @@ -60,6 +62,7 @@ class ApplicationPackage(object): self._raw_ui_cache = None self._logo_cache = None self._classes_cache = {} + self._blob_cache = None @property def full_name(self): @@ -107,6 +110,12 @@ class ApplicationPackage(object): self._load_logo(False) return self._logo_cache + @property + def blob(self): + if not self._blob_cache: + self._blob_cache = _pack_dir(self._source_directory) + return self._blob_cache + def get_class(self, name): if name not in self._classes_cache: self._load_class(name) @@ -209,6 +218,23 @@ def load_from_dir(source_directory, filename='manifest.yaml', preload=False, return package +def _zipdir(path, zipf): + for root, dirs, files in os.walk(path): + for f in files: + abspath = os.path.join(root, f) + relpath = os.path.relpath(abspath, path) + zipf.write(abspath, relpath) + + +def _pack_dir(source_directory): + blob = byte_buffer() + zipf = zipfile.ZipFile(blob, mode='w') + _zipdir(source_directory, zipf) + zipf.close() + + return blob.getvalue() + + def load_from_file(archive_path, target_dir=None, drop_dir=False, loader=DummyLoader): if not os.path.isfile(archive_path):