nextmv 1.0.0.dev1__py3-none-any.whl → 1.0.0.dev3__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.
- nextmv/__about__.py +1 -1
- nextmv/cli/cloud/__init__.py +4 -0
- nextmv/cli/cloud/batch/get.py +1 -1
- nextmv/cli/cloud/input_set/create.py +5 -3
- nextmv/cli/cloud/input_set/update.py +1 -1
- nextmv/cli/cloud/scenario/get.py +1 -1
- nextmv/cli/cloud/secrets/update.py +1 -1
- nextmv/cli/cloud/shadow/__init__.py +33 -0
- nextmv/cli/cloud/shadow/create.py +184 -0
- nextmv/cli/cloud/shadow/delete.py +68 -0
- nextmv/cli/cloud/shadow/get.py +61 -0
- nextmv/cli/cloud/shadow/list.py +63 -0
- nextmv/cli/cloud/shadow/metadata.py +66 -0
- nextmv/cli/cloud/shadow/start.py +43 -0
- nextmv/cli/cloud/shadow/stop.py +43 -0
- nextmv/cli/cloud/shadow/update.py +96 -0
- nextmv/cli/cloud/switchback/__init__.py +33 -0
- nextmv/cli/cloud/switchback/create.py +147 -0
- nextmv/cli/cloud/switchback/delete.py +68 -0
- nextmv/cli/cloud/switchback/get.py +62 -0
- nextmv/cli/cloud/switchback/list.py +63 -0
- nextmv/cli/cloud/switchback/metadata.py +68 -0
- nextmv/cli/cloud/switchback/start.py +43 -0
- nextmv/cli/cloud/switchback/stop.py +43 -0
- nextmv/cli/cloud/switchback/update.py +96 -0
- nextmv/cli/options.py +28 -0
- nextmv/cloud/__init__.py +10 -0
- nextmv/cloud/application/__init__.py +4 -0
- nextmv/cloud/application/_shadow.py +314 -0
- nextmv/cloud/application/_switchback.py +315 -0
- nextmv/cloud/shadow.py +190 -0
- nextmv/cloud/switchback.py +189 -0
- {nextmv-1.0.0.dev1.dist-info → nextmv-1.0.0.dev3.dist-info}/METADATA +1 -1
- {nextmv-1.0.0.dev1.dist-info → nextmv-1.0.0.dev3.dist-info}/RECORD +37 -15
- {nextmv-1.0.0.dev1.dist-info → nextmv-1.0.0.dev3.dist-info}/WHEEL +0 -0
- {nextmv-1.0.0.dev1.dist-info → nextmv-1.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {nextmv-1.0.0.dev1.dist-info → nextmv-1.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application mixin for managing switchback tests.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from nextmv.cloud.switchback import SwitchbackTest, SwitchbackTestMetadata, TestComparisonSingle
|
|
9
|
+
from nextmv.run import Run
|
|
10
|
+
from nextmv.safe import safe_id
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from . import Application
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ApplicationSwitchbackMixin:
|
|
17
|
+
"""
|
|
18
|
+
Mixin class for managing switchback tests within an application.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def switchback_test(self: "Application", switchback_test_id: str) -> SwitchbackTest:
|
|
22
|
+
"""
|
|
23
|
+
Get a switchback test. This method also returns the runs of the switchback
|
|
24
|
+
test under the `.runs` attribute.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
switchback_test_id : str
|
|
29
|
+
ID of the switchback test.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
SwitchbackTest
|
|
34
|
+
The requested switchback test details.
|
|
35
|
+
|
|
36
|
+
Raises
|
|
37
|
+
------
|
|
38
|
+
requests.HTTPError
|
|
39
|
+
If the response status code is not 2xx.
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
--------
|
|
43
|
+
>>> switchback_test = app.switchback_test("switchback-123")
|
|
44
|
+
>>> print(switchback_test.name)
|
|
45
|
+
'My Switchback Test'
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
response = self.client.request(
|
|
49
|
+
method="GET",
|
|
50
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
exp = SwitchbackTest.from_dict(response.json())
|
|
54
|
+
|
|
55
|
+
runs_response = self.client.request(
|
|
56
|
+
method="GET",
|
|
57
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/runs",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
runs = [Run.from_dict(run) for run in runs_response.json().get("runs", [])]
|
|
61
|
+
exp.runs = runs
|
|
62
|
+
|
|
63
|
+
return exp
|
|
64
|
+
|
|
65
|
+
def switchback_test_metadata(self: "Application", switchback_test_id: str) -> SwitchbackTestMetadata:
|
|
66
|
+
"""
|
|
67
|
+
Get metadata for a switchback test.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
switchback_test_id : str
|
|
72
|
+
ID of the switchback test.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
SwitchbackTestMetadata
|
|
77
|
+
The requested switchback test metadata.
|
|
78
|
+
|
|
79
|
+
Raises
|
|
80
|
+
------
|
|
81
|
+
requests.HTTPError
|
|
82
|
+
If the response status code is not 2xx.
|
|
83
|
+
|
|
84
|
+
Examples
|
|
85
|
+
--------
|
|
86
|
+
>>> metadata = app.switchback_test_metadata("switchback-123")
|
|
87
|
+
>>> print(metadata.name)
|
|
88
|
+
'My Switchback Test'
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
response = self.client.request(
|
|
92
|
+
method="GET",
|
|
93
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/metadata",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return SwitchbackTestMetadata.from_dict(response.json())
|
|
97
|
+
|
|
98
|
+
def delete_switchback_test(self: "Application", switchback_test_id: str) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Delete a switchback test.
|
|
101
|
+
|
|
102
|
+
Deletes a switchback test along with all the associated information,
|
|
103
|
+
such as its runs.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
switchback_test_id : str
|
|
108
|
+
ID of the switchback test to delete.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
requests.HTTPError
|
|
113
|
+
If the response status code is not 2xx.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> app.delete_switchback_test("switchback-123")
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
_ = self.client.request(
|
|
121
|
+
method="DELETE",
|
|
122
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def list_switchback_tests(self: "Application") -> list[SwitchbackTest]:
|
|
126
|
+
"""
|
|
127
|
+
List all switchback tests.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
list[SwitchbackTest]
|
|
132
|
+
List of switchback tests.
|
|
133
|
+
|
|
134
|
+
Raises
|
|
135
|
+
------
|
|
136
|
+
requests.HTTPError
|
|
137
|
+
If the response status code is not 2xx.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
response = self.client.request(
|
|
141
|
+
method="GET",
|
|
142
|
+
endpoint=f"{self.experiments_endpoint}/switchback",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return [SwitchbackTest.from_dict(switchback_test) for switchback_test in response.json().get("items", [])]
|
|
146
|
+
|
|
147
|
+
def new_switchback_test(
|
|
148
|
+
self: "Application",
|
|
149
|
+
comparison: TestComparisonSingle,
|
|
150
|
+
unit_duration_minutes: float,
|
|
151
|
+
units: int,
|
|
152
|
+
switchback_test_id: str | None = None,
|
|
153
|
+
name: str | None = None,
|
|
154
|
+
description: str | None = None,
|
|
155
|
+
start: datetime | None = None,
|
|
156
|
+
) -> SwitchbackTest:
|
|
157
|
+
"""
|
|
158
|
+
Create a new switchback test in draft mode. Switchback tests are
|
|
159
|
+
experiments that alternate between different instances over specified
|
|
160
|
+
time intervals.
|
|
161
|
+
|
|
162
|
+
Use the `comparison` parameter to define how to set up the instance
|
|
163
|
+
comparison. The test will alternate between the baseline and candidate
|
|
164
|
+
instances defined in the comparison.
|
|
165
|
+
|
|
166
|
+
You may specify `start` to make the switchback test start at a
|
|
167
|
+
specific time. Alternatively, you may use the `start_switchback_test`
|
|
168
|
+
method to start the test.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
comparison : TestComparisonSingle
|
|
173
|
+
Comparison defining the baseline and candidate instances.
|
|
174
|
+
unit_duration_minutes : float
|
|
175
|
+
Duration of each interval in minutes.
|
|
176
|
+
units : int
|
|
177
|
+
Total number of intervals in the switchback test.
|
|
178
|
+
switchback_test_id : Optional[str], default=None
|
|
179
|
+
Optional ID for the switchback test. Will be generated if not
|
|
180
|
+
provided.
|
|
181
|
+
name : Optional[str], default=None
|
|
182
|
+
Optional name of the switchback test. If not provided, the ID will
|
|
183
|
+
be used as the name.
|
|
184
|
+
description : Optional[str], default=None
|
|
185
|
+
Optional description of the switchback test.
|
|
186
|
+
start : Optional[datetime], default=None
|
|
187
|
+
Optional scheduled start time for the switchback test.
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
SwitchbackTest
|
|
192
|
+
The created switchback test.
|
|
193
|
+
|
|
194
|
+
Raises
|
|
195
|
+
------
|
|
196
|
+
requests.HTTPError
|
|
197
|
+
If the response status code is not 2xx.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
# Generate ID if not provided
|
|
201
|
+
if switchback_test_id is None:
|
|
202
|
+
switchback_test_id = safe_id("switchback")
|
|
203
|
+
|
|
204
|
+
# Use ID as name if name not provided
|
|
205
|
+
if name is None:
|
|
206
|
+
name = switchback_test_id
|
|
207
|
+
|
|
208
|
+
payload = {
|
|
209
|
+
"id": id,
|
|
210
|
+
"name": name,
|
|
211
|
+
"comparison": comparison,
|
|
212
|
+
"generate_random_plan": {
|
|
213
|
+
"unit_duration_minutes": unit_duration_minutes,
|
|
214
|
+
"units": units,
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if description is not None:
|
|
219
|
+
payload["description"] = description
|
|
220
|
+
if start is not None:
|
|
221
|
+
payload["generate_random_plan"]["start"] = start.isoformat()
|
|
222
|
+
|
|
223
|
+
response = self.client.request(
|
|
224
|
+
method="POST",
|
|
225
|
+
endpoint=f"{self.experiments_endpoint}/switchback",
|
|
226
|
+
payload=payload,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return SwitchbackTest.from_dict(response.json())
|
|
230
|
+
|
|
231
|
+
def start_switchback_test(self: "Application", switchback_test_id: str) -> None:
|
|
232
|
+
"""
|
|
233
|
+
Start a switchback test. Create a switchback test in draft mode using the
|
|
234
|
+
`new_switchback_test` method, then use this method to start the test.
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
switchback_test_id : str
|
|
239
|
+
ID of the switchback test to start.
|
|
240
|
+
|
|
241
|
+
Raises
|
|
242
|
+
------
|
|
243
|
+
requests.HTTPError
|
|
244
|
+
If the response status code is not 2xx.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
_ = self.client.request(
|
|
248
|
+
method="PUT",
|
|
249
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/start",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def stop_switchback_test(self: "Application", switchback_test_id: str) -> None:
|
|
253
|
+
"""
|
|
254
|
+
Stop a switchback test. The test should already have started before using
|
|
255
|
+
this method.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
switchback_test_id : str
|
|
260
|
+
ID of the switchback test to stop.
|
|
261
|
+
|
|
262
|
+
Raises
|
|
263
|
+
------
|
|
264
|
+
requests.HTTPError
|
|
265
|
+
If the response status code is not 2xx.
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
_ = self.client.request(
|
|
269
|
+
method="PUT",
|
|
270
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/stop",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def update_switchback_test(
|
|
274
|
+
self: "Application",
|
|
275
|
+
switchback_test_id: str,
|
|
276
|
+
name: str | None = None,
|
|
277
|
+
description: str | None = None,
|
|
278
|
+
) -> SwitchbackTest:
|
|
279
|
+
"""
|
|
280
|
+
Update a switchback test.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
switchback_test_id : str
|
|
285
|
+
ID of the switchback test to update.
|
|
286
|
+
name : Optional[str], default=None
|
|
287
|
+
Optional name of the switchback test.
|
|
288
|
+
description : Optional[str], default=None
|
|
289
|
+
Optional description of the switchback test.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
SwitchbackTest
|
|
294
|
+
The information with the updated switchback test.
|
|
295
|
+
|
|
296
|
+
Raises
|
|
297
|
+
------
|
|
298
|
+
requests.HTTPError
|
|
299
|
+
If the response status code is not 2xx.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
payload = {}
|
|
303
|
+
|
|
304
|
+
if name is not None:
|
|
305
|
+
payload["name"] = name
|
|
306
|
+
if description is not None:
|
|
307
|
+
payload["description"] = description
|
|
308
|
+
|
|
309
|
+
response = self.client.request(
|
|
310
|
+
method="PATCH",
|
|
311
|
+
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
|
|
312
|
+
payload=payload,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return SwitchbackTest.from_dict(response.json())
|
nextmv/cloud/shadow.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classes for working with Nextmv Cloud shadow tests.
|
|
3
|
+
|
|
4
|
+
This module provides classes for interacting with shadow tests in Nextmv Cloud.
|
|
5
|
+
It details the core data structures for these types of experiments.
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
TestComparison
|
|
10
|
+
A structure to define comparison parameters for tests.
|
|
11
|
+
StartEvents
|
|
12
|
+
A structure to define start events for tests.
|
|
13
|
+
TerminationEvents
|
|
14
|
+
A structure to define termination events for tests.
|
|
15
|
+
ShadowTestMetadata
|
|
16
|
+
Metadata for a Nextmv Cloud shadow test.
|
|
17
|
+
ShadowTest
|
|
18
|
+
A Nextmv Cloud shadow test definition.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from pydantic import AliasChoices, Field
|
|
25
|
+
|
|
26
|
+
from nextmv.base_model import BaseModel
|
|
27
|
+
from nextmv.cloud.batch_experiment import ExperimentStatus
|
|
28
|
+
from nextmv.run import Run
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestComparison(BaseModel):
|
|
32
|
+
"""
|
|
33
|
+
A structure to define comparison parameters for tests.
|
|
34
|
+
|
|
35
|
+
You can import the `TestComparison` class directly from `cloud`:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from nextmv.cloud import TestComparison
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
baseline_instance_id : str
|
|
44
|
+
ID of the baseline instance for comparison.
|
|
45
|
+
candidate_instance_ids : list[str]
|
|
46
|
+
List of candidate instance IDs to compare against the baseline.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
baseline_instance_id: str
|
|
50
|
+
"""ID of the baseline instance for comparison."""
|
|
51
|
+
candidate_instance_ids: list[str]
|
|
52
|
+
"""List of candidate instance IDs to compare against the baseline."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class StartEvents(BaseModel):
|
|
56
|
+
"""
|
|
57
|
+
A structure to define start events for tests.
|
|
58
|
+
|
|
59
|
+
You can import the `StartEvents` class directly from `cloud`:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from nextmv.cloud import StartEvents
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
time : datetime, optional
|
|
68
|
+
Scheduled time for the test to start.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
time: datetime | None = None
|
|
72
|
+
"""Scheduled time for the test to start."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TerminationEvents(BaseModel):
|
|
76
|
+
"""
|
|
77
|
+
A structure to define termination events for tests.
|
|
78
|
+
|
|
79
|
+
You can import the `TerminationEvents` class directly from `cloud`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from nextmv.cloud import TerminationEvents
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
time : datetime, optional
|
|
88
|
+
Scheduled time for the test to terminate.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
maximum_runs: int
|
|
92
|
+
"""
|
|
93
|
+
Maximum number of runs for the test. Min should be 1, max should be 300.
|
|
94
|
+
"""
|
|
95
|
+
time: datetime | None = None
|
|
96
|
+
"""
|
|
97
|
+
Scheduled time for the test to terminate. A zero value means no
|
|
98
|
+
limit.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def model_post_init(self, __context):
|
|
102
|
+
if self.maximum_runs < 1:
|
|
103
|
+
raise ValueError("maximum_runs must be at least 1")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ShadowTestMetadata(BaseModel):
|
|
107
|
+
"""
|
|
108
|
+
Metadata for a Nextmv Cloud shadow test.
|
|
109
|
+
|
|
110
|
+
You can import the `ShadowTestMetadata` class directly from `cloud`:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from nextmv.cloud import ShadowTestMetadata
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
shadow_test_id : str, optional
|
|
119
|
+
The unique identifier of the shadow test.
|
|
120
|
+
name : str, optional
|
|
121
|
+
Name of the shadow test.
|
|
122
|
+
description : str, optional
|
|
123
|
+
Description of the shadow test.
|
|
124
|
+
app_id : str, optional
|
|
125
|
+
ID of the application to which the shadow test belongs.
|
|
126
|
+
created_at : datetime, optional
|
|
127
|
+
Creation date of the shadow test.
|
|
128
|
+
updated_at : datetime, optional
|
|
129
|
+
Last update date of the shadow test.
|
|
130
|
+
status : ExperimentStatus, optional
|
|
131
|
+
The current status of the shadow test.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
shadow_test_id: str | None = Field(
|
|
135
|
+
serialization_alias="id",
|
|
136
|
+
validation_alias=AliasChoices("id", "shadow_test_id"),
|
|
137
|
+
default=None,
|
|
138
|
+
)
|
|
139
|
+
"""The unique identifier of the shadow test."""
|
|
140
|
+
name: str | None = None
|
|
141
|
+
"""Name of the shadow test."""
|
|
142
|
+
description: str | None = None
|
|
143
|
+
"""Description of the shadow test."""
|
|
144
|
+
app_id: str | None = None
|
|
145
|
+
"""ID of the application to which the shadow test belongs."""
|
|
146
|
+
created_at: datetime | None = None
|
|
147
|
+
"""Creation date of the shadow test."""
|
|
148
|
+
updated_at: datetime | None = None
|
|
149
|
+
"""Last update date of the shadow test."""
|
|
150
|
+
status: ExperimentStatus | None = None
|
|
151
|
+
"""The current status of the shadow test."""
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ShadowTest(ShadowTestMetadata):
|
|
155
|
+
"""
|
|
156
|
+
A Nextmv Cloud shadow test definition.
|
|
157
|
+
|
|
158
|
+
A shadow test is a type of experiment where runs are executed in parallel
|
|
159
|
+
to compare different instances.
|
|
160
|
+
|
|
161
|
+
You can import the `ShadowTest` class directly from `cloud`:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from nextmv.cloud import ShadowTest
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
completed_at : datetime, optional
|
|
170
|
+
Completion date of the shadow test, if applicable.
|
|
171
|
+
comparisons : list[TestComparison], optional
|
|
172
|
+
List of test comparisons defined in the shadow test.
|
|
173
|
+
start_events : StartEvents, optional
|
|
174
|
+
Start events for the shadow test.
|
|
175
|
+
termination_events : TerminationEvents, optional
|
|
176
|
+
Termination events for the shadow test.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
completed_at: datetime | None = None
|
|
180
|
+
"""Completion date of the shadow test, if applicable."""
|
|
181
|
+
comparisons: list[TestComparison] | None = None
|
|
182
|
+
"""List of test comparisons defined in the shadow test."""
|
|
183
|
+
start_events: StartEvents | None = None
|
|
184
|
+
"""Start events for the shadow test."""
|
|
185
|
+
termination_events: TerminationEvents | None = None
|
|
186
|
+
"""Termination events for the shadow test."""
|
|
187
|
+
grouped_distributional_summaries: list[dict[str, Any]] | None = None
|
|
188
|
+
"""Grouped distributional summaries of the shadow test."""
|
|
189
|
+
runs: list[Run] | None = None
|
|
190
|
+
"""List of runs in the shadow test."""
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classes for working with Nextmv Cloud switchback tests.
|
|
3
|
+
|
|
4
|
+
This module provides classes for interacting with switchback tests in Nextmv Cloud.
|
|
5
|
+
It details the core data structures for these types of experiments.
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
TestComparisonSingle
|
|
10
|
+
A structure to define a single comparison for tests.
|
|
11
|
+
SwitchbackPlanUnit
|
|
12
|
+
A structure to define a single unit in the switchback plan.
|
|
13
|
+
SwitchbackPlan
|
|
14
|
+
A structure to define the switchback plan for tests.
|
|
15
|
+
SwitchbackTestMetadata
|
|
16
|
+
Metadata for a Nextmv Cloud switchback test.
|
|
17
|
+
SwitchbackTest
|
|
18
|
+
A Nextmv Cloud switchback test definition.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
|
|
23
|
+
from pydantic import AliasChoices, Field
|
|
24
|
+
|
|
25
|
+
from nextmv.base_model import BaseModel
|
|
26
|
+
from nextmv.cloud.batch_experiment import ExperimentStatus
|
|
27
|
+
from nextmv.run import Run
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestComparisonSingle(BaseModel):
|
|
31
|
+
"""
|
|
32
|
+
A structure to define a single comparison for tests.
|
|
33
|
+
|
|
34
|
+
You can import the `TestComparisonSingle` class directly from `cloud`:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from nextmv.cloud import TestComparisonSingle
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
baseline_instance_id : str
|
|
43
|
+
ID of the baseline instance for comparison.
|
|
44
|
+
candidate_instance_id : str
|
|
45
|
+
ID of the candidate instance for comparison.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
baseline_instance_id: str
|
|
49
|
+
"""ID of the baseline instance for comparison."""
|
|
50
|
+
candidate_instance_id: str
|
|
51
|
+
"""ID of the candidate instance for comparison."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SwitchbackPlanUnit(BaseModel):
|
|
55
|
+
"""
|
|
56
|
+
A structure to define a single unit in the switchback plan.
|
|
57
|
+
|
|
58
|
+
You can import the `SwitchbackPlanUnit` class directly from `cloud`:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from nextmv.cloud import SwitchbackPlanUnit
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
duration_minutes : float
|
|
67
|
+
Duration of this interval in minutes.
|
|
68
|
+
instance_id : str
|
|
69
|
+
ID of the instance to run during this unit.
|
|
70
|
+
index : int
|
|
71
|
+
Index of this unit in the switchback plan.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
duration_minutes: float
|
|
75
|
+
"""Duration of this interval in minutes."""
|
|
76
|
+
instance_id: str
|
|
77
|
+
"""ID of the instance to run during this unit."""
|
|
78
|
+
index: int
|
|
79
|
+
"""Index of this unit in the switchback plan."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SwitchbackPlan(BaseModel):
|
|
83
|
+
"""
|
|
84
|
+
A structure to define the switchback plan for tests.
|
|
85
|
+
|
|
86
|
+
You can import the `SwitchbackPlan` class directly from `cloud`:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from nextmv.cloud import SwitchbackPlan
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
interval_duration_seconds : int
|
|
95
|
+
Duration of each interval in seconds.
|
|
96
|
+
total_intervals : int
|
|
97
|
+
Total number of intervals in the switchback test.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
start: datetime | None = None
|
|
101
|
+
"""Start time of the switchback test."""
|
|
102
|
+
units: list[SwitchbackPlanUnit] | None = None
|
|
103
|
+
"""List of switchback plan units."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class SwitchbackTestMetadata(BaseModel):
|
|
107
|
+
"""
|
|
108
|
+
Metadata for a Nextmv Cloud switchback test.
|
|
109
|
+
|
|
110
|
+
You can import the `SwitchbackTestMetadata` class directly from `cloud`:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from nextmv.cloud import SwitchbackTestMetadata
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
switchback_test_id : str, optional
|
|
119
|
+
The unique identifier of the switchback test.
|
|
120
|
+
name : str, optional
|
|
121
|
+
Name of the switchback test.
|
|
122
|
+
description : str, optional
|
|
123
|
+
Description of the switchback test.
|
|
124
|
+
app_id : str, optional
|
|
125
|
+
ID of the application to which the switchback test belongs.
|
|
126
|
+
created_at : datetime, optional
|
|
127
|
+
Creation date of the switchback test.
|
|
128
|
+
updated_at : datetime, optional
|
|
129
|
+
Last update date of the switchback test.
|
|
130
|
+
status : ExperimentStatus, optional
|
|
131
|
+
The current status of the switchback test.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
switchback_test_id: str | None = Field(
|
|
135
|
+
serialization_alias="id",
|
|
136
|
+
validation_alias=AliasChoices("id", "switchback_test_id"),
|
|
137
|
+
default=None,
|
|
138
|
+
)
|
|
139
|
+
"""The unique identifier of the switchback test."""
|
|
140
|
+
name: str | None = None
|
|
141
|
+
"""Name of the switchback test."""
|
|
142
|
+
description: str | None = None
|
|
143
|
+
"""Description of the switchback test."""
|
|
144
|
+
app_id: str | None = None
|
|
145
|
+
"""ID of the application to which the switchback test belongs."""
|
|
146
|
+
created_at: datetime | None = None
|
|
147
|
+
"""Creation date of the switchback test."""
|
|
148
|
+
updated_at: datetime | None = None
|
|
149
|
+
"""Last update date of the switchback test."""
|
|
150
|
+
status: ExperimentStatus | None = None
|
|
151
|
+
"""The current status of the switchback test."""
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class SwitchbackTest(SwitchbackTestMetadata):
|
|
155
|
+
"""
|
|
156
|
+
A Nextmv Cloud switchback test definition.
|
|
157
|
+
|
|
158
|
+
A switchback test is a type of experiment where runs are executed in
|
|
159
|
+
sequential intervals, alternating between different instances to compare
|
|
160
|
+
their performance.
|
|
161
|
+
|
|
162
|
+
You can import the `SwitchbackTest` class directly from `cloud`:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from nextmv.cloud import SwitchbackTest
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
completed_at : datetime, optional
|
|
171
|
+
Completion date of the switchback test, if applicable.
|
|
172
|
+
comparison : TestComparisonSingle, optional
|
|
173
|
+
Test comparison defined in the switchback test.
|
|
174
|
+
start_events : StartEvents, optional
|
|
175
|
+
Start events for the switchback test.
|
|
176
|
+
termination_events : TerminationEvents, optional
|
|
177
|
+
Termination events for the switchback test.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
started_at: datetime | None = None
|
|
181
|
+
"""Start date of the switchback test, if applicable."""
|
|
182
|
+
completed_at: datetime | None = None
|
|
183
|
+
"""Completion date of the switchback test, if applicable."""
|
|
184
|
+
comparison: TestComparisonSingle | None = None
|
|
185
|
+
"""Test comparison defined in the switchback test."""
|
|
186
|
+
plan: SwitchbackPlan | None = None
|
|
187
|
+
"""Switchback plan defining the intervals and instance switching."""
|
|
188
|
+
runs: list[Run] | None = None
|
|
189
|
+
"""List of runs in the switchback test."""
|