diff --git a/network/ValhallaServer.py b/network/ValhallaServer.py index b4a1e4e..3a1f425 100644 --- a/network/ValhallaServer.py +++ b/network/ValhallaServer.py @@ -163,7 +163,7 @@ class ValhallaServer: except Exception as ex: pass - def get_vms_data_from_server(self): + def get_vms_data_from_server(self) ->dict(): try: vms_data = {} for image_id in self.vm_ids_list_from_server: @@ -175,6 +175,7 @@ class ValhallaServer: data = response.json() vms_data[f"{image_id}"] = data self.vms_metadata = vms_data + return vms_data except Exception as ex: print(str(ex)) diff --git a/network/__pycache__/ValhallaServer.cpython-310.pyc b/network/__pycache__/ValhallaServer.cpython-310.pyc index 23d3323..953e60c 100644 Binary files a/network/__pycache__/ValhallaServer.cpython-310.pyc and b/network/__pycache__/ValhallaServer.cpython-310.pyc differ diff --git a/utils/MachineData.py b/utils/MachineData.py index 7eaefa1..ed1a964 100644 --- a/utils/MachineData.py +++ b/utils/MachineData.py @@ -3,6 +3,7 @@ import socket import yaml import os import psutil +from string import Template class MachineData(): @@ -67,6 +68,21 @@ class MachineData(): except Exception as e: print(str(e)) exit(-1) - -class VirtualmachineConfig(): - def __init__(cpu_count: int, memory_size: int, ): + + def generate_runner_script(self, file_path: str): + substitution_mapper = { + "CPU_CORES_COUNT": (self.cpu_count/2), + "MEMORY_ALLOCATION_SIZE": f"{(self.ram_size/2)}M", + "IMAGE_PATH": "image.qcow2" + } + try: + with open("vm_run_script.template", "r") as file: + src = file.read() + temp_src = Template(src) + result = temp_src.substitute(substitution_mapper) + result_file = open(f"{file_path}/run.sh", "w") + result_file.write(result) + result_file.flush() + result_file.close() + except Exception as ex: + print(str(ex)) diff --git a/valhalla-client b/valhalla-client index 92627b0..a2b0407 100644 --- a/valhalla-client +++ b/valhalla-client @@ -1,28 +1,150 @@ from network.ValhallaServer import ValhallaServer from utils.MachineData import MachineData +import argparse +from picotui.widgets import * +from picotui.menu import * +from picotui.context import Context +import os +import json machine_data = MachineData() -server = ValhallaServer( - server_address=machine_data.server_url, - server_port=machine_data.server_port, - server_user=machine_data.server_access_username, - server_access_password=machine_data.server_password, - logging_level=machine_data.client_loglevel, -) +def synchronize(): + server = ValhallaServer( + server_address=machine_data.server_url, + server_port=machine_data.server_port, + server_user=machine_data.server_access_username, + server_access_password=machine_data.server_password, + logging_level=machine_data.client_loglevel, + ) + server.authenticate() + server.get_server_data() + server.update_machine_data_on_server(machine_data=machine_data) + vm_list = server.get_vm_list_from_server(machine_data=machine_data) + print(vm_list) + vm_data = server.get_vms_data_from_server() + with open("vms_data.json", "w") as vm_data_file: + vm_data_file.write(json.dumps(vm_data)) + vm_data_file.flush() + server.update_vms_images_from_server() + for image_id in vm_data: + run_script_path = f"images/valhalla/{vm_data[image_id]['image_name']}/{vm_data[image_id]['image_version']}" + print(run_script_path) + machine_data.generate_runner_script(run_script_path) + +def runner(): + d = None + image_choice_vm_id = None + vm_data = {} + with open("vms_data.json", "r") as vm_data_file: + try: + vm_data = json.loads(vm_data_file.read()) + except Exception as ex: + print(f"ERROR: {str(ex)}") + # This routine is called to redraw screen "in menu's background" + def screen_redraw(s, allow_cursor=False): + with open("vms_data.json", "r") as vm_data_file: + try: + vm_data = json.loads(vm_data_file.read()) + except Exception as ex: + print(f"ERROR: {str(ex)}") + s.attr_color(C_WHITE, C_BLUE) + s.cls() + s.attr_reset() + d.redraw() + def main_loop(): + while 1: + key = m.get_input() + + if isinstance(key, list): + # Mouse click + x, y = key + if m.inside(x, y): + m.focus = True + + if m.focus: + # If menu is focused, it gets events. If menu is cancelled, + # it loses focus. Otherwise, if menu selection is made, we + # quit with with menu result. + res = m.handle_input(key) + if res == ACTION_CANCEL: + m.focus = False + elif res is not None and res is not True: + return res + else: + # If menu isn't focused, it can be focused by pressing F9. + if key == KEY_F9: + m.focus = True + m.redraw() + continue + # Otherwise, dialog gets input + res = d.handle_input(key) + if res is not None and res is not True: + return res + with Context(): + + d = Dialog(10, 5, 80, 24) + d.add(12, 1, WLabel("Virtual Machine selection screen")) + d.add(1, 2, WLabel("VMs:")) + vm_names_table = [] + for vm_id in vm_data: + vm_names_table.append(f"{vm_id}: {vm_data[vm_id]['image_name']} - ver. {vm_data[vm_id]['image_version']}") + w_listbox = WListBox(64, 4, vm_names_table) + d.add(1,3, w_listbox) + + d.add(1, 6, "Selected listbox value:") + w_listbox_val = WLabel("", w=64) + d.add(1, 7, w_listbox_val) -server.authenticate() + def listbox_changed(w): + val = w.items[w.choice] + w_listbox_val.t = val + w_listbox_val.redraw() + + w_listbox.on("changed", listbox_changed) -server.get_server_data() + b = WButton(8, "OK") + d.add(10, 11, b) + b.finish_dialog = ACTION_OK -server.update_machine_data_on_server(machine_data=machine_data) + b = WButton(8, "Cancel") + d.add(23, 11, b) + b.finish_dialog = ACTION_CANCEL -vm_list = server.get_vm_list_from_server(machine_data=machine_data) + screen_redraw(Screen) + Screen.set_screen_redraw(screen_redraw) + + menu_file = WMenuBox([("Exit", "ex")]) + m = WMenuBar([("File", menu_file), ("About", "About")]) + m.permanent = True + m.redraw() + + res = main_loop() + image_choice_vm_id = int(str(vm_names_table[int(w_listbox.choice)]).split(":")[0]) + print(image_choice_vm_id) + try: + print(f"Running VM: {vm_data[f'{image_choice_vm_id}']['image_name']}:{vm_data[f'{image_choice_vm_id}']['image_version']}") + os.execvp(f"images/valhalla/{vm_data[f'{image_choice_vm_id}']['image_name']}/{vm_data[f'{image_choice_vm_id}']['image_version']}/run.sh") + except Exception as ex: + print(str(ex)) + +parser = argparse.ArgumentParser( + prog="valhalla-client", + description="Client for running VM machines", +) -print(vm_list) +function_mapper = { + "runner": runner, + "synchronize": synchronize, +} -server.get_vms_data_from_server() +parser.add_argument("command", choices=function_mapper) +args = parser.parse_args() -server.update_vms_images_from_server() +fun = function_mapper[args.command] +try: + fun() +except Exception as ex: + print(f"Invalid command: {str(ex)}") # vim:ft=py \ No newline at end of file diff --git a/vm_run_script.template b/vm_run_script.template new file mode 100644 index 0000000..370dff5 --- /dev/null +++ b/vm_run_script.template @@ -0,0 +1,7 @@ +#!/bin/bash +## QEMU (VM) command +qemu-system-x86_64 -enable-kvm -m $MEMORY_ALLOCATION_SIZE \ +-cpu host,kvm=off,hv_relaxed,hv_spinlocks=0x1fff,hv_time,hv_vapic,hv_vendor_id=0xDEADBEEFFF \ +-rtc clock=host,base=localtime -smp 4,sockets=1,cores=$CPU_CORES_COUNT,threads=2 \ +-device virtio-net-pci,netdev=n1 -netdev user,id=n1 \ +-hda $IMAGE_PATH & \ No newline at end of file diff --git a/vms_data.json b/vms_data.json new file mode 100644 index 0000000..366def4 --- /dev/null +++ b/vms_data.json @@ -0,0 +1 @@ +{"4": {"image_file": "temp_file.qcow2", "image_hash": "d41d8cd98f00b204e9800998ecf8427e", "image_id": "4", "image_name": "cool-image-name", "image_name_version_combo": "cool-image-name@v0.0.2alpha", "image_version": "v0.0.2alpha"}, "3": {"image_file": "temp_file.qcow2", "image_hash": "d41d8cd98f00b204e9800998ecf8427e", "image_id": "3", "image_name": "cool-image-name", "image_name_version_combo": "cool-image-name@v0.0.1alpha", "image_version": "v0.0.1alpha"}} \ No newline at end of file