Compare commits
No commits in common. "4e1b2d7dd4128fb3dff39d441d31ac0efc49f7e3" and "fa3b0b1b272b0dcadc7f9176390adfaeb6c89774" have entirely different histories.
4e1b2d7dd4
...
fa3b0b1b27
40
README.md
40
README.md
@ -90,8 +90,8 @@ The file condition will check if a file exists or not inside the LXC/VM.
|
|||||||
"conditions": {
|
"conditions": {
|
||||||
// use a single file
|
// use a single file
|
||||||
"file": "/var/data/traefikv2/traefik.toml"
|
"file": "/var/data/traefikv2/traefik.toml"
|
||||||
// or use a list
|
// or use a list, but only use one
|
||||||
"file": ["/var/data/traefikv2/traefik.toml", "/var/data/config/traefikv2/docker-compose.yml"]
|
"files": ["/var/data/traefikv2/traefik.toml", "/var/data/config/traefikv2/docker-compose.yml"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
It can be an array of file using ``["file1", "file2"]`` or just one file in double quote ``"file"``.
|
It can be an array of file using ``["file1", "file2"]`` or just one file in double quote ``"file"``.
|
||||||
@ -102,8 +102,8 @@ The folder condition will check if a folder exists or not inside the LXC/VM.
|
|||||||
"conditions": {
|
"conditions": {
|
||||||
// use a single folder
|
// use a single folder
|
||||||
"folder": "/var/data/traefikv2"
|
"folder": "/var/data/traefikv2"
|
||||||
// or use a list
|
// or use a list, but only use one
|
||||||
"folder": ["/var/data/traefikv2", "/var/data/config/traefikv2"]
|
"folders": ["/var/data/traefikv2", "/var/data/config/traefikv2"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
It can be an array of folders using ``["folder1", "folder2"]`` or just one folder in double quote ``"file"``.
|
It can be an array of folders using ``["folder1", "folder2"]`` or just one folder in double quote ``"file"``.
|
||||||
@ -114,8 +114,8 @@ The programs condition will check if a program is installed or not in the LXC/VM
|
|||||||
"conditions": {
|
"conditions": {
|
||||||
// use a single program
|
// use a single program
|
||||||
"program": "docker"
|
"program": "docker"
|
||||||
// or use a list
|
// or use a list, but only use one
|
||||||
"program": ["docker", "docker-compose"]
|
"programs": ["docker", "docker-compose"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
It can be an array of programs using ``["program1", "program2"]`` or just one program in double quote ``"program"``.
|
It can be an array of programs using ``["program1", "program2"]`` or just one program in double quote ``"program"``.
|
||||||
@ -138,7 +138,7 @@ The docker condition will check if a docker (or podman) container is running or
|
|||||||
"conditions": {
|
"conditions": {
|
||||||
// use a single container
|
// use a single container
|
||||||
"container": "traefikv2"
|
"container": "traefikv2"
|
||||||
// or use a list
|
// or use a list, but only use one
|
||||||
"container": ["traefikv2", "portainer"]
|
"container": ["traefikv2", "portainer"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -157,7 +157,7 @@ The script step will execute a script.
|
|||||||
{
|
{
|
||||||
"type": "script",
|
"type": "script",
|
||||||
// use path for local scripts
|
// use path for local scripts
|
||||||
"local_path": "global/install-docker.sh" // local path (here: resources/scripts/install-docker.sh)
|
"path": "/global/install-docker.sh" // local path (here: resources/scripts/install-docker.sh)
|
||||||
// or use url for remote scripts
|
// or use url for remote scripts
|
||||||
"url": "https://xyz.abc/scripts/install-docker.sh" // remote url
|
"url": "https://xyz.abc/scripts/install-docker.sh" // remote url
|
||||||
// or use lxc_path for scripts inside the lxc
|
// or use lxc_path for scripts inside the lxc
|
||||||
@ -182,7 +182,7 @@ The file step will create / copy a file to the VM/LXC.
|
|||||||
{
|
{
|
||||||
"type": "file_copy",
|
"type": "file_copy",
|
||||||
"path": "traefik.toml", // local path (here: resources/lxc/<id>/traefik.toml)
|
"path": "traefik.toml", // local path (here: resources/lxc/<id>/traefik.toml)
|
||||||
"destination": "/var/data/traefikv2/", // lxc/vm path
|
"destination": "/var/data/traefikv2/traefik.toml", // lxc/vm path
|
||||||
"permissions": "644" // (optional) permissions of the file
|
"permissions": "644" // (optional) permissions of the file
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -204,8 +204,8 @@ The folder step will copy a folder to the VM/LXC.
|
|||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"type": "folder_copy",
|
"type": "folder_copy",
|
||||||
"path": "data/", // local path (here: resources/lxc/<id>/data/)
|
"path": "/data/", // local path (here: resources/lxc/<id>/data/)
|
||||||
"destination": "/var/", // lxc/vm path
|
"destination": "/var/data/traefikv2", // lxc/vm path
|
||||||
"permissions": "755" // (optional) permissions of the folder
|
"permissions": "755" // (optional) permissions of the folder
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -260,7 +260,7 @@ The git step will clone a git repo on the VM/LXC.
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
*Note: At the moment, no authentication is supported, so only works on public repo.*
|
*Note: At the the moment, no authentication is supported, so only works on public repo.*
|
||||||
|
|
||||||
### Download
|
### Download
|
||||||
The download step will download a file on the VM/LXC.
|
The download step will download a file on the VM/LXC.
|
||||||
@ -270,8 +270,8 @@ The download step will download a file on the VM/LXC.
|
|||||||
"type": "download",
|
"type": "download",
|
||||||
// download a single file
|
// download a single file
|
||||||
"url": "https://git.abc.xyz/file.tar.gz", // download url
|
"url": "https://git.abc.xyz/file.tar.gz", // download url
|
||||||
// or use a list of urls
|
// or use a list of urls, but only use one
|
||||||
"url": ["https://git.abc.xyz/file1.tar.gz", "https://git.abc.xyz/file2.tar.gz"] // download urls
|
"urls": ["https://git.abc.xyz/file1.tar.gz", "https://git.abc.xyz/file2.tar.gz"] // download urls
|
||||||
"destination": "/tmp/" // lxc/vm path
|
"destination": "/tmp/" // lxc/vm path
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -298,8 +298,8 @@ The install-package step will install a package on the VM/LXC.
|
|||||||
"type": "install-package",
|
"type": "install-package",
|
||||||
// install a single package
|
// install a single package
|
||||||
"package": "git",
|
"package": "git",
|
||||||
// or use a list of packages
|
// or use a list of packages, but only use one
|
||||||
"package": ["git", "docker"]
|
"packages": ["git", "docker"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -314,8 +314,8 @@ The remove-package step will remove a package on the VM/LXC.
|
|||||||
"type": "remove-package",
|
"type": "remove-package",
|
||||||
// remove a single package
|
// remove a single package
|
||||||
"package": "git"
|
"package": "git"
|
||||||
// or use a list of packages
|
// or use a list of packages, but only use one
|
||||||
"package": ["git", "docker"]
|
"packages": ["git", "docker"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -357,8 +357,8 @@ The replace-in-file step will replace a string in a file.
|
|||||||
"type": "replace-in-file",
|
"type": "replace-in-file",
|
||||||
// replace in a single file
|
// replace in a single file
|
||||||
"path": "/var/data/config/traefikv2/traefik.toml", // inside lxc/vm
|
"path": "/var/data/config/traefikv2/traefik.toml", // inside lxc/vm
|
||||||
// or use a list of files
|
// or use a list of files, but only use one
|
||||||
"path": ["/var/data/config/traefikv2/traefik.toml", "/var/data/config/traefikv2/traefik2.toml"] // inside lxc/vm
|
"paths": ["/var/data/config/traefikv2/traefik.toml", "/var/data/config/traefikv2/traefik2.toml"] // inside lxc/vm
|
||||||
"search": "abc", // string to search
|
"search": "abc", // string to search
|
||||||
"replace": "xyz", // string to replace
|
"replace": "xyz", // string to replace
|
||||||
"case_sensitive": true, // (optional) case sensitive? default: true
|
"case_sensitive": true, // (optional) case sensitive? default: true
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if which ssh >/dev/null 2>&1; then
|
|
||||||
echo "SSH is installed"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "SSH is not installed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if lsb_release -a 2>/dev/null | grep -q -E "Debian|Ubuntu"; then
|
|
||||||
echo "Running Debian or Ubuntu"
|
|
||||||
apt-get install openssh-server
|
|
||||||
systemctl start sshd
|
|
||||||
|
|
||||||
elif cat /etc/os-release 2>/dev/null | grep -q -i "alpine"; then
|
|
||||||
echo "Running Alpine"
|
|
||||||
|
|
||||||
# Stop and remove Dropbear
|
|
||||||
rc-service dropbear stop
|
|
||||||
rc-update del dropbear
|
|
||||||
apk del dropbear* -f
|
|
||||||
|
|
||||||
# Stop and remove SSHD
|
|
||||||
rc-service sshd stop
|
|
||||||
rc-update del sshd
|
|
||||||
apk del openssh* -f
|
|
||||||
|
|
||||||
# Clean up Dropbear and SSH configurations
|
|
||||||
rm -rf /etc/dropbear
|
|
||||||
rm -rf /etc/ssh
|
|
||||||
rm /etc/init.d/ssh
|
|
||||||
|
|
||||||
# Reboot now if needed
|
|
||||||
# reboot now
|
|
||||||
|
|
||||||
# Install OpenSSH and necessary packages
|
|
||||||
apk add openssh gcompat libstdc++ curl bash git grep
|
|
||||||
apk add procps --no-cache
|
|
||||||
|
|
||||||
# Start and add SSHD to startup
|
|
||||||
rc-service sshd start
|
|
||||||
rc-update add sshd
|
|
||||||
|
|
||||||
# Update AllowTcpForwarding setting
|
|
||||||
sed -i 's/^#*AllowTcpForwarding.*/AllowTcpForwarding yes/' /etc/ssh/sshd_config
|
|
||||||
|
|
||||||
# Update PermitTunnel setting
|
|
||||||
sed -i 's/^#*PermitTunnel.*/PermitTunnel yes/' /etc/ssh/sshd_config
|
|
||||||
|
|
||||||
# Uncomment the line if needed (remove '#' at the beginning)
|
|
||||||
sed -i 's/^#PermitTunnel.*/PermitTunnel yes/' /etc/ssh/sshd_config
|
|
||||||
|
|
||||||
# Restart the SSH service to apply the changes
|
|
||||||
/etc/init.d/sshd restart
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Unknown distribution"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set PermitRootLogin to prohibit-password
|
|
||||||
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
|
@ -1,5 +1,3 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if which docker >/dev/null 2>&1; then
|
if which docker >/dev/null 2>&1; then
|
||||||
echo "Docker is installed"
|
echo "Docker is installed"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -7,8 +7,7 @@ from ..utils import proxmox_utils
|
|||||||
class LXC:
|
class LXC:
|
||||||
"""LXC object"""
|
"""LXC object"""
|
||||||
|
|
||||||
def __init__(self, lxc_id: int, lxc_hostname: str, os: dict, resources: dict, network: dict, options: dict,
|
def __init__(self, lxc_id, lxc_hostname, os, resources, network, options, creation, deploy):
|
||||||
creation: dict, deploy: dict):
|
|
||||||
self.lxc_id = lxc_id
|
self.lxc_id = lxc_id
|
||||||
self.lxc_hostname = lxc_hostname
|
self.lxc_hostname = lxc_hostname
|
||||||
|
|
||||||
@ -53,12 +52,9 @@ class LXC:
|
|||||||
return hash(self.lxc_id)
|
return hash(self.lxc_id)
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
"""Get LXC ID
|
"""
|
||||||
|
Get LXC ID
|
||||||
Returns
|
:return: lxc id
|
||||||
-------
|
|
||||||
int
|
|
||||||
LXC ID
|
|
||||||
"""
|
"""
|
||||||
return self.lxc_id
|
return self.lxc_id
|
||||||
|
|
||||||
@ -99,7 +95,7 @@ class LXC:
|
|||||||
# TODO: might have to run "pveam update" before running this command on fresh install of PVE
|
# TODO: might have to run "pveam update" before running this command on fresh install of PVE
|
||||||
|
|
||||||
template_name = proxmox_utils.run_command_on_pve(
|
template_name = proxmox_utils.run_command_on_pve(
|
||||||
f"pveam available --section system | awk /\'{self.os_name}-{self.os_release}/\' | awk \'{{print $2}}\'")
|
f"pveam available --section system | awk /'{self.os_name}-{self.os_release}/' | awk '{{print \\$2}}'")
|
||||||
|
|
||||||
is_template_downloaded = proxmox_utils.run_command_on_pve(command=f"pveam list local | awk /'{template_name}/'")
|
is_template_downloaded = proxmox_utils.run_command_on_pve(command=f"pveam list local | awk /'{template_name}/'")
|
||||||
|
|
||||||
@ -174,7 +170,7 @@ class LXC:
|
|||||||
if self.is_running():
|
if self.is_running():
|
||||||
if self.has_program("ip"):
|
if self.has_program("ip"):
|
||||||
return self.run_command(
|
return self.run_command(
|
||||||
command="ip addr | grep \'state UP\' -A2 | tail -n1 | awk \'{{print $2}}\' | cut -f1 -d\'/\'")
|
command="ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print \$2}' | cut -f1 -d'/'")
|
||||||
elif self.has_program("ifconfig"):
|
elif self.has_program("ifconfig"):
|
||||||
return self.run_command(command="ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
|
return self.run_command(command="ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
|
||||||
|
|
||||||
@ -311,11 +307,6 @@ class LXC:
|
|||||||
logging.info(f"Creating LXC {self.lxc_id}")
|
logging.info(f"Creating LXC {self.lxc_id}")
|
||||||
proxmox_utils.run_command_on_pve(command=self.get_pct_command(create=True), warn_exit_status=True)
|
proxmox_utils.run_command_on_pve(command=self.get_pct_command(create=True), warn_exit_status=True)
|
||||||
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
logging.info("Setting up SSH for LXC")
|
|
||||||
commands_utils.run_script(lxc=self, step={"local_path": "protected/scripts/install-config-ssh.sh"})
|
|
||||||
|
|
||||||
def run_creation(self):
|
def run_creation(self):
|
||||||
"""
|
"""
|
||||||
Run the creations checks and steps
|
Run the creations checks and steps
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from . import LXC
|
|
||||||
from ..utils import proxmox_utils
|
from ..utils import proxmox_utils
|
||||||
from ..utils.resources_utils import get_path
|
from ..utils.resources_utils import get_path
|
||||||
|
|
||||||
|
|
||||||
def run_script(lxc: LXC, step: dict):
|
def run_script(lxc, step):
|
||||||
"""Method use to dispatch the script to the correct method depending on the type of script, being
|
"""Method use to dispatch the script to the correct method depending on the type of script, being
|
||||||
local (on the machine running this program), remote (url) or in the LXC.
|
local (on the machine running this program), remote (url) or in the LXC.
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ def run_script(lxc: LXC, step: dict):
|
|||||||
lxc : LXC
|
lxc : LXC
|
||||||
The LXC object used to run the script
|
The LXC object used to run the script
|
||||||
step: dict
|
step: dict
|
||||||
Dictionary containing the step information and configuration about the script to run
|
Dictionary containing the step information about the script to run
|
||||||
Typically read from the "creation/steps/<step>" in JSON file
|
Typically read from the "creation/steps/<step>" in JSON file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -24,9 +23,8 @@ def run_script(lxc: LXC, step: dict):
|
|||||||
install_package(lxc, "bash")
|
install_package(lxc, "bash")
|
||||||
|
|
||||||
# Run local script
|
# Run local script
|
||||||
if "local_path" in step:
|
if "path" in step:
|
||||||
path = get_path(lxc, step["local_path"])
|
_run_local_script_on_lxc(lxc, step["path"])
|
||||||
_run_local_script_on_lxc(lxc, path)
|
|
||||||
|
|
||||||
# Run remote script
|
# Run remote script
|
||||||
elif "url" in step:
|
elif "url" in step:
|
||||||
@ -34,78 +32,33 @@ def run_script(lxc: LXC, step: dict):
|
|||||||
|
|
||||||
# Run script in LXC
|
# Run script in LXC
|
||||||
elif "lxc_path" in step:
|
elif "lxc_path" in step:
|
||||||
path = get_path(lxc, step["lxc_path"])
|
_run_script_on_lxc(lxc, step["lxc_path"])
|
||||||
_run_script_on_lxc(lxc, path)
|
|
||||||
|
|
||||||
|
|
||||||
def _run_script_on_lxc(lxc: LXC, path: Path):
|
def _run_script_on_lxc(lxc, path: "Path inside the LXC"):
|
||||||
"""Runs a script present inside the LXC storage, without copying or downloading it
|
"""Run a script in the LXC"""
|
||||||
|
|
||||||
Parameters
|
lxc.run_command(f"bash {path}")
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object used to run the script
|
|
||||||
path: pathlib.Path
|
|
||||||
Path to the script inside the LXC storage
|
|
||||||
"""
|
|
||||||
|
|
||||||
lxc.run_command(f"bash {str(path)}")
|
|
||||||
|
|
||||||
|
|
||||||
def _run_local_script_on_lxc(lxc: LXC, path: Path):
|
def _run_local_script_on_lxc(lxc, path: "Path to the script on the machine running this program"):
|
||||||
"""Runs a script present on the machine running this program, inside the LXC
|
"""Run a local script in the LXC"""
|
||||||
It works by copying the script from the local machine to the PVE using SCP and
|
|
||||||
then using "pct push" to send it to the LXC.
|
|
||||||
|
|
||||||
Parameters
|
path = get_path(lxc, path)
|
||||||
----------
|
with open(path, "r") as file:
|
||||||
lxc: LXC
|
script = file.read()
|
||||||
The LXC object used to run the script
|
lxc.run_command("bash <<EOF\n" + script + "\nEOF")
|
||||||
path: pathlib.Path
|
|
||||||
Path object to the script on the local machine (machine running this program)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Copy the script to /tmp and run it from there
|
|
||||||
proxmox_utils.copy_file_to_lxc(lxc.get_id(), str(path), "/tmp/pdj-scripts/")
|
|
||||||
lxc.run_command(f"bash /tmp/pdj-scripts/{path.name}")
|
|
||||||
|
|
||||||
# with open(path, "r") as file:
|
|
||||||
# script = file.read()
|
|
||||||
#
|
|
||||||
# # TODO : Might work with EOF instead of copying the script to /tmp but unable to make it work
|
|
||||||
# # lxc.run_command("bash <<EOF\n" + script + "\nEOF")
|
|
||||||
|
|
||||||
|
|
||||||
def _run_remote_script_on_lxc(lxc: LXC, url: str):
|
def _run_remote_script_on_lxc(lxc, url: "URL to the script"):
|
||||||
"""Runs a script from a URL on a LXC
|
"""Run a remote script in the LXC"""
|
||||||
Usually this should not be called directly but rather through the run_script() method with the
|
|
||||||
according step configuration (local_path, url or lxc_path) which is itself usually read from the JSON file.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object used to run the script
|
|
||||||
url: str
|
|
||||||
URL of the script to run
|
|
||||||
"""
|
|
||||||
|
|
||||||
if lxc.has_program("curl"):
|
if lxc.has_program("curl"):
|
||||||
lxc.run_command(f"curl -sSL {url} | bash")
|
lxc.run_command(f"curl -sSL {url} | bash")
|
||||||
|
|
||||||
|
|
||||||
def create_folder_in_lxc(lxc: LXC, path: str, permission=755):
|
def create_folder_in_lxc(lxc, path, permission=755):
|
||||||
"""Create a folder in the LXC
|
"""Create a folder in the LXC"""
|
||||||
It's possible to specify the permission of the folder, by default it uses 755
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object of the LXC to create the folder in
|
|
||||||
path: str
|
|
||||||
Path to the folder to create in the LXC
|
|
||||||
permission: int, optional
|
|
||||||
Permission of the folder to create in the LXC (default is 755)
|
|
||||||
"""
|
|
||||||
|
|
||||||
lxc.run_command(f"mkdir -p {path}")
|
lxc.run_command(f"mkdir -p {path}")
|
||||||
|
|
||||||
@ -113,72 +66,47 @@ def create_folder_in_lxc(lxc: LXC, path: str, permission=755):
|
|||||||
lxc.run_command(f"chmod -R {permission} {path}")
|
lxc.run_command(f"chmod -R {permission} {path}")
|
||||||
|
|
||||||
|
|
||||||
def create_file_in_lxc(lxc: LXC, path: str, permission=644):
|
def create_file_in_lxc(lxc, path, permission=644):
|
||||||
"""Create a file in the LXC
|
"""Create a file in the LXC"""
|
||||||
It's possible to specify the permission of the file, by default it uses 644
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object of the LXC to create the file in
|
|
||||||
path: str
|
|
||||||
Path to the file to create in the LXC
|
|
||||||
permission: int, optional
|
|
||||||
Permission of the file to create in the LXC (default is 644)
|
|
||||||
"""
|
|
||||||
|
|
||||||
lxc.run_command(f"touch {path}")
|
lxc.run_command(f"touch {path}")
|
||||||
if permission != 644:
|
if permission != 644:
|
||||||
lxc.run_command(f"chmod {permission} {path}")
|
lxc.run_command(f"chmod {permission} {path}")
|
||||||
|
|
||||||
|
|
||||||
def remove_file_in_lxc(lxc: LXC, path: str):
|
def copy_local_file_to_lxc(lxc, path, destination):
|
||||||
"""Remove a file in the LXC
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object of the LXC to remove the file in
|
|
||||||
path: str
|
|
||||||
Path to the file to remove in the LXC
|
|
||||||
"""
|
|
||||||
|
|
||||||
lxc.run_command(f"rm {path}")
|
|
||||||
|
|
||||||
|
|
||||||
def copy_local_file_to_lxc(lxc: LXC, path: Path, destination: str):
|
|
||||||
"""Copy a local file to the LXC
|
"""Copy a local file to the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
lxc : LXC
|
lxc : LXC
|
||||||
The LXC object of the LXC to copy the file to
|
The LXC object of the LXC to copy the file to
|
||||||
path: pathlib.Path
|
path: str
|
||||||
Path object to the file on the machine running this program
|
Path to the file on the machine running this program
|
||||||
destination: str
|
destination: str
|
||||||
Path to the destination in the LXC
|
Path to the destination in the LXC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proxmox_utils.run_command_locally(f"scp {str(path)} {lxc.get_ssh_string()}:{destination}")
|
proxmox_utils.run_command_locally(f"scp {path} {lxc.get_ssh_string()}:{destination}")
|
||||||
|
|
||||||
|
|
||||||
def copy_local_folder_to_lxc(lxc: LXC, path: Path, destination: str):
|
def copy_local_folder_to_lxc(lxc, path, destination):
|
||||||
"""Copy a local folder to the LXC
|
"""Copy a local folder to the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
lxc : LXC
|
lxc : LXC
|
||||||
The LXC object of the LXC to copy the folder to
|
The LXC object of the LXC to copy the folder to
|
||||||
path: pathlib.Path
|
path: str
|
||||||
Path object to the folder on the machine running this program
|
Path to the folder on the machine running this program
|
||||||
destination: str
|
destination: str
|
||||||
Path to the destination in the LXC
|
Path to the destination in the LXC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proxmox_utils.run_command_locally(f"scp -r {str(path)} {lxc.get_ssh_string()}:{destination}")
|
proxmox_utils.run_command_locally(f"scp -r {path} {lxc.get_ssh_string()}:{destination}")
|
||||||
|
|
||||||
|
|
||||||
def run_docker_command(lxc: LXC, container: str, command: str):
|
def run_docker_command(lxc, container, command):
|
||||||
"""Run a command inside a docker container on the LXC
|
"""Run a command inside a docker container on the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -189,16 +117,11 @@ def run_docker_command(lxc: LXC, container: str, command: str):
|
|||||||
Name of the docker container to run the command in
|
Name of the docker container to run the command in
|
||||||
command : str
|
command : str
|
||||||
Command to run in the docker container
|
Command to run in the docker container
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> run_docker_command(lxc, "<container-name>", "<command>")
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lxc.run_command(f"docker exec -it {container} {command}")
|
lxc.run_command(f"docker exec -it {container} {command}")
|
||||||
|
|
||||||
|
|
||||||
def run_docker_compose_command(lxc: LXC, command: str, working_directory: str = None):
|
def run_docker_compose_command(lxc, command, working_directory=None):
|
||||||
"""Run a docker-compose command in the LXC
|
"""Run a docker-compose command in the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -207,14 +130,13 @@ def run_docker_compose_command(lxc: LXC, command: str, working_directory: str =
|
|||||||
The LXC object of the LXC to run the docker-compose command in
|
The LXC object of the LXC to run the docker-compose command in
|
||||||
command: str
|
command: str
|
||||||
The docker-compose command to run
|
The docker-compose command to run
|
||||||
working_directory: str, optional
|
working_directory: str
|
||||||
The working directory to run the command in
|
The working directory to run the command in
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
>>> run_docker_compose_command(lxc, "up -d", "/home/user/traefik")
|
>>> run_docker_compose_command(lxc, "up -d", "/home/user/traefik")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
docker_compose_exec = "docker-compose"
|
docker_compose_exec = "docker-compose"
|
||||||
if not lxc.has_program(docker_compose_exec):
|
if not lxc.has_program(docker_compose_exec):
|
||||||
docker_compose_exec = "docker compose"
|
docker_compose_exec = "docker compose"
|
||||||
@ -225,33 +147,16 @@ def run_docker_compose_command(lxc: LXC, command: str, working_directory: str =
|
|||||||
lxc.run_command(f"{docker_compose_exec} {command}")
|
lxc.run_command(f"{docker_compose_exec} {command}")
|
||||||
|
|
||||||
|
|
||||||
def download_file(lxc: LXC, url: str, destination: str):
|
def download_file(lxc, url, destination):
|
||||||
"""Download a file from a URL to the LXC and save it to the destination
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
The LXC object of the LXC to download the file to
|
|
||||||
url: str
|
|
||||||
URL of the file to download
|
|
||||||
destination: str
|
|
||||||
Path to the destination to save the file to
|
|
||||||
Needs to end with a trailing slash
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> download_file(lxc, "https://example.com/file.zip", "/home/user/")
|
|
||||||
|
|
||||||
"""
|
|
||||||
"""Download a file from a URL to the LXC"""
|
"""Download a file from a URL to the LXC"""
|
||||||
if type(url) is list:
|
if type(url) is list:
|
||||||
for u in url:
|
for u in url:
|
||||||
lxc.run_command(f"wget {u} --directory-prefix={destination}")
|
lxc.run_command(f"wget {u} -O {destination}")
|
||||||
else:
|
else:
|
||||||
lxc.run_command(f"wget {url} --directory-prefix={destination}")
|
lxc.run_command(f"wget {url} -O {destination}")
|
||||||
|
|
||||||
|
|
||||||
def unzip_file(lxc: LXC, path: str, destination: str = None):
|
def unzip_file(lxc, path, destination=None):
|
||||||
"""Unzip a file in the LXC
|
"""Unzip a file in the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -260,15 +165,13 @@ def unzip_file(lxc: LXC, path: str, destination: str = None):
|
|||||||
The LXC object of the LXC to unzip the file in
|
The LXC object of the LXC to unzip the file in
|
||||||
path: str
|
path: str
|
||||||
Path to the file to unzip
|
Path to the file to unzip
|
||||||
destination: str, optional
|
destination: str
|
||||||
Path to the destination to unzip the file to
|
Path to the destination to unzip the file to
|
||||||
If not specified, it will unzip the file in the same directory as the file
|
|
||||||
Needs to end with a trailing slash
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
>>> unzip_file(lxc, "/home/user/file.zip", "/home/user/extracted_files/")
|
>>> unzip_file(lxc, "/home/user/file.zip", "/home/user/destination")
|
||||||
>>> unzip_file(lxc, "/home/user/file.tar.gz", "/home/user/extracted_files/")
|
>>> unzip_file(lxc, "/home/user/file.tar.gz", "/home/user/destination")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if destination is None:
|
if destination is None:
|
||||||
@ -277,10 +180,10 @@ def unzip_file(lxc: LXC, path: str, destination: str = None):
|
|||||||
if ".zip" in path:
|
if ".zip" in path:
|
||||||
lxc.run_command(f"unzip {path} -d {destination}")
|
lxc.run_command(f"unzip {path} -d {destination}")
|
||||||
elif ".tar.gz" in path:
|
elif ".tar.gz" in path:
|
||||||
lxc.run_command(f"mkdir -p {destination} && tar -xzf {path} --directory {destination}")
|
lxc.run_command(f"tar -xzf {path} -C {destination}")
|
||||||
|
|
||||||
|
|
||||||
def install_package(lxc: LXC, package: str or list):
|
def install_package(lxc, package):
|
||||||
"""Install a package in the LXC
|
"""Install a package in the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -288,7 +191,7 @@ def install_package(lxc: LXC, package: str or list):
|
|||||||
lxc: LXC
|
lxc: LXC
|
||||||
The LXC object of the LXC to install the package in
|
The LXC object of the LXC to install the package in
|
||||||
package: str or list
|
package: str or list
|
||||||
Name(s) of the package(s) to install
|
Name of the package to install
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@ -303,7 +206,7 @@ def install_package(lxc: LXC, package: str or list):
|
|||||||
lxc.run_command(f"{proxmox_utils.get_install_package_command(lxc.get_os_name())} {package}")
|
lxc.run_command(f"{proxmox_utils.get_install_package_command(lxc.get_os_name())} {package}")
|
||||||
|
|
||||||
|
|
||||||
def remove_package(lxc: LXC, package: str or list):
|
def remove_package(lxc, package):
|
||||||
"""Remove a package in the LXC
|
"""Remove a package in the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -311,7 +214,7 @@ def remove_package(lxc: LXC, package: str or list):
|
|||||||
lxc: LXC
|
lxc: LXC
|
||||||
The LXC object of the LXC to remove the package in
|
The LXC object of the LXC to remove the package in
|
||||||
package: str or list
|
package: str or list
|
||||||
Name(s) of the package(s) to remove
|
Name of the package to remove
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@ -329,20 +232,20 @@ def remove_package(lxc: LXC, package: str or list):
|
|||||||
lxc.run_command(f"{proxmox_utils.get_remove_package_command(lxc.get_os_name())} {package}")
|
lxc.run_command(f"{proxmox_utils.get_remove_package_command(lxc.get_os_name())} {package}")
|
||||||
|
|
||||||
|
|
||||||
def replace_in_files(lxc: LXC, path: str or list, search: str, replace: str, case_sensitive: bool = False):
|
def replace_in_files(lxc, path, search, replace, case_sensitive=False):
|
||||||
"""Replace a string in one or multiples files in the LXC
|
"""Replace a string in one or multiples files in the LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
lxc : LXC
|
lxc : LXC
|
||||||
The LXC object of the LXC to replace the string in
|
The LXC object of the LXC to replace the string in
|
||||||
path : str or list of str
|
path : str or list
|
||||||
Path to the file(s) to replace the string in
|
Path to the file(s) to replace the string in
|
||||||
search : str
|
search : str
|
||||||
String to search for
|
String to search for
|
||||||
replace : str
|
replace : str
|
||||||
String to replace the search string with
|
String to replace the search string with
|
||||||
case_sensitive : bool, optional
|
case_sensitive : bool
|
||||||
Whether the search should be case sensitive or not
|
Whether the search should be case sensitive or not
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@ -355,6 +258,6 @@ def replace_in_files(lxc: LXC, path: str or list, search: str, replace: str, cas
|
|||||||
|
|
||||||
if type(path) is list:
|
if type(path) is list:
|
||||||
for p in path:
|
for p in path:
|
||||||
lxc.run_command(f"sed {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {p}")
|
lxc.run_command(f"sed -i {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {p}")
|
||||||
else:
|
else:
|
||||||
lxc.run_command(f"sed {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {path}")
|
lxc.run_command(f"sed -i {'-i' if case_sensitive else ''} 's/{search}/{replace}/g' {path}")
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from . import commands_utils, LXC
|
from . import commands_utils
|
||||||
from ..utils.resources_utils import get_path
|
|
||||||
|
|
||||||
|
|
||||||
def are_all_conditions_met(lxc: LXC):
|
def are_all_conditions_met(lxc):
|
||||||
"""
|
"""
|
||||||
Check conditions for running the creation steps for an LXC.
|
Check conditions for running the creation steps for an LXC.
|
||||||
|
|
||||||
@ -44,29 +43,28 @@ def are_all_conditions_met(lxc: LXC):
|
|||||||
return all(result)
|
return all(result)
|
||||||
|
|
||||||
|
|
||||||
def optional(var: any, placeholder: any):
|
def optional(var, placeholder):
|
||||||
"""Return a placeholder if the given variable is None, otherwise return the variable
|
"""Return a placeholder if the given variable is None, otherwise return the variable
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
var: any
|
var
|
||||||
Variable to check if it is None
|
Variable to check if it is None
|
||||||
placeholder: any
|
placeholder
|
||||||
Placeholder to return if the variable is None
|
Placeholder to return if the variable is None
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
any[any]
|
var or placeholder
|
||||||
The variable if it is not None, otherwise the placeholder
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
"""
|
||||||
if var is None:
|
if var is None:
|
||||||
return placeholder
|
return placeholder
|
||||||
else:
|
else:
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
def run_steps(lxc: LXC):
|
def run_steps(lxc):
|
||||||
"""Run the creation steps for the given LXC
|
"""Run the creation steps for the given LXC
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -87,11 +85,11 @@ def run_steps(lxc: LXC):
|
|||||||
case "file_create":
|
case "file_create":
|
||||||
commands_utils.create_file_in_lxc(lxc, step["path"], optional(step["permission"], 644))
|
commands_utils.create_file_in_lxc(lxc, step["path"], optional(step["permission"], 644))
|
||||||
case "file_copy":
|
case "file_copy":
|
||||||
commands_utils.copy_local_file_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"])
|
commands_utils.copy_local_file_to_lxc(lxc, step["path"], step["destination"])
|
||||||
case "folder":
|
case "folder":
|
||||||
commands_utils.create_folder_in_lxc(lxc, step["path"], optional(step["permission"], 755))
|
commands_utils.create_folder_in_lxc(lxc, step["path"], optional(step["permission"], 755))
|
||||||
case "folder_copy":
|
case "folder_copy":
|
||||||
commands_utils.copy_local_folder_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"])
|
commands_utils.copy_local_folder_to_lxc(lxc, step["path"], step["destination"])
|
||||||
case "command":
|
case "command":
|
||||||
lxc.run_command(command=step["command"], working_directory=optional(step["working_directory"], None))
|
lxc.run_command(command=step["command"], working_directory=optional(step["working_directory"], None))
|
||||||
case "docker":
|
case "docker":
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from . import LXC
|
from . import LXC
|
||||||
|
|
||||||
@ -18,12 +17,12 @@ def get_all_lxcs():
|
|||||||
return lxcs
|
return lxcs
|
||||||
|
|
||||||
|
|
||||||
def get_lxc(lxc_id: int):
|
def get_lxc(lxc_id):
|
||||||
"""Get LXC by ID
|
"""Get LXC by ID
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
lxc_id : int
|
lxc_id : str
|
||||||
ID of the LXC to get
|
ID of the LXC to get
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@ -39,23 +38,23 @@ def get_lxc(lxc_id: int):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def load_lxc(lxc_id: int, filepath: Path):
|
def load_lxc(file, lxc_id):
|
||||||
"""Load LXC from JSON file
|
"""Load LXC from JSON file
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
lxc_id : int
|
file : str
|
||||||
|
Path to the JSON file of the LXC to load
|
||||||
|
lxc_id : str
|
||||||
ID of the LXC to load
|
ID of the LXC to load
|
||||||
filepath : pathlib.Path
|
|
||||||
Path object to the JSON file of the LXC to load
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
>>> load_lxc(100, Path("<full-path>/resources/lxc/100/config.json"))
|
>>> load_lxc("./resources/lxc/100/config.json", "100")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Load JSON data
|
# Load JSON data
|
||||||
data = json.loads(filepath.read_text())
|
data = json.loads(file)
|
||||||
|
|
||||||
# Extract values from JSON
|
# Extract values from JSON
|
||||||
lxc_id = lxc_id
|
lxc_id = lxc_id
|
||||||
|
14
src/main.py
14
src/main.py
@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
import os
|
||||||
|
|
||||||
from . import project_path
|
from . import project_path
|
||||||
from .lxc.utils import load_lxc, get_all_lxcs
|
from .lxc.utils import load_lxc, get_all_lxcs
|
||||||
@ -7,17 +7,19 @@ from .lxc.utils import load_lxc, get_all_lxcs
|
|||||||
|
|
||||||
def run():
|
def run():
|
||||||
# Read all files in the resources directory
|
# Read all files in the resources directory
|
||||||
resources = Path(project_path).joinpath("resources").glob("*")
|
resources = os.listdir(project_path / "resources")
|
||||||
|
|
||||||
# Go through each LXC file
|
# Go through each LXC file
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
if resource == "lxc":
|
if resource == "lxc":
|
||||||
# Read all files in the LXC directory
|
# Read all files in the LXC directory
|
||||||
lxc_folders = Path(project_path).joinpath("resources", "lxc").glob("*")
|
lxc_folders = os.listdir(project_path / "resources" / "lxc")
|
||||||
for lxc_folder in lxc_folders:
|
for lxc_folder in lxc_folders:
|
||||||
lxc_file = Path(project_path).joinpath("resources", "lxc", lxc_folder, "config.json")
|
lxc_file = os.path.join(project_path / "resources" / "lxc", lxc_folder, "config.json")
|
||||||
|
# Open the file
|
||||||
load_lxc(filepath=lxc_file, lxc_id=int(lxc_folder.name))
|
with open(lxc_file, "r") as file:
|
||||||
|
# Load the LXC
|
||||||
|
load_lxc(file.read(), lxc_id=lxc_folder)
|
||||||
|
|
||||||
for lxc in get_all_lxcs():
|
for lxc in get_all_lxcs():
|
||||||
logging.info(f"Loading LXC {lxc.lxc_id}")
|
logging.info(f"Loading LXC {lxc.lxc_id}")
|
||||||
|
@ -1,84 +1,56 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .. import project_path
|
from .. import project_path
|
||||||
|
|
||||||
|
|
||||||
def get_pve_version():
|
def get_pve_version():
|
||||||
"""Get PVE version
|
"""
|
||||||
|
Get PVE version
|
||||||
Returns
|
:return: pve version
|
||||||
-------
|
|
||||||
str
|
|
||||||
PVE version
|
|
||||||
"""
|
"""
|
||||||
return run_command_on_pve(command="pveversion", warn_exit_status=True)
|
return run_command_on_pve(command="pveversion", warn_exit_status=True)
|
||||||
|
|
||||||
|
|
||||||
def get_pve_hostname():
|
def get_pve_hostname():
|
||||||
"""Get PVE hostname
|
"""
|
||||||
|
Get PVE hostname
|
||||||
Returns
|
:return: pve hostname
|
||||||
-------
|
|
||||||
str
|
|
||||||
PVE hostname
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return run_command_on_pve(command="hostname", warn_exit_status=True)
|
return run_command_on_pve(command="hostname", warn_exit_status=True)
|
||||||
|
|
||||||
|
|
||||||
def does_lxc_exist(lxc_id: int):
|
def does_lxc_exist(lxc_id):
|
||||||
"""Check if an LXC exists with the given ID
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc_id: int
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
bool
|
|
||||||
Does LXC exist?
|
|
||||||
"""
|
"""
|
||||||
|
Check if LXC exists
|
||||||
|
|
||||||
# TODO: only check in VMID column
|
:param lxc_id: lxc id
|
||||||
return str(lxc_id) in run_command_on_pve(command=f"pct list | awk '/{lxc_id}/'")
|
:return: does lxc exists
|
||||||
|
|
||||||
|
|
||||||
def does_qemu_vm_exist(vm_id: int):
|
|
||||||
"""Check if a QEMU VM exists with the given ID
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
vm_id: int
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
bool
|
|
||||||
Does QEMU VM exist?
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: only check in VMID column
|
# TODO: only check in VMID column
|
||||||
return str(vm_id) in run_command_on_pve(command=f"qm list {vm_id}")
|
return lxc_id in run_command_on_pve(command=f"pct list | awk '/{lxc_id}/'")
|
||||||
|
|
||||||
|
|
||||||
def execute_tteck_script(script_url: str, env_variables: list):
|
def does_qemu_vm_exist(vm_id):
|
||||||
"""Execute TTECK script with already filled environment variables to run silently (non-interactive)
|
"""
|
||||||
|
Check if QEMU VM exists
|
||||||
|
|
||||||
Parameters
|
:param vm_id: vm id
|
||||||
----------
|
:return: does qemu vm exists
|
||||||
script_url: str
|
"""
|
||||||
script url (github or other)
|
# TODO: only check in VMID column
|
||||||
env_variables: list
|
return vm_id in run_command_on_pve(command=f"qm list {vm_id}")
|
||||||
list of environment variables
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
def execute_tteck_script(script_url, env_variables):
|
||||||
int
|
"""
|
||||||
status code
|
Execute TTECK script with already filled environment variables to run silently (non-interactive)
|
||||||
|
|
||||||
|
:param script_url: script url (github or other)
|
||||||
|
:param env_variables: list of environment variables
|
||||||
|
:return: status code
|
||||||
"""
|
"""
|
||||||
|
|
||||||
env_variables = " ".join(env_variables)
|
env_variables = " ".join(env_variables)
|
||||||
@ -87,46 +59,20 @@ def execute_tteck_script(script_url: str, env_variables: list):
|
|||||||
|
|
||||||
|
|
||||||
def get_config():
|
def get_config():
|
||||||
"""Get the JSON content of config.json file as a dict
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict
|
|
||||||
JSON content of config.json file
|
|
||||||
"""
|
|
||||||
|
|
||||||
config_file = os.path.join(project_path / "resources" / "config.json")
|
config_file = os.path.join(project_path / "resources" / "config.json")
|
||||||
with open(config_file, "r") as file:
|
with open(config_file, "r") as file:
|
||||||
return json.loads(file.read())
|
return json.loads(file.read())
|
||||||
|
|
||||||
|
|
||||||
def run_command_on_pve(command: str, warn_exit_status: bool = False, only_code: bool = False, local: bool = False):
|
def run_command_on_pve(command, warn_exit_status=False, only_code=False, local=False):
|
||||||
"""Run command on the Proxmox VE host
|
"""
|
||||||
|
Run command on PVE
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
command: str
|
|
||||||
command to run
|
|
||||||
warn_exit_status: bool, optional
|
|
||||||
should an exception be thrown if the exit status is not 0
|
|
||||||
only_code: bool, optional
|
|
||||||
should it only return the exit code and not the stdout
|
|
||||||
local: bool, optional
|
|
||||||
should it be run locally not via ssh even with local mode off in the pve section of config.json
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
command output by default
|
|
||||||
int
|
|
||||||
command exit code if only_code is True
|
|
||||||
|
|
||||||
Raises
|
|
||||||
------
|
|
||||||
Exception
|
|
||||||
if the exit status is not 0 and warn_exit_status is True
|
|
||||||
|
|
||||||
|
:param local: should be run locally not via ssh even with local mode off
|
||||||
|
:param only_code: should we only return the exit code and not the stdout
|
||||||
|
:param warn_exit_status: should an exception be thrown if the exit status is not 0
|
||||||
|
:param command: command
|
||||||
|
:return: command
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Get config
|
# Get config
|
||||||
@ -135,7 +81,7 @@ def run_command_on_pve(command: str, warn_exit_status: bool = False, only_code:
|
|||||||
# Check if PVE is local or remote
|
# Check if PVE is local or remote
|
||||||
if config['pve']['local'] or local:
|
if config['pve']['local'] or local:
|
||||||
# Run command and return output (not as bytes)
|
# Run command and return output (not as bytes)
|
||||||
logging.debug(f"Running command on PVE/Machine running this script (locally): {command}")
|
logging.debug(f"Running command on PVE (locally): {command}")
|
||||||
|
|
||||||
command = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
command = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
encoding="utf-8")
|
encoding="utf-8")
|
||||||
@ -171,45 +117,12 @@ def run_command_on_pve(command: str, warn_exit_status: bool = False, only_code:
|
|||||||
return command.stdout.rstrip()
|
return command.stdout.rstrip()
|
||||||
|
|
||||||
|
|
||||||
def run_command_locally(command: str, warn_exit_status: bool = False, only_code: bool = False):
|
def run_command_locally(command, warn_exit_status=False, only_code=False):
|
||||||
"""Force running command locally even if local mode is off in config.json
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
run_command_on_pve
|
|
||||||
"""
|
|
||||||
|
|
||||||
run_command_on_pve(command=command, warn_exit_status=warn_exit_status, only_code=only_code, local=True)
|
run_command_on_pve(command=command, warn_exit_status=warn_exit_status, only_code=only_code, local=True)
|
||||||
|
|
||||||
|
|
||||||
def run_command_ssh(command: str, host: str, username: str, port: int = 22, warn_exit_status: bool = False,
|
def run_command_ssh(command, host, username, port=22, warn_exit_status=False, only_code=False):
|
||||||
only_code: bool = False):
|
# Run command on PVE via SSH and return output
|
||||||
"""Run command on a remote host via SSH
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
command: str
|
|
||||||
command to run
|
|
||||||
host: str
|
|
||||||
host to run the command on
|
|
||||||
username: str
|
|
||||||
username to use to connect to the host (usually root for lxc and vm)
|
|
||||||
port: int, optional
|
|
||||||
port to use to connect to the host (default: 22)
|
|
||||||
warn_exit_status: bool, optional
|
|
||||||
should an exception be thrown if the exit status is not 0
|
|
||||||
only_code: bool, optional
|
|
||||||
should it only return the exit code and not the stdout
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
command output by default
|
|
||||||
int
|
|
||||||
command exit code if only_code is True
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
logging.debug(f"Running command on host {host} (ssh): {command}")
|
logging.debug(f"Running command on host {host} (ssh): {command}")
|
||||||
|
|
||||||
# catch errors code
|
# catch errors code
|
||||||
@ -226,24 +139,12 @@ def run_command_ssh(command: str, host: str, username: str, port: int = 22, warn
|
|||||||
return command.stdout.rstrip()
|
return command.stdout.rstrip()
|
||||||
|
|
||||||
|
|
||||||
def get_install_package_command(distribution: str):
|
def get_install_package_command(distribution):
|
||||||
"""Retrieve the correct command to install a package based on the distribution specified
|
"""
|
||||||
It supports all the distribution supported by Proxmox VE (from the pct command documentation).
|
Get the install package without interaction command based on the distribution.
|
||||||
Debian, Ubuntu, CentOS, Fedora, Gentoo, Alpine, ArchLinux, Devuan, NixOS, OpenSUSE
|
|
||||||
|
|
||||||
Parameters
|
:param distribution: Distribution name
|
||||||
----------
|
:return: Install package command
|
||||||
distribution: str
|
|
||||||
Name of the distribution as specific in the proxmox pct command documentation
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
https://pve.proxmox.com/pve-docs/pct.1.html
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
Beginning of the command to install a package, the package name should be appended to it
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
distribution = distribution.lower()
|
distribution = distribution.lower()
|
||||||
@ -272,24 +173,12 @@ def get_install_package_command(distribution: str):
|
|||||||
raise Exception(f"Unsupported distribution: {distribution}")
|
raise Exception(f"Unsupported distribution: {distribution}")
|
||||||
|
|
||||||
|
|
||||||
def get_remove_package_command(distribution: str):
|
def get_remove_package_command(distribution):
|
||||||
"""Retrieve the correct command to uninstall a package based on the distribution specified
|
"""
|
||||||
It supports all the distribution supported by Proxmox VE (from the pct command documentation).
|
Get the remove package without interaction command based on the distribution.
|
||||||
Debian, Ubuntu, CentOS, Fedora, Gentoo, Alpine, ArchLinux, Devuan, NixOS, OpenSUSE
|
|
||||||
|
|
||||||
Parameters
|
:param distribution: Distribution name
|
||||||
----------
|
:return: Remove package command
|
||||||
distribution: str
|
|
||||||
Name of the distribution as specific in the proxmox pct command documentation
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
https://pve.proxmox.com/pve-docs/pct.1.html
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
Beginning of the command to uninstall a package, the package name should be appended to it
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
distribution = distribution.lower()
|
distribution = distribution.lower()
|
||||||
@ -319,59 +208,8 @@ def get_remove_package_command(distribution: str):
|
|||||||
|
|
||||||
|
|
||||||
def get_ssh_public_key():
|
def get_ssh_public_key():
|
||||||
""" Retrieve the SSH public key of the machine running this script
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
SSH public key
|
|
||||||
"""
|
"""
|
||||||
|
Get SSH public key
|
||||||
# TODO: maybe implement custom path for the key
|
:return: ssh public key
|
||||||
|
"""
|
||||||
return run_command_on_pve(command="cat ~/.ssh/id_rsa.pub", warn_exit_status=True)
|
return run_command_on_pve(command="cat ~/.ssh/id_rsa.pub", warn_exit_status=True)
|
||||||
|
|
||||||
|
|
||||||
def copy_file_to_pve(path: Path, destination: str):
|
|
||||||
"""Copy a local file (on the machine running this script) to the PVE
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
path: pathlib.Path
|
|
||||||
Path object to file on local machine
|
|
||||||
destination: str
|
|
||||||
Destination on PVE
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> copy_file_to_pve(Path("/home/user/file.txt"), "/root/file.txt")
|
|
||||||
"""
|
|
||||||
|
|
||||||
config = get_config()
|
|
||||||
run_command_locally(f"scp {str(path)} {config['pve']['root']}@{config['pve']['host']}:{destination}")
|
|
||||||
|
|
||||||
|
|
||||||
def copy_file_to_lxc(lxc_id: int, path: Path, destination: str):
|
|
||||||
"""Copy a local file (on the machine running this script) to the PVE and finally to the LXC
|
|
||||||
It works by copying the script from the local machine to the PVE using SCP and
|
|
||||||
then using "pct push" to send it to the LXC.
|
|
||||||
Sometimes SSH is not installed on freshs LXC, so by using pct push we are sure that it will work.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc_id: int
|
|
||||||
LXC ID
|
|
||||||
path: pathlib.Path
|
|
||||||
Path object to file on local machine
|
|
||||||
destination: str
|
|
||||||
Destination on LXC
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> copy_file_to_lxc(100, Path("/home/user/file.txt"), "/root/")
|
|
||||||
"""
|
|
||||||
|
|
||||||
file = path.name
|
|
||||||
temp_path = f"/tmp/proxmoxdeployscripts/{file}"
|
|
||||||
|
|
||||||
copy_file_to_pve(path, temp_path)
|
|
||||||
run_command_on_pve(f"pct push {lxc_id} {temp_path} {destination}")
|
|
||||||
|
@ -1,44 +1,15 @@
|
|||||||
import pathlib
|
import os
|
||||||
|
|
||||||
from .. import project_path
|
from .. import project_path
|
||||||
from ..lxc import LXC
|
|
||||||
|
|
||||||
|
|
||||||
def get_path(lxc: LXC, path: str):
|
def get_path(lxc, path):
|
||||||
"""Returns the complete path to a global, protected or LXC-relative resource file.
|
parent = os.path.join(project_path / "resources")
|
||||||
|
|
||||||
For example, if the path is "global/test.txt", the function will return the complete path to
|
if path.startswith("/global/"):
|
||||||
the file in the resources/ folder.
|
|
||||||
Another example, if the path is "protected/test.txt", the function will return the complete path to
|
|
||||||
the file in the protected_resources/ folder.
|
|
||||||
Other folders can be added like "global/scripts/test.sh" or "protected/scripts/test.sh".
|
|
||||||
|
|
||||||
If none of the above conditions are met, the function will return the path relative to the LXC folder
|
|
||||||
depending on the given LXC object.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
lxc: LXC
|
|
||||||
LXC object to use to get the relative path
|
|
||||||
Can be omitted if you know what you are doing and the path is either global or protected.
|
|
||||||
path: str
|
|
||||||
Path to the resource file
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
pathlib.Path
|
|
||||||
Complete path to the resource file
|
|
||||||
|
|
||||||
"""
|
|
||||||
parent = pathlib.Path(project_path).joinpath("resources")
|
|
||||||
|
|
||||||
if path.startswith("global/"):
|
|
||||||
# Use global folder
|
# Use global folder
|
||||||
return parent.joinpath(path[len("global/"):])
|
return parent + "/scripts/" + path[len("/global/"):]
|
||||||
elif path.startswith("protected/"):
|
|
||||||
# Use protected folder
|
|
||||||
return parent.parent.joinpath("protected_resources", path[len("protected/"):])
|
|
||||||
else:
|
else:
|
||||||
# Use VM/LXC folder
|
# Use VM/LXC folder
|
||||||
lxc_id = lxc.get_id()
|
lxc_id = lxc.get_id()
|
||||||
return parent.joinpath("lxc", lxc_id, path)
|
return parent + f"/resources/lxc/{lxc_id}/{path}"
|
||||||
|
Loading…
Reference in New Issue
Block a user