diff --git a/cluster_api.py b/cluster_api.py index ce92b6e..f6bcf43 100644 --- a/cluster_api.py +++ b/cluster_api.py @@ -1,30 +1,37 @@ - #from main import base_url , api_key , requests import requests import os 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 - url= f"http://{base_url}/api/clusters" - response = requests.get(url , headers={'Authorization' : api_key}) - +def cluster_info(base_url, api_key): # output short clusters overview + url = f"http://{base_url}/api/clusters" + response = requests.get(url, headers={'Authorization': api_key}) + console = Console() if response.status_code == 200: cluster_info = response.json() results_cluster_info = cluster_info['results'] - - print("\nShort clusters overview:") - print(f"\nClusters total: {cluster_info['count']}") - print("-" * 51) + os.system('cls' if os.name == 'nt' else 'clear') + console.rule("[bold cyan]Short Clusters Overview") + console.print(f"[bold]Clusters total:[/] {cluster_info['count']}\n") + panels = [] for x in results_cluster_info: - - print(f"\nCluster Name: {x['verbose_name']} ({x['status']})") - print(f"Nodes: {x['nodes_count']}") - print(f"Total CPU: {x['cpu_count']} Cores || CPU Usage: {round(x['cpu_used_percent_user'] , 2)}%")#output is rounded by 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 - - print("-" * 51) - + panel_content = ( + f"[bold]Nodes:[/] {x['nodes_count']}\n" + f"[bold]Total CPU:[/] {x['cpu_count']} Cores / [bold]CPU Usage:[/] {round(x['cpu_used_percent_user'], 2)}%\n" + f"[bold]Total RAM:[/] {int(x['memory_count']/1024)} GB / [bold]RAM Usage:[/] {round(x['mem_used_percent_user'], 2)}%" + ) + 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: - print(f"Failed to retrieve data {response.status_code}") - Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") - os.system('cls' if os.name=='nt' else 'clear') \ No newline at end of file + 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') \ No newline at end of file diff --git a/config_data_import.py b/config_data_import.py index df562da..137e912 100644 --- a/config_data_import.py +++ b/config_data_import.py @@ -10,16 +10,11 @@ def config_menu(config_relative_path): cls() 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\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") console = Console() - - console.print(Panel(config_menu_options, -title="[gold bold]SpaceVM Utility - Utility Configuration" , style="dark_orange" , width=150 , padding = 2)) - - + console.print(Panel(config_menu_options, title="[gold bold]SpaceVM Utility - Utility Configuration" , border_style="magenta" , width=150 , padding = 2)) sub_choice=str(input("\n>>> ")) - if sub_choice == "1": config_show(config_relative_path) if sub_choice == "2": @@ -27,10 +22,11 @@ title="[gold bold]SpaceVM Utility - Utility Configuration" , style="dark_orange" def config_show(config_relative_path): cls() - print("Current configuration:\n") + console.rule(title = "Current configuration" , align="center" , style="yellow") with open(config_relative_path, "r") as f: 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): vm_uuids = [] diff --git a/data_pools_api.py b/data_pools_api.py index d4774c2..422c29e 100644 --- a/data_pools_api.py +++ b/data_pools_api.py @@ -1,23 +1,36 @@ import requests import os 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 - url= f"http://{base_url}//api/data-pools/" - response = requests.get(url , headers={'Authorization' : api_key}) - +def data_pools(base_url, api_key): # output data pool info + url = f"http://{base_url}//api/data-pools/" + response = requests.get(url, headers={'Authorization': api_key}) + console = Console() if response.status_code == 200: data_pools = response.json() results_data_pools_info = data_pools['results'] - print("\nData pools overview:") - print(f"\nData pools total: {data_pools['count']}") - print("-" * 44) + os.system('cls' if os.name == 'nt' else 'clear') + console.rule("[bold cyan]Data Pools Overview") + console.print(f"[bold]Data pools total:[/] {data_pools['count']}\n") + panels = [] for x in results_data_pools_info: - print(f"\nData pool: {x['verbose_name']} ({x['status']})") - print(f"Type: {x['type']} | Used: {round((x['free_space']/1024), 1)} Gb / {round((x['size'] / 1024), 1)} Gb") - print(f"UUID: {x['id']}") - print("-" * 44) + panel_content = ( + f"[bold]Type:[/] {x['type']}\n" + f"[bold]Used:[/] {round((x['free_space']/1024), 1)} Gb / {round((x['size'] / 1024), 1)} Gb\n" + f"[bold]UUID:[/] [italic]{x['id']}" + ) + 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: - print(f"Failed to retrieve data {response.status_code} ") - Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") - os.system('cls' if os.name=='nt' else 'clear') \ No newline at end of file + 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') \ No newline at end of file diff --git a/disk_edit_mode.py b/disk_edit_mode.py index bcb15fb..43c1eff 100644 --- a/disk_edit_mode.py +++ b/disk_edit_mode.py @@ -2,15 +2,18 @@ import os import requests from domain_api import * from rich.prompt import Prompt +from rich.console import Console , Align -def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids): - print("\033[H\033[2J", end="") - print("### DISK EDIT MODE ###\n") - print("1) Delete vDisk by UUID") - print("2) Delete ALL vDisks on selected Virtual Machine") - print("3) Create Disk") - print("4) Prepare VMs for Courses™") - print("\nENTER - return to Utility Main Menu ") +def disk_edit_mode(base_url , api_key , data_pool_uuid , vm_uuids): + os.system('cls' if os.name=='nt' else 'clear') + diks_edit_menu_options="[gold bold][1] [grey53 italic]Delete vDisk by UUID\n[/grey53 italic] \ +\n[gold bold][2] [grey53 italic]Delete ALL vDisks on selected Virtual Machine[/grey53 italic]\n \ +\n[gold bold][3] [grey53 italic]Create Disk[/grey53 italic]\n \ +\n[gold bold][4] [grey53 italic]Prepare VMs for Courses™[/grey53 italic]\n \ +\n\n[green_yellow bold]ENTER - return to 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>>> ")) if sub_choice == "1": 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) for x in disk_uuids: 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": 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") 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 domain_uuid = y.strip('\n') 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) for y in disk_uuids: 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 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_all_content = get_domain_all_content(base_url , api_key , domain_uuid) 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, 20, "falloc") create_and_attach_disk(base_url , api_key , domain_uuid , data_pool_uuid, 20, "falloc") - print("Done. Happy virtualization :D") - Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") \ No newline at end of file + console.print("[bold green]\nDone. Happy virtualization :thumbs_up::thumbs_up:") + Prompt.ask("[green_yellow bold]ENTER - return to Main Menu.. :right_arrow_curving_down:") + os.system('cls' if os.name=='nt' else 'clear') \ No newline at end of file diff --git a/domain_api.py b/domain_api.py index ef7a538..b9bf28b 100644 --- a/domain_api.py +++ b/domain_api.py @@ -1,11 +1,13 @@ # functions for working with domain-api import requests import secrets #for generating unique names - -from rich.console import Console +import os +from rich.console import Console , Align from rich.columns import Columns 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 @@ -74,57 +76,70 @@ def delete_disk(base_url , api_key , vdisk_uuid): def get_disk_info(domain_all_content): + console = Console() # check for "vdisks" field in recieved json response if 'vdisks' not in domain_all_content: print("No 'vdisks' field in recieved data") return - # get vdisk list disks = domain_all_content['vdisks'] - # check for 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 + + disk_info_renderables = [] # Print info for each disk 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: - print(f"Name: {disk['verbose_name']}") - print(f"UUID: {disk['id']}") - print(f"Size: {disk['size']} GB") - print("-" * 51) + output_string = ( + f"[bold]Name:[/] {disk['verbose_name']}\n" + f"[bold]UUID:[/] [italic]{disk['id']}[/italic]\n" + f"[bold]Size:[/] {disk['size']} GB") + disk_info_renderables.append(Panel(output_string, expand=False, border_style="magenta")) else: print("ERROR: failed to retrieve vdisk data.") + console.print(Columns(disk_info_renderables)) -def vm_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) +def vm_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) if domain_info: - print("\n" , "=" * 14 , "Virtual Machine Info" , "=" * 15) - print(f"\t VM: {domain_info['verbose_name']}") - print(f"\t Power State: {power_state[domain_info['user_power_state']]}") #translating status code to "pretty name" - print(f"\t vDisks: {domain_info['vdisks_count']}") - print("-" * 19 , "vDisks Info" , "-" * 19) + console = Console() + 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']}" + vm_info_renderable = Panel(vm_info_lines, title=f"[bold magenta]{domain_info['verbose_name']}" , expand=False , border_style="yellow") + vm_info_renderable=Align.center(vm_info_renderable, vertical="middle") + 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) + console.rule(style="yellow") -def vm_info_short(base_url , api_key): #output data pool info - url= f"http://{base_url}//api/domains/" - response = requests.get(url , headers={'Authorization' : api_key}) + +def vm_info_short(base_url, api_key): + url = f"http://{base_url}/api/domains/" + response = requests.get(url, headers={'Authorization': api_key}) if response.status_code == 200: vm_info_short = response.json() results_vm_info_short = vm_info_short['results'] - print(f"\nShort VM overview | Total: {vm_info_short['count']}") - print("=" * 43) + os.system('cls' if os.name=='nt' else 'clear') + 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: - print(f" VM: {x['verbose_name']}") - print(f"UUID: {x['id']}") - print("-" * 43) - + output_string = f"VM: [bold]{x['verbose_name']}" + f"\nUUID: [italic]{x['id']}" + output_renderable = Panel(output_string, expand=False, border_style="magenta") + output_renderables.append(output_renderable) #adds current renderable + console.print(Columns(output_renderables)) #print renderables by columns 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): 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) 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 else: print(f"ERROR creating vDisk :\n {response.status_code} - {response.text}") diff --git a/main.py b/main.py index d3f6fbe..0ad5154 100644 --- a/main.py +++ b/main.py @@ -24,13 +24,14 @@ menu_choice=0 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][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][6] [grey53 italic]Show VMs Name / UUID[/grey53 italic]\n \ \n\n[green_yellow bold]ENTER - exit Utility" 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() +os.system('cls' if os.name=='nt' else 'clear') while(menu_choice != ""): #main menu loop console.print(Panel(menu_options, 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": cluster_info(base_url , api_key) if menu_choice == "4": - print("\033[H\033[2J", end="") + os.system('cls' if os.name=='nt' else 'clear') for x in vm_uuids: vm_info(base_url , api_key , x) + Prompt.ask("[green_yellow bold]Press ENTER to proceed.. :right_arrow_curving_down:") if menu_choice == "5": data_pools(base_url , api_key) 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 ") \ No newline at end of file