mirror of
https://github.com/dw-0/kiauh.git
synced 2025-12-23 15:53:36 +05:00
Compare commits
11 Commits
142b4498a3
...
ad0dbf63b8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0dbf63b8 | ||
|
|
9dedf38079 | ||
|
|
1b4c76d080 | ||
|
|
d20d82aeac | ||
|
|
16a28ffda0 | ||
|
|
a9367cc064 | ||
|
|
b165d88855 | ||
|
|
6c59d58193 | ||
|
|
b4f5c3c1ac | ||
|
|
b69ecbc9b5 | ||
|
|
fc9fa39eee |
@@ -11,7 +11,7 @@
|
||||
|
||||
from abc import abstractmethod, ABC
|
||||
from pathlib import Path
|
||||
from typing import List, Union, Optional, Type, TypeVar
|
||||
from typing import List, Type, TypeVar
|
||||
|
||||
from kiauh.utils.constants import SYSTEMD, CURRENT_USER
|
||||
|
||||
@@ -25,7 +25,7 @@ class BaseInstance(ABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
suffix: Optional[str],
|
||||
suffix: str,
|
||||
instance_type: B = B,
|
||||
):
|
||||
self._instance_type = instance_type
|
||||
@@ -52,7 +52,7 @@ class BaseInstance(ABC):
|
||||
return self._suffix
|
||||
|
||||
@suffix.setter
|
||||
def suffix(self, value: Union[str, None]) -> None:
|
||||
def suffix(self, value: str) -> None:
|
||||
self._suffix = value
|
||||
|
||||
@property
|
||||
@@ -144,7 +144,7 @@ class BaseInstance(ABC):
|
||||
|
||||
def get_service_file_name(self, extension: bool = False) -> str:
|
||||
name = f"{self.__class__.__name__.lower()}"
|
||||
if self.suffix is not None:
|
||||
if self.suffix != "":
|
||||
name += f"-{self.suffix}"
|
||||
|
||||
return name if not extension else f"{name}.service"
|
||||
@@ -153,7 +153,7 @@ class BaseInstance(ABC):
|
||||
return SYSTEMD.joinpath(self.get_service_file_name(extension=True))
|
||||
|
||||
def get_data_dir_name_from_suffix(self) -> str:
|
||||
if self._suffix is None:
|
||||
if self._suffix == "":
|
||||
return "printer"
|
||||
elif self._suffix.isdigit():
|
||||
return f"printer_{self._suffix}"
|
||||
|
||||
@@ -207,10 +207,8 @@ class InstanceManager:
|
||||
|
||||
return instance_list
|
||||
|
||||
def _get_instance_suffix(self, file_path: Path) -> Union[str, None]:
|
||||
full_name = file_path.name.split(".")[0]
|
||||
|
||||
return full_name.split("-")[-1] if "-" in full_name else None
|
||||
def _get_instance_suffix(self, file_path: Path) -> str:
|
||||
return file_path.stem.split("-")[-1] if "-" in file_path.stem else ""
|
||||
|
||||
def _sort_instance_list(self, s: Union[int, str, None]):
|
||||
if s is None:
|
||||
|
||||
8
kiauh/core/instance_manager/name_scheme.py
Normal file
8
kiauh/core/instance_manager/name_scheme.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from enum import unique, Enum
|
||||
|
||||
|
||||
@unique
|
||||
class NameScheme(Enum):
|
||||
SINGLE = "SINGLE"
|
||||
INDEX = "INDEX"
|
||||
CUSTOM = "CUSTOM"
|
||||
@@ -9,10 +9,9 @@
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||
# ======================================================================= #
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
from typing import List
|
||||
|
||||
from kiauh.core.instance_manager.base_instance import BaseInstance
|
||||
from kiauh.modules.klipper import KLIPPER_DIR, KLIPPER_ENV_DIR, MODULE_PATH
|
||||
@@ -26,11 +25,11 @@ class Klipper(BaseInstance):
|
||||
def blacklist(cls) -> List[str]:
|
||||
return ["None", "mcu"]
|
||||
|
||||
def __init__(self, suffix: str = None):
|
||||
def __init__(self, suffix: str = ""):
|
||||
super().__init__(instance_type=self, suffix=suffix)
|
||||
self.klipper_dir: Path = KLIPPER_DIR
|
||||
self.env_dir: Path = KLIPPER_ENV_DIR
|
||||
self._cfg_file = self._get_cfg()
|
||||
self._cfg_file = self.cfg_dir.joinpath("printer.cfg")
|
||||
self._log = self.log_dir.joinpath("klippy.log")
|
||||
self._serial = self.comms_dir.joinpath("klippy.serial")
|
||||
self._uds = self.comms_dir.joinpath("klippy.sock")
|
||||
@@ -153,9 +152,3 @@ class Klipper(BaseInstance):
|
||||
env_file_content = env_file_content.replace("%LOG%", str(self.log))
|
||||
env_file_content = env_file_content.replace("%UDS%", str(self.uds))
|
||||
return env_file_content
|
||||
|
||||
def _get_cfg(self) -> Union[Path, None]:
|
||||
cfg_file_loc = self.cfg_dir.joinpath("printer.cfg")
|
||||
if cfg_file_loc.is_file():
|
||||
return cfg_file_loc
|
||||
return None
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
# ======================================================================= #
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
from typing import List
|
||||
|
||||
from kiauh import KIAUH_CFG
|
||||
from kiauh.core.backup_manager.backup_manager import BackupManager
|
||||
from kiauh.core.config_manager.config_manager import ConfigManager
|
||||
from kiauh.core.instance_manager.instance_manager import InstanceManager
|
||||
from kiauh.core.instance_manager.name_scheme import NameScheme
|
||||
from kiauh.modules.klipper import (
|
||||
EXIT_KLIPPER_SETUP,
|
||||
DEFAULT_KLIPPER_REPO_URL,
|
||||
@@ -25,20 +26,21 @@ from kiauh.modules.klipper import (
|
||||
)
|
||||
from kiauh.modules.klipper.klipper import Klipper
|
||||
from kiauh.modules.klipper.klipper_dialogs import (
|
||||
print_instance_overview,
|
||||
print_select_instance_count_dialog,
|
||||
print_update_warn_dialog,
|
||||
print_select_custom_name_dialog,
|
||||
)
|
||||
from kiauh.modules.klipper.klipper_utils import (
|
||||
handle_convert_single_to_multi_instance_names,
|
||||
handle_new_multi_instance_names,
|
||||
handle_existing_multi_instance_names,
|
||||
handle_disruptive_system_packages,
|
||||
check_user_groups,
|
||||
handle_single_to_multi_conversion,
|
||||
handle_to_multi_instance_conversion,
|
||||
create_example_printer_cfg,
|
||||
detect_name_scheme,
|
||||
add_to_existing,
|
||||
get_install_count,
|
||||
assign_custom_name,
|
||||
)
|
||||
from kiauh.core.repo_manager.repo_manager import RepoManager
|
||||
from kiauh.modules.moonraker.moonraker import Moonraker
|
||||
from kiauh.utils.input_utils import get_confirm, get_number_input
|
||||
from kiauh.utils.logger import Logger
|
||||
from kiauh.utils.system_utils import (
|
||||
@@ -50,51 +52,82 @@ from kiauh.utils.system_utils import (
|
||||
)
|
||||
|
||||
|
||||
# TODO: this method needs refactoring! (but it works for now)
|
||||
def install_klipper() -> None:
|
||||
im = InstanceManager(Klipper)
|
||||
kl_instances: List[Klipper] = im.instances
|
||||
|
||||
add_additional = handle_existing_instances(im.instances)
|
||||
if len(im.instances) > 0 and not add_additional:
|
||||
# ask to add new instances, if there are existing ones
|
||||
if kl_instances and not add_to_existing():
|
||||
Logger.print_status(EXIT_KLIPPER_SETUP)
|
||||
return
|
||||
|
||||
print_select_instance_count_dialog()
|
||||
question = f"Number of{' additional' if len(im.instances) > 0 else ''} Klipper instances to set up"
|
||||
install_count = get_number_input(question, 1, default=1, allow_go_back=True)
|
||||
install_count = get_install_count()
|
||||
# install_count = None -> user entered "b" to go back
|
||||
if install_count is None:
|
||||
Logger.print_status(EXIT_KLIPPER_SETUP)
|
||||
return
|
||||
|
||||
instance_names = set_instance_suffix(im.instances, install_count)
|
||||
if instance_names is None:
|
||||
Logger.print_status(EXIT_KLIPPER_SETUP)
|
||||
return
|
||||
# create a dict of the size of the existing instances + install count
|
||||
name_scheme = NameScheme.SINGLE
|
||||
single_to_multi = len(kl_instances) == 1 and kl_instances[0].suffix == ""
|
||||
name_dict = {c: "" for c in range(len(kl_instances) + install_count)}
|
||||
|
||||
if (not kl_instances and install_count > 1) or single_to_multi:
|
||||
print_select_custom_name_dialog()
|
||||
if get_confirm("Assign custom names?", False, allow_go_back=True):
|
||||
name_scheme = NameScheme.CUSTOM
|
||||
else:
|
||||
name_scheme = NameScheme.INDEX
|
||||
|
||||
# if there are more moonraker instances installed than klipper, we
|
||||
# load their names into the name_dict, as we will detect and enforce that naming scheme
|
||||
mr_instances: List[Moonraker] = InstanceManager(Moonraker).instances
|
||||
if len(mr_instances) > len(kl_instances):
|
||||
for k, v in enumerate(mr_instances):
|
||||
name_dict[k] = v.suffix
|
||||
name_scheme = detect_name_scheme(mr_instances)
|
||||
elif len(kl_instances) > 1:
|
||||
for k, v in enumerate(kl_instances):
|
||||
name_dict[k] = v.suffix
|
||||
name_scheme = detect_name_scheme(kl_instances)
|
||||
|
||||
# set instance names if multiple instances will be created
|
||||
if name_scheme != NameScheme.SINGLE:
|
||||
for k in name_dict:
|
||||
if name_dict[k] == "" and name_scheme == NameScheme.INDEX:
|
||||
name_dict[k] = str(k + 1)
|
||||
elif name_dict[k] == "" and name_scheme == NameScheme.CUSTOM:
|
||||
assign_custom_name(k, name_dict)
|
||||
|
||||
create_example_cfg = get_confirm("Create example printer.cfg?")
|
||||
|
||||
if len(im.instances) < 1:
|
||||
if not kl_instances:
|
||||
setup_klipper_prerequesites()
|
||||
|
||||
convert_single_to_multi = (
|
||||
len(im.instances) == 1 and im.instances[0].suffix is None and install_count >= 1
|
||||
)
|
||||
|
||||
for name in instance_names:
|
||||
if convert_single_to_multi:
|
||||
current_instance = handle_single_to_multi_conversion(im, name)
|
||||
convert_single_to_multi = False
|
||||
count = 0
|
||||
for name in name_dict:
|
||||
if name_dict[name] in [n.suffix for n in kl_instances]:
|
||||
continue
|
||||
else:
|
||||
current_instance = Klipper(suffix=name)
|
||||
count += 1
|
||||
|
||||
im.current_instance = current_instance
|
||||
if single_to_multi:
|
||||
handle_to_multi_instance_conversion(name_dict[name])
|
||||
single_to_multi = False
|
||||
count -= 1
|
||||
else:
|
||||
new_instance = Klipper(suffix=name_dict[name])
|
||||
im.current_instance = new_instance
|
||||
im.create_instance()
|
||||
im.enable_instance()
|
||||
|
||||
if create_example_cfg:
|
||||
create_example_printer_cfg(current_instance)
|
||||
|
||||
create_example_printer_cfg(new_instance)
|
||||
im.start_instance()
|
||||
|
||||
if count == install_count:
|
||||
break
|
||||
|
||||
im.reload_daemon()
|
||||
|
||||
# step 4: check/handle conflicting packages/services
|
||||
@@ -137,41 +170,6 @@ def install_klipper_packages(klipper_dir: Path) -> None:
|
||||
install_system_packages(packages)
|
||||
|
||||
|
||||
def handle_existing_instances(instance_list: List[Klipper]) -> bool:
|
||||
instance_count = len(instance_list)
|
||||
|
||||
if instance_count > 0:
|
||||
print_instance_overview(instance_list)
|
||||
if not get_confirm("Add new instances?", allow_go_back=True):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def set_instance_suffix(
|
||||
instance_list: List[Klipper], install_count: int
|
||||
) -> List[Union[str, None]]:
|
||||
instance_count = len(instance_list)
|
||||
|
||||
# new single instance install
|
||||
if instance_count == 0 and install_count == 1:
|
||||
return [None]
|
||||
|
||||
# convert single instance install to multi install
|
||||
elif instance_count == 1 and install_count >= 1 and instance_list[0].suffix is None:
|
||||
return handle_convert_single_to_multi_instance_names(install_count)
|
||||
|
||||
# new multi instance install
|
||||
elif instance_count == 0 and install_count > 1:
|
||||
return handle_new_multi_instance_names(instance_count, install_count)
|
||||
|
||||
# existing multi instance install
|
||||
elif instance_count > 1:
|
||||
return handle_existing_multi_instance_names(
|
||||
instance_count, install_count, instance_list
|
||||
)
|
||||
|
||||
|
||||
def update_klipper() -> None:
|
||||
print_update_warn_dialog()
|
||||
if not get_confirm("Update Klipper now?"):
|
||||
|
||||
@@ -20,16 +20,20 @@ from pathlib import Path
|
||||
from typing import List, Union, Literal, Dict
|
||||
|
||||
from kiauh.core.config_manager.config_manager import ConfigManager
|
||||
from kiauh.core.instance_manager.base_instance import BaseInstance
|
||||
from kiauh.core.instance_manager.instance_manager import InstanceManager
|
||||
from kiauh.core.instance_manager.name_scheme import NameScheme
|
||||
from kiauh.modules.klipper import MODULE_PATH, KLIPPER_DIR, KLIPPER_ENV_DIR
|
||||
from kiauh.modules.klipper.klipper import Klipper
|
||||
from kiauh.modules.klipper.klipper_dialogs import (
|
||||
print_missing_usergroup_dialog,
|
||||
print_select_custom_name_dialog,
|
||||
print_instance_overview,
|
||||
print_select_instance_count_dialog,
|
||||
)
|
||||
from kiauh.modules.moonraker.moonraker_utils import moonraker_to_multi_conversion
|
||||
from kiauh.utils.common import get_install_status_common, get_repo_name
|
||||
from kiauh.utils.constants import CURRENT_USER
|
||||
from kiauh.utils.input_utils import get_confirm, get_string_input
|
||||
from kiauh.utils.input_utils import get_confirm, get_string_input, get_number_input
|
||||
from kiauh.utils.logger import Logger
|
||||
from kiauh.utils.system_utils import mask_system_service
|
||||
|
||||
@@ -41,84 +45,57 @@ def get_klipper_status() -> Dict[Literal["status", "repo"], str]:
|
||||
}
|
||||
|
||||
|
||||
def assign_custom_names(
|
||||
instance_count: int, install_count: int, instance_list: List[Klipper] = None
|
||||
) -> List[str]:
|
||||
instance_names = []
|
||||
exclude = Klipper.blacklist()
|
||||
|
||||
# if an instance_list is provided, exclude all existing instance suffixes
|
||||
if instance_list is not None:
|
||||
for instance in instance_list:
|
||||
exclude.append(instance.suffix)
|
||||
|
||||
for i in range(instance_count + install_count):
|
||||
question = f"Enter name for instance {i + 1}"
|
||||
name = get_string_input(question, exclude=exclude)
|
||||
instance_names.append(name)
|
||||
exclude.append(name)
|
||||
|
||||
return instance_names
|
||||
def add_to_existing() -> bool:
|
||||
kl_instances = InstanceManager(Klipper).instances
|
||||
print_instance_overview(kl_instances)
|
||||
return get_confirm("Add new instances?", allow_go_back=True)
|
||||
|
||||
|
||||
def handle_convert_single_to_multi_instance_names(
|
||||
install_count: int,
|
||||
) -> Union[List[str], None]:
|
||||
print_select_custom_name_dialog()
|
||||
choice = get_confirm("Assign custom names?", False, allow_go_back=True)
|
||||
if choice is True:
|
||||
# instance_count = 0 and install_count + 1 as we want to assign a new name to the existing single install
|
||||
return assign_custom_names(0, install_count + 1)
|
||||
elif choice is False:
|
||||
# "install_count + 2" as we need to account for the existing single install
|
||||
_range = range(1, install_count + 2)
|
||||
return [str(i) for i in _range]
|
||||
|
||||
return None
|
||||
def get_install_count() -> Union[int, None]:
|
||||
kl_instances = InstanceManager(Klipper).instances
|
||||
print_select_instance_count_dialog()
|
||||
question = f"Number of{' additional' if len(kl_instances) > 0 else ''} Klipper instances to set up"
|
||||
return get_number_input(question, 1, default=1, allow_go_back=True)
|
||||
|
||||
|
||||
def handle_new_multi_instance_names(
|
||||
instance_count: int, install_count: int
|
||||
) -> Union[List[str], None]:
|
||||
print_select_custom_name_dialog()
|
||||
choice = get_confirm("Assign custom names?", False, allow_go_back=True)
|
||||
if choice is True:
|
||||
return assign_custom_names(instance_count, install_count)
|
||||
elif choice is False:
|
||||
_range = range(1, install_count + 1)
|
||||
return [str(i) for i in _range]
|
||||
|
||||
return None
|
||||
def assign_custom_name(key: int, name_dict: Dict[int, str]) -> None:
|
||||
existing_names = []
|
||||
existing_names.extend(Klipper.blacklist())
|
||||
existing_names.extend(name_dict[n] for n in name_dict)
|
||||
question = f"Enter name for instance {key + 1}"
|
||||
name_dict[key] = get_string_input(question, exclude=existing_names)
|
||||
|
||||
|
||||
def handle_existing_multi_instance_names(
|
||||
instance_count: int, install_count: int, instance_list: List[Klipper]
|
||||
) -> List[str]:
|
||||
if has_custom_names(instance_list):
|
||||
return assign_custom_names(instance_count, install_count, instance_list)
|
||||
def handle_to_multi_instance_conversion(new_name: str) -> None:
|
||||
Logger.print_status("Converting single instance to multi instances ...")
|
||||
klipper_to_multi_conversion(new_name)
|
||||
moonraker_to_multi_conversion(new_name)
|
||||
|
||||
|
||||
def klipper_to_multi_conversion(new_name: str) -> None:
|
||||
Logger.print_status("Convert Klipper single to multi instance ...")
|
||||
im = InstanceManager(Klipper)
|
||||
im.current_instance = im.instances[0]
|
||||
# temporarily store the data dir path
|
||||
old_data_dir = im.instances[0].data_dir
|
||||
# remove the old single instance
|
||||
im.stop_instance()
|
||||
im.disable_instance()
|
||||
im.delete_instance()
|
||||
# create a new klipper instance with the new name
|
||||
im.current_instance = Klipper(suffix=new_name)
|
||||
new_data_dir: Path = im.current_instance.data_dir
|
||||
|
||||
# rename the old data dir and use it for the new instance
|
||||
Logger.print_status(f"Rename '{old_data_dir}' to '{new_data_dir}' ...")
|
||||
if not new_data_dir.is_dir():
|
||||
old_data_dir.rename(new_data_dir)
|
||||
else:
|
||||
start = get_highest_index(instance_list) + 1
|
||||
_range = range(start, start + install_count)
|
||||
return [str(i) for i in _range]
|
||||
Logger.print_info(f"'{new_data_dir}' already exist. Skipped ...")
|
||||
|
||||
|
||||
def handle_single_to_multi_conversion(
|
||||
instance_manager: InstanceManager, name: str
|
||||
) -> Klipper:
|
||||
instance_list = instance_manager.instances
|
||||
instance_manager.current_instance = instance_list[0]
|
||||
old_data_dir_name = instance_manager.instances[0].data_dir
|
||||
instance_manager.stop_instance()
|
||||
instance_manager.disable_instance()
|
||||
instance_manager.delete_instance(del_remnants=False)
|
||||
instance_manager.current_instance = Klipper(suffix=name)
|
||||
new_data_dir_name = instance_manager.current_instance.data_dir
|
||||
try:
|
||||
Path(old_data_dir_name).rename(new_data_dir_name)
|
||||
return instance_manager.current_instance
|
||||
except OSError as e:
|
||||
log = f"Cannot rename {old_data_dir_name} to {new_data_dir_name}:\n{e}"
|
||||
Logger.print_error(log)
|
||||
im.create_instance()
|
||||
im.enable_instance()
|
||||
im.start_instance()
|
||||
|
||||
|
||||
def check_user_groups():
|
||||
@@ -189,13 +166,13 @@ def handle_disruptive_system_packages() -> None:
|
||||
Logger.print_warn(warn_msg)
|
||||
|
||||
|
||||
def has_custom_names(instance_list: List[Klipper]) -> bool:
|
||||
def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme:
|
||||
pattern = re.compile("^\d+$")
|
||||
for instance in instance_list:
|
||||
if not pattern.match(instance.suffix):
|
||||
return True
|
||||
return NameScheme.CUSTOM
|
||||
|
||||
return False
|
||||
return NameScheme.INDEX
|
||||
|
||||
|
||||
def get_highest_index(instance_list: List[Klipper]) -> int:
|
||||
@@ -205,12 +182,12 @@ def get_highest_index(instance_list: List[Klipper]) -> int:
|
||||
|
||||
def create_example_printer_cfg(instance: Klipper) -> None:
|
||||
Logger.print_status(f"Creating example printer.cfg in '{instance.cfg_dir}'")
|
||||
if instance.cfg_file is not None:
|
||||
Logger.print_info(f"printer.cfg in '{instance.cfg_dir}' already exists.")
|
||||
if instance.cfg_file.is_file():
|
||||
Logger.print_info(f"'{instance.cfg_file}' already exists.")
|
||||
return
|
||||
|
||||
source = MODULE_PATH.joinpath("res/printer.cfg")
|
||||
target = instance.cfg_dir.joinpath("printer.cfg")
|
||||
target = instance.cfg_file
|
||||
try:
|
||||
shutil.copy(source, target)
|
||||
except OSError as e:
|
||||
|
||||
@@ -52,7 +52,7 @@ def run_mainsail_removal(
|
||||
|
||||
def remove_mainsail_dir() -> None:
|
||||
Logger.print_status("Removing Mainsail ...")
|
||||
if not Path(MAINSAIL_DIR).exists():
|
||||
if not MAINSAIL_DIR.exists():
|
||||
Logger.print_info(f"'{MAINSAIL_DIR}' does not exist. Skipping ...")
|
||||
return
|
||||
|
||||
@@ -80,12 +80,13 @@ def remove_nginx_logs() -> None:
|
||||
remove_file(Path("/var/log/nginx/mainsail-error.log"), True)
|
||||
|
||||
im = InstanceManager(Klipper)
|
||||
if not im.instances:
|
||||
instances: List[Klipper] = im.instances
|
||||
if not instances:
|
||||
return
|
||||
|
||||
for instance in im.instances:
|
||||
remove_file(Path(instance.log_dir, "mainsail-access.log"))
|
||||
remove_file(Path(instance.log_dir, "mainsail-error.log"))
|
||||
for instance in instances:
|
||||
remove_file(instance.log_dir.joinpath("mainsail-access.log"))
|
||||
remove_file(instance.log_dir.joinpath("mainsail-error.log"))
|
||||
|
||||
except (OSError, subprocess.CalledProcessError) as e:
|
||||
Logger.print_error(f"Unable to NGINX logs:\n{e}")
|
||||
@@ -94,16 +95,16 @@ def remove_nginx_logs() -> None:
|
||||
def remove_updater_section(name: str) -> None:
|
||||
Logger.print_status("Remove updater section from moonraker.conf ...")
|
||||
im = InstanceManager(Moonraker)
|
||||
if not im.instances:
|
||||
Logger.print_info("Moonraker not installed. Skipping ...")
|
||||
instances: List[Moonraker] = im.instances
|
||||
if not instances:
|
||||
Logger.print_info("Moonraker not installed. Skipped ...")
|
||||
return
|
||||
|
||||
for instance in im.instances:
|
||||
log = f"Remove section '{name}' in '{instance.cfg_file}' ..."
|
||||
Logger.print_status(log)
|
||||
for instance in instances:
|
||||
Logger.print_status(f"Remove section '{name}' in '{instance.cfg_file}' ...")
|
||||
|
||||
if not Path(instance.cfg_file).exists():
|
||||
Logger.print_info("Section not present. Skipping ...")
|
||||
if not instance.cfg_file.is_file():
|
||||
Logger.print_info(f"'{instance.cfg_file}' does not exist. Skipped ...")
|
||||
continue
|
||||
|
||||
cm = ConfigManager(instance.cfg_file)
|
||||
@@ -117,7 +118,7 @@ def remove_updater_section(name: str) -> None:
|
||||
|
||||
def remove_mainsail_cfg_dir() -> None:
|
||||
Logger.print_status("Removing mainsail-config ...")
|
||||
if not Path(MAINSAIL_CONFIG_DIR).exists():
|
||||
if not MAINSAIL_CONFIG_DIR.exists():
|
||||
Logger.print_info(f"'{MAINSAIL_CONFIG_DIR}' does not exist. Skipping ...")
|
||||
return
|
||||
|
||||
@@ -129,11 +130,12 @@ def remove_mainsail_cfg_dir() -> None:
|
||||
|
||||
def remove_mainsail_cfg_symlink() -> None:
|
||||
Logger.print_status("Removing mainsail.cfg symlinks ...")
|
||||
im = InstanceManager(Moonraker)
|
||||
for instance in im.instances:
|
||||
Logger.print_status(f"Removing symlink from '{instance.cfg_dir}' ...")
|
||||
im = InstanceManager(Klipper)
|
||||
instances: List[Klipper] = im.instances
|
||||
for instance in instances:
|
||||
Logger.print_status(f"Removing symlink from '{instance.cfg_file}' ...")
|
||||
try:
|
||||
remove_file(Path(instance.cfg_dir, "mainsail.cfg"))
|
||||
remove_file(instance.cfg_dir.joinpath("mainsail.cfg"))
|
||||
except subprocess.CalledProcessError:
|
||||
Logger.print_error("Failed to remove symlink!")
|
||||
|
||||
@@ -141,15 +143,16 @@ def remove_mainsail_cfg_symlink() -> None:
|
||||
def remove_printer_cfg_include() -> None:
|
||||
Logger.print_status("Remove mainsail-config include from printer.cfg ...")
|
||||
im = InstanceManager(Klipper)
|
||||
if not im.instances:
|
||||
instances: List[Klipper] = im.instances
|
||||
if not instances:
|
||||
Logger.print_info("Klipper not installed. Skipping ...")
|
||||
return
|
||||
|
||||
for instance in im.instances:
|
||||
for instance in instances:
|
||||
log = f"Removing include from '{instance.cfg_file}' ..."
|
||||
Logger.print_status(log)
|
||||
|
||||
if not Path(instance.cfg_file).exists():
|
||||
if not instance.cfg_file.is_file():
|
||||
continue
|
||||
|
||||
cm = ConfigManager(instance.cfg_file)
|
||||
|
||||
@@ -59,21 +59,22 @@ from kiauh.utils.system_utils import (
|
||||
|
||||
|
||||
def run_mainsail_installation() -> None:
|
||||
im_mr = InstanceManager(Moonraker)
|
||||
is_moonraker_installed = len(im_mr.instances) > 0
|
||||
mr_im = InstanceManager(Moonraker)
|
||||
mr_instances: List[Moonraker] = mr_im.instances
|
||||
|
||||
enable_remotemode = False
|
||||
if not is_moonraker_installed:
|
||||
if not mr_instances:
|
||||
print_moonraker_not_found_dialog()
|
||||
do_continue = get_confirm("Continue Mainsail installation?", allow_go_back=True)
|
||||
if do_continue:
|
||||
enable_remotemode = True
|
||||
else:
|
||||
if not get_confirm("Continue Mainsail installation?", allow_go_back=True):
|
||||
return
|
||||
|
||||
is_mainsail_installed = Path.home().joinpath("mainsail").exists()
|
||||
# if moonraker is not installed or multiple instances
|
||||
# are installed we enable mainsails remote mode
|
||||
if not mr_instances or len(mr_instances) > 1:
|
||||
enable_remotemode = True
|
||||
|
||||
do_reinstall = False
|
||||
if is_mainsail_installed:
|
||||
if Path.home().joinpath("mainsail").exists():
|
||||
print_mainsail_already_installed_dialog()
|
||||
do_reinstall = get_confirm("Re-install Mainsail?", allow_go_back=True)
|
||||
if do_reinstall:
|
||||
@@ -81,10 +82,10 @@ def run_mainsail_installation() -> None:
|
||||
else:
|
||||
return
|
||||
|
||||
im_kl = InstanceManager(Klipper)
|
||||
is_klipper_installed = len(im_kl.instances) > 0
|
||||
kl_im = InstanceManager(Klipper)
|
||||
kl_instances = kl_im.instances
|
||||
install_ms_config = False
|
||||
if is_klipper_installed:
|
||||
if kl_instances:
|
||||
print_install_mainsail_config_dialog()
|
||||
question = "Download the recommended macros?"
|
||||
install_ms_config = get_confirm(question, allow_go_back=False)
|
||||
@@ -108,31 +109,31 @@ def run_mainsail_installation() -> None:
|
||||
restore_config_json()
|
||||
if enable_remotemode:
|
||||
enable_mainsail_remotemode()
|
||||
if is_moonraker_installed:
|
||||
if mr_instances:
|
||||
patch_moonraker_conf(
|
||||
im_mr.instances,
|
||||
mr_instances,
|
||||
"Mainsail",
|
||||
"update_manager mainsail",
|
||||
"mainsail-updater.conf",
|
||||
)
|
||||
im_mr.restart_all_instance()
|
||||
if install_ms_config and is_klipper_installed:
|
||||
mr_im.restart_all_instance()
|
||||
if install_ms_config and kl_instances:
|
||||
download_mainsail_cfg()
|
||||
create_mainsail_cfg_symlink(im_kl.instances)
|
||||
create_mainsail_cfg_symlink(kl_instances)
|
||||
patch_moonraker_conf(
|
||||
im_mr.instances,
|
||||
mr_instances,
|
||||
"mainsail-config",
|
||||
"update_manager mainsail-config",
|
||||
"mainsail-config-updater.conf",
|
||||
)
|
||||
patch_printer_config(im_kl.instances)
|
||||
im_kl.restart_all_instance()
|
||||
patch_printer_config(kl_instances)
|
||||
kl_im.restart_all_instance()
|
||||
|
||||
copy_upstream_nginx_cfg()
|
||||
copy_common_vars_nginx_cfg()
|
||||
create_mainsail_nginx_cfg(mainsail_port)
|
||||
if is_klipper_installed:
|
||||
symlink_webui_nginx_log(im_kl.instances)
|
||||
if kl_instances:
|
||||
symlink_webui_nginx_log(kl_instances)
|
||||
control_systemd_service("nginx", "restart")
|
||||
|
||||
except Exception as e:
|
||||
@@ -147,11 +148,13 @@ def run_mainsail_installation() -> None:
|
||||
def download_mainsail() -> None:
|
||||
try:
|
||||
Logger.print_status("Downloading Mainsail ...")
|
||||
download_file(MAINSAIL_URL, Path.home(), "mainsail.zip")
|
||||
target = Path.home().joinpath("mainsail.zip")
|
||||
download_file(MAINSAIL_URL, target, True)
|
||||
Logger.print_ok("Download complete!")
|
||||
|
||||
Logger.print_status("Extracting mainsail.zip ...")
|
||||
unzip(Path.home().joinpath("mainsail.zip"), MAINSAIL_DIR)
|
||||
target.unlink(missing_ok=True)
|
||||
Logger.print_ok("OK!")
|
||||
|
||||
except Exception:
|
||||
|
||||
@@ -57,17 +57,17 @@ class MainsailRemoveMenu(BaseMenu):
|
||||
| Enter a number and hit enter to select / deselect |
|
||||
| the specific option for removal. |
|
||||
|-------------------------------------------------------|
|
||||
| 0) Everything |
|
||||
| 0) Select everything |
|
||||
|-------------------------------------------------------|
|
||||
| 1) {o1} Remove Mainsail Web UI |
|
||||
| 1) {o1} Remove Mainsail |
|
||||
| 2) {o2} Remove mainsail-config |
|
||||
| 3) {o3} Backup Mainsail config.json |
|
||||
| 3) {o3} Backup config.json |
|
||||
| |
|
||||
| printer.cfg & moonraker.conf |
|
||||
| 4) {o4} Remove Moonraker updater section |
|
||||
| 4) {o4} Remove Moonraker update section |
|
||||
| 5) {o5} Remove printer.cfg include |
|
||||
|-------------------------------------------------------|
|
||||
| 6) Start removal |
|
||||
| 6) Continue |
|
||||
"""
|
||||
)[1:]
|
||||
print(menu, end="")
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||
# ======================================================================= #
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
@@ -27,11 +26,11 @@ class Moonraker(BaseInstance):
|
||||
def blacklist(cls) -> List[str]:
|
||||
return ["None", "mcu"]
|
||||
|
||||
def __init__(self, suffix: str = None):
|
||||
def __init__(self, suffix: str = ""):
|
||||
super().__init__(instance_type=self, suffix=suffix)
|
||||
self.moonraker_dir: Path = MOONRAKER_DIR
|
||||
self.env_dir: Path = MOONRAKER_ENV_DIR
|
||||
self.cfg_file = self._get_cfg()
|
||||
self.cfg_file = self.cfg_dir.joinpath("moonraker.conf")
|
||||
self.port = self._get_port()
|
||||
self.backup_dir = self.data_dir.joinpath("backup")
|
||||
self.certs_dir = self.data_dir.joinpath("certs")
|
||||
@@ -138,14 +137,8 @@ class Moonraker(BaseInstance):
|
||||
)
|
||||
return env_file_content
|
||||
|
||||
def _get_cfg(self) -> Union[Path, None]:
|
||||
cfg_file_loc = self.cfg_dir.joinpath("moonraker.conf")
|
||||
if cfg_file_loc.is_file():
|
||||
return cfg_file_loc
|
||||
return None
|
||||
|
||||
def _get_port(self) -> Union[int, None]:
|
||||
if self.cfg_file is None:
|
||||
if not self.cfg_file.is_file():
|
||||
return None
|
||||
|
||||
cm = ConfigManager(cfg_file=self.cfg_file)
|
||||
|
||||
@@ -54,21 +54,25 @@ from kiauh.utils.system_utils import (
|
||||
|
||||
|
||||
def check_moonraker_install_requirements() -> bool:
|
||||
kl_im = InstanceManager(Klipper)
|
||||
kl_instance_list = kl_im.instances
|
||||
kl_instance_count = len(kl_instance_list)
|
||||
|
||||
if not (sys.version_info.major >= 3 and sys.version_info.minor >= 7):
|
||||
Logger.print_error("Versioncheck failed!")
|
||||
Logger.print_error("Python 3.7 or newer required to run Moonraker.")
|
||||
return False
|
||||
|
||||
is_klipper_installed = kl_instance_count > 0
|
||||
if not is_klipper_installed:
|
||||
kl_instance_count = len(InstanceManager(Klipper).instances)
|
||||
if kl_instance_count < 1:
|
||||
Logger.print_warn("Klipper not installed!")
|
||||
Logger.print_warn("Moonraker cannot be installed! Install Klipper first.")
|
||||
return False
|
||||
|
||||
mr_instance_count = len(InstanceManager(Moonraker).instances)
|
||||
if mr_instance_count >= kl_instance_count:
|
||||
Logger.print_warn("Unable to install more Moonraker instances!")
|
||||
Logger.print_warn("More Klipper instances required.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def install_moonraker() -> None:
|
||||
if not check_moonraker_install_requirements():
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
# ======================================================================= #
|
||||
|
||||
import shutil
|
||||
from typing import Dict, Literal
|
||||
from typing import Dict, Literal, List
|
||||
|
||||
from kiauh.core.config_manager.config_manager import ConfigManager
|
||||
from kiauh.core.instance_manager.instance_manager import InstanceManager
|
||||
from kiauh.modules.moonraker import (
|
||||
DEFAULT_MOONRAKER_PORT,
|
||||
MODULE_PATH,
|
||||
@@ -40,12 +41,12 @@ def create_example_moonraker_conf(
|
||||
instance: Moonraker, ports_map: Dict[str, int]
|
||||
) -> None:
|
||||
Logger.print_status(f"Creating example moonraker.conf in '{instance.cfg_dir}'")
|
||||
if instance.cfg_file is not None:
|
||||
Logger.print_info(f"moonraker.conf in '{instance.cfg_dir}' already exists.")
|
||||
if instance.cfg_file.is_file():
|
||||
Logger.print_info(f"'{instance.cfg_file}' already exists.")
|
||||
return
|
||||
|
||||
source = MODULE_PATH.joinpath("res/moonraker.conf")
|
||||
target = instance.cfg_dir.joinpath("moonraker.conf")
|
||||
target = instance.cfg_file
|
||||
try:
|
||||
shutil.copy(source, target)
|
||||
except OSError as e:
|
||||
@@ -83,3 +84,36 @@ def create_example_moonraker_conf(
|
||||
|
||||
cm.write_config()
|
||||
Logger.print_ok(f"Example moonraker.conf created in '{instance.cfg_dir}'")
|
||||
|
||||
|
||||
def moonraker_to_multi_conversion(new_name: str) -> None:
|
||||
"""
|
||||
Converts the first instance in the List of Moonraker instances to an instance
|
||||
with a new name. This method will be called when converting from a single Klipper
|
||||
instance install to a multi instance install when Moonraker is also already
|
||||
installed with a single instance.
|
||||
:param new_name: new name the previous single instance is renamed to
|
||||
:return: None
|
||||
"""
|
||||
im = InstanceManager(Moonraker)
|
||||
instances: List[Moonraker] = im.instances
|
||||
if not instances:
|
||||
return
|
||||
|
||||
# in case there are multiple Moonraker instances, we don't want to do anything
|
||||
if len(instances) > 1:
|
||||
Logger.print_info("More than a single Moonraker instance found. Skipped ...")
|
||||
return
|
||||
|
||||
Logger.print_status("Convert Moonraker single to multi instance ...")
|
||||
# remove the old single instance
|
||||
im.current_instance = im.instances[0]
|
||||
im.stop_instance()
|
||||
im.disable_instance()
|
||||
im.delete_instance()
|
||||
# create a new klipper instance with the new name
|
||||
im.current_instance = Moonraker(suffix=new_name)
|
||||
# create, enable and start the new moonraker instance
|
||||
im.create_instance()
|
||||
im.enable_instance()
|
||||
im.start_instance()
|
||||
|
||||
@@ -68,7 +68,7 @@ def get_repo_name(repo_dir: Path) -> str:
|
||||
result = "/".join(result.decode().strip().split("/")[-2:])
|
||||
return f"{COLOR_CYAN}{result}{RESET_FORMAT}"
|
||||
except subprocess.CalledProcessError:
|
||||
return f"{COLOR_YELLOW}Unknown{RESET_FORMAT}"
|
||||
return f"{COLOR_RED}-{RESET_FORMAT}"
|
||||
|
||||
|
||||
def get_install_status_common(
|
||||
@@ -111,10 +111,12 @@ def get_install_status_webui(
|
||||
nginx_cfg_exist = check_file_exist(nginx_cfg)
|
||||
upstreams_cfg_exist = check_file_exist(upstreams_cfg)
|
||||
common_cfg_exist = check_file_exist(common_cfg)
|
||||
status = [dir_exist, nginx_cfg_exist, upstreams_cfg_exist, common_cfg_exist]
|
||||
if all(status):
|
||||
status = [dir_exist, nginx_cfg_exist]
|
||||
general_nginx_status = [upstreams_cfg_exist, common_cfg_exist]
|
||||
|
||||
if all(status) and all(general_nginx_status):
|
||||
return f"{COLOR_GREEN}Installed!{RESET_FORMAT}"
|
||||
elif not any(status):
|
||||
elif not all(status):
|
||||
return f"{COLOR_RED}Not installed!{RESET_FORMAT}"
|
||||
else:
|
||||
return f"{COLOR_YELLOW}Incomplete!{RESET_FORMAT}"
|
||||
|
||||
@@ -246,24 +246,20 @@ def get_ipv4_addr() -> str:
|
||||
s.close()
|
||||
|
||||
|
||||
def download_file(
|
||||
url: str, target_folder: Path, target_name: str, show_progress=True
|
||||
) -> None:
|
||||
def download_file(url: str, target: Path, show_progress=True) -> None:
|
||||
"""
|
||||
Helper method for downloading files from a provided URL |
|
||||
:param url: the url to the file
|
||||
:param target_folder: the target folder to download the file into
|
||||
:param target_name: the name of the downloaded file
|
||||
:param target: the target path incl filename
|
||||
:param show_progress: show download progress or not
|
||||
:return: None
|
||||
"""
|
||||
target_path = target_folder.joinpath(target_name)
|
||||
try:
|
||||
if show_progress:
|
||||
urllib.request.urlretrieve(url, target_path, download_progress)
|
||||
urllib.request.urlretrieve(url, target, download_progress)
|
||||
sys.stdout.write("\n")
|
||||
else:
|
||||
urllib.request.urlretrieve(url, target_path)
|
||||
urllib.request.urlretrieve(url, target)
|
||||
except urllib.error.HTTPError as e:
|
||||
Logger.print_error(f"Download failed! HTTP error occured: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user