oscar-python 1.0.3__tar.gz → 1.1.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.
Files changed (22) hide show
  1. {oscar_python-1.0.3 → oscar_python-1.1.0}/PKG-INFO +11 -18
  2. {oscar_python-1.0.3 → oscar_python-1.1.0}/README.md +10 -17
  3. oscar_python-1.1.0/oscar_python/_utils.py +112 -0
  4. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/client.py +63 -16
  5. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/PKG-INFO +11 -18
  6. oscar_python-1.0.3/oscar_python/_utils.py +0 -47
  7. {oscar_python-1.0.3 → oscar_python-1.1.0}/LICENSE +0 -0
  8. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/__init__.py +0 -0
  9. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/_providers/_minio.py +0 -0
  10. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/_providers/_onedata.py +0 -0
  11. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/_providers/_providers_base.py +0 -0
  12. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/_providers/_s3.py +0 -0
  13. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/_providers/_webdav.py +0 -0
  14. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/local_test.py +0 -0
  15. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python/storage.py +0 -0
  16. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/SOURCES.txt +0 -0
  17. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/dependency_links.txt +0 -0
  18. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/not-zip-safe +0 -0
  19. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/requires.txt +0 -0
  20. {oscar_python-1.0.3 → oscar_python-1.1.0}/oscar_python.egg-info/top_level.txt +0 -0
  21. {oscar_python-1.0.3 → oscar_python-1.1.0}/setup.cfg +0 -0
  22. {oscar_python-1.0.3 → oscar_python-1.1.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oscar_python
3
- Version: 1.0.3
3
+ Version: 1.1.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
@@ -11,18 +11,18 @@ Classifier: License :: OSI Approved :: Apache Software License
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
 
14
- ## Python OSCAR API
14
+ ## Python OSCAR client
15
15
 
16
16
  [![Build](https://github.com/grycap/oscar_python/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
17
17
  ![PyPI](https://img.shields.io/pypi/v/oscar_python)
18
18
 
19
- This package provides an API to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
19
+ This package provides a client to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
20
20
 
21
21
  ### Contents
22
- - [Python OSCAR API](#python-oscar-api)
22
+ - [Python OSCAR client](#python-oscar-client)
23
23
  - [Contents](#contents)
24
24
  - [Sample usage](#sample-usage)
25
- - [API methods](#api-methods)
25
+ - [Client methods](#client-methods)
26
26
  - [Cluster methods](#cluster-methods)
27
27
  - [Service methods](#service-methods)
28
28
  - [Logs methods](#logs-methods)
@@ -54,14 +54,14 @@ client = Client("cluster-id","https://cluster-endpoint", "username", "password",
54
54
 
55
55
  try:
56
56
  client.create_service("/absolute_path/cowsay.yaml")
57
- res = client.run_service("cowsay", '{"message": "Hi there"}')
58
- if res.status_code == 200:
59
- print(res.text)
57
+ response = client.run_service("cowsay", input = '{"message": "Hi there"}')
58
+ if response.status_code == 200:
59
+ print(response.text)
60
60
  except Exception as err:
61
61
  print("Failed with: ", err)
62
62
  ```
63
63
 
64
- ### API methods
64
+ ### Client methods
65
65
 
66
66
  #### Cluster methods
67
67
 
@@ -113,12 +113,11 @@ response = client.remove_service("service_name") # returns an http response
113
113
 
114
114
  **run_service**
115
115
 
116
- The `input` parameter may not be passed if the function doesn't require input.
116
+ *`input`, `output` and `timeout` are optional parameters.*
117
117
 
118
118
  ``` python
119
119
  # make a synchronous execution
120
- response = client.run_service("service_name", input="input") # returns an http response
121
-
120
+ response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
122
121
  ```
123
122
 
124
123
  #### Logs methods
@@ -176,9 +175,3 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
176
175
  # download a file from a remote path to a local path
177
176
  response = storage_service.download_file("storage_provider", "local_path", "remote_path")
178
177
  ```
179
-
180
-
181
-
182
-
183
-
184
-
@@ -1,15 +1,15 @@
1
- ## Python OSCAR API
1
+ ## Python OSCAR client
2
2
 
3
3
  [![Build](https://github.com/grycap/oscar_python/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
4
4
  ![PyPI](https://img.shields.io/pypi/v/oscar_python)
5
5
 
6
- This package provides an API to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
6
+ This package provides a client to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
7
7
 
8
8
  ### Contents
9
- - [Python OSCAR API](#python-oscar-api)
9
+ - [Python OSCAR client](#python-oscar-client)
10
10
  - [Contents](#contents)
11
11
  - [Sample usage](#sample-usage)
12
- - [API methods](#api-methods)
12
+ - [Client methods](#client-methods)
13
13
  - [Cluster methods](#cluster-methods)
14
14
  - [Service methods](#service-methods)
15
15
  - [Logs methods](#logs-methods)
@@ -41,14 +41,14 @@ client = Client("cluster-id","https://cluster-endpoint", "username", "password",
41
41
 
42
42
  try:
43
43
  client.create_service("/absolute_path/cowsay.yaml")
44
- res = client.run_service("cowsay", '{"message": "Hi there"}')
45
- if res.status_code == 200:
46
- print(res.text)
44
+ response = client.run_service("cowsay", input = '{"message": "Hi there"}')
45
+ if response.status_code == 200:
46
+ print(response.text)
47
47
  except Exception as err:
48
48
  print("Failed with: ", err)
49
49
  ```
50
50
 
51
- ### API methods
51
+ ### Client methods
52
52
 
53
53
  #### Cluster methods
54
54
 
@@ -100,12 +100,11 @@ response = client.remove_service("service_name") # returns an http response
100
100
 
101
101
  **run_service**
102
102
 
103
- The `input` parameter may not be passed if the function doesn't require input.
103
+ *`input`, `output` and `timeout` are optional parameters.*
104
104
 
105
105
  ``` python
106
106
  # make a synchronous execution
107
- response = client.run_service("service_name", input="input") # returns an http response
108
-
107
+ response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
109
108
  ```
110
109
 
111
110
  #### Logs methods
@@ -163,9 +162,3 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
163
162
  # download a file from a remote path to a local path
164
163
  response = storage_service.download_file("storage_provider", "local_path", "remote_path")
165
164
  ```
166
-
167
-
168
-
169
-
170
-
171
-
@@ -0,0 +1,112 @@
1
+ # Copyright (C) GRyCAP - I3M - UPV
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import base64
16
+ import os
17
+ import requests
18
+ import liboidcagent as agent
19
+ _DEFAULT_TIMEOUT = 60
20
+
21
+ """ Generic http request """
22
+ def make_request(c , path, method, **kwargs):
23
+
24
+ if "timeout" in kwargs.keys() and kwargs["timeout"]:
25
+ timeout = kwargs["timeout"]
26
+ else:
27
+ timeout = _DEFAULT_TIMEOUT
28
+
29
+ url = c.endpoint+path
30
+ headers = get_headers(c)
31
+ if method in ["post", "put"]:
32
+ if "token" in kwargs.keys() and kwargs["token"]:
33
+ headers = get_headers_with_token(kwargs["token"])
34
+ if "data" in kwargs.keys() and kwargs["data"]:
35
+ result = requests.request(method, url, headers=headers, verify=c.ssl, data=kwargs["data"], timeout=timeout)
36
+ else:
37
+ result = requests.request(method, url, headers=headers, verify=c.ssl, timeout=timeout)
38
+
39
+ if "handle" in kwargs.keys() and kwargs["handle"] == False:
40
+ return result
41
+
42
+ result.raise_for_status()
43
+ return result
44
+
45
+ """ Function to generate headers with basic authentication or OIDC """
46
+ def get_headers(c):
47
+ if c._AUTH_TYPE == "basicauth":
48
+ usr_pass_as_bytes = bytes(c.user+":"+c.password,"utf-8")
49
+ usr_pass_base_64 = base64.b64encode(usr_pass_as_bytes).decode("utf-8")
50
+ return {"Authorization": "Basic "+ usr_pass_base_64}
51
+ if c._AUTH_TYPE == "oidc-agent":
52
+ token = agent.get_access_token(c.shortname)
53
+ return get_headers_with_token(token)
54
+ if c._AUTH_TYPE == "oidc":
55
+ return get_headers_with_token(c.oidc_token)
56
+
57
+ """ Function to generate headers with token auth """
58
+ def get_headers_with_token(token):
59
+ return {"Authorization": "Bearer "+ str(token)}
60
+
61
+ def write_text_file(content, file_path):
62
+ with open(file_path, 'w') as f:
63
+ f.write(content)
64
+
65
+ def isBase64(st):
66
+ try:
67
+ base64.b64decode(st)
68
+ return True
69
+ except:
70
+ return False
71
+
72
+ def decode_b64(b64_str, file_out):
73
+ file_extension = os.path.splitext(file_out)[1]
74
+ try:
75
+ decoded_data = base64.b64decode(b64_str)
76
+
77
+ if file_extension in [".txt", ".json"]:
78
+ decode = 'w'
79
+ decoded_data = decoded_data.decode("utf-8")
80
+ else:
81
+ decode = 'wb'
82
+
83
+ with open(file_out, decode) as f:
84
+ f.write(decoded_data)
85
+
86
+ except ValueError:
87
+ print('Error decoding output: Invalid base64 string.')
88
+ except OSError:
89
+ print('Error decoding output: Failed to write decoded data to file.')
90
+
91
+ def encode_input(data):
92
+ if os.path.isfile(data):
93
+ try:
94
+ with open(data, 'rb') as file:
95
+ return base64.b64encode(file.read())
96
+ except FileNotFoundError:
97
+ print('Error encoding input: File {0} not found.'.format(data))
98
+ except OSError:
99
+ print('Error encoding input: Failed to read file.')
100
+ else:
101
+ message_bytes = data.encode('ascii')
102
+ return base64.b64encode(message_bytes)
103
+
104
+ def decode_output(output, file_path):
105
+ if(isBase64(output)):
106
+ decode_b64(output, file_path)
107
+ return
108
+ if(isinstance(output,str)):
109
+ write_text_file(output,file_path)
110
+ return
111
+
112
+
@@ -15,6 +15,7 @@
15
15
 
16
16
  import json
17
17
  import yaml
18
+ import liboidcagent as agent
18
19
  import oscar_python._utils as utils
19
20
  from oscar_python.storage import Storage
20
21
 
@@ -25,10 +26,6 @@ _LOGS_PATH = "/system/logs"
25
26
  _RUN_PATH = "/run"
26
27
  #_JOB_PATH = "/job"
27
28
 
28
- _MINIO = "minio"
29
- _S3 = "s3"
30
- _ONE_DATA = "onedata"
31
- _WEBDAV = "webdav"
32
29
 
33
30
  _GET = "get"
34
31
  _POST = "post"
@@ -37,12 +34,47 @@ _DELETE = "delete"
37
34
 
38
35
  class Client:
39
36
  #Cluster info
40
- def __init__(self, id, endpoint, user, password, ssl) -> None:
41
- self.id = id
42
- self.endpoint = endpoint
43
- self.user = user
44
- self.password = password
45
- self.ssl = ssl
37
+ def __init__(self, options) -> None:
38
+ self.set_auth_type(options)
39
+ if self._AUTH_TYPE == 'basicauth':
40
+ self.basic_auth_client(options)
41
+ if self._AUTH_TYPE == 'oidc-agent':
42
+ self.oidc_agent_client(options)
43
+ if self._AUTH_TYPE == 'oidc':
44
+ self.oidc_client(options)
45
+
46
+ def basic_auth_client(self, options):
47
+ self.id = options['cluster_id']
48
+ self.endpoint = options['endpoint']
49
+ self.user = options['user']
50
+ self.password = options['password']
51
+ self.ssl = bool(options['ssl'])
52
+
53
+ def oidc_agent_client(self, options):
54
+ self.id = options['cluster_id']
55
+ self.endpoint = options['endpoint']
56
+ self.shortname = options['shortname']
57
+ self.ssl = bool(options['ssl'])
58
+
59
+ def oidc_client(self, options):
60
+ self.id = options['cluster_id']
61
+ self.endpoint = options['endpoint']
62
+ self.oidc_token = options['oidc_token']
63
+ self.ssl = bool(options['ssl'])
64
+
65
+ def set_auth_type(self, options):
66
+ if 'user' in options:
67
+ self._AUTH_TYPE = "basicauth"
68
+ elif 'shortname' in options:
69
+ self._AUTH_TYPE = "oidc-agent"
70
+ try:
71
+ agent.get_access_token(options['shortname'])
72
+ except agent.OidcAgentError as e:
73
+ print("ERROR oidc-agent: {}".format(e))
74
+ elif 'oidc_token' in options:
75
+ self._AUTH_TYPE = "oidc"
76
+ else:
77
+ raise ValueError("Unrecognized authentication credentials in options")
46
78
 
47
79
  """ Creates a generic storage client to interact with the storage providers
48
80
  defined on a specific service of the refered OSCAR cluster """
@@ -86,7 +118,7 @@ class Client:
86
118
  with open(svc["script"]) as s:
87
119
  svc["script"] = s.read()
88
120
  except IOError as err:
89
- raise("Bouldn't read script")
121
+ raise("Couldn't read script")
90
122
 
91
123
  # cpu parameter has to be string on the request
92
124
  if type(svc["cpu"]) is int or type(svc["cpu"]) is float: svc["cpu"]= str(svc["cpu"])
@@ -110,13 +142,28 @@ class Client:
110
142
  def remove_service(self, name):
111
143
  return utils.make_request(self, _SVC_PATH+"/"+name, _DELETE)
112
144
 
113
- """ Run a synchronous execution """
114
- def run_service(self, name, input=""):
115
- token = self._get_token(name)
116
- if input: return utils.make_request(self, _RUN_PATH+"/"+name, _POST, data=input, token=token)
145
+ """ Run a synchronous execution.
146
+ If an output is provided the result is decoded onto the file.
147
+ In both cases the function returns the HTTP response."""
148
+ def run_service(self, name, **kwargs):
149
+ if "input" in kwargs.keys() and kwargs["input"]:
150
+ exec_input = kwargs["input"]
151
+ token = self._get_token(name)
152
+
153
+ send_data = utils.encode_input(exec_input)
154
+
155
+ if "timeout" in kwargs.keys() and kwargs["timeout"]:
156
+ response = utils._make_request(self, _RUN_PATH+"/"+name, _POST, data=send_data, token=token, timeout=kwargs["timeout"])
157
+ else:
158
+ response = utils.make_request(self, _RUN_PATH+"/"+name, _POST, data=send_data, token=token)
159
+
160
+ if "output" in kwargs.keys() and kwargs["output"]:
161
+ utils.decode_output(response.text, kwargs["output"])
162
+ return response
163
+
117
164
  return utils.make_request(self, _RUN_PATH+"/"+name, _POST, token=token)
118
165
 
119
- """ Run an asynchronous execution (not usable at the moment). """
166
+ """ Run an asynchronous execution (unable at the moment). """
120
167
  #TODO
121
168
  """ def _run_job(self, name, input_path =""):
122
169
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oscar-python
3
- Version: 1.0.3
3
+ Version: 1.1.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
@@ -11,18 +11,18 @@ Classifier: License :: OSI Approved :: Apache Software License
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
 
14
- ## Python OSCAR API
14
+ ## Python OSCAR client
15
15
 
16
16
  [![Build](https://github.com/grycap/oscar_python/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
17
17
  ![PyPI](https://img.shields.io/pypi/v/oscar_python)
18
18
 
19
- This package provides an API to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
19
+ This package provides a client to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
20
20
 
21
21
  ### Contents
22
- - [Python OSCAR API](#python-oscar-api)
22
+ - [Python OSCAR client](#python-oscar-client)
23
23
  - [Contents](#contents)
24
24
  - [Sample usage](#sample-usage)
25
- - [API methods](#api-methods)
25
+ - [Client methods](#client-methods)
26
26
  - [Cluster methods](#cluster-methods)
27
27
  - [Service methods](#service-methods)
28
28
  - [Logs methods](#logs-methods)
@@ -54,14 +54,14 @@ client = Client("cluster-id","https://cluster-endpoint", "username", "password",
54
54
 
55
55
  try:
56
56
  client.create_service("/absolute_path/cowsay.yaml")
57
- res = client.run_service("cowsay", '{"message": "Hi there"}')
58
- if res.status_code == 200:
59
- print(res.text)
57
+ response = client.run_service("cowsay", input = '{"message": "Hi there"}')
58
+ if response.status_code == 200:
59
+ print(response.text)
60
60
  except Exception as err:
61
61
  print("Failed with: ", err)
62
62
  ```
63
63
 
64
- ### API methods
64
+ ### Client methods
65
65
 
66
66
  #### Cluster methods
67
67
 
@@ -113,12 +113,11 @@ response = client.remove_service("service_name") # returns an http response
113
113
 
114
114
  **run_service**
115
115
 
116
- The `input` parameter may not be passed if the function doesn't require input.
116
+ *`input`, `output` and `timeout` are optional parameters.*
117
117
 
118
118
  ``` python
119
119
  # make a synchronous execution
120
- response = client.run_service("service_name", input="input") # returns an http response
121
-
120
+ response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
122
121
  ```
123
122
 
124
123
  #### Logs methods
@@ -176,9 +175,3 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
176
175
  # download a file from a remote path to a local path
177
176
  response = storage_service.download_file("storage_provider", "local_path", "remote_path")
178
177
  ```
179
-
180
-
181
-
182
-
183
-
184
-
@@ -1,47 +0,0 @@
1
- # Copyright (C) GRyCAP - I3M - UPV
2
-
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
-
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import base64
16
- import requests
17
-
18
- """ Generic http request """
19
- def make_request(c , path, method, **kwargs):
20
- url = c.endpoint+path
21
- headers = get_headers(c)
22
- if method in ["post", "put"]:
23
- if "token" in kwargs.keys() and kwargs["token"]:
24
- headers = get_headers_with_token(kwargs["token"])
25
- if "data" in kwargs.keys() and kwargs["data"]:
26
- result = requests.request(method, url, headers=headers, verify=c.ssl, data=kwargs["data"])
27
- else:
28
- result = requests.request(method, url, headers=headers, verify=c.ssl)
29
-
30
- if "handle" in kwargs.keys() and kwargs["handle"] == False:
31
- return result
32
-
33
- result.raise_for_status()
34
- return result
35
-
36
- """ Function to generate headers with basic authentication """
37
- def get_headers(c):
38
- usr_pass_as_bytes = bytes(c.user+":"+c.password,"utf-8")
39
- usr_pass_base_64 = base64.b64encode(usr_pass_as_bytes).decode("utf-8")
40
- return {"Authorization": "Basic "+ usr_pass_base_64}
41
-
42
- """ Function to generate headers with token auth """
43
- def get_headers_with_token(token):
44
- return {"Authorization": "Bearer "+ str(token)}
45
-
46
- def raise_http_errors(response):
47
- response.raise_for_status()
File without changes
File without changes
File without changes