trainml 0.5.5__py3-none-any.whl → 0.5.6__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.
- tests/unit/cli/cloudbender/test_cli_reservation_unit.py +10 -14
- tests/unit/cli/test_cli_project_unit.py +5 -9
- tests/unit/cloudbender/test_services_unit.py +161 -0
- tests/unit/conftest.py +13 -13
- tests/unit/test_projects_unit.py +34 -48
- trainml/__init__.py +1 -1
- trainml/cli/cloudbender/__init__.py +1 -1
- trainml/cli/cloudbender/service.py +129 -0
- trainml/cli/project.py +10 -15
- trainml/cloudbender/cloudbender.py +2 -2
- trainml/cloudbender/services.py +115 -0
- trainml/projects.py +19 -30
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/METADATA +1 -1
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/RECORD +18 -15
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/LICENSE +0 -0
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/WHEEL +0 -0
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/entry_points.txt +0 -0
- {trainml-0.5.5.dist-info → trainml-0.5.6.dist-info}/top_level.txt +0 -0
|
@@ -4,35 +4,31 @@ import click
|
|
|
4
4
|
from unittest.mock import AsyncMock, patch
|
|
5
5
|
from pytest import mark, fixture, raises
|
|
6
6
|
|
|
7
|
-
pytestmark = [mark.cli, mark.unit, mark.cloudbender, mark.
|
|
7
|
+
pytestmark = [mark.cli, mark.unit, mark.cloudbender, mark.services]
|
|
8
8
|
|
|
9
|
-
from trainml.cli.cloudbender import
|
|
10
|
-
from trainml.cloudbender.
|
|
9
|
+
from trainml.cli.cloudbender import service as specimen
|
|
10
|
+
from trainml.cloudbender.services import Service
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def test_list(runner,
|
|
13
|
+
def test_list(runner, mock_services):
|
|
14
14
|
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
15
15
|
mock_trainml.cloudbender = AsyncMock()
|
|
16
|
-
mock_trainml.cloudbender.
|
|
17
|
-
mock_trainml.cloudbender.
|
|
18
|
-
return_value=mock_reservations
|
|
19
|
-
)
|
|
16
|
+
mock_trainml.cloudbender.services = AsyncMock()
|
|
17
|
+
mock_trainml.cloudbender.services.list = AsyncMock(return_value=mock_services)
|
|
20
18
|
result = runner.invoke(
|
|
21
19
|
specimen,
|
|
22
20
|
args=["list", "--provider=prov-id-1", "--region=reg-id-1"],
|
|
23
21
|
)
|
|
24
22
|
assert result.exit_code == 0
|
|
25
|
-
mock_trainml.cloudbender.
|
|
23
|
+
mock_trainml.cloudbender.services.list.assert_called_once_with(
|
|
26
24
|
provider_uuid="prov-id-1", region_uuid="reg-id-1"
|
|
27
25
|
)
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
def test_list_no_provider(runner,
|
|
28
|
+
def test_list_no_provider(runner, mock_services):
|
|
31
29
|
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
32
30
|
mock_trainml.cloudbender = AsyncMock()
|
|
33
|
-
mock_trainml.cloudbender.
|
|
34
|
-
mock_trainml.cloudbender.
|
|
35
|
-
return_value=mock_reservations
|
|
36
|
-
)
|
|
31
|
+
mock_trainml.cloudbender.services = AsyncMock()
|
|
32
|
+
mock_trainml.cloudbender.services.list = AsyncMock(return_value=mock_services)
|
|
37
33
|
result = runner.invoke(specimen, ["list"])
|
|
38
34
|
assert result.exit_code != 0
|
|
@@ -23,9 +23,7 @@ def test_list(runner, mock_projects):
|
|
|
23
23
|
def test_list_datastores(runner, mock_project_datastores):
|
|
24
24
|
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
25
25
|
mock_project = create_autospec(Project)
|
|
26
|
-
mock_project.list_datastores = AsyncMock(
|
|
27
|
-
return_value=mock_project_datastores
|
|
28
|
-
)
|
|
26
|
+
mock_project.list_datastores = AsyncMock(return_value=mock_project_datastores)
|
|
29
27
|
mock_trainml.projects.get = AsyncMock(return_value=mock_project)
|
|
30
28
|
result = runner.invoke(specimen, ["list-datastores"])
|
|
31
29
|
print(result)
|
|
@@ -33,14 +31,12 @@ def test_list_datastores(runner, mock_project_datastores):
|
|
|
33
31
|
mock_project.list_datastores.assert_called_once()
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
def
|
|
34
|
+
def test_list_services(runner, mock_project_services):
|
|
37
35
|
with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
|
|
38
36
|
mock_project = create_autospec(Project)
|
|
39
|
-
mock_project.
|
|
40
|
-
return_value=mock_project_reservations
|
|
41
|
-
)
|
|
37
|
+
mock_project.list_services = AsyncMock(return_value=mock_project_services)
|
|
42
38
|
mock_trainml.projects.get = AsyncMock(return_value=mock_project)
|
|
43
|
-
result = runner.invoke(specimen, ["list-
|
|
39
|
+
result = runner.invoke(specimen, ["list-services"])
|
|
44
40
|
print(result)
|
|
45
41
|
assert result.exit_code == 0
|
|
46
|
-
mock_project.
|
|
42
|
+
mock_project.list_services.assert_called_once()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from unittest.mock import AsyncMock, patch
|
|
5
|
+
from pytest import mark, fixture, raises
|
|
6
|
+
from aiohttp import WSMessage, WSMsgType
|
|
7
|
+
|
|
8
|
+
import trainml.cloudbender.services as specimen
|
|
9
|
+
from trainml.exceptions import (
|
|
10
|
+
ApiError,
|
|
11
|
+
SpecificationError,
|
|
12
|
+
TrainMLException,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
pytestmark = [mark.sdk, mark.unit, mark.cloudbender, mark.services]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@fixture
|
|
19
|
+
def services(mock_trainml):
|
|
20
|
+
yield specimen.Services(mock_trainml)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@fixture
|
|
24
|
+
def service(mock_trainml):
|
|
25
|
+
yield specimen.Service(
|
|
26
|
+
mock_trainml,
|
|
27
|
+
provider_uuid="1",
|
|
28
|
+
region_uuid="a",
|
|
29
|
+
service_id="x",
|
|
30
|
+
name="On-Prem Service",
|
|
31
|
+
public=False,
|
|
32
|
+
hostname="app1.proximl.cloud",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RegionsTests:
|
|
37
|
+
@mark.asyncio
|
|
38
|
+
async def test_get_service(
|
|
39
|
+
self,
|
|
40
|
+
services,
|
|
41
|
+
mock_trainml,
|
|
42
|
+
):
|
|
43
|
+
api_response = dict()
|
|
44
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
45
|
+
await services.get("1234", "5687", "91011")
|
|
46
|
+
mock_trainml._query.assert_called_once_with(
|
|
47
|
+
"/provider/1234/region/5687/service/91011", "GET", {}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@mark.asyncio
|
|
51
|
+
async def test_list_services(
|
|
52
|
+
self,
|
|
53
|
+
services,
|
|
54
|
+
mock_trainml,
|
|
55
|
+
):
|
|
56
|
+
api_response = dict()
|
|
57
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
58
|
+
await services.list("1234", "5687")
|
|
59
|
+
mock_trainml._query.assert_called_once_with(
|
|
60
|
+
"/provider/1234/region/5687/service", "GET", {}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@mark.asyncio
|
|
64
|
+
async def test_remove_service(
|
|
65
|
+
self,
|
|
66
|
+
services,
|
|
67
|
+
mock_trainml,
|
|
68
|
+
):
|
|
69
|
+
api_response = dict()
|
|
70
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
71
|
+
await services.remove("1234", "4567", "8910")
|
|
72
|
+
mock_trainml._query.assert_called_once_with(
|
|
73
|
+
"/provider/1234/region/4567/service/8910", "DELETE", {}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@mark.asyncio
|
|
77
|
+
async def test_create_service(self, services, mock_trainml):
|
|
78
|
+
requested_config = dict(
|
|
79
|
+
provider_uuid="provider-id-1",
|
|
80
|
+
region_uuid="region-id-1",
|
|
81
|
+
name="On-Prem Service",
|
|
82
|
+
public=False,
|
|
83
|
+
)
|
|
84
|
+
expected_payload = dict(
|
|
85
|
+
name="On-Prem Service",
|
|
86
|
+
public=False,
|
|
87
|
+
)
|
|
88
|
+
api_response = {
|
|
89
|
+
"provider_uuid": "provider-id-1",
|
|
90
|
+
"region_uuid": "region-id-1",
|
|
91
|
+
"service_id": "service-id-1",
|
|
92
|
+
"name": "On-Prem Service",
|
|
93
|
+
"public": False,
|
|
94
|
+
"hostname": "app1.proximl.cloud",
|
|
95
|
+
"createdAt": "2020-12-31T23:59:59.000Z",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
99
|
+
response = await services.create(**requested_config)
|
|
100
|
+
mock_trainml._query.assert_called_once_with(
|
|
101
|
+
"/provider/provider-id-1/region/region-id-1/service",
|
|
102
|
+
"POST",
|
|
103
|
+
None,
|
|
104
|
+
expected_payload,
|
|
105
|
+
)
|
|
106
|
+
assert response.id == "service-id-1"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class serviceTests:
|
|
110
|
+
def test_service_properties(self, service):
|
|
111
|
+
assert isinstance(service.id, str)
|
|
112
|
+
assert isinstance(service.provider_uuid, str)
|
|
113
|
+
assert isinstance(service.region_uuid, str)
|
|
114
|
+
assert isinstance(service.public, bool)
|
|
115
|
+
assert isinstance(service.name, str)
|
|
116
|
+
assert isinstance(service.hostname, str)
|
|
117
|
+
|
|
118
|
+
def test_service_str(self, service):
|
|
119
|
+
string = str(service)
|
|
120
|
+
regex = r"^{.*\"service_id\": \"" + service.id + r"\".*}$"
|
|
121
|
+
assert isinstance(string, str)
|
|
122
|
+
assert re.match(regex, string)
|
|
123
|
+
|
|
124
|
+
def test_service_repr(self, service):
|
|
125
|
+
string = repr(service)
|
|
126
|
+
regex = r"^Service\( trainml , \*\*{.*'service_id': '" + service.id + r"'.*}\)$"
|
|
127
|
+
assert isinstance(string, str)
|
|
128
|
+
assert re.match(regex, string)
|
|
129
|
+
|
|
130
|
+
def test_service_bool(self, service, mock_trainml):
|
|
131
|
+
empty_service = specimen.Service(mock_trainml)
|
|
132
|
+
assert bool(service)
|
|
133
|
+
assert not bool(empty_service)
|
|
134
|
+
|
|
135
|
+
@mark.asyncio
|
|
136
|
+
async def test_service_remove(self, service, mock_trainml):
|
|
137
|
+
api_response = dict()
|
|
138
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
139
|
+
await service.remove()
|
|
140
|
+
mock_trainml._query.assert_called_once_with(
|
|
141
|
+
"/provider/1/region/a/service/x", "DELETE"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@mark.asyncio
|
|
145
|
+
async def test_service_refresh(self, service, mock_trainml):
|
|
146
|
+
api_response = {
|
|
147
|
+
"provider_uuid": "provider-id-1",
|
|
148
|
+
"region_uuid": "region-id-1",
|
|
149
|
+
"service_id": "service-id-1",
|
|
150
|
+
"name": "On-Prem Service",
|
|
151
|
+
"public": False,
|
|
152
|
+
"hostname": "app1.proximl.cloud",
|
|
153
|
+
"createdAt": "2020-12-31T23:59:59.000Z",
|
|
154
|
+
}
|
|
155
|
+
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
156
|
+
response = await service.refresh()
|
|
157
|
+
mock_trainml._query.assert_called_once_with(
|
|
158
|
+
f"/provider/1/region/a/service/x", "GET"
|
|
159
|
+
)
|
|
160
|
+
assert service.id == "service-id-1"
|
|
161
|
+
assert response.id == "service-id-1"
|
tests/unit/conftest.py
CHANGED
|
@@ -17,7 +17,7 @@ from trainml.projects import (
|
|
|
17
17
|
Projects,
|
|
18
18
|
Project,
|
|
19
19
|
ProjectDatastore,
|
|
20
|
-
|
|
20
|
+
ProjectService,
|
|
21
21
|
)
|
|
22
22
|
from trainml.cloudbender import Cloudbender
|
|
23
23
|
from trainml.cloudbender.providers import Provider, Providers
|
|
@@ -25,7 +25,7 @@ from trainml.cloudbender.regions import Region, Regions
|
|
|
25
25
|
from trainml.cloudbender.nodes import Node, Nodes
|
|
26
26
|
from trainml.cloudbender.devices import Device, Devices
|
|
27
27
|
from trainml.cloudbender.datastores import Datastore, Datastores
|
|
28
|
-
from trainml.cloudbender.
|
|
28
|
+
from trainml.cloudbender.services import Service, Services
|
|
29
29
|
from trainml.cloudbender.device_configs import DeviceConfig, DeviceConfigs
|
|
30
30
|
|
|
31
31
|
|
|
@@ -887,27 +887,27 @@ def mock_project_datastores():
|
|
|
887
887
|
|
|
888
888
|
|
|
889
889
|
@fixture(scope="session")
|
|
890
|
-
def
|
|
890
|
+
def mock_services():
|
|
891
891
|
trainml = Mock()
|
|
892
892
|
yield [
|
|
893
|
-
|
|
893
|
+
Service(
|
|
894
894
|
trainml,
|
|
895
895
|
**{
|
|
896
896
|
"provider_uuid": "prov-id-1",
|
|
897
897
|
"region_uuid": "reg-id-1",
|
|
898
|
-
"
|
|
898
|
+
"service_id": "res-id-1",
|
|
899
899
|
"type": "port",
|
|
900
900
|
"name": "On-Prem Service A",
|
|
901
901
|
"resource": "8001",
|
|
902
902
|
"hostname": "service-a.local",
|
|
903
903
|
},
|
|
904
904
|
),
|
|
905
|
-
|
|
905
|
+
Service(
|
|
906
906
|
trainml,
|
|
907
907
|
**{
|
|
908
908
|
"provider_uuid": "prov-id-2",
|
|
909
909
|
"region_uuid": "reg-id-2",
|
|
910
|
-
"
|
|
910
|
+
"service_id": "res-id-2",
|
|
911
911
|
"type": "port",
|
|
912
912
|
"name": "Cloud Service B",
|
|
913
913
|
"resource": "8001",
|
|
@@ -918,10 +918,10 @@ def mock_reservations():
|
|
|
918
918
|
|
|
919
919
|
|
|
920
920
|
@fixture(scope="session")
|
|
921
|
-
def
|
|
921
|
+
def mock_project_services():
|
|
922
922
|
trainml = Mock()
|
|
923
923
|
yield [
|
|
924
|
-
|
|
924
|
+
ProjectService(
|
|
925
925
|
trainml,
|
|
926
926
|
**{
|
|
927
927
|
"project_uuid": "proj-id-1",
|
|
@@ -933,7 +933,7 @@ def mock_project_reservations():
|
|
|
933
933
|
"hostname": "service-a.local",
|
|
934
934
|
},
|
|
935
935
|
),
|
|
936
|
-
|
|
936
|
+
ProjectService(
|
|
937
937
|
trainml,
|
|
938
938
|
**{
|
|
939
939
|
"project_uuid": "proj-id-1",
|
|
@@ -990,7 +990,7 @@ def mock_trainml(
|
|
|
990
990
|
mock_nodes,
|
|
991
991
|
mock_devices,
|
|
992
992
|
mock_datastores,
|
|
993
|
-
|
|
993
|
+
mock_services,
|
|
994
994
|
mock_device_configs,
|
|
995
995
|
):
|
|
996
996
|
trainml = create_autospec(TrainML)
|
|
@@ -1028,8 +1028,8 @@ def mock_trainml(
|
|
|
1028
1028
|
trainml.cloudbender.devices.list = AsyncMock(return_value=mock_devices)
|
|
1029
1029
|
trainml.cloudbender.datastores = create_autospec(Datastores)
|
|
1030
1030
|
trainml.cloudbender.datastores.list = AsyncMock(return_value=mock_datastores)
|
|
1031
|
-
trainml.cloudbender.
|
|
1032
|
-
trainml.cloudbender.
|
|
1031
|
+
trainml.cloudbender.services = create_autospec(Services)
|
|
1032
|
+
trainml.cloudbender.services.list = AsyncMock(return_value=mock_services)
|
|
1033
1033
|
trainml.cloudbender.device_configs = create_autospec(DeviceConfigs)
|
|
1034
1034
|
trainml.cloudbender.device_configs.list = AsyncMock(
|
|
1035
1035
|
return_value=mock_device_configs
|
tests/unit/test_projects_unit.py
CHANGED
|
@@ -49,11 +49,11 @@ def project_datastore(mock_trainml):
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
@fixture
|
|
52
|
-
def
|
|
53
|
-
yield specimen.
|
|
52
|
+
def project_service(mock_trainml):
|
|
53
|
+
yield specimen.ProjectService(
|
|
54
54
|
mock_trainml,
|
|
55
55
|
id="res-id-1",
|
|
56
|
-
name="
|
|
56
|
+
name="service 1",
|
|
57
57
|
project_uuid="proj-id-1",
|
|
58
58
|
region_uuid="reg-id-1",
|
|
59
59
|
type="port",
|
|
@@ -72,9 +72,7 @@ class ProjectsTests:
|
|
|
72
72
|
api_response = dict()
|
|
73
73
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
74
74
|
await projects.get("1234")
|
|
75
|
-
mock_trainml._query.assert_called_once_with(
|
|
76
|
-
"/project/1234", "GET", dict()
|
|
77
|
-
)
|
|
75
|
+
mock_trainml._query.assert_called_once_with("/project/1234", "GET", dict())
|
|
78
76
|
|
|
79
77
|
@mark.asyncio
|
|
80
78
|
async def test_list_projects(
|
|
@@ -96,9 +94,7 @@ class ProjectsTests:
|
|
|
96
94
|
api_response = dict()
|
|
97
95
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
98
96
|
await projects.remove("4567")
|
|
99
|
-
mock_trainml._query.assert_called_once_with(
|
|
100
|
-
"/project/4567", "DELETE", dict()
|
|
101
|
-
)
|
|
97
|
+
mock_trainml._query.assert_called_once_with("/project/4567", "DELETE", dict())
|
|
102
98
|
|
|
103
99
|
@mark.asyncio
|
|
104
100
|
async def test_create_project_simple(self, projects, mock_trainml):
|
|
@@ -156,36 +152,36 @@ class ProjectDatastoreTests:
|
|
|
156
152
|
assert not bool(empty_project_datastore)
|
|
157
153
|
|
|
158
154
|
|
|
159
|
-
class
|
|
160
|
-
def
|
|
161
|
-
assert isinstance(
|
|
162
|
-
assert isinstance(
|
|
163
|
-
assert isinstance(
|
|
164
|
-
assert isinstance(
|
|
165
|
-
assert isinstance(
|
|
166
|
-
assert isinstance(
|
|
167
|
-
assert isinstance(
|
|
155
|
+
class ProjectServiceTests:
|
|
156
|
+
def test_project_service_properties(self, project_service):
|
|
157
|
+
assert isinstance(project_service.id, str)
|
|
158
|
+
assert isinstance(project_service.name, str)
|
|
159
|
+
assert isinstance(project_service.project_uuid, str)
|
|
160
|
+
assert isinstance(project_service.type, str)
|
|
161
|
+
assert isinstance(project_service.hostname, str)
|
|
162
|
+
assert isinstance(project_service.resource, str)
|
|
163
|
+
assert isinstance(project_service.region_uuid, str)
|
|
168
164
|
|
|
169
|
-
def
|
|
170
|
-
string = str(
|
|
171
|
-
regex = r"^{.*\"id\": \"" +
|
|
165
|
+
def test_project_service_str(self, project_service):
|
|
166
|
+
string = str(project_service)
|
|
167
|
+
regex = r"^{.*\"id\": \"" + project_service.id + r"\".*}$"
|
|
172
168
|
assert isinstance(string, str)
|
|
173
169
|
assert re.match(regex, string)
|
|
174
170
|
|
|
175
|
-
def
|
|
176
|
-
string = repr(
|
|
171
|
+
def test_project_service_repr(self, project_service):
|
|
172
|
+
string = repr(project_service)
|
|
177
173
|
regex = (
|
|
178
|
-
r"^
|
|
179
|
-
+
|
|
174
|
+
r"^ProjectService\( trainml , \*\*{.*'id': '"
|
|
175
|
+
+ project_service.id
|
|
180
176
|
+ r"'.*}\)$"
|
|
181
177
|
)
|
|
182
178
|
assert isinstance(string, str)
|
|
183
179
|
assert re.match(regex, string)
|
|
184
180
|
|
|
185
|
-
def
|
|
186
|
-
|
|
187
|
-
assert bool(
|
|
188
|
-
assert not bool(
|
|
181
|
+
def test_project_service_bool(self, project_service, mock_trainml):
|
|
182
|
+
empty_project_service = specimen.ProjectService(mock_trainml)
|
|
183
|
+
assert bool(project_service)
|
|
184
|
+
assert not bool(empty_project_service)
|
|
189
185
|
|
|
190
186
|
|
|
191
187
|
class ProjectTests:
|
|
@@ -203,9 +199,7 @@ class ProjectTests:
|
|
|
203
199
|
|
|
204
200
|
def test_project_repr(self, project):
|
|
205
201
|
string = repr(project)
|
|
206
|
-
regex = (
|
|
207
|
-
r"^Project\( trainml , \*\*{.*'id': '" + project.id + r"'.*}\)$"
|
|
208
|
-
)
|
|
202
|
+
regex = r"^Project\( trainml , \*\*{.*'id': '" + project.id + r"'.*}\)$"
|
|
209
203
|
assert isinstance(string, str)
|
|
210
204
|
assert re.match(regex, string)
|
|
211
205
|
|
|
@@ -226,18 +220,14 @@ class ProjectTests:
|
|
|
226
220
|
api_response = dict()
|
|
227
221
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
228
222
|
await project.refresh_datastores()
|
|
229
|
-
mock_trainml._query.assert_called_once_with(
|
|
230
|
-
"/project/1/datastores", "PATCH"
|
|
231
|
-
)
|
|
223
|
+
mock_trainml._query.assert_called_once_with("/project/1/datastores", "PATCH")
|
|
232
224
|
|
|
233
225
|
@mark.asyncio
|
|
234
|
-
async def
|
|
226
|
+
async def test_project_refresh_services(self, project, mock_trainml):
|
|
235
227
|
api_response = dict()
|
|
236
228
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
237
|
-
await project.
|
|
238
|
-
mock_trainml._query.assert_called_once_with(
|
|
239
|
-
"/project/1/reservations", "PATCH"
|
|
240
|
-
)
|
|
229
|
+
await project.refresh_services()
|
|
230
|
+
mock_trainml._query.assert_called_once_with("/project/1/services", "PATCH")
|
|
241
231
|
|
|
242
232
|
@mark.asyncio
|
|
243
233
|
async def test_project_list_datastores(self, project, mock_trainml):
|
|
@@ -259,13 +249,11 @@ class ProjectTests:
|
|
|
259
249
|
]
|
|
260
250
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
261
251
|
resp = await project.list_datastores()
|
|
262
|
-
mock_trainml._query.assert_called_once_with(
|
|
263
|
-
"/project/1/datastores", "GET"
|
|
264
|
-
)
|
|
252
|
+
mock_trainml._query.assert_called_once_with("/project/1/datastores", "GET")
|
|
265
253
|
assert len(resp) == 2
|
|
266
254
|
|
|
267
255
|
@mark.asyncio
|
|
268
|
-
async def
|
|
256
|
+
async def test_project_list_services(self, project, mock_trainml):
|
|
269
257
|
api_response = [
|
|
270
258
|
{
|
|
271
259
|
"project_uuid": "proj-id-1",
|
|
@@ -287,8 +275,6 @@ class ProjectTests:
|
|
|
287
275
|
},
|
|
288
276
|
]
|
|
289
277
|
mock_trainml._query = AsyncMock(return_value=api_response)
|
|
290
|
-
resp = await project.
|
|
291
|
-
mock_trainml._query.assert_called_once_with(
|
|
292
|
-
"/project/1/reservations", "GET"
|
|
293
|
-
)
|
|
278
|
+
resp = await project.list_services()
|
|
279
|
+
mock_trainml._query.assert_called_once_with("/project/1/services", "GET")
|
|
294
280
|
assert len(resp) == 2
|
trainml/__init__.py
CHANGED
|
@@ -15,4 +15,4 @@ from trainml.cli.cloudbender.region import region
|
|
|
15
15
|
from trainml.cli.cloudbender.node import node
|
|
16
16
|
from trainml.cli.cloudbender.device import device
|
|
17
17
|
from trainml.cli.cloudbender.datastore import datastore
|
|
18
|
-
from trainml.cli.cloudbender.
|
|
18
|
+
from trainml.cli.cloudbender.service import service
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from trainml.cli import cli, pass_config, search_by_id_name
|
|
3
|
+
from trainml.cli.cloudbender import cloudbender
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@cloudbender.group()
|
|
7
|
+
@pass_config
|
|
8
|
+
def service(config):
|
|
9
|
+
"""trainML CloudBender service commands."""
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@service.command()
|
|
14
|
+
@click.option(
|
|
15
|
+
"--provider",
|
|
16
|
+
"-p",
|
|
17
|
+
type=click.STRING,
|
|
18
|
+
required=True,
|
|
19
|
+
help="The provider ID of the region.",
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"--region",
|
|
23
|
+
"-r",
|
|
24
|
+
type=click.STRING,
|
|
25
|
+
required=True,
|
|
26
|
+
help="The region ID to list services for.",
|
|
27
|
+
)
|
|
28
|
+
@pass_config
|
|
29
|
+
def list(config, provider, region):
|
|
30
|
+
"""List services."""
|
|
31
|
+
data = [
|
|
32
|
+
["ID", "NAME", "HOSTNAME"],
|
|
33
|
+
[
|
|
34
|
+
"-" * 80,
|
|
35
|
+
"-" * 80,
|
|
36
|
+
"-" * 80,
|
|
37
|
+
],
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
services = config.trainml.run(
|
|
41
|
+
config.trainml.client.cloudbender.services.list(
|
|
42
|
+
provider_uuid=provider, region_uuid=region
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
for service in services:
|
|
47
|
+
data.append(
|
|
48
|
+
[
|
|
49
|
+
service.id,
|
|
50
|
+
service.name,
|
|
51
|
+
service.hostname,
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
for row in data:
|
|
56
|
+
click.echo(
|
|
57
|
+
"{: >25.24} {: >29.28} {: >40.39}" "".format(*row),
|
|
58
|
+
file=config.stdout,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@service.command()
|
|
63
|
+
@click.option(
|
|
64
|
+
"--provider",
|
|
65
|
+
"-p",
|
|
66
|
+
type=click.STRING,
|
|
67
|
+
required=True,
|
|
68
|
+
help="The provider ID of the region.",
|
|
69
|
+
)
|
|
70
|
+
@click.option(
|
|
71
|
+
"--region",
|
|
72
|
+
"-r",
|
|
73
|
+
type=click.STRING,
|
|
74
|
+
required=True,
|
|
75
|
+
help="The region ID to create the service in.",
|
|
76
|
+
)
|
|
77
|
+
@click.option(
|
|
78
|
+
"--public/--no-public",
|
|
79
|
+
default=True,
|
|
80
|
+
show_default=True,
|
|
81
|
+
help="Service should be accessible from the public internet.",
|
|
82
|
+
)
|
|
83
|
+
@click.argument("name", type=click.STRING, required=True)
|
|
84
|
+
@pass_config
|
|
85
|
+
def create(config, provider, region, public, name):
|
|
86
|
+
"""
|
|
87
|
+
Creates a service.
|
|
88
|
+
"""
|
|
89
|
+
return config.trainml.run(
|
|
90
|
+
config.trainml.client.cloudbender.services.create(
|
|
91
|
+
provider_uuid=provider, region_uuid=region, name=name, public=public
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@service.command()
|
|
97
|
+
@click.option(
|
|
98
|
+
"--provider",
|
|
99
|
+
"-p",
|
|
100
|
+
type=click.STRING,
|
|
101
|
+
required=True,
|
|
102
|
+
help="The provider ID of the region.",
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
"--region",
|
|
106
|
+
"-r",
|
|
107
|
+
type=click.STRING,
|
|
108
|
+
required=True,
|
|
109
|
+
help="The region ID to remove the service from.",
|
|
110
|
+
)
|
|
111
|
+
@click.argument("service", type=click.STRING)
|
|
112
|
+
@pass_config
|
|
113
|
+
def remove(config, provider, region, service):
|
|
114
|
+
"""
|
|
115
|
+
Remove a service.
|
|
116
|
+
|
|
117
|
+
RESERVATION may be specified by name or ID, but ID is preferred.
|
|
118
|
+
"""
|
|
119
|
+
services = config.trainml.run(
|
|
120
|
+
config.trainml.client.cloudbender.services.list(
|
|
121
|
+
provider_uuid=provider, region_uuid=region
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
found = search_by_id_name(service, services)
|
|
126
|
+
if None is found:
|
|
127
|
+
raise click.UsageError("Cannot find specified service.")
|
|
128
|
+
|
|
129
|
+
return config.trainml.run(found.remove())
|
trainml/cli/project.py
CHANGED
|
@@ -115,40 +115,35 @@ def list_datastores(config):
|
|
|
115
115
|
|
|
116
116
|
@project.command()
|
|
117
117
|
@pass_config
|
|
118
|
-
def
|
|
119
|
-
"""List project
|
|
118
|
+
def list_services(config):
|
|
119
|
+
"""List project services."""
|
|
120
120
|
data = [
|
|
121
|
-
["ID", "NAME", "
|
|
121
|
+
["ID", "NAME", "HOSTNAME", "REGION_UUID"],
|
|
122
122
|
[
|
|
123
123
|
"-" * 80,
|
|
124
124
|
"-" * 80,
|
|
125
125
|
"-" * 80,
|
|
126
126
|
"-" * 80,
|
|
127
|
-
"-" * 80,
|
|
128
|
-
"-" * 80,
|
|
129
127
|
],
|
|
130
128
|
]
|
|
131
129
|
project = config.trainml.run(
|
|
132
130
|
config.trainml.client.projects.get(config.trainml.client.project)
|
|
133
131
|
)
|
|
134
132
|
|
|
135
|
-
|
|
133
|
+
services = config.trainml.run(project.list_services())
|
|
136
134
|
|
|
137
|
-
for
|
|
135
|
+
for service in services:
|
|
138
136
|
data.append(
|
|
139
137
|
[
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
reservation.hostname,
|
|
145
|
-
reservation.region_uuid,
|
|
138
|
+
service.id,
|
|
139
|
+
service.name,
|
|
140
|
+
service.hostname,
|
|
141
|
+
service.region_uuid,
|
|
146
142
|
]
|
|
147
143
|
)
|
|
148
144
|
|
|
149
145
|
for row in data:
|
|
150
146
|
click.echo(
|
|
151
|
-
"{: >38.36} {: >30.28} {: >
|
|
152
|
-
"".format(*row),
|
|
147
|
+
"{: >38.36} {: >30.28} {: >30.28} {: >38.36}" "".format(*row),
|
|
153
148
|
file=config.stdout,
|
|
154
149
|
)
|
|
@@ -3,7 +3,7 @@ from .regions import Regions
|
|
|
3
3
|
from .nodes import Nodes
|
|
4
4
|
from .devices import Devices
|
|
5
5
|
from .datastores import Datastores
|
|
6
|
-
from .
|
|
6
|
+
from .services import Services
|
|
7
7
|
from .device_configs import DeviceConfigs
|
|
8
8
|
|
|
9
9
|
|
|
@@ -15,5 +15,5 @@ class Cloudbender(object):
|
|
|
15
15
|
self.nodes = Nodes(trainml)
|
|
16
16
|
self.devices = Devices(trainml)
|
|
17
17
|
self.datastores = Datastores(trainml)
|
|
18
|
-
self.
|
|
18
|
+
self.services = Services(trainml)
|
|
19
19
|
self.device_configs = DeviceConfigs(trainml)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Services(object):
|
|
6
|
+
def __init__(self, trainml):
|
|
7
|
+
self.trainml = trainml
|
|
8
|
+
|
|
9
|
+
async def get(self, provider_uuid, region_uuid, id, **kwargs):
|
|
10
|
+
resp = await self.trainml._query(
|
|
11
|
+
f"/provider/{provider_uuid}/region/{region_uuid}/service/{id}",
|
|
12
|
+
"GET",
|
|
13
|
+
kwargs,
|
|
14
|
+
)
|
|
15
|
+
return Service(self.trainml, **resp)
|
|
16
|
+
|
|
17
|
+
async def list(self, provider_uuid, region_uuid, **kwargs):
|
|
18
|
+
resp = await self.trainml._query(
|
|
19
|
+
f"/provider/{provider_uuid}/region/{region_uuid}/service",
|
|
20
|
+
"GET",
|
|
21
|
+
kwargs,
|
|
22
|
+
)
|
|
23
|
+
services = [Service(self.trainml, **service) for service in resp]
|
|
24
|
+
return services
|
|
25
|
+
|
|
26
|
+
async def create(
|
|
27
|
+
self,
|
|
28
|
+
provider_uuid,
|
|
29
|
+
region_uuid,
|
|
30
|
+
name,
|
|
31
|
+
public,
|
|
32
|
+
**kwargs,
|
|
33
|
+
):
|
|
34
|
+
logging.info(f"Creating Service {name}")
|
|
35
|
+
data = dict(
|
|
36
|
+
name=name,
|
|
37
|
+
public=public,
|
|
38
|
+
**kwargs,
|
|
39
|
+
)
|
|
40
|
+
payload = {k: v for k, v in data.items() if v is not None}
|
|
41
|
+
resp = await self.trainml._query(
|
|
42
|
+
f"/provider/{provider_uuid}/region/{region_uuid}/service",
|
|
43
|
+
"POST",
|
|
44
|
+
None,
|
|
45
|
+
payload,
|
|
46
|
+
)
|
|
47
|
+
service = Service(self.trainml, **resp)
|
|
48
|
+
logging.info(f"Created Service {name} with id {service.id}")
|
|
49
|
+
return service
|
|
50
|
+
|
|
51
|
+
async def remove(self, provider_uuid, region_uuid, id, **kwargs):
|
|
52
|
+
await self.trainml._query(
|
|
53
|
+
f"/provider/{provider_uuid}/region/{region_uuid}/service/{id}",
|
|
54
|
+
"DELETE",
|
|
55
|
+
kwargs,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Service:
|
|
60
|
+
def __init__(self, trainml, **kwargs):
|
|
61
|
+
self.trainml = trainml
|
|
62
|
+
self._service = kwargs
|
|
63
|
+
self._id = self._service.get("service_id")
|
|
64
|
+
self._provider_uuid = self._service.get("provider_uuid")
|
|
65
|
+
self._region_uuid = self._service.get("region_uuid")
|
|
66
|
+
self._public = self._service.get("public")
|
|
67
|
+
self._name = self._service.get("name")
|
|
68
|
+
self._hostname = self._service.get("hostname")
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def id(self) -> str:
|
|
72
|
+
return self._id
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def provider_uuid(self) -> str:
|
|
76
|
+
return self._provider_uuid
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def region_uuid(self) -> str:
|
|
80
|
+
return self._region_uuid
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def public(self) -> bool:
|
|
84
|
+
return self._public
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def name(self) -> str:
|
|
88
|
+
return self._name
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def hostname(self) -> str:
|
|
92
|
+
return self._hostname
|
|
93
|
+
|
|
94
|
+
def __str__(self):
|
|
95
|
+
return json.dumps({k: v for k, v in self._service.items()})
|
|
96
|
+
|
|
97
|
+
def __repr__(self):
|
|
98
|
+
return f"Service( trainml , **{self._service.__repr__()})"
|
|
99
|
+
|
|
100
|
+
def __bool__(self):
|
|
101
|
+
return bool(self._id)
|
|
102
|
+
|
|
103
|
+
async def remove(self):
|
|
104
|
+
await self.trainml._query(
|
|
105
|
+
f"/provider/{self._provider_uuid}/region/{self._region_uuid}/service/{self._id}",
|
|
106
|
+
"DELETE",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
async def refresh(self):
|
|
110
|
+
resp = await self.trainml._query(
|
|
111
|
+
f"/provider/{self._provider_uuid}/region/{self._region_uuid}/service/{self._id}",
|
|
112
|
+
"GET",
|
|
113
|
+
)
|
|
114
|
+
self.__init__(self.trainml, **resp)
|
|
115
|
+
return self
|
trainml/projects.py
CHANGED
|
@@ -72,17 +72,17 @@ class ProjectDatastore:
|
|
|
72
72
|
return bool(self._id)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
class
|
|
75
|
+
class ProjectService:
|
|
76
76
|
def __init__(self, trainml, **kwargs):
|
|
77
77
|
self.trainml = trainml
|
|
78
|
-
self.
|
|
79
|
-
self._id = self.
|
|
80
|
-
self._project_uuid = self.
|
|
81
|
-
self._name = self.
|
|
82
|
-
self._type = self.
|
|
83
|
-
self._hostname = self.
|
|
84
|
-
self._resource = self.
|
|
85
|
-
self._region_uuid = self.
|
|
78
|
+
self._service = kwargs
|
|
79
|
+
self._id = self._service.get("id")
|
|
80
|
+
self._project_uuid = self._service.get("project_uuid")
|
|
81
|
+
self._name = self._service.get("name")
|
|
82
|
+
self._type = self._service.get("type")
|
|
83
|
+
self._hostname = self._service.get("hostname")
|
|
84
|
+
self._resource = self._service.get("resource")
|
|
85
|
+
self._region_uuid = self._service.get("region_uuid")
|
|
86
86
|
|
|
87
87
|
@property
|
|
88
88
|
def id(self) -> str:
|
|
@@ -113,12 +113,10 @@ class ProjectReservation:
|
|
|
113
113
|
return self._region_uuid
|
|
114
114
|
|
|
115
115
|
def __str__(self):
|
|
116
|
-
return json.dumps({k: v for k, v in self.
|
|
116
|
+
return json.dumps({k: v for k, v in self._service.items()})
|
|
117
117
|
|
|
118
118
|
def __repr__(self):
|
|
119
|
-
return (
|
|
120
|
-
f"ProjectReservation( trainml , **{self._reservation.__repr__()})"
|
|
121
|
-
)
|
|
119
|
+
return f"ProjectService( trainml , **{self._service.__repr__()})"
|
|
122
120
|
|
|
123
121
|
def __bool__(self):
|
|
124
122
|
return bool(self._id)
|
|
@@ -162,26 +160,17 @@ class Project:
|
|
|
162
160
|
await self.trainml._query(f"/project/{self._id}", "DELETE")
|
|
163
161
|
|
|
164
162
|
async def list_datastores(self):
|
|
165
|
-
resp = await self.trainml._query(
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
datastores = [
|
|
169
|
-
ProjectDatastore(self.trainml, **datastore) for datastore in resp
|
|
170
|
-
]
|
|
163
|
+
resp = await self.trainml._query(f"/project/{self._id}/datastores", "GET")
|
|
164
|
+
datastores = [ProjectDatastore(self.trainml, **datastore) for datastore in resp]
|
|
171
165
|
return datastores
|
|
172
166
|
|
|
173
|
-
async def
|
|
174
|
-
resp = await self.trainml._query(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
reservations = [
|
|
178
|
-
ProjectReservation(self.trainml, **reservation)
|
|
179
|
-
for reservation in resp
|
|
180
|
-
]
|
|
181
|
-
return reservations
|
|
167
|
+
async def list_services(self):
|
|
168
|
+
resp = await self.trainml._query(f"/project/{self._id}/services", "GET")
|
|
169
|
+
services = [ProjectService(self.trainml, **service) for service in resp]
|
|
170
|
+
return services
|
|
182
171
|
|
|
183
172
|
async def refresh_datastores(self):
|
|
184
173
|
await self.trainml._query(f"/project/{self._id}/datastores", "PATCH")
|
|
185
174
|
|
|
186
|
-
async def
|
|
187
|
-
await self.trainml._query(f"/project/{self._id}/
|
|
175
|
+
async def refresh_services(self):
|
|
176
|
+
await self.trainml._query(f"/project/{self._id}/services", "PATCH")
|
|
@@ -15,7 +15,7 @@ tests/integration/test_volumes_integration.py,sha256=gOmZpwwFxqeOAVmfKWSTmuyshx8
|
|
|
15
15
|
tests/integration/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
tests/integration/cloudbender/test_providers_integration.py,sha256=oV8ydFsosDZ_Z1Dkg2IN-ZhWuIl5e_HkHAORMsOsAJc,1473
|
|
17
17
|
tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
tests/unit/conftest.py,sha256=
|
|
18
|
+
tests/unit/conftest.py,sha256=QW9K3c-rEB8epqOW0tUwTGqcfJv14AW0ovLNqdstpAc,31129
|
|
19
19
|
tests/unit/test_auth.py,sha256=nfhlOCR7rUsn_MaD8QQtBc2v0k8pIxqbzGgRAZK1WGc,858
|
|
20
20
|
tests/unit/test_checkpoints_unit.py,sha256=4Add2DXZCuriSZ0atvOXc8fsEGMaEfPhYmT8Q3UgP5E,16008
|
|
21
21
|
tests/unit/test_connections_unit.py,sha256=FzN2ddQxNpjxzNGUsXhjTk0HnD24wSPelPTL4o_r-Ho,5507
|
|
@@ -25,7 +25,7 @@ tests/unit/test_exceptions.py,sha256=3tAok6kAU1QRjN7qTNVYuSGWDg7IEoK__OXFLyzLr7k
|
|
|
25
25
|
tests/unit/test_gpu_types_unit.py,sha256=c9ie6YSYT5onBnlmHvHWON9WgQiJ1eO2C-4Tk-UPQHg,2054
|
|
26
26
|
tests/unit/test_jobs_unit.py,sha256=bZxN9HUfHCyQCjZCZGn6WFIhu8S5FU1z5ZG9sgH2XEg,26835
|
|
27
27
|
tests/unit/test_models_unit.py,sha256=uezWF7FUHGmCSQBtpyyKhBttTnCTRjxU22NsHdJLYYg,15064
|
|
28
|
-
tests/unit/test_projects_unit.py,sha256=
|
|
28
|
+
tests/unit/test_projects_unit.py,sha256=V7PvCFiXoWI_3dyGJnruBzNbNsolBHdUpaR_1XtHmzI,9238
|
|
29
29
|
tests/unit/test_trainml.py,sha256=8vAKvFD1xYsx_VY4HFVa0b1MUlMoNApY6TO8r7vI-UQ,1701
|
|
30
30
|
tests/unit/test_volumes_unit.py,sha256=KHVmdbQIiX8tEE09U-XsH-vl6wfYGVoRzR_UQJlhOVE,15305
|
|
31
31
|
tests/unit/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -36,7 +36,7 @@ tests/unit/cli/test_cli_environment_unit.py,sha256=7FFKFPVa6yqJqujTXD_tsSUDruW5K
|
|
|
36
36
|
tests/unit/cli/test_cli_gpu_unit.py,sha256=FIq3tQIDmeD-pvxkhJKMykfnlWcVxho2vRkoCMXgQxE,650
|
|
37
37
|
tests/unit/cli/test_cli_job_unit.py,sha256=xUqkLFDIyI1ExiVVgr-218gQSlFSYCm4RRX_eyipJhY,611
|
|
38
38
|
tests/unit/cli/test_cli_model_unit.py,sha256=fE-CRVg8gbtDlwrKBkf-hc9x7EhFlYeE3jlum1E27EA,629
|
|
39
|
-
tests/unit/cli/test_cli_project_unit.py,sha256=
|
|
39
|
+
tests/unit/cli/test_cli_project_unit.py,sha256=pi0N-XVH5wsJQ6idFcRsIofWSAdj8KtK1gwf1J3l2JY,1688
|
|
40
40
|
tests/unit/cli/test_cli_volume_unit.py,sha256=oggGL2eLiaExP6rSdFmQevxLp6nw5o7SKUEqMKBmy_A,644
|
|
41
41
|
tests/unit/cli/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
tests/unit/cli/cloudbender/test_cli_datastore_unit.py,sha256=DQWDjqg4viBZRONi00nVzqF9rJ5qKOKRub9pKbTmMWU,1381
|
|
@@ -44,7 +44,7 @@ tests/unit/cli/cloudbender/test_cli_device_unit.py,sha256=2BSMyXQ8fOzNKh_-pa_tx7
|
|
|
44
44
|
tests/unit/cli/cloudbender/test_cli_node_unit.py,sha256=KbK7axJ1L4y4sN7KQRpOVIqphnNpi0aFW4-HlYLtwnI,1316
|
|
45
45
|
tests/unit/cli/cloudbender/test_cli_provider_unit.py,sha256=Rm-tRNPbTTB7ZzkkIpLfDp_pEYfqihjB0ZYk_EPQUfs,781
|
|
46
46
|
tests/unit/cli/cloudbender/test_cli_region_unit.py,sha256=iH5AbrzZ-R2EJ-Bd2HFN7FN2lTpkr3-pCLR59ZVvdQU,1262
|
|
47
|
-
tests/unit/cli/cloudbender/test_cli_reservation_unit.py,sha256=
|
|
47
|
+
tests/unit/cli/cloudbender/test_cli_reservation_unit.py,sha256=4LDOJDXygMuho2cdI2K59eq4oyiry9hNaG0avEr0_tw,1311
|
|
48
48
|
tests/unit/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
tests/unit/cloudbender/test_datastores_unit.py,sha256=54mPokxhrRjlkBfqpmeA_q-PLml-HUNNit91aQVTpCg,5398
|
|
50
50
|
tests/unit/cloudbender/test_device_configs_unit.py,sha256=lzyCuF7MRoQrtJVTQFL27lqPnRwQFv25htPgJqDuQI8,5714
|
|
@@ -53,7 +53,8 @@ tests/unit/cloudbender/test_nodes_unit.py,sha256=BDpfJXCBNNpLt5rhJMk2BVXDQ_4QSmx
|
|
|
53
53
|
tests/unit/cloudbender/test_providers_unit.py,sha256=OgxifgC1IqLH8DNMKXy1Ne9_7a75ea6kHEOfRSRoQuQ,4373
|
|
54
54
|
tests/unit/cloudbender/test_regions_unit.py,sha256=BbJICLIQmlotpA1UmLD0KTW_H9g2UW0J8ZYzQk1_Xjc,6299
|
|
55
55
|
tests/unit/cloudbender/test_reservations_unit.py,sha256=nWEZ_p9EF2C49nbgL7Dt4NG2Irmyt94ZqJJQDyNfGFI,5624
|
|
56
|
-
|
|
56
|
+
tests/unit/cloudbender/test_services_unit.py,sha256=zhYfCHSaWN6GzLTmt3RuXZCr2bZjptUEakzE4UK4Rw4,5043
|
|
57
|
+
trainml/__init__.py,sha256=VWx1-OcS6OKjpjgZwXYaDSluXSUTDqT-xV9rj07ufnc,432
|
|
57
58
|
trainml/__main__.py,sha256=JgErYkiskih8Y6oRwowALtR-rwQhAAdqOYWjQraRIPI,59
|
|
58
59
|
trainml/auth.py,sha256=gruZv27nhttrCbhcVQTH9kZkF2uMm1E06SwA_2pQAHQ,26565
|
|
59
60
|
trainml/checkpoints.py,sha256=726yaeTHzIPvkQ-BhqmvJ6u0s1Oh732N6UAFbuWEv0Q,8274
|
|
@@ -64,7 +65,7 @@ trainml/exceptions.py,sha256=MG1FkcjRacv3HoPuBS1IWLCUk0wGHEQ6DaOzXNymsNI,4094
|
|
|
64
65
|
trainml/gpu_types.py,sha256=mm-dwfYc02192bmYPIJmzesndyBcoOdkKYBaYZXOUwU,1901
|
|
65
66
|
trainml/jobs.py,sha256=iuSKkZDK908K0JwZjSbEk1G6IzdKp7vGkXsAUfih6R8,17838
|
|
66
67
|
trainml/models.py,sha256=Lqs3OJMuOZXx8cfGFNC3JZ8nVK6l9_jU1xM1Uw1c5UQ,7750
|
|
67
|
-
trainml/projects.py,sha256=
|
|
68
|
+
trainml/projects.py,sha256=11as6-XwSabaWrkVc7UqnT2izJYDbp6BdcOdgr3z3Dc,5001
|
|
68
69
|
trainml/trainml.py,sha256=EBnqQ3Q291xrPKYuN6xKm5yt0mJQOJ3b7GAlR-fl8NI,10864
|
|
69
70
|
trainml/volumes.py,sha256=x4_QLPnPCuqEWR9FjwotCVPYFhdiZuWjp4tKKEU-Ne4,8094
|
|
70
71
|
trainml/cli/__init__.py,sha256=Gvj6oGSEtgpb40ACtiVeMD93GM-uy15MG6VlX6rwdwA,4346
|
|
@@ -74,19 +75,20 @@ trainml/cli/dataset.py,sha256=Pc00M6t7hGoRzCxznmmkijsWhG4PhIfG7UkrwtwykTY,6871
|
|
|
74
75
|
trainml/cli/environment.py,sha256=dfm_T8OlCM5M8dLyOQBapJl3eFuVIku2P4JO6V0cYVQ,1019
|
|
75
76
|
trainml/cli/gpu.py,sha256=CMcQyl2qbUgc2bc-gvUVT6X7bq2-sgiCHl3hyZ6kFWM,883
|
|
76
77
|
trainml/cli/model.py,sha256=hR23E6ttRXcLk-RofkPK6wUXMO7OU6sT6jTEHTmUg9Q,6111
|
|
77
|
-
trainml/cli/project.py,sha256=
|
|
78
|
+
trainml/cli/project.py,sha256=f772bHs68AVRY60l7dbVKgeDmDC3u2bZjqrz7zm7xvQ,3314
|
|
78
79
|
trainml/cli/volume.py,sha256=kDUss93N78DT-YlLjC6I3jEq5nBWfRNNR5M4tY_F_Zg,6246
|
|
79
|
-
trainml/cli/cloudbender/__init__.py,sha256=
|
|
80
|
+
trainml/cli/cloudbender/__init__.py,sha256=mnHLTfXv3yxgk77rhq9ie7BAkLpXqc3hPlCkOxVfIQ4,526
|
|
80
81
|
trainml/cli/cloudbender/datastore.py,sha256=gJ-comfAq65uiPoONQ35UIDLNVN7QKMf3l_2EcTN6zY,3478
|
|
81
82
|
trainml/cli/cloudbender/device.py,sha256=KGZCFwwvS4tWsWuudrhlvquu_IFtV7LCUAOmCajicic,3453
|
|
82
83
|
trainml/cli/cloudbender/node.py,sha256=iN_WaPCxOhtgDtnSsIFAEMGADG4MKiLjWoez6YSYwZI,3843
|
|
83
84
|
trainml/cli/cloudbender/provider.py,sha256=oFjZWKfFQjNY7OtDu7nUdfv-RTmQc_Huuug963D3BdA,1726
|
|
84
85
|
trainml/cli/cloudbender/region.py,sha256=X6-FYOb-pGpOEazn-NbsYSwa9ergB7FGATFkTe4a8Pk,2892
|
|
85
86
|
trainml/cli/cloudbender/reservation.py,sha256=z2oMYwp-w_Keo1DepKUtuRnwiGz2VscVHDYWEFap1gs,3569
|
|
87
|
+
trainml/cli/cloudbender/service.py,sha256=lHTYkUUtFMtrGgaAPoGAHaRtJq5HuwRKapx5PEl_Slk,2869
|
|
86
88
|
trainml/cli/job/__init__.py,sha256=ljY-ELeXhXQ7txASbJEKGBom7OXfNyy7sWILz3nxRAE,6545
|
|
87
89
|
trainml/cli/job/create.py,sha256=pfOCqs5Vfk4PAI5KZpXHJ1vp3DDe4ccvYzieh0oFexY,34288
|
|
88
90
|
trainml/cloudbender/__init__.py,sha256=iE29obtC0_9f0IhRvHQcG5aY58fVhVYipTakpjAhdss,64
|
|
89
|
-
trainml/cloudbender/cloudbender.py,sha256=
|
|
91
|
+
trainml/cloudbender/cloudbender.py,sha256=7qIClWo5uvXm8U_u0_nrs7rNUIegyj9QzvktX10HxSE,618
|
|
90
92
|
trainml/cloudbender/datastores.py,sha256=biVGifedc3r1DcuxsfCQh-f1Tw4HcJMMJfdgHxPfkKM,3506
|
|
91
93
|
trainml/cloudbender/device_configs.py,sha256=DJWiGFaOE4C4xLE1BLDAiEjeL4T00R3FA_pb1xnSOr4,3399
|
|
92
94
|
trainml/cloudbender/devices.py,sha256=QORNmKdLJoqGZmeWXRnivC1JmNBIw-ebvf4bsoem3r8,5660
|
|
@@ -94,9 +96,10 @@ trainml/cloudbender/nodes.py,sha256=7HV2VLmxiUcJ-Kc6AAXS3M8C_XO-HKmaVgJpPdVnBQk,
|
|
|
94
96
|
trainml/cloudbender/providers.py,sha256=-gkdiTu6Ah2znUuyyc3ZuRALagW8s1-OgqVjtlvc1AU,2036
|
|
95
97
|
trainml/cloudbender/regions.py,sha256=Aqc_MeLVAeEv21e-lR5u8x1eintqUhZT2DBiQG3AcEE,3570
|
|
96
98
|
trainml/cloudbender/reservations.py,sha256=rOrGXWIUHON4ad2aufEcvK4Yv_Mv3dDoScUtLJE8LWw,3586
|
|
97
|
-
trainml
|
|
98
|
-
trainml-0.5.
|
|
99
|
-
trainml-0.5.
|
|
100
|
-
trainml-0.5.
|
|
101
|
-
trainml-0.5.
|
|
102
|
-
trainml-0.5.
|
|
99
|
+
trainml/cloudbender/services.py,sha256=mXCdXwWgV1uhPQ05e0qwnqUBrx3gM4sdCnIiiK4j9Ic,3222
|
|
100
|
+
trainml-0.5.6.dist-info/LICENSE,sha256=s0lpBxhSSUEpMavwde-Vb6K_K7xDCTTvSpNznVqVGR0,1069
|
|
101
|
+
trainml-0.5.6.dist-info/METADATA,sha256=TziEO1YevXuR39PH2doZ1rho_6HYgiPUCm0oKXunemU,7345
|
|
102
|
+
trainml-0.5.6.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
103
|
+
trainml-0.5.6.dist-info/entry_points.txt,sha256=OzBDm2wXby1bSGF02jTVxzRFZLejnbFiLHXhKdW3Bds,63
|
|
104
|
+
trainml-0.5.6.dist-info/top_level.txt,sha256=Y1kLFRWKUW7RG8BX7cvejHF_yW8wBOaRYF1JQHENY4w,23
|
|
105
|
+
trainml-0.5.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|