Compare commits

...

7 Commits

Author SHA1 Message Date
dw-0
bd425a08dd Merge e06a4e3197 into dbe15e3a32 2024-06-27 20:58:34 +02:00
dw-0
e06a4e3197 fix: add recursive removal of files
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-06-27 20:58:33 +02:00
dw-0
4df89c9917 refactor: oe uninstaller
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-06-27 20:23:34 +02:00
dw-0
b19ba840ea refactor: add padding option to dialog
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-06-27 20:15:29 +02:00
dw-0
83fdb715a2 refactor: remove redundant steps ocoeverywhere already takes care of
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-06-27 18:09:06 +02:00
dw-0
726df7502b Merge branch 'refs/heads/kiauh-v6-dev' into feat/oe-for-v6 2024-06-27 17:57:08 +02:00
dw-0
dbe15e3a32 feat: add ipv6 check before installing webclients
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-06-27 17:55:17 +02:00
17 changed files with 128 additions and 72 deletions

View File

@@ -88,7 +88,6 @@ def print_multi_instance_warning(instances: List[Klipper]) -> None:
"The following instances were found:", "The following instances were found:",
*_instances, *_instances,
], ],
end="",
) )

View File

@@ -145,7 +145,6 @@ def update_klipper() -> None:
"All Klipper instances will be restarted during the update process and " "All Klipper instances will be restarted during the update process and "
"ongoing prints WILL FAIL.", "ongoing prints WILL FAIL.",
], ],
end="",
) )
if not get_confirm("Update Klipper now?"): if not get_confirm("Update Klipper now?"):

View File

@@ -220,7 +220,6 @@ def check_user_groups():
"INFO:", "INFO:",
"Relog required for group assignments to take effect!", "Relog required for group assignments to take effect!",
], ],
end="",
) )
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"): if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):
@@ -272,7 +271,7 @@ def handle_disruptive_system_packages() -> None:
"Please fix the problem manually. Otherwise, this may have " "Please fix the problem manually. Otherwise, this may have "
"undesirable effects on the operation of Klipper." "undesirable effects on the operation of Klipper."
], ],
end="", padding_bottom="",
) )

View File

@@ -353,7 +353,6 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
"\n\n", "\n\n",
"If you are unsure, stick to the default 250000!", "If you are unsure, stick to the default 250000!",
], ],
end="",
) )
self.flash_options.selected_baudrate = get_number_input( self.flash_options.selected_baudrate = get_number_input(
question="Please set the baud rate", question="Please set the baud rate",

View File

@@ -62,7 +62,6 @@ def install_klipperscreen() -> None:
"KlipperScreens update manager configuration for Moonraker " "KlipperScreens update manager configuration for Moonraker "
"will not be added to any moonraker.conf.", "will not be added to any moonraker.conf.",
], ],
end="",
) )
if not get_confirm( if not get_confirm(
"Continue KlipperScreen installation?", "Continue KlipperScreen installation?",

View File

@@ -58,7 +58,6 @@ def install_mobileraker() -> None:
"Mobileraker's companion's update manager configuration for Moonraker " "Mobileraker's companion's update manager configuration for Moonraker "
"will not be added to any moonraker.conf.", "will not be added to any moonraker.conf.",
], ],
end="",
) )
if not get_confirm( if not get_confirm(
"Continue Mobileraker's companion installation?", "Continue Mobileraker's companion installation?",

View File

@@ -62,11 +62,17 @@ class Octoeverywhere(BaseInstance):
raise raise
def delete(self) -> None: def delete(self) -> None:
Logger.print_status("Removing OctoEverywhere for Klipper Instance ...") service_file = self.get_service_file_name(extension=True)
service_file_path = self.get_service_file_path()
Logger.print_status(
f"Deleting OctoEverywhere for Klipper Instance: {service_file}"
)
try: try:
cmd = f"OE_REMOVE_SCRIPT {self.cfg_dir}/moonraker.conf" command = ["sudo", "rm", "-f", service_file_path]
run(cmd, check=True, shell=True) run(command, check=True)
Logger.print_ok(f"Service file deleted: {service_file_path}")
except CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"Error deleting instance: {e}") Logger.print_error(f"Error deleting service file: {e}")
raise raise

View File

@@ -7,7 +7,7 @@
# This file may be distributed under the terms of the GNU GPLv3 license # # This file may be distributed under the terms of the GNU GPLv3 license #
# ======================================================================= # # ======================================================================= #
import json import json
import shutil from pathlib import Path
from typing import List from typing import List
from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker import Moonraker
@@ -28,7 +28,7 @@ from utils.config_utils import (
add_config_section, add_config_section,
remove_config_section, remove_config_section,
) )
from utils.fs_utils import remove_file from utils.fs_utils import run_remove_routines
from utils.git_utils import git_clone_wrapper, git_pull_wrapper from utils.git_utils import git_clone_wrapper, git_pull_wrapper
from utils.input_utils import get_confirm from utils.input_utils import get_confirm
from utils.logger import DialogType, Logger from utils.logger import DialogType, Logger
@@ -47,8 +47,6 @@ def install_octoeverywhere() -> None:
if not moonraker_exists(): if not moonraker_exists():
return return
# if obico is already installed, ask if the user wants to repair an
# incomplete installation or link to the obico server
oe_im = InstanceManager(Octoeverywhere) oe_im = InstanceManager(Octoeverywhere)
oe_instances: List[Octoeverywhere] = oe_im.instances oe_instances: List[Octoeverywhere] = oe_im.instances
if oe_instances: if oe_instances:
@@ -59,7 +57,6 @@ def install_octoeverywhere() -> None:
"It is save to run the installer again to link your " "It is save to run the installer again to link your "
"printer or repair any issues.", "printer or repair any issues.",
], ],
end="",
) )
if not get_confirm("Re-run OctoEverywhere installation?"): if not get_confirm("Re-run OctoEverywhere installation?"):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
@@ -80,7 +77,6 @@ def install_octoeverywhere() -> None:
"\n\n", "\n\n",
"The setup will apply the same names to OctoEverywhere!", "The setup will apply the same names to OctoEverywhere!",
], ],
end="",
) )
if not get_confirm( if not get_confirm(
@@ -98,14 +94,7 @@ def install_octoeverywhere() -> None:
for moonraker in mr_instances: for moonraker in mr_instances:
oe_im.current_instance = Octoeverywhere(suffix=moonraker.suffix) oe_im.current_instance = Octoeverywhere(suffix=moonraker.suffix)
oe_im.create_instance() oe_im.create_instance()
oe_im.enable_instance()
oe_im.start_instance()
cmd_sysctl_manage("daemon-reload")
# TODO: probably done by OE already?
# add to moonraker update manager
patch_moonraker_conf(mr_instances)
mr_im.restart_all_instance() mr_im.restart_all_instance()
Logger.print_dialog( Logger.print_dialog(
@@ -200,36 +189,40 @@ def remove_oe_instances(
def remove_oe_dir() -> None: def remove_oe_dir() -> None:
Logger.print_status("Removing OctoEverywhere for Klipper directory ...")
if not OE_DIR.exists(): if not OE_DIR.exists():
Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...") Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...")
return return
try: run_remove_routines(OE_DIR)
shutil.rmtree(OE_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{OE_DIR}':\n{e}")
def remove_oe_env() -> None: def remove_oe_env() -> None:
Logger.print_status("Removing OctoEverywhere for Klipper environment ...")
if not OE_ENV_DIR.exists(): if not OE_ENV_DIR.exists():
Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...") Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...")
return return
try: run_remove_routines(OE_ENV_DIR)
shutil.rmtree(OE_ENV_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{OE_ENV_DIR}':\n{e}")
def delete_oe_logs(instances: List[Octoeverywhere]) -> None: def delete_oe_logs(instances: List[Octoeverywhere]) -> None:
Logger.print_status("Removing OctoEverywhere logs ...") Logger.print_status("Removing OctoEverywhere logs ...")
all_logfiles = [] all_logfiles = []
for instance in instances: for instance in instances:
all_logfiles = list(instance.log_dir.glob(f"{OE_LOG_NAME}*")) all_logfiles = list(instance.log_dir.glob(f"{OE_LOG_NAME}*"))
install_log = Path.home().joinpath("octoeverywhere-installer.log")
if install_log.exists():
all_logfiles.append(install_log)
if not all_logfiles: if not all_logfiles:
Logger.print_info("No OctoEverywhere logs found. Skipped ...") Logger.print_info("No OctoEverywhere logs found. Skipped ...")
return return
for log in all_logfiles: for log in all_logfiles:
Logger.print_status(f"Remove '{log}'") Logger.print_status(f"Remove '{log}'")
remove_file(log) run_remove_routines(log)

View File

@@ -13,6 +13,7 @@ from typing import List
from components.webui_client.base_data import BaseWebClient from components.webui_client.base_data import BaseWebClient
from core.menus.base_menu import print_back_footer from core.menus.base_menu import print_back_footer
from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
from utils.logger import DialogType, Logger
def print_moonraker_not_found_dialog(): def print_moonraker_not_found_dialog():
@@ -84,25 +85,33 @@ def print_client_port_select_dialog(name: str, port: int, ports_in_use: List[int
print(dialog, end="") print(dialog, end="")
def print_install_client_config_dialog(client: BaseWebClient): def print_install_client_config_dialog(client: BaseWebClient) -> None:
name = client.display_name name = client.display_name
url = client.client_config.repo_url.replace(".git", "") url = client.client_config.repo_url.replace(".git", "")
line1 = f"have {name} fully functional and working." Logger.print_dialog(
line2 = f"The recommended macros for {name} can be seen here:" DialogType.INFO,
dialog = textwrap.dedent( [
f""" f"It is recommended to use special macros in order to have {name} fully "
/=======================================================\\ f"functional and working.",
| It is recommended to use special macros in order to | "\n\n",
| {line1:<54}| f"The recommended macros for {name} can be seen here:",
| | url,
| {line2:<54}| "\n\n",
| {url:<54}| "If you already use these macros skip this step. Otherwise you should "
| | "consider to answer with 'Y' to download the recommended macros.",
| If you already use these macros skip this step. | ],
| Otherwise you should consider to answer with 'Y' to | )
| download the recommended macros. |
\\=======================================================/
"""
)[1:]
print(dialog, end="")
def print_ipv6_warning_dialog() -> None:
Logger.print_dialog(
DialogType.WARNING,
[
"It looks like IPv6 is enabled on this system!",
"This may cause issues with the installation of NGINX in the following "
"steps! It is recommended to disable IPv6 on your system to avoid this issue.",
"\n\n",
"If you think this warning is a false alarm, and you are sure that "
"IPv6 is disabled, you can continue with the installation.",
],
)

View File

@@ -24,6 +24,7 @@ from components.webui_client.client_config.client_config_setup import (
from components.webui_client.client_dialogs import ( from components.webui_client.client_dialogs import (
print_client_port_select_dialog, print_client_port_select_dialog,
print_install_client_config_dialog, print_install_client_config_dialog,
print_ipv6_warning_dialog,
print_moonraker_not_found_dialog, print_moonraker_not_found_dialog,
) )
from components.webui_client.client_utils import ( from components.webui_client.client_utils import (
@@ -49,6 +50,7 @@ from utils.fs_utils import (
from utils.input_utils import get_confirm, get_number_input from utils.input_utils import get_confirm, get_number_input
from utils.logger import Logger from utils.logger import Logger
from utils.sys_utils import ( from utils.sys_utils import (
check_ipv6,
cmd_sysctl_service, cmd_sysctl_service,
download_file, download_file,
get_ipv4_addr, get_ipv4_addr,
@@ -115,6 +117,13 @@ def install_client(client: BaseWebClient) -> None:
) )
valid_port = is_valid_port(port, ports_in_use) valid_port = is_valid_port(port, ports_in_use)
# check if ipv6 is enabled, as this may cause issues with nginx
if check_ipv6():
print_ipv6_warning_dialog()
if not get_confirm(f"Continue with {client.display_name} installation?"):
Logger.print_info(f"Exiting {client.display_name} installation ...")
return
check_install_dependencies(["nginx", "unzip"]) check_install_dependencies(["nginx", "unzip"])
try: try:

View File

@@ -144,7 +144,6 @@ class SettingsMenu(BaseMenu):
f"New {display_name} repository branch:", f"New {display_name} repository branch:",
f"{branch}", f"{branch}",
], ],
end="",
) )
if get_confirm("Apply changes?", allow_go_back=True): if get_confirm("Apply changes?", allow_go_back=True):

View File

@@ -219,6 +219,5 @@ class KiauhSettings:
"● default.kiauh.cfg", "● default.kiauh.cfg",
"● kiauh.cfg", "● kiauh.cfg",
], ],
end="",
) )
kill() kill()

View File

@@ -192,7 +192,6 @@ class ObicoExtension(BaseExtension):
"http://server_ip:port", "http://server_ip:port",
"For instance, 'http://192.168.0.5:3334'.", "For instance, 'http://192.168.0.5:3334'.",
], ],
end="",
) )
def _print_moonraker_instances(self, mr_instances) -> None: def _print_moonraker_instances(self, mr_instances) -> None:
@@ -206,7 +205,6 @@ class ObicoExtension(BaseExtension):
"\n\n", "\n\n",
"The setup will apply the same names to Obico!", "The setup will apply the same names to Obico!",
], ],
end="",
) )
def _print_is_already_installed(self) -> None: def _print_is_already_installed(self) -> None:
@@ -221,7 +219,6 @@ class ObicoExtension(BaseExtension):
"L) Link printer to the Obico server", "L) Link printer to the Obico server",
"R) Repair installation", "R) Repair installation",
], ],
end="",
) )
def _get_server_url(self) -> None: def _get_server_url(self) -> None:
@@ -324,7 +321,6 @@ class ObicoExtension(BaseExtension):
"If you don't want to link the printer now, you can restart the " "If you don't want to link the printer now, you can restart the "
"linking process later by running this installer again.", "linking process later by running this installer again.",
], ],
end="",
) )
if not get_confirm("Do you want to link the printers now?"): if not get_confirm("Do you want to link the printers now?"):
Logger.print_info("Linking to Obico server skipped ...") Logger.print_info("Linking to Obico server skipped ...")

View File

@@ -150,7 +150,6 @@ def moonraker_exists(name: str = "") -> bool:
"No Moonraker instances found!", "No Moonraker instances found!",
f"{info}. Please install Moonraker first!", f"{info}. Please install Moonraker first!",
], ],
end="",
) )
return False return False
return True return True

View File

@@ -59,9 +59,9 @@ def create_symlink(source: Path, target: Path, sudo=False) -> None:
raise raise
def remove_with_sudo(file_path: Path) -> None: def remove_with_sudo(file: Path) -> None:
try: try:
cmd = ["sudo", "rm", "-f", file_path] cmd = ["sudo", "rm", "-rf", file]
run(cmd, stderr=PIPE, check=True) run(cmd, stderr=PIPE, check=True)
except CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"Failed to remove file: {e}") Logger.print_error(f"Failed to remove file: {e}")
@@ -79,6 +79,30 @@ def remove_file(file_path: Path, sudo=False) -> None:
raise raise
def run_remove_routines(file: Path) -> None:
try:
if not file.exists():
Logger.print_info(f"File '{file}' does not exist. Skipped ...")
return
if file.is_dir():
shutil.rmtree(file)
elif file.is_file():
file.unlink()
else:
raise OSError(f"File '{file}' is neither a file nor a directory!")
Logger.print_ok("Successfully removed!")
except OSError as e:
Logger.print_error(f"Unable to delete '{file}':\n{e}")
try:
Logger.print_info("Trying to remove with sudo ...")
remove_with_sudo(file)
Logger.print_ok("Successfully removed!")
except CalledProcessError as e:
Logger.print_error(f"Error deleting '{file}' with sudo:\n{e}")
Logger.print_error("Remove this directory manually!")
def unzip(filepath: Path, target_dir: Path) -> None: def unzip(filepath: Path, target_dir: Path) -> None:
""" """
Helper function to unzip a zip-archive into a target directory | Helper function to unzip a zip-archive into a target directory |

View File

@@ -90,7 +90,8 @@ class Logger:
center_content: bool = False, center_content: bool = False,
custom_title: str = None, custom_title: str = None,
custom_color: DialogCustomColor = None, custom_color: DialogCustomColor = None,
end: str = "\n", padding_top: int = 1,
padding_bottom: int = 1,
) -> None: ) -> None:
dialog_color = Logger._get_dialog_color(title, custom_color) dialog_color = Logger._get_dialog_color(title, custom_color)
dialog_title = Logger._get_dialog_title(title, custom_title) dialog_title = Logger._get_dialog_title(title, custom_title)
@@ -99,10 +100,12 @@ class Logger:
top = Logger._format_top_border(dialog_color) top = Logger._format_top_border(dialog_color)
bottom = Logger._format_bottom_border() bottom = Logger._format_bottom_border()
print("\n" * padding_top)
print( print(
f"{top}{dialog_title_formatted}{dialog_content}{bottom}", f"{top}{dialog_title_formatted}{dialog_content}{bottom}",
end=end, end="",
) )
print("\n" * padding_bottom)
@staticmethod @staticmethod
def _get_dialog_title(title: DialogType, custom_title: str = None) -> str: def _get_dialog_title(title: DialogType, custom_title: str = None) -> str:
@@ -120,18 +123,12 @@ class Logger:
@staticmethod @staticmethod
def _format_top_border(color: str) -> str: def _format_top_border(color: str) -> str:
return textwrap.dedent( return f"{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
f"""
{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
"""
)[1:-1]
@staticmethod @staticmethod
def _format_bottom_border() -> str: def _format_bottom_border() -> str:
return textwrap.dedent( return (
f""" f"\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{RESET_FORMAT}"
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
{RESET_FORMAT}"""
) )
@staticmethod @staticmethod

View File

@@ -402,3 +402,34 @@ def log_process(process: Popen) -> None:
if process.poll() is not None: if process.poll() is not None:
break break
def check_ipv6() -> bool:
"""
Check if IPv6 is enabled
:return: True if IPv6 is enabled, False otherwise
"""
try:
file1 = Path("/proc/sys/net/ipv6/conf/all/disable_ipv6")
if file1.exists():
with open(file1, "r") as file:
if file.read().strip() == "0":
return True
elif file.read().strip() == "1":
return False
file3 = Path("/etc/sysctl.conf")
if file3.exists():
with open(file3, "r") as file:
for line in file.readlines():
if (
"net.ipv6.conf.all.disable_ipv6" in line
and not line.startswith("#")
and line.split("=")[1].strip() == "0"
):
return True
return False
except Exception as e:
Logger.print_error(f"Error checking IPv6: {e}")
return True