diff --git a/README.md b/README.md index fcccaed..c7f71ed 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ The remove-package step will remove a package on the VM/LXC. *Note : At the the moment, only apt, apk, dnf, yum are supported.* *Warning : Packages can have different names depending on the Linux distribution.* -### Reboot +### Power The reboot step will reboot the VM/LXC. ```json "steps": [ @@ -331,6 +331,23 @@ The reboot step will reboot the VM/LXC. } ] ``` +The start step will start the VM/LXC. +```json +"steps": [ + { + "type": "start" + } +] +``` + +The stop step will stop the VM/LXC. +```json +"steps": [ + { + "type": "stop" + } +] +``` ### Replace in file The replace-in-file step will replace a string in a file. diff --git a/src/utils/creation_utils.py b/src/utils/creation_utils.py index ebf27f7..1009c23 100644 --- a/src/utils/creation_utils.py +++ b/src/utils/creation_utils.py @@ -67,13 +67,27 @@ def run_steps(lxc): case "folder_copy": lxc_commands_utils.copy_local_folder_to_lxc(lxc, step["path"], step["destination"]) case "command": - lxc.run_command(step["command"], optional(step["working_directory"], None)) + lxc.run_command(command=step["command"], working_directory=optional(step["working_directory"], None)) case "docker": lxc_commands_utils.run_docker_command(lxc, step["container"], step["command"]) case "docker_compose": lxc_commands_utils.run_docker_compose_command(lxc, step["command"], optional(step["working_directory"], None)) case "git": - lxc.run_command(f"git clone {step['url']} {step['destination']}") + lxc.run_command(command=f"git clone {step['url']} {step['destination']}") case "download": lxc_commands_utils.download_file(lxc, step["url"], step["destination"]) + case "unzip": + lxc_commands_utils.unzip_file(lxc, step["path"], optional(step["destination"], None)) + case "install-package": + lxc_commands_utils.install_package(lxc, step["package"]) + case "remove-package": + lxc_commands_utils.remove_package(lxc, step["package"]) + case "start": + lxc.start() + case "stop": + lxc.stop() + case "reboot": + lxc.reboot() + case "replace-in-files": + lxc_commands_utils.replace_in_files(lxc, step["files"], step["search"], step["replace"]) diff --git a/src/utils/lxc_commands_utils.py b/src/utils/lxc_commands_utils.py index 52a5ddd..7837267 100644 --- a/src/utils/lxc_commands_utils.py +++ b/src/utils/lxc_commands_utils.py @@ -1,3 +1,5 @@ +from pathlib import Path + from src.utils import proxmox_utils from src.utils.resources_utils import get_path @@ -74,3 +76,40 @@ def download_file(lxc, url, destination): lxc.run_command(f"wget {u} -O {destination}") else: lxc.run_command(f"wget {url} -O {destination}") + + +def unzip_file(lxc, path, destination=None): + if destination is None: + destination = Path(path).parent + + if ".zip" in path: + lxc.run_command(f"unzip {path} -d {destination}") + elif ".tar.gz" in path: + lxc.run_command(f"tar -xzf {path} -C {destination}") + + +def install_package(lxc, package): + if type(package) is list: + for p in package: + lxc.run_command(f"{proxmox_utils.get_install_package_command(lxc.get_os_name())} {p} -y") + else: + lxc.run_command(f"{proxmox_utils.get_install_package_command(lxc.get_os_name())} {package} -y") + + +def remove_package(lxc, package): + if type(package) is list: + packages = [] + for p in package: + packages.append(p) + lxc.run_command(f"{proxmox_utils.get_remove_package_command(lxc.get_os_name())} {' '.join(packages)}") + + else: + lxc.run_command(f"{proxmox_utils.get_remove_package_command(lxc.get_os_name())} {package}") + + +def replace_in_files(lxc, path, search, replace, case_sensitive=False): + if type(path) is list: + for p in path: + lxc.run_command(f"sed -i {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {p}") + else: + lxc.run_command(f"sed -i {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {path}") diff --git a/src/utils/lxc_utils.py b/src/utils/lxc_utils.py index 91df21b..3d340db 100644 --- a/src/utils/lxc_utils.py +++ b/src/utils/lxc_utils.py @@ -148,11 +148,11 @@ class LXC: template_name = proxmox_utils.run_command_on_pve( f"pveam available --section system | awk /'{self.os_name}-{self.os_release}/' | awk '{{print \\$2}}'") - is_template_downloaded = proxmox_utils.run_command_on_pve(f"pveam list local | awk /'{template_name}/'") + is_template_downloaded = proxmox_utils.run_command_on_pve(command=f"pveam list local | awk /'{template_name}/'") if is_template_downloaded == "": logging.info(f"Template {template_name} not found, downloading it...") - proxmox_utils.run_command_on_pve(f"pveam download local {template_name}", True) + proxmox_utils.run_command_on_pve(command=f"pveam download local {template_name}", warn_exit_status=True) return f"local:vztmpl/{template_name}" @@ -305,7 +305,7 @@ class LXC: Is running :return: is lxc running? (boolean) """ - return proxmox_utils.run_command_on_pve(f"pct list | awk '/running/ && /{self.lxc_id}/'") != "" + return proxmox_utils.run_command_on_pve(command=f"pct list | awk '/running/ && /{self.lxc_id}/'") != "" def does_exist(self): """ @@ -323,7 +323,21 @@ class LXC: return else: logging.info(f"Starting LXC {self.lxc_id}") - proxmox_utils.run_command_on_pve(f"pct start {self.lxc_id}", True) + proxmox_utils.run_command_on_pve(command=f"pct start {self.lxc_id}", warn_exit_status=True) + + def stop(self): + """ + Stop LXC + """ + logging.info(f"Stopping LXC {self.lxc_id}") + proxmox_utils.run_command_on_pve(command=f"pct stop {self.lxc_id}", warn_exit_status=True) + + def reboot(self): + """ + Reboot LXC + """ + logging.info(f"Rebooting LXC {self.lxc_id}") + proxmox_utils.run_command_on_pve(command=f"pct reboot {self.lxc_id}", warn_exit_status=True) def create(self): """ @@ -331,10 +345,10 @@ class LXC: """ if self.does_exist(): logging.info(f"LXC {self.lxc_id} already exists, skipping creation") - proxmox_utils.run_command_on_pve(self.get_pct_command(create=False), True) + proxmox_utils.run_command_on_pve(command=self.get_pct_command(create=False), warn_exit_status=True) else: logging.info(f"Creating LXC {self.lxc_id}") - proxmox_utils.run_command_on_pve(self.get_pct_command(create=True), True) + proxmox_utils.run_command_on_pve(command=self.get_pct_command(create=True), warn_exit_status=True) if proxmox_utils.get_config()['settings']['setAuthorizedHostsSSH']: pve_public_key = proxmox_utils.get_ssh_public_key() @@ -393,9 +407,11 @@ class LXC: command = f"cd {working_directory} && {command}" if only_code: - return proxmox_utils.run_command_on_pve(f"pct exec {self.lxc_id} -- {command}", warn_exit_status, only_code) + return proxmox_utils.run_command_on_pve(command=f"pct exec {self.lxc_id} -- {command}", + warn_exit_status=warn_exit_status, only_code=only_code) - return proxmox_utils.run_command_on_pve(f"pct exec {self.lxc_id} -- {command}", warn_exit_status) + return proxmox_utils.run_command_on_pve(command=f"pct exec {self.lxc_id} -- {command}", + warn_exit_status=warn_exit_status) def get_pct_command(self, create=True): """ diff --git a/src/utils/proxmox_utils.py b/src/utils/proxmox_utils.py index f88c056..cdacb34 100644 --- a/src/utils/proxmox_utils.py +++ b/src/utils/proxmox_utils.py @@ -11,7 +11,7 @@ def get_pve_version(): Get PVE version :return: pve version """ - return run_command_on_pve("pveversion", True) + return run_command_on_pve(command="pveversion", warn_exit_status=True) def get_pve_hostname(): @@ -19,7 +19,7 @@ def get_pve_hostname(): Get PVE hostname :return: pve hostname """ - return run_command_on_pve("hostname", True) + return run_command_on_pve(command="hostname", warn_exit_status=True) def does_lxc_exist(lxc_id): @@ -30,7 +30,7 @@ def does_lxc_exist(lxc_id): :return: does lxc exists """ # TODO: only check in VMID column - return lxc_id in run_command_on_pve(f"pct list | awk '/{lxc_id}/'") + return lxc_id in run_command_on_pve(command=f"pct list | awk '/{lxc_id}/'") def does_qemu_vm_exist(vm_id): @@ -41,7 +41,7 @@ def does_qemu_vm_exist(vm_id): :return: does qemu vm exists """ # TODO: only check in VMID column - return vm_id in run_command_on_pve(f"qm list {vm_id}") + return vm_id in run_command_on_pve(command=f"qm list {vm_id}") def execute_tteck_script(script_url, env_variables): @@ -55,7 +55,7 @@ def execute_tteck_script(script_url, env_variables): env_variables = " ".join(env_variables) - run_command_on_pve(f"{env_variables} && bash -c \"$(wget -qLO - {script_url}\"", True) + run_command_on_pve(command=f"{env_variables} && bash -c \"$(wget -qLO - {script_url}\"", warn_exit_status=True) def get_config(): @@ -118,7 +118,75 @@ def run_command_on_pve(command, warn_exit_status=False, only_code=False, local=F def run_command_locally(command, warn_exit_status=False, only_code=False): - run_command_on_pve(command, warn_exit_status, only_code, local=True) + run_command_on_pve(command=command, warn_exit_status=warn_exit_status, only_code=only_code, local=True) + + +def get_install_package_command(distribution): + """ + Get the install package without interaction command based on the distribution. + + :param distribution: Distribution name + :return: Install package command + """ + + distribution = distribution.lower() + + if distribution == "debian": + return "apt-get install -y" + elif distribution == "ubuntu": + return "apt-get install -y" + elif distribution == "centos": + return "yum install -y" + elif distribution == "fedora": + return "yum install -y" + elif distribution == "gentoo": + return "emerge -a" + elif distribution == "alpine": + return "apk add --no-cache" + elif distribution == "archlinux": + return "pacman -S --noconfirm" + elif distribution == "devuan": + return "apt-get install -y" + elif distribution == "nixos": + return "nix-env -i" + elif distribution == "opensuse": + return "zypper install -y" + else: + raise Exception(f"Unsupported distribution: {distribution}") + + +def get_remove_package_command(distribution): + """ + Get the remove package without interaction command based on the distribution. + + :param distribution: Distribution name + :return: Remove package command + """ + + distribution = distribution.lower() + + if distribution == "debian": + return "apt-get remove -y" + elif distribution == "ubuntu": + return "apt-get remove -y" + elif distribution == "centos": + return "yum remove -y" + elif distribution == "fedora": + return "yum remove -y" + elif distribution == "gentoo": + return "emerge -C" + elif distribution == "alpine": + return "apk del" + elif distribution == "archlinux": + return "pacman -R --noconfirm" + elif distribution == "devuan": + return "apt-get remove -y" + elif distribution == "nixos": + return "nix-env -e" + elif distribution == "opensuse": + return "zypper remove -y" + else: + raise Exception(f"Unsupported distribution: {distribution}") def get_ssh_public_key(): @@ -126,4 +194,4 @@ def get_ssh_public_key(): Get SSH public key :return: ssh public key """ - return run_command_on_pve("cat ~/.ssh/id_rsa.pub", True) + return run_command_on_pve(command="cat ~/.ssh/id_rsa.pub", warn_exit_status=True)