oscar-python 1.2.0__tar.gz → 1.3.0__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.
- {oscar_python-1.2.0 → oscar_python-1.3.0}/PKG-INFO +21 -2
- {oscar_python-1.2.0 → oscar_python-1.3.0}/README.md +3 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_providers/_minio.py +4 -3
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_providers/_onedata.py +16 -13
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_providers/_providers_base.py +4 -3
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_providers/_s3.py +15 -13
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_providers/_webdav.py +7 -7
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/_utils.py +26 -20
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/client.py +28 -22
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/client_anon.py +4 -3
- oscar_python-1.3.0/oscar_python/default_client.py +39 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/local_test.py +1 -1
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/storage.py +14 -14
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/PKG-INFO +22 -3
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/SOURCES.txt +8 -1
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/requires.txt +1 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/top_level.txt +1 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/setup.py +3 -2
- oscar_python-1.3.0/tests/test_client.py +129 -0
- oscar_python-1.3.0/tests/test_default_client.py +61 -0
- oscar_python-1.3.0/tests/test_onedata.py +66 -0
- oscar_python-1.3.0/tests/test_s3.py +48 -0
- oscar_python-1.3.0/tests/test_storage.py +51 -0
- oscar_python-1.3.0/tests/test_utils.py +94 -0
- oscar_python-1.3.0/tests/test_webdav.py +50 -0
- oscar_python-1.2.0/oscar_python/default_client.py +0 -35
- {oscar_python-1.2.0 → oscar_python-1.3.0}/LICENSE +0 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python/__init__.py +0 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/dependency_links.txt +0 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/oscar_python.egg-info/not-zip-safe +0 -0
- {oscar_python-1.2.0 → oscar_python-1.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: oscar_python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: OSCAR API for python
|
|
5
5
|
Home-page: https://github.com/grycap/oscar_python
|
|
6
6
|
Author: GRyCAP - Universitat Politecnica de Valencia
|
|
@@ -10,6 +10,22 @@ Classifier: Programming Language :: Python :: 3
|
|
|
10
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
|
+
Requires-Dist: webdavclient3==3.14.6
|
|
14
|
+
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: boto3
|
|
16
|
+
Requires-Dist: setuptools>=40.8.0
|
|
17
|
+
Requires-Dist: pyyaml
|
|
18
|
+
Requires-Dist: aiohttp
|
|
19
|
+
Requires-Dist: liboidcagent
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: license
|
|
27
|
+
Dynamic: requires-dist
|
|
28
|
+
Dynamic: summary
|
|
13
29
|
|
|
14
30
|
## Python OSCAR client
|
|
15
31
|
|
|
@@ -170,6 +186,9 @@ response = client.remove_service("service_name") # returns an http response
|
|
|
170
186
|
``` python
|
|
171
187
|
# make a synchronous execution
|
|
172
188
|
response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
|
|
189
|
+
|
|
190
|
+
# make an asynchronous execution
|
|
191
|
+
response = client.run_service("service_name", input="input", async_call=True) # returns an http response
|
|
173
192
|
```
|
|
174
193
|
|
|
175
194
|
#### Logs methods
|
|
@@ -157,6 +157,9 @@ response = client.remove_service("service_name") # returns an http response
|
|
|
157
157
|
``` python
|
|
158
158
|
# make a synchronous execution
|
|
159
159
|
response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
|
|
160
|
+
|
|
161
|
+
# make an asynchronous execution
|
|
162
|
+
response = client.run_service("service_name", input="input", async_call=True) # returns an http response
|
|
160
163
|
```
|
|
161
164
|
|
|
162
165
|
#### Logs methods
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import boto3
|
|
16
16
|
from oscar_python._providers._s3 import S3
|
|
17
17
|
|
|
18
18
|
_DEFAULT_MINIO_ENDPOINT = 'http://minio-service.minio:9000'
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
class Minio(S3):
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
def _get_client(self, c):
|
|
23
24
|
"""Return Minio client with user configuration."""
|
|
24
25
|
if c["endpoint"] == '':
|
|
@@ -30,4 +31,4 @@ class Minio(S3):
|
|
|
30
31
|
region_name=c["region"],
|
|
31
32
|
verify=c["verify"],
|
|
32
33
|
aws_access_key_id=c["access_key"],
|
|
33
|
-
aws_secret_access_key=c["secret_key"])
|
|
34
|
+
aws_secret_access_key=c["secret_key"])
|
|
@@ -10,29 +10,30 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from oscar_python._providers._providers_base import StorageProvider
|
|
16
16
|
import requests
|
|
17
17
|
import json
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
# TODO fix error returns
|
|
20
21
|
class Onedata(StorageProvider):
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
_CDMI_PATH = '/cdmi'
|
|
23
24
|
_CDMI_VERSION_HEADER = {'X-CDMI-Specification-Version': '1.1.1'}
|
|
24
25
|
|
|
25
26
|
def __init__(self, credentials) -> None:
|
|
26
27
|
super().__init__()
|
|
27
28
|
self._get_client(credentials)
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
def _get_client(self, c):
|
|
30
31
|
self.oneprovider_space = c["space"]
|
|
31
32
|
self.oneprovider_host = c["oneprovider_host"]
|
|
32
33
|
self.headers = {'X-Auth-Token': c["token"]}
|
|
33
34
|
self.url = (f'https://{self.oneprovider_host}{self._CDMI_PATH}/'
|
|
34
35
|
f'{self.oneprovider_space}/')
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
def upload_file(self, local_path, remote_path):
|
|
37
38
|
file_name = local_path.split('/')[-1]
|
|
38
39
|
if not self._folder_exists(remote_path):
|
|
@@ -57,12 +58,13 @@ class Onedata(StorageProvider):
|
|
|
57
58
|
file_name = remote_path.split('/')[-1]
|
|
58
59
|
url = self.url+remote_path
|
|
59
60
|
res = requests.get(url=url, headers=self.headers)
|
|
60
|
-
if res.status_code == 200:
|
|
61
|
+
if res.status_code == 200:
|
|
62
|
+
print("Saving file to {0}/{1}".format(local_path, file_name))
|
|
61
63
|
self._save_file(local_path+"/"+file_name, res.content, mode='wb')
|
|
62
|
-
|
|
64
|
+
|
|
63
65
|
def list_files_from_path(self, path):
|
|
64
66
|
headers = {**self._CDMI_VERSION_HEADER, **self.headers}
|
|
65
|
-
return requests.get(url
|
|
67
|
+
return requests.get(url=self.url+path+"/", headers=headers)
|
|
66
68
|
|
|
67
69
|
def _save_file(self, path, content, mode='w'):
|
|
68
70
|
with open(path, mode) as fwc:
|
|
@@ -72,12 +74,13 @@ class Onedata(StorageProvider):
|
|
|
72
74
|
|
|
73
75
|
def _folder_exists(self, folder_name):
|
|
74
76
|
headers = {**self._CDMI_VERSION_HEADER, **self.headers}
|
|
75
|
-
response = requests.get(url
|
|
77
|
+
response = requests.get(url=self.url+folder_name+"/", headers=headers)
|
|
76
78
|
if response.status_code == 200:
|
|
77
79
|
return True
|
|
78
80
|
return False
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
def _create_folder(self, folder_name):
|
|
81
|
-
response = requests.put(url
|
|
82
|
-
if response.status_code
|
|
83
|
-
return
|
|
83
|
+
response = requests.put(url=self.url+folder_name+"/", headers=self.headers)
|
|
84
|
+
if response.status_code == 201:
|
|
85
|
+
return True
|
|
86
|
+
return False
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import abc
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
class StorageProvider(abc.ABC):
|
|
18
19
|
@abc.abstractmethod
|
|
19
20
|
def download_file(self, local_path, remote_path):
|
|
@@ -22,11 +23,11 @@ class StorageProvider(abc.ABC):
|
|
|
22
23
|
@abc.abstractmethod
|
|
23
24
|
def upload_file(self, local_path, remote_path):
|
|
24
25
|
"""Generic method to be implemented by all the storage providers."""
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
@abc.abstractmethod
|
|
27
28
|
def list_files_from_path(self, path):
|
|
28
29
|
"""Generic method to be implemented by all the storage providers."""
|
|
29
30
|
|
|
30
31
|
@abc.abstractmethod
|
|
31
32
|
def _get_client(self):
|
|
32
|
-
"""Generic method to be implemented by all the storage providers."""
|
|
33
|
+
"""Generic method to be implemented by all the storage providers."""
|
|
@@ -10,19 +10,19 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from aiohttp import ClientError
|
|
16
|
-
import logging
|
|
17
16
|
import boto3
|
|
18
17
|
from oscar_python._providers._providers_base import StorageProvider
|
|
19
18
|
|
|
19
|
+
|
|
20
20
|
class S3(StorageProvider):
|
|
21
21
|
def __init__(self, credentials) -> None:
|
|
22
22
|
super().__init__()
|
|
23
23
|
self.client = self._get_client(credentials)
|
|
24
24
|
self.resource = self._get_resource(credentials)
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
def _get_client(self, c):
|
|
27
27
|
"""Returns S3 client with default configuration."""
|
|
28
28
|
if c is None:
|
|
@@ -42,39 +42,41 @@ class S3(StorageProvider):
|
|
|
42
42
|
if region == '':
|
|
43
43
|
region = None
|
|
44
44
|
return boto3.resource('s3',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
region_name=region,
|
|
46
|
+
aws_access_key_id=c["access_key"],
|
|
47
|
+
aws_secret_access_key=c["secret_key"])
|
|
48
48
|
|
|
49
49
|
def list_files_from_path(self, path):
|
|
50
50
|
prefix = ""
|
|
51
51
|
path_split = path.split('/')
|
|
52
52
|
bucket = path_split[0]
|
|
53
53
|
if len(path_split) > 1:
|
|
54
|
-
prefix = path.split('/',2)[1]
|
|
55
|
-
print("Reading content from path:",path)
|
|
54
|
+
prefix = path.split('/', 2)[1]
|
|
55
|
+
print("Reading content from path:", path)
|
|
56
56
|
return self.client.list_objects(Bucket=bucket, Prefix=prefix)
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
def upload_file(self, local_path, remote_path):
|
|
59
59
|
bucket_name = remote_path.split('/')[0]
|
|
60
|
-
file_key = remote_path.split('/',1)[1]
|
|
60
|
+
file_key = remote_path.split('/', 1)[1]
|
|
61
61
|
file_name = local_path.split('/')[-1]
|
|
62
62
|
print("Uploading to bucket '{0}' with key '{1}'".format(bucket_name,file_key))
|
|
63
63
|
with open(local_path, 'rb') as data:
|
|
64
64
|
try:
|
|
65
|
-
self.client.upload_fileobj(data, bucket_name, file_key+"/"+file_name)
|
|
65
|
+
self.client.upload_fileobj(data, bucket_name, file_key + "/" + file_name)
|
|
66
|
+
return True
|
|
66
67
|
except ClientError as err:
|
|
67
68
|
print("Error uploading file: ", err)
|
|
68
69
|
return False
|
|
69
70
|
|
|
70
71
|
def download_file(self, local_path, remote_path):
|
|
71
72
|
bucket_name = remote_path.split('/')[0]
|
|
72
|
-
file_key = remote_path.split('/',1)[1]
|
|
73
|
+
file_key = remote_path.split('/', 1)[1]
|
|
73
74
|
file_path = local_path+"/"+remote_path.split('/')[-1]
|
|
74
75
|
print("Downloading from bucket '{0}' to path '{1}' with key '{2}'".format(bucket_name, file_path, file_key))
|
|
75
76
|
with open(file_path, 'wb') as data:
|
|
76
77
|
try:
|
|
77
78
|
self.client.download_fileobj(bucket_name, file_key, data)
|
|
79
|
+
return True
|
|
78
80
|
except ClientError as err:
|
|
79
81
|
print("Error downloading file: ", err)
|
|
80
|
-
return False
|
|
82
|
+
return False
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from oscar_python._providers._providers_base import StorageProvider
|
|
16
16
|
from webdav3.client import Client
|
|
17
17
|
from webdav3.exceptions import WebDavException
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
class WebDav(StorageProvider):
|
|
20
21
|
def __init__(self, credentials) -> None:
|
|
21
22
|
self.client = self._get_client(credentials)
|
|
@@ -26,9 +27,9 @@ class WebDav(StorageProvider):
|
|
|
26
27
|
if 'https://' not in hostname:
|
|
27
28
|
hostname = 'https://'+hostname
|
|
28
29
|
options = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
'webdav_hostname': hostname,
|
|
31
|
+
'webdav_login': c["login"],
|
|
32
|
+
'webdav_password': c["password"]
|
|
32
33
|
}
|
|
33
34
|
return Client(options=options)
|
|
34
35
|
|
|
@@ -45,13 +46,12 @@ class WebDav(StorageProvider):
|
|
|
45
46
|
except WebDavException as exception:
|
|
46
47
|
print("error uploading file to path '{0}': {1}".format(remote_path+"/"+file_name, exception))
|
|
47
48
|
|
|
48
|
-
|
|
49
49
|
def download_file(self, local_path, remote_path):
|
|
50
50
|
file_name = remote_path.split('/')[-1]
|
|
51
51
|
try:
|
|
52
52
|
self.client.download_sync(remote_path, local_path+"/"+file_name)
|
|
53
53
|
except WebDavException as exception:
|
|
54
54
|
print("error downloading file from path '{0}': {1}".format(remote_path, exception))
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
def list_files_from_path(self, path):
|
|
57
|
-
return self.client.list(path)
|
|
57
|
+
return self.client.list(path)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
13
|
+
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import base64
|
|
16
16
|
import os
|
|
@@ -18,18 +18,19 @@ import requests
|
|
|
18
18
|
import liboidcagent as agent
|
|
19
19
|
_DEFAULT_TIMEOUT = 60
|
|
20
20
|
|
|
21
|
-
""" Generic http request """
|
|
22
|
-
def make_request(c , path, method, **kwargs):
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
def make_request(c, path, method, **kwargs):
|
|
23
|
+
""" Generic http request """
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
headers = get_headers(c)
|
|
26
|
+
|
|
27
|
+
if "timeout" in kwargs.keys() and kwargs["timeout"]:
|
|
27
28
|
timeout = kwargs["timeout"]
|
|
28
|
-
else:
|
|
29
|
+
else:
|
|
29
30
|
timeout = _DEFAULT_TIMEOUT
|
|
30
31
|
|
|
31
32
|
url = c.endpoint+path
|
|
32
|
-
|
|
33
|
+
|
|
33
34
|
if method in ["post", "put"]:
|
|
34
35
|
if "token" in kwargs.keys() and kwargs["token"]:
|
|
35
36
|
headers = get_headers_with_token(kwargs["token"])
|
|
@@ -40,37 +41,42 @@ def make_request(c , path, method, **kwargs):
|
|
|
40
41
|
|
|
41
42
|
if "handle" in kwargs.keys() and kwargs["handle"] == False:
|
|
42
43
|
return result
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
result.raise_for_status()
|
|
45
46
|
return result
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
def get_headers(c):
|
|
50
|
+
""" Function to generate headers with basic authentication or OIDC """
|
|
49
51
|
if c._AUTH_TYPE == "basicauth":
|
|
50
|
-
usr_pass_as_bytes = bytes(c.user+":"+c.password,"utf-8")
|
|
52
|
+
usr_pass_as_bytes = bytes(c.user + ":" + c.password, "utf-8")
|
|
51
53
|
usr_pass_base_64 = base64.b64encode(usr_pass_as_bytes).decode("utf-8")
|
|
52
|
-
return {"Authorization": "Basic "+ usr_pass_base_64}
|
|
54
|
+
return {"Authorization": "Basic " + usr_pass_base_64}
|
|
53
55
|
if c._AUTH_TYPE == "oidc-agent":
|
|
54
56
|
token = agent.get_access_token(c.shortname)
|
|
55
57
|
return get_headers_with_token(token)
|
|
56
58
|
if c._AUTH_TYPE == "oidc":
|
|
57
59
|
return get_headers_with_token(c.oidc_token)
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
|
|
60
62
|
def get_headers_with_token(token):
|
|
61
|
-
|
|
63
|
+
""" Function to generate headers with token auth """
|
|
64
|
+
return {"Authorization": "Bearer " + str(token)}
|
|
65
|
+
|
|
62
66
|
|
|
63
67
|
def write_text_file(content, file_path):
|
|
64
68
|
with open(file_path, 'w') as f:
|
|
65
69
|
f.write(content)
|
|
66
70
|
|
|
71
|
+
|
|
67
72
|
def isBase64(st):
|
|
68
73
|
try:
|
|
69
74
|
base64.b64decode(st)
|
|
70
75
|
return True
|
|
71
|
-
except:
|
|
76
|
+
except Exception:
|
|
72
77
|
return False
|
|
73
78
|
|
|
79
|
+
|
|
74
80
|
def decode_b64(b64_str, file_out):
|
|
75
81
|
file_extension = os.path.splitext(file_out)[1]
|
|
76
82
|
try:
|
|
@@ -80,7 +86,7 @@ def decode_b64(b64_str, file_out):
|
|
|
80
86
|
decode = 'w'
|
|
81
87
|
decoded_data = decoded_data.decode("utf-8")
|
|
82
88
|
else:
|
|
83
|
-
decode = 'wb'
|
|
89
|
+
decode = 'wb'
|
|
84
90
|
|
|
85
91
|
with open(file_out, decode) as f:
|
|
86
92
|
f.write(decoded_data)
|
|
@@ -90,6 +96,7 @@ def decode_b64(b64_str, file_out):
|
|
|
90
96
|
except OSError:
|
|
91
97
|
print('Error decoding output: Failed to write decoded data to file.')
|
|
92
98
|
|
|
99
|
+
|
|
93
100
|
def encode_input(data):
|
|
94
101
|
if os.path.isfile(data):
|
|
95
102
|
try:
|
|
@@ -103,12 +110,11 @@ def encode_input(data):
|
|
|
103
110
|
message_bytes = data.encode('ascii')
|
|
104
111
|
return base64.b64encode(message_bytes)
|
|
105
112
|
|
|
113
|
+
|
|
106
114
|
def decode_output(output, file_path):
|
|
107
|
-
if
|
|
115
|
+
if isBase64(output):
|
|
108
116
|
decode_b64(output, file_path)
|
|
109
117
|
return
|
|
110
|
-
if
|
|
111
|
-
write_text_file(output,file_path)
|
|
118
|
+
if isinstance(output, str):
|
|
119
|
+
write_text_file(output, file_path)
|
|
112
120
|
return
|
|
113
|
-
|
|
114
|
-
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
12
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
|
-
# limitations under the License.
|
|
14
|
+
# limitations under the License.
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
18
|
import yaml
|
|
19
19
|
import liboidcagent as agent
|
|
20
20
|
import oscar_python._utils as utils
|
|
21
|
-
from default_client import DefaultClient
|
|
21
|
+
from oscar_python.default_client import DefaultClient
|
|
22
22
|
from oscar_python.storage import Storage
|
|
23
23
|
|
|
24
24
|
_INFO_PATH = "/system/info"
|
|
@@ -26,7 +26,10 @@ _CONFIG_PATH = "/system/config"
|
|
|
26
26
|
_SVC_PATH = "/system/services"
|
|
27
27
|
_LOGS_PATH = "/system/logs"
|
|
28
28
|
_RUN_PATH = "/run"
|
|
29
|
-
|
|
29
|
+
_STATUS_PATH="/system/status"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# _JOB_PATH = "/job"
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
_GET = "get"
|
|
@@ -34,8 +37,9 @@ _POST = "post"
|
|
|
34
37
|
_PUT = "put"
|
|
35
38
|
_DELETE = "delete"
|
|
36
39
|
|
|
40
|
+
|
|
37
41
|
class Client(DefaultClient):
|
|
38
|
-
#Cluster info
|
|
42
|
+
# Cluster info
|
|
39
43
|
def __init__(self, options) -> None:
|
|
40
44
|
self.set_auth_type(options)
|
|
41
45
|
if self._AUTH_TYPE == 'basicauth':
|
|
@@ -57,7 +61,7 @@ class Client(DefaultClient):
|
|
|
57
61
|
self.endpoint = options['endpoint']
|
|
58
62
|
self.shortname = options['shortname']
|
|
59
63
|
self.ssl = bool(options['ssl'])
|
|
60
|
-
|
|
64
|
+
|
|
61
65
|
def oidc_client(self, options):
|
|
62
66
|
self.id = options['cluster_id']
|
|
63
67
|
self.endpoint = options['endpoint']
|
|
@@ -78,7 +82,7 @@ class Client(DefaultClient):
|
|
|
78
82
|
else:
|
|
79
83
|
raise ValueError("Unrecognized authentication credentials in options")
|
|
80
84
|
|
|
81
|
-
""" Creates a generic storage client to interact with the storage providers
|
|
85
|
+
""" Creates a generic storage client to interact with the storage providers
|
|
82
86
|
defined on a specific service of the refered OSCAR cluster """
|
|
83
87
|
def create_storage_client(self, svc):
|
|
84
88
|
return Storage(
|
|
@@ -96,7 +100,7 @@ class Client(DefaultClient):
|
|
|
96
100
|
""" List all services from the current cluster """
|
|
97
101
|
def list_services(self):
|
|
98
102
|
return utils.make_request(self, _SVC_PATH, _GET)
|
|
99
|
-
|
|
103
|
+
|
|
100
104
|
""" Retreive a specific service """
|
|
101
105
|
def get_service(self, name):
|
|
102
106
|
return utils.make_request(self, _SVC_PATH+"/"+name, _GET)
|
|
@@ -111,15 +115,15 @@ class Client(DefaultClient):
|
|
|
111
115
|
try:
|
|
112
116
|
svc = element[self.id]
|
|
113
117
|
except KeyError as err:
|
|
114
|
-
raise("FDL clusterID does not match current clusterID: {0}".format(err))
|
|
118
|
+
raise Exception("FDL clusterID does not match current clusterID: {0}".format(err))
|
|
115
119
|
try:
|
|
116
120
|
with open(svc["script"]) as s:
|
|
117
121
|
svc["script"] = s.read()
|
|
118
|
-
except IOError
|
|
119
|
-
raise("Couldn't read script")
|
|
120
|
-
|
|
122
|
+
except IOError:
|
|
123
|
+
raise Exception("Couldn't read script")
|
|
124
|
+
|
|
121
125
|
# cpu parameter has to be string on the request
|
|
122
|
-
if type(svc["cpu"]) is int or type(svc["cpu"]) is float: svc["cpu"]= str(svc["cpu"])
|
|
126
|
+
if type(svc["cpu"]) is int or type(svc["cpu"]) is float: svc["cpu"] = str(svc["cpu"])
|
|
123
127
|
|
|
124
128
|
except ValueError as err:
|
|
125
129
|
print(err)
|
|
@@ -127,7 +131,11 @@ class Client(DefaultClient):
|
|
|
127
131
|
else:
|
|
128
132
|
raise ValueError("Bad yaml format: {0}".format(fdl))
|
|
129
133
|
return svc
|
|
130
|
-
|
|
134
|
+
|
|
135
|
+
""" Get status of a cluster (CPU and Memory) """
|
|
136
|
+
def get_cluster_status(self):
|
|
137
|
+
return utils.make_request(self, _STATUS_PATH, _GET)
|
|
138
|
+
|
|
131
139
|
""" Make the request to create a new service """
|
|
132
140
|
def _apply_service(self, svc, method):
|
|
133
141
|
# Check if service already exists when the function is called from create_service
|
|
@@ -136,18 +144,16 @@ class Client(DefaultClient):
|
|
|
136
144
|
if svc_exists.status_code == 200:
|
|
137
145
|
raise ValueError("A service with name '{0}' is already present on the cluster".format(svc["name"]))
|
|
138
146
|
return utils.make_request(self, _SVC_PATH, method, data=json.dumps(svc))
|
|
139
|
-
|
|
147
|
+
|
|
140
148
|
""" Create a service on the current cluster from a FDL file or a JSON definition """
|
|
141
149
|
def create_service(self, service_definition):
|
|
142
150
|
if type(service_definition) is dict:
|
|
143
151
|
return self._apply_service(service_definition, _POST)
|
|
144
152
|
if os.path.isfile(service_definition):
|
|
145
|
-
|
|
146
|
-
service = self._check_fdl_definition(service_definition)
|
|
147
|
-
except Exception:
|
|
148
|
-
raise
|
|
153
|
+
service = self._check_fdl_definition(service_definition)
|
|
149
154
|
return self._apply_service(service, _POST)
|
|
150
|
-
|
|
155
|
+
raise ValueError("Service definition must be a dictionary or a file path")
|
|
156
|
+
|
|
151
157
|
""" Update a specific service from a FDL file or a JSON definition """
|
|
152
158
|
def update_service(self, name, new_service):
|
|
153
159
|
# Check if service exists before update
|
|
@@ -158,7 +164,7 @@ class Client(DefaultClient):
|
|
|
158
164
|
return self._apply_service(new_service, _PUT)
|
|
159
165
|
if os.path.isfile(new_service):
|
|
160
166
|
try:
|
|
161
|
-
|
|
167
|
+
service = self._check_fdl_definition(new_service)
|
|
162
168
|
except Exception:
|
|
163
169
|
raise
|
|
164
170
|
return self._apply_service(service, _PUT)
|
|
@@ -178,11 +184,11 @@ class Client(DefaultClient):
|
|
|
178
184
|
except ValueError as err:
|
|
179
185
|
return err
|
|
180
186
|
return fdl_yaml
|
|
181
|
-
|
|
187
|
+
|
|
182
188
|
""" Get logs of a service job """
|
|
183
189
|
def get_job_logs(self, svc, job):
|
|
184
190
|
return utils.make_request(self, _LOGS_PATH+"/"+svc+"/"+job, _GET)
|
|
185
|
-
|
|
191
|
+
|
|
186
192
|
""" List a service jobs """
|
|
187
193
|
def list_jobs(self, svc):
|
|
188
194
|
return utils.make_request(self, _LOGS_PATH+"/"+svc, _GET)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from default_client import DefaultClient
|
|
1
|
+
from oscar_python.default_client import DefaultClient
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|
class AnonymousClient(DefaultClient):
|
|
4
|
-
#Cluster info
|
|
5
|
+
# Cluster info
|
|
5
6
|
def __init__(self, options) -> None:
|
|
6
7
|
self.id = options['cluster_id']
|
|
7
8
|
self.endpoint = options["endpoint"]
|
|
8
9
|
self.ssl = bool(options['ssl'])
|
|
9
|
-
self._AUTH_TYPE = "anon"
|
|
10
|
+
self._AUTH_TYPE = "anon"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import oscar_python._utils as utils
|
|
3
|
+
|
|
4
|
+
_RUN_PATH = "/run"
|
|
5
|
+
_JOB_PATH = "/job"
|
|
6
|
+
_POST = "post"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DefaultClient(metaclass=abc.ABCMeta):
|
|
10
|
+
|
|
11
|
+
_AUTH_TYPE = ''
|
|
12
|
+
|
|
13
|
+
""" Run an execution.
|
|
14
|
+
If async is set to True the execution is run asynchronously.
|
|
15
|
+
If an output is provided the result is decoded onto the file.
|
|
16
|
+
In both cases the function returns the HTTP response."""
|
|
17
|
+
def run_service(self, name, **kwargs):
|
|
18
|
+
if kwargs.get("token"):
|
|
19
|
+
token = kwargs["token"]
|
|
20
|
+
else:
|
|
21
|
+
token = self._get_token(name)
|
|
22
|
+
|
|
23
|
+
send_data = None
|
|
24
|
+
if kwargs.get("input"):
|
|
25
|
+
send_data = kwargs["input"]
|
|
26
|
+
if not isinstance(send_data, dict):
|
|
27
|
+
send_data = utils.encode_input(send_data)
|
|
28
|
+
|
|
29
|
+
path = _RUN_PATH + "/"+name
|
|
30
|
+
if kwargs.get("async_call"):
|
|
31
|
+
path = _JOB_PATH + "/" + name
|
|
32
|
+
|
|
33
|
+
response = utils.make_request(self, path, _POST, data=send_data,
|
|
34
|
+
token=token, timeout=kwargs.get("timeout"))
|
|
35
|
+
|
|
36
|
+
if kwargs.get("output"):
|
|
37
|
+
utils.decode_output(response.text, kwargs["output"])
|
|
38
|
+
|
|
39
|
+
return response
|