qarnot 2.18.0__tar.gz → 2.19.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.
- {qarnot-2.18.0/qarnot.egg-info → qarnot-2.19.0}/PKG-INFO +1 -1
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/_version.py +3 -3
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/computing_quotas.py +16 -4
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/forced_network_rule.py +41 -14
- qarnot-2.19.0/qarnot/multi_slots_settings.py +48 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/pool.py +52 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/task.py +27 -0
- {qarnot-2.18.0 → qarnot-2.19.0/qarnot.egg-info}/PKG-INFO +1 -1
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot.egg-info/SOURCES.txt +1 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/requirements.txt +3 -3
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_pool.py +40 -1
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_task.py +13 -1
- {qarnot-2.18.0 → qarnot-2.19.0}/LICENSE +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/MANIFEST.in +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/README.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/Makefile +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/make.bat +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/_static/qarnot.png +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/carbon_facts.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/computeindex.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/computing_quotas.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/forced_network_rule.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/hardware_constraint.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/job.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/paginate.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/pool.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/privileges.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/retry_settings.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/scheduling_type.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/secrets.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/status.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/compute/task.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/connection.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/exceptions.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/storage/advanced_bucket.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/storage/bucket.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/storage/storage.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/api/storage/storageindex.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/basic.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/conf.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/index.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/installation.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/doc/source/qarnot.rst +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/pyproject.toml +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/__init__.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/_filter.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/_retry.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/_util.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/advanced_bucket.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/bucket.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/carbon_facts.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/connection.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/error.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/exceptions.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/forced_constant.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/hardware_constraint.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/helper.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/job.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/paginate.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/privileges.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/retry_settings.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/scheduling_type.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/secrets.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/status.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot/storage.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot.egg-info/dependency_links.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot.egg-info/requires.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/qarnot.egg-info/top_level.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/requirements-doc.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/requirements-lint.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/requirements-optional.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/requirements-test.txt +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/setup.cfg +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/setup.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_advanced_bucket.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_bucket.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_carbon_facts.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_connection.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_hardware_constraints.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_import.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_job.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_paginate.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_retry.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_secrets.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_status.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/test/test_util.py +0 -0
- {qarnot-2.18.0 → qarnot-2.19.0}/versioneer.py +0 -0
|
@@ -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
|
|
|
@@ -70,7 +70,7 @@ class UserReservedSchedulingQuota(UserSchedulingQuota):
|
|
|
70
70
|
"""Describes a reserved scheduling quota for the user.
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
|
-
def __init__(self, machine_key: str, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
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
74
|
"""Create a new UserReservedSchedulingQuota object describing a reserved scheduling quota for the user.
|
|
75
75
|
|
|
76
76
|
:param str machine_key: Machine key of the reservation.
|
|
@@ -86,6 +86,11 @@ class UserReservedSchedulingQuota(UserSchedulingQuota):
|
|
|
86
86
|
|
|
87
87
|
Machine key of the reservation.
|
|
88
88
|
"""
|
|
89
|
+
self.reservation_name = reservation_name
|
|
90
|
+
""":type: :class:`str`
|
|
91
|
+
|
|
92
|
+
Name of the reservation.
|
|
93
|
+
"""
|
|
89
94
|
|
|
90
95
|
@classmethod
|
|
91
96
|
def from_json(cls, json: Dict[str, Any]):
|
|
@@ -97,6 +102,7 @@ class UserReservedSchedulingQuota(UserSchedulingQuota):
|
|
|
97
102
|
if json is None:
|
|
98
103
|
return None
|
|
99
104
|
return cls(
|
|
105
|
+
json.get('reservationName'),
|
|
100
106
|
json.get('machineKey'),
|
|
101
107
|
json.get('maxCores'),
|
|
102
108
|
json.get('runningCoresCount'),
|
|
@@ -142,7 +148,7 @@ class UserComputingQuotas(object):
|
|
|
142
148
|
return cls(
|
|
143
149
|
UserSchedulingQuota.from_json(json.get('flex')),
|
|
144
150
|
UserSchedulingQuota.from_json(json.get('onDemand')),
|
|
145
|
-
[UserReservedSchedulingQuota.from_json(v) for v in json.get('reserved'
|
|
151
|
+
[UserReservedSchedulingQuota.from_json(v) for v in (json.get('reserved') if json.get('reserved') is not None else []) if v is not None]
|
|
146
152
|
)
|
|
147
153
|
|
|
148
154
|
|
|
@@ -201,7 +207,7 @@ class OrganizationReservedSchedulingQuota(OrganizationSchedulingQuota):
|
|
|
201
207
|
"""Describes a reserved scheduling quota for the organization.
|
|
202
208
|
"""
|
|
203
209
|
|
|
204
|
-
def __init__(self, machine_key: str, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
210
|
+
def __init__(self, reservation_name: str, machine_key: str, max_cores: int, running_cores_count: int, max_instances: int, running_instances_count: int):
|
|
205
211
|
"""Create a new OrganizationReservedSchedulingQuota object describing a reserved scheduling quota for the organization.
|
|
206
212
|
|
|
207
213
|
:param str machine_key: Machine key of the reservation.
|
|
@@ -217,6 +223,11 @@ class OrganizationReservedSchedulingQuota(OrganizationSchedulingQuota):
|
|
|
217
223
|
|
|
218
224
|
Machine key of the reservation.
|
|
219
225
|
"""
|
|
226
|
+
self.reservation_name = reservation_name
|
|
227
|
+
""":type: :class:`str`
|
|
228
|
+
|
|
229
|
+
Name of the reservation.
|
|
230
|
+
"""
|
|
220
231
|
|
|
221
232
|
@classmethod
|
|
222
233
|
def from_json(cls, json: Dict[str, Any]):
|
|
@@ -228,6 +239,7 @@ class OrganizationReservedSchedulingQuota(OrganizationSchedulingQuota):
|
|
|
228
239
|
if json is None:
|
|
229
240
|
return None
|
|
230
241
|
return cls(
|
|
242
|
+
json.get('reservationName'),
|
|
231
243
|
json.get('machineKey'),
|
|
232
244
|
json.get('maxCores'),
|
|
233
245
|
json.get('runningCoresCount'),
|
|
@@ -279,7 +291,7 @@ class OrganizationComputingQuotas(object):
|
|
|
279
291
|
json.get('name'),
|
|
280
292
|
OrganizationSchedulingQuota.from_json(json.get('flex')),
|
|
281
293
|
OrganizationSchedulingQuota.from_json(json.get('onDemand')),
|
|
282
|
-
[OrganizationReservedSchedulingQuota.from_json(v) for v in json.get('reserved'
|
|
294
|
+
[OrganizationReservedSchedulingQuota.from_json(v) for v in (json.get('reserved') if json.get('reserved') is not None else []) if v is not None]
|
|
283
295
|
)
|
|
284
296
|
|
|
285
297
|
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -128,6 +130,7 @@ class Pool(object):
|
|
|
128
130
|
self._default_resources_cache_ttl_sec: Optional[int] = None
|
|
129
131
|
self._privileges: Privileges = Privileges()
|
|
130
132
|
self._default_retry_settings: RetrySettings = RetrySettings()
|
|
133
|
+
self._multi_slots_settings: Optional[MultiSlotsSettings] = None
|
|
131
134
|
self._forced_network_rules: List[ForcedNetworkRule] = []
|
|
132
135
|
self._secrets_access_rights: SecretsAccessRights = SecretsAccessRights()
|
|
133
136
|
|
|
@@ -238,10 +241,13 @@ class Pool(object):
|
|
|
238
241
|
self._hardware_constraints = [HardwareConstraint.from_json(hw_constraint_dict) for hw_constraint_dict in json_pool.get("hardwareConstraints", [])]
|
|
239
242
|
self._default_resources_cache_ttl_sec = json_pool.get("defaultResourcesCacheTTLSec", None)
|
|
240
243
|
self._targeted_reserved_machine_key = json_pool.get("targetedReservedMachineKey", None)
|
|
244
|
+
self._targeted_reservation_name = json_pool.get("targetedReservationName", None)
|
|
241
245
|
if 'privileges' in json_pool:
|
|
242
246
|
self._privileges = Privileges.from_json(json_pool.get("privileges"))
|
|
243
247
|
if 'defaultRetrySettings' in json_pool:
|
|
244
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"))
|
|
245
251
|
if 'schedulingType' in json_pool:
|
|
246
252
|
self._scheduling_type = SchedulingType.from_string(json_pool.get("schedulingType"))
|
|
247
253
|
self._forced_network_rules = [ForcedNetworkRule.from_json(forced_network_dict) for forced_network_dict in json_pool.get("forcedNetworkRules", [])]
|
|
@@ -309,12 +315,18 @@ class Pool(object):
|
|
|
309
315
|
if self._targeted_reserved_machine_key is not None:
|
|
310
316
|
json_pool['targetedReservedMachineKey'] = self._targeted_reserved_machine_key
|
|
311
317
|
|
|
318
|
+
if self._targeted_reservation_name is not None:
|
|
319
|
+
json_pool['targetedReservationName'] = self._targeted_reservation_name
|
|
320
|
+
|
|
312
321
|
if self._forced_network_rules is not None:
|
|
313
322
|
json_pool['forcedNetworkRules'] = [x.to_json() for x in self._forced_network_rules]
|
|
314
323
|
|
|
315
324
|
if self._secrets_access_rights:
|
|
316
325
|
json_pool['secretsAccessRights'] = self._secrets_access_rights.to_json()
|
|
317
326
|
|
|
327
|
+
if self._multi_slots_settings:
|
|
328
|
+
json_pool['multiSlotsSettings'] = self._multi_slots_settings.to_json()
|
|
329
|
+
|
|
318
330
|
return json_pool
|
|
319
331
|
|
|
320
332
|
def submit(self):
|
|
@@ -1279,6 +1291,24 @@ class Pool(object):
|
|
|
1279
1291
|
raise AttributeError("can't set attribute on a submitted job")
|
|
1280
1292
|
self._completion_time_to_live = _util.parse_to_timespan_string(value)
|
|
1281
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
|
+
|
|
1282
1312
|
@property
|
|
1283
1313
|
def previous_state(self):
|
|
1284
1314
|
"""
|
|
@@ -1497,6 +1527,9 @@ class Pool(object):
|
|
|
1497
1527
|
|
|
1498
1528
|
:getter: The reserved machine key when using the "reserved" scheduling type
|
|
1499
1529
|
|
|
1530
|
+
.. deprecated:: v2.19.0
|
|
1531
|
+
Use `self.targeted_reservation_name` instead.
|
|
1532
|
+
|
|
1500
1533
|
:raises AttributeError: trying to set this after the pool is submitted
|
|
1501
1534
|
"""
|
|
1502
1535
|
return self._targeted_reserved_machine_key
|
|
@@ -1510,6 +1543,25 @@ class Pool(object):
|
|
|
1510
1543
|
|
|
1511
1544
|
self._targeted_reserved_machine_key = value
|
|
1512
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
|
+
|
|
1513
1565
|
def __repr__(self):
|
|
1514
1566
|
return '{0} - {1} - {2} - {3} - {5} - InstanceCount : {4} - Resources : {6} '\
|
|
1515
1567
|
'Tag {7} - IsElastic {8} - ElasticMin {9} - ElasticMax {10} - ElasticMinIdle {11} -'\
|
|
@@ -132,6 +132,7 @@ class Task(object):
|
|
|
132
132
|
self._secrets_access_rights: SecretsAccessRights = SecretsAccessRights()
|
|
133
133
|
self._scheduling_type = scheduling_type
|
|
134
134
|
self._targeted_reserved_machine_key: str = None
|
|
135
|
+
self._targeted_reservation_name: str = None
|
|
135
136
|
self._dependentOn: List[Uuid] = []
|
|
136
137
|
|
|
137
138
|
self._auto_update = True
|
|
@@ -521,6 +522,7 @@ class Task(object):
|
|
|
521
522
|
self._hardware_constraints = [HardwareConstraint.from_json(hw_constraint_dict) for hw_constraint_dict in json_task.get("hardwareConstraints", [])]
|
|
522
523
|
self._default_resources_cache_ttl_sec = json_task.get("defaultResourcesCacheTTLSec", None)
|
|
523
524
|
self._targeted_reserved_machine_key = json_task.get("targetedReservedMachineKey", None)
|
|
525
|
+
self._targeted_reservation_name = json_task.get("targetedReservationName", None)
|
|
524
526
|
if 'privileges' in json_task:
|
|
525
527
|
self._privileges = Privileges.from_json(json_task.get("privileges"))
|
|
526
528
|
if 'retrySettings' in json_task:
|
|
@@ -1641,6 +1643,9 @@ class Task(object):
|
|
|
1641
1643
|
|
|
1642
1644
|
:getter: The reserved machine key when using the "reserved" scheduling type
|
|
1643
1645
|
|
|
1646
|
+
.. deprecated:: v2.19.0
|
|
1647
|
+
Use `self.targeted_reservation_name` instead.
|
|
1648
|
+
|
|
1644
1649
|
:raises AttributeError: trying to set this after the task is submitted
|
|
1645
1650
|
"""
|
|
1646
1651
|
return self._targeted_reserved_machine_key
|
|
@@ -1654,6 +1659,25 @@ class Task(object):
|
|
|
1654
1659
|
|
|
1655
1660
|
self._targeted_reserved_machine_key = value
|
|
1656
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
|
+
|
|
1657
1681
|
@property
|
|
1658
1682
|
def auto_delete(self):
|
|
1659
1683
|
"""Autodelete this Task if it is finished and your max number of task is reach
|
|
@@ -1892,6 +1916,9 @@ class Task(object):
|
|
|
1892
1916
|
if self._targeted_reserved_machine_key is not None:
|
|
1893
1917
|
json_task["targetedReservedMachineKey"] = self._targeted_reserved_machine_key
|
|
1894
1918
|
|
|
1919
|
+
if self._targeted_reservation_name is not None:
|
|
1920
|
+
json_task["targetedReservationName"] = self._targeted_reservation_name
|
|
1921
|
+
|
|
1895
1922
|
if self._forced_network_rules is not None:
|
|
1896
1923
|
json_task['forcedNetworkRules'] = [x.to_json() for x in self._forced_network_rules]
|
|
1897
1924
|
|
|
@@ -3,6 +3,7 @@ import uuid
|
|
|
3
3
|
import pytest
|
|
4
4
|
import qarnot
|
|
5
5
|
from qarnot.forced_network_rule import ForcedNetworkRule
|
|
6
|
+
from qarnot.multi_slots_settings import MultiSlotsSettings
|
|
6
7
|
from qarnot.pool import Pool
|
|
7
8
|
from qarnot.privileges import Privileges
|
|
8
9
|
from qarnot.bucket import Bucket
|
|
@@ -263,6 +264,19 @@ class TestPoolProperties:
|
|
|
263
264
|
assert {"prefix": "another/prefix"} in json_pool['secretsAccessRights']["byPrefix"]
|
|
264
265
|
|
|
265
266
|
|
|
267
|
+
def test_pool_multi_slots_settings_deserialization(self):
|
|
268
|
+
pool = Pool(self.conn, "pool-multi-slots-deserialization", "profile")
|
|
269
|
+
pool._update(default_json_pool)
|
|
270
|
+
assert MultiSlotsSettings(4) == pool.multi_slots_settings
|
|
271
|
+
|
|
272
|
+
def test_pool_multi_slots_settings_deserialization_none(self):
|
|
273
|
+
pool = Pool(self.conn, "pool-multi-slots-deserialization-none", "profile")
|
|
274
|
+
|
|
275
|
+
pool_json = copy.deepcopy(default_json_pool)
|
|
276
|
+
del pool_json["multiSlotsSettings"]
|
|
277
|
+
pool._update(pool_json)
|
|
278
|
+
assert None == pool.multi_slots_settings
|
|
279
|
+
|
|
266
280
|
@pytest.mark.parametrize("json", [
|
|
267
281
|
{
|
|
268
282
|
"bySecret": [
|
|
@@ -388,6 +402,7 @@ class TestPoolProperties:
|
|
|
388
402
|
def test_pool_reserved_scheduling_serialization(self):
|
|
389
403
|
pool = Pool(self.conn, "pool-with-reserved-scheduling", "profile", scheduling_type=ReservedScheduling())
|
|
390
404
|
pool.targeted_reserved_machine_key = "reservedMachine"
|
|
405
|
+
pool.targeted_reservation_name = "my-reservation"
|
|
391
406
|
assert pool.scheduling_type is not None
|
|
392
407
|
print(pool.scheduling_type)
|
|
393
408
|
assert isinstance(pool.scheduling_type, ReservedScheduling)
|
|
@@ -412,10 +427,24 @@ class TestPoolProperties:
|
|
|
412
427
|
assert isinstance(pool_from_json.scheduling_type, ReservedScheduling)
|
|
413
428
|
assert pool_from_json.scheduling_type.schedulingType == ReservedScheduling.schedulingType
|
|
414
429
|
assert pool.targeted_reserved_machine_key == "reservedMachine"
|
|
430
|
+
assert pool.targeted_reservation_name == "my-reservation"
|
|
431
|
+
|
|
432
|
+
def test_pool_multi_slots_settings_serialization(self):
|
|
433
|
+
pool = Pool(self.conn, "pool-test-multi-slots-settings-serialization", "profile")
|
|
434
|
+
pool.multi_slots_settings = MultiSlotsSettings(4)
|
|
435
|
+
|
|
436
|
+
json_pool = pool._to_json()
|
|
437
|
+
assert json_pool["multiSlotsSettings"]["slotsPerNode"] == 4
|
|
438
|
+
|
|
439
|
+
def test_pool_no_multi_slots_settings_serialization(self):
|
|
440
|
+
pool = Pool(self.conn, "pool-test-no-multi-slots-settings-serialization", "profile")
|
|
441
|
+
|
|
442
|
+
json_pool = pool._to_json()
|
|
443
|
+
assert "multiSlotsSettings" not in json_pool
|
|
415
444
|
|
|
416
445
|
def test_pool_forced_network_rules_serialization(self):
|
|
417
446
|
pool = Pool(self.conn, "pool-with-forced-network-rules", "profile")
|
|
418
|
-
inbound_rule = ForcedNetworkRule(True, "tcp", "1234", "bound-to-be-alive", priority="1000", description="Inbound test")
|
|
447
|
+
inbound_rule = ForcedNetworkRule(True, "tcp", "1234", "bound-to-be-alive", priority="1000", description="Inbound test", name="my-living-network", application_type="https")
|
|
419
448
|
outbound_rule = ForcedNetworkRule(False, "tcp", public_port="666", public_host="bound-to-the-devil", priority="1000", description="Outbound test")
|
|
420
449
|
rules = [
|
|
421
450
|
inbound_rule,
|
|
@@ -435,11 +464,17 @@ class TestPoolProperties:
|
|
|
435
464
|
assert json_inbound_rule["to"] == inbound_rule.to
|
|
436
465
|
assert json_inbound_rule["priority"] == inbound_rule.priority
|
|
437
466
|
assert json_inbound_rule["description"] == inbound_rule.description
|
|
467
|
+
assert json_inbound_rule["name"] == inbound_rule.name
|
|
468
|
+
assert json_inbound_rule["applicationType"] == inbound_rule.application_type
|
|
438
469
|
json_outbound_rule = json_pool['forcedNetworkRules'][1]
|
|
439
470
|
assert json_outbound_rule["inbound"] == outbound_rule.inbound
|
|
440
471
|
assert json_outbound_rule["proto"] == outbound_rule.proto
|
|
441
472
|
assert json_outbound_rule["priority"] == outbound_rule.priority
|
|
442
473
|
assert json_outbound_rule["description"] == outbound_rule.description
|
|
474
|
+
assert json_outbound_rule["publicHost"] == outbound_rule.public_host
|
|
475
|
+
assert json_outbound_rule["publicPort"] == outbound_rule.public_port
|
|
476
|
+
assert json_outbound_rule.get("name") is None
|
|
477
|
+
assert json_outbound_rule.get("applicationType") is None
|
|
443
478
|
|
|
444
479
|
# fields that need to be non null for the deserialization to not fail
|
|
445
480
|
json_pool['creationDate'] = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -459,8 +494,12 @@ class TestPoolProperties:
|
|
|
459
494
|
assert inbound_from_json.to == inbound_rule.to
|
|
460
495
|
assert inbound_from_json.priority == inbound_rule.priority
|
|
461
496
|
assert inbound_from_json.description == inbound_rule.description
|
|
497
|
+
assert inbound_from_json.name == inbound_rule.name
|
|
498
|
+
assert inbound_from_json.application_type == inbound_rule.application_type
|
|
462
499
|
outbound_from_json = pool_from_json.forced_network_rules[1]
|
|
463
500
|
assert outbound_from_json.inbound == outbound_rule.inbound
|
|
464
501
|
assert outbound_from_json.proto == outbound_rule.proto
|
|
465
502
|
assert outbound_from_json.priority == outbound_rule.priority
|
|
466
503
|
assert outbound_from_json.description == outbound_rule.description
|
|
504
|
+
assert outbound_from_json.public_host == outbound_rule.public_host
|
|
505
|
+
assert outbound_from_json.public_port == outbound_rule.public_port
|
|
@@ -368,6 +368,7 @@ class TestTaskProperties:
|
|
|
368
368
|
def test_task_reserved_scheduling_serialization(self, mock_conn):
|
|
369
369
|
task = Task(mock_conn, "task-with-reserved-scheduling", scheduling_type=ReservedScheduling())
|
|
370
370
|
task.targeted_reserved_machine_key = "reservedMachine"
|
|
371
|
+
task.targeted_reservation_name = "my-reservation"
|
|
371
372
|
assert task.scheduling_type is not None
|
|
372
373
|
print(task.scheduling_type)
|
|
373
374
|
assert isinstance(task.scheduling_type, ReservedScheduling)
|
|
@@ -392,6 +393,7 @@ class TestTaskProperties:
|
|
|
392
393
|
assert isinstance(task_from_json.scheduling_type, ReservedScheduling)
|
|
393
394
|
assert task_from_json.scheduling_type.schedulingType == ReservedScheduling.schedulingType
|
|
394
395
|
assert task.targeted_reserved_machine_key == "reservedMachine"
|
|
396
|
+
assert task.targeted_reservation_name == "my-reservation"
|
|
395
397
|
|
|
396
398
|
def test_task_secrets_access_rights_are_serialized_correctly(self, mock_conn):
|
|
397
399
|
task = Task(mock_conn, "task-secrets-access-rights-serialization")
|
|
@@ -464,7 +466,7 @@ class TestTaskProperties:
|
|
|
464
466
|
|
|
465
467
|
def test_task_forced_network_rules_serialization(self, mock_conn):
|
|
466
468
|
task = Task(mock_conn, "task-with-forced-network-rules")
|
|
467
|
-
inbound_rule = ForcedNetworkRule(True, "tcp", "1234", "bound-to-be-alive", priority="1000", description="Inbound test")
|
|
469
|
+
inbound_rule = ForcedNetworkRule(True, "tcp", "1234", "bound-to-be-alive", priority="1000", description="Inbound test", name="my-living-network", application_type="https")
|
|
468
470
|
outbound_rule = ForcedNetworkRule(False, "tcp", public_port="666", public_host="bound-to-the-devil", priority="1000", description="Outbound test")
|
|
469
471
|
rules = [
|
|
470
472
|
inbound_rule,
|
|
@@ -484,11 +486,17 @@ class TestTaskProperties:
|
|
|
484
486
|
assert json_inbound_rule["to"] == inbound_rule.to
|
|
485
487
|
assert json_inbound_rule["priority"] == inbound_rule.priority
|
|
486
488
|
assert json_inbound_rule["description"] == inbound_rule.description
|
|
489
|
+
assert json_inbound_rule["name"] == inbound_rule.name
|
|
490
|
+
assert json_inbound_rule["applicationType"] == inbound_rule.application_type
|
|
487
491
|
json_outbound_rule = json_task['forcedNetworkRules'][1]
|
|
488
492
|
assert json_outbound_rule["inbound"] == outbound_rule.inbound
|
|
489
493
|
assert json_outbound_rule["proto"] == outbound_rule.proto
|
|
490
494
|
assert json_outbound_rule["priority"] == outbound_rule.priority
|
|
491
495
|
assert json_outbound_rule["description"] == outbound_rule.description
|
|
496
|
+
assert json_outbound_rule["publicHost"] == outbound_rule.public_host
|
|
497
|
+
assert json_outbound_rule["publicPort"] == outbound_rule.public_port
|
|
498
|
+
assert json_outbound_rule.get("name") is None
|
|
499
|
+
assert json_outbound_rule.get("applicationType") is None
|
|
492
500
|
|
|
493
501
|
# fields that need to be non null for the deserialization to not fail
|
|
494
502
|
json_task['creationDate'] = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -508,11 +516,15 @@ class TestTaskProperties:
|
|
|
508
516
|
assert inbound_from_json.to == inbound_rule.to
|
|
509
517
|
assert inbound_from_json.priority == inbound_rule.priority
|
|
510
518
|
assert inbound_from_json.description == inbound_rule.description
|
|
519
|
+
assert inbound_from_json.name == inbound_rule.name
|
|
520
|
+
assert inbound_from_json.application_type == inbound_rule.application_type
|
|
511
521
|
outbound_from_json = task_from_json.forced_network_rules[1]
|
|
512
522
|
assert outbound_from_json.inbound == outbound_rule.inbound
|
|
513
523
|
assert outbound_from_json.proto == outbound_rule.proto
|
|
514
524
|
assert outbound_from_json.priority == outbound_rule.priority
|
|
515
525
|
assert outbound_from_json.description == outbound_rule.description
|
|
526
|
+
assert outbound_from_json.public_host == outbound_rule.public_host
|
|
527
|
+
assert outbound_from_json.public_port == outbound_rule.public_port
|
|
516
528
|
|
|
517
529
|
# WARNING: this test last at least 80s because task.wait() wait for 10s between each update calls and the task go through 8 different states
|
|
518
530
|
# To make the test faster some states can be removed (the 7 first states are all the states that correspond to a non complete task and keep
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|