Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/refactoring #65

Merged
merged 33 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/python_lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Python lint

on:
- pull_request
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install Ruff
run: |
python -m pip install --upgrade pip
pip install ruff

- name: Run Ruff
run: |
ruff check .
45 changes: 45 additions & 0 deletions frameworks/VboxMachine/VboxMachine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from VBoxWrapper import VirtualMachine

from frameworks.decorators import vm_is_turn_on
from .vm_data import VmData
from .configs import VmConfig


class VboxMachine:

def __init__(self, name: str, config_path: str = None):
self.vm_config = VmConfig(config_path=config_path)
self.vm = VirtualMachine(name)
self.name = name
self.data = None

@vm_is_turn_on
def create_data(self):
self.data = VmData(
ip=self.vm.network.get_ip(),
user=self.vm.get_logged_user(),
name=self.name,
local_dir=self.vm.get_parameter('CfgFile')
)

def run(self, headless: bool = True, status_bar: bool = False, timeout: int = 600):
if self.vm.power_status():
self.vm.stop()

self.vm.snapshot.restore()
self.configurate()
self.vm.run(headless=headless)
self.vm.network.wait_up(status_bar=status_bar, timeout=timeout)
self.vm.wait_logged_user(status_bar=status_bar, timeout=timeout)
self.create_data()

def configurate(self):
self.vm.set_cpus(self.vm_config.cpus)
self.vm.nested_virtualization(self.vm_config.nested_virtualization)
self.vm.set_memory(self.vm_config.memory)
self.vm.audio(self.vm_config.audio)
self.vm.speculative_execution_control(self.vm_config.speculative_execution_control)

def stop(self):
self.vm.stop()
5 changes: 5 additions & 0 deletions frameworks/VboxMachine/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from .VboxMachine import VboxMachine
from .configs import VmConfig

__all__ = [VboxMachine, VmConfig]
4 changes: 4 additions & 0 deletions frameworks/VboxMachine/configs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from .vm_config import VmConfig

__all__ = [VmConfig]
64 changes: 64 additions & 0 deletions frameworks/VboxMachine/configs/vm_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
import json
from rich import print
from pydantic import BaseModel, conint
from host_tools import singleton


class SystemConfigModel(BaseModel):
"""
A Pydantic model for validating the system configuration parameters.

Attributes:
cpus (int): The number of CPUs allocated for the system. Must be an integer >= 1.
memory (int): The amount of memory in MB. Must be an integer >= 512.
"""

cpus: conint(ge=1)
memory: conint(ge=512)
audio: bool
nested_virtualization: bool
speculative_execution_control: bool


@singleton
class VmConfig:
"""
Configuration class for system settings.

Attributes:
cpus (int): The number of CPUs allocated for the system.
memory (int): The amount of memory in MB.
"""
def __init__(self, config_path: str):
self.config_path = config_path
self._config = self._load_config(self.config_path)
self.cpus = self._config.cpus
self.memory = self._config.memory
self.audio = self._config.audio
self.nested_virtualization = self._config.nested_virtualization
self.speculative_execution_control = self._config.speculative_execution_control

@staticmethod
def _load_config(file_path: str) -> SystemConfigModel:
"""
Loads the system configuration from a JSON file and returns a SystemConfigModel instance.

:param file_path: The path to the configuration JSON file.
:return: An instance of SystemConfigModel containing the loaded configuration.
"""
with open(file_path, 'r') as f:
return SystemConfigModel(**json.load(f))

def display_config(self):
"""
Displays the loaded system configuration.
"""
print(
f"[green]|INFO| System Configuration:\n"
f" CPUs: {self.cpus}\n"
f" Memory: {self.memory}MB\n"
f" Audio Enabled: {self.audio}\n"
f" Nested Virtualization: {self.nested_virtualization}\n"
f" Speculative Execution Control: {self.speculative_execution_control}"
)
10 changes: 10 additions & 0 deletions frameworks/VboxMachine/vm_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from dataclasses import dataclass


@dataclass
class VmData:
user: str
ip: str
name: str
local_dir: str
11 changes: 11 additions & 0 deletions frameworks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from .VboxMachine import VboxMachine, VmConfig
from .console import MyConsole
from .report import Report

__all__ = [
VboxMachine,
MyConsole,
Report,
VmConfig
]
4 changes: 3 additions & 1 deletion frameworks/decorators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
from .decorators import *
from .decorators import vm_data_created, vm_is_turn_on, retry

__all__ = ['vm_data_created', 'vm_is_turn_on', 'retry']
83 changes: 20 additions & 63 deletions frameworks/decorators/decorators.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# -*- coding: utf-8 -*-
from functools import wraps
from multiprocessing import Process
from time import perf_counter, sleep
from time import sleep

from VBoxWrapper import VirtualMachinException
from rich import print


def singleton(class_):
__instances = {}
def vm_data_created(method):
@wraps(method)
def wrapper(self, *args, **kwargs):

@wraps(class_)
def getinstance(*args, **kwargs):
if class_ not in __instances:
__instances[class_] = class_(*args, **kwargs)
return __instances[class_]
if self.vm.data is None:
raise VirtualMachinException("Vm data has not been created, Please start the VM before creating data.")

return getinstance
return method(self, *args, **kwargs)

return wrapper

def vm_is_turn_on(method):
@wraps(method)
def wrapper(self, *args, **kwargs):

if not self.vm.power_status():
raise VirtualMachinException("Virtual machine is not turned on. Please start the VM before creating data.")

return method(self, *args, **kwargs)

return wrapper

def retry(
max_attempts: int = 3,
Expand Down Expand Up @@ -44,56 +54,3 @@ def inner(*args, **kwargs):
return inner

return wrapper


def highlighter(color: str = 'green'):
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
print(f'[{color}]-' * 90)
result = func(*args, **kwargs)
print(f'[{color}]-' * 90)
return result

return inner

return wrapper


def async_processing(target):
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
process = Process(target=target)
process.start()
result = func(*args, **kwargs)
process.terminate()
return result

return inner

return wrapper


def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = perf_counter()
result = func(*args, **kwargs)
print(f"[green]|INFO| Time existing the function `{func.__name__}`: {(perf_counter() - start_time):.02f}")
return result

return wrapper


def memoize(func):
__cache = {}

@wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(kwargs.items()))
if key not in __cache:
__cache[key] = func(*args, **kwargs)
return __cache[key]

return wrapper
2 changes: 2 additions & 0 deletions frameworks/report/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
from .report import Report

__all__ = [Report]
19 changes: 10 additions & 9 deletions frameworks/report/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,24 @@ def value_count(df: pd.DataFrame, column_name: str) -> str:

def insert_column(self, path: str, location: str, column_name: str, value: str, delimiter='\t') -> pd.DataFrame:
df = self.read(path, delimiter=delimiter)
if column_name not in df.columns:
if column_name not in df.columns:
df.insert(loc=df.columns.get_loc(location), column=column_name, value=value)
else:
print(f"[green]|INFO| Column `{column_name}` already exists in `{path}`")
print(f"[cyan]|INFO| Column `{column_name}` already exists in `{path}`")
return df

def merge(self, reports: list, result_csv_path: str, delimiter='\t') -> str | None:
if reports:
merge_reports = []
for csv_ in reports:
if isfile(csv_):
report = self.read(csv_, delimiter)
if report is not None:
merge_reports.append(report)
merge_reports = [
self.read(csv_, delimiter)
for csv_ in reports
if isfile(csv_) and self.read(csv_, delimiter) is not None
]

if merge_reports:
df = pd.concat(merge_reports, ignore_index=True)
df.to_csv(result_csv_path, index=False, sep=delimiter)
return result_csv_path

print('[green]|INFO| No files to merge')

@staticmethod
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ host-tools = { git = "https://github.com/l8556/host_tools.git", branch = "master
telegram = { git = "https://github.com/l8556/TelegramApi.git", branch = "master" }
VBoxWrapper = { git = "https://github.com/l8556/VBoxWrapper.git", branch = "master" }
ssh-wrapper = { git = "https://github.com/l8556/SshWrapper.git", branch = "master" }
ruff = "^0.6.4"
pydantic = "^2.9.1"


[build-system]
Expand Down
20 changes: 5 additions & 15 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,18 @@
from rich import print

from VBoxWrapper import VirtualMachine, Vbox
from tests.data import TestData
from tests.desktop_tests import DesktopTest
import tests.multiprocessing as multiprocess
from frameworks.console import MyConsole
from tests.tools.desktop_report import DesktopReport
from tests.desktop_tests.tools.test_data import TestData
from tests.desktop_tests import DesktopTest, DesktopReport
import tests.desktop_tests.multiprocessing as multiprocess
from host_tools import Process, Service
from elevate import elevate

console = MyConsole().console
print = console.print


@task
def desktop_test(
c,
version=None,
update_from=None,
update_from_version=None,
name=None,
processes=None,
detailed_telegram=False,
Expand All @@ -34,7 +29,7 @@ def desktop_test(

data = TestData(
version=version if version else Prompt.ask('[red]Please enter version'),
update_from=update_from,
update_from=update_from_version,
telegram=detailed_telegram,
config_path=join(getcwd(), 'custom_config.json') if custom_config else join(getcwd(), 'config.json'),
custom_config_mode=custom_config
Expand All @@ -51,7 +46,6 @@ def desktop_test(
report.get_full(data.version)
report.send_to_tg(data.version, data.title, data.tg_token, data.tg_chat_id, data.update_from) if not name else ...


@task
def run_vm(c, name: str = '', headless=False):
vm = VirtualMachine(Vbox().check_vm_names(name))
Expand All @@ -60,7 +54,6 @@ def run_vm(c, name: str = '', headless=False):
vm.wait_logged_user(status_bar=True)
return print(f"[green]ip: [red]{vm.network.get_ip()}[/]\nuser: [red]{vm.get_logged_user()}[/]")


@task
def stop_vm(c, name: str = None, group_name: str = None):
if name:
Expand All @@ -78,19 +71,16 @@ def stop_vm(c, name: str = None, group_name: str = None):
print(f"[green]|INFO| Shutting down the virtual machine: [red]{vm_info[0]}[/]")
virtualmachine.stop()


@task
def vm_list(c, group_name: str = None):
vm_names = Vbox().vm_list(group_name)
print(vm_names)
return vm_names


@task
def out_info(c, name: str = '', full: bool = False):
print(VirtualMachine(Vbox().check_vm_names(name)).get_info(full=full))


@task
def group_list(c):
group_names = Vbox().get_group_list()
Expand Down
Loading
Loading