request-vm-on-golem 0.1.38__py3-none-any.whl → 0.1.40__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.
- {request_vm_on_golem-0.1.38.dist-info → request_vm_on_golem-0.1.40.dist-info}/METADATA +3 -3
- {request_vm_on_golem-0.1.38.dist-info → request_vm_on_golem-0.1.40.dist-info}/RECORD +8 -8
- requestor/cli/commands.py +54 -8
- requestor/services/provider_service.py +93 -2
- requestor/services/vm_service.py +1 -1
- requestor/ssh/manager.py +10 -2
- {request_vm_on_golem-0.1.38.dist-info → request_vm_on_golem-0.1.40.dist-info}/WHEEL +0 -0
- {request_vm_on_golem-0.1.38.dist-info → request_vm_on_golem-0.1.40.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: request-vm-on-golem
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.40
|
4
4
|
Summary: VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network
|
5
5
|
Keywords: golem,vm,cloud,decentralized,cli
|
6
6
|
Author: Phillip Jensen
|
@@ -175,7 +175,7 @@ Example output:
|
|
175
175
|
────────────────────────────────────────────────
|
176
176
|
🌍 Available Providers (3 total)
|
177
177
|
────────────────────────────────────────────────
|
178
|
-
Provider ID Country CPU Memory
|
178
|
+
Provider ID Country CPU Memory Disk
|
179
179
|
provider-1 🌍 SE 💻 4 🧠 8GB 💾 40GB
|
180
180
|
provider-2 🌍 US 💻 8 🧠 16GB 💾 80GB
|
181
181
|
provider-3 🌍 DE 💻 2 🧠 4GB 💾 20GB
|
@@ -206,7 +206,7 @@ Example output:
|
|
206
206
|
VM Details
|
207
207
|
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
|
208
208
|
🏷️ Name : my-webserver
|
209
|
-
💻 Resources : 2 CPU, 4GB RAM, 20GB
|
209
|
+
💻 Resources : 2 CPU, 4GB RAM, 20GB Disk
|
210
210
|
🟢 Status : running
|
211
211
|
|
212
212
|
Connection Details
|
@@ -1,7 +1,7 @@
|
|
1
1
|
requestor/__init__.py,sha256=OqSUAh1uZBMx7GW0MoSMg967PVdmT8XdPJx3QYjwkak,116
|
2
2
|
requestor/api/main.py,sha256=7utCzFNbh5Ol-vsBWeSwT4lXeHD7zdA-GFZuS3rHMWc,2180
|
3
3
|
requestor/cli/__init__.py,sha256=e3E4oEGxmGj-STPtFkQwg_qIWhR0JAiAQdw3G1hXciU,37
|
4
|
-
requestor/cli/commands.py,sha256=
|
4
|
+
requestor/cli/commands.py,sha256=sCodN_skF24IkWo6x73H3SeahdE1uFlzjJ2BpMfiLrs,28991
|
5
5
|
requestor/config.py,sha256=O39E-Wa-ewqdC9XP5nvj3zkOs52mevvFMyQGtHaqANk,4668
|
6
6
|
requestor/db/__init__.py,sha256=Gm5DfWls6uvCZZ3HGGnyRHswbUQdeA5OGN8yPwH0hc8,88
|
7
7
|
requestor/db/sqlite.py,sha256=l5pWbx2qlHuar1N_a0B9tVnmumLJY1w5rp3yZ7jmsC0,4146
|
@@ -11,14 +11,14 @@ requestor/provider/client.py,sha256=OUP7CoOCCtKD6DB9eqFkOXK6A2BLFdM4DWSkoulJQxg,
|
|
11
11
|
requestor/run.py,sha256=GqOG6n34szt8Sp3SEqjRV6huWm737yCN6YnBqoiwI-U,1785
|
12
12
|
requestor/services/__init__.py,sha256=1qSn_6RMn0KB0A7LCnY2IW6_tC3HBQsdfkFeV-h94eM,172
|
13
13
|
requestor/services/database_service.py,sha256=GlSrzzzd7PSYQJNup00sxkB-B2PMr1__04K8k5QSWvs,2996
|
14
|
-
requestor/services/provider_service.py,sha256=
|
14
|
+
requestor/services/provider_service.py,sha256=auUc5XSHWbtOzyLHFqJ4RF337rCNYThzb7TjvUaK_uo,14542
|
15
15
|
requestor/services/ssh_service.py,sha256=tcOCtk2SlB9Uuv-P2ghR22e7BJ9kigQh5b4zSGdPFns,4280
|
16
|
-
requestor/services/vm_service.py,sha256=
|
16
|
+
requestor/services/vm_service.py,sha256=LhNBf9V5-m0ssPGikEM8R2K0vwwFDPQ8KIgCM6vX0XI,6817
|
17
17
|
requestor/ssh/__init__.py,sha256=hNgSqJ5s1_AwwxVRyFjUqh_LTBpI4Hmzq0F-f_wXN9g,119
|
18
|
-
requestor/ssh/manager.py,sha256=
|
18
|
+
requestor/ssh/manager.py,sha256=3jQtbbK7CVC2yD1zCO88jGXh2fBcuv3CzWEqDLuaQVk,9758
|
19
19
|
requestor/utils/logging.py,sha256=oFNpO8pJboYM8Wp7g3HOU4HFyBTKypVdY15lUiz1a4I,3721
|
20
20
|
requestor/utils/spinner.py,sha256=PUHJdTD9jpUHur__01_qxXy87WFfNmjQbD_sLG-KlGo,2459
|
21
|
-
request_vm_on_golem-0.1.
|
22
|
-
request_vm_on_golem-0.1.
|
23
|
-
request_vm_on_golem-0.1.
|
24
|
-
request_vm_on_golem-0.1.
|
21
|
+
request_vm_on_golem-0.1.40.dist-info/METADATA,sha256=eyg8EzOeDCRq_wZKZw1KgGN89HvxMhHYCfWr49SxyHc,9944
|
22
|
+
request_vm_on_golem-0.1.40.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
23
|
+
request_vm_on_golem-0.1.40.dist-info/entry_points.txt,sha256=Z-skRNpJ8aZcIl_En9mEm1ygkp9FKy0bzQoL3zO52-0,44
|
24
|
+
request_vm_on_golem-0.1.40.dist-info/RECORD,,
|
requestor/cli/commands.py
CHANGED
@@ -14,7 +14,28 @@ except ImportError:
|
|
14
14
|
# Python < 3.8
|
15
15
|
import importlib_metadata as metadata
|
16
16
|
|
17
|
-
from ..config import config
|
17
|
+
from ..config import config
|
18
|
+
|
19
|
+
# `ensure_config` is responsible for creating default configuration
|
20
|
+
# files and directories on first run. Some unit tests replace the
|
21
|
+
# entire `requestor.config` module with a lightweight stub that only
|
22
|
+
# provides a `config` object. Importing `ensure_config` in such
|
23
|
+
# scenarios would raise an ``ImportError`` which prevents the CLI
|
24
|
+
# module from being imported at all. To make the CLI resilient during
|
25
|
+
# tests we try to import ``ensure_config`` but fall back to a no-op
|
26
|
+
# when it isn't available.
|
27
|
+
try:
|
28
|
+
from ..config import ensure_config # type: ignore
|
29
|
+
except Exception: # pragma: no cover - used only when tests stub the module
|
30
|
+
def ensure_config() -> None:
|
31
|
+
"""Fallback ``ensure_config`` used in tests.
|
32
|
+
|
33
|
+
When the real configuration module is replaced with a stub the
|
34
|
+
CLI should still be importable. The stub simply does nothing
|
35
|
+
which is sufficient for the unit tests exercising the CLI
|
36
|
+
command mappings.
|
37
|
+
"""
|
38
|
+
pass
|
18
39
|
from ..provider.client import ProviderClient
|
19
40
|
from ..errors import RequestorError
|
20
41
|
from ..utils.logging import setup_logger
|
@@ -69,7 +90,7 @@ def vm():
|
|
69
90
|
@vm.command(name='providers')
|
70
91
|
@click.option('--cpu', type=int, help='Minimum CPU cores required')
|
71
92
|
@click.option('--memory', type=int, help='Minimum memory (GB) required')
|
72
|
-
@click.option('--storage', type=int, help='Minimum
|
93
|
+
@click.option('--storage', type=int, help='Minimum disk (GB) required')
|
73
94
|
@click.option('--country', help='Preferred provider country')
|
74
95
|
@click.option('--driver', type=click.Choice(['central', 'golem-base']), default=None, help='Discovery driver to use')
|
75
96
|
@click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
|
@@ -85,7 +106,7 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
85
106
|
if memory:
|
86
107
|
logger.detail(f"Memory: {memory}GB+")
|
87
108
|
if storage:
|
88
|
-
logger.detail(f"
|
109
|
+
logger.detail(f"Disk: {storage}GB+")
|
89
110
|
if country:
|
90
111
|
logger.detail(f"Country: {country}")
|
91
112
|
|
@@ -96,6 +117,9 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
96
117
|
# Initialize provider service
|
97
118
|
provider_service = ProviderService()
|
98
119
|
async with provider_service:
|
120
|
+
# If a full spec is provided, enable per-provider estimate display
|
121
|
+
if cpu and memory and storage:
|
122
|
+
provider_service.estimate_spec = (cpu, memory, storage)
|
99
123
|
providers = await provider_service.find_providers(
|
100
124
|
cpu=cpu,
|
101
125
|
memory=memory,
|
@@ -108,6 +132,12 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
108
132
|
logger.warning("No providers found matching criteria")
|
109
133
|
return {"providers": []}
|
110
134
|
|
135
|
+
# If JSON requested and full spec provided, include estimates per provider
|
136
|
+
if as_json and cpu and memory and storage:
|
137
|
+
for p in providers:
|
138
|
+
est = provider_service.compute_estimate(p, (cpu, memory, storage))
|
139
|
+
if est is not None:
|
140
|
+
p['estimate'] = est
|
111
141
|
result = {"providers": providers}
|
112
142
|
|
113
143
|
if as_json:
|
@@ -142,9 +172,10 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
142
172
|
@click.option('--provider-id', required=True, help='Provider ID to use')
|
143
173
|
@click.option('--cpu', type=int, required=True, help='Number of CPU cores')
|
144
174
|
@click.option('--memory', type=int, required=True, help='Memory in GB')
|
145
|
-
@click.option('--storage', type=int, required=True, help='
|
175
|
+
@click.option('--storage', type=int, required=True, help='Disk in GB')
|
176
|
+
@click.option('--yes', is_flag=True, help='Do not prompt for confirmation')
|
146
177
|
@async_command
|
147
|
-
async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage: int):
|
178
|
+
async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage: int, yes: bool):
|
148
179
|
"""Create a new VM on a specific provider."""
|
149
180
|
try:
|
150
181
|
# Show configuration details
|
@@ -152,7 +183,7 @@ async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage:
|
|
152
183
|
click.echo(click.style(" VM Configuration", fg="blue", bold=True))
|
153
184
|
click.echo("─" * 60)
|
154
185
|
click.echo(f" Provider : {click.style(provider_id, fg='cyan')}")
|
155
|
-
click.echo(f" Resources : {click.style(f'{cpu} CPU, {memory}GB RAM, {storage}GB
|
186
|
+
click.echo(f" Resources : {click.style(f'{cpu} CPU, {memory}GB RAM, {storage}GB Disk', fg='cyan')}")
|
156
187
|
click.echo("─" * 60 + "\n")
|
157
188
|
|
158
189
|
# Now start the deployment with spinner
|
@@ -170,6 +201,21 @@ async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage:
|
|
170
201
|
if not provider_ip and config.environment == "production":
|
171
202
|
raise RequestorError("Provider IP address not found in advertisement")
|
172
203
|
|
204
|
+
# Before proceeding, show estimated monthly price and confirm
|
205
|
+
provider_service.estimate_spec = (cpu, memory, storage)
|
206
|
+
est_row = await provider_service.format_provider_row(provider, colorize=False)
|
207
|
+
# Columns: ... [7]=USD/core/mo, [8]=USD/GB RAM/mo, [9]=USD/GB Disk/mo, [10]=Est. $/mo, [11]=Est. GLM/mo
|
208
|
+
est_usd = est_row[10]
|
209
|
+
est_glm = est_row[11]
|
210
|
+
price_str = f"~${est_usd}/mo" if est_usd != '—' else "(no USD pricing)"
|
211
|
+
if est_glm != '—':
|
212
|
+
price_str += f" (~{est_glm} GLM/mo)"
|
213
|
+
click.echo(click.style(f" 💵 Estimated Monthly Cost: {price_str}", fg='yellow', bold=True))
|
214
|
+
if not yes:
|
215
|
+
if not click.confirm("Proceed with VM creation?", default=True):
|
216
|
+
logger.warning("Creation cancelled by user")
|
217
|
+
return
|
218
|
+
|
173
219
|
# Setup SSH
|
174
220
|
ssh_service = SSHService(config.ssh_key_dir)
|
175
221
|
key_pair = await ssh_service.get_key_pair()
|
@@ -201,7 +247,7 @@ async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage:
|
|
201
247
|
click.echo(click.style(" VM Details", fg="blue", bold=True))
|
202
248
|
click.echo(" " + "┈" * 25)
|
203
249
|
click.echo(f" 🏷️ Name : {click.style(name, fg='cyan')}")
|
204
|
-
click.echo(f" 💻 Resources : {click.style(f'{cpu} CPU, {memory}GB RAM, {storage}GB
|
250
|
+
click.echo(f" 💻 Resources : {click.style(f'{cpu} CPU, {memory}GB RAM, {storage}GB Disk', fg='cyan')}")
|
205
251
|
click.echo(f" 🟢 Status : {click.style('running', fg='green')}")
|
206
252
|
|
207
253
|
# Connection Details Section
|
@@ -320,7 +366,7 @@ async def info_vm(name: str, as_json: bool):
|
|
320
366
|
"SSH Port",
|
321
367
|
"CPU",
|
322
368
|
"Memory (GB)",
|
323
|
-
"
|
369
|
+
"Disk (GB)",
|
324
370
|
]
|
325
371
|
|
326
372
|
row = [
|
@@ -15,6 +15,39 @@ class ProviderService:
|
|
15
15
|
def __init__(self):
|
16
16
|
self.session = None
|
17
17
|
self.golem_base_client = None
|
18
|
+
# Optional spec (cpu, memory, storage) to compute estimates for display
|
19
|
+
self.estimate_spec: Optional[tuple[int, int, int]] = None
|
20
|
+
|
21
|
+
def compute_estimate(self, provider: Dict, spec: tuple[int, int, int]) -> Optional[Dict]:
|
22
|
+
"""Compute estimated pricing for a given spec, if provider has pricing.
|
23
|
+
|
24
|
+
Returns dict with usd_per_month, glm_per_month (if GLM per-unit available),
|
25
|
+
and usd_per_hour, or None if insufficient pricing data.
|
26
|
+
"""
|
27
|
+
pricing = provider.get('pricing') or {}
|
28
|
+
usd_core = pricing.get('usd_per_core_month')
|
29
|
+
usd_ram = pricing.get('usd_per_gb_ram_month')
|
30
|
+
usd_storage = pricing.get('usd_per_gb_storage_month')
|
31
|
+
if usd_core is None or usd_ram is None or usd_storage is None:
|
32
|
+
return None
|
33
|
+
cpu, mem, sto = spec
|
34
|
+
try:
|
35
|
+
usd_per_month = float(usd_core) * cpu + float(usd_ram) * mem + float(usd_storage) * sto
|
36
|
+
glm_core = pricing.get('glm_per_core_month')
|
37
|
+
glm_ram = pricing.get('glm_per_gb_ram_month')
|
38
|
+
glm_storage = pricing.get('glm_per_gb_storage_month')
|
39
|
+
glm_per_month = None
|
40
|
+
if glm_core is not None and glm_ram is not None and glm_storage is not None:
|
41
|
+
glm_per_month = float(glm_core) * cpu + float(glm_ram) * mem + float(glm_storage) * sto
|
42
|
+
usd_per_hour = usd_per_month / 730.0
|
43
|
+
# Round for display consistency
|
44
|
+
return {
|
45
|
+
'usd_per_month': round(usd_per_month, 4),
|
46
|
+
'usd_per_hour': round(usd_per_hour, 6),
|
47
|
+
'glm_per_month': round(glm_per_month, 8) if glm_per_month is not None else None,
|
48
|
+
}
|
49
|
+
except Exception:
|
50
|
+
return None
|
18
51
|
|
19
52
|
async def __aenter__(self):
|
20
53
|
self.session = aiohttp.ClientSession()
|
@@ -91,6 +124,14 @@ class ProviderService:
|
|
91
124
|
'memory': int(annotations.get('golem_memory', 0)),
|
92
125
|
'storage': int(annotations.get('golem_storage', 0)),
|
93
126
|
},
|
127
|
+
'pricing': {
|
128
|
+
'usd_per_core_month': annotations.get('golem_price_usd_core_month'),
|
129
|
+
'usd_per_gb_ram_month': annotations.get('golem_price_usd_ram_gb_month'),
|
130
|
+
'usd_per_gb_storage_month': annotations.get('golem_price_usd_storage_gb_month'),
|
131
|
+
'glm_per_core_month': annotations.get('golem_price_glm_core_month'),
|
132
|
+
'glm_per_gb_ram_month': annotations.get('golem_price_glm_ram_gb_month'),
|
133
|
+
'glm_per_gb_storage_month': annotations.get('golem_price_glm_storage_gb_month'),
|
134
|
+
},
|
94
135
|
'created_at_block': metadata.expires_at_block - (config.advertisement_interval * 2)
|
95
136
|
}
|
96
137
|
if provider['provider_id']:
|
@@ -225,6 +266,31 @@ class ProviderService:
|
|
225
266
|
|
226
267
|
updated_at_str = await self._format_block_timestamp(provider.get('created_at_block', 0))
|
227
268
|
|
269
|
+
pricing = provider.get('pricing') or {}
|
270
|
+
usd_core = pricing.get('usd_per_core_month')
|
271
|
+
usd_ram = pricing.get('usd_per_gb_ram_month')
|
272
|
+
usd_storage = pricing.get('usd_per_gb_storage_month')
|
273
|
+
|
274
|
+
# Precompute estimates if a spec is set and pricing available
|
275
|
+
est_usd = '—'
|
276
|
+
est_glm = '—'
|
277
|
+
est_hr_usd = '—'
|
278
|
+
if self.estimate_spec and all(p is not None for p in (usd_core, usd_ram, usd_storage)):
|
279
|
+
spec_cpu, spec_mem, spec_sto = self.estimate_spec
|
280
|
+
try:
|
281
|
+
est_usd_val = (float(usd_core) * spec_cpu) + (float(usd_ram) * spec_mem) + (float(usd_storage) * spec_sto)
|
282
|
+
est_usd = round(est_usd_val, 4)
|
283
|
+
est_hr_usd = round(est_usd_val / 730.0, 6)
|
284
|
+
# If GLM per-unit is present, compute GLM estimate as well
|
285
|
+
glm_core = pricing.get('glm_per_core_month')
|
286
|
+
glm_ram = pricing.get('glm_per_gb_ram_month')
|
287
|
+
glm_storage = pricing.get('glm_per_gb_storage_month')
|
288
|
+
if all(x is not None for x in (glm_core, glm_ram, glm_storage)):
|
289
|
+
est_glm_val = (float(glm_core) * spec_cpu) + (float(glm_ram) * spec_mem) + (float(glm_storage) * spec_sto)
|
290
|
+
est_glm = round(est_glm_val, 8)
|
291
|
+
except Exception:
|
292
|
+
pass
|
293
|
+
|
228
294
|
row = [
|
229
295
|
provider['provider_id'],
|
230
296
|
provider['provider_name'],
|
@@ -233,18 +299,38 @@ class ProviderService:
|
|
233
299
|
provider['resources']['cpu'],
|
234
300
|
provider['resources']['memory'],
|
235
301
|
provider['resources']['storage'],
|
302
|
+
usd_core if usd_core is not None else '—',
|
303
|
+
usd_ram if usd_ram is not None else '—',
|
304
|
+
usd_storage if usd_storage is not None else '—',
|
305
|
+
est_usd,
|
306
|
+
est_glm,
|
236
307
|
updated_at_str
|
237
308
|
]
|
238
309
|
|
239
310
|
if colorize:
|
240
311
|
# Format Provider ID
|
241
|
-
|
312
|
+
id_txt = style(row[0], fg="yellow")
|
313
|
+
if est_hr_usd != '—':
|
314
|
+
id_txt += style(f" (~${est_hr_usd}/hr)", fg="yellow")
|
315
|
+
row[0] = id_txt
|
242
316
|
|
243
317
|
# Format resources with icons and colors
|
244
318
|
row[4] = style(f"💻 {row[4]}", fg="cyan", bold=True)
|
245
319
|
row[5] = style(f"🧠 {row[5]}", fg="cyan", bold=True)
|
246
320
|
row[6] = style(f"💾 {row[6]}", fg="cyan", bold=True)
|
247
321
|
|
322
|
+
# Format pricing with currency markers
|
323
|
+
if usd_core != '—':
|
324
|
+
row[7] = style(f"${row[7]}/mo", fg="magenta")
|
325
|
+
if usd_ram != '—':
|
326
|
+
row[8] = style(f"${row[8]}/GB/mo", fg="magenta")
|
327
|
+
if usd_storage != '—':
|
328
|
+
row[9] = style(f"${row[9]}/GB/mo", fg="magenta")
|
329
|
+
if est_usd != '—':
|
330
|
+
row[10] = style(f"~${row[10]}/mo", fg="yellow", bold=True)
|
331
|
+
if est_glm != '—':
|
332
|
+
row[11] = style(f"~{row[11]} GLM/mo", fg="yellow")
|
333
|
+
|
248
334
|
# Format location info
|
249
335
|
row[3] = style(f"🌍 {row[3]}", fg="green", bold=True)
|
250
336
|
|
@@ -260,6 +346,11 @@ class ProviderService:
|
|
260
346
|
"Country",
|
261
347
|
"CPU",
|
262
348
|
"Memory (GB)",
|
263
|
-
"
|
349
|
+
"Disk (GB)",
|
350
|
+
"USD/core/mo",
|
351
|
+
"USD/GB RAM/mo",
|
352
|
+
"USD/GB Disk/mo",
|
353
|
+
"Est. $/mo",
|
354
|
+
"Est. GLM/mo",
|
264
355
|
"Updated"
|
265
356
|
]
|
requestor/services/vm_service.py
CHANGED
requestor/ssh/manager.py
CHANGED
@@ -53,9 +53,17 @@ class SSHKeyManager:
|
|
53
53
|
|
54
54
|
# Create Golem directory if needed
|
55
55
|
self.golem_dir.mkdir(parents=True, exist_ok=True)
|
56
|
-
# Secure directory permissions (on Unix-like systems)
|
56
|
+
# Secure directory permissions (on Unix-like systems). If the directory
|
57
|
+
# is a system path (e.g., "/tmp") or not owned/permission-changeable
|
58
|
+
# by the current user, ignore the error to avoid test and runtime failures.
|
57
59
|
if os.name == 'posix':
|
58
|
-
|
60
|
+
try:
|
61
|
+
os.chmod(self.golem_dir, 0o700)
|
62
|
+
except PermissionError:
|
63
|
+
logger.warning(
|
64
|
+
"Could not set permissions on %s; continuing without chmod",
|
65
|
+
self.golem_dir,
|
66
|
+
)
|
59
67
|
|
60
68
|
async def get_key_pair(self) -> KeyPair:
|
61
69
|
"""Get the SSH key pair to use.
|
File without changes
|
{request_vm_on_golem-0.1.38.dist-info → request_vm_on_golem-0.1.40.dist-info}/entry_points.txt
RENAMED
File without changes
|