Complete menus overhaul

This commit is contained in:
OVERLORD7F
2025-05-21 14:12:37 +03:00
parent d7477da9b6
commit 291e9cfe75
6 changed files with 129 additions and 90 deletions

View File

@@ -1,30 +1,37 @@
#from main import base_url , api_key , requests #from main import base_url , api_key , requests
import requests import requests
import os import os
from rich.prompt import Prompt from rich.prompt import Prompt
from rich.console import Console
from rich.panel import Panel
from rich.align import Align
def cluster_info(base_url, api_key): # output short clusters overview def cluster_info(base_url, api_key): # output short clusters overview
url = f"http://{base_url}/api/clusters" url = f"http://{base_url}/api/clusters"
response = requests.get(url, headers={'Authorization': api_key}) response = requests.get(url, headers={'Authorization': api_key})
console = Console()
if response.status_code == 200: if response.status_code == 200:
cluster_info = response.json() cluster_info = response.json()
results_cluster_info = cluster_info['results'] results_cluster_info = cluster_info['results']
os.system('cls' if os.name == 'nt' else 'clear')
print("\nShort clusters overview:") console.rule("[bold cyan]Short Clusters Overview")
print(f"\nClusters total: {cluster_info['count']}") console.print(f"[bold]Clusters total:[/] {cluster_info['count']}\n")
print("-" * 51) panels = []
for x in results_cluster_info: for x in results_cluster_info:
panel_content = (
print(f"\nCluster Name: {x['verbose_name']} ({x['status']})") f"[bold]Nodes:[/] {x['nodes_count']}\n"
print(f"Nodes: {x['nodes_count']}") f"[bold]Total CPU:[/] {x['cpu_count']} Cores / [bold]CPU Usage:[/] {round(x['cpu_used_percent_user'], 2)}%\n"
print(f"Total CPU: {x['cpu_count']} Cores || CPU Usage: {round(x['cpu_used_percent_user'] , 2)}%")#output is rounded by 2 f"[bold]Total RAM:[/] {int(x['memory_count']/1024)} GB / [bold]RAM Usage:[/] {round(x['mem_used_percent_user'], 2)}%"
print(f"Total RAM: {int(x['memory_count']/1024)}GB || RAM Usage: {round(x['mem_used_percent_user'] , 2)}%") #RAM pretty output = mb-to-gb + set 'int' to remove .0 )
panel = Panel(
print("-" * 51) Align.left(panel_content),
title=f"[bold gold3]{x['verbose_name']}[/] [red]({x['status']})[/]",
else: border_style="magenta",
print(f"Failed to retrieve data {response.status_code}") expand=False
Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") )
panels.append(panel)
console.print(*panels, sep="\n")
else:
console.print(f"[red]Failed to retrieve data {response.status_code}[/]")
Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.. :right_arrow_curving_down:")
os.system('cls' if os.name == 'nt' else 'clear') os.system('cls' if os.name == 'nt' else 'clear')

View File

@@ -10,16 +10,11 @@ def config_menu(config_relative_path):
cls() cls()
config_menu_options="[gold bold][1] [grey53 italic]Show current configuration\n[/grey53 italic] \ config_menu_options="[gold bold][1] [grey53 italic]Show current configuration\n[/grey53 italic] \
\n[gold bold][2] [grey53 italic]Change configuraion[/grey53 italic]\n \ \n[gold bold][2] [grey53 italic]Change configuraion[/grey53 italic]\n \
\n\n[green_yellow bold]ENTER - exit Utility" \n\n[green_yellow bold]ENTER - return to Main Menu"
config_menu_options=Align.center(config_menu_options, vertical="middle") config_menu_options=Align.center(config_menu_options, vertical="middle")
console = Console() console = Console()
console.print(Panel(config_menu_options, title="[gold bold]SpaceVM Utility - Utility Configuration" , border_style="magenta" , width=150 , padding = 2))
console.print(Panel(config_menu_options,
title="[gold bold]SpaceVM Utility - Utility Configuration" , style="dark_orange" , width=150 , padding = 2))
sub_choice=str(input("\n>>> ")) sub_choice=str(input("\n>>> "))
if sub_choice == "1": if sub_choice == "1":
config_show(config_relative_path) config_show(config_relative_path)
if sub_choice == "2": if sub_choice == "2":
@@ -27,10 +22,11 @@ title="[gold bold]SpaceVM Utility - Utility Configuration" , style="dark_orange"
def config_show(config_relative_path): def config_show(config_relative_path):
cls() cls()
print("Current configuration:\n") console.rule(title = "Current configuration" , align="center" , style="yellow")
with open(config_relative_path, "r") as f: with open(config_relative_path, "r") as f:
print(f.read()) print(f.read())
Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") console.rule(style="yellow")
Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.. :right_arrow_curving_down:")
def import_vm_uuid(config_relative_path): def import_vm_uuid(config_relative_path):
vm_uuids = [] vm_uuids = []

View File

@@ -1,23 +1,36 @@
import requests import requests
import os import os
from rich.prompt import Prompt from rich.prompt import Prompt
from rich.console import Console
from rich.panel import Panel
from rich.align import Align
def data_pools(base_url, api_key): # output data pool info def data_pools(base_url, api_key): # output data pool info
url = f"http://{base_url}//api/data-pools/" url = f"http://{base_url}//api/data-pools/"
response = requests.get(url, headers={'Authorization': api_key}) response = requests.get(url, headers={'Authorization': api_key})
console = Console()
if response.status_code == 200: if response.status_code == 200:
data_pools = response.json() data_pools = response.json()
results_data_pools_info = data_pools['results'] results_data_pools_info = data_pools['results']
print("\nData pools overview:") os.system('cls' if os.name == 'nt' else 'clear')
print(f"\nData pools total: {data_pools['count']}") console.rule("[bold cyan]Data Pools Overview")
print("-" * 44) console.print(f"[bold]Data pools total:[/] {data_pools['count']}\n")
for x in results_data_pools_info: panels = []
print(f"\nData pool: {x['verbose_name']} ({x['status']})") for x in results_data_pools_info:
print(f"Type: {x['type']} | Used: {round((x['free_space']/1024), 1)} Gb / {round((x['size'] / 1024), 1)} Gb") panel_content = (
print(f"UUID: {x['id']}") f"[bold]Type:[/] {x['type']}\n"
print("-" * 44) f"[bold]Used:[/] {round((x['free_space']/1024), 1)} Gb / {round((x['size'] / 1024), 1)} Gb\n"
else: f"[bold]UUID:[/] [italic]{x['id']}"
print(f"Failed to retrieve data {response.status_code} ") )
Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") panel = Panel(
Align.left(panel_content),
title=f"[bold gold3]{x['verbose_name']}[/] [red]({x['status']})[/]",
border_style="magenta",
expand=False
)
panels.append(panel)
console.print(*panels, sep="\n")
else:
console.print(f"[red]Failed to retrieve data {response.status_code}[/]")
Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.. :right_arrow_curving_down:")
os.system('cls' if os.name == 'nt' else 'clear') os.system('cls' if os.name == 'nt' else 'clear')

View File

@@ -2,15 +2,18 @@ import os
import requests import requests
from domain_api import * from domain_api import *
from rich.prompt import Prompt from rich.prompt import Prompt
from rich.console import Console , Align
def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids): def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids):
print("\033[H\033[2J", end="") os.system('cls' if os.name=='nt' else 'clear')
print("### DISK EDIT MODE ###\n") diks_edit_menu_options="[gold bold][1] [grey53 italic]Delete vDisk by UUID\n[/grey53 italic] \
print("1) Delete vDisk by UUID") \n[gold bold][2] [grey53 italic]Delete ALL vDisks on selected Virtual Machine[/grey53 italic]\n \
print("2) Delete ALL vDisks on selected Virtual Machine") \n[gold bold][3] [grey53 italic]Create Disk[/grey53 italic]\n \
print("3) Create Disk") \n[gold bold][4] [grey53 italic]Prepare VMs for Courses™[/grey53 italic]\n \
print("4) Prepare VMs for Courses™") \n\n[green_yellow bold]ENTER - return to Main Menu"
print("\nENTER - return to Utility Main Menu ") diks_edit_menu_options = Align.center(diks_edit_menu_options, vertical="middle")
console = Console()
console.print(Panel(diks_edit_menu_options, title="[bold red]Disk Edit Mode" , border_style="magenta" , width=150 , padding = 2))
sub_choice=str(input("\n>>> ")) sub_choice=str(input("\n>>> "))
if sub_choice == "1": if sub_choice == "1":
read_input=input("Input vDisk uuid to delete: ") read_input=input("Input vDisk uuid to delete: ")
@@ -25,7 +28,7 @@ def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids):
disk_uuids = get_disk_uuids(base_url , api_key , domain_all_content) disk_uuids = get_disk_uuids(base_url , api_key , domain_all_content)
for x in disk_uuids: for x in disk_uuids:
delete_disk(base_url , api_key , x) delete_disk(base_url , api_key , x)
print("All attached vDisks has been deleted!") console.print("[bold red]All attached vDisks has been deleted!")
if sub_choice == "3": if sub_choice == "3":
vdisk_size=str(input("Enter disk size (GB): ")) vdisk_size=str(input("Enter disk size (GB): "))
@@ -35,7 +38,8 @@ def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids):
create_and_attach_disk(base_url , api_key , vm_uuids[select_uuids] , data_pool_uuid , vdisk_size , "falloc") create_and_attach_disk(base_url , api_key , vm_uuids[select_uuids] , data_pool_uuid , vdisk_size , "falloc")
if sub_choice == "4": if sub_choice == "4":
print("#" * 5 , "Preparing VMs for Courses" , "#" * 5) os.system('cls' if os.name=='nt' else 'clear')
console.rule(title="[bold magenta]Preparing VMs for Courses" , align="center" , style="grey53" , characters = "=")
for y in vm_uuids: #power-on check for y in vm_uuids: #power-on check
domain_uuid = y.strip('\n') domain_uuid = y.strip('\n')
vm_check_power(base_url , api_key , domain_uuid) vm_check_power(base_url , api_key , domain_uuid)
@@ -48,15 +52,16 @@ def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids):
disk_uuids = get_disk_uuids(base_url , api_key , domain_all_content) disk_uuids = get_disk_uuids(base_url , api_key , domain_all_content)
for y in disk_uuids: for y in disk_uuids:
delete_disk(base_url , api_key , y) delete_disk(base_url , api_key , y)
print("All attached vDisks has been deleted!") console.print("[bold red]All attached vDisks has been deleted!")
for z in vm_uuids: # only for creating disks for z in vm_uuids: # only for creating disks
domain_uuid = z.strip('\n') domain_uuid = z.strip('\n')
print(f"\nCreating and attaching disk to VM {domain_uuid}") console.print(f"\n[bold underline yellow]Creating and attaching disk to VM {domain_uuid}")
domain_info = get_domain_info(base_url , api_key , domain_uuid) domain_info = get_domain_info(base_url , api_key , domain_uuid)
domain_all_content = get_domain_all_content(base_url , api_key , domain_uuid) domain_all_content = get_domain_all_content(base_url , api_key , domain_uuid)
if domain_info: if domain_info:
create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 10, "falloc") create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 10, "falloc")
create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 20, "falloc") create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 20, "falloc")
create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 20, "falloc") create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 20, "falloc")
print("Done. Happy virtualization :D") console.print("[bold green]\nDone. Happy virtualization :thumbs_up::thumbs_up:")
Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.. :right_arrow_curving_down:")
os.system('cls' if os.name=='nt' else 'clear')

View File

@@ -1,11 +1,13 @@
# functions for working with domain-api # functions for working with domain-api
import requests import requests
import secrets #for generating unique names import secrets #for generating unique names
import os
from rich.console import Console from rich.console import Console , Align
from rich.columns import Columns from rich.columns import Columns
from rich.panel import Panel from rich.panel import Panel
from rich.prompt import Prompt
console = Console() #necessary for pretty menus & output
power_state = ["Unknown" , "Off" , "Suspend" , "On"] #3 - on; 2 - suspend; 1 - off; 0 - unknown power_state = ["Unknown" , "Off" , "Suspend" , "On"] #3 - on; 2 - suspend; 1 - off; 0 - unknown
@@ -74,57 +76,70 @@ def delete_disk(base_url , api_key , vdisk_uuid):
def get_disk_info(domain_all_content): def get_disk_info(domain_all_content):
console = Console()
# check for "vdisks" field in recieved json response # check for "vdisks" field in recieved json response
if 'vdisks' not in domain_all_content: if 'vdisks' not in domain_all_content:
print("No 'vdisks' field in recieved data") print("No 'vdisks' field in recieved data")
return return
# get vdisk list # get vdisk list
disks = domain_all_content['vdisks'] disks = domain_all_content['vdisks']
# check for disks # check for disks
if not disks: if not disks:
print("No 'disks' field in recieved data. \nProbably VM does not have any attached disks?") console.print("[bold yellow]No 'disks' field in recieved data. \nProbably VM does not have any attached disks?")
return return
disk_info_renderables = []
# Print info for each disk # Print info for each disk
for disk in disks: for disk in disks:
# check for requiered fileds # check for required fields
if 'id' in disk and 'verbose_name' in disk and 'size' in disk: if 'id' in disk and 'verbose_name' in disk and 'size' in disk:
print(f"Name: {disk['verbose_name']}") output_string = (
print(f"UUID: {disk['id']}") f"[bold]Name:[/] {disk['verbose_name']}\n"
print(f"Size: {disk['size']} GB") f"[bold]UUID:[/] [italic]{disk['id']}[/italic]\n"
print("-" * 51) f"[bold]Size:[/] {disk['size']} GB")
disk_info_renderables.append(Panel(output_string, expand=False, border_style="magenta"))
else: else:
print("ERROR: failed to retrieve vdisk data.") print("ERROR: failed to retrieve vdisk data.")
console.print(Columns(disk_info_renderables))
def vm_info(base_url, api_key, vm_uuids): def vm_info(base_url, api_key, vm_uuids):
domain_info = get_domain_info(base_url, api_key, vm_uuids) domain_info = get_domain_info(base_url, api_key, vm_uuids)
domain_all_content = get_domain_all_content(base_url, api_key, vm_uuids) domain_all_content = get_domain_all_content(base_url, api_key, vm_uuids)
if domain_info: if domain_info:
print("\n" , "=" * 14 , "Virtual Machine Info" , "=" * 15) console = Console()
print(f"\t VM: {domain_info['verbose_name']}") vm_info_lines = f"[bold]Power State:[/] [bold red]{power_state[domain_info['user_power_state']]}[/bold red] \n[bold]vDisks:[/] {domain_info['vdisks_count']}"
print(f"\t Power State: {power_state[domain_info['user_power_state']]}") #translating status code to "pretty name" vm_info_renderable = Panel(vm_info_lines, title=f"[bold magenta]{domain_info['verbose_name']}" , expand=False , border_style="yellow")
print(f"\t vDisks: {domain_info['vdisks_count']}") vm_info_renderable=Align.center(vm_info_renderable, vertical="middle")
print("-" * 19 , "vDisks Info" , "-" * 19) print("\n")
console.rule(style="yellow")
console.print(vm_info_renderable)
console.rule(title = "[bold yellow]vDisks Info" , style="grey53" , align="center")
get_disk_info(domain_all_content) get_disk_info(domain_all_content)
console.rule(style="yellow")
def vm_info_short(base_url , api_key): #output data pool info
url= f"http://{base_url}//api/domains/" def vm_info_short(base_url, api_key):
url = f"http://{base_url}/api/domains/"
response = requests.get(url, headers={'Authorization': api_key}) response = requests.get(url, headers={'Authorization': api_key})
if response.status_code == 200: if response.status_code == 200:
vm_info_short = response.json() vm_info_short = response.json()
results_vm_info_short = vm_info_short['results'] results_vm_info_short = vm_info_short['results']
print(f"\nShort VM overview | Total: {vm_info_short['count']}") os.system('cls' if os.name=='nt' else 'clear')
print("=" * 43) console.print(Align.center(Panel(f"[bold magenta]Short VM overview | Total: {vm_info_short['count']}", expand=True , border_style="yellow") , vertical="middle"))
console.rule(style="grey53")
output_renderables = []
for x in results_vm_info_short: for x in results_vm_info_short:
print(f" VM: {x['verbose_name']}") output_string = f"VM: [bold]{x['verbose_name']}" + f"\nUUID: [italic]{x['id']}"
print(f"UUID: {x['id']}") output_renderable = Panel(output_string, expand=False, border_style="magenta")
print("-" * 43) output_renderables.append(output_renderable) #adds current renderable
console.print(Columns(output_renderables)) #print renderables by columns
else: else:
print(f"Failed to retrieve data {response.status_code}") print(f"Failed to retrieve data {response.status_code}")
console.rule(style="grey53")
Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.... :right_arrow_curving_down:")
os.system('cls' if os.name=='nt' else 'clear')
def create_and_attach_disk(base_url , api_key , vm_id, data_pool_uuid, vdisk_size, preallocation): def create_and_attach_disk(base_url , api_key , vm_id, data_pool_uuid, vdisk_size, preallocation):
domain_name=get_domain_info(base_url , api_key , vm_id) domain_name=get_domain_info(base_url , api_key , vm_id)
@@ -143,7 +158,7 @@ def create_and_attach_disk(base_url , api_key , vm_id, data_pool_uuid, vdisk_siz
} }
response = requests.post(url , headers=headers, json=payload) response = requests.post(url , headers=headers, json=payload)
if response.status_code == 200: if response.status_code == 200:
print(f"vDisk {disk_name} - {vdisk_size}GB has been created and attached to VM - {vm_id}") print(f"vDisk {disk_name} ({vdisk_size}GB) has been created and attached")
return True return True
else: else:
print(f"ERROR creating vDisk :\n {response.status_code} - {response.text}") print(f"ERROR creating vDisk :\n {response.status_code} - {response.text}")

View File

@@ -24,13 +24,14 @@ menu_choice=0
menu_options="[gold bold][1] [grey53 italic]Manage utility config\n[/grey53 italic] \ menu_options="[gold bold][1] [grey53 italic]Manage utility config\n[/grey53 italic] \
\n[gold bold][2] [grey53 italic]Enter disk edit mode[/grey53 italic]\n \ \n[gold bold][2] [grey53 italic]Enter disk edit mode[/grey53 italic]\n \
\n[gold bold][3] [grey53 italic]Show breif cluster overview[/grey53 italic]\n \ \n[gold bold][3] [grey53 italic]Show breif cluster overview[/grey53 italic]\n \
\n[gold bold][4] [grey53 italic]Show VM info[/grey53 italic]\n \ \n[gold bold][4] [grey53 italic]Show VM info \n (for selected VMs in config)[/grey53 italic]\n \
\n[gold bold][5] [grey53 italic]Show data pools[/grey53 italic]\n \ \n[gold bold][5] [grey53 italic]Show data pools[/grey53 italic]\n \
\n[gold bold][6] [grey53 italic]Show VMs Name / UUID[/grey53 italic]\n \ \n[gold bold][6] [grey53 italic]Show VMs Name / UUID[/grey53 italic]\n \
\n\n[green_yellow bold]ENTER - exit Utility" \n\n[green_yellow bold]ENTER - exit Utility"
menu_options=Align.center(menu_options, vertical="middle") menu_options=Align.center(menu_options, vertical="middle")
menu_subtitle = "[blue bold][link=github.com/OVERLORD7F]:wrench: Project_GitHub[/link] [yellow]| [magenta bold][link=spacevm.ru/docs/]:books: SpaceVM_Docs[/link] [yellow]| [red bold][link=comptek.ru]:briefcase: Comptek[/link]" menu_subtitle = "[blue bold][link=https://github.com/OVERLORD7F/SpaceVM_VM_Utility]:wrench: Project_GitHub[/link] [yellow]| [magenta bold][link=https://spacevm.ru/docs/]:books: SpaceVM_Docs[/link] [yellow]| [red bold][link=https://comptek.ru]:briefcase: Comptek[/link]"
console = Console() console = Console()
os.system('cls' if os.name=='nt' else 'clear')
while(menu_choice != ""): #main menu loop while(menu_choice != ""): #main menu loop
console.print(Panel(menu_options, console.print(Panel(menu_options,
title="[bold magenta]SpaceVM Utility - Main Menu" , subtitle = menu_subtitle, subtitle_align="right" , style="yellow" , width=150 , padding = 2)) title="[bold magenta]SpaceVM Utility - Main Menu" , subtitle = menu_subtitle, subtitle_align="right" , style="yellow" , width=150 , padding = 2))
@@ -42,11 +43,13 @@ title="[bold magenta]SpaceVM Utility - Main Menu" , subtitle = menu_subtitle, su
if menu_choice == "3": if menu_choice == "3":
cluster_info(base_url , api_key) cluster_info(base_url , api_key)
if menu_choice == "4": if menu_choice == "4":
print("\033[H\033[2J", end="") os.system('cls' if os.name=='nt' else 'clear')
for x in vm_uuids: for x in vm_uuids:
vm_info(base_url , api_key , x) vm_info(base_url , api_key , x)
Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:")
if menu_choice == "5": if menu_choice == "5":
data_pools(base_url , api_key) data_pools(base_url , api_key)
if menu_choice == "6": if menu_choice == "6":
vm_info_short(base_url , api_key) vm_info_short(base_url , api_key)
os.system('cls' if os.name=='nt' else 'clear') #clears screen before looping back to main menu
console.print("[red bold]Exiting Utility ") console.print("[red bold]Exiting Utility ")