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.
- shellfoundry/__init__.py +8 -0
- shellfoundry/__main__.py +7 -0
- shellfoundry/bootstrap.py +169 -0
- shellfoundry/commands/__init__.py +0 -0
- shellfoundry/commands/config_command.py +87 -0
- shellfoundry/commands/delete_command.py +24 -0
- shellfoundry/commands/dist_command.py +34 -0
- shellfoundry/commands/extend_command.py +182 -0
- shellfoundry/commands/generate_command.py +44 -0
- shellfoundry/commands/get_templates_command.py +151 -0
- shellfoundry/commands/install_command.py +81 -0
- shellfoundry/commands/list_command.py +110 -0
- shellfoundry/commands/new_command.py +393 -0
- shellfoundry/commands/pack_command.py +45 -0
- shellfoundry/commands/show_command.py +55 -0
- shellfoundry/data/standards.json +44 -0
- shellfoundry/data/templates.yml +117 -0
- shellfoundry/decorators/__init__.py +1 -0
- shellfoundry/decorators/standards.py +20 -0
- shellfoundry/decorators/version_check.py +52 -0
- shellfoundry/exceptions.py +52 -0
- shellfoundry/models/__init__.py +0 -0
- shellfoundry/models/install_config.py +82 -0
- shellfoundry/models/shell_template.py +22 -0
- shellfoundry/models/shellfoundry_settings.py +13 -0
- shellfoundry/utilities/__init__.py +114 -0
- shellfoundry/utilities/archive_creator.py +35 -0
- shellfoundry/utilities/cloudshell_api/__init__.py +1 -0
- shellfoundry/utilities/cloudshell_api/client_wrapper.py +71 -0
- shellfoundry/utilities/config/__init__.py +0 -0
- shellfoundry/utilities/config/config_context.py +44 -0
- shellfoundry/utilities/config/config_file_creation.py +32 -0
- shellfoundry/utilities/config/config_providers.py +50 -0
- shellfoundry/utilities/config/config_record.py +24 -0
- shellfoundry/utilities/config_reader.py +154 -0
- shellfoundry/utilities/constants.py +37 -0
- shellfoundry/utilities/cookiecutter_integration.py +67 -0
- shellfoundry/utilities/driver_generator.py +121 -0
- shellfoundry/utilities/filters.py +45 -0
- shellfoundry/utilities/installer.py +44 -0
- shellfoundry/utilities/modifiers/__init__.py +0 -0
- shellfoundry/utilities/modifiers/configuration/__init__.py +0 -0
- shellfoundry/utilities/modifiers/configuration/aggregated_modifiers.py +26 -0
- shellfoundry/utilities/modifiers/configuration/password_modification.py +48 -0
- shellfoundry/utilities/modifiers/definition/__init__.py +0 -0
- shellfoundry/utilities/modifiers/definition/definition_modification.py +205 -0
- shellfoundry/utilities/package_builder.py +152 -0
- shellfoundry/utilities/python_dependencies_packager.py +53 -0
- shellfoundry/utilities/repository_downloader.py +76 -0
- shellfoundry/utilities/shell_config_reader.py +59 -0
- shellfoundry/utilities/shell_datamodel_merger.py +44 -0
- shellfoundry/utilities/shell_package.py +59 -0
- shellfoundry/utilities/shell_package_builder.py +149 -0
- shellfoundry/utilities/shell_package_installer.py +231 -0
- shellfoundry/utilities/standards/__init__.py +3 -0
- shellfoundry/utilities/standards/consts.py +2 -0
- shellfoundry/utilities/standards/standards_retriever.py +32 -0
- shellfoundry/utilities/standards/standards_versions.py +34 -0
- shellfoundry/utilities/temp_dir_context.py +17 -0
- shellfoundry/utilities/template_retriever.py +309 -0
- shellfoundry/utilities/template_url.py +38 -0
- shellfoundry/utilities/template_versions.py +47 -0
- shellfoundry/utilities/validations/__init__.py +2 -0
- shellfoundry/utilities/validations/shell_generation_validation.py +29 -0
- shellfoundry/utilities/validations/shell_name_validations.py +10 -0
- shellfoundry/utilities/version_utilities.py +19 -0
- shellfoundry-1.2.25.dist-info/LICENSE +201 -0
- shellfoundry-1.2.25.dist-info/METADATA +417 -0
- shellfoundry-1.2.25.dist-info/RECORD +72 -0
- shellfoundry-1.2.25.dist-info/WHEEL +5 -0
- shellfoundry-1.2.25.dist-info/entry_points.txt +2 -0
- shellfoundry-1.2.25.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from io import open
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from shellfoundry.utilities.archive_creator import ArchiveCreator
|
|
11
|
+
from shellfoundry.utilities.shell_package import ShellPackage
|
|
12
|
+
from shellfoundry.utilities.temp_dir_context import TempDirContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ShellPackageBuilder(object):
|
|
16
|
+
DRIVER_DIR = "src"
|
|
17
|
+
DEPLOY_DIR = "deployments"
|
|
18
|
+
|
|
19
|
+
def pack(self, path):
|
|
20
|
+
"""Creates TOSCA based Shell package."""
|
|
21
|
+
self._remove_all_pyc(path)
|
|
22
|
+
shell_package = ShellPackage(path)
|
|
23
|
+
shell_name = shell_package.get_shell_name()
|
|
24
|
+
shell_real_name = shell_package.get_name_from_definition()
|
|
25
|
+
with TempDirContext(shell_name) as package_path:
|
|
26
|
+
self._copy_tosca_meta(package_path, "")
|
|
27
|
+
tosca_meta = self._read_tosca_meta(path)
|
|
28
|
+
|
|
29
|
+
shell_definition_path = tosca_meta["Entry-Definitions"]
|
|
30
|
+
|
|
31
|
+
self._copy_shell_definition(package_path, "", shell_definition_path)
|
|
32
|
+
|
|
33
|
+
with open(shell_definition_path, encoding="utf8") as shell_definition_file:
|
|
34
|
+
shell_definition = yaml.safe_load(shell_definition_file)
|
|
35
|
+
|
|
36
|
+
if "template_icon" in shell_definition["metadata"]:
|
|
37
|
+
self._copy_artifact(
|
|
38
|
+
shell_definition["metadata"]["template_icon"], package_path
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
for node_type in list(shell_definition["node_types"].values()):
|
|
42
|
+
if "artifacts" not in node_type:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
artifact_path_list = []
|
|
46
|
+
for artifact_name, artifact in node_type["artifacts"].items():
|
|
47
|
+
if artifact_name == "driver":
|
|
48
|
+
artifact_path_list.append(
|
|
49
|
+
self._create_driver(
|
|
50
|
+
path="",
|
|
51
|
+
package_path=os.curdir,
|
|
52
|
+
dir_path=self.DRIVER_DIR,
|
|
53
|
+
driver_name=os.path.basename(artifact["file"]),
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
elif artifact_name == "deployment":
|
|
57
|
+
artifact_path_list.append(
|
|
58
|
+
self._create_driver(
|
|
59
|
+
path="",
|
|
60
|
+
package_path=os.curdir,
|
|
61
|
+
dir_path=self.DEPLOY_DIR,
|
|
62
|
+
driver_name=os.path.basename(artifact["file"]),
|
|
63
|
+
mandatory=False,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
self._copy_artifact(artifact["file"], package_path)
|
|
68
|
+
|
|
69
|
+
zip_path = self._zip_package(package_path, "", shell_real_name)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
self._remove_build_artifacts(artifact_path_list)
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
click.echo("Shell package was successfully created: " + zip_path)
|
|
77
|
+
|
|
78
|
+
def _copy_artifact(self, artifact_path, package_path):
|
|
79
|
+
if os.path.exists(artifact_path):
|
|
80
|
+
click.echo("Adding artifact to shell package: " + artifact_path)
|
|
81
|
+
self._copy_file(src_file_path=artifact_path, dest_dir_path=package_path)
|
|
82
|
+
else:
|
|
83
|
+
click.echo("Missing artifact not added to shell package: " + artifact_path)
|
|
84
|
+
|
|
85
|
+
def _read_tosca_meta(self, path):
|
|
86
|
+
tosca_meta = {}
|
|
87
|
+
shell_package = ShellPackage(path)
|
|
88
|
+
with open(shell_package.get_metadata_path(), encoding="utf8") as meta_file:
|
|
89
|
+
for meta_line in meta_file:
|
|
90
|
+
(key, val) = meta_line.split(":")
|
|
91
|
+
tosca_meta[key] = val.strip()
|
|
92
|
+
return tosca_meta
|
|
93
|
+
|
|
94
|
+
def _copy_shell_icon(self, package_path, path):
|
|
95
|
+
self._copy_file(
|
|
96
|
+
src_file_path=os.path.join(path, "shell-icon.png"),
|
|
97
|
+
dest_dir_path=package_path,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _copy_shell_definition(self, package_path, path, shell_definition):
|
|
101
|
+
self._copy_file(
|
|
102
|
+
src_file_path=os.path.join(path, shell_definition),
|
|
103
|
+
dest_dir_path=package_path,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def _copy_tosca_meta(self, package_path, path):
|
|
107
|
+
shell_package = ShellPackage(path)
|
|
108
|
+
self._copy_file(
|
|
109
|
+
src_file_path=shell_package.get_metadata_path(),
|
|
110
|
+
dest_dir_path=os.path.join(package_path, "TOSCA-Metadata"),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _remove_all_pyc(package_path):
|
|
115
|
+
for root, dirs, files in os.walk(package_path):
|
|
116
|
+
for file in files:
|
|
117
|
+
if file.endswith(".pyc"):
|
|
118
|
+
os.remove(os.path.join(root, file))
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _create_driver(path, package_path, dir_path, driver_name, mandatory=True):
|
|
122
|
+
dir_to_zip = os.path.join(path, dir_path)
|
|
123
|
+
if os.path.exists(dir_to_zip):
|
|
124
|
+
zip_file_path = os.path.join(package_path, driver_name)
|
|
125
|
+
ArchiveCreator.make_archive(zip_file_path, "zip", dir_to_zip)
|
|
126
|
+
return os.path.abspath(zip_file_path)
|
|
127
|
+
elif mandatory:
|
|
128
|
+
raise click.ClickException(
|
|
129
|
+
"Invalid driver structure. Can't find '{}' driver folder.".format(
|
|
130
|
+
dir_path
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def _copy_file(src_file_path, dest_dir_path):
|
|
136
|
+
if not os.path.exists(dest_dir_path):
|
|
137
|
+
os.makedirs(dest_dir_path)
|
|
138
|
+
shutil.copy(src_file_path, dest_dir_path)
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def _zip_package(package_path, path, package_name):
|
|
142
|
+
zip_file_path = os.path.join(path, "dist", package_name)
|
|
143
|
+
return ArchiveCreator.make_archive(zip_file_path, "zip", package_path)
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _remove_build_artifacts(artifacts_path_list):
|
|
147
|
+
for artifact_path in artifacts_path_list:
|
|
148
|
+
if artifact_path and os.path.exists(artifact_path):
|
|
149
|
+
os.remove(artifact_path)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from urllib.error import HTTPError
|
|
11
|
+
except ImportError:
|
|
12
|
+
from urllib2 import HTTPError
|
|
13
|
+
|
|
14
|
+
from cloudshell.rest.api import PackagingRestApiClient
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from cloudshell.rest.exceptions import FeatureUnavailable, ShellNotFound
|
|
18
|
+
except ImportError:
|
|
19
|
+
from cloudshell.rest.exceptions import (
|
|
20
|
+
FeatureUnavailable,
|
|
21
|
+
ShellNotFoundException as ShellNotFound,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from shellfoundry.exceptions import FatalError
|
|
25
|
+
from shellfoundry.utilities.config_reader import CloudShellConfigReader, Configuration
|
|
26
|
+
from shellfoundry.utilities.constants import (
|
|
27
|
+
CLOUDSHELL_MAX_RETRIES,
|
|
28
|
+
CLOUDSHELL_RETRY_INTERVAL_SEC,
|
|
29
|
+
DEFAULT_TIME_WAIT,
|
|
30
|
+
)
|
|
31
|
+
from shellfoundry.utilities.shell_package import ShellPackage
|
|
32
|
+
|
|
33
|
+
SHELL_IS_OFFICIAL_FLAG = "IsOfficial"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ShellPackageInstaller(object):
|
|
37
|
+
GLOBAL_DOMAIN = "Global"
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
self.cloudshell_config_reader = Configuration(CloudShellConfigReader())
|
|
41
|
+
|
|
42
|
+
def install(self, path):
|
|
43
|
+
"""Install new or Update existed Shell."""
|
|
44
|
+
shell_package = ShellPackage(path)
|
|
45
|
+
shell_name = shell_package.get_name_from_definition()
|
|
46
|
+
shell_filename = shell_name + ".zip"
|
|
47
|
+
package_full_path = os.path.join(path, "dist", shell_filename)
|
|
48
|
+
|
|
49
|
+
cloudshell_config = self.cloudshell_config_reader.read()
|
|
50
|
+
|
|
51
|
+
if cloudshell_config.domain != self.GLOBAL_DOMAIN:
|
|
52
|
+
raise click.UsageError(
|
|
53
|
+
"Gen2 shells could not be installed into non Global domain."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
cs_connection_label = "Connecting to CloudShell at {}:{}".format(
|
|
57
|
+
cloudshell_config.host, cloudshell_config.port
|
|
58
|
+
)
|
|
59
|
+
with click.progressbar(
|
|
60
|
+
length=CLOUDSHELL_MAX_RETRIES, show_eta=False, label=cs_connection_label
|
|
61
|
+
) as pbar:
|
|
62
|
+
try:
|
|
63
|
+
client = self._open_connection_to_quali_server(
|
|
64
|
+
cloudshell_config, pbar, retry=CLOUDSHELL_MAX_RETRIES
|
|
65
|
+
)
|
|
66
|
+
finally:
|
|
67
|
+
self._render_pbar_finish(pbar)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
is_official = client.get_shell(shell_name=shell_name).get(
|
|
71
|
+
SHELL_IS_OFFICIAL_FLAG, False
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if is_official:
|
|
75
|
+
click.confirm(
|
|
76
|
+
text="Upgrading to a custom version of the shell will limit you "
|
|
77
|
+
"only to customized versions of this shell from now on. "
|
|
78
|
+
"You won't be able to upgrade it to an official version of the shell in the future." # noqa: E501
|
|
79
|
+
"\nDo you wish to continue?",
|
|
80
|
+
abort=True,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
except FeatureUnavailable:
|
|
84
|
+
# try to update shell first
|
|
85
|
+
pass
|
|
86
|
+
except ShellNotFound:
|
|
87
|
+
# try to install shell
|
|
88
|
+
pass
|
|
89
|
+
except click.Abort:
|
|
90
|
+
raise
|
|
91
|
+
except Exception as e:
|
|
92
|
+
raise FatalError(
|
|
93
|
+
self._parse_installation_error(
|
|
94
|
+
"Failed to get information about installed shell", e
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
pbar_install_shell_len = 2 # amount of possible actions (update and add)
|
|
99
|
+
installation_label = "Installing shell into CloudShell".ljust(
|
|
100
|
+
len(cs_connection_label)
|
|
101
|
+
)
|
|
102
|
+
with click.progressbar(
|
|
103
|
+
length=pbar_install_shell_len, show_eta=False, label=installation_label
|
|
104
|
+
) as pbar:
|
|
105
|
+
try:
|
|
106
|
+
client.update_shell(package_full_path)
|
|
107
|
+
except ShellNotFound:
|
|
108
|
+
self._increase_pbar(pbar, DEFAULT_TIME_WAIT)
|
|
109
|
+
self._add_new_shell(client, package_full_path)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
self._increase_pbar(pbar, DEFAULT_TIME_WAIT)
|
|
112
|
+
raise FatalError(
|
|
113
|
+
self._parse_installation_error("Failed to update shell", e)
|
|
114
|
+
)
|
|
115
|
+
finally:
|
|
116
|
+
self._render_pbar_finish(pbar)
|
|
117
|
+
|
|
118
|
+
def delete(self, shell_name):
|
|
119
|
+
"""Delete Shell."""
|
|
120
|
+
cloudshell_config = self.cloudshell_config_reader.read()
|
|
121
|
+
|
|
122
|
+
if cloudshell_config.domain != self.GLOBAL_DOMAIN:
|
|
123
|
+
raise click.UsageError(
|
|
124
|
+
"Gen2 shells could not be deleted from non Global domain."
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
cs_connection_label = "Connecting to CloudShell at {}:{}".format(
|
|
128
|
+
cloudshell_config.host, cloudshell_config.port
|
|
129
|
+
)
|
|
130
|
+
with click.progressbar(
|
|
131
|
+
length=CLOUDSHELL_MAX_RETRIES, show_eta=False, label=cs_connection_label
|
|
132
|
+
) as pbar:
|
|
133
|
+
try:
|
|
134
|
+
client = self._open_connection_to_quali_server(
|
|
135
|
+
cloudshell_config, pbar, retry=CLOUDSHELL_MAX_RETRIES
|
|
136
|
+
)
|
|
137
|
+
finally:
|
|
138
|
+
self._render_pbar_finish(pbar)
|
|
139
|
+
|
|
140
|
+
pbar_install_shell_len = 2 # amount of possible actions (update and add)
|
|
141
|
+
installation_label = "Deleting shell from CloudShell".ljust(
|
|
142
|
+
len(cs_connection_label)
|
|
143
|
+
)
|
|
144
|
+
with click.progressbar(
|
|
145
|
+
length=pbar_install_shell_len, show_eta=False, label=installation_label
|
|
146
|
+
) as pbar:
|
|
147
|
+
try:
|
|
148
|
+
client.delete_shell(shell_name)
|
|
149
|
+
except FeatureUnavailable:
|
|
150
|
+
self._increase_pbar(pbar, DEFAULT_TIME_WAIT)
|
|
151
|
+
raise click.ClickException(
|
|
152
|
+
"Delete shell command unavailable (probably due to CloudShell version below 9.2)" # noqa: E501
|
|
153
|
+
)
|
|
154
|
+
except ShellNotFound:
|
|
155
|
+
self._increase_pbar(pbar, DEFAULT_TIME_WAIT)
|
|
156
|
+
raise click.ClickException(
|
|
157
|
+
"Shell '{shell_name}' doesn't exist on CloudShell".format(
|
|
158
|
+
shell_name=shell_name
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
self._increase_pbar(pbar, DEFAULT_TIME_WAIT)
|
|
163
|
+
raise click.ClickException(
|
|
164
|
+
self._parse_installation_error("Failed to delete shell", e)
|
|
165
|
+
)
|
|
166
|
+
finally:
|
|
167
|
+
self._render_pbar_finish(pbar)
|
|
168
|
+
|
|
169
|
+
def _open_connection_to_quali_server(self, cloudshell_config, pbar, retry):
|
|
170
|
+
if retry == 0:
|
|
171
|
+
raise FatalError(
|
|
172
|
+
"Connection to CloudShell Server failed. "
|
|
173
|
+
"Please make sure it is up and running properly."
|
|
174
|
+
)
|
|
175
|
+
try:
|
|
176
|
+
try:
|
|
177
|
+
client = PackagingRestApiClient.login(
|
|
178
|
+
host=cloudshell_config.host,
|
|
179
|
+
port=cloudshell_config.port,
|
|
180
|
+
username=cloudshell_config.username,
|
|
181
|
+
password=cloudshell_config.password,
|
|
182
|
+
domain=cloudshell_config.domain,
|
|
183
|
+
)
|
|
184
|
+
return client
|
|
185
|
+
except AttributeError:
|
|
186
|
+
client = PackagingRestApiClient(
|
|
187
|
+
ip=cloudshell_config.host,
|
|
188
|
+
port=cloudshell_config.port,
|
|
189
|
+
username=cloudshell_config.username,
|
|
190
|
+
password=cloudshell_config.password,
|
|
191
|
+
domain=cloudshell_config.domain,
|
|
192
|
+
)
|
|
193
|
+
return client
|
|
194
|
+
except HTTPError as e:
|
|
195
|
+
if e.code == 401:
|
|
196
|
+
raise FatalError(
|
|
197
|
+
"Login to CloudShell failed. "
|
|
198
|
+
"Please verify the credentials in the config"
|
|
199
|
+
)
|
|
200
|
+
raise FatalError(
|
|
201
|
+
"Connection to CloudShell Server failed. "
|
|
202
|
+
"Please make sure it is up and running properly."
|
|
203
|
+
)
|
|
204
|
+
except Exception:
|
|
205
|
+
self._increase_pbar(pbar, time_wait=CLOUDSHELL_RETRY_INTERVAL_SEC)
|
|
206
|
+
return self._open_connection_to_quali_server(
|
|
207
|
+
cloudshell_config, pbar, retry - 1
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def _add_new_shell(self, client, package_full_path):
|
|
211
|
+
try:
|
|
212
|
+
client.add_shell(package_full_path)
|
|
213
|
+
except Exception as e:
|
|
214
|
+
raise FatalError(
|
|
215
|
+
self._parse_installation_error("Failed to add new shell", e)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def _parse_installation_error(self, base_message, error):
|
|
219
|
+
try:
|
|
220
|
+
cs_message = json.loads(str(error))["Message"]
|
|
221
|
+
except Exception:
|
|
222
|
+
cs_message = ""
|
|
223
|
+
return "{}. CloudShell responded with: '{}'".format(base_message, cs_message)
|
|
224
|
+
|
|
225
|
+
def _increase_pbar(self, pbar, time_wait):
|
|
226
|
+
time.sleep(time_wait)
|
|
227
|
+
pbar.make_step(1)
|
|
228
|
+
|
|
229
|
+
def _render_pbar_finish(self, pbar):
|
|
230
|
+
pbar.finish()
|
|
231
|
+
pbar.render_progress()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from io import open
|
|
6
|
+
|
|
7
|
+
from ..cloudshell_api import create_cloudshell_client
|
|
8
|
+
|
|
9
|
+
from shellfoundry.decorators.standards import standard_transformation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Standards(object):
|
|
13
|
+
@standard_transformation
|
|
14
|
+
def fetch(self, **kwargs):
|
|
15
|
+
alternative = kwargs.get("alternative", None)
|
|
16
|
+
if not alternative:
|
|
17
|
+
return self._fetch_from_cloudshell()
|
|
18
|
+
return self._fetch_from_alternative_path(alternative)
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def _fetch_from_cloudshell():
|
|
22
|
+
cs_client = create_cloudshell_client()
|
|
23
|
+
try:
|
|
24
|
+
return cs_client.get_installed_standards()
|
|
25
|
+
except Exception:
|
|
26
|
+
raise
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _fetch_from_alternative_path(alternative_path):
|
|
30
|
+
with open(alternative_path, mode="r", encoding="utf8") as stream:
|
|
31
|
+
response = stream.read()
|
|
32
|
+
return json.loads(response)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from pkg_resources import parse_version
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StandardVersionsFactory(object):
|
|
8
|
+
def create(self, standards):
|
|
9
|
+
return StandardVersions(standards)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StandardVersions(object):
|
|
13
|
+
def __init__(self, standards):
|
|
14
|
+
if not standards:
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from shellfoundry import __file__ as sf_file
|
|
18
|
+
|
|
19
|
+
raise Exception(
|
|
20
|
+
"Standards list is empty. Please verify that {} exists".format(
|
|
21
|
+
os.path.join(os.path.dirname(sf_file), "data", "standards.json")
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.standards = standards
|
|
26
|
+
|
|
27
|
+
def get_latest_version(self, standard):
|
|
28
|
+
standards = self.standards.get(standard, None)
|
|
29
|
+
if standards is None:
|
|
30
|
+
raise Exception("Failed to find latest version")
|
|
31
|
+
|
|
32
|
+
latest_version = str(max(list(map(parse_version, standards))))
|
|
33
|
+
if latest_version:
|
|
34
|
+
return latest_version
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import tempfile
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TempDirContext:
|
|
6
|
+
def __init__(self, remove_dir_on_error=True, prefix=""):
|
|
7
|
+
self.temp_dir = None
|
|
8
|
+
self.prefix = prefix
|
|
9
|
+
self._remove_dir_on_error = remove_dir_on_error
|
|
10
|
+
|
|
11
|
+
def __enter__(self):
|
|
12
|
+
self.temp_dir = tempfile.mkdtemp(prefix=self.prefix)
|
|
13
|
+
return self.temp_dir
|
|
14
|
+
|
|
15
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
16
|
+
if not exc_val or (exc_val and self._remove_dir_on_error):
|
|
17
|
+
shutil.rmtree(self.temp_dir, ignore_errors=True)
|