Compare commits

...

11 Commits

Author SHA1 Message Date
dw-0
ad0dbf63b8 refactor(Mainsail): enable remote mode if moonraker multi instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 00:11:11 +01:00
dw-0
9dedf38079 refactor(KIAUH): big refactor of instance handling
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-26 23:37:35 +01:00
dw-0
1b4c76d080 fix(KIAUH): more file path handling improvements
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:31:18 +01:00
dw-0
d20d82aeac fix(Mainsail): proper check if config exists
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:06:40 +01:00
dw-0
16a28ffda0 fix(Klipper/Moonraker): config files now always have a Path, are never None anymore
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:03:44 +01:00
dw-0
a9367cc064 fix(Klipper): remove obsolete method parameter
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:01:18 +01:00
dw-0
b165d88855 fix(Moonraker): missing return statement if all requirements met
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:57:22 +01:00
dw-0
6c59d58193 refactor(KIAUH): use red dash instead of "Unknown" if repo info not available
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:44:04 +01:00
dw-0
b4f5c3c1ac refactor(Mainsail): remove mainsail.zip after extracting content
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:16:38 +01:00
dw-0
b69ecbc9b5 fix(KIAUH): wrong logic in status detection
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 19:56:43 +01:00
dw-0
fc9fa39eee refactor(Mainsail): use same wording in MainsailRemoveMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 19:43:30 +01:00
14 changed files with 254 additions and 245 deletions

View File

@@ -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}"

View File

@@ -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:

View File

@@ -0,0 +1,8 @@
from enum import unique, Enum
@unique
class NameScheme(Enum):
SINGLE = "SINGLE"
INDEX = "INDEX"
CUSTOM = "CUSTOM"

View File

@@ -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

View File

@@ -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?"):

View File

@@ -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:

View File

@@ -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)

View 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:

View File

@@ -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="")

View File

@@ -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)

View 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():

View File

@@ -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()

View File

@@ -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}"

View File

@@ -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