proximl 0.5.0__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {proximl-0.5.0/proximl.egg-info → proximl-0.5.2}/PKG-INFO +1 -1
  2. {proximl-0.5.0 → proximl-0.5.2}/proximl/__init__.py +1 -1
  3. {proximl-0.5.0 → proximl-0.5.2}/proximl/auth.py +3 -7
  4. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/__init__.py +1 -0
  5. proximl-0.5.2/proximl/cli/cloudbender/device.py +157 -0
  6. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/dataset.py +1 -3
  7. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/job/create.py +3 -3
  8. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/model.py +2 -0
  9. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/cloudbender.py +2 -0
  10. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/datastores.py +8 -0
  11. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/device_configs.py +8 -0
  12. proximl-0.5.2/proximl/cloudbender/devices.py +190 -0
  13. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/nodes.py +22 -0
  14. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/providers.py +8 -0
  15. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/regions.py +8 -0
  16. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/reservations.py +8 -0
  17. {proximl-0.5.0 → proximl-0.5.2}/proximl/datasets.py +25 -14
  18. {proximl-0.5.0 → proximl-0.5.2}/proximl/models.py +22 -3
  19. {proximl-0.5.0 → proximl-0.5.2}/proximl/proximl.py +2 -1
  20. {proximl-0.5.0 → proximl-0.5.2/proximl.egg-info}/PKG-INFO +1 -1
  21. {proximl-0.5.0 → proximl-0.5.2}/proximl.egg-info/SOURCES.txt +4 -0
  22. {proximl-0.5.0 → proximl-0.5.2}/pyproject.toml +1 -0
  23. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_jobs_integration.py +7 -12
  24. proximl-0.5.2/tests/unit/cli/cloudbender/test_cli_device_unit.py +38 -0
  25. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_datastores_unit.py +20 -0
  26. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_device_configs_unit.py +21 -0
  27. proximl-0.5.2/tests/unit/cloudbender/test_devices_unit.py +270 -0
  28. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_nodes_unit.py +43 -0
  29. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_providers_unit.py +16 -0
  30. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_regions_unit.py +18 -0
  31. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/test_reservations_unit.py +20 -0
  32. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/conftest.py +51 -0
  33. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_auth.py +1 -1
  34. {proximl-0.5.0 → proximl-0.5.2}/LICENSE +0 -0
  35. {proximl-0.5.0 → proximl-0.5.2}/README.md +0 -0
  36. {proximl-0.5.0 → proximl-0.5.2}/examples/__init__.py +0 -0
  37. {proximl-0.5.0 → proximl-0.5.2}/examples/create_dataset_and_training_job.py +0 -0
  38. {proximl-0.5.0 → proximl-0.5.2}/examples/local_storage.py +0 -0
  39. {proximl-0.5.0 → proximl-0.5.2}/examples/training_inference_pipeline.py +0 -0
  40. {proximl-0.5.0 → proximl-0.5.2}/proximl/__main__.py +0 -0
  41. {proximl-0.5.0 → proximl-0.5.2}/proximl/checkpoints.py +0 -0
  42. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/__init__.py +0 -0
  43. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/checkpoint.py +0 -0
  44. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/datastore.py +0 -0
  45. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/node.py +0 -0
  46. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/provider.py +0 -0
  47. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/region.py +0 -0
  48. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/cloudbender/reservation.py +0 -0
  49. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/connection.py +0 -0
  50. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/environment.py +0 -0
  51. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/gpu.py +0 -0
  52. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/job/__init__.py +0 -0
  53. {proximl-0.5.0 → proximl-0.5.2}/proximl/cli/project.py +0 -0
  54. {proximl-0.5.0 → proximl-0.5.2}/proximl/cloudbender/__init__.py +0 -0
  55. {proximl-0.5.0 → proximl-0.5.2}/proximl/connections.py +0 -0
  56. {proximl-0.5.0 → proximl-0.5.2}/proximl/environments.py +0 -0
  57. {proximl-0.5.0 → proximl-0.5.2}/proximl/exceptions.py +0 -0
  58. {proximl-0.5.0 → proximl-0.5.2}/proximl/gpu_types.py +0 -0
  59. {proximl-0.5.0 → proximl-0.5.2}/proximl/jobs.py +0 -0
  60. {proximl-0.5.0 → proximl-0.5.2}/proximl/projects.py +0 -0
  61. {proximl-0.5.0 → proximl-0.5.2}/proximl.egg-info/dependency_links.txt +0 -0
  62. {proximl-0.5.0 → proximl-0.5.2}/proximl.egg-info/entry_points.txt +0 -0
  63. {proximl-0.5.0 → proximl-0.5.2}/proximl.egg-info/requires.txt +0 -0
  64. {proximl-0.5.0 → proximl-0.5.2}/proximl.egg-info/top_level.txt +0 -0
  65. {proximl-0.5.0 → proximl-0.5.2}/setup.cfg +0 -0
  66. {proximl-0.5.0 → proximl-0.5.2}/setup.py +0 -0
  67. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/__init__.py +0 -0
  68. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/cloudbender/__init__.py +0 -0
  69. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/cloudbender/test_providers_integration.py +0 -0
  70. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/conftest.py +0 -0
  71. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_checkpoints_integration.py +0 -0
  72. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_datasets_integration.py +0 -0
  73. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_environments_integration.py +0 -0
  74. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_gpu_types_integration.py +0 -0
  75. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_models_integration.py +0 -0
  76. {proximl-0.5.0 → proximl-0.5.2}/tests/integration/test_projects_integration.py +0 -0
  77. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/__init__.py +0 -0
  78. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/__init__.py +0 -0
  79. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/__init__.py +0 -0
  80. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/test_cli_datastore_unit.py +0 -0
  81. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/test_cli_node_unit.py +0 -0
  82. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/test_cli_provider_unit.py +0 -0
  83. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/test_cli_region_unit.py +0 -0
  84. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/cloudbender/test_cli_reservation_unit.py +0 -0
  85. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/conftest.py +0 -0
  86. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_checkpoint_unit.py +0 -0
  87. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_datasets_unit.py +0 -0
  88. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_environment_unit.py +0 -0
  89. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_gpu_unit.py +0 -0
  90. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_job_unit.py +0 -0
  91. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_model_unit.py +0 -0
  92. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cli/test_cli_project_unit.py +0 -0
  93. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/cloudbender/__init__.py +0 -0
  94. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_checkpoints_unit.py +0 -0
  95. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_connections_unit.py +0 -0
  96. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_datasets_unit.py +0 -0
  97. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_environments_unit.py +0 -0
  98. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_exceptions.py +0 -0
  99. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_gpu_types_unit.py +0 -0
  100. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_jobs_unit.py +0 -0
  101. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_models_unit.py +0 -0
  102. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_projects_unit.py +0 -0
  103. {proximl-0.5.0 → proximl-0.5.2}/tests/unit/test_proximl.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: proximl
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: proxiML client SDK and command line utilities
5
5
  Home-page: https://github.com/proxiML/python-sdk
6
6
  Author: proxiML
@@ -13,5 +13,5 @@ logging.basicConfig(
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
- __version__ = "0.5.0"
16
+ __version__ = "0.5.2"
17
17
  __all__ = "ProxiML"
@@ -222,10 +222,6 @@ n_hex = (
222
222
  g_hex = "2"
223
223
  info_bits = bytearray("Caldera Derived Key", "utf-8")
224
224
 
225
- CONFIG_DIR = os.path.expanduser(
226
- os.environ.get("PROXIML_CONFIG_DIR") or "~/.proximl"
227
- )
228
-
229
225
 
230
226
  def hash_sha256(buf):
231
227
  """AuthenticationHelper.hash"""
@@ -512,9 +508,9 @@ class AWSSRP(object):
512
508
 
513
509
 
514
510
  class Auth(object):
515
- def __init__(self, domain_suffix="proximl.ai", **kwargs):
511
+ def __init__(self, config_dir, domain_suffix="proximl.ai", **kwargs):
516
512
  try:
517
- with open(f"{CONFIG_DIR}/environment.json", "r") as file:
513
+ with open(f"{config_dir}/environment.json", "r") as file:
518
514
  env_str = file.read().replace("\n", "")
519
515
  env = json.loads(env_str)
520
516
  except:
@@ -544,7 +540,7 @@ class Auth(object):
544
540
  )
545
541
 
546
542
  try:
547
- with open(f"{CONFIG_DIR}/credentials.json", "r") as file:
543
+ with open(f"{config_dir}/credentials.json", "r") as file:
548
544
  key_str = file.read().replace("\n", "")
549
545
  keys = json.loads(key_str)
550
546
  except:
@@ -13,5 +13,6 @@ def cloudbender(config):
13
13
  from proximl.cli.cloudbender.provider import provider
14
14
  from proximl.cli.cloudbender.region import region
15
15
  from proximl.cli.cloudbender.node import node
16
+ from proximl.cli.cloudbender.device import device
16
17
  from proximl.cli.cloudbender.datastore import datastore
17
18
  from proximl.cli.cloudbender.reservation import reservation
@@ -0,0 +1,157 @@
1
+ import click
2
+ from proximl.cli import cli, pass_config, search_by_id_name
3
+ from proximl.cli.cloudbender import cloudbender
4
+
5
+
6
+ @cloudbender.group()
7
+ @pass_config
8
+ def device(config):
9
+ """proxiML CloudBender device commands."""
10
+ pass
11
+
12
+
13
+ @device.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 devices for.",
27
+ )
28
+ @pass_config
29
+ def list(config, provider, region):
30
+ """List devices."""
31
+ data = [
32
+ [
33
+ "ID",
34
+ "NAME",
35
+ "STATUS",
36
+ "JOB STATUS",
37
+ "ONLINE",
38
+ "MAINTENANCE",
39
+ ],
40
+ [
41
+ "-" * 80,
42
+ "-" * 80,
43
+ "-" * 80,
44
+ "-" * 80,
45
+ "-" * 80,
46
+ "-" * 80,
47
+ "-" * 80,
48
+ ],
49
+ ]
50
+
51
+ devices = config.proximl.run(
52
+ config.proximl.client.cloudbender.devices.list(
53
+ provider_uuid=provider, region_uuid=region
54
+ )
55
+ )
56
+
57
+ for device in devices:
58
+ data.append(
59
+ [
60
+ device.id,
61
+ device.name,
62
+ device.status,
63
+ device.job_status,
64
+ "X" if device.online else "",
65
+ "X" if device.maintenance_mode else "",
66
+ ]
67
+ )
68
+
69
+ for row in data:
70
+ click.echo(
71
+ "{: >37.36} {: >29.28} {: >9.8} {: >11.10} {: >7.6} {: >12.11}"
72
+ "".format(*row),
73
+ file=config.stdout,
74
+ )
75
+
76
+
77
+ @device.command()
78
+ @click.option(
79
+ "--provider",
80
+ "-p",
81
+ type=click.STRING,
82
+ required=True,
83
+ help="The provider ID of the region.",
84
+ )
85
+ @click.option(
86
+ "--region",
87
+ "-r",
88
+ type=click.STRING,
89
+ required=True,
90
+ help="The region ID to create the region in.",
91
+ )
92
+ @click.option(
93
+ "--minion-id",
94
+ "-m",
95
+ type=click.STRING,
96
+ required=True,
97
+ help="The minion_id of the new node.",
98
+ )
99
+ @click.option(
100
+ "--hostname",
101
+ "-h",
102
+ type=click.STRING,
103
+ help="The hostname (if different from name)",
104
+ )
105
+ @click.argument("name", type=click.STRING, required=True)
106
+ @pass_config
107
+ def create(config, provider, region, minion_id, hostname, name):
108
+ """
109
+ Creates a node.
110
+ """
111
+ if not hostname:
112
+ hostname = name
113
+ return config.proximl.run(
114
+ config.proximl.client.cloudbender.devices.create(
115
+ provider_uuid=provider,
116
+ region_uuid=region,
117
+ friendly_name=name,
118
+ hostname=hostname,
119
+ minion_id=minion_id,
120
+ )
121
+ )
122
+
123
+
124
+ @device.command()
125
+ @click.option(
126
+ "--provider",
127
+ "-p",
128
+ type=click.STRING,
129
+ required=True,
130
+ help="The provider ID of the region.",
131
+ )
132
+ @click.option(
133
+ "--region",
134
+ "-r",
135
+ type=click.STRING,
136
+ required=True,
137
+ help="The region ID to delete the node from.",
138
+ )
139
+ @click.argument("device", type=click.STRING)
140
+ @pass_config
141
+ def remove(config, provider, region, node):
142
+ """
143
+ Remove a device.
144
+
145
+ DEVICE may be specified by name or ID, but ID is preferred.
146
+ """
147
+ devices = config.proximl.run(
148
+ config.proximl.client.cloudbender.devices.list(
149
+ provider_uuid=provider, region_uuid=region
150
+ )
151
+ )
152
+
153
+ found = search_by_id_name(device, devices)
154
+ if None is found:
155
+ raise click.UsageError("Cannot find specified device.")
156
+
157
+ return config.proximl.run(found.remove())
@@ -252,9 +252,7 @@ def rename(config, dataset, name):
252
252
  DATASET may be specified by name or ID, but ID is preferred.
253
253
  """
254
254
  try:
255
- dataset = config.proximl.run(
256
- config.proximl.client.datasets.get(dataset)
257
- )
255
+ dataset = config.proximl.run(config.proximl.client.datasets.get(dataset))
258
256
  if dataset is None:
259
257
  raise click.UsageError("Cannot find specified dataset.")
260
258
  except:
@@ -683,7 +683,7 @@ def training(
683
683
  config.proximl.client.jobs.create(
684
684
  name=name,
685
685
  type="training",
686
- gpu_type=gpu_type,
686
+ gpu_types=gpu_type,
687
687
  cpu_count=cpu_count,
688
688
  disk_size=disk_size,
689
689
  workers=[command for command in commands],
@@ -1021,7 +1021,7 @@ def inference(
1021
1021
  config.proximl.client.jobs.create(
1022
1022
  name=name,
1023
1023
  type="inference",
1024
- gpu_type=gpu_type,
1024
+ gpu_types=gpu_type,
1025
1025
  cpu_count=cpu_count,
1026
1026
  disk_size=disk_size,
1027
1027
  workers=[command],
@@ -1330,7 +1330,7 @@ def endpoint(
1330
1330
  config.proximl.client.jobs.create(
1331
1331
  name=name,
1332
1332
  type="endpoint",
1333
- gpu_type=gpu_type,
1333
+ gpu_types=gpu_type,
1334
1334
  cpu_count=cpu_count,
1335
1335
  disk_size=disk_size,
1336
1336
  endpoint=dict(routes=routes),
@@ -1,4 +1,5 @@
1
1
  import click
2
+ import logging
2
3
  from proximl.cli import cli, pass_config, search_by_id_name
3
4
 
4
5
 
@@ -64,6 +65,7 @@ def connect(config, model, attach):
64
65
  models = config.proximl.run(config.proximl.client.models.list())
65
66
 
66
67
  found = search_by_id_name(model, models)
68
+ logging.debug(found)
67
69
  if None is found:
68
70
  raise click.UsageError("Cannot find specified model.")
69
71
 
@@ -1,6 +1,7 @@
1
1
  from .providers import Providers
2
2
  from .regions import Regions
3
3
  from .nodes import Nodes
4
+ from .devices import Devices
4
5
  from .datastores import Datastores
5
6
  from .reservations import Reservations
6
7
  from .device_configs import DeviceConfigs
@@ -12,6 +13,7 @@ class Cloudbender(object):
12
13
  self.providers = Providers(proximl)
13
14
  self.regions = Regions(proximl)
14
15
  self.nodes = Nodes(proximl)
16
+ self.devices = Devices(proximl)
15
17
  self.datastores = Datastores(proximl)
16
18
  self.reservations = Reservations(proximl)
17
19
  self.device_configs = DeviceConfigs(proximl)
@@ -118,3 +118,11 @@ class Datastore:
118
118
  f"/provider/{self._provider_uuid}/region/{self._region_uuid}/datastore/{self._id}",
119
119
  "DELETE",
120
120
  )
121
+
122
+ async def refresh(self):
123
+ resp = await self.proximl._query(
124
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/datastore/{self._id}",
125
+ "GET",
126
+ )
127
+ self.__init__(self.proximl, **resp)
128
+ return self
@@ -107,3 +107,11 @@ class DeviceConfig:
107
107
  f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/config/{self._id}",
108
108
  "DELETE",
109
109
  )
110
+
111
+ async def refresh(self):
112
+ resp = await self.proximl._query(
113
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/config/{self._id}",
114
+ "GET",
115
+ )
116
+ self.__init__(self.proximl, **resp)
117
+ return self
@@ -0,0 +1,190 @@
1
+ import json
2
+ import logging
3
+
4
+
5
+ class Devices(object):
6
+ def __init__(self, proximl):
7
+ self.proximl = proximl
8
+
9
+ async def get(self, provider_uuid, region_uuid, id, **kwargs):
10
+ resp = await self.proximl._query(
11
+ f"/provider/{provider_uuid}/region/{region_uuid}/device/{id}",
12
+ "GET",
13
+ kwargs,
14
+ )
15
+ return Device(self.proximl, **resp)
16
+
17
+ async def list(self, provider_uuid, region_uuid, **kwargs):
18
+ resp = await self.proximl._query(
19
+ f"/provider/{provider_uuid}/region/{region_uuid}/device",
20
+ "GET",
21
+ kwargs,
22
+ )
23
+ devices = [Device(self.proximl, **device) for device in resp]
24
+ return devices
25
+
26
+ async def create(
27
+ self,
28
+ provider_uuid,
29
+ region_uuid,
30
+ friendly_name,
31
+ hostname,
32
+ minion_id,
33
+ **kwargs,
34
+ ):
35
+ logging.info(f"Creating Device {friendly_name}")
36
+ data = dict(
37
+ friendly_name=friendly_name,
38
+ hostname=hostname,
39
+ minion_id=minion_id,
40
+ type="device",
41
+ service="compute",
42
+ **kwargs,
43
+ )
44
+ payload = {k: v for k, v in data.items() if v is not None}
45
+ resp = await self.proximl._query(
46
+ f"/provider/{provider_uuid}/region/{region_uuid}/device",
47
+ "POST",
48
+ None,
49
+ payload,
50
+ )
51
+ device = Device(self.proximl, **resp)
52
+ logging.info(f"Created Device {friendly_name} with id {device.id}")
53
+ return device
54
+
55
+ async def remove(self, provider_uuid, region_uuid, id, **kwargs):
56
+ await self.proximl._query(
57
+ f"/provider/{provider_uuid}/region/{region_uuid}/device/{id}",
58
+ "DELETE",
59
+ kwargs,
60
+ )
61
+
62
+
63
+ class Device:
64
+ def __init__(self, proximl, **kwargs):
65
+ self.proximl = proximl
66
+ self._device = kwargs
67
+ self._id = self._device.get("device_id")
68
+ self._provider_uuid = self._device.get("provider_uuid")
69
+ self._region_uuid = self._device.get("region_uuid")
70
+ self._name = self._device.get("friendly_name")
71
+ self._hostname = self._device.get("hostname")
72
+ self._status = self._device.get("status")
73
+ self._online = self._device.get("online")
74
+ self._maintenance_mode = self._device.get("maintenance_mode")
75
+ self._device_config_id = self._device.get("device_config_id")
76
+ self._job_status = self._device.get("job_status")
77
+ self._job_last_deployed = self._device.get("job_last_deployed")
78
+ self._job_config_id = self._device.get("job_config_id")
79
+ self._job_config_revision = self._device.get("job_config_revision")
80
+
81
+ @property
82
+ def id(self) -> str:
83
+ return self._id
84
+
85
+ @property
86
+ def provider_uuid(self) -> str:
87
+ return self._provider_uuid
88
+
89
+ @property
90
+ def region_uuid(self) -> str:
91
+ return self._region_uuid
92
+
93
+ @property
94
+ def name(self) -> str:
95
+ return self._name
96
+
97
+ @property
98
+ def hostname(self) -> str:
99
+ return self._hostname
100
+
101
+ @property
102
+ def status(self) -> str:
103
+ return self._status
104
+
105
+ @property
106
+ def online(self) -> bool:
107
+ return self._online
108
+
109
+ @property
110
+ def maintenance_mode(self) -> bool:
111
+ return self._maintenance_mode
112
+
113
+ @property
114
+ def device_config_id(self) -> str:
115
+ return self._device_config_id
116
+
117
+ @property
118
+ def job_status(self) -> str:
119
+ return self._job_status
120
+
121
+ @property
122
+ def job_last_deployed(self) -> str:
123
+ return self._job_last_deployed
124
+
125
+ @property
126
+ def job_config_id(self) -> str:
127
+ return self._job_config_id
128
+
129
+ @property
130
+ def job_config_revision(self) -> str:
131
+ return self._job_config_revision
132
+
133
+ def __str__(self):
134
+ return json.dumps({k: v for k, v in self._device.items()})
135
+
136
+ def __repr__(self):
137
+ return f"Device( proximl , **{self._device.__repr__()})"
138
+
139
+ def __bool__(self):
140
+ return bool(self._id)
141
+
142
+ async def remove(self):
143
+ await self.proximl._query(
144
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}",
145
+ "DELETE",
146
+ )
147
+
148
+ async def refresh(self):
149
+ resp = await self.proximl._query(
150
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}",
151
+ "GET",
152
+ )
153
+ self.__init__(self.proximl, **resp)
154
+ return self
155
+
156
+ async def toggle_maintenance(self):
157
+ await self.proximl._query(
158
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}/maintenance",
159
+ "PATCH",
160
+ )
161
+
162
+ async def run_action(self, command):
163
+ await self.proximl._query(
164
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}/action",
165
+ "POST",
166
+ None,
167
+ dict(command=command),
168
+ )
169
+
170
+ async def set_config(self, device_config_id):
171
+ resp = await self.proximl._query(
172
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}",
173
+ "PATCH",
174
+ None,
175
+ dict(device_config_id=device_config_id),
176
+ )
177
+ self.__init__(self.proximl, **resp)
178
+ return self
179
+
180
+ async def deploy_endpoint(self):
181
+ await self.proximl._query(
182
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}/deploy",
183
+ "PUT",
184
+ )
185
+
186
+ async def stop_endpoint(self):
187
+ await self.proximl._query(
188
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/device/{self._id}/stop",
189
+ "PUT",
190
+ )
@@ -131,3 +131,25 @@ class Node:
131
131
  f"/provider/{self._provider_uuid}/region/{self._region_uuid}/node/{self._id}",
132
132
  "DELETE",
133
133
  )
134
+
135
+ async def refresh(self):
136
+ resp = await self.proximl._query(
137
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/node/{self._id}",
138
+ "GET",
139
+ )
140
+ self.__init__(self.proximl, **resp)
141
+ return self
142
+
143
+ async def toggle_maintenance(self):
144
+ await self.proximl._query(
145
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/node/{self._id}/maintenance",
146
+ "PATCH",
147
+ )
148
+
149
+ async def run_action(self, command):
150
+ await self.proximl._query(
151
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/node/{self._id}/action",
152
+ "POST",
153
+ None,
154
+ dict(command=command),
155
+ )
@@ -61,3 +61,11 @@ class Provider:
61
61
 
62
62
  async def remove(self):
63
63
  await self.proximl._query(f"/provider/{self._id}", "DELETE")
64
+
65
+ async def refresh(self):
66
+ resp = await self.proximl._query(
67
+ f"/provider/{self._id}",
68
+ "GET",
69
+ )
70
+ self.__init__(self.proximl, **resp)
71
+ return self
@@ -80,6 +80,14 @@ class Region:
80
80
  f"/provider/{self._provider_uuid}/region/{self._id}", "DELETE"
81
81
  )
82
82
 
83
+ async def refresh(self):
84
+ resp = await self.proximl._query(
85
+ f"/provider/{self._provider_uuid}/region/{self._id}",
86
+ "GET",
87
+ )
88
+ self.__init__(self.proximl, **resp)
89
+ return self
90
+
83
91
  async def add_dataset(self, project_uuid, dataset_uuid, **kwargs):
84
92
  await self.proximl._query(
85
93
  f"/provider/{self._provider_uuid}/region/{self._id}/dataset",
@@ -116,3 +116,11 @@ class Reservation:
116
116
  f"/provider/{self._provider_uuid}/region/{self._region_uuid}/reservation/{self._id}",
117
117
  "DELETE",
118
118
  )
119
+
120
+ async def refresh(self):
121
+ resp = await self.proximl._query(
122
+ f"/provider/{self._provider_uuid}/region/{self._region_uuid}/reservation/{self._id}",
123
+ "GET",
124
+ )
125
+ self.__init__(self.proximl, **resp)
126
+ return self
@@ -37,8 +37,7 @@ class Datasets(object):
37
37
  source_type=source_type,
38
38
  source_uri=source_uri,
39
39
  source_options=kwargs.get("source_options"),
40
- project_uuid=kwargs.get("project_uuid")
41
- or self.proximl.active_project,
40
+ project_uuid=kwargs.get("project_uuid") or self.proximl.active_project,
42
41
  )
43
42
  payload = {k: v for k, v in data.items() if v is not None}
44
43
  logging.info(f"Creating Dataset {name}")
@@ -119,11 +118,13 @@ class Dataset:
119
118
  entity_type="dataset",
120
119
  project_uuid=self._dataset.get("project_uuid"),
121
120
  cidr=self._dataset.get("vpn").get("cidr"),
122
- ssh_port=self._dataset.get("vpn")
123
- .get("client")
124
- .get("ssh_port"),
125
- input_path=self._dataset.get("source_uri"),
126
- output_path=None,
121
+ ssh_port=self._dataset.get("vpn").get("client").get("ssh_port"),
122
+ input_path=self._dataset.get("source_uri")
123
+ if self.status in ["new", "downloading"]
124
+ else None,
125
+ output_path=self._dataset.get("output_uri")
126
+ if self.status == "exporting"
127
+ else None,
127
128
  )
128
129
  else:
129
130
  details = dict()
@@ -133,7 +134,7 @@ class Dataset:
133
134
  if self.status in ["ready", "failed"]:
134
135
  raise SpecificationError(
135
136
  "status",
136
- f"You can only connect to new or downloading datasets.",
137
+ f"You can only connect to downloading or exporting datasets.",
137
138
  )
138
139
  if self.status == "new":
139
140
  await self.wait_for("downloading")
@@ -167,15 +168,27 @@ class Dataset:
167
168
  self.__init__(self.proximl, **resp)
168
169
  return self
169
170
 
171
+ async def export(self, output_type, output_uri, output_options=dict()):
172
+ resp = await self.proximl._query(
173
+ f"/dataset/{self._id}/export",
174
+ "POST",
175
+ dict(project_uuid=self._project_uuid),
176
+ dict(
177
+ output_type=output_type,
178
+ output_uri=output_uri,
179
+ output_options=output_options,
180
+ ),
181
+ )
182
+ self.__init__(self.proximl, **resp)
183
+ return self
184
+
170
185
  def _get_msg_handler(self, msg_handler):
171
186
  def handler(data):
172
187
  if data.get("type") == "subscription":
173
188
  if msg_handler:
174
189
  msg_handler(data)
175
190
  else:
176
- timestamp = datetime.fromtimestamp(
177
- int(data.get("time")) / 1000
178
- )
191
+ timestamp = datetime.fromtimestamp(int(data.get("time")) / 1000)
179
192
  print(
180
193
  f"{timestamp.strftime('%m/%d/%Y, %H:%M:%S')}: {data.get('msg').rstrip()}"
181
194
  )
@@ -212,9 +225,7 @@ class Dataset:
212
225
  return
213
226
  POLL_INTERVAL_MIN = 5
214
227
  POLL_INTERVAL_MAX = 60
215
- POLL_INTERVAL = max(
216
- min(timeout / 60, POLL_INTERVAL_MAX), POLL_INTERVAL_MIN
217
- )
228
+ POLL_INTERVAL = max(min(timeout / 60, POLL_INTERVAL_MAX), POLL_INTERVAL_MIN)
218
229
  retry_count = math.ceil(timeout / POLL_INTERVAL)
219
230
  count = 0
220
231
  while count < retry_count: