cloudos-cli 2.17.0__py3-none-any.whl

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 (41) hide show
  1. cloudos_cli/__init__.py +11 -0
  2. cloudos_cli/__main__.py +1297 -0
  3. cloudos_cli/_version.py +1 -0
  4. cloudos_cli/clos.py +726 -0
  5. cloudos_cli/jobs/__init__.py +8 -0
  6. cloudos_cli/jobs/job.py +555 -0
  7. cloudos_cli/queue/__init__.py +8 -0
  8. cloudos_cli/queue/queue.py +139 -0
  9. cloudos_cli/utils/__init__.py +9 -0
  10. cloudos_cli/utils/errors.py +32 -0
  11. cloudos_cli/utils/requests.py +75 -0
  12. cloudos_cli-2.17.0.dist-info/LICENSE +674 -0
  13. cloudos_cli-2.17.0.dist-info/METADATA +1060 -0
  14. cloudos_cli-2.17.0.dist-info/RECORD +41 -0
  15. cloudos_cli-2.17.0.dist-info/WHEEL +5 -0
  16. cloudos_cli-2.17.0.dist-info/entry_points.txt +2 -0
  17. cloudos_cli-2.17.0.dist-info/top_level.txt +2 -0
  18. tests/__init__.py +0 -0
  19. tests/functions_for_pytest.py +7 -0
  20. tests/test_clos/__init__.py +0 -0
  21. tests/test_clos/test_create_cromwell_header.py +35 -0
  22. tests/test_clos/test_cromwell_switch.py +77 -0
  23. tests/test_clos/test_detect_workflow.py +47 -0
  24. tests/test_clos/test_get_cromwell_status.py +77 -0
  25. tests/test_clos/test_get_curated_workflow_list.py +72 -0
  26. tests/test_clos/test_get_job_list.py +79 -0
  27. tests/test_clos/test_get_job_status.py +75 -0
  28. tests/test_clos/test_get_project_list.py +74 -0
  29. tests/test_clos/test_get_user_info.py +68 -0
  30. tests/test_clos/test_get_workflow_list.py +87 -0
  31. tests/test_clos/test_is_module.py +48 -0
  32. tests/test_clos/test_process_job_list.py +74 -0
  33. tests/test_clos/test_process_project_list.py +36 -0
  34. tests/test_clos/test_process_workflow_list.py +36 -0
  35. tests/test_clos/test_wait_job_completion.py +40 -0
  36. tests/test_clos/test_workflow_import.py +77 -0
  37. tests/test_jobs/__init__.py +0 -0
  38. tests/test_jobs/test_convert_nextflow_to_json.py +104 -0
  39. tests/test_jobs/test_project_id.py +67 -0
  40. tests/test_jobs/test_send_job.py +84 -0
  41. tests/test_jobs/test_workflow_id.py +67 -0
@@ -0,0 +1,139 @@
1
+ """
2
+ This is the main class to create job queues.
3
+ """
4
+
5
+ import requests
6
+ import json
7
+ import pandas as pd
8
+ from dataclasses import dataclass
9
+ from typing import Union
10
+ from cloudos_cli.clos import Cloudos
11
+ from cloudos_cli.utils.errors import BadRequestException
12
+
13
+
14
+ @dataclass
15
+ class Queue(Cloudos):
16
+ """Class to store and operate job queues.
17
+
18
+ Parameters
19
+ ----------
20
+ cloudos_url : string
21
+ The CloudOS service url.
22
+ apikey : string
23
+ Your CloudOS API key.
24
+ cromwell_token : string
25
+ Cromwell server token.
26
+ workspace_id : string
27
+ The specific Cloudos workspace id.
28
+ verify: [bool|string]
29
+ Whether to use SSL verification or not. Alternatively, if
30
+ a string is passed, it will be interpreted as the path to
31
+ the SSL certificate file.
32
+ """
33
+ workspace_id: str
34
+ verify: Union[bool, str] = True
35
+
36
+ def get_job_queues(self):
37
+ """Get all the job queues from a CloudOS workspace.
38
+
39
+ Returns
40
+ -------
41
+ r : list
42
+ A list of dicts, each corresponding to a job queue.
43
+ """
44
+ headers = {"apikey": self.apikey}
45
+ r = requests.get("{}/api/v1/teams/aws/v2/job-queues?teamId={}".format(self.cloudos_url,
46
+ self.workspace_id),
47
+ headers=headers, verify=self.verify)
48
+ if r.status_code >= 400:
49
+ raise BadRequestException(r)
50
+ return json.loads(r.content)
51
+
52
+ @staticmethod
53
+ def process_queue_list(r, all_fields=False):
54
+ """Process a queue list from a self.get_job_queues call.
55
+
56
+ Parameters
57
+ ----------
58
+ r : list
59
+ A list of dicts, each corresponding to a job queue.
60
+ all_fields : bool. Default=False
61
+ Whether to return a reduced version of the DataFrame containing
62
+ only the selected columns or the full DataFrame.
63
+
64
+ Returns
65
+ -------
66
+ df : pandas.DataFrame
67
+ A DataFrame with the requested columns from the job queues.
68
+ """
69
+ COLUMNS = ['id',
70
+ 'name',
71
+ 'label',
72
+ 'description',
73
+ 'isDefault',
74
+ 'resourceType',
75
+ 'executor',
76
+ 'status'
77
+ ]
78
+ df_full = pd.json_normalize(r)
79
+ if all_fields:
80
+ df = df_full
81
+ else:
82
+ df = df_full.loc[:, COLUMNS]
83
+ return df
84
+
85
+ def fetch_job_queue_id(self, workflow_type, batch=True, job_queue=None):
86
+ """Fetches CloudOS ID for a given job queue.
87
+
88
+ This method will try to find the
89
+ corresponding CloudOS ID for the job_queue in a given workspace. If
90
+ job_queue=None, this method will select the available default queue in
91
+ the workspace, or the newest "ready" job queue if no default queues are
92
+ available.
93
+
94
+ Parameters
95
+ ----------
96
+ workflow_type : str ['wdl'|'cromwell'|'nextflow']
97
+ The type of workflow to run.
98
+ batch: bool
99
+ Whether to create a batch job or an ignite one.
100
+ job_queue : str or None
101
+ The name of the job queue to search. If None, a default one will be selected.
102
+
103
+ Returns
104
+ -------
105
+ job_queue_id : str or None
106
+ The CloudOS ID for the selected job queue, or None if batch=False.
107
+ """
108
+ if not batch:
109
+ return None
110
+ if workflow_type == 'wdl':
111
+ workflow_type = 'cromwell'
112
+ if workflow_type not in ['cromwell', 'nextflow']:
113
+ raise ValueError('[ERROR] Only nextflow or cromwell workflows are allowed when ' +
114
+ 'running using AWS batch.')
115
+ job_queues = self.get_job_queues()
116
+ available_queues = [q for q in job_queues if q['status'] == 'Ready' and
117
+ q['executor'] == workflow_type]
118
+ if len(available_queues) == 0:
119
+ raise Exception(f'[ERROR] There are no available job queues for {workflow_type} ' +
120
+ 'workflows. Consider creating one using CloudOS UI.')
121
+ default_queue = [q for q in available_queues if q['isDefault']]
122
+ if len(default_queue) > 0:
123
+ default_queue_id = default_queue[0]['id']
124
+ default_queue_name = default_queue[0]['label']
125
+ queue_as_default = 'CloudOS default'
126
+ else:
127
+ default_queue_id = available_queues[-1]['id']
128
+ default_queue_name = available_queues[-1]['label']
129
+ queue_as_default = 'most recent suitable'
130
+ if job_queue is None:
131
+ print(f'[Message] No job queue was specified, using the {queue_as_default} queue: ' +
132
+ f'{default_queue_name}.')
133
+ return default_queue_id
134
+ selected_queue = [q for q in available_queues if q['label'] == job_queue]
135
+ if len(selected_queue) == 0:
136
+ print(f'[Message] Queue \'{job_queue}\' you specified was not found, using the {queue_as_default} ' +
137
+ f'queue instead: {default_queue_name}.')
138
+ return default_queue_id
139
+ return selected_queue[0]['id']
@@ -0,0 +1,9 @@
1
+ """
2
+ Utility functions and classes to use across the package.
3
+ """
4
+
5
+ from .errors import BadRequestException, TimeOutException
6
+ from .requests import retry_requests_get, retry_requests_post
7
+
8
+
9
+ __all__ = ['errors', 'requests']
@@ -0,0 +1,32 @@
1
+ """
2
+ Specific classes to handle errors.
3
+ """
4
+
5
+
6
+ class BadRequestException(Exception):
7
+ """Handle bad request exceptions and shows improved messages.
8
+
9
+ Parameters
10
+ ----------
11
+ rv : requests.Response
12
+ The request variable returned that caused the error.
13
+ """
14
+ def __init__(self, rv):
15
+ msg = "Server returned status {}. Reason: {}".format(rv.status_code, rv.reason)
16
+ super(BadRequestException, self).__init__(msg)
17
+ self.rv = rv
18
+
19
+
20
+ class TimeOutException(Exception):
21
+ """Handle TimeOut exceptions and shows improved messages.
22
+
23
+ Parameters
24
+ ----------
25
+ rv : requests.Response
26
+ The request variable returned that caused the error.
27
+ """
28
+ def __init__(self, rv):
29
+ msg = ("Server exceeded the max time to process request. " +
30
+ "Status: {}; Reason: {}".format(rv.status_code, rv.reason))
31
+ super(TimeOutException, self).__init__(msg)
32
+ self.rv = rv
@@ -0,0 +1,75 @@
1
+ """
2
+ Specific functions to wrapp error strategy for requests
3
+ """
4
+
5
+ import requests
6
+ from requests.adapters import HTTPAdapter
7
+ from urllib3.util import Retry
8
+
9
+
10
+ def retry_requests_get(url, total=5, status_forcelist=[429, 500, 502, 503, 504], **kwargs):
11
+ """Wrap normal requests get with an error strategy.
12
+
13
+ Parameters
14
+ ----------
15
+ url : string
16
+ The request URL
17
+ total : int
18
+ Total number of retries
19
+ status_forcelist : list
20
+ A list of ints with the status codes to trigger the retries
21
+
22
+ Return
23
+ ------
24
+ response : requests.Response
25
+ The Response object returned by the API server
26
+ """
27
+ retry_strategy = Retry(
28
+ total=total,
29
+ status_forcelist=status_forcelist
30
+ )
31
+ # Create an HTTP adapter with the retry strategy and mount it to session
32
+ adapter = HTTPAdapter(max_retries=retry_strategy)
33
+
34
+ # Create a new session object
35
+ session = requests.Session()
36
+ session.mount('http://', adapter)
37
+ session.mount('https://', adapter)
38
+
39
+ # Make a request using the session object
40
+ response = session.get(url, **kwargs)
41
+ return response
42
+
43
+
44
+ def retry_requests_post(url, total=5, status_forcelist=[429, 500, 502, 503, 504], **kwargs):
45
+ """Wrap normal requests post with an error strategy.
46
+
47
+ Parameters
48
+ ----------
49
+ url : string
50
+ The request URL
51
+ total : int
52
+ Total number of retries
53
+ status_forcelist : list
54
+ A list of ints with the status codes to trigger the retries
55
+
56
+ Return
57
+ ------
58
+ response : requests.Response
59
+ The Response object returned by the API server
60
+ """
61
+ retry_strategy = Retry(
62
+ total=total,
63
+ status_forcelist=status_forcelist
64
+ )
65
+ # Create an HTTP adapter with the retry strategy and mount it to session
66
+ adapter = HTTPAdapter(max_retries=retry_strategy)
67
+
68
+ # Create a new session object
69
+ session = requests.Session()
70
+ session.mount('http://', adapter)
71
+ session.mount('https://', adapter)
72
+
73
+ # Make a request using the session object
74
+ response = session.post(url, **kwargs)
75
+ return response