qarnot 2.17.0__py3-none-any.whl → 2.19.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.
- qarnot/_version.py +3 -3
- qarnot/computing_quotas.py +340 -0
- qarnot/connection.py +23 -6
- qarnot/forced_network_rule.py +41 -14
- qarnot/helper.py +4 -3
- qarnot/multi_slots_settings.py +48 -0
- qarnot/pool.py +74 -0
- qarnot/task.py +51 -2
- {qarnot-2.17.0.dist-info → qarnot-2.19.0.dist-info}/METADATA +3 -2
- {qarnot-2.17.0.dist-info → qarnot-2.19.0.dist-info}/RECORD +13 -11
- {qarnot-2.17.0.dist-info → qarnot-2.19.0.dist-info}/WHEEL +1 -1
- {qarnot-2.17.0.dist-info → qarnot-2.19.0.dist-info/licenses}/LICENSE +0 -0
- {qarnot-2.17.0.dist-info → qarnot-2.19.0.dist-info}/top_level.txt +0 -0
qarnot/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-09-04T17:06:17+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "v2.
|
|
14
|
+
"full-revisionid": "2436d80006a87be548b2235b12f0a6f041ef66c1",
|
|
15
|
+
"version": "v2.19.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Copyright 2025 Qarnot computing
|
|
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
|
+
from typing import Optional, List, Dict, Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UserSchedulingQuota(object):
|
|
19
|
+
"""Describes a scheduling quota for the user.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
23
|
+
"""Create a new UserSchedulingQuota object describing a scheduling quota for the user.
|
|
24
|
+
|
|
25
|
+
:param int max_cores: Maximum number of cores that can be simultaneously used with this scheduling plan.
|
|
26
|
+
:param int running_cores_count: Number of cores that are currently running with this scheduling plan.
|
|
27
|
+
:param int max_instances: Maximum number of instances that can be simultaneously used with this scheduling plan.
|
|
28
|
+
:param int running_instances_count: Number of instances that are currently running with this scheduling plan.
|
|
29
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserSchedulingQuota`.
|
|
30
|
+
"""
|
|
31
|
+
self.max_cores = max_cores
|
|
32
|
+
""":type: :class:`int`
|
|
33
|
+
|
|
34
|
+
Maximum number of cores that can be simultaneously used with this scheduling plan.
|
|
35
|
+
"""
|
|
36
|
+
self.running_cores_count = running_cores_count
|
|
37
|
+
""":type: :class:`int`
|
|
38
|
+
|
|
39
|
+
Number of cores that are currently running with this scheduling plan.
|
|
40
|
+
"""
|
|
41
|
+
self.max_instances = max_instances
|
|
42
|
+
""":type: :class:`int`
|
|
43
|
+
|
|
44
|
+
Maximum number of instances that can be simultaneously used with this scheduling plan.
|
|
45
|
+
"""
|
|
46
|
+
self.running_instances_count = running_instances_count
|
|
47
|
+
""":type: :class:`int`
|
|
48
|
+
|
|
49
|
+
Number of instances that are currently running with this scheduling plan.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
54
|
+
"""Create a new UserSchedulingQuota object from json describing a scheduling quota for a user.
|
|
55
|
+
|
|
56
|
+
:param dict json: Dictionary representing the user scheduling plan
|
|
57
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserSchedulingQuota`.
|
|
58
|
+
"""
|
|
59
|
+
if json is None:
|
|
60
|
+
return None
|
|
61
|
+
return cls(
|
|
62
|
+
json.get('maxCores'),
|
|
63
|
+
json.get('runningCoresCount'),
|
|
64
|
+
json.get('maxInstances'),
|
|
65
|
+
json.get('runningInstancesCount'),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class UserReservedSchedulingQuota(UserSchedulingQuota):
|
|
70
|
+
"""Describes a reserved scheduling quota for the user.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, reservation_name: str, machine_key: str, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
74
|
+
"""Create a new UserReservedSchedulingQuota object describing a reserved scheduling quota for the user.
|
|
75
|
+
|
|
76
|
+
:param str machine_key: Machine key of the reservation.
|
|
77
|
+
:param int max_cores: Maximum number of cores that can be simultaneously used with this reserved machine specification.
|
|
78
|
+
:param int running_cores_count: Number of cores that are currently running with this reserved machine specification.
|
|
79
|
+
:param int max_instances: Maximum number of instances that can be simultaneously used with this reserved machine specification.
|
|
80
|
+
:param int running_instances_count: Number of instances that are currently running with this reserved machine specification.
|
|
81
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserReservedSchedulingQuota`.
|
|
82
|
+
"""
|
|
83
|
+
super().__init__(max_cores, running_cores_count, max_instances, running_instances_count)
|
|
84
|
+
self.machine_key = machine_key
|
|
85
|
+
""":type: :class:`str`
|
|
86
|
+
|
|
87
|
+
Machine key of the reservation.
|
|
88
|
+
"""
|
|
89
|
+
self.reservation_name = reservation_name
|
|
90
|
+
""":type: :class:`str`
|
|
91
|
+
|
|
92
|
+
Name of the reservation.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
97
|
+
"""Create a new UserReservedSchedulingQuota object from json describing a reserved scheduling quota for a user.
|
|
98
|
+
|
|
99
|
+
:param dict json: Dictionary representing the user reserved scheduling quota
|
|
100
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserReservedSchedulingQuota`.
|
|
101
|
+
"""
|
|
102
|
+
if json is None:
|
|
103
|
+
return None
|
|
104
|
+
return cls(
|
|
105
|
+
json.get('reservationName'),
|
|
106
|
+
json.get('machineKey'),
|
|
107
|
+
json.get('maxCores'),
|
|
108
|
+
json.get('runningCoresCount'),
|
|
109
|
+
json.get('maxInstances'),
|
|
110
|
+
json.get('runningInstancesCount'),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class UserComputingQuotas(object):
|
|
115
|
+
"""Describes the user's computing quotas.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __init__(self, flex: UserSchedulingQuota, on_demand: UserSchedulingQuota, reserved: List[UserReservedSchedulingQuota]):
|
|
119
|
+
"""Create a new UserComputingQuotas object describing the user's computing quotas.
|
|
120
|
+
|
|
121
|
+
:param `~qarnot.computing_quotas.UserSchedulingQuota` flex: Quotas for Flex scheduling plan.
|
|
122
|
+
:param `~qarnot.computing_quotas.UserSchedulingQuota` on_demand: Quotas for OnDemand scheduling plan.
|
|
123
|
+
:param List of `~qarnot.computing_quotas.UserReservedSchedulingQuota` reserved: Quotas for Reserved scheduling plan.
|
|
124
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserComputingQuotas`.
|
|
125
|
+
"""
|
|
126
|
+
self.flex = flex
|
|
127
|
+
""":type: :class:`~qarnot.computing_quotas.UserSchedulingQuota`
|
|
128
|
+
|
|
129
|
+
Quotas for Flex scheduling plan."""
|
|
130
|
+
self.on_demand = on_demand
|
|
131
|
+
""":type: :class:`~qarnot.computing_quotas.UserSchedulingQuota`
|
|
132
|
+
|
|
133
|
+
Quotas for OnDemand scheduling plan."""
|
|
134
|
+
self.reserved = reserved
|
|
135
|
+
""":type: list(:class:`~qarnot.computing_quotas.UserReservedSchedulingQuota`)
|
|
136
|
+
|
|
137
|
+
Quotas for Reserved scheduling plan."""
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
141
|
+
"""Create a new UserComputingQuotas object from json describing the user's computing quotas.
|
|
142
|
+
|
|
143
|
+
:param dict json: Dictionary representing the user computing quota
|
|
144
|
+
:returns: The created :class:`~qarnot.computing_quotas.UserComputingQuotas`.
|
|
145
|
+
"""
|
|
146
|
+
if json is None:
|
|
147
|
+
return None
|
|
148
|
+
return cls(
|
|
149
|
+
UserSchedulingQuota.from_json(json.get('flex')),
|
|
150
|
+
UserSchedulingQuota.from_json(json.get('onDemand')),
|
|
151
|
+
[UserReservedSchedulingQuota.from_json(v) for v in (json.get('reserved') if json.get('reserved') is not None else []) if v is not None]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class OrganizationSchedulingQuota(object):
|
|
156
|
+
"""Describes a scheduling quota for the organization.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(self, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
160
|
+
"""Create a new OrganizationSchedulingQuota object describing a scheduling quota for the organization.
|
|
161
|
+
|
|
162
|
+
:param int max_cores: Maximum number of cores that can be simultaneously used with this scheduling plan.
|
|
163
|
+
:param int running_cores_count: Number of cores that are currently running with this scheduling plan.
|
|
164
|
+
:param int max_instances: Maximum number of instances that can be simultaneously used with this scheduling plan.
|
|
165
|
+
:param int running_instances_count: Number of instances that are currently running with this scheduling plan.
|
|
166
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationSchedulingQuota`.
|
|
167
|
+
"""
|
|
168
|
+
self.max_cores = max_cores
|
|
169
|
+
""":type: :class:`int`
|
|
170
|
+
|
|
171
|
+
Maximum number of cores that can be simultaneously used with this scheduling plan.
|
|
172
|
+
"""
|
|
173
|
+
self.running_cores_count = running_cores_count
|
|
174
|
+
""":type: :class:`int`
|
|
175
|
+
|
|
176
|
+
Number of cores that are currently running with this scheduling plan.
|
|
177
|
+
"""
|
|
178
|
+
self.max_instances = max_instances
|
|
179
|
+
""":type: :class:`int`
|
|
180
|
+
|
|
181
|
+
Maximum number of instances that can be simultaneously used with this scheduling plan.
|
|
182
|
+
"""
|
|
183
|
+
self.running_instances_count = running_instances_count
|
|
184
|
+
""":type: :class:`int`
|
|
185
|
+
|
|
186
|
+
Number of instances that are currently running with this scheduling plan.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
191
|
+
"""Create a new OrganizationSchedulingQuota object from json describing a scheduling quota for the organization.
|
|
192
|
+
|
|
193
|
+
:param dict json: Dictionary representing the organization scheduling plan
|
|
194
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationSchedulingQuota`.
|
|
195
|
+
"""
|
|
196
|
+
if json is None:
|
|
197
|
+
return None
|
|
198
|
+
return cls(
|
|
199
|
+
json.get('maxCores'),
|
|
200
|
+
json.get('runningCoresCount'),
|
|
201
|
+
json.get('maxInstances'),
|
|
202
|
+
json.get('runningInstancesCount'),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class OrganizationReservedSchedulingQuota(OrganizationSchedulingQuota):
|
|
207
|
+
"""Describes a reserved scheduling quota for the organization.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(self, reservation_name: str, machine_key: str, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
211
|
+
"""Create a new OrganizationReservedSchedulingQuota object describing a reserved scheduling quota for the organization.
|
|
212
|
+
|
|
213
|
+
:param str machine_key: Machine key of the reservation.
|
|
214
|
+
:param int max_cores: Maximum number of cores that can be simultaneously used with this reserved machine specification.
|
|
215
|
+
:param int running_cores_count: Number of cores that are currently running with this reserved machine specification.
|
|
216
|
+
:param int max_instances: Maximum number of instances that can be simultaneously used with this reserved machine specification.
|
|
217
|
+
:param int running_instances_count: Number of instances that are currently running with this reserved machine specification.
|
|
218
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationReservedSchedulingQuota`.
|
|
219
|
+
"""
|
|
220
|
+
super().__init__(max_cores, running_cores_count, max_instances, running_instances_count)
|
|
221
|
+
self.machine_key = machine_key
|
|
222
|
+
""":type: :class:`str`
|
|
223
|
+
|
|
224
|
+
Machine key of the reservation.
|
|
225
|
+
"""
|
|
226
|
+
self.reservation_name = reservation_name
|
|
227
|
+
""":type: :class:`str`
|
|
228
|
+
|
|
229
|
+
Name of the reservation.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
234
|
+
"""Create a new OrganizationReservedSchedulingQuota object from json describing a reserved scheduling quota for a organization.
|
|
235
|
+
|
|
236
|
+
:param dict json: Dictionary representing the organization reserved scheduling quota
|
|
237
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationReservedSchedulingQuota`.
|
|
238
|
+
"""
|
|
239
|
+
if json is None:
|
|
240
|
+
return None
|
|
241
|
+
return cls(
|
|
242
|
+
json.get('reservationName'),
|
|
243
|
+
json.get('machineKey'),
|
|
244
|
+
json.get('maxCores'),
|
|
245
|
+
json.get('runningCoresCount'),
|
|
246
|
+
json.get('maxInstances'),
|
|
247
|
+
json.get('runningInstancesCount'),
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class OrganizationComputingQuotas(object):
|
|
252
|
+
"""Describes the organization's computing quotas.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def __init__(self, name: str, flex: OrganizationSchedulingQuota, on_demand: OrganizationSchedulingQuota, reserved: List[OrganizationReservedSchedulingQuota]):
|
|
256
|
+
"""Create a new OrganizationComputingQuotas object describing the organization's computing quotas.
|
|
257
|
+
|
|
258
|
+
:param `str` name: Name of the organization.
|
|
259
|
+
:param `~qarnot.computing_quotas.OrganizationSchedulingQuota` flex: Quotas for Flex scheduling plan.
|
|
260
|
+
:param `~qarnot.computing_quotas.OrganizationSchedulingQuota` on_demand: Quotas for OnDemand scheduling plan.
|
|
261
|
+
:param List of `~qarnot.computing_quotas.OrganizationReservedSchedulingQuota` reserved: Quotas for Reserved scheduling plan.
|
|
262
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationComputingQuotas`.
|
|
263
|
+
"""
|
|
264
|
+
self.name = name
|
|
265
|
+
""":type: :class:`str`
|
|
266
|
+
|
|
267
|
+
Name of the organization."""
|
|
268
|
+
self.flex = flex
|
|
269
|
+
""":type: :class:`~qarnot.computing_quotas.OrganizationSchedulingQuota`
|
|
270
|
+
|
|
271
|
+
Quotas for Flex scheduling plan."""
|
|
272
|
+
self.on_demand = on_demand
|
|
273
|
+
""":type: :class:`~qarnot.computing_quotas.OrganizationSchedulingQuota`
|
|
274
|
+
|
|
275
|
+
Quotas for OnDemand scheduling plan."""
|
|
276
|
+
self.reserved = reserved
|
|
277
|
+
""":type: list(:class:`~qarnot.computing_quotas.OrganizationReservedSchedulingQuota`)
|
|
278
|
+
|
|
279
|
+
Quotas for Reserved scheduling plan."""
|
|
280
|
+
|
|
281
|
+
@classmethod
|
|
282
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
283
|
+
"""Create a new OrganizationComputingQuotas object from json describing the organization's computing quotas.
|
|
284
|
+
|
|
285
|
+
:param dict json: Dictionary representing the organization computing quota
|
|
286
|
+
:returns: The created :class:`~qarnot.computing_quotas.OrganizationComputingQuotas`.
|
|
287
|
+
"""
|
|
288
|
+
if json is None:
|
|
289
|
+
return None
|
|
290
|
+
return cls(
|
|
291
|
+
json.get('name'),
|
|
292
|
+
OrganizationSchedulingQuota.from_json(json.get('flex')),
|
|
293
|
+
OrganizationSchedulingQuota.from_json(json.get('onDemand')),
|
|
294
|
+
[OrganizationReservedSchedulingQuota.from_json(v) for v in (json.get('reserved') if json.get('reserved') is not None else []) if v is not None]
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class ComputingQuotas(object):
|
|
299
|
+
"""Describes user and organization computing quotas.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
def __init__(self, user_computing_quotas: Optional[UserComputingQuotas], organization_computing_quotas: Optional[OrganizationComputingQuotas] = None):
|
|
303
|
+
"""Create a new ComputingQuotas object describing user and organization computing quotas.
|
|
304
|
+
|
|
305
|
+
:param user_computing_quotas: the user related computing quotas
|
|
306
|
+
:type user_computing_quotas: `~qarnot.computing_quotas.UserComputingQuotas`, optional
|
|
307
|
+
:param organization_computing_quotas: the organization related computing quotas
|
|
308
|
+
:type organization_computing_quotas: `~qarnot.computing_quotas.OrganizationComputingQuotas`, optional
|
|
309
|
+
:returns: The created :class:`~qarnot.computing_quotas.ComputingQuotas`.
|
|
310
|
+
"""
|
|
311
|
+
self.user = user_computing_quotas
|
|
312
|
+
""":type: :class:`~qarnot.computing_quotas.UserComputingQuotas`
|
|
313
|
+
|
|
314
|
+
The user related computing quotas."""
|
|
315
|
+
self.organization = organization_computing_quotas
|
|
316
|
+
""":type: :class:`~qarnot.computing_quotas.OrganizationComputingQuotas`
|
|
317
|
+
|
|
318
|
+
The organization related computing quotas."""
|
|
319
|
+
|
|
320
|
+
@classmethod
|
|
321
|
+
def from_json(cls, json: Dict[str, Any]):
|
|
322
|
+
"""Create a new ComputingQuotas object from json describing user and organization computing quotas.
|
|
323
|
+
|
|
324
|
+
:param dict json: Dictionary representing the computing quotas
|
|
325
|
+
:returns: The created :class:`~qarnot.computing_quotas.ComputingQuotas`
|
|
326
|
+
"""
|
|
327
|
+
if json is None:
|
|
328
|
+
return None
|
|
329
|
+
user_computing_quotas = UserComputingQuotas.from_json(json.get('user'))
|
|
330
|
+
organization_computing_quotas = OrganizationComputingQuotas.from_json(json.get('organization'))
|
|
331
|
+
return cls(user_computing_quotas, organization_computing_quotas)
|
|
332
|
+
|
|
333
|
+
@classmethod
|
|
334
|
+
def from_json_legacy(cls, json: Dict[str, Any]):
|
|
335
|
+
if json is None:
|
|
336
|
+
return None
|
|
337
|
+
flex = UserSchedulingQuota(json.get('maxFlexCores'), json.get('runningFlexCoreCount'), json.get('maxFlexInstances'), json.get('runningFlexInstanceCount'))
|
|
338
|
+
onDemand = UserSchedulingQuota(json.get('maxOnDemandCores'), json.get('runningOnDemandCoreCount'), json.get('maxOnDemandInstances'), json.get('runningOnDemandInstanceCount'))
|
|
339
|
+
user = UserComputingQuotas(flex, onDemand, [])
|
|
340
|
+
return cls(user, None)
|
qarnot/connection.py
CHANGED
|
@@ -28,6 +28,7 @@ from .task import Task, BulkTaskResponse
|
|
|
28
28
|
from .pool import Pool
|
|
29
29
|
from .paginate import PaginateResponse, OffsetResponse
|
|
30
30
|
from .bucket import Bucket
|
|
31
|
+
from .computing_quotas import ComputingQuotas
|
|
31
32
|
from .job import Job
|
|
32
33
|
from ._filter import create_pool_filter, create_task_filter, create_job_filter
|
|
33
34
|
from ._retry import with_retry
|
|
@@ -95,8 +96,8 @@ class Connection(object):
|
|
|
95
96
|
unsafe=False
|
|
96
97
|
|
|
97
98
|
"""
|
|
98
|
-
self.logger = logger if logger is not None else Log.get_logger_for_stream(sys.stdout)
|
|
99
|
-
self.logger_stderr = logger if logger is not None else Log.get_logger_for_stream(sys.stderr) # to avoid breaking change of task stderr logs
|
|
99
|
+
self.logger = logger if logger is not None else Log.get_logger_for_stream(sys.stdout, "stdout")
|
|
100
|
+
self.logger_stderr = logger if logger is not None else Log.get_logger_for_stream(sys.stderr, "stderr") # to avoid breaking change of task stderr logs
|
|
100
101
|
self._version = "qarnot-sdk-python/" + __version__
|
|
101
102
|
self._http = requests.session()
|
|
102
103
|
self._retry_count = retry_count
|
|
@@ -997,21 +998,37 @@ class UserInfo(object):
|
|
|
997
998
|
""":type: :class:`int`
|
|
998
999
|
|
|
999
1000
|
Number of cores currently submitted or running."""
|
|
1000
|
-
self.
|
|
1001
|
+
self.computing_quotas = ComputingQuotas.from_json(info.get('computingQuotas')) or ComputingQuotas.from_json_legacy(info)
|
|
1002
|
+
""":type: :class:`~qarnot.computing_quotas.ComputingQuotas`
|
|
1003
|
+
|
|
1004
|
+
Computing quotas information of the user and his organization."""
|
|
1005
|
+
self.max_flex_instances = self.computing_quotas.user.flex.max_instances if self.computing_quotas is not None else info.get('maxFlexInstances')
|
|
1001
1006
|
""":type: :class:`int`
|
|
1002
1007
|
|
|
1008
|
+
.. deprecated:: v2.18.0
|
|
1009
|
+
Use `self.computing_quotas` instead.
|
|
1010
|
+
|
|
1003
1011
|
Maximum number of instances simultaneously used with Flex scheduling plan."""
|
|
1004
|
-
self.max_flex_cores = info.get('maxFlexCores')
|
|
1012
|
+
self.max_flex_cores = self.computing_quotas.user.flex.max_cores if self.computing_quotas is not None else info.get('maxFlexCores')
|
|
1005
1013
|
""":type: :class:`int`
|
|
1006
1014
|
|
|
1015
|
+
.. deprecated:: v2.18.0
|
|
1016
|
+
Use `self.computing_quotas` instead.
|
|
1017
|
+
|
|
1007
1018
|
Maximum number of cores simultaneously used with Flex scheduling plan."""
|
|
1008
|
-
self.max_on_demand_instances = info.get('maxOnDemandInstances')
|
|
1019
|
+
self.max_on_demand_instances = self.computing_quotas.user.on_demand.max_instances if self.computing_quotas is not None else info.get('maxOnDemandInstances')
|
|
1009
1020
|
""":type: :class:`int`
|
|
1010
1021
|
|
|
1022
|
+
.. deprecated:: v2.18.0
|
|
1023
|
+
Use `self.computing_quotas` instead.
|
|
1024
|
+
|
|
1011
1025
|
Maximum number of instances simultaneously used with OnDemand scheduling plan."""
|
|
1012
|
-
self.max_on_demand_cores = info.get('maxOnDemandCores')
|
|
1026
|
+
self.max_on_demand_cores = self.computing_quotas.user.on_demand.max_cores if self.computing_quotas is not None else info.get('maxOnDemandCores')
|
|
1013
1027
|
""":type: :class:`int`
|
|
1014
1028
|
|
|
1029
|
+
.. deprecated:: v2.18.0
|
|
1030
|
+
Use `self.computing_quotas` instead.
|
|
1031
|
+
|
|
1015
1032
|
Maximum number of cores simultaneously used with OnDemand scheduling plan."""
|
|
1016
1033
|
|
|
1017
1034
|
|
qarnot/forced_network_rule.py
CHANGED
|
@@ -20,7 +20,18 @@ class ForcedNetworkRule(object):
|
|
|
20
20
|
priority: str = None,
|
|
21
21
|
description: str = None,
|
|
22
22
|
to_qbox: Optional[bool] = None,
|
|
23
|
-
to_payload: Optional[bool] = None
|
|
23
|
+
to_payload: Optional[bool] = None,
|
|
24
|
+
name: str = None,
|
|
25
|
+
application_type: str = None):
|
|
26
|
+
|
|
27
|
+
self.name = name
|
|
28
|
+
""":type: :class:`str`
|
|
29
|
+
|
|
30
|
+
Name of the associated rule."""
|
|
31
|
+
self.application_type = application_type
|
|
32
|
+
""":type: :class:`str`
|
|
33
|
+
|
|
34
|
+
Application layer protocol used / hint about it (e.g. ssh, http, https...)."""
|
|
24
35
|
self.inbound = inbound
|
|
25
36
|
""":type: :class:`bool`
|
|
26
37
|
|
|
@@ -84,6 +95,14 @@ class ForcedNetworkRule(object):
|
|
|
84
95
|
:returns: The created :class:`~qarnot.forced_network_rule.ForcedNetworkRule`
|
|
85
96
|
"""
|
|
86
97
|
|
|
98
|
+
name: str = None
|
|
99
|
+
if 'name' in json:
|
|
100
|
+
name = str(json.get("name"))
|
|
101
|
+
|
|
102
|
+
application_type: str = None
|
|
103
|
+
if 'applicationType' in json:
|
|
104
|
+
application_type = str(json.get("applicationType"))
|
|
105
|
+
|
|
87
106
|
inbound: bool = bool(json.get("inbound"))
|
|
88
107
|
proto: str = str(json.get("proto"))
|
|
89
108
|
|
|
@@ -96,12 +115,12 @@ class ForcedNetworkRule(object):
|
|
|
96
115
|
to = str(json.get("to"))
|
|
97
116
|
|
|
98
117
|
public_host: str = None
|
|
99
|
-
if '
|
|
100
|
-
public_host = str(json.get("
|
|
118
|
+
if 'publicHost' in json:
|
|
119
|
+
public_host = str(json.get("publicHost"))
|
|
101
120
|
|
|
102
121
|
public_port: str = None
|
|
103
|
-
if '
|
|
104
|
-
public_port = str(json.get("
|
|
122
|
+
if 'publicPort' in json:
|
|
123
|
+
public_port = str(json.get("publicPort"))
|
|
105
124
|
|
|
106
125
|
forwarder: str = None
|
|
107
126
|
if 'forwarder' in json:
|
|
@@ -116,12 +135,12 @@ class ForcedNetworkRule(object):
|
|
|
116
135
|
description = str(json.get("description"))
|
|
117
136
|
|
|
118
137
|
to_qbox: Optional[bool] = None
|
|
119
|
-
if '
|
|
120
|
-
to_qbox = bool(json.get("
|
|
138
|
+
if 'toQBox' in json:
|
|
139
|
+
to_qbox = bool(json.get("toQBox"))
|
|
121
140
|
|
|
122
141
|
to_payload: Optional[bool] = None
|
|
123
|
-
if '
|
|
124
|
-
to_payload = bool(json.get("
|
|
142
|
+
if 'toPayload' in json:
|
|
143
|
+
to_payload = bool(json.get("toPayload"))
|
|
125
144
|
|
|
126
145
|
return ForcedNetworkRule(
|
|
127
146
|
inbound,
|
|
@@ -134,7 +153,9 @@ class ForcedNetworkRule(object):
|
|
|
134
153
|
priority,
|
|
135
154
|
description,
|
|
136
155
|
to_qbox,
|
|
137
|
-
to_payload
|
|
156
|
+
to_payload,
|
|
157
|
+
name,
|
|
158
|
+
application_type)
|
|
138
159
|
|
|
139
160
|
def to_json(self):
|
|
140
161
|
result: Dict[str, Union[str, bool]] = {
|
|
@@ -142,6 +163,12 @@ class ForcedNetworkRule(object):
|
|
|
142
163
|
"proto": self.proto,
|
|
143
164
|
}
|
|
144
165
|
|
|
166
|
+
if self.name is not None:
|
|
167
|
+
result["name"] = self.name
|
|
168
|
+
|
|
169
|
+
if self.application_type is not None:
|
|
170
|
+
result["applicationType"] = self.application_type
|
|
171
|
+
|
|
145
172
|
if self.port is not None:
|
|
146
173
|
result["port"] = self.port
|
|
147
174
|
|
|
@@ -149,10 +176,10 @@ class ForcedNetworkRule(object):
|
|
|
149
176
|
result["to"] = self.to
|
|
150
177
|
|
|
151
178
|
if self.public_host is not None:
|
|
152
|
-
result["
|
|
179
|
+
result["publicHost"] = self.public_host
|
|
153
180
|
|
|
154
181
|
if self.public_port is not None:
|
|
155
|
-
result["
|
|
182
|
+
result["publicPort"] = self.public_port
|
|
156
183
|
|
|
157
184
|
if self.forwarder is not None:
|
|
158
185
|
result["forwarder"] = self.forwarder
|
|
@@ -164,9 +191,9 @@ class ForcedNetworkRule(object):
|
|
|
164
191
|
result["description"] = self.description
|
|
165
192
|
|
|
166
193
|
if self.to_qbox is not None:
|
|
167
|
-
result["
|
|
194
|
+
result["toQBox"] = self.to_qbox
|
|
168
195
|
|
|
169
196
|
if self.to_payload is not None:
|
|
170
|
-
result["
|
|
197
|
+
result["toPayload"] = self.to_payload
|
|
171
198
|
|
|
172
199
|
return result
|
qarnot/helper.py
CHANGED
|
@@ -14,7 +14,7 @@ class Log():
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
@staticmethod
|
|
17
|
-
def get_logger_for_stream(stream: TextIO = None, log_format: str = DEFAULT_LOG_FORMAT):
|
|
17
|
+
def get_logger_for_stream(stream: TextIO = None, name: str = None, log_format: str = DEFAULT_LOG_FORMAT):
|
|
18
18
|
"""Create a logger whose output is a stream.
|
|
19
19
|
|
|
20
20
|
:param TextIO stream:
|
|
@@ -31,12 +31,13 @@ class Log():
|
|
|
31
31
|
:rtype: logging.Logger
|
|
32
32
|
:returns: The created logger.
|
|
33
33
|
"""
|
|
34
|
-
|
|
34
|
+
if name is None:
|
|
35
|
+
name = __name__
|
|
35
36
|
formatter = logging.Formatter(log_format)
|
|
36
37
|
handler = logging.StreamHandler(stream if stream is not None else sys.stdout)
|
|
37
38
|
handler.setFormatter(formatter)
|
|
38
39
|
|
|
39
|
-
logger = logging.getLogger(
|
|
40
|
+
logger = logging.getLogger(name)
|
|
40
41
|
logger.addHandler(handler)
|
|
41
42
|
logger.setLevel(logging.INFO)
|
|
42
43
|
return logger
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Multi Slots Settings that can be used when creating a pool"""
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MultiSlotsSettings(object):
|
|
7
|
+
"""Represents task multi slots settings."""
|
|
8
|
+
|
|
9
|
+
_slotsPerNode: int = None
|
|
10
|
+
|
|
11
|
+
def __init__(self, slotsPerNode: int = None):
|
|
12
|
+
"""Create a new :class:`~qarnot.multi_slots_settings.MultiSlotsSettings`.
|
|
13
|
+
|
|
14
|
+
:param slotsPerNode: slots per node
|
|
15
|
+
:type slotsPerNode: int
|
|
16
|
+
"""
|
|
17
|
+
self._slotsPerNode = slotsPerNode
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_json(cls, json: Dict[str, int]):
|
|
21
|
+
"""Create the multi slots settings from json.
|
|
22
|
+
|
|
23
|
+
:param dict json: Dictionary representing the multi slots settings
|
|
24
|
+
:returns: The created :class:`~qarnot.multi_slots_settings.MultiSlotsSettings`
|
|
25
|
+
"""
|
|
26
|
+
slotsPerNode: int = json.get("slotsPerNode")
|
|
27
|
+
return MultiSlotsSettings(slotsPerNode)
|
|
28
|
+
|
|
29
|
+
def to_json(self) -> Dict[str, int]:
|
|
30
|
+
"""Get a dict ready to be json packed.
|
|
31
|
+
|
|
32
|
+
:return: the json elements of the class.
|
|
33
|
+
:rtype: `dict`
|
|
34
|
+
"""
|
|
35
|
+
return {
|
|
36
|
+
"slotsPerNode": self._slotsPerNode
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def __eq__(self, other):
|
|
40
|
+
if other is None or not isinstance(other, MultiSlotsSettings):
|
|
41
|
+
return False
|
|
42
|
+
return self._slotsPerNode == other._slotsPerNode
|
|
43
|
+
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
return "multi slots settings: slotsPerNode {}.".format(self._slotsPerNode)
|
|
46
|
+
|
|
47
|
+
def __repr__(self) -> str:
|
|
48
|
+
return "multi_slots_settings.MultiSlotsSettings(slotsPerNode: {})".format(self._slotsPerNode)
|
qarnot/pool.py
CHANGED
|
@@ -19,6 +19,7 @@ from typing import Dict, List, Optional
|
|
|
19
19
|
|
|
20
20
|
from qarnot.carbon_facts import CarbonClient, CarbonFacts
|
|
21
21
|
from qarnot.retry_settings import RetrySettings
|
|
22
|
+
from qarnot.multi_slots_settings import MultiSlotsSettings
|
|
22
23
|
from qarnot.forced_network_rule import ForcedNetworkRule
|
|
23
24
|
from qarnot.secrets import SecretsAccessRights
|
|
24
25
|
|
|
@@ -86,6 +87,7 @@ class Pool(object):
|
|
|
86
87
|
self._update_cache_time = 5
|
|
87
88
|
self._scheduling_type = scheduling_type
|
|
88
89
|
self._targeted_reserved_machine_key: str = None
|
|
90
|
+
self._targeted_reservation_name: str = None
|
|
89
91
|
|
|
90
92
|
self._last_cache = time.time()
|
|
91
93
|
self._instancecount = instancecount
|
|
@@ -114,6 +116,7 @@ class Pool(object):
|
|
|
114
116
|
self._queued_or_running_task_instances_count = 0.0
|
|
115
117
|
|
|
116
118
|
self._completion_time_to_live = "00:00:00"
|
|
119
|
+
self._max_time_queue_seconds: int = None
|
|
117
120
|
self._auto_delete = False
|
|
118
121
|
self._tasks_wait_for_synchronization = False
|
|
119
122
|
|
|
@@ -127,6 +130,7 @@ class Pool(object):
|
|
|
127
130
|
self._default_resources_cache_ttl_sec: Optional[int] = None
|
|
128
131
|
self._privileges: Privileges = Privileges()
|
|
129
132
|
self._default_retry_settings: RetrySettings = RetrySettings()
|
|
133
|
+
self._multi_slots_settings: Optional[MultiSlotsSettings] = None
|
|
130
134
|
self._forced_network_rules: List[ForcedNetworkRule] = []
|
|
131
135
|
self._secrets_access_rights: SecretsAccessRights = SecretsAccessRights()
|
|
132
136
|
|
|
@@ -215,6 +219,8 @@ class Pool(object):
|
|
|
215
219
|
if 'completionTimeToLive' in json_pool:
|
|
216
220
|
self._completion_time_to_live = json_pool.get("completionTimeToLive")
|
|
217
221
|
|
|
222
|
+
self._max_time_queue_seconds = json_pool.get("maxTimeQueueSeconds", None)
|
|
223
|
+
|
|
218
224
|
if 'elasticProperty' in json_pool:
|
|
219
225
|
elasticProperty = json_pool.get("elasticProperty")
|
|
220
226
|
self._is_elastic = elasticProperty.get("isElastic")
|
|
@@ -235,10 +241,13 @@ class Pool(object):
|
|
|
235
241
|
self._hardware_constraints = [HardwareConstraint.from_json(hw_constraint_dict) for hw_constraint_dict in json_pool.get("hardwareConstraints", [])]
|
|
236
242
|
self._default_resources_cache_ttl_sec = json_pool.get("defaultResourcesCacheTTLSec", None)
|
|
237
243
|
self._targeted_reserved_machine_key = json_pool.get("targetedReservedMachineKey", None)
|
|
244
|
+
self._targeted_reservation_name = json_pool.get("targetedReservationName", None)
|
|
238
245
|
if 'privileges' in json_pool:
|
|
239
246
|
self._privileges = Privileges.from_json(json_pool.get("privileges"))
|
|
240
247
|
if 'defaultRetrySettings' in json_pool:
|
|
241
248
|
self._default_retry_settings = RetrySettings.from_json(json_pool.get("defaultRetrySettings"))
|
|
249
|
+
if 'multiSlotsSettings' in json_pool:
|
|
250
|
+
self._multi_slots_settings = MultiSlotsSettings.from_json(json_pool.get("multiSlotsSettings"))
|
|
242
251
|
if 'schedulingType' in json_pool:
|
|
243
252
|
self._scheduling_type = SchedulingType.from_string(json_pool.get("schedulingType"))
|
|
244
253
|
self._forced_network_rules = [ForcedNetworkRule.from_json(forced_network_dict) for forced_network_dict in json_pool.get("forcedNetworkRules", [])]
|
|
@@ -297,18 +306,27 @@ class Pool(object):
|
|
|
297
306
|
json_pool['privileges'] = self._privileges.to_json()
|
|
298
307
|
json_pool['defaultRetrySettings'] = self._default_retry_settings.to_json()
|
|
299
308
|
|
|
309
|
+
if self._max_time_queue_seconds is not None:
|
|
310
|
+
json_pool['maxTimeQueueSeconds'] = self._max_time_queue_seconds
|
|
311
|
+
|
|
300
312
|
if self._scheduling_type is not None:
|
|
301
313
|
json_pool['schedulingType'] = self._scheduling_type.schedulingType
|
|
302
314
|
|
|
303
315
|
if self._targeted_reserved_machine_key is not None:
|
|
304
316
|
json_pool['targetedReservedMachineKey'] = self._targeted_reserved_machine_key
|
|
305
317
|
|
|
318
|
+
if self._targeted_reservation_name is not None:
|
|
319
|
+
json_pool['targetedReservationName'] = self._targeted_reservation_name
|
|
320
|
+
|
|
306
321
|
if self._forced_network_rules is not None:
|
|
307
322
|
json_pool['forcedNetworkRules'] = [x.to_json() for x in self._forced_network_rules]
|
|
308
323
|
|
|
309
324
|
if self._secrets_access_rights:
|
|
310
325
|
json_pool['secretsAccessRights'] = self._secrets_access_rights.to_json()
|
|
311
326
|
|
|
327
|
+
if self._multi_slots_settings:
|
|
328
|
+
json_pool['multiSlotsSettings'] = self._multi_slots_settings.to_json()
|
|
329
|
+
|
|
312
330
|
return json_pool
|
|
313
331
|
|
|
314
332
|
def submit(self):
|
|
@@ -1273,6 +1291,24 @@ class Pool(object):
|
|
|
1273
1291
|
raise AttributeError("can't set attribute on a submitted job")
|
|
1274
1292
|
self._completion_time_to_live = _util.parse_to_timespan_string(value)
|
|
1275
1293
|
|
|
1294
|
+
@property
|
|
1295
|
+
def multi_slots_settings(self):
|
|
1296
|
+
"""
|
|
1297
|
+
:getter: Returns this pool's multi slots settings.
|
|
1298
|
+
:type: :class:`~qarnot.multi_slots_settings.MultiSlotsSettings`
|
|
1299
|
+
:default_value: None
|
|
1300
|
+
"""
|
|
1301
|
+
self._update_if_summary()
|
|
1302
|
+
return self._multi_slots_settings
|
|
1303
|
+
|
|
1304
|
+
@multi_slots_settings.setter
|
|
1305
|
+
def multi_slots_settings(self, value):
|
|
1306
|
+
"""Setter for multi_slots_settings, this can only be set before pool's submission"""
|
|
1307
|
+
self._update_if_summary()
|
|
1308
|
+
if self._multi_slots_settings is not None:
|
|
1309
|
+
raise AttributeError("can't set attribute on a submitted pool")
|
|
1310
|
+
self._multi_slots_settings = value
|
|
1311
|
+
|
|
1276
1312
|
@property
|
|
1277
1313
|
def previous_state(self):
|
|
1278
1314
|
"""
|
|
@@ -1431,6 +1467,22 @@ class Pool(object):
|
|
|
1431
1467
|
|
|
1432
1468
|
self._privileges._exportApiAndStorageCredentialsInEnvironment = True
|
|
1433
1469
|
|
|
1470
|
+
@property
|
|
1471
|
+
def max_time_queue_seconds(self):
|
|
1472
|
+
"""
|
|
1473
|
+
:type: :class:`uint`
|
|
1474
|
+
:getter: Max time to wait before time out when there is not any place to execute the pool.
|
|
1475
|
+
|
|
1476
|
+
pool's max time queue seconds
|
|
1477
|
+
"""
|
|
1478
|
+
self._update_if_summary()
|
|
1479
|
+
return self._max_time_queue_seconds
|
|
1480
|
+
|
|
1481
|
+
@max_time_queue_seconds.setter
|
|
1482
|
+
def max_time_queue_seconds(self, value: int):
|
|
1483
|
+
"""Setter for max_time_queue_seconds."""
|
|
1484
|
+
self._max_time_queue_seconds = value
|
|
1485
|
+
|
|
1434
1486
|
@property
|
|
1435
1487
|
def default_retry_settings(self) -> RetrySettings:
|
|
1436
1488
|
""":type: :class:`~qarnot.retry_settings.RetrySettings`
|
|
@@ -1475,6 +1527,9 @@ class Pool(object):
|
|
|
1475
1527
|
|
|
1476
1528
|
:getter: The reserved machine key when using the "reserved" scheduling type
|
|
1477
1529
|
|
|
1530
|
+
.. deprecated:: v2.19.0
|
|
1531
|
+
Use `self.targeted_reservation_name` instead.
|
|
1532
|
+
|
|
1478
1533
|
:raises AttributeError: trying to set this after the pool is submitted
|
|
1479
1534
|
"""
|
|
1480
1535
|
return self._targeted_reserved_machine_key
|
|
@@ -1488,6 +1543,25 @@ class Pool(object):
|
|
|
1488
1543
|
|
|
1489
1544
|
self._targeted_reserved_machine_key = value
|
|
1490
1545
|
|
|
1546
|
+
@property
|
|
1547
|
+
def targeted_reservation_name(self) -> str:
|
|
1548
|
+
""":type: :class:`str`
|
|
1549
|
+
|
|
1550
|
+
:getter: The name of the reservation that describes the targeted machines when using the "reserved" scheduling type
|
|
1551
|
+
|
|
1552
|
+
:raises AttributeError: trying to set this after the task is submitted
|
|
1553
|
+
"""
|
|
1554
|
+
return self._targeted_reservation_name
|
|
1555
|
+
|
|
1556
|
+
@targeted_reservation_name.setter
|
|
1557
|
+
def targeted_reservation_name(self, value: str):
|
|
1558
|
+
"""Setted for targeted_reservation_name
|
|
1559
|
+
"""
|
|
1560
|
+
if self.uuid is not None:
|
|
1561
|
+
raise AttributeError("can't set attribute on a launched task")
|
|
1562
|
+
|
|
1563
|
+
self._targeted_reservation_name = value
|
|
1564
|
+
|
|
1491
1565
|
def __repr__(self):
|
|
1492
1566
|
return '{0} - {1} - {2} - {3} - {5} - InstanceCount : {4} - Resources : {6} '\
|
|
1493
1567
|
'Tag {7} - IsElastic {8} - ElasticMin {9} - ElasticMax {10} - ElasticMinIdle {11} -'\
|
qarnot/task.py
CHANGED
|
@@ -19,7 +19,7 @@ from os import makedirs, path
|
|
|
19
19
|
import time
|
|
20
20
|
import warnings
|
|
21
21
|
import sys
|
|
22
|
-
from typing import Dict, Optional, Union, List, Any, Callable
|
|
22
|
+
from typing import Dict, Optional, Union, List, Any, Callable, Sequence
|
|
23
23
|
|
|
24
24
|
from qarnot.carbon_facts import CarbonClient, CarbonFacts
|
|
25
25
|
from qarnot.retry_settings import RetrySettings
|
|
@@ -40,6 +40,7 @@ from .exceptions import MissingTaskException, MaxTaskException, NotEnoughCredits
|
|
|
40
40
|
|
|
41
41
|
try:
|
|
42
42
|
from progressbar import AnimatedMarker, Bar, Percentage, AdaptiveETA, ProgressBar
|
|
43
|
+
from progressbar.widgets import WidgetBase
|
|
43
44
|
except ImportError:
|
|
44
45
|
pass
|
|
45
46
|
|
|
@@ -131,6 +132,7 @@ class Task(object):
|
|
|
131
132
|
self._secrets_access_rights: SecretsAccessRights = SecretsAccessRights()
|
|
132
133
|
self._scheduling_type = scheduling_type
|
|
133
134
|
self._targeted_reserved_machine_key: str = None
|
|
135
|
+
self._targeted_reservation_name: str = None
|
|
134
136
|
self._dependentOn: List[Uuid] = []
|
|
135
137
|
|
|
136
138
|
self._auto_update = True
|
|
@@ -172,6 +174,7 @@ class Task(object):
|
|
|
172
174
|
self._progress = None
|
|
173
175
|
self._execution_time = None
|
|
174
176
|
self._wall_time = None
|
|
177
|
+
self._max_time_queue_seconds: int = None
|
|
175
178
|
self._end_date = None
|
|
176
179
|
self._upload_results_on_cancellation: Optional[bool] = None
|
|
177
180
|
self._hardware_constraints: List[HardwareConstraint] = []
|
|
@@ -513,11 +516,13 @@ class Task(object):
|
|
|
513
516
|
self._progress = json_task.get("progress", None)
|
|
514
517
|
self._execution_time = json_task.get("executionTime", None)
|
|
515
518
|
self._wall_time = json_task.get("wallTime", None)
|
|
519
|
+
self._max_time_queue_seconds = json_task.get("maxTimeQueueSeconds", None)
|
|
516
520
|
self._end_date = json_task.get("endDate", None)
|
|
517
521
|
self._labels = json_task.get("labels", {})
|
|
518
522
|
self._hardware_constraints = [HardwareConstraint.from_json(hw_constraint_dict) for hw_constraint_dict in json_task.get("hardwareConstraints", [])]
|
|
519
523
|
self._default_resources_cache_ttl_sec = json_task.get("defaultResourcesCacheTTLSec", None)
|
|
520
524
|
self._targeted_reserved_machine_key = json_task.get("targetedReservedMachineKey", None)
|
|
525
|
+
self._targeted_reservation_name = json_task.get("targetedReservationName", None)
|
|
521
526
|
if 'privileges' in json_task:
|
|
522
527
|
self._privileges = Privileges.from_json(json_task.get("privileges"))
|
|
523
528
|
if 'retrySettings' in json_task:
|
|
@@ -589,7 +594,7 @@ class Task(object):
|
|
|
589
594
|
|
|
590
595
|
if live_progress:
|
|
591
596
|
try:
|
|
592
|
-
widgets = [
|
|
597
|
+
widgets: Sequence[WidgetBase | str] = [
|
|
593
598
|
Percentage(),
|
|
594
599
|
' ', AnimatedMarker(),
|
|
595
600
|
' ', Bar(),
|
|
@@ -1638,6 +1643,9 @@ class Task(object):
|
|
|
1638
1643
|
|
|
1639
1644
|
:getter: The reserved machine key when using the "reserved" scheduling type
|
|
1640
1645
|
|
|
1646
|
+
.. deprecated:: v2.19.0
|
|
1647
|
+
Use `self.targeted_reservation_name` instead.
|
|
1648
|
+
|
|
1641
1649
|
:raises AttributeError: trying to set this after the task is submitted
|
|
1642
1650
|
"""
|
|
1643
1651
|
return self._targeted_reserved_machine_key
|
|
@@ -1651,6 +1659,25 @@ class Task(object):
|
|
|
1651
1659
|
|
|
1652
1660
|
self._targeted_reserved_machine_key = value
|
|
1653
1661
|
|
|
1662
|
+
@property
|
|
1663
|
+
def targeted_reservation_name(self) -> str:
|
|
1664
|
+
""":type: :class:`str`
|
|
1665
|
+
|
|
1666
|
+
:getter: The name of the reservation that describes the targeted machines when using the "reserved" scheduling type
|
|
1667
|
+
|
|
1668
|
+
:raises AttributeError: trying to set this after the task is submitted
|
|
1669
|
+
"""
|
|
1670
|
+
return self._targeted_reservation_name
|
|
1671
|
+
|
|
1672
|
+
@targeted_reservation_name.setter
|
|
1673
|
+
def targeted_reservation_name(self, value: str):
|
|
1674
|
+
"""Setted for targeted_reservation_name
|
|
1675
|
+
"""
|
|
1676
|
+
if self.uuid is not None:
|
|
1677
|
+
raise AttributeError("can't set attribute on a launched task")
|
|
1678
|
+
|
|
1679
|
+
self._targeted_reservation_name = value
|
|
1680
|
+
|
|
1654
1681
|
@property
|
|
1655
1682
|
def auto_delete(self):
|
|
1656
1683
|
"""Autodelete this Task if it is finished and your max number of task is reach
|
|
@@ -1778,6 +1805,22 @@ class Task(object):
|
|
|
1778
1805
|
self._update_if_summary()
|
|
1779
1806
|
return self._wall_time
|
|
1780
1807
|
|
|
1808
|
+
@property
|
|
1809
|
+
def max_time_queue_seconds(self):
|
|
1810
|
+
"""
|
|
1811
|
+
:type: :class:`uint`
|
|
1812
|
+
:getter: Max time to wait before time out when there is not any place to execute the task.
|
|
1813
|
+
|
|
1814
|
+
task's max time queue seconds
|
|
1815
|
+
"""
|
|
1816
|
+
self._update_if_summary()
|
|
1817
|
+
return self._max_time_queue_seconds
|
|
1818
|
+
|
|
1819
|
+
@max_time_queue_seconds.setter
|
|
1820
|
+
def max_time_queue_seconds(self, value: int):
|
|
1821
|
+
"""Setter for max_time_queue_seconds."""
|
|
1822
|
+
self._max_time_queue_seconds = value
|
|
1823
|
+
|
|
1781
1824
|
@property
|
|
1782
1825
|
def snapshot_interval(self):
|
|
1783
1826
|
"""
|
|
@@ -1836,6 +1879,9 @@ class Task(object):
|
|
|
1836
1879
|
self._resource_object_advanced = [x.to_json() for x in self._resource_objects]
|
|
1837
1880
|
json_task['advancedResourceBuckets'] = self._resource_object_advanced
|
|
1838
1881
|
|
|
1882
|
+
if self._max_time_queue_seconds is not None:
|
|
1883
|
+
json_task['maxTimeQueueSeconds'] = self._max_time_queue_seconds
|
|
1884
|
+
|
|
1839
1885
|
if self._result_object is not None:
|
|
1840
1886
|
json_task['resultBucket'] = self._result_object.uuid
|
|
1841
1887
|
if self._result_object._cache_ttl_sec is not None:
|
|
@@ -1870,6 +1916,9 @@ class Task(object):
|
|
|
1870
1916
|
if self._targeted_reserved_machine_key is not None:
|
|
1871
1917
|
json_task["targetedReservedMachineKey"] = self._targeted_reserved_machine_key
|
|
1872
1918
|
|
|
1919
|
+
if self._targeted_reservation_name is not None:
|
|
1920
|
+
json_task["targetedReservationName"] = self._targeted_reservation_name
|
|
1921
|
+
|
|
1873
1922
|
if self._forced_network_rules is not None:
|
|
1874
1923
|
json_task['forcedNetworkRules'] = [x.to_json() for x in self._forced_network_rules]
|
|
1875
1924
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: qarnot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.19.0
|
|
4
4
|
Summary: Qarnot Computing SDK
|
|
5
5
|
Home-page: https://computing.qarnot.com
|
|
6
6
|
Author: Qarnot computing
|
|
@@ -27,6 +27,7 @@ Dynamic: classifier
|
|
|
27
27
|
Dynamic: description
|
|
28
28
|
Dynamic: home-page
|
|
29
29
|
Dynamic: license
|
|
30
|
+
Dynamic: license-file
|
|
30
31
|
Dynamic: requires-dist
|
|
31
32
|
Dynamic: requires-python
|
|
32
33
|
Dynamic: summary
|
|
@@ -2,29 +2,31 @@ qarnot/__init__.py,sha256=BCIFvWIe1EbzdZAMeKnyK9HYIqNHlSsbaaDMIL7VghA,5626
|
|
|
2
2
|
qarnot/_filter.py,sha256=J--0lOY2rverPEE3zrvuipYkOd9T_4HrW4eVNJqheac,10109
|
|
3
3
|
qarnot/_retry.py,sha256=4QdtI5Y_Jshja8zDxhx_g17dzABCIUGXQsjcPl65u7g,890
|
|
4
4
|
qarnot/_util.py,sha256=fmAq55CmfDTTB7yaOLspayC3Mvre7SCzp0YwWsnwpDA,5673
|
|
5
|
-
qarnot/_version.py,sha256=
|
|
5
|
+
qarnot/_version.py,sha256=cs1IDGNzxS3VmGc68RuKml3o2BY_Hw0Eo2b6p2eCVwI,499
|
|
6
6
|
qarnot/advanced_bucket.py,sha256=dfhHOz0foJfEQoaCdGiS_-28w4Y6rq36ZloPgoEQiLE,7927
|
|
7
7
|
qarnot/bucket.py,sha256=aNuJqfYUm2YCQ0m8SBl384xspYkc9SJy6_G-FEKs8nk,24449
|
|
8
8
|
qarnot/carbon_facts.py,sha256=E_tBMsa2byEMgKAEilIFRsCE6_T978Zl7-ZXk4--PPI,10292
|
|
9
|
-
qarnot/
|
|
9
|
+
qarnot/computing_quotas.py,sha256=gXKAZ1QqnXGzR17yTYNmjDF1TBEXALtCHzEe51wk4to,15882
|
|
10
|
+
qarnot/connection.py,sha256=x71AuXeO33iyxbdoWEQecjsyI_8VSvX7PXztzsNpzLE,46022
|
|
10
11
|
qarnot/error.py,sha256=WCkkILJzOi06Q5QRBfacU41D0MQeFCPsQc9Ub1Y6SXw,734
|
|
11
12
|
qarnot/exceptions.py,sha256=yt_iwCw_9pFdoKeOTxsr05kqW5Gu-th3gSfos5zI26g,2729
|
|
12
13
|
qarnot/forced_constant.py,sha256=-i4b_JO10YiWuJ7Q0bmWE_TEwtf8qSeLlkMTAbH55EM,1309
|
|
13
|
-
qarnot/forced_network_rule.py,sha256=
|
|
14
|
+
qarnot/forced_network_rule.py,sha256=gAalmj5p3MDXEG_jRfszhUTpOSKDMH_BFiXDuSnnyOc,5306
|
|
14
15
|
qarnot/hardware_constraint.py,sha256=YZi4FgaJQ84mxVDT4u2WxqJ-i_bikwssidTRHRc3JHA,13694
|
|
15
|
-
qarnot/helper.py,sha256=
|
|
16
|
+
qarnot/helper.py,sha256=HWy0ollEMoGvCXbAY_FfgkhJjpHRRmkNtavvrJba2PY,1564
|
|
16
17
|
qarnot/job.py,sha256=bag9NbugWCSf18J2c6nFKmWFFusXP58dfYYxd8CMsy8,19508
|
|
18
|
+
qarnot/multi_slots_settings.py,sha256=RsSC-3YGu0wBsQlOgvJ8oMKFvApN6XEGdC5ozgQyXXs,1542
|
|
17
19
|
qarnot/paginate.py,sha256=DaUYDPAS0M8hf0hph8GuMSzTASymSUOpp-WqbfevX-s,1387
|
|
18
|
-
qarnot/pool.py,sha256=
|
|
20
|
+
qarnot/pool.py,sha256=IaRYjCamrLHm9BWpjDmbxRhNIjb8f7m88wDIi6gGRa8,59165
|
|
19
21
|
qarnot/privileges.py,sha256=6j3n8q_RNdZ8bBPD9misHEpS0CbcQByqzxlOu8CS-rU,1896
|
|
20
22
|
qarnot/retry_settings.py,sha256=Illobh-1U8hPdzkffJu5TFKM_NdzCNYS2j1teBljX94,2512
|
|
21
23
|
qarnot/scheduling_type.py,sha256=j90APc1ji7xe2Z9OH9l81tjO12k0Wf9bSaG-_UCyeNI,2223
|
|
22
24
|
qarnot/secrets.py,sha256=v6-1UNhCnnW71eJYMeqhcUYLHnsnONGi4Qa3aoiG9qc,12354
|
|
23
25
|
qarnot/status.py,sha256=dCVsh9_ewIASZcreATbUd2qJ-cUMk0cRkyVLdrOot3Q,12348
|
|
24
26
|
qarnot/storage.py,sha256=jAist_J_6yzmRrkF5jqYNG3mhEP_y7KqCdNv4dJcHuM,6929
|
|
25
|
-
qarnot/task.py,sha256=
|
|
26
|
-
qarnot-2.
|
|
27
|
-
qarnot-2.
|
|
28
|
-
qarnot-2.
|
|
29
|
-
qarnot-2.
|
|
30
|
-
qarnot-2.
|
|
27
|
+
qarnot/task.py,sha256=uUfnF2BDq6X8jcGaVRDgK2emFWydwbH_b2QdwXnhtzc,77172
|
|
28
|
+
qarnot-2.19.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
29
|
+
qarnot-2.19.0.dist-info/METADATA,sha256=rR-mDeDXMVjIRA3PuZbTs2bLt9TAAxPO_EDv9mSRAb0,2543
|
|
30
|
+
qarnot-2.19.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
31
|
+
qarnot-2.19.0.dist-info/top_level.txt,sha256=acRyoLZNyf_kuGTwHQgfZv2MfdTcZstyNjBhOxFtHzU,7
|
|
32
|
+
qarnot-2.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|