From 5c75771355ee51c1239695c5e516077aafb71515 Mon Sep 17 00:00:00 2001 From: Wojciech Janota Date: Wed, 7 Dec 2022 10:20:51 +0100 Subject: [PATCH] WIP --- .gitignore | 2 + config.yml | 5 + .../cool-image-name/v0.0.2alpha/image.qcow2 | 4 + network/ValhallaServer.py | 194 ++++++++++++++++-- .../ValhallaServer.cpython-310.pyc | Bin 1474 -> 5642 bytes utils/MachineData.py | 72 +++++++ utils/__init__.py | 1 + valhalla-client | 23 ++- 8 files changed, 285 insertions(+), 16 deletions(-) create mode 100644 config.yml create mode 100644 images/valhalla/cool-image-name/v0.0.2alpha/image.qcow2 create mode 100644 utils/MachineData.py create mode 100644 utils/__init__.py diff --git a/.gitignore b/.gitignore index 135af53..82aecaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .env/** +.env +__pycache__ __pycache__/** \ No newline at end of file diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..86ff5bb --- /dev/null +++ b/config.yml @@ -0,0 +1,5 @@ +server_port: 5000 +server_url: "http://localhost" +server_password: "sekret_password" +server_access_username: "user" +client_loglevel: "DEBUG" \ No newline at end of file diff --git a/images/valhalla/cool-image-name/v0.0.2alpha/image.qcow2 b/images/valhalla/cool-image-name/v0.0.2alpha/image.qcow2 new file mode 100644 index 0000000..c8bdf25 --- /dev/null +++ b/images/valhalla/cool-image-name/v0.0.2alpha/image.qcow2 @@ -0,0 +1,4 @@ +duiuwqhdiuhwqiudhewiufhwieuhiuwhfiuiueyr837r87438r3iuh87duihew8732roh87yro3r +ry398yr983yr937yr743tfhuieh9843ty347nv743nyv34[9yt34 +4yv9[843qyt9843yt98439843ry3948feiuh9843fy934t +ru09438ur9843ur9843ut9834tup43349]] \ No newline at end of file diff --git a/network/ValhallaServer.py b/network/ValhallaServer.py index 1fee919..b4a1e4e 100644 --- a/network/ValhallaServer.py +++ b/network/ValhallaServer.py @@ -1,37 +1,201 @@ import requests as req import logging -import json +import os +import time +import urllib3 +import shutil -class ValhallaServer(): +from utils.MachineData import MachineData - def __init__(self, server_address: str, server_port: str, server_user: str, server_access_password: str, logging_level: str): + +class ValhallaServer: + def __init__( + self, + server_address: str, + server_port: str, + server_user: str, + server_access_password: str, + logging_level: str, + ): self.server_address = server_address self.server_port = server_port self.server_user = server_user + self.server_name = "not connected" + self.server_version = "not_connected" self.server_access_password = server_access_password self.logging_level = logging_level self.logger = logging.getLogger(__name__) self.bearer_token = "" + self.auth_headers = {} + self.user_id = 0 + self.vm_ids_list_from_server = [] + self.vms_metadata = {} log_level_mapping_dict = { - "NOTSET": 0, - "DEBUG": 10, - "INFO": 20, - "WARNING": 30, - "ERROR": 40, - "CRITICAL": 50, - } + "NOTSET": 0, + "DEBUG": 10, + "INFO": 20, + "WARNING": 30, + "ERROR": 40, + "CRITICAL": 50, + } self.logger.setLevel(log_level_mapping_dict[logging_level]) - + def authenticate(self): try: login_data = { "username": self.server_user, - "password": self.server_access_password + "password": self.server_access_password, } headers = {"Content-Type": "application/json"} - print(json.dumps(login_data)) - response = req.post(url=f"{self.server_address}:{self.server_port}/login", json=login_data, headers=headers) - self.logger.info(str(response.json())) + response = req.post( + url=f"{self.server_address}:{self.server_port}/login", + json=login_data, + headers=headers, + ) + # self.logger.info(str(response.json())) + if response.status_code == 202: + user_data = response.json() + self.bearer_token = user_data["token"] + self.user_id = user_data["user_id"] + self.auth_headers = {"Authorization": f"Bearer {self.bearer_token}"} + else: + raise Exception("Bad input or server not responding") + except Exception as ex: + self.logger.error(f"{str(ex)}") + + def get_server_data(self): + try: + response = req.get( + url=f"{self.server_address}:{self.server_port}/", + headers=self.auth_headers, + ) print(str(response.json())) + if response.status_code == 200: + server_data = response.json() + self.server_version = server_data["server_version"] + self.server_name = server_data["server_name"] except Exception as ex: self.logger.error(f"{str(ex)}") + + def update_machine_data_on_server(self, machine_data: MachineData): + try: + response = req.get( + url=f"{self.server_address}:{self.server_port}/clients/{machine_data.mac_address}", + headers=self.auth_headers, + ) + if response.status_code == 401: + self.authenticate() + print(self.bearer_token) + response = req.get( + url=f"{self.server_address}:{self.server_port}/clients/{machine_data.mac_address}", + headers=self.auth_headers, + ) + print(f"{str(response.json())}") + client_data = { + "mac_address": machine_data.mac_address, + "ip_address": machine_data.ip_address, + "hostname": machine_data.hostname, + "client_version": machine_data.client_version, + "vm_list_on_machine": [], + } + if response.status_code == 404: + print("Client not registered, registering now...") + for i in range(1, 5): + register_response = req.post( + url=f"{self.server_address}:{self.server_port}/clients", + headers=self.auth_headers, + json=client_data, + ) + if register_response == 201: + print("Registered successfully") + break + else: + print("Failed to register, waiting 5s before trying again...") + time.sleep(5) + continue + + if response.status_code == 200: + client_data_from_server = response.json() + try: + client_data.pop("vm_list_on_machine") + except KeyError: + pass + if client_data_from_server != client_data: + print("Client data needs updating, updating now...") + for i in range(1, 5): + register_response = req.put( + url=f"{self.server_address}:{self.server_port}/clients", + headers=self.auth_headers, + json=client_data, + ) + if register_response.status_code == 202: + print("Updated successfully") + break + elif response.status_code == 401: + print("Reauthentication needed...") + self.authenticate() + else: + print("Failed to update, waiting 5s before trying again...") + time.sleep(5) + continue + + except Exception as ex: + self.logger.error(f"{str(ex)}") + + def get_vm_list_from_server(self, machine_data: MachineData): + try: + response = req.get( + url=f"{self.server_address}:{self.server_port}/clients/{machine_data.mac_address}/vms", + headers=self.auth_headers, + ) + if response.status_code == 401: + self.authenticate() + response = req.get( + url=f"{self.server_address}:{self.server_port}/clients/{machine_data.mac_address}/vms", + headers=self.auth_headers, + ) + if response.status_code == 200: + vm_list = response.json() + self.vm_ids_list_from_server = vm_list + return vm_list + else: + print(f"Error getting data from remote server: {response.json()}") + except Exception as ex: + pass + + def get_vms_data_from_server(self): + try: + vms_data = {} + for image_id in self.vm_ids_list_from_server: + response = req.get( + url=f"{self.server_address}:{self.server_port}/images/{image_id}", + headers=self.auth_headers, + ) + if response.status_code == 200: + data = response.json() + vms_data[f"{image_id}"] = data + self.vms_metadata = vms_data + except Exception as ex: + print(str(ex)) + + def update_vms_images_from_server(self): + try: + for image_id in self.vm_ids_list_from_server: + image_data = self.vms_metadata[f"{image_id}"] + print(f"{image_data['image_name']} download") + http = urllib3.PoolManager() + self.authenticate() + filename = f"images/{self.server_name}/{image_data['image_name']}/{image_data['image_version']}/image.qcow2" + os.makedirs(os.path.dirname(filename), exist_ok=True) + with http.request( + method="GET", + url=f"{self.server_address}:{self.server_port}/images/{image_id}/download", + preload_content=False, + headers=self.auth_headers, + ) as r, open( + filename, + "wb", + ) as out_file: + shutil.copyfileobj(r, out_file) + except Exception as ex: + print(str(ex)) diff --git a/network/__pycache__/ValhallaServer.cpython-310.pyc b/network/__pycache__/ValhallaServer.cpython-310.pyc index 33acd813de2d0b47de8259e1ad1043511cd9f47c..23d33233e1a0a673a0601a255cb19d82ce280c92 100644 GIT binary patch literal 5642 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 P2AL5hpg;l(JVSm1q13Cl diff --git a/utils/MachineData.py b/utils/MachineData.py new file mode 100644 index 0000000..7eaefa1 --- /dev/null +++ b/utils/MachineData.py @@ -0,0 +1,72 @@ +from getmac import get_mac_address as gma +import socket +import yaml +import os +import psutil + +class MachineData(): + + def get_ip_address(self): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + return s.getsockname()[0] + + def __init__(self): + self.mac_address = gma() + self.ip_address = str(self.get_ip_address()) + self.hostname = socket.gethostname() + self.client_version = "v0.0.1alpha" + self.cpu_count = os.cpu_count() + self.ram_size = (psutil.virtual_memory().total / 1024) / 1024 + + try: + with open("config.yml", "r") as stream: + try: + config_file = yaml.safe_load(stream) + except Exception as e: + print(e) + except FileNotFoundError as e: + print(str(e)) + exit(-1) + config = config_file + + try: + server_url_override = os.environ.get("VALHALLA_SERVER_URL") + server_port_override = os.environ.get("VALHALLA_SERVER_PORT") + server_password_override = os.environ.get("VALHALLA_SERVER_PASSWORD") + server_access_username_override = os.environ.get( + "VALHALLA_SERVER_ACCESS_USERNAME") + client_logging_override = os.environ.get("VALHALLA_CLIENT_LOGLEVEL") + + if server_port_override: + config["server_port"] = server_port_override + + if server_url_override: + config["server_url"] = server_url_override + + if server_password_override: + config["server_password"] = server_password_override + + if server_access_username_override: + config["server_access_username"] = server_access_username_override + + if client_logging_override: + config["client_loglevel"] = client_logging_override + + self.server_url = config["server_url"] + self.server_port = config["server_port"] + self.server_password = config["server_password"] + self.server_access_username = config["server_access_username"] + self.client_loglevel = config["client_loglevel"] + except KeyError as ke: + print(str(ke)) + pass + except TypeError as te: + print(str(te)) + pass + except Exception as e: + print(str(e)) + exit(-1) + +class VirtualmachineConfig(): + def __init__(cpu_count: int, memory_size: int, ): diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..f453450 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +from . import MachineData \ No newline at end of file diff --git a/valhalla-client b/valhalla-client index 3c10ddc..92627b0 100644 --- a/valhalla-client +++ b/valhalla-client @@ -1,7 +1,28 @@ from network.ValhallaServer import ValhallaServer +from utils.MachineData import MachineData -server = ValhallaServer(server_address="http://localhost", server_port=str(5000), server_user="user", server_access_password="sekret_password", logging_level="DEBUG") +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, +) 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) + +server.get_vms_data_from_server() + +server.update_vms_images_from_server() + # vim:ft=py \ No newline at end of file