shellfoundry 1.2.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. shellfoundry/__init__.py +8 -0
  2. shellfoundry/__main__.py +7 -0
  3. shellfoundry/bootstrap.py +169 -0
  4. shellfoundry/commands/__init__.py +0 -0
  5. shellfoundry/commands/config_command.py +87 -0
  6. shellfoundry/commands/delete_command.py +24 -0
  7. shellfoundry/commands/dist_command.py +34 -0
  8. shellfoundry/commands/extend_command.py +182 -0
  9. shellfoundry/commands/generate_command.py +44 -0
  10. shellfoundry/commands/get_templates_command.py +151 -0
  11. shellfoundry/commands/install_command.py +81 -0
  12. shellfoundry/commands/list_command.py +110 -0
  13. shellfoundry/commands/new_command.py +393 -0
  14. shellfoundry/commands/pack_command.py +45 -0
  15. shellfoundry/commands/show_command.py +55 -0
  16. shellfoundry/data/standards.json +44 -0
  17. shellfoundry/data/templates.yml +117 -0
  18. shellfoundry/decorators/__init__.py +1 -0
  19. shellfoundry/decorators/standards.py +20 -0
  20. shellfoundry/decorators/version_check.py +52 -0
  21. shellfoundry/exceptions.py +52 -0
  22. shellfoundry/models/__init__.py +0 -0
  23. shellfoundry/models/install_config.py +82 -0
  24. shellfoundry/models/shell_template.py +22 -0
  25. shellfoundry/models/shellfoundry_settings.py +13 -0
  26. shellfoundry/utilities/__init__.py +114 -0
  27. shellfoundry/utilities/archive_creator.py +35 -0
  28. shellfoundry/utilities/cloudshell_api/__init__.py +1 -0
  29. shellfoundry/utilities/cloudshell_api/client_wrapper.py +71 -0
  30. shellfoundry/utilities/config/__init__.py +0 -0
  31. shellfoundry/utilities/config/config_context.py +44 -0
  32. shellfoundry/utilities/config/config_file_creation.py +32 -0
  33. shellfoundry/utilities/config/config_providers.py +50 -0
  34. shellfoundry/utilities/config/config_record.py +24 -0
  35. shellfoundry/utilities/config_reader.py +154 -0
  36. shellfoundry/utilities/constants.py +37 -0
  37. shellfoundry/utilities/cookiecutter_integration.py +67 -0
  38. shellfoundry/utilities/driver_generator.py +121 -0
  39. shellfoundry/utilities/filters.py +45 -0
  40. shellfoundry/utilities/installer.py +44 -0
  41. shellfoundry/utilities/modifiers/__init__.py +0 -0
  42. shellfoundry/utilities/modifiers/configuration/__init__.py +0 -0
  43. shellfoundry/utilities/modifiers/configuration/aggregated_modifiers.py +26 -0
  44. shellfoundry/utilities/modifiers/configuration/password_modification.py +48 -0
  45. shellfoundry/utilities/modifiers/definition/__init__.py +0 -0
  46. shellfoundry/utilities/modifiers/definition/definition_modification.py +205 -0
  47. shellfoundry/utilities/package_builder.py +152 -0
  48. shellfoundry/utilities/python_dependencies_packager.py +53 -0
  49. shellfoundry/utilities/repository_downloader.py +76 -0
  50. shellfoundry/utilities/shell_config_reader.py +59 -0
  51. shellfoundry/utilities/shell_datamodel_merger.py +44 -0
  52. shellfoundry/utilities/shell_package.py +59 -0
  53. shellfoundry/utilities/shell_package_builder.py +149 -0
  54. shellfoundry/utilities/shell_package_installer.py +231 -0
  55. shellfoundry/utilities/standards/__init__.py +3 -0
  56. shellfoundry/utilities/standards/consts.py +2 -0
  57. shellfoundry/utilities/standards/standards_retriever.py +32 -0
  58. shellfoundry/utilities/standards/standards_versions.py +34 -0
  59. shellfoundry/utilities/temp_dir_context.py +17 -0
  60. shellfoundry/utilities/template_retriever.py +309 -0
  61. shellfoundry/utilities/template_url.py +38 -0
  62. shellfoundry/utilities/template_versions.py +47 -0
  63. shellfoundry/utilities/validations/__init__.py +2 -0
  64. shellfoundry/utilities/validations/shell_generation_validation.py +29 -0
  65. shellfoundry/utilities/validations/shell_name_validations.py +10 -0
  66. shellfoundry/utilities/version_utilities.py +19 -0
  67. shellfoundry-1.2.25.dist-info/LICENSE +201 -0
  68. shellfoundry-1.2.25.dist-info/METADATA +417 -0
  69. shellfoundry-1.2.25.dist-info/RECORD +72 -0
  70. shellfoundry-1.2.25.dist-info/WHEEL +5 -0
  71. shellfoundry-1.2.25.dist-info/entry_points.txt +2 -0
  72. shellfoundry-1.2.25.dist-info/top_level.txt +1 -0
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+ import re
6
+ from io import open
7
+
8
+ import ruamel.yaml as yaml
9
+
10
+ from shellfoundry.exceptions import YmlFieldMissingException
11
+ from shellfoundry.utilities.constants import (
12
+ TEMPLATE_PROPERTY,
13
+ TEMPLATE_VERSION,
14
+ TOSCA_META_LOCATION,
15
+ )
16
+
17
+
18
+ class DefinitionModification(object):
19
+ def __init__(self, shell_path):
20
+ self.shell_path = shell_path
21
+ self.entry_definition = os.path.join(
22
+ self.shell_path, self._find_entry_definition()
23
+ )
24
+
25
+ def edit_definition(self, field, value):
26
+ """Modify shell-definition.yaml.
27
+
28
+ :params field str: field name to modify
29
+ :params value str: new value to update
30
+ """
31
+ self._edit_yaml(self.entry_definition, field, value)
32
+
33
+ def edit_tosca_meta(self, field, value):
34
+ with open(
35
+ os.path.join(self.shell_path, TOSCA_META_LOCATION), "r", encoding="utf8"
36
+ ) as tosca_file:
37
+ is_changed = False
38
+ tosca_data = []
39
+ for line in tosca_file:
40
+ if field in line:
41
+ line = re.sub(r":\s+.*", ": {}".format(value), line)
42
+ is_changed = True
43
+ tosca_data.append(line)
44
+
45
+ if not is_changed:
46
+ tosca_data.append("\n{field}: {value}".format(field=field, value=value))
47
+
48
+ with open(
49
+ os.path.join(self.shell_path, TOSCA_META_LOCATION), "w", encoding="utf8"
50
+ ) as tosca_file:
51
+ tosca_file.writelines(tosca_data)
52
+
53
+ def add_field_to_definition(self, field, value=None, overwrite=False):
54
+ """Add new field to shell-definition.yaml.
55
+
56
+ :params field str: field name to add
57
+ :params value str: value to add
58
+ :params overwrite bool: overwrite value if it already exists
59
+ """
60
+ try:
61
+ if overwrite:
62
+ self.edit_definition(field, value)
63
+ except YmlFieldMissingException:
64
+ value = value or self._get_value_from_definition(TEMPLATE_VERSION)
65
+ yaml_parser = yaml.YAML()
66
+ loaded = self._load_yaml(
67
+ yaml_parser=yaml_parser, yaml_file=self.entry_definition
68
+ )
69
+
70
+ section, field_name = field.split("/", 1)
71
+ loaded[section].update({field_name: value})
72
+ self._edit_file(
73
+ yaml_file=self.entry_definition, yaml_parser=yaml_parser, data=loaded
74
+ )
75
+
76
+ def add_properties(self, attribute_names):
77
+ """Add property to shell-definition.yaml file.
78
+
79
+ :params fields tuple/list: sequence of properties name that will be added
80
+ """
81
+ results = list(map(self._add_property, attribute_names))
82
+
83
+ for item in zip(attribute_names, results):
84
+ self._comment_attribute(*item)
85
+
86
+ def get_artifacts_files(self, artifact_name_list):
87
+ yaml_parser = yaml.YAML()
88
+ shell_definition = self._load_yaml(yaml_parser, self.entry_definition)
89
+
90
+ for node_type in list(shell_definition["node_types"].values()):
91
+ if "artifacts" not in node_type:
92
+ continue
93
+
94
+ result = {}
95
+ for artifact_name, artifact in node_type["artifacts"].items():
96
+ if artifact_name in artifact_name_list:
97
+ result.update({artifact_name: artifact["file"]})
98
+
99
+ return result
100
+
101
+ def _find_entry_definition(self):
102
+ with open(
103
+ os.path.join(self.shell_path, TOSCA_META_LOCATION), "r"
104
+ ) as tosca_file:
105
+ entry_definition = dict(
106
+ list(map(str.strip, str(line).split(":", 1))) for line in tosca_file
107
+ )["Entry-Definitions"]
108
+
109
+ return entry_definition
110
+
111
+ def _load_yaml(self, yaml_parser, yaml_file):
112
+ with open(yaml_file, encoding="utf8") as stream:
113
+ try:
114
+ yaml_parser.indent(offset=2)
115
+ return yaml_parser.load(stream=stream)
116
+ except yaml.YAMLError as exc:
117
+ print(exc) # noqa: T001
118
+
119
+ def _edit_yaml(self, yaml_file, field, value):
120
+ yaml_parser = yaml.YAML()
121
+ loaded = self._load_yaml(yaml_parser=yaml_parser, yaml_file=yaml_file)
122
+
123
+ field_name = field.split("/")[-1]
124
+ self._get_inner_dict_recursively(loaded, field)[field_name] = value
125
+
126
+ self._edit_file(yaml_file=yaml_file, yaml_parser=yaml_parser, data=loaded)
127
+
128
+ def _edit_file(self, yaml_file, yaml_parser, data):
129
+ with open(yaml_file, "wb") as f:
130
+ yaml_parser.dump(data, stream=f)
131
+
132
+ def _get_inner_dict_recursively(self, dic, field):
133
+ split = field.split("/", 1)
134
+ i = dic.get(split[0])
135
+ if not i:
136
+ raise YmlFieldMissingException("Field does not exists")
137
+ if not isinstance(i, dict) and len(split) == 1:
138
+ return dic
139
+
140
+ return self._get_inner_dict_recursively(i, split[1])
141
+
142
+ def _get_value_from_definition(self, field):
143
+ yaml_parser = yaml.YAML()
144
+ loaded = self._load_yaml(yaml_parser, self.entry_definition)
145
+
146
+ field_name = field.split("/")[-1]
147
+ value = self._get_inner_dict_recursively(loaded, field)[field_name]
148
+ return value
149
+
150
+ def _add_property(self, attribute_name):
151
+ """Add property to shell-definition.yaml file.
152
+
153
+ :params fields list: list of properties name that will be added
154
+ """
155
+ yaml_parser = yaml.YAML()
156
+ loaded = self._load_yaml(yaml_parser, self.entry_definition)
157
+
158
+ nodes = loaded.get("node_types")
159
+
160
+ is_last = False
161
+ if nodes:
162
+ for key, value in nodes.items():
163
+ if key.startswith("vendor."):
164
+ properties_data = value.get("properties", {})
165
+ if properties_data:
166
+ properties_data.update({attribute_name: TEMPLATE_PROPERTY})
167
+ is_last = False
168
+ else:
169
+ value.insert(
170
+ 1, "properties", {attribute_name: TEMPLATE_PROPERTY}
171
+ )
172
+ is_last = True
173
+ break
174
+
175
+ self._edit_file(
176
+ yaml_file=self.entry_definition, yaml_parser=yaml_parser, data=loaded
177
+ )
178
+
179
+ return is_last
180
+
181
+ def _comment_attribute(self, attribute_name, is_last=False):
182
+ """Comment attribute in shell-definishion.yaml file."""
183
+ spaces = None
184
+ need_comment = False
185
+ lines = []
186
+ with open(self.entry_definition, "r", encoding="utf8") as f:
187
+ for line in f:
188
+ stripped = line.lstrip(" ")
189
+ if stripped.startswith("{}:".format(attribute_name)):
190
+ if is_last:
191
+ lines[-1] = "# {}".format(lines[-1])
192
+ spaces = len(line) - len(stripped)
193
+ need_comment = True
194
+ lines.append("# {}".format(line))
195
+ continue
196
+
197
+ if need_comment and spaces and (len(line) - len(stripped)) > spaces:
198
+ lines.append("# {}".format(line))
199
+ continue
200
+
201
+ need_comment = False
202
+ lines.append(line)
203
+
204
+ with open(self.entry_definition, "w", encoding="utf8") as f:
205
+ f.writelines(lines)
@@ -0,0 +1,152 @@
1
+ import codecs
2
+ import mimetypes
3
+ import os
4
+ import shutil
5
+ import xml.etree.ElementTree as etree
6
+
7
+ import click
8
+
9
+ from shellfoundry.utilities.archive_creator import ArchiveCreator
10
+ from shellfoundry.utilities.shell_datamodel_merger import ShellDataModelMerger
11
+ from shellfoundry.utilities.version_utilities import DriverVersionTimestampBased
12
+
13
+
14
+ class PackageBuilder(object):
15
+ def __init__(self, driver_version_strategy=None):
16
+ self.driver_version_strategy = (
17
+ driver_version_strategy or DriverVersionTimestampBased()
18
+ )
19
+
20
+ def build_package(self, path, package_name, driver_name):
21
+ package_path = os.path.join(path, "package")
22
+ self._copy_metadata(package_path, path)
23
+ self._copy_datamodel(package_path, path)
24
+ self._copy_categories(package_path, path)
25
+ self._copy_images(package_path, path)
26
+ self._copy_shellconfig(package_path, path)
27
+ self._create_driver(package_path, path, driver_name)
28
+ zip_path = self._zip_package(package_path, path, package_name)
29
+ shutil.rmtree(path=package_path, ignore_errors=True)
30
+ click.echo("Shell package was successfully created:")
31
+ click.echo(zip_path)
32
+
33
+ def _copy_metadata(self, package_path, path):
34
+ src_file_path = os.path.join(path, "datamodel", "metadata.xml")
35
+ PackageBuilder._copy_file(package_path, src_file_path)
36
+
37
+ @staticmethod
38
+ def _get_file_content_as_string(path):
39
+ with codecs.open(path, "r", encoding="utf8") as f:
40
+ text = f.read()
41
+ return text
42
+
43
+ @staticmethod
44
+ def _save_to_utf_file(content, dest_path):
45
+ with codecs.open(dest_path, "w", "utf-8-sig") as f:
46
+ if isinstance(content, bytes):
47
+ content = content.decode()
48
+ f.write(content)
49
+
50
+ @staticmethod
51
+ def _save_to_file(content, dest_path):
52
+ with codecs.open(dest_path, "w") as f:
53
+ if isinstance(content, bytes):
54
+ content = content.decode()
55
+ f.write(content)
56
+
57
+ @staticmethod
58
+ def _copy_datamodel(package_path, path):
59
+ shell_model_path = os.path.join(path, "datamodel", "shell_model.xml")
60
+ src_dm_file_path = os.path.join(path, "datamodel", "datamodel.xml")
61
+ dest_dir_path = os.path.join(package_path, "DataModel")
62
+
63
+ if os.path.exists(shell_model_path):
64
+ shell_model = PackageBuilder._get_file_content_as_string(shell_model_path)
65
+ dm = PackageBuilder._get_file_content_as_string(src_dm_file_path)
66
+ merger = ShellDataModelMerger()
67
+ merged_dm = merger.merge_shell_model(dm, shell_model)
68
+ if not os.path.exists(dest_dir_path):
69
+ os.makedirs(dest_dir_path)
70
+ PackageBuilder._save_to_utf_file(
71
+ merged_dm, os.path.join(dest_dir_path, "datamodel.xml")
72
+ )
73
+
74
+ else:
75
+ PackageBuilder._copy_file(dest_dir_path, src_dm_file_path)
76
+
77
+ @staticmethod
78
+ def _is_image(file):
79
+ file_type, encoding = mimetypes.guess_type(file)
80
+ return file_type and "image" in file_type
81
+
82
+ @staticmethod
83
+ def _copy_images(package_path, path):
84
+ dest_dir_path = os.path.join(package_path, "DataModel")
85
+ datamodel_dir = os.path.join(path, "datamodel")
86
+ for root, _, files in os.walk(datamodel_dir):
87
+ images = [
88
+ dir_file for dir_file in files if PackageBuilder._is_image(dir_file)
89
+ ]
90
+ for image in images:
91
+ PackageBuilder._copy_file(dest_dir_path, os.path.join(root, image))
92
+
93
+ @staticmethod
94
+ def _copy_file(dest_dir_path, src_file_path):
95
+ if not os.path.exists(dest_dir_path):
96
+ os.makedirs(dest_dir_path)
97
+ shutil.copy(src_file_path, dest_dir_path)
98
+
99
+ @staticmethod
100
+ def _copy_shellconfig(package_path, path):
101
+ src_file_path = os.path.join(path, "datamodel", "shellconfig.xml")
102
+ if os.path.exists(src_file_path):
103
+ dest_dir_path = os.path.join(package_path, "Configuration")
104
+ PackageBuilder._copy_file(dest_dir_path, src_file_path)
105
+
106
+ @staticmethod
107
+ def _copy_categories(package_path, path):
108
+ src_file_path = os.path.join(path, "categories", "categories.xml")
109
+ if os.path.exists(src_file_path):
110
+ dest_dir_path = os.path.join(package_path, "Categories")
111
+ PackageBuilder._copy_file(dest_dir_path, src_file_path)
112
+
113
+ def _create_driver(self, package_path, path, driver_name):
114
+ dir_to_zip = os.path.join(path, "src")
115
+ drivermetadata_path = os.path.join(dir_to_zip, "drivermetadata.xml")
116
+ version = self._update_driver_version(drivermetadata_path)
117
+ zip_file_path = os.path.join(
118
+ package_path, "Resource Drivers - Python", driver_name
119
+ )
120
+ ArchiveCreator.make_archive(zip_file_path, "zip", dir_to_zip)
121
+ if version: # version was replaced
122
+ self._update_driver_version(drivermetadata_path, version)
123
+
124
+ @staticmethod
125
+ def _parse_xml(xml_string):
126
+ parser = etree.XMLParser(encoding="utf-8")
127
+ return etree.fromstring(xml_string, parser)
128
+
129
+ def _update_driver_version(self, metadata_path, version=""):
130
+ if not os.path.isfile(metadata_path):
131
+ return None
132
+
133
+ metadata = self._get_file_content_as_string(metadata_path)
134
+ metadata_xml = self._parse_xml(metadata)
135
+ curver = metadata_xml.get("Version")
136
+
137
+ if version:
138
+ metadata_xml.set("Version", version)
139
+ self._save_to_file(etree.tostring(metadata_xml), metadata_path)
140
+ return None
141
+ elif self.driver_version_strategy.supports_version_pattern(curver):
142
+ newver = self.driver_version_strategy.get_version(curver)
143
+ metadata_xml.set("Version", newver)
144
+ self._save_to_file(etree.tostring(metadata_xml), metadata_path)
145
+ return curver
146
+ else:
147
+ return None
148
+
149
+ @staticmethod
150
+ def _zip_package(package_path, path, package_name):
151
+ zip_file_path = os.path.join(path, "dist", package_name)
152
+ return ArchiveCreator.make_archive(zip_file_path, "zip", package_path)
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+ import shutil
6
+
7
+ try:
8
+ from pip import main as pip_main
9
+ except Exception:
10
+ from pip._internal import main as pip_main
11
+
12
+
13
+ class PythonDependenciesPackager(object):
14
+ CS_PYPI_PORT = 8036
15
+
16
+ def __init__(self):
17
+ pass
18
+
19
+ def save_offline_dependencies(
20
+ self, requirements_path, dest_path, cs_server_address=None
21
+ ):
22
+
23
+ if os.path.isdir(dest_path):
24
+ shutil.rmtree(path=dest_path, ignore_errors=True)
25
+
26
+ if not os.path.exists(requirements_path):
27
+ return
28
+
29
+ proxy = os.environ.get("http_proxy")
30
+ pip_args = ["download"]
31
+ if proxy:
32
+ pip_args.append("--proxy")
33
+ pip_args.append(proxy)
34
+
35
+ if cs_server_address:
36
+ pip_args.append(
37
+ "--trusted-host={cs_server_address}".format(
38
+ cs_server_address=cs_server_address
39
+ )
40
+ )
41
+ pip_args.append(
42
+ "--extra-index-url=http://{cs_server_address}:{cs_pypi_port}".format(
43
+ cs_server_address=cs_server_address, cs_pypi_port=self.CS_PYPI_PORT
44
+ )
45
+ )
46
+
47
+ pip_args.append(
48
+ "--requirement={requirements_path}".format(
49
+ requirements_path=requirements_path
50
+ )
51
+ )
52
+ pip_args.append("--dest={dest_path}".format(dest_path=dest_path))
53
+ pip_main(pip_args)
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+ import zipfile
6
+ from abc import ABCMeta, abstractmethod
7
+ from io import open
8
+
9
+ import requests
10
+
11
+ from .template_url import construct_template_url
12
+
13
+ from shellfoundry.exceptions import VersionRequestException
14
+
15
+
16
+ class DownloadedRepoExtractor:
17
+ def __init__(self):
18
+ pass
19
+
20
+ __metaclass__ = ABCMeta
21
+
22
+ @abstractmethod
23
+ def extract_to_folder(self, repo_link, folder):
24
+ pass
25
+
26
+
27
+ class ZipDownloadedRepoExtractor(DownloadedRepoExtractor):
28
+ def extract_to_folder(self, repo_link, folder):
29
+ super(ZipDownloadedRepoExtractor, self).extract_to_folder(repo_link, folder)
30
+ with zipfile.ZipFile(repo_link, "r") as z:
31
+ infos = z.infolist()
32
+ z.extractall(folder)
33
+ return [info.filename for info in infos]
34
+
35
+
36
+ class RepositoryDownloader(object):
37
+ def __init__(self, repo_extractor=ZipDownloadedRepoExtractor()):
38
+ self.repo_extractor = repo_extractor
39
+
40
+ def download_template(
41
+ self, target_dir, repo_address, branch, is_need_construct=True
42
+ ):
43
+ if is_need_construct:
44
+ download_url = construct_template_url(repo_address, branch)
45
+ else:
46
+ download_url = repo_address
47
+ archive_path = ""
48
+ try:
49
+ archive_path = self.download_file(download_url, target_dir)
50
+
51
+ repo_content = self.repo_extractor.extract_to_folder(
52
+ archive_path, target_dir
53
+ )
54
+
55
+ # The first entry is always the root folder by git zipball convention
56
+ root_dir = repo_content[0]
57
+
58
+ return os.path.join(target_dir, root_dir)
59
+ finally:
60
+ if os.path.exists(archive_path):
61
+ os.remove(archive_path)
62
+
63
+ def download_file(self, url, directory):
64
+ local_filename = os.path.join(directory, url.split("/")[-1])
65
+ # NOTE the stream=True parameter
66
+ r = requests.get(url, stream=True)
67
+ if r.status_code != requests.codes.ok:
68
+ raise VersionRequestException(
69
+ "Failed to download zip file from {}".format(url)
70
+ )
71
+ with open(local_filename, "wb") as f:
72
+ for chunk in r.iter_content(chunk_size=1024):
73
+ if chunk: # filter out keep-alive new chunks
74
+ f.write(chunk)
75
+ # f.flush() commented by recommendation from J.F.Sebastian
76
+ return local_filename
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import os
4
+ from io import open
5
+
6
+ import yaml
7
+
8
+ from shellfoundry.exceptions import ShellYmlMissingException, WrongShellYmlException
9
+
10
+ VERSION = "version"
11
+ DESCRIPTION = "description"
12
+ EMAIL = "email"
13
+ AUTHOR = "author"
14
+ NAME = "name"
15
+ SHELL = "shell"
16
+ DRIVER_NAME = "driver_name"
17
+
18
+
19
+ class ProjectConfig(object):
20
+ def __init__(self, name, author, email, description, version, driver_name):
21
+ self.version = version
22
+ self.description = description
23
+ self.email = email
24
+ self.author = author
25
+ self.name = name
26
+ self.driver_name = driver_name
27
+
28
+
29
+ class ShellConfigReader(object):
30
+ def read(self):
31
+ config_path = os.path.join(os.getcwd(), "shell.yml")
32
+
33
+ if not os.path.isfile(config_path):
34
+ raise ShellYmlMissingException("shell.yml is missing")
35
+
36
+ with open(config_path, encoding="utf8") as stream:
37
+ config = yaml.safe_load(stream.read())
38
+
39
+ if not config or SHELL not in config:
40
+ raise WrongShellYmlException("shell section is missing in shell.yml")
41
+
42
+ install_config = config[SHELL]
43
+
44
+ name = self._get_with_default(install_config, NAME, "")
45
+ author = self._get_with_default(install_config, AUTHOR, "")
46
+ email = self._get_with_default(install_config, EMAIL, "")
47
+ description = self._get_with_default(install_config, DESCRIPTION, "")
48
+ version = self._get_with_default(install_config, VERSION, "")
49
+ driver_name = self._get_with_default(install_config, DRIVER_NAME, "")
50
+
51
+ return ProjectConfig(name, author, email, description, version, driver_name)
52
+
53
+ @staticmethod
54
+ def _get_with_default(install_config, parameter_name, default_value):
55
+ return (
56
+ install_config[parameter_name]
57
+ if install_config and parameter_name in install_config
58
+ else default_value
59
+ )
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import xml.etree.ElementTree as etree
4
+
5
+
6
+ class ShellDataModelMerger:
7
+ def _parse_xml(self, xml_string):
8
+ parser = etree.XMLParser(encoding="utf-8")
9
+ return etree.fromstring(xml_string, parser)
10
+
11
+ def merge_shell_model(self, datamodel, shell_model):
12
+ etree.register_namespace(
13
+ "",
14
+ "http://schemas.qualisystems.com/ResourceManagement/DataModelSchema.xsd", # noqa: E501
15
+ )
16
+ datamodel_tree = self._parse_xml(datamodel)
17
+ shell_tree = self._parse_xml(shell_model)
18
+
19
+ shell_family_element = shell_tree.find(".//ShellModel")
20
+ if shell_family_element is None:
21
+ raise Exception("Missing ShellModel element in shell_model.xml file")
22
+
23
+ family_name = shell_family_element.get("Family")
24
+
25
+ family_xpath_expression = ".//{{http://schemas.qualisystems.com/ResourceManagement/DataModelSchema.xsd}}ResourceFamily[@Name='{family_name}']".format( # noqa: E501
26
+ family_name=family_name
27
+ )
28
+ dm_family_element = datamodel_tree.find(family_xpath_expression)
29
+
30
+ if dm_family_element is None:
31
+ raise Exception("Shell family not found:" + family_name)
32
+ model_insertion_point = dm_family_element.find(
33
+ ".//{http://schemas.qualisystems.com/ResourceManagement/DataModelSchema.xsd}Models" # noqa: E501
34
+ )
35
+ models = shell_tree.find(".//ShellModel")
36
+ model_insertion_point.extend(models)
37
+
38
+ attributes = shell_tree.find(".//ShellAttributes")
39
+ attributes_insertion_point = datamodel_tree.find(
40
+ ".//{http://schemas.qualisystems.com/ResourceManagement/DataModelSchema.xsd}Attributes" # noqa: E501
41
+ )
42
+ attributes_insertion_point.extend(attributes)
43
+
44
+ return etree.tostring(datamodel_tree)
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import os
4
+ from io import open
5
+
6
+ import yaml
7
+
8
+ LAYER_ONE_PREFIX = "CloudshellL1"
9
+
10
+
11
+ class ShellPackage(object):
12
+ def __init__(self, path):
13
+ self.path = path
14
+ self.real_shell_name = None
15
+
16
+ def get_shell_name(self):
17
+ """Returns shell name."""
18
+ head, shell_name = os.path.split(self.path)
19
+ return shell_name.title().replace("-", "").replace("_", "")
20
+
21
+ def get_name_from_definition(self, should_reload=False):
22
+ """Get shell name from shell-definition.yaml.
23
+
24
+ :param bool should_reload: Should reload from
25
+ :return: template name section from shell-definition.yml or equivalent written in entry definition in tosca.meta # noqa: E501
26
+ :rtype: str
27
+ """
28
+ # reload the shell name if persisted member is empty or explicit request for reload # noqa: E501
29
+ if not self.real_shell_name or should_reload:
30
+ self._reload_name()
31
+ return self.real_shell_name
32
+
33
+ def is_layer_one(self):
34
+ """Determines whether a shell is Layer 1."""
35
+ return bool(LAYER_ONE_PREFIX in self.get_shell_name())
36
+
37
+ def is_tosca(self):
38
+ """Determines whether a shell is a TOSCA based shell."""
39
+ return os.path.exists(self.get_metadata_path())
40
+
41
+ def get_metadata_path(self):
42
+ """Returns file path of the TOSCA meta file."""
43
+ return os.path.join(self.path, "TOSCA-Metadata", "TOSCA.meta")
44
+
45
+ def _reload_name(self):
46
+ """Reloads the name from the entry definition in the tosca.meta file."""
47
+ # fetch entry definition from tosca.meta file
48
+ with open(self.get_metadata_path()) as stream:
49
+ s = str(stream.read())
50
+ entry_definition = dict(
51
+ list(map(str.strip, line.split(":", 1)))
52
+ for line in s.splitlines()
53
+ if line.strip()
54
+ )["Entry-Definitions"]
55
+
56
+ # fetch template name from entry definition file retrieved earlier
57
+ with open(os.path.join(self.path, entry_definition), encoding="utf8") as stream:
58
+ definition = yaml.safe_load(stream)
59
+ self.real_shell_name = definition["metadata"]["template_name"]