syneto_api 0.2.22__tar.gz → 0.2.24__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.
- {syneto_api-0.2.22 → syneto_api-0.2.24}/PKG-INFO +4 -2
- {syneto_api-0.2.22 → syneto_api-0.2.24}/pyproject.toml +5 -2
- {syneto_api-0.2.22 → syneto_api-0.2.24}/setup.py +4 -2
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/api_client.py +60 -14
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/auth_api.py +9 -1
- syneto_api-0.2.24/src/syneto_api/keyring.py +17 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/README.md +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/__init__.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/central_api.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/jobs_api.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/protection_api.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/settings.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/storage_api.py +0 -0
- {syneto_api-0.2.22 → syneto_api-0.2.24}/src/syneto_api/virt_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: syneto-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.24
|
|
4
4
|
Summary: Syneto Client API library
|
|
5
5
|
Home-page: https://github.com/SynetoNet/syneto-api
|
|
6
6
|
License: Proprietary
|
|
@@ -12,7 +12,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.6
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.7
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: aiodns (>=3,<4)
|
|
16
|
+
Requires-Dist: aiohttp (>=3.7,<4.0)
|
|
17
|
+
Requires-Dist: cchardet (>=2.1,<3.0)
|
|
16
18
|
Requires-Dist: inflection (>=0.5,<0.6)
|
|
17
19
|
Requires-Dist: python-dotenv (>=0.16,<0.17)
|
|
18
20
|
Requires-Dist: requests[secure] (>=2.25,<3.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "syneto_api"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.24"
|
|
4
4
|
description = "Syneto Client API library"
|
|
5
5
|
authors = ["Your Name <you@example.com>"]
|
|
6
6
|
license = "Proprietary"
|
|
@@ -13,11 +13,14 @@ python = "^3.6"
|
|
|
13
13
|
python-dotenv = "^0.16"
|
|
14
14
|
requests = {extras = ["secure"], version = "^2.25"}
|
|
15
15
|
inflection = "^0.5"
|
|
16
|
-
aiohttp =
|
|
16
|
+
aiohttp = "^3.7"
|
|
17
|
+
aiodns = "^3"
|
|
18
|
+
cchardet = "^2.1"
|
|
17
19
|
|
|
18
20
|
[tool.poetry.dev-dependencies]
|
|
19
21
|
pytest = "^6.2.2"
|
|
20
22
|
ipdb = "^0.13.7"
|
|
23
|
+
pytest-asyncio = "^0.15"
|
|
21
24
|
|
|
22
25
|
[build-system]
|
|
23
26
|
requires = ["poetry>=0.12"]
|
|
@@ -11,14 +11,16 @@ package_data = \
|
|
|
11
11
|
{'': ['*']}
|
|
12
12
|
|
|
13
13
|
install_requires = \
|
|
14
|
-
['
|
|
14
|
+
['aiodns>=3,<4',
|
|
15
|
+
'aiohttp>=3.7,<4.0',
|
|
16
|
+
'cchardet>=2.1,<3.0',
|
|
15
17
|
'inflection>=0.5,<0.6',
|
|
16
18
|
'python-dotenv>=0.16,<0.17',
|
|
17
19
|
'requests[secure]>=2.25,<3.0']
|
|
18
20
|
|
|
19
21
|
setup_kwargs = {
|
|
20
22
|
'name': 'syneto-api',
|
|
21
|
-
'version': '0.2.
|
|
23
|
+
'version': '0.2.24',
|
|
22
24
|
'description': 'Syneto Client API library',
|
|
23
25
|
'long_description': '# Syneto API\n\nSyneto Client API library: authentication, storage, virtualization and protection\n\n# Installation\n\n```\n$ pip install syneto-api\n```\n\n# Basic Usage\n\n```\nfrom syneto_api import Authentication, Virtualization, Storage, Protection\n\nauth_api = Authentication(url_base="https://syneto-instance-ip-address/api/auth", insecure_ssl=True)\nresponse = auth_api.login(username="admin", password="admin")\njwt = response[\'jwt\']\n\nvirt_api = Virtualization(url_base="https://syneto-instance-ip-address/api/virtualization", insecure_ssl=True)\nvirt_api.set_auth_jwt(jwt)\nprint(virt_api.get_vms())\n\nstorage_api = Storage(url_base="https://syneto-instance-ip-address/api/storage", insecure_ssl=True)\nstorage_api.set_auth_jwt(jwt)\nprint(storage_api.get_pools())\n```\n\n# Environment Variables\n\nFor conveninence, the base urls for the api endpoints are also accepted as environment variables, please see below.\n\n```\nAUTH_SERVICE=https://syneto-instance-ip-address/api/auth\nVIRTUALIZATION_SERVICE=https://syneto-instance-ip-address/api/virtualization\nSTORAGE_SERVICE=https://syneto-instance-ip-address/api/storage\nPROTECTION_SERVICE=https://syneto-instance-ip-address/api/protection\n```\n\nIf you are using self-signed SSL certificates, set the following env. so that the http request library does not perform ssl verification. \n\n```\nALLOW_INSECURE_SSL=True\n```\n',
|
|
24
26
|
'author': 'Your Name',
|
|
@@ -5,8 +5,10 @@ import requests
|
|
|
5
5
|
import urllib.parse
|
|
6
6
|
import logging
|
|
7
7
|
import traceback
|
|
8
|
+
import urllib.parse
|
|
8
9
|
|
|
9
10
|
from .settings import get_env_flag
|
|
11
|
+
from .keyring import KeyRing
|
|
10
12
|
|
|
11
13
|
logger = logging.getLogger(__name__)
|
|
12
14
|
|
|
@@ -55,8 +57,13 @@ class APIClientBase:
|
|
|
55
57
|
self.url_base = f"{default_protocol}://{self.url_base}"
|
|
56
58
|
|
|
57
59
|
self.url_base = self.url_base.rstrip("/")
|
|
58
|
-
|
|
59
60
|
self.headers = {"accept": "application/json"}
|
|
61
|
+
parsed_base_url = urllib.parse.urlparse(self.url_base)
|
|
62
|
+
keyring = KeyRing(parsed_base_url.netloc)
|
|
63
|
+
JWT_TOKEN = keyring.get_token()
|
|
64
|
+
if JWT_TOKEN is not None:
|
|
65
|
+
self.headers.update({"Authorization": f"Bearer {JWT_TOKEN}"})
|
|
66
|
+
|
|
60
67
|
self._verify_ssl_cert = True
|
|
61
68
|
self._force_json = True
|
|
62
69
|
if insecure_ssl is not None:
|
|
@@ -66,6 +73,9 @@ class APIClientBase:
|
|
|
66
73
|
|
|
67
74
|
self._use_asyncio = True
|
|
68
75
|
|
|
76
|
+
def async(self, use_async=True):
|
|
77
|
+
self._use_asyncio = use_async
|
|
78
|
+
|
|
69
79
|
def _request(
|
|
70
80
|
self,
|
|
71
81
|
method: str,
|
|
@@ -82,14 +92,17 @@ class APIClientBase:
|
|
|
82
92
|
if not success_codes:
|
|
83
93
|
success_codes = APIClientBase.SUCCESS_CODES[method]
|
|
84
94
|
|
|
95
|
+
if self._use_asyncio and is_event_loop_available():
|
|
96
|
+
return self._async_request(method, url, body, success_codes, error_subject)
|
|
97
|
+
else:
|
|
98
|
+
return self._sync_request(method, url, body, success_codes, error_subject)
|
|
99
|
+
|
|
100
|
+
async def _async_request(self, method: str, url: str, body: dict, success_codes: list, error_subject: str):
|
|
85
101
|
try:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else:
|
|
91
|
-
logger.debug(f"Calling {method} {url} with requests lib")
|
|
92
|
-
return self._sync_request(method, url, body, success_codes, error_subject)
|
|
102
|
+
logger.debug(f"Calling {method} {url} with aiohttp lib")
|
|
103
|
+
# loop = asyncio.get_event_loop()
|
|
104
|
+
# return loop.run_until_complete(self._async_request(method, url, body, success_codes, error_subject))
|
|
105
|
+
return await self._unsafe_async_request(method, url, body, success_codes, error_subject)
|
|
93
106
|
|
|
94
107
|
except APIException as e:
|
|
95
108
|
raise
|
|
@@ -97,29 +110,29 @@ class APIClientBase:
|
|
|
97
110
|
except (aiohttp.ServerTimeoutError, requests.Timeout) as e:
|
|
98
111
|
logger.warning(f"Failed {method} request to {url}, network timeout: {str(e)}")
|
|
99
112
|
error = self._append_url_if_not_local_service("network timeout")
|
|
100
|
-
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
113
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body,) from e
|
|
101
114
|
|
|
102
115
|
except (aiohttp.ClientSSLError, requests.exceptions.SSLError) as e:
|
|
103
116
|
logger.warning(f"Failed {method} request to {url}, ssl error: {str(e)}")
|
|
104
117
|
error = self._append_url_if_not_local_service("ssl error")
|
|
105
|
-
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
118
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body,) from e
|
|
106
119
|
|
|
107
120
|
except (aiohttp.ClientConnectionError, requests.ConnectionError) as e:
|
|
108
121
|
logger.warning(f"Failed {method} request to {url}, connect error: {str(e)}")
|
|
109
122
|
error = self._append_url_if_not_local_service("network connect error")
|
|
110
|
-
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
123
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body,) from e
|
|
111
124
|
|
|
112
125
|
except (aiohttp.ClientError, requests.RequestException) as e:
|
|
113
126
|
logger.warning(f"Failed {method} request to {url}, network error: {str(e)}")
|
|
114
127
|
error = self._append_url_if_not_local_service("network error")
|
|
115
|
-
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
128
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body,) from e
|
|
116
129
|
|
|
117
130
|
except Exception as e:
|
|
118
131
|
logger.warning(f"Failed {method} request to {url}: {str(e)}")
|
|
119
132
|
error = self._append_url_if_not_local_service(str(e) or type(e).__name__)
|
|
120
|
-
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
133
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body,) from e
|
|
121
134
|
|
|
122
|
-
async def
|
|
135
|
+
async def _unsafe_async_request(self, method: str, url: str, body: dict, success_codes: list, error_subject: str):
|
|
123
136
|
headers = None
|
|
124
137
|
if self.headers is not None and len(self.headers) > 0:
|
|
125
138
|
headers = self.headers
|
|
@@ -160,6 +173,39 @@ class APIClientBase:
|
|
|
160
173
|
)
|
|
161
174
|
|
|
162
175
|
def _sync_request(self, method: str, url: str, body: dict, success_codes: list, error_subject: str):
|
|
176
|
+
try:
|
|
177
|
+
logger.debug(f"Calling {method} {url} with requests lib")
|
|
178
|
+
return self._unsafe_sync_request(method, url, body, success_codes, error_subject)
|
|
179
|
+
|
|
180
|
+
except APIException as e:
|
|
181
|
+
raise
|
|
182
|
+
|
|
183
|
+
except (aiohttp.ServerTimeoutError, requests.Timeout) as e:
|
|
184
|
+
logger.warning(f"Failed {method} request to {url}, network timeout: {str(e)}")
|
|
185
|
+
error = self._append_url_if_not_local_service("network timeout")
|
|
186
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
187
|
+
|
|
188
|
+
except (aiohttp.ClientSSLError, requests.exceptions.SSLError) as e:
|
|
189
|
+
logger.warning(f"Failed {method} request to {url}, ssl error: {str(e)}")
|
|
190
|
+
error = self._append_url_if_not_local_service("ssl error")
|
|
191
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
192
|
+
|
|
193
|
+
except (aiohttp.ClientConnectionError, requests.ConnectionError) as e:
|
|
194
|
+
logger.warning(f"Failed {method} request to {url}, connect error: {str(e)}")
|
|
195
|
+
error = self._append_url_if_not_local_service("network connect error")
|
|
196
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
197
|
+
|
|
198
|
+
except (aiohttp.ClientError, requests.RequestException) as e:
|
|
199
|
+
logger.warning(f"Failed {method} request to {url}, network error: {str(e)}")
|
|
200
|
+
error = self._append_url_if_not_local_service("network error")
|
|
201
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.warning(f"Failed {method} request to {url}: {str(e)}")
|
|
205
|
+
error = self._append_url_if_not_local_service(str(e) or type(e).__name__)
|
|
206
|
+
raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
|
|
207
|
+
|
|
208
|
+
def _unsafe_sync_request(self, method: str, url: str, body: dict, success_codes: list, error_subject: str):
|
|
163
209
|
method_func = self._get_method_func(method)
|
|
164
210
|
method_args = {"verify": self._verify_ssl_cert}
|
|
165
211
|
if self.headers is not None and len(self.headers) > 0:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from .api_client import APIClientBase
|
|
3
|
+
from .keyring import KeyRing
|
|
4
|
+
import urllib.parse
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
class Authentication(APIClientBase):
|
|
@@ -8,7 +10,13 @@ class Authentication(APIClientBase):
|
|
|
8
10
|
|
|
9
11
|
def login(self, username: str, password: str):
|
|
10
12
|
body = {"username": username, "password": password}
|
|
11
|
-
|
|
13
|
+
response = self.post_request("/login", body=body)
|
|
14
|
+
if "jwt" in response:
|
|
15
|
+
token = response["jwt"]
|
|
16
|
+
parsed_base_url = urllib.parse.urlparse(self.url_base)
|
|
17
|
+
keyring = KeyRing(parsed_base_url.netloc)
|
|
18
|
+
keyring.set_token(token)
|
|
19
|
+
return response
|
|
12
20
|
|
|
13
21
|
def get_public_key(self, username: str):
|
|
14
22
|
return self.get_request("/public-key", query_args={"username": username})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Multiton(type):
|
|
2
|
+
__instances = {}
|
|
3
|
+
|
|
4
|
+
def __call__(cls, url: str, *args, **kwds):
|
|
5
|
+
if url not in cls.__instances:
|
|
6
|
+
cls.__instances[url] = super(Multiton, cls).__call__(*args, **kwds)
|
|
7
|
+
return cls.__instances[url]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class KeyRing(metaclass=Multiton):
|
|
11
|
+
__JWT_TOKEN = None
|
|
12
|
+
|
|
13
|
+
def set_token(self, token: str):
|
|
14
|
+
self.__JWT_TOKEN = token
|
|
15
|
+
|
|
16
|
+
def get_token(self):
|
|
17
|
+
return self.__JWT_TOKEN
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|