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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: syneto-api
3
- Version: 0.2.22
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: aiohttp[speedups] (>=3.7,<4.0)
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.22"
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 = {version = "^3.7", extras = ["speedups"]}
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
- ['aiohttp[speedups]>=3.7,<4.0',
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.22',
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
- if self._use_asyncio and is_event_loop_available():
87
- logger.debug(f"Calling {method} {url} with aiohttp lib")
88
- loop = asyncio.get_event_loop()
89
- return loop.run_until_complete(self._async_request(method, url, body, success_codes, error_subject))
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 _async_request(self, method: str, url: str, body: dict, success_codes: list, error_subject: str):
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
- return self.post_request("/login", body=body)
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