nextmv 1.0.0.dev2__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 +2 -0
- nextmv/cli/cloud/shadow/__init__.py +1 -1
- nextmv/cli/cloud/shadow/create.py +2 -2
- nextmv/cli/cloud/shadow/stop.py +5 -5
- nextmv/cli/cloud/shadow/update.py +1 -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 +14 -0
- nextmv/cloud/__init__.py +5 -0
- nextmv/cloud/application/__init__.py +2 -0
- nextmv/cloud/application/_switchback.py +315 -0
- nextmv/cloud/switchback.py +189 -0
- {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev3.dist-info}/METADATA +1 -1
- {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev3.dist-info}/RECORD +25 -14
- {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev3.dist-info}/WHEEL +0 -0
- {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud switchback update command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from nextmv.cli.configuration.config import build_app
|
|
11
|
+
from nextmv.cli.message import in_progress, print_json, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption, SwitchbackTestIDOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def update(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
switchback_test_id: SwitchbackTestIDOption,
|
|
22
|
+
description: Annotated[
|
|
23
|
+
str | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--description",
|
|
26
|
+
"-d",
|
|
27
|
+
help="Updated description of the switchback test.",
|
|
28
|
+
metavar="DESCRIPTION",
|
|
29
|
+
),
|
|
30
|
+
] = None,
|
|
31
|
+
name: Annotated[
|
|
32
|
+
str | None,
|
|
33
|
+
typer.Option(
|
|
34
|
+
"--name",
|
|
35
|
+
"-n",
|
|
36
|
+
help="Updated name of the switchback test.",
|
|
37
|
+
metavar="NAME",
|
|
38
|
+
),
|
|
39
|
+
] = None,
|
|
40
|
+
output: Annotated[
|
|
41
|
+
str | None,
|
|
42
|
+
typer.Option(
|
|
43
|
+
"--output",
|
|
44
|
+
"-o",
|
|
45
|
+
help="Saves the updated switchback test information to this location.",
|
|
46
|
+
metavar="OUTPUT_PATH",
|
|
47
|
+
),
|
|
48
|
+
] = None,
|
|
49
|
+
profile: ProfileOption = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Update a Nextmv Cloud switchback test.
|
|
53
|
+
|
|
54
|
+
Update the name and/or description of a switchback test. Any fields not
|
|
55
|
+
specified will remain unchanged.
|
|
56
|
+
|
|
57
|
+
[bold][underline]Examples[/underline][/bold]
|
|
58
|
+
|
|
59
|
+
- Update the name of a switchback test.
|
|
60
|
+
$ [green]nextmv cloud switchback update --app-id hare-app --switchback-test-id carrot-feast \\
|
|
61
|
+
--name "Spring Carrot Harvest"[/green]
|
|
62
|
+
|
|
63
|
+
- Update the description of a switchback test.
|
|
64
|
+
$ [green]nextmv cloud switchback update --app-id hare-app --switchback-test-id bunny-hop-routes \\
|
|
65
|
+
--description "Optimizing hop paths through the meadow"[/green]
|
|
66
|
+
|
|
67
|
+
- Update both name and description and save the result.
|
|
68
|
+
$ [green]nextmv cloud switchback update --app-id hare-app --switchback-test-id lettuce-delivery \\
|
|
69
|
+
--name "Warren Lettuce Express" --description "Fast lettuce delivery to all burrows" \\
|
|
70
|
+
--output updated-switchback-test.json[/green]
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
74
|
+
|
|
75
|
+
in_progress(msg="Updating switchback test...")
|
|
76
|
+
switchback_test = cloud_app.update_switchback_test(
|
|
77
|
+
switchback_test_id=switchback_test_id,
|
|
78
|
+
name=name,
|
|
79
|
+
description=description,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
switchback_test_dict = switchback_test.to_dict()
|
|
83
|
+
success(
|
|
84
|
+
f"Switchback test [magenta]{switchback_test_id}[/magenta] updated successfully "
|
|
85
|
+
f"in application [magenta]{app_id}[/magenta]."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if output is not None and output != "":
|
|
89
|
+
with open(output, "w") as f:
|
|
90
|
+
json.dump(switchback_test_dict, f, indent=2)
|
|
91
|
+
|
|
92
|
+
success(msg=f"Updated switchback test information saved to [magenta]{output}[/magenta].")
|
|
93
|
+
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
print_json(switchback_test_dict)
|
nextmv/cli/options.py
CHANGED
|
@@ -204,3 +204,17 @@ ShadowTestIDOption = Annotated[
|
|
|
204
204
|
metavar="SHADOW_TEST_ID",
|
|
205
205
|
),
|
|
206
206
|
]
|
|
207
|
+
|
|
208
|
+
# switchback_test_id option - can be used in any command that requires a switchback test ID.
|
|
209
|
+
# Define it as follows in commands or callbacks, as necessary:
|
|
210
|
+
# switchback_test_id: SwitchbackTestIDOption
|
|
211
|
+
SwitchbackTestIDOption = Annotated[
|
|
212
|
+
str,
|
|
213
|
+
typer.Option(
|
|
214
|
+
"--switchback-test-id",
|
|
215
|
+
"-s",
|
|
216
|
+
help="The Nextmv Cloud switchback test ID to use for this action.",
|
|
217
|
+
envvar="NEXTMV_SWITCHBACK_TEST_ID",
|
|
218
|
+
metavar="SWITCHBACK_TEST_ID",
|
|
219
|
+
),
|
|
220
|
+
]
|
nextmv/cloud/__init__.py
CHANGED
|
@@ -95,6 +95,11 @@ from .shadow import ShadowTestMetadata as ShadowTestMetadata
|
|
|
95
95
|
from .shadow import StartEvents as StartEvents
|
|
96
96
|
from .shadow import TerminationEvents as TerminationEvents
|
|
97
97
|
from .shadow import TestComparison as TestComparison
|
|
98
|
+
from .switchback import SwitchbackPlan as SwitchbackPlan
|
|
99
|
+
from .switchback import SwitchbackPlanUnit as SwitchbackPlanUnit
|
|
100
|
+
from .switchback import SwitchbackTest as SwitchbackTest
|
|
101
|
+
from .switchback import SwitchbackTestMetadata as SwitchbackTestMetadata
|
|
102
|
+
from .switchback import TestComparisonSingle as TestComparisonSingle
|
|
98
103
|
from .url import DownloadURL as DownloadURL
|
|
99
104
|
from .url import UploadURL as UploadURL
|
|
100
105
|
from .version import Version as Version
|
|
@@ -42,6 +42,7 @@ from nextmv.cloud.application._managed_input import ApplicationManagedInputMixin
|
|
|
42
42
|
from nextmv.cloud.application._run import ApplicationRunMixin
|
|
43
43
|
from nextmv.cloud.application._secrets import ApplicationSecretsMixin
|
|
44
44
|
from nextmv.cloud.application._shadow import ApplicationShadowMixin
|
|
45
|
+
from nextmv.cloud.application._switchback import ApplicationSwitchbackMixin
|
|
45
46
|
from nextmv.cloud.application._utils import _is_not_exist_error
|
|
46
47
|
from nextmv.cloud.application._version import ApplicationVersionMixin
|
|
47
48
|
from nextmv.cloud.client import Client
|
|
@@ -102,6 +103,7 @@ class Application(
|
|
|
102
103
|
ApplicationInputSetMixin,
|
|
103
104
|
ApplicationManagedInputMixin,
|
|
104
105
|
ApplicationShadowMixin,
|
|
106
|
+
ApplicationSwitchbackMixin,
|
|
105
107
|
):
|
|
106
108
|
"""
|
|
107
109
|
A published decision model that can be executed.
|
|
@@ -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())
|
|
@@ -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."""
|