ciocore 6.3.2rc1__py2.py3-none-any.whl → 6.4.0__py2.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.
Potentially problematic release.
This version of ciocore might be problematic. Click here for more details.
- ciocore/VERSION +1 -1
- ciocore/__about__.py +3 -20
- ciocore/api_client.py +116 -236
- ciocore/cli/__init__.py +3 -0
- ciocore/cli/conductor.py +206 -0
- ciocore/config.py +44 -50
- ciocore/data.py +131 -85
- ciocore/downloader.py +64 -46
- ciocore/hardware_set.py +99 -326
- ciocore/package_environment.py +74 -48
- ciocore/package_tree.py +377 -300
- ciocore/uploader/_uploader.py +2 -6
- ciocore-6.4.0.data/scripts/conductor +19 -0
- ciocore-6.4.0.data/scripts/conductor.bat +13 -0
- {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/METADATA +12 -35
- ciocore-6.4.0.dist-info/RECORD +51 -0
- {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/WHEEL +1 -1
- tests/instance_type_fixtures.py +9 -43
- tests/mocks/api_client_mock.py +31 -0
- tests/test_hardware_set.py +4 -50
- ciocore/cli.py +0 -336
- ciocore/dev_inst_tagger.py +0 -120
- ciocore/docsite/404.html +0 -723
- ciocore/docsite/apidoc/api_client/index.html +0 -2590
- ciocore/docsite/apidoc/apidoc/index.html +0 -868
- ciocore/docsite/apidoc/config/index.html +0 -1562
- ciocore/docsite/apidoc/data/index.html +0 -1550
- ciocore/docsite/apidoc/hardware_set/index.html +0 -2324
- ciocore/docsite/apidoc/package_environment/index.html +0 -1430
- ciocore/docsite/apidoc/package_tree/index.html +0 -2310
- ciocore/docsite/assets/_mkdocstrings.css +0 -16
- ciocore/docsite/assets/images/favicon.png +0 -0
- ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js +0 -29
- ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js.map +0 -8
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ar.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.da.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.de.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.du.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.es.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fi.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fr.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hi.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hu.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hy.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.it.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ja.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.jp.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.kn.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ko.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.multi.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.nl.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.no.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.pt.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ro.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ru.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sa.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sv.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ta.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.te.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.th.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.tr.min.js +0 -18
- ciocore/docsite/assets/javascripts/lunr/min/lunr.vi.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/min/lunr.zh.min.js +0 -1
- ciocore/docsite/assets/javascripts/lunr/tinyseg.js +0 -206
- ciocore/docsite/assets/javascripts/lunr/wordcut.js +0 -6708
- ciocore/docsite/assets/javascripts/workers/search.dfff1995.min.js +0 -42
- ciocore/docsite/assets/javascripts/workers/search.dfff1995.min.js.map +0 -8
- ciocore/docsite/assets/stylesheets/main.83068744.min.css +0 -1
- ciocore/docsite/assets/stylesheets/main.83068744.min.css.map +0 -1
- ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css +0 -1
- ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css.map +0 -1
- ciocore/docsite/cmdline/docs/index.html +0 -834
- ciocore/docsite/cmdline/downloader/index.html +0 -897
- ciocore/docsite/cmdline/packages/index.html +0 -841
- ciocore/docsite/cmdline/uploader/index.html +0 -950
- ciocore/docsite/how-to-guides/index.html +0 -831
- ciocore/docsite/index.html +0 -853
- ciocore/docsite/logo.png +0 -0
- ciocore/docsite/objects.inv +0 -0
- ciocore/docsite/search/search_index.json +0 -1
- ciocore/docsite/sitemap.xml +0 -3
- ciocore/docsite/sitemap.xml.gz +0 -0
- ciocore/docsite/stylesheets/extra.css +0 -26
- ciocore/docsite/stylesheets/tables.css +0 -170
- ciocore/package_query.py +0 -171
- ciocore-6.3.2rc1.dist-info/RECORD +0 -115
- ciocore-6.3.2rc1.dist-info/entry_points.txt +0 -2
- tests/test_cli.py +0 -161
- tests/test_downloader.py +0 -56
- {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/top_level.txt +0 -0
ciocore/data.py
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
A singleton containing all the data from Conductor endpoints that's needed for writing submission
|
|
3
|
+
tools. Specifiically, it provides projects, instance types, and software package data.
|
|
4
|
+
|
|
5
|
+
It's also possible to use cached fixtures for development purposes.
|
|
3
6
|
|
|
4
|
-
Since the data is stored at the module level, you can access it from anywhere in your code without the need to pass it around.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
9
|
import json
|
|
8
10
|
import os
|
|
9
|
-
import requests
|
|
10
11
|
from ciocore.package_tree import PackageTree
|
|
11
|
-
from ciocore import api_client
|
|
12
|
+
from ciocore import api_client
|
|
12
13
|
from ciocore.hardware_set import HardwareSet
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# USE_INST_TAGGER = int(os.environ.get("CIO_FEATURE_DEV_INST_TAGGER", 0))
|
|
17
|
-
|
|
18
14
|
__data__ = {}
|
|
19
15
|
__products__ = None
|
|
20
16
|
__fixtures_dir__ = None
|
|
@@ -22,24 +18,35 @@ __platforms__ = None
|
|
|
22
18
|
|
|
23
19
|
def init(*products, **kwargs):
|
|
24
20
|
"""
|
|
25
|
-
Initialize
|
|
21
|
+
Initialize and let the module know what host products to provide in the `software` property.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
* **`products`** -- Provide a list of products as a filter. If no products are given, the
|
|
26
|
+
software data structure is built using all products from the packages endpoint. If you provide
|
|
27
|
+
more than one product, they must all be host level products.
|
|
29
28
|
|
|
30
|
-
Keyword
|
|
31
|
-
product (str): `DEPRECATED` Provide one product for which to get software packages.
|
|
29
|
+
Keyword Arguments:
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
* **`product`** -- (DEPRECATED) You can provide one product with the product keyword, or you can
|
|
32
|
+
provide the string `"all"`. This is ignored if any product args are present. -- Defaults to
|
|
33
|
+
`None`.
|
|
34
|
+
|
|
35
|
+
???+ example
|
|
36
|
+
``` python
|
|
37
|
+
|
|
38
|
+
from ciocore import data as coredata
|
|
39
|
+
coredata.init()
|
|
36
40
|
# OR
|
|
37
|
-
|
|
41
|
+
coredata.init("maya-io")
|
|
38
42
|
# OR LEGACY
|
|
39
|
-
|
|
43
|
+
coredata.init(product="all")
|
|
40
44
|
# OR
|
|
41
|
-
|
|
45
|
+
coredata.init(product="maya-io")
|
|
46
|
+
|
|
47
|
+
```
|
|
42
48
|
"""
|
|
49
|
+
|
|
43
50
|
global __products__
|
|
44
51
|
global __platforms__
|
|
45
52
|
if products:
|
|
@@ -55,72 +62,76 @@ def init(*products, **kwargs):
|
|
|
55
62
|
__products__ = [kwargs.get("product")]
|
|
56
63
|
else:
|
|
57
64
|
__products__ = []
|
|
58
|
-
print("SETTING PLATFORMS")
|
|
59
|
-
__platforms__ = set(kwargs.get("platforms", ["windows", "linux"]))
|
|
60
|
-
print("PLATFORMS: ", __platforms__)
|
|
61
|
-
print("DONE SETTING PLATFORMS")
|
|
62
65
|
|
|
66
|
+
__platforms__ = set(kwargs.get("platforms", ["windows", "linux"]))
|
|
67
|
+
|
|
68
|
+
|
|
63
69
|
def data(force=False):
|
|
64
70
|
"""
|
|
65
|
-
|
|
71
|
+
Provides projects, instance types, and software package data.
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
If data has been fetched already, then return it, otherwise make requests to fetch it.
|
|
74
|
+
The user may have to authentiicate.
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
The set of instance types and software are each potentially pruned to match the available
|
|
77
|
+
platforms represented by the other. If the instance types come from an orchestrator that
|
|
78
|
+
provides both windows and linux machines, and the software product(s) are available on both
|
|
79
|
+
platforms, then no pruning occurs. However, if there are no windows machines, then any windows
|
|
80
|
+
software is removed from the package tree. Likewise, if a product is chosen that only runs on
|
|
81
|
+
Windows, then Linux instance types will be culled from the list of available hardware.
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
dict: Keys are `projects`, `instance_types`, `software`.
|
|
83
|
+
Keyword Arguments:
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
* **`force`** -- If `True`, then fetch fresh data -- Defaults to `False`.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
represented by each other. For example, if the instance types come from an orchestrator that
|
|
82
|
-
provides both Windows and Linux machines, and the software product(s) are available on both
|
|
83
|
-
platforms, no pruning occurs. However, if there are no Windows machines available, any Windows
|
|
84
|
-
software will be removed from the package tree. Similarly, if a product is chosen that only runs
|
|
85
|
-
on Windows, Linux instance types will not appearin the list of available hardware.
|
|
89
|
+
* **`ValueError`** -- Module was not initialized with the [init()](#init) method.
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Returns:
|
|
88
92
|
|
|
89
|
-
*
|
|
93
|
+
* A dictionary with 3 keys: `projects`, `instance_types`, `software`.
|
|
90
94
|
|
|
91
|
-
*
|
|
92
|
-
|
|
95
|
+
* `projects` is a list of project names for the authenticated account.
|
|
96
|
+
* `instance_types` is an instace of HardwareSet, from which you can get the data in various forms.
|
|
97
|
+
* `software` is a [PackageTree](/developer/ciocore/package_tree) object containing either all
|
|
98
|
+
software available at conductor, or a subset according to the product specified on
|
|
99
|
+
initialization.
|
|
93
100
|
|
|
94
|
-
* **software** is a PackageTree object containing either all
|
|
95
|
-
the software available at Conductor, or a subset based on specified products.
|
|
96
101
|
|
|
102
|
+
???+ example
|
|
103
|
+
``` python
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
from ciocore import data as coredata
|
|
106
|
+
coredata.init(product="maya-io")
|
|
107
|
+
coredata.data()["software"]
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
<ciocore.package_tree.PackageTree object at 0x10e9a4040>
|
|
109
|
+
# Result:
|
|
110
|
+
# <ciocore.package_tree.PackageTree object at 0x10e9a4040>
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
ATestForScott
|
|
112
|
+
coredata.data()["projects"][0]
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
# Result:
|
|
115
|
+
# ATestForScott
|
|
116
|
+
|
|
117
|
+
coredata.data()["instance_types"][-1]["description"]
|
|
118
|
+
# Result:
|
|
119
|
+
# 160 core, 3844GB Mem
|
|
120
|
+
```
|
|
110
121
|
"""
|
|
111
122
|
|
|
112
123
|
global __data__
|
|
113
124
|
global __products__
|
|
125
|
+
global __fixtures_dir__
|
|
114
126
|
global __platforms__
|
|
115
127
|
|
|
116
128
|
if __products__ is None:
|
|
117
129
|
raise ValueError(
|
|
118
130
|
'Data must be initialized before use, e.g. data.init("maya-io") or data.init().'
|
|
119
131
|
)
|
|
120
|
-
|
|
132
|
+
|
|
121
133
|
if force:
|
|
122
134
|
clear()
|
|
123
|
-
init(*products_copy)
|
|
124
135
|
|
|
125
136
|
if __data__ == {}:
|
|
126
137
|
# PROJECTS
|
|
@@ -132,8 +143,6 @@ def data(force=False):
|
|
|
132
143
|
|
|
133
144
|
# INST_TYPES
|
|
134
145
|
instance_types = _get_json_fixture("instance_types")
|
|
135
|
-
if not instance_types:
|
|
136
|
-
instance_types = dev_inst_tagger.request_instance_types()
|
|
137
146
|
if not instance_types:
|
|
138
147
|
instance_types = api_client.request_instance_types()
|
|
139
148
|
|
|
@@ -154,13 +163,14 @@ def data(force=False):
|
|
|
154
163
|
host_products = []
|
|
155
164
|
kwargs["product"] = __products__[0]
|
|
156
165
|
|
|
166
|
+
|
|
157
167
|
software_tree = PackageTree(software, *host_products, **kwargs)
|
|
158
168
|
|
|
159
169
|
if software_tree:
|
|
160
170
|
__data__["software"] = software_tree
|
|
161
171
|
# Revisit instance types to filter out any that are not needed for any software package.
|
|
162
172
|
sw_platforms = software_tree.platforms()
|
|
163
|
-
|
|
173
|
+
|
|
164
174
|
instance_types = [
|
|
165
175
|
it for it in instance_types if it["operating_system"] in sw_platforms
|
|
166
176
|
]
|
|
@@ -174,17 +184,25 @@ def data(force=False):
|
|
|
174
184
|
|
|
175
185
|
def valid():
|
|
176
186
|
"""
|
|
177
|
-
Check validity.
|
|
187
|
+
Check validity of the data.
|
|
178
188
|
|
|
179
189
|
Returns:
|
|
180
|
-
bool: True if `projects`, `instance_types`, and `software` are valid.
|
|
181
190
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
* True if `projects`, `instance_types`, and `software` exists.
|
|
192
|
+
|
|
193
|
+
???+ example
|
|
194
|
+
``` python
|
|
195
|
+
|
|
196
|
+
from ciocore import data as coredata
|
|
197
|
+
coredata.valid()
|
|
198
|
+
|
|
199
|
+
# Result:
|
|
200
|
+
# True
|
|
187
201
|
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
"""
|
|
205
|
+
global __data__
|
|
188
206
|
if not __data__.get("projects"):
|
|
189
207
|
return False
|
|
190
208
|
if not __data__.get("instance_types"):
|
|
@@ -196,24 +214,45 @@ def valid():
|
|
|
196
214
|
|
|
197
215
|
def clear():
|
|
198
216
|
"""
|
|
199
|
-
Clear out data.
|
|
217
|
+
Clear out the data.
|
|
200
218
|
|
|
201
|
-
[valid()](
|
|
219
|
+
[valid()](#valid) returns False after clear().
|
|
202
220
|
"""
|
|
203
221
|
global __data__
|
|
204
|
-
global __products__
|
|
205
|
-
global __platforms__
|
|
206
222
|
__data__ = {}
|
|
207
223
|
__products__ = None
|
|
208
224
|
__platforms__ = None
|
|
225
|
+
"""
|
|
226
|
+
Let the module know what host products to provide in the `software` property.
|
|
227
|
+
|
|
228
|
+
Keyword Arguments:
|
|
229
|
+
|
|
230
|
+
* **`products`** -- Product args, such as `maya-io, cinema4d` -- Defaults to `None` which means all.
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
|
|
234
|
+
* **`ValueError`** -- _description_.
|
|
235
|
+
|
|
236
|
+
???+ example
|
|
237
|
+
``` python
|
|
238
|
+
|
|
239
|
+
from ciocore import data as coredata
|
|
240
|
+
coredata.init()
|
|
241
|
+
# OR
|
|
242
|
+
coredata.init("maya-io")
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
"""
|
|
209
246
|
|
|
210
247
|
|
|
211
248
|
def products():
|
|
212
249
|
"""
|
|
213
250
|
|
|
214
251
|
Returns:
|
|
215
|
-
|
|
252
|
+
|
|
253
|
+
* The product names. An empty array signifies all products.
|
|
216
254
|
"""
|
|
255
|
+
global __products__
|
|
217
256
|
return __products__
|
|
218
257
|
|
|
219
258
|
|
|
@@ -222,9 +261,9 @@ def set_fixtures_dir(path):
|
|
|
222
261
|
Specify a directory in which to find JSON files representing the three sets of data to provide.
|
|
223
262
|
The individual filenames are:
|
|
224
263
|
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
264
|
+
* projects.json
|
|
265
|
+
* instance_types.json
|
|
266
|
+
* software.json
|
|
228
267
|
|
|
229
268
|
These files could be used in an environment where machines can't access the internet. They are
|
|
230
269
|
also useful as a cache for developers who need to reload often as it avoids waiting for the
|
|
@@ -232,16 +271,21 @@ def set_fixtures_dir(path):
|
|
|
232
271
|
|
|
233
272
|
In order to get the content for the fixtures files, use the following Example
|
|
234
273
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
>>> software = api_client.request_software_packages()
|
|
274
|
+
???+ example
|
|
275
|
+
``` python
|
|
276
|
+
|
|
277
|
+
from ciocore import api_client
|
|
240
278
|
|
|
241
|
-
|
|
279
|
+
projects = api_client.request_projects()
|
|
280
|
+
instance_types = api_client.request_instance_types()
|
|
281
|
+
software = api_client.request_software_packages()
|
|
282
|
+
|
|
283
|
+
# Write that data as JSON to the filenames listed above.
|
|
284
|
+
```
|
|
242
285
|
|
|
243
286
|
Arguments:
|
|
244
|
-
|
|
287
|
+
|
|
288
|
+
* **`path`** -- Directory in which to find the above files.
|
|
245
289
|
|
|
246
290
|
"""
|
|
247
291
|
|
|
@@ -250,6 +294,7 @@ def set_fixtures_dir(path):
|
|
|
250
294
|
|
|
251
295
|
|
|
252
296
|
def _get_json_fixture(resource):
|
|
297
|
+
global __fixtures_dir__
|
|
253
298
|
if __fixtures_dir__:
|
|
254
299
|
cache_path = os.path.join(__fixtures_dir__, "{}.json".format(resource))
|
|
255
300
|
if os.path.isfile(cache_path):
|
|
@@ -262,10 +307,11 @@ def _get_json_fixture(resource):
|
|
|
262
307
|
|
|
263
308
|
def platforms():
|
|
264
309
|
"""
|
|
265
|
-
The set of platforms that both software and instance types
|
|
310
|
+
The set of platforms that are found in both software and instance types.
|
|
266
311
|
|
|
267
312
|
Returns:
|
|
268
|
-
|
|
313
|
+
|
|
314
|
+
* A set containing platforms: windows and/or linux or neither.
|
|
269
315
|
"""
|
|
316
|
+
global __platforms__
|
|
270
317
|
return __platforms__
|
|
271
|
-
|
ciocore/downloader.py
CHANGED
|
@@ -18,10 +18,10 @@ import time
|
|
|
18
18
|
import threading
|
|
19
19
|
import hashlib
|
|
20
20
|
|
|
21
|
+
import ciocore
|
|
21
22
|
from ciocore import config
|
|
22
23
|
from ciocore import api_client, common, loggeria
|
|
23
24
|
from ciocore.common import CONDUCTOR_LOGGER_NAME
|
|
24
|
-
from cioseq.sequence import Sequence
|
|
25
25
|
|
|
26
26
|
try:
|
|
27
27
|
import Queue as queue
|
|
@@ -291,12 +291,12 @@ class Downloader(object):
|
|
|
291
291
|
downloader.print_uptime()
|
|
292
292
|
|
|
293
293
|
@classmethod
|
|
294
|
-
def download_jobs(cls, job_ids, thread_count=None, output_dir=None):
|
|
294
|
+
def download_jobs(cls, job_ids, task_id=None, thread_count=None, output_dir=None):
|
|
295
295
|
"""
|
|
296
296
|
Run the downloader for explicit jobs, and terminate afterwards.
|
|
297
297
|
"""
|
|
298
298
|
downloader = cls(thread_count=thread_count, output_dir=output_dir)
|
|
299
|
-
downloader.start(job_ids)
|
|
299
|
+
thread_states = downloader.start(job_ids, task_id=task_id)
|
|
300
300
|
while not common.SIGINT_EXIT and (
|
|
301
301
|
not downloader.pending_queue.empty() or not downloader.downloading_queue.empty()
|
|
302
302
|
):
|
|
@@ -306,7 +306,7 @@ class Downloader(object):
|
|
|
306
306
|
downloader._print_download_history()
|
|
307
307
|
downloader.print_uptime()
|
|
308
308
|
|
|
309
|
-
def start(self, job_ids=None, summary_interval=10):
|
|
309
|
+
def start(self, job_ids=None, task_id=None, summary_interval=10):
|
|
310
310
|
# Create new queues
|
|
311
311
|
self.start_time = time.time()
|
|
312
312
|
self.pending_queue = queue.Queue()
|
|
@@ -316,7 +316,7 @@ class Downloader(object):
|
|
|
316
316
|
# If a job id has been specified then only load the queue up with that work
|
|
317
317
|
if job_ids:
|
|
318
318
|
self.history_queue_max = None
|
|
319
|
-
self.get_jobs_downloads(job_ids)
|
|
319
|
+
self.get_jobs_downloads(job_ids, task_id)
|
|
320
320
|
|
|
321
321
|
# otherwise create a queue thread the polls the app for wor
|
|
322
322
|
else:
|
|
@@ -461,12 +461,10 @@ class Downloader(object):
|
|
|
461
461
|
# Wait for the queue to be consumed before querying for more
|
|
462
462
|
self.nap()
|
|
463
463
|
|
|
464
|
-
|
|
465
464
|
def nap(self):
|
|
466
465
|
while not common.SIGINT_EXIT:
|
|
467
466
|
|
|
468
467
|
time.sleep(self.naptime)
|
|
469
|
-
# Pretty sure this return should be unindented!
|
|
470
468
|
return
|
|
471
469
|
|
|
472
470
|
@common.dec_timer_exit(log_level=logging.DEBUG)
|
|
@@ -479,51 +477,24 @@ class Downloader(object):
|
|
|
479
477
|
except Exception as e:
|
|
480
478
|
logger.exception("Could not get next download")
|
|
481
479
|
|
|
482
|
-
def get_jobs_downloads(self, job_ids):
|
|
480
|
+
def get_jobs_downloads(self, job_ids, task_id):
|
|
483
481
|
"""
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
Job ids are padded to 5 digits, and task ids are padded to 3 digits.
|
|
482
|
+
Get each job and optional comma-separated task list.
|
|
487
483
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
for tid in job["tasks"] or [None]:
|
|
500
|
-
downloads = _get_job_download(endpoint, self.api_client, job["job_id"], tid)
|
|
484
|
+
There will only be a task list if there is just one job, due to earlier arg validation.
|
|
485
|
+
|
|
486
|
+
If there is no task list, _get_job_download is called with tid=None (i.e. the whole job)
|
|
487
|
+
"""
|
|
488
|
+
task_ids = [t for t in task_id.split(",") if t] if task_id else [None]
|
|
489
|
+
|
|
490
|
+
for job_id in job_ids:
|
|
491
|
+
endpoint = self.endpoint_downloads_job % job_id
|
|
492
|
+
for tid in task_ids:
|
|
493
|
+
downloads = _get_job_download(endpoint, self.api_client, job_id, tid)
|
|
501
494
|
if downloads:
|
|
502
495
|
for task_download in downloads.get("downloads", []):
|
|
503
496
|
print("putting in queue: %s" % task_download)
|
|
504
497
|
self.pending_queue.put(task_download, block=True)
|
|
505
|
-
|
|
506
|
-
@staticmethod
|
|
507
|
-
def _flatten(job_ids):
|
|
508
|
-
"""Create a list of job objects with keys: job_id and tasks.
|
|
509
|
-
|
|
510
|
-
see: get_jobs_downloads() function see tests/test_downloader.py for examples.
|
|
511
|
-
"""
|
|
512
|
-
result = []
|
|
513
|
-
for job_id in job_ids:
|
|
514
|
-
if ":" in job_id:
|
|
515
|
-
job_id, range_spec = job_id.split(":")
|
|
516
|
-
try:
|
|
517
|
-
seq = Sequence.create(range_spec)
|
|
518
|
-
tasks = seq.expand("###")
|
|
519
|
-
except (ValueError, TypeError):
|
|
520
|
-
tasks = None
|
|
521
|
-
else:
|
|
522
|
-
tasks = None
|
|
523
|
-
result.append({"job_id": job_id.zfill(5), "tasks": tasks})
|
|
524
|
-
return result
|
|
525
|
-
|
|
526
|
-
|
|
527
498
|
|
|
528
499
|
@common.dec_catch_exception(raise_=True)
|
|
529
500
|
def download_target(self, pending_queue, downloading_queue, task_download_state):
|
|
@@ -1249,6 +1220,53 @@ def _in_queue(the_queue, item_dict, key):
|
|
|
1249
1220
|
if item[key] == item_dict[key]:
|
|
1250
1221
|
return True
|
|
1251
1222
|
|
|
1223
|
+
|
|
1224
|
+
def run_downloader(args):
|
|
1225
|
+
"""
|
|
1226
|
+
Start the downloader. If a job id(s) were given, exit the downloader upon completion.
|
|
1227
|
+
Otherwise, run the downloader indefinitely (daemon mode), polling the Conductor cloud app for
|
|
1228
|
+
files that need to be downloaded.
|
|
1229
|
+
"""
|
|
1230
|
+
|
|
1231
|
+
# Set up logging
|
|
1232
|
+
log_level_name = args.get("log_level")
|
|
1233
|
+
log_level = loggeria.LEVEL_MAP.get(log_level_name)
|
|
1234
|
+
log_dirpath = args.get("log_dir")
|
|
1235
|
+
set_logging(log_level, log_dirpath)
|
|
1236
|
+
|
|
1237
|
+
api_client.ApiClient.register_client(client_name = Downloader.CLIENT_NAME, client_version=ciocore.__version__)
|
|
1238
|
+
|
|
1239
|
+
logger.debug("Downloader args: %s", args)
|
|
1240
|
+
|
|
1241
|
+
job_ids = args.get("job_id")
|
|
1242
|
+
thread_count = args.get("thread_count")
|
|
1243
|
+
|
|
1244
|
+
if job_ids:
|
|
1245
|
+
Downloader.download_jobs(
|
|
1246
|
+
job_ids,
|
|
1247
|
+
task_id=args.get("task_id"),
|
|
1248
|
+
thread_count=thread_count,
|
|
1249
|
+
output_dir=args.get("output"),
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
else:
|
|
1253
|
+
Downloader.start_daemon(
|
|
1254
|
+
thread_count=thread_count, location=args.get("location"), output_dir=args.get("output")
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
def set_logging(level=None, log_dirpath=None):
|
|
1259
|
+
log_filepath = None
|
|
1260
|
+
if log_dirpath:
|
|
1261
|
+
log_filepath = os.path.join(log_dirpath, "conductor_dl_log")
|
|
1262
|
+
loggeria.setup_conductor_logging(
|
|
1263
|
+
logger_level=level,
|
|
1264
|
+
console_formatter=LOG_FORMATTER,
|
|
1265
|
+
file_formatter=LOG_FORMATTER,
|
|
1266
|
+
log_filepath=log_filepath,
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
|
|
1252
1270
|
def report_error(self, download_id, error_message):
|
|
1253
1271
|
try:
|
|
1254
1272
|
logger.error("failing upload due to: \n%s" % error_message)
|