cloudshell-rest-api 8.2.3.2__tar.gz → 9.0.2__tar.gz
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.
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/HISTORY.rst +8 -0
- cloudshell_rest_api-9.0.2/MANIFEST.in +8 -0
- cloudshell_rest_api-9.0.2/PKG-INFO +118 -0
- cloudshell_rest_api-9.0.2/README.md +39 -0
- cloudshell_rest_api-9.0.2/cloudshell/__init__.py +3 -0
- cloudshell_rest_api-9.0.2/cloudshell/rest/__init__.py +3 -0
- cloudshell_rest_api-9.0.2/cloudshell/rest/api.py +207 -0
- cloudshell_rest_api-9.0.2/cloudshell/rest/exceptions.py +14 -0
- cloudshell_rest_api-9.0.2/cloudshell/rest/models.py +74 -0
- cloudshell_rest_api-9.0.2/cloudshell/rest/progress_bar.py +66 -0
- cloudshell_rest_api-9.0.2/cloudshell_rest_api.egg-info/PKG-INFO +118 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/cloudshell_rest_api.egg-info/SOURCES.txt +6 -4
- cloudshell_rest_api-9.0.2/cloudshell_rest_api.egg-info/requires.txt +5 -0
- cloudshell_rest_api-9.0.2/requirements.txt +5 -0
- cloudshell_rest_api-9.0.2/setup.cfg +4 -0
- cloudshell_rest_api-9.0.2/setup.py +38 -0
- cloudshell_rest_api-9.0.2/test_requirements.txt +3 -0
- cloudshell_rest_api-9.0.2/tests/__init__.py +1 -0
- cloudshell_rest_api-9.0.2/tests/multipart_matcher.py +26 -0
- cloudshell_rest_api-9.0.2/tests/test_api.py +487 -0
- cloudshell_rest_api-9.0.2/version.txt +1 -0
- cloudshell-rest-api-8.2.3.2/MANIFEST.in +0 -15
- cloudshell-rest-api-8.2.3.2/PKG-INFO +0 -115
- cloudshell-rest-api-8.2.3.2/README.rst +0 -57
- cloudshell-rest-api-8.2.3.2/cloudshell/__init__.py +0 -5
- cloudshell-rest-api-8.2.3.2/cloudshell/rest/__init__.py +0 -5
- cloudshell-rest-api-8.2.3.2/cloudshell/rest/api.py +0 -235
- cloudshell-rest-api-8.2.3.2/cloudshell/rest/exceptions.py +0 -6
- cloudshell-rest-api-8.2.3.2/cloudshell/version.py +0 -1
- cloudshell-rest-api-8.2.3.2/cloudshell_rest_api.egg-info/PKG-INFO +0 -115
- cloudshell-rest-api-8.2.3.2/cloudshell_rest_api.egg-info/requires.txt +0 -1
- cloudshell-rest-api-8.2.3.2/requirements.txt +0 -1
- cloudshell-rest-api-8.2.3.2/setup.cfg +0 -26
- cloudshell-rest-api-8.2.3.2/setup.py +0 -47
- cloudshell-rest-api-8.2.3.2/test_requirements.txt +0 -2
- cloudshell-rest-api-8.2.3.2/tests/__init__.py +0 -1
- cloudshell-rest-api-8.2.3.2/tests/test_packaging_rest_api_client.py +0 -378
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/AUTHORS.rst +0 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/CONTRIBUTING.rst +0 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/LICENSE +0 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/cloudshell_rest_api.egg-info/dependency_links.txt +0 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/cloudshell_rest_api.egg-info/not-zip-safe +0 -0
- {cloudshell-rest-api-8.2.3.2 → cloudshell_rest_api-9.0.2}/cloudshell_rest_api.egg-info/top_level.txt +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
History
|
|
3
3
|
=======
|
|
4
4
|
|
|
5
|
+
9.0.0 (2023-05-04)
|
|
6
|
+
--------------------
|
|
7
|
+
|
|
8
|
+
* Added get_shell_as_model, get_installed_standards_as_model
|
|
9
|
+
* Added the ability to connect with an existing token
|
|
10
|
+
* Added progress bar for downloading/uploading files
|
|
11
|
+
* Removed support of Python2
|
|
12
|
+
|
|
5
13
|
8.2.2.0 (2019-01-08)
|
|
6
14
|
--------------------
|
|
7
15
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudshell-rest-api
|
|
3
|
+
Version: 9.0.2
|
|
4
|
+
Summary: Python client for the CloudShell REST API
|
|
5
|
+
Home-page: https://github.com/QualiSystems/cloudshell-rest-api
|
|
6
|
+
Author: Boris Modylevsky
|
|
7
|
+
Author-email: borismod@gmail.com
|
|
8
|
+
License: Apache Software License 2.0
|
|
9
|
+
Keywords: cloudshell quali sandbox cloud rest api
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Requires-Python: ~=3.7
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
License-File: AUTHORS.rst
|
|
19
|
+
Requires-Dist: requests<3,>=2.23
|
|
20
|
+
Requires-Dist: attrs<24,>=21
|
|
21
|
+
Requires-Dist: typing-extensions>=4.4
|
|
22
|
+
Requires-Dist: alive_progress~=3.0
|
|
23
|
+
Requires-Dist: requests_toolbelt~=1.0
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: keywords
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
# cloudshell-rest-api
|
|
37
|
+
|
|
38
|
+
[](https://github.com/QualiSystems/cloudshell-rest-api/actions?query=branch%3Amaster)
|
|
39
|
+
[](https://codecov.io/gh/QualiSystems/cloudshell-rest-api)
|
|
40
|
+
[](https://badge.fury.io/py/cloudshell-rest-api)
|
|
41
|
+
[](https://github.com/python/black)
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
* Add Shell - adds a new Shell Entity (supported from CloudShell 8.0)
|
|
46
|
+
* Update Shell - updates an existing Shell Entity (supported from CloudShell 8.0)
|
|
47
|
+
* Delete Shell - removes an existing Shell Entity (supported from CloudShell 9.2)
|
|
48
|
+
* Get Shell - get Shell's information
|
|
49
|
+
* Get Installed Standards - gets a list of standards and matching versions installed on CloudShell (supported from CloudShell 8.1)
|
|
50
|
+
* Import Package - imports a package to CloudShell
|
|
51
|
+
* Export Package - exports a package from CloudShell
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install cloudshell-rest-api
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Getting started
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from cloudshell.rest.api import PackagingRestApiClient
|
|
64
|
+
# Loging to CloudShell
|
|
65
|
+
client = PackagingRestApiClient.login("HOST", "USERNAME", "PASSWORD", "DOMAIN")
|
|
66
|
+
# Or connect with a token
|
|
67
|
+
client = PackagingRestApiClient("HOST", "TOKEN")
|
|
68
|
+
# Add a new Shell to CloudShell
|
|
69
|
+
client.add_shell("SHELL_PATH.zip")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
* Free software: Apache Software License 2.0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
=======
|
|
78
|
+
History
|
|
79
|
+
=======
|
|
80
|
+
|
|
81
|
+
9.0.0 (2023-05-04)
|
|
82
|
+
--------------------
|
|
83
|
+
|
|
84
|
+
* Added get_shell_as_model, get_installed_standards_as_model
|
|
85
|
+
* Added the ability to connect with an existing token
|
|
86
|
+
* Added progress bar for downloading/uploading files
|
|
87
|
+
* Removed support of Python2
|
|
88
|
+
|
|
89
|
+
8.2.2.0 (2019-01-08)
|
|
90
|
+
--------------------
|
|
91
|
+
|
|
92
|
+
* Added delete shell method to rest-api
|
|
93
|
+
|
|
94
|
+
8.2.0.0 (2017-09-19)
|
|
95
|
+
--------------------
|
|
96
|
+
|
|
97
|
+
* newest addition to rest-api. please welcome get_shell - returns a requested shell information
|
|
98
|
+
|
|
99
|
+
8.1.1.0 (2017-08-27)
|
|
100
|
+
--------------------
|
|
101
|
+
|
|
102
|
+
* remodeling update_shell to also accept shell name
|
|
103
|
+
|
|
104
|
+
8.1.0.1 (2017-06-12)
|
|
105
|
+
--------------------
|
|
106
|
+
|
|
107
|
+
* Bug fix
|
|
108
|
+
|
|
109
|
+
8.1.0.0 (2017-06-04)
|
|
110
|
+
--------------------
|
|
111
|
+
|
|
112
|
+
* Query installed standards on CloudShell server
|
|
113
|
+
|
|
114
|
+
7.2.0.6 (2016-09-01)
|
|
115
|
+
--------------------
|
|
116
|
+
|
|
117
|
+
* First release of with Add Shell and Update Shell features
|
|
118
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# cloudshell-rest-api
|
|
2
|
+
|
|
3
|
+
[](https://github.com/QualiSystems/cloudshell-rest-api/actions?query=branch%3Amaster)
|
|
4
|
+
[](https://codecov.io/gh/QualiSystems/cloudshell-rest-api)
|
|
5
|
+
[](https://badge.fury.io/py/cloudshell-rest-api)
|
|
6
|
+
[](https://github.com/python/black)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
* Add Shell - adds a new Shell Entity (supported from CloudShell 8.0)
|
|
11
|
+
* Update Shell - updates an existing Shell Entity (supported from CloudShell 8.0)
|
|
12
|
+
* Delete Shell - removes an existing Shell Entity (supported from CloudShell 9.2)
|
|
13
|
+
* Get Shell - get Shell's information
|
|
14
|
+
* Get Installed Standards - gets a list of standards and matching versions installed on CloudShell (supported from CloudShell 8.1)
|
|
15
|
+
* Import Package - imports a package to CloudShell
|
|
16
|
+
* Export Package - exports a package from CloudShell
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install cloudshell-rest-api
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Getting started
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from cloudshell.rest.api import PackagingRestApiClient
|
|
29
|
+
# Loging to CloudShell
|
|
30
|
+
client = PackagingRestApiClient.login("HOST", "USERNAME", "PASSWORD", "DOMAIN")
|
|
31
|
+
# Or connect with a token
|
|
32
|
+
client = PackagingRestApiClient("HOST", "TOKEN")
|
|
33
|
+
# Add a new Shell to CloudShell
|
|
34
|
+
client.add_shell("SHELL_PATH.zip")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
* Free software: Apache Software License 2.0
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Generator
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import BinaryIO
|
|
7
|
+
from urllib.parse import urljoin
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from attrs import define, field
|
|
11
|
+
from typing_extensions import Self
|
|
12
|
+
|
|
13
|
+
from cloudshell.rest.exceptions import (
|
|
14
|
+
FeatureUnavailable,
|
|
15
|
+
LoginFailedError,
|
|
16
|
+
PackagingRestApiError,
|
|
17
|
+
ShellNotFound,
|
|
18
|
+
)
|
|
19
|
+
from cloudshell.rest.models import ShellInfo, StandardInfo
|
|
20
|
+
from cloudshell.rest.progress_bar import iter_resp_with_pb, upload_with_pb
|
|
21
|
+
|
|
22
|
+
DEFAULT_API_TIMEOUT = 15
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@define
|
|
26
|
+
class PackagingRestApiClient:
|
|
27
|
+
host: str
|
|
28
|
+
_token: str = field(repr=False)
|
|
29
|
+
port: int = 9000
|
|
30
|
+
_show_progress: bool = field(default=False, repr=False)
|
|
31
|
+
_api_timeout: int = field(default=DEFAULT_API_TIMEOUT, repr=False)
|
|
32
|
+
_api_url: str = field(init=False)
|
|
33
|
+
_headers: dict[str, str] = field(init=False)
|
|
34
|
+
|
|
35
|
+
def __attrs_post_init__(self):
|
|
36
|
+
self._api_url = _get_api_url(self.host, self.port)
|
|
37
|
+
self._headers = {"Authorization": f"Basic {self._token}"}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def login(
|
|
41
|
+
cls,
|
|
42
|
+
host: str,
|
|
43
|
+
username: str,
|
|
44
|
+
password: str,
|
|
45
|
+
domain: str = "Global",
|
|
46
|
+
port: int = 9000,
|
|
47
|
+
show_progress: bool = False,
|
|
48
|
+
api_timeout: int = DEFAULT_API_TIMEOUT,
|
|
49
|
+
) -> Self:
|
|
50
|
+
url = urljoin(_get_api_url(host, port), "Auth/Login")
|
|
51
|
+
req_data = {
|
|
52
|
+
"username": username,
|
|
53
|
+
"password": password,
|
|
54
|
+
"domain": domain,
|
|
55
|
+
}
|
|
56
|
+
resp = requests.put(url, data=req_data, timeout=api_timeout)
|
|
57
|
+
if resp.status_code == 401:
|
|
58
|
+
raise LoginFailedError(resp.text)
|
|
59
|
+
elif resp.status_code != 200:
|
|
60
|
+
raise PackagingRestApiError(resp.text)
|
|
61
|
+
token = resp.text.strip("'\"")
|
|
62
|
+
return cls(
|
|
63
|
+
host, token, port, show_progress=show_progress, api_timeout=api_timeout
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def add_shell_from_buffer(self, file_obj: BinaryIO | bytes) -> None:
|
|
67
|
+
"""Add a new Shell from the buffer or binary."""
|
|
68
|
+
url = urljoin(self._api_url, "Shells")
|
|
69
|
+
req_data = {"files": ("file", file_obj)}
|
|
70
|
+
headers = self._headers.copy()
|
|
71
|
+
|
|
72
|
+
with upload_with_pb(req_data, headers, show=self._show_progress) as new_data:
|
|
73
|
+
resp = requests.post(
|
|
74
|
+
url, data=new_data, headers=headers, timeout=self._api_timeout
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if resp.status_code != 201:
|
|
78
|
+
msg = f"Can't add shell, response: {resp.text}"
|
|
79
|
+
raise PackagingRestApiError(msg)
|
|
80
|
+
|
|
81
|
+
def add_shell(self, shell_path: str | Path) -> None:
|
|
82
|
+
"""Adds a new Shell Entity to CloudShell.
|
|
83
|
+
|
|
84
|
+
If the shell exists, exception will be thrown
|
|
85
|
+
"""
|
|
86
|
+
with open(shell_path, "rb") as f:
|
|
87
|
+
self.add_shell_from_buffer(f)
|
|
88
|
+
|
|
89
|
+
def update_shell_from_buffer(
|
|
90
|
+
self, file_obj: BinaryIO | bytes, shell_name: str
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Updates an existing Shell from the buffer or binary."""
|
|
93
|
+
url = urljoin(self._api_url, f"Shells/{shell_name}")
|
|
94
|
+
req_data = {"files": ("file", file_obj)}
|
|
95
|
+
headers = self._headers.copy()
|
|
96
|
+
|
|
97
|
+
with upload_with_pb(req_data, headers, show=self._show_progress) as new_data:
|
|
98
|
+
resp = requests.put(
|
|
99
|
+
url, data=new_data, headers=headers, timeout=self._api_timeout
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if resp.status_code == 404:
|
|
103
|
+
raise ShellNotFound()
|
|
104
|
+
elif resp.status_code != 200:
|
|
105
|
+
msg = f"Can't update shell, response: {resp.text}"
|
|
106
|
+
raise PackagingRestApiError(msg)
|
|
107
|
+
|
|
108
|
+
def update_shell(
|
|
109
|
+
self, shell_path: str | Path, shell_name: str | None = None
|
|
110
|
+
) -> None:
|
|
111
|
+
"""Updates an existing Shell Entity in CloudShell."""
|
|
112
|
+
shell_name = shell_name or os.path.basename(shell_path).rsplit(".", 1)[0]
|
|
113
|
+
with open(shell_path, "rb") as f:
|
|
114
|
+
self.update_shell_from_buffer(f, shell_name)
|
|
115
|
+
|
|
116
|
+
def get_installed_standards(self) -> list[dict]:
|
|
117
|
+
"""Gets all standards installed on CloudShell."""
|
|
118
|
+
url = urljoin(self._api_url, "Standards")
|
|
119
|
+
resp = requests.get(url, headers=self._headers, timeout=self._api_timeout)
|
|
120
|
+
if resp.status_code == 404:
|
|
121
|
+
raise FeatureUnavailable()
|
|
122
|
+
elif resp.status_code != 200:
|
|
123
|
+
raise PackagingRestApiError(resp.text)
|
|
124
|
+
return resp.json()
|
|
125
|
+
|
|
126
|
+
def get_installed_standards_as_models(self) -> list[StandardInfo]:
|
|
127
|
+
"""Get all standards installed on CloudShell as models."""
|
|
128
|
+
return [StandardInfo.from_dict(s) for s in self.get_installed_standards()]
|
|
129
|
+
|
|
130
|
+
def get_shell(self, shell_name: str) -> dict:
|
|
131
|
+
"""Get a Shell's information."""
|
|
132
|
+
url = urljoin(self._api_url, f"Shells/{shell_name}")
|
|
133
|
+
resp = requests.get(url, headers=self._headers, timeout=self._api_timeout)
|
|
134
|
+
if resp.status_code == 404:
|
|
135
|
+
raise FeatureUnavailable()
|
|
136
|
+
elif resp.status_code == 400:
|
|
137
|
+
raise ShellNotFound()
|
|
138
|
+
elif resp.status_code != 200:
|
|
139
|
+
raise PackagingRestApiError(resp.text)
|
|
140
|
+
return resp.json()
|
|
141
|
+
|
|
142
|
+
def get_shell_as_model(self, shell_name: str) -> ShellInfo:
|
|
143
|
+
"""Get a Shell's information as model."""
|
|
144
|
+
return ShellInfo.from_dict(self.get_shell(shell_name))
|
|
145
|
+
|
|
146
|
+
def delete_shell(self, shell_name: str) -> None:
|
|
147
|
+
"""Delete a Shell from the CloudShell."""
|
|
148
|
+
url = urljoin(self._api_url, f"Shells/{shell_name}")
|
|
149
|
+
resp = requests.delete(url, headers=self._headers, timeout=self._api_timeout)
|
|
150
|
+
if resp.status_code == 404:
|
|
151
|
+
raise FeatureUnavailable()
|
|
152
|
+
elif resp.status_code == 400:
|
|
153
|
+
raise ShellNotFound()
|
|
154
|
+
elif resp.status_code != 200:
|
|
155
|
+
raise PackagingRestApiError(resp.text)
|
|
156
|
+
|
|
157
|
+
def export_package(self, topologies: list[str]) -> Generator[bytes, None, None]:
|
|
158
|
+
"""Export a package with the topologies from the CloudShell."""
|
|
159
|
+
url = urljoin(self._api_url, "Package/ExportPackage")
|
|
160
|
+
req_data = {"TopologyNames": topologies}
|
|
161
|
+
resp = requests.post(
|
|
162
|
+
url,
|
|
163
|
+
headers=self._headers,
|
|
164
|
+
json=req_data,
|
|
165
|
+
stream=True,
|
|
166
|
+
timeout=self._api_timeout,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if resp.status_code == 404:
|
|
170
|
+
raise FeatureUnavailable()
|
|
171
|
+
elif resp.status_code != 200:
|
|
172
|
+
raise PackagingRestApiError(resp.text)
|
|
173
|
+
|
|
174
|
+
yield from iter_resp_with_pb(resp, show=self._show_progress)
|
|
175
|
+
|
|
176
|
+
def export_package_to_file(
|
|
177
|
+
self, topologies: list[str], file_path: str | Path
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Export a package with the topologies and save it to the file."""
|
|
180
|
+
with open(file_path, "wb") as f:
|
|
181
|
+
for data in self.export_package(topologies):
|
|
182
|
+
f.write(data)
|
|
183
|
+
|
|
184
|
+
def import_package_from_buffer(self, file_obj: BinaryIO | bytes) -> None:
|
|
185
|
+
"""Import the package from buffer to the CloudShell."""
|
|
186
|
+
url = urljoin(self._api_url, "Package/ImportPackage")
|
|
187
|
+
req_data = {"files": ("file", file_obj)}
|
|
188
|
+
headers = self._headers.copy()
|
|
189
|
+
|
|
190
|
+
with upload_with_pb(req_data, headers, show=self._show_progress) as new_data:
|
|
191
|
+
resp = requests.post(
|
|
192
|
+
url, data=new_data, headers=headers, timeout=self._api_timeout
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if resp.status_code == 404:
|
|
196
|
+
raise FeatureUnavailable()
|
|
197
|
+
elif resp.status_code != 200:
|
|
198
|
+
raise PackagingRestApiError(resp.text)
|
|
199
|
+
|
|
200
|
+
def import_package(self, package_path: str | Path) -> None:
|
|
201
|
+
"""Import the package from the file to the CloudShell."""
|
|
202
|
+
with open(package_path, "rb") as f:
|
|
203
|
+
self.import_package_from_buffer(f)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _get_api_url(host: str, port: int) -> str:
|
|
207
|
+
return f"http://{host}:{port}/API/"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class PackagingRestApiError(Exception):
|
|
2
|
+
"""Base packaging REST API Error."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ShellNotFound(PackagingRestApiError):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FeatureUnavailable(PackagingRestApiError):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LoginFailedError(PackagingRestApiError):
|
|
14
|
+
pass
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from attrs import define, field
|
|
4
|
+
from typing_extensions import Self
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@define
|
|
8
|
+
class UserInfo:
|
|
9
|
+
user_name: str
|
|
10
|
+
email: str
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def from_dict(cls, info_dict: dict) -> Self:
|
|
14
|
+
return cls(
|
|
15
|
+
user_name=info_dict["Username"],
|
|
16
|
+
email=info_dict["Email"],
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@define
|
|
21
|
+
class ExecutionEnvironmentType:
|
|
22
|
+
position: int
|
|
23
|
+
path: str
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_dict(cls, info_dict: dict) -> Self:
|
|
27
|
+
return cls(
|
|
28
|
+
position=info_dict["Position"],
|
|
29
|
+
path=info_dict["Path"],
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@define
|
|
34
|
+
class ShellInfo:
|
|
35
|
+
id: str = field(repr=False) # noqa: A003
|
|
36
|
+
name: str
|
|
37
|
+
version: str
|
|
38
|
+
standard_type: str = field(repr=False)
|
|
39
|
+
modification_date: str = field(repr=False)
|
|
40
|
+
last_modified_by_user: UserInfo = field(repr=False)
|
|
41
|
+
author: str = field(repr=False)
|
|
42
|
+
is_official: bool
|
|
43
|
+
based_on: str = field(repr=False)
|
|
44
|
+
execution_environment_type: ExecutionEnvironmentType = field(repr=False)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_dict(cls, info_dict: dict) -> Self:
|
|
48
|
+
return cls(
|
|
49
|
+
id=info_dict["Id"],
|
|
50
|
+
name=info_dict["Name"],
|
|
51
|
+
version=info_dict["Version"],
|
|
52
|
+
standard_type=info_dict["StandardType"],
|
|
53
|
+
modification_date=info_dict["ModificationDate"],
|
|
54
|
+
last_modified_by_user=UserInfo.from_dict(info_dict["LastModifiedByUser"]),
|
|
55
|
+
author=info_dict["Author"],
|
|
56
|
+
is_official=info_dict["IsOfficial"],
|
|
57
|
+
based_on=info_dict["BasedOn"],
|
|
58
|
+
execution_environment_type=ExecutionEnvironmentType.from_dict(
|
|
59
|
+
info_dict["ExecutionEnvironmentType"]
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@define
|
|
65
|
+
class StandardInfo:
|
|
66
|
+
standard_name: str
|
|
67
|
+
versions: list[str]
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def from_dict(cls, info_dict: dict) -> Self:
|
|
71
|
+
return cls(
|
|
72
|
+
standard_name=info_dict["StandardName"],
|
|
73
|
+
versions=info_dict["Versions"],
|
|
74
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
|
|
6
|
+
from alive_progress import alive_bar
|
|
7
|
+
from requests import Response
|
|
8
|
+
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def iter_resp_with_pb(
|
|
12
|
+
resp: Response, show: bool = True, chunk: int = 4096
|
|
13
|
+
) -> Generator[bytes, None, None]:
|
|
14
|
+
"""Iterate over response content with progress bar.
|
|
15
|
+
|
|
16
|
+
The request should be streaming.
|
|
17
|
+
"""
|
|
18
|
+
total = int(resp.headers.get("content-length", 0))
|
|
19
|
+
|
|
20
|
+
pb_context = alive_bar(
|
|
21
|
+
total,
|
|
22
|
+
unit="B",
|
|
23
|
+
scale=True,
|
|
24
|
+
disable=not show,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with pb_context as bar:
|
|
28
|
+
for data in resp.iter_content(chunk_size=chunk):
|
|
29
|
+
size = len(data)
|
|
30
|
+
bar(size)
|
|
31
|
+
yield data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@contextmanager
|
|
35
|
+
def upload_with_pb(data: dict, headers: dict, show: bool = True):
|
|
36
|
+
"""Upload data with progress bar.
|
|
37
|
+
|
|
38
|
+
Returns a new data object which should be used in the request.
|
|
39
|
+
Updates the headers with the new content type.
|
|
40
|
+
"""
|
|
41
|
+
e = MultipartEncoder(data)
|
|
42
|
+
pb_context = alive_bar(
|
|
43
|
+
e.len,
|
|
44
|
+
unit="B",
|
|
45
|
+
scale=True,
|
|
46
|
+
disable=not show,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
with pb_context as bar:
|
|
50
|
+
m = MultipartEncoderMonitor(e, _create_upload_callback(bar))
|
|
51
|
+
headers["Content-Type"] = m.content_type
|
|
52
|
+
|
|
53
|
+
yield m
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _create_upload_callback(bar):
|
|
57
|
+
bytes_read = 0
|
|
58
|
+
|
|
59
|
+
def callback(monitor):
|
|
60
|
+
nonlocal bytes_read
|
|
61
|
+
new_bytes = monitor.bytes_read - bytes_read
|
|
62
|
+
bytes_read = monitor.bytes_read
|
|
63
|
+
if new_bytes:
|
|
64
|
+
bar(new_bytes)
|
|
65
|
+
|
|
66
|
+
return callback
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudshell-rest-api
|
|
3
|
+
Version: 9.0.2
|
|
4
|
+
Summary: Python client for the CloudShell REST API
|
|
5
|
+
Home-page: https://github.com/QualiSystems/cloudshell-rest-api
|
|
6
|
+
Author: Boris Modylevsky
|
|
7
|
+
Author-email: borismod@gmail.com
|
|
8
|
+
License: Apache Software License 2.0
|
|
9
|
+
Keywords: cloudshell quali sandbox cloud rest api
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Requires-Python: ~=3.7
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
License-File: AUTHORS.rst
|
|
19
|
+
Requires-Dist: requests<3,>=2.23
|
|
20
|
+
Requires-Dist: attrs<24,>=21
|
|
21
|
+
Requires-Dist: typing-extensions>=4.4
|
|
22
|
+
Requires-Dist: alive_progress~=3.0
|
|
23
|
+
Requires-Dist: requests_toolbelt~=1.0
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: keywords
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
# cloudshell-rest-api
|
|
37
|
+
|
|
38
|
+
[](https://github.com/QualiSystems/cloudshell-rest-api/actions?query=branch%3Amaster)
|
|
39
|
+
[](https://codecov.io/gh/QualiSystems/cloudshell-rest-api)
|
|
40
|
+
[](https://badge.fury.io/py/cloudshell-rest-api)
|
|
41
|
+
[](https://github.com/python/black)
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
* Add Shell - adds a new Shell Entity (supported from CloudShell 8.0)
|
|
46
|
+
* Update Shell - updates an existing Shell Entity (supported from CloudShell 8.0)
|
|
47
|
+
* Delete Shell - removes an existing Shell Entity (supported from CloudShell 9.2)
|
|
48
|
+
* Get Shell - get Shell's information
|
|
49
|
+
* Get Installed Standards - gets a list of standards and matching versions installed on CloudShell (supported from CloudShell 8.1)
|
|
50
|
+
* Import Package - imports a package to CloudShell
|
|
51
|
+
* Export Package - exports a package from CloudShell
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install cloudshell-rest-api
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Getting started
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from cloudshell.rest.api import PackagingRestApiClient
|
|
64
|
+
# Loging to CloudShell
|
|
65
|
+
client = PackagingRestApiClient.login("HOST", "USERNAME", "PASSWORD", "DOMAIN")
|
|
66
|
+
# Or connect with a token
|
|
67
|
+
client = PackagingRestApiClient("HOST", "TOKEN")
|
|
68
|
+
# Add a new Shell to CloudShell
|
|
69
|
+
client.add_shell("SHELL_PATH.zip")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
* Free software: Apache Software License 2.0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
=======
|
|
78
|
+
History
|
|
79
|
+
=======
|
|
80
|
+
|
|
81
|
+
9.0.0 (2023-05-04)
|
|
82
|
+
--------------------
|
|
83
|
+
|
|
84
|
+
* Added get_shell_as_model, get_installed_standards_as_model
|
|
85
|
+
* Added the ability to connect with an existing token
|
|
86
|
+
* Added progress bar for downloading/uploading files
|
|
87
|
+
* Removed support of Python2
|
|
88
|
+
|
|
89
|
+
8.2.2.0 (2019-01-08)
|
|
90
|
+
--------------------
|
|
91
|
+
|
|
92
|
+
* Added delete shell method to rest-api
|
|
93
|
+
|
|
94
|
+
8.2.0.0 (2017-09-19)
|
|
95
|
+
--------------------
|
|
96
|
+
|
|
97
|
+
* newest addition to rest-api. please welcome get_shell - returns a requested shell information
|
|
98
|
+
|
|
99
|
+
8.1.1.0 (2017-08-27)
|
|
100
|
+
--------------------
|
|
101
|
+
|
|
102
|
+
* remodeling update_shell to also accept shell name
|
|
103
|
+
|
|
104
|
+
8.1.0.1 (2017-06-12)
|
|
105
|
+
--------------------
|
|
106
|
+
|
|
107
|
+
* Bug fix
|
|
108
|
+
|
|
109
|
+
8.1.0.0 (2017-06-04)
|
|
110
|
+
--------------------
|
|
111
|
+
|
|
112
|
+
* Query installed standards on CloudShell server
|
|
113
|
+
|
|
114
|
+
7.2.0.6 (2016-09-01)
|
|
115
|
+
--------------------
|
|
116
|
+
|
|
117
|
+
* First release of with Add Shell and Update Shell features
|
|
118
|
+
|