request-vm-on-golem 0.1.34__tar.gz → 0.1.36__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 (23) hide show
  1. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/PKG-INFO +1 -1
  2. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/pyproject.toml +1 -1
  3. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/cli/commands.py +103 -36
  4. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/run.py +21 -13
  5. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/README.md +0 -0
  6. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/__init__.py +0 -0
  7. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/api/main.py +0 -0
  8. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/cli/__init__.py +0 -0
  9. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/config.py +0 -0
  10. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/db/__init__.py +0 -0
  11. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/db/sqlite.py +0 -0
  12. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/errors.py +0 -0
  13. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/provider/__init__.py +0 -0
  14. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/provider/client.py +0 -0
  15. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/services/__init__.py +0 -0
  16. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/services/database_service.py +0 -0
  17. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/services/provider_service.py +0 -0
  18. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/services/ssh_service.py +0 -0
  19. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/services/vm_service.py +0 -0
  20. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/ssh/__init__.py +0 -0
  21. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/ssh/manager.py +0 -0
  22. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/utils/logging.py +0 -0
  23. {request_vm_on_golem-0.1.34 → request_vm_on_golem-0.1.36}/requestor/utils/spinner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: request-vm-on-golem
3
- Version: 0.1.34
3
+ Version: 0.1.36
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "request-vm-on-golem"
3
- version = "0.1.34"
3
+ version = "0.1.36"
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"
@@ -1,6 +1,7 @@
1
1
  """CLI interface for VM on Golem."""
2
2
  import click
3
3
  import asyncio
4
+ import json
4
5
  from typing import Optional
5
6
  from pathlib import Path
6
7
  import subprocess
@@ -70,8 +71,9 @@ def vm():
70
71
  @click.option('--storage', type=int, help='Minimum storage (GB) required')
71
72
  @click.option('--country', help='Preferred provider country')
72
73
  @click.option('--driver', type=click.Choice(['central', 'golem-base']), default=None, help='Discovery driver to use')
74
+ @click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
73
75
  @async_command
74
- async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str]):
76
+ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str], as_json: bool):
75
77
  """List available providers matching requirements."""
76
78
  try:
77
79
  # Log search criteria if any
@@ -85,11 +87,11 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
85
87
  logger.detail(f"Storage: {storage}GB+")
86
88
  if country:
87
89
  logger.detail(f"Country: {country}")
88
-
90
+
89
91
  # Determine the discovery driver being used
90
92
  discovery_driver = driver or config.discovery_driver
91
93
  logger.process(f"Querying discovery service via {discovery_driver}")
92
-
94
+
93
95
  # Initialize provider service
94
96
  provider_service = ProviderService()
95
97
  async with provider_service:
@@ -103,24 +105,31 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
103
105
 
104
106
  if not providers:
105
107
  logger.warning("No providers found matching criteria")
106
- return
108
+ return {"providers": []}
107
109
 
108
- # Format provider information using service with colors
109
- headers = provider_service.provider_headers
110
- rows = await asyncio.gather(*(provider_service.format_provider_row(p, colorize=True) for p in providers))
110
+ result = {"providers": providers}
111
111
 
112
- # Show fancy header
113
- click.echo("\n" + "─" * 80)
114
- click.echo(click.style(f" 🌍 Available Providers ({len(providers)} total)", fg="blue", bold=True))
115
- click.echo("─" * 80)
112
+ if as_json:
113
+ click.echo(json.dumps(result, indent=2))
114
+ else:
115
+ # Format provider information using service with colors
116
+ headers = provider_service.provider_headers
117
+ rows = await asyncio.gather(*(provider_service.format_provider_row(p, colorize=True) for p in providers))
116
118
 
117
- # Show table with colored headers
118
- click.echo("\n" + tabulate(
119
- rows,
120
- headers=[click.style(h, bold=True) for h in headers],
121
- tablefmt="grid"
122
- ))
123
- click.echo("\n" + "─" * 80)
119
+ # Show fancy header
120
+ click.echo("\n" + "─" * 80)
121
+ click.echo(click.style(f" 🌍 Available Providers ({len(providers)} total)", fg="blue", bold=True))
122
+ click.echo("─" * 80)
123
+
124
+ # Show table with colored headers
125
+ click.echo("\n" + tabulate(
126
+ rows,
127
+ headers=[click.style(h, bold=True) for h in headers],
128
+ tablefmt="grid"
129
+ ))
130
+ click.echo("\n" + "─" * 80)
131
+
132
+ return result
124
133
 
125
134
  except Exception as e:
126
135
  logger.error(f"Failed to list providers: {str(e)}")
@@ -274,6 +283,56 @@ async def ssh_vm(name: str):
274
283
  raise click.Abort()
275
284
 
276
285
 
286
+ @vm.command(name='info')
287
+ @click.argument('name')
288
+ @click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
289
+ @async_command
290
+ async def info_vm(name: str, as_json: bool):
291
+ """Show information about a VM."""
292
+ try:
293
+ logger.command(f"ℹ️ Getting info for VM '{name}'")
294
+
295
+ # Initialize VM service
296
+ ssh_service = SSHService(config.ssh_key_dir)
297
+ vm_service = VMService(db_service, ssh_service)
298
+
299
+ # Retrieve VM details
300
+ vm = await vm_service.get_vm(name)
301
+ if not vm:
302
+ raise click.BadParameter(f"VM '{name}' not found")
303
+
304
+ result = vm
305
+
306
+ if as_json:
307
+ click.echo(json.dumps(result, indent=2))
308
+ else:
309
+ headers = [
310
+ "Status",
311
+ "IP Address",
312
+ "SSH Port",
313
+ "CPU",
314
+ "Memory (GB)",
315
+ "Storage (GB)",
316
+ ]
317
+
318
+ row = [
319
+ vm.get("status", "unknown"),
320
+ vm["provider_ip"],
321
+ vm["config"].get("ssh_port", "N/A"),
322
+ vm["config"]["cpu"],
323
+ vm["config"]["memory"],
324
+ vm["config"]["storage"],
325
+ ]
326
+
327
+ click.echo("\n" + tabulate([row], headers=headers, tablefmt="grid"))
328
+
329
+ return result
330
+
331
+ except Exception as e:
332
+ logger.error(f"Failed to get VM info: {str(e)}")
333
+ raise click.Abort()
334
+
335
+
277
336
  @vm.command(name='destroy')
278
337
  @click.argument('name')
279
338
  @async_command
@@ -520,37 +579,45 @@ def run_api_server(host: str, port: int, reload: bool):
520
579
 
521
580
 
522
581
  @vm.command(name='list')
582
+ @click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
523
583
  @async_command
524
- async def list_vms():
584
+ async def list_vms(as_json: bool):
525
585
  """List all VMs."""
526
586
  try:
527
587
  logger.command("📋 Listing your VMs")
528
588
  logger.process("Fetching VM details")
529
-
589
+
530
590
  # Initialize VM service with temporary client (not needed for listing)
531
591
  ssh_service = SSHService(config.ssh_key_dir)
532
592
  vm_service = VMService(db_service, ssh_service, None)
533
593
  vms = await vm_service.list_vms()
534
594
  if not vms:
535
595
  logger.warning("No VMs found")
536
- return
596
+ return {"vms": []}
537
597
 
538
- # Format VM information using service
539
- headers = vm_service.vm_headers
540
- rows = [vm_service.format_vm_row(vm, colorize=True) for vm in vms]
598
+ result = {"vms": vms}
541
599
 
542
- # Show fancy header
543
- click.echo("\n" + "─" * 60)
544
- click.echo(click.style(f" 📋 Your VMs ({len(vms)} total)", fg="blue", bold=True))
545
- click.echo("─" * 60)
546
-
547
- # Show table with colored status
548
- click.echo("\n" + tabulate(
549
- rows,
550
- headers=[click.style(h, bold=True) for h in headers],
551
- tablefmt="grid"
552
- ))
553
- click.echo("\n" + "─" * 60)
600
+ if as_json:
601
+ click.echo(json.dumps(result, indent=2))
602
+ else:
603
+ # Format VM information using service
604
+ headers = vm_service.vm_headers
605
+ rows = [vm_service.format_vm_row(vm, colorize=True) for vm in vms]
606
+
607
+ # Show fancy header
608
+ click.echo("\n" + "─" * 60)
609
+ click.echo(click.style(f" 📋 Your VMs ({len(vms)} total)", fg="blue", bold=True))
610
+ click.echo("─" * 60)
611
+
612
+ # Show table with colored status
613
+ click.echo("\n" + tabulate(
614
+ rows,
615
+ headers=[click.style(h, bold=True) for h in headers],
616
+ tablefmt="grid"
617
+ ))
618
+ click.echo("\n" + "─" * 60)
619
+
620
+ return result
554
621
 
555
622
  except Exception as e:
556
623
  error_msg = str(e)
@@ -5,30 +5,36 @@ from pathlib import Path
5
5
  from dotenv import load_dotenv
6
6
 
7
7
  from requestor.utils.logging import setup_logger
8
- from requestor.cli.commands import cli
9
8
 
10
9
  # Configure logging with debug mode from environment variable
11
10
  logger = setup_logger(__name__)
12
11
 
13
- def check_requirements():
14
- """Check if all requirements are met."""
15
- # Check required directories
16
- ssh_key_dir = os.environ.get(
17
- 'GOLEM_REQUESTOR_SSH_KEY_DIR',
18
- str(Path.home() / '.golem' / 'requestor' / 'ssh')
12
+
13
+ def get_ssh_key_dir() -> Path:
14
+ """Return the path to the SSH key directory."""
15
+ return Path(
16
+ os.environ.get(
17
+ "GOLEM_REQUESTOR_SSH_KEY_DIR",
18
+ str(Path.home() / ".golem" / "requestor" / "ssh"),
19
+ )
19
20
  )
20
-
21
+
22
+
23
+ def secure_directory(path: Path) -> bool:
24
+ """Create the directory if needed and set strict permissions."""
21
25
  try:
22
- # Create and secure directories
23
- path = Path(ssh_key_dir)
24
26
  path.mkdir(parents=True, exist_ok=True)
25
- path.chmod(0o700) # Secure permissions for SSH keys
26
- except Exception as e:
27
+ path.chmod(0o700)
28
+ except Exception as e: # pragma: no cover - OS-related failures
27
29
  logger.error(f"Failed to create required directories: {e}")
28
30
  return False
29
-
30
31
  return True
31
32
 
33
+
34
+ def check_requirements() -> bool:
35
+ """Check if all requirements are met."""
36
+ return secure_directory(get_ssh_key_dir())
37
+
32
38
  def main():
33
39
  """Run the requestor CLI."""
34
40
  try:
@@ -44,6 +50,8 @@ def main():
44
50
  sys.exit(1)
45
51
 
46
52
  # Run CLI
53
+ from requestor.cli.commands import cli
54
+
47
55
  cli()
48
56
  except Exception as e:
49
57
  logger.error(f"Failed to start requestor CLI: {e}")