request-vm-on-golem 0.1.53__tar.gz → 0.1.54__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.
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/PKG-INFO +1 -1
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/pyproject.toml +1 -1
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/cli/commands.py +53 -2
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/config.py +3 -1
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/run.py +10 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/utils/logging.py +15 -4
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/utils/spinner.py +12 -11
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/README.md +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/api/main.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/cli/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/data/deployments/l2.json +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/db/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/db/sqlite.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/errors.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/payments/blockchain_service.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/payments/monitor.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/provider/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/provider/client.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/security/faucet.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/database_service.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/provider_service.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/ssh_service.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/vm_service.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/ssh/__init__.py +0 -0
- {request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/ssh/manager.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "request-vm-on-golem"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.54"
|
4
4
|
description = "VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network"
|
5
5
|
authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
|
6
6
|
readme = "README.md"
|
@@ -138,6 +138,8 @@ def vm():
|
|
138
138
|
async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str], payments_network: Optional[str] = None, all_payments: bool = False, as_json: bool = False, network: Optional[str] = None):
|
139
139
|
"""List available providers matching requirements."""
|
140
140
|
try:
|
141
|
+
if as_json:
|
142
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
141
143
|
if network:
|
142
144
|
config.network = network
|
143
145
|
# Log search criteria if any
|
@@ -181,7 +183,10 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
181
183
|
|
182
184
|
if not providers:
|
183
185
|
logger.warning("No providers found matching criteria")
|
184
|
-
|
186
|
+
result = {"providers": []}
|
187
|
+
if as_json:
|
188
|
+
click.echo(json.dumps(result, indent=2))
|
189
|
+
return result
|
185
190
|
|
186
191
|
# If JSON requested and full spec provided, include estimates per provider
|
187
192
|
if as_json and cpu and memory and storage:
|
@@ -216,6 +221,12 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
216
221
|
except Exception as e:
|
217
222
|
logger.error(f"Failed to list providers: {str(e)}")
|
218
223
|
raise click.Abort()
|
224
|
+
finally:
|
225
|
+
if as_json:
|
226
|
+
try:
|
227
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
228
|
+
except Exception:
|
229
|
+
pass
|
219
230
|
|
220
231
|
|
221
232
|
@vm.command(name='create')
|
@@ -385,6 +396,8 @@ def vm_stream():
|
|
385
396
|
async def stream_list(as_json: bool):
|
386
397
|
"""List payment stream status for all known VMs."""
|
387
398
|
try:
|
399
|
+
if as_json:
|
400
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
388
401
|
vms = await db_service.list_vms()
|
389
402
|
if not vms:
|
390
403
|
logger.warning("No VMs found in local database")
|
@@ -461,6 +474,12 @@ async def stream_list(as_json: bool):
|
|
461
474
|
except Exception as e:
|
462
475
|
logger.error(f"Failed to list streams: {e}")
|
463
476
|
raise click.Abort()
|
477
|
+
finally:
|
478
|
+
if as_json:
|
479
|
+
try:
|
480
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
481
|
+
except Exception:
|
482
|
+
pass
|
464
483
|
|
465
484
|
|
466
485
|
@vm_stream.command('open')
|
@@ -550,6 +569,8 @@ async def stream_topup(stream_id: int, glm: float | None, hours: int | None):
|
|
550
569
|
async def stream_status(name: str, as_json: bool):
|
551
570
|
"""Show the payment stream status for a VM by name."""
|
552
571
|
try:
|
572
|
+
if as_json:
|
573
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
553
574
|
# Resolve VM and provider
|
554
575
|
vm = await db_service.get_vm(name)
|
555
576
|
if not vm:
|
@@ -580,6 +601,12 @@ async def stream_status(name: str, as_json: bool):
|
|
580
601
|
except Exception as e:
|
581
602
|
logger.error(f"Failed to fetch stream status: {e}")
|
582
603
|
raise click.Abort()
|
604
|
+
finally:
|
605
|
+
if as_json:
|
606
|
+
try:
|
607
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
608
|
+
except Exception:
|
609
|
+
pass
|
583
610
|
|
584
611
|
|
585
612
|
@vm_stream.command('inspect')
|
@@ -589,6 +616,8 @@ async def stream_status(name: str, as_json: bool):
|
|
589
616
|
async def stream_inspect(stream_id: int, as_json: bool):
|
590
617
|
"""Inspect a stream directly on-chain (no provider required)."""
|
591
618
|
try:
|
619
|
+
if as_json:
|
620
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
592
621
|
from web3 import Web3
|
593
622
|
from golem_streaming_abi import STREAM_PAYMENT_ABI
|
594
623
|
w3 = Web3(Web3.HTTPProvider(config.polygon_rpc_url))
|
@@ -632,6 +661,12 @@ async def stream_inspect(stream_id: int, as_json: bool):
|
|
632
661
|
except Exception as e:
|
633
662
|
logger.error(f"Failed to inspect stream: {e}")
|
634
663
|
raise click.Abort()
|
664
|
+
finally:
|
665
|
+
if as_json:
|
666
|
+
try:
|
667
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
668
|
+
except Exception:
|
669
|
+
pass
|
635
670
|
|
636
671
|
|
637
672
|
@cli.group()
|
@@ -726,6 +761,8 @@ def connect_vm(name: str):
|
|
726
761
|
async def info_vm(name: str, as_json: bool):
|
727
762
|
"""Show information about a VM."""
|
728
763
|
try:
|
764
|
+
if as_json:
|
765
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
729
766
|
logger.command(f"ℹ️ Getting info for VM '{name}'")
|
730
767
|
|
731
768
|
# Initialize VM service
|
@@ -767,6 +804,12 @@ async def info_vm(name: str, as_json: bool):
|
|
767
804
|
except Exception as e:
|
768
805
|
logger.error(f"Failed to get VM info: {str(e)}")
|
769
806
|
raise click.Abort()
|
807
|
+
finally:
|
808
|
+
if as_json:
|
809
|
+
try:
|
810
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
811
|
+
except Exception:
|
812
|
+
pass
|
770
813
|
|
771
814
|
|
772
815
|
@vm.command(name='destroy')
|
@@ -1054,6 +1097,8 @@ def run_api_server(host: str, port: int, reload: bool):
|
|
1054
1097
|
async def list_vms(as_json: bool):
|
1055
1098
|
"""List all VMs."""
|
1056
1099
|
try:
|
1100
|
+
if as_json:
|
1101
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
1057
1102
|
logger.command("📋 Listing your VMs")
|
1058
1103
|
logger.process("Fetching VM details")
|
1059
1104
|
|
@@ -1086,7 +1131,6 @@ async def list_vms(as_json: bool):
|
|
1086
1131
|
tablefmt="grid"
|
1087
1132
|
))
|
1088
1133
|
click.echo("\n" + "─" * 60)
|
1089
|
-
|
1090
1134
|
return result
|
1091
1135
|
|
1092
1136
|
except Exception as e:
|
@@ -1096,6 +1140,13 @@ async def list_vms(as_json: bool):
|
|
1096
1140
|
logger.error(f"Failed to list VMs: {error_msg}")
|
1097
1141
|
raise click.Abort()
|
1098
1142
|
|
1143
|
+
finally:
|
1144
|
+
if as_json:
|
1145
|
+
try:
|
1146
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
1147
|
+
except Exception:
|
1148
|
+
pass
|
1149
|
+
|
1099
1150
|
|
1100
1151
|
def main():
|
1101
1152
|
"""Entry point for the CLI."""
|
@@ -4,6 +4,7 @@ import os
|
|
4
4
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
5
5
|
from pydantic import Field, field_validator, ValidationInfo
|
6
6
|
import os
|
7
|
+
import sys
|
7
8
|
|
8
9
|
|
9
10
|
def ensure_config() -> None:
|
@@ -33,7 +34,8 @@ def ensure_config() -> None:
|
|
33
34
|
created = True
|
34
35
|
|
35
36
|
if created:
|
36
|
-
|
37
|
+
# Write to stderr so stdout stays clean for JSON outputs
|
38
|
+
print("Using default settings – run with --help to customize", file=sys.stderr)
|
37
39
|
|
38
40
|
|
39
41
|
ensure_config()
|
@@ -4,6 +4,16 @@ import sys
|
|
4
4
|
from pathlib import Path
|
5
5
|
from dotenv import load_dotenv
|
6
6
|
|
7
|
+
if "--json" in sys.argv:
|
8
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
9
|
+
try:
|
10
|
+
import logging as _logging
|
11
|
+
_logging.getLogger().setLevel(_logging.CRITICAL)
|
12
|
+
_logging.getLogger('rlp').setLevel(_logging.CRITICAL)
|
13
|
+
_logging.getLogger('rlp.codec').setLevel(_logging.CRITICAL)
|
14
|
+
except Exception:
|
15
|
+
pass
|
16
|
+
|
7
17
|
from requestor.utils.logging import setup_logger
|
8
18
|
|
9
19
|
# Configure logging with debug mode from environment variable
|
@@ -61,6 +61,8 @@ def setup_logger(name: Optional[str] = None) -> logging.Logger:
|
|
61
61
|
|
62
62
|
# Check DEBUG environment variable
|
63
63
|
debug = os.getenv('DEBUG', '').lower() in ('1', 'true', 'yes')
|
64
|
+
# Global silence switch for JSON/machine outputs
|
65
|
+
silence = os.getenv('GOLEM_SILENCE_LOGS', '').lower() in ('1', 'true', 'yes')
|
64
66
|
|
65
67
|
# Prevent duplicate logs by removing root handlers
|
66
68
|
root = logging.getLogger()
|
@@ -86,13 +88,22 @@ def setup_logger(name: Optional[str] = None) -> logging.Logger:
|
|
86
88
|
style='%'
|
87
89
|
)
|
88
90
|
fancy_handler.setFormatter(fancy_formatter)
|
89
|
-
|
90
|
-
|
91
|
-
|
91
|
+
# Suppress DEBUG unless DEBUG=1; suppress everything if silence
|
92
|
+
def _filter(record: logging.LogRecord) -> bool:
|
93
|
+
if silence:
|
94
|
+
return False
|
95
|
+
return (record.levelno != DEBUG) or debug
|
96
|
+
fancy_handler.addFilter(_filter)
|
92
97
|
logger.addHandler(fancy_handler)
|
93
98
|
logger.propagate = False # Prevent propagation to avoid duplicates
|
94
99
|
|
95
|
-
if
|
100
|
+
if silence:
|
101
|
+
logger.setLevel(CRITICAL)
|
102
|
+
# Silence common libraries and root logger
|
103
|
+
logging.getLogger().setLevel(CRITICAL)
|
104
|
+
logging.getLogger('asyncio').setLevel(CRITICAL)
|
105
|
+
logging.getLogger('aiosqlite').setLevel(CRITICAL)
|
106
|
+
elif debug:
|
96
107
|
logger.setLevel(DEBUG)
|
97
108
|
# Enable debug logging for other libraries
|
98
109
|
logging.getLogger('asyncio').setLevel(DEBUG)
|
@@ -11,26 +11,27 @@ class Spinner:
|
|
11
11
|
self.busy = False
|
12
12
|
self.spinner_visible = False
|
13
13
|
self.message = message
|
14
|
-
|
14
|
+
# Use stderr so stdout can remain machine-readable (e.g., --json outputs)
|
15
|
+
sys.stderr.write('\033[?25l') # Hide cursor
|
15
16
|
|
16
17
|
def write_next(self):
|
17
18
|
"""Write the next spinner frame."""
|
18
19
|
with self._screen_lock:
|
19
20
|
if not self.spinner_visible:
|
20
|
-
sys.
|
21
|
+
sys.stderr.write(f"\r{next(self.spinner)} {self.message}")
|
21
22
|
self.spinner_visible = True
|
22
|
-
sys.
|
23
|
+
sys.stderr.flush()
|
23
24
|
|
24
25
|
def remove_spinner(self, cleanup=False):
|
25
26
|
"""Remove the spinner from the terminal."""
|
26
27
|
with self._screen_lock:
|
27
28
|
if self.spinner_visible:
|
28
|
-
sys.
|
29
|
-
sys.
|
30
|
-
sys.
|
29
|
+
sys.stderr.write('\r')
|
30
|
+
sys.stderr.write(' ' * (len(self.message) + 2))
|
31
|
+
sys.stderr.write('\r')
|
31
32
|
if cleanup:
|
32
|
-
sys.
|
33
|
-
sys.
|
33
|
+
sys.stderr.write('\033[?25h') # Show cursor
|
34
|
+
sys.stderr.flush()
|
34
35
|
self.spinner_visible = False
|
35
36
|
|
36
37
|
def spinner_task(self):
|
@@ -56,11 +57,11 @@ class Spinner:
|
|
56
57
|
self.remove_spinner(cleanup=True)
|
57
58
|
if exc_type is None:
|
58
59
|
# Show checkmark on success
|
59
|
-
sys.
|
60
|
+
sys.stderr.write(f"\r✓ {self.message}\n")
|
60
61
|
else:
|
61
62
|
# Show X on failure
|
62
|
-
sys.
|
63
|
-
sys.
|
63
|
+
sys.stderr.write(f"\r✗ {self.message}\n")
|
64
|
+
sys.stderr.flush()
|
64
65
|
|
65
66
|
def step(message):
|
66
67
|
"""Decorator to add a spinning progress indicator to a function."""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/data/deployments/l2.json
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/payments/blockchain_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/database_service.py
RENAMED
File without changes
|
{request_vm_on_golem-0.1.53 → request_vm_on_golem-0.1.54}/requestor/services/provider_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|