mirror of
https://github.com/gyptazy/ProxLB.git
synced 2026-04-06 04:41:58 +02:00
Compare commits
66 Commits
packaging/
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
360920433a | ||
|
|
4e39c4cba2 | ||
|
|
baaf01e39b | ||
|
|
0acc822777 | ||
|
|
53723a0fdd | ||
|
|
f451afd6d7 | ||
|
|
e2aeeffdde | ||
|
|
3d33d5db44 | ||
|
|
12be935115 | ||
|
|
038d1e0fc3 | ||
|
|
b3dd4c7e3a | ||
|
|
8de8a263db | ||
|
|
cf357445c6 | ||
|
|
9d34d5bcd5 | ||
|
|
4a3f10ccc2 | ||
|
|
968d49adb0 | ||
|
|
585f6d511b | ||
|
|
c0558dda3b | ||
|
|
a8470aca52 | ||
|
|
d5f1853d15 | ||
|
|
3da8f0638e | ||
|
|
513e74c2e2 | ||
|
|
3841cd80ee | ||
|
|
86c9134b10 | ||
|
|
ae2e15c2ae | ||
|
|
1230aa2ef4 | ||
|
|
f00edb6387 | ||
|
|
4402a8775a | ||
|
|
8dc538eb1e | ||
|
|
26add60e70 | ||
|
|
5b6897d250 | ||
|
|
df1b9cb179 | ||
|
|
57282a0273 | ||
|
|
e0ed3773af | ||
|
|
fb50afe473 | ||
|
|
5dc747eb31 | ||
|
|
87b82cfccb | ||
|
|
c73bbd1538 | ||
|
|
19e2d9e3ae | ||
|
|
bb990f024c | ||
|
|
9413d46bc4 | ||
|
|
b48fd07f83 | ||
|
|
a08f088bbd | ||
|
|
31c455c835 | ||
|
|
82704ce887 | ||
|
|
8a08dd5a51 | ||
|
|
389950c150 | ||
|
|
8d004efc03 | ||
|
|
3b5f6da2a5 | ||
|
|
2f7f83f68e | ||
|
|
8d46ebf758 | ||
|
|
dc499c3fb6 | ||
|
|
17da45e0aa | ||
|
|
680124a23e | ||
|
|
b12cb96b29 | ||
|
|
66b7e58bdc | ||
|
|
e303bddf2e | ||
|
|
7c0fd9d76a | ||
|
|
5aa1e8ee04 | ||
|
|
110afb3c5f | ||
|
|
9eee27afa4 | ||
|
|
b3f9a2b04b | ||
|
|
6c38f9ea07 | ||
|
|
4900cfb53b | ||
|
|
e2a33e9805 | ||
|
|
1caf628e96 |
@@ -1,2 +0,0 @@
|
||||
feature:
|
||||
- Add Proxmox API authentication support. [#125]
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
ref: 'development'
|
||||
|
||||
- name: Set up Docker with Debian image
|
||||
run: |
|
||||
@@ -39,13 +39,9 @@ jobs:
|
||||
docker run --rm -v $(pwd):/workspace -w /workspace debian:latest bash -c "
|
||||
# Install dependencies
|
||||
apt-get update && \
|
||||
apt-get install -y python3 python3-setuptools debhelper dh-python python3-pip python3-stdeb python3-proxmoxer python3-requests python3-urllib3 devscripts python3-all && \
|
||||
# Build package using stdeb / setuptools
|
||||
# python3 setup.py --command-packages=stdeb.command bdist_deb && \
|
||||
# Build native package
|
||||
dpkg-buildpackage -us -uc && \
|
||||
mkdir package && \
|
||||
mv ../*.deb package/ && \
|
||||
apt-get install -y python3 python3-setuptools debhelper dh-python python3-pip python3-stdeb python3-proxmoxer python3-requests python3-urllib3 && \
|
||||
# Build package
|
||||
python3 setup.py --command-packages=stdeb.command bdist_deb && \
|
||||
echo 'OK: Debian package successfully created.'
|
||||
"
|
||||
|
||||
@@ -53,7 +49,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-package
|
||||
path: package/*.deb
|
||||
path: deb_dist/*.deb
|
||||
|
||||
integration-test-debian:
|
||||
needs: build-package-debian
|
||||
@@ -63,16 +59,15 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: debian-package
|
||||
path: package/
|
||||
path: deb_dist/
|
||||
|
||||
- name: Set up Docker with Debian image
|
||||
run: docker pull debian:latest
|
||||
|
||||
- name: Install and test Debian package in Docker container
|
||||
run: |
|
||||
docker run --rm -v $(pwd)/package:/package -w /package debian:latest bash -c "
|
||||
docker run --rm -v $(pwd)/deb_dist:/deb_dist -w /deb_dist debian:latest bash -c "
|
||||
apt-get update && \
|
||||
apt-get install -y systemd && \
|
||||
apt-get install -y ./proxlb*.deb && \
|
||||
apt-get install -y ./python3-proxlb*.deb && \
|
||||
python3 -c 'import proxlb; print(\"OK: Debian package successfully installed.\")'
|
||||
"
|
||||
16
README.md
16
README.md
@@ -141,9 +141,7 @@ The following options can be set in the configuration file `proxlb.yaml`:
|
||||
| `proxmox_api` | | | | |
|
||||
| | hosts | ['virt01.example.com', '10.10.10.10', 'fe01::bad:code::cafe'] | `List` | List of Proxmox nodes. Can be IPv4, IPv6 or mixed. |
|
||||
| | user | root@pam | `Str` | Username for the API. |
|
||||
| | pass | FooBar | `Str` | Password for the API. (Recommended: Use API token authorization!) |
|
||||
| | token_id | proxlb | `Str` | Token ID of the user for the API. |
|
||||
| | token_secret | 430e308f-1337-1337-beef-1337beefcafe | `Str` | Secret of the token ID for the API. |
|
||||
| | pass | FooBar | `Str` | Password for the API. |
|
||||
| | ssl_verification | True | `Bool` | Validate SSL certificates (1) or ignore (0). (default: 1, type: bool) |
|
||||
| | timeout | 10 | `Int` | Timeout for the Proxmox API in sec. (default: 10) |
|
||||
| `proxmox_cluster` | | | | |
|
||||
@@ -152,7 +150,7 @@ The following options can be set in the configuration file `proxlb.yaml`:
|
||||
| | overprovisioning | False | `Bool` | Avoids balancing when nodes would become overprovisioned. |
|
||||
| `balancing` | | | | |
|
||||
| | enable | True | `Bool` | Enables the guest balancing. (default: True)|
|
||||
| | enforce_affinity | True | `Bool` | Enforcing affinity/anti-affinity rules but balancing might become worse. (default: False) |
|
||||
| | force | True | `Bool` | Enforcing affinity/anti-affinity rules but balancing might become worse. (default: False) |
|
||||
| | parallel | False | `Bool` | If guests should be moved in parallel or sequentially. (default: False)|
|
||||
| | live | True | `Bool` | If guests should be moved live or shutdown. (default: True)|
|
||||
| | with_local_disks | True | `Bool` | If balancing of guests should include local disks (default: True)|
|
||||
@@ -171,9 +169,7 @@ An example of the configuration file looks like:
|
||||
proxmox_api:
|
||||
hosts: ['virt01.example.com', '10.10.10.10', 'fe01::bad:code::cafe']
|
||||
user: root@pam
|
||||
#pass: crazyPassw0rd!
|
||||
token_id: proxlb
|
||||
token_secret: 430e308f-1337-1337-beef-1337beefcafe
|
||||
pass: crazyPassw0rd!
|
||||
ssl_verification: False
|
||||
timeout: 10
|
||||
|
||||
@@ -184,7 +180,7 @@ proxmox_cluster:
|
||||
|
||||
balancing:
|
||||
enable: True
|
||||
enforce_affinity: False
|
||||
force: False
|
||||
parallel: False
|
||||
live: True
|
||||
with_local_disks: True
|
||||
@@ -252,10 +248,10 @@ The `maintenance_nodes` option allows operators to designate one or more Proxmox
|
||||
Bugs can be reported via the GitHub issue tracker [here](https://github.com/gyptazy/ProxLB/issues). You may also report bugs via email or deliver PRs to fix them on your own. Therefore, you might also see the contributing chapter.
|
||||
|
||||
### Contributing
|
||||
Feel free to add further documentation, to adjust already existing one or to contribute with code. Please take care about the style guide and naming conventions. You can find more in our [CONTRIBUTING.md](https://github.com/gyptazy/ProxLB/blob/main/CONTRIBUTING.md) file.
|
||||
Feel free to add further documentation, to adjust already existing one or to contribute with code. Please take care about the style guide and naming conventions. You can find more in our [CONTRIBUTING.md](https://github.com/gyptazy/ProxLB/blob/development/CONTRIBUTING.md) file.
|
||||
|
||||
### Documentation
|
||||
You can also find additional and more detailed documentation within the [docs/](https://github.com/gyptazy/ProxLB/tree/main/docs) directory.
|
||||
You can also find additional and more detailed documentation within the [docs/](https://github.com/gyptazy/ProxLB/tree/development/docs) directory.
|
||||
|
||||
### Support
|
||||
If you need assistance or have any questions, we offer support through our dedicated [chat room](https://matrix.to/#/#proxlb:gyptazy.com) in Matrix or [Discord](https://discord.gg/JemGu7WbfQ). Join our community for real-time help, advice, and discussions. The Matrix and Discord room are bridged to ensure that the communication is not splitted - so simply feel free to join which fits most to you!
|
||||
|
||||
@@ -2,9 +2,6 @@ proxmox_api:
|
||||
hosts: ['virt01.example.com', '10.10.10.10', 'fe01::bad:code::cafe']
|
||||
user: root@pam
|
||||
pass: crazyPassw0rd!
|
||||
# API Token method
|
||||
# token_id: proxlb
|
||||
# token_secret: 430e308f-1337-1337-beef-1337beefcafe
|
||||
ssl_verification: False
|
||||
timeout: 10
|
||||
|
||||
@@ -15,7 +12,7 @@ proxmox_cluster:
|
||||
|
||||
balancing:
|
||||
enable: True
|
||||
enforce_affinity: False
|
||||
force: False
|
||||
parallel: False
|
||||
live: True
|
||||
with_local_disks: True
|
||||
@@ -28,4 +25,4 @@ balancing:
|
||||
service:
|
||||
daemon: False
|
||||
schedule: 12
|
||||
log_level: DEBUG
|
||||
log_level: DEBUG
|
||||
5
debian/changelog
vendored
5
debian/changelog
vendored
@@ -1,5 +0,0 @@
|
||||
proxlb (1.1.0) stable; urgency=medium
|
||||
|
||||
* Refactored code base of ProxLB. (Closes: #114)
|
||||
|
||||
-- Florian Paul Azim Hoberg <gyptazy@gyptazy.com> Mon, 17 Mar 2025 18:55:02 +0000
|
||||
12
debian/control
vendored
12
debian/control
vendored
@@ -1,12 +0,0 @@
|
||||
Source: proxlb
|
||||
Maintainer: Florian Paul Azim Hoberg <gyptazy@gyptazy.com>
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Standards-Version: 4.5.0
|
||||
Build-Depends: debhelper-compat (= 13), dh-python, python3-all, python3-setuptools
|
||||
|
||||
Package: proxlb
|
||||
Architecture: all
|
||||
Depends: ${python3:Depends}, ${misc:Depends}, python3-requests, python3-urllib3, python3-proxmoxer, python3-yaml
|
||||
Description: A DRS alike Load Balancer for Proxmox Clusters
|
||||
An advanced DRS alike loadbalancer for Proxmox clusters that also supports maintenance modes and affinity/anti-affinity rules.
|
||||
2
debian/install
vendored
2
debian/install
vendored
@@ -1,2 +0,0 @@
|
||||
proxlb /usr/lib/python3/dist-packages/
|
||||
service/proxlb.service /lib/systemd/system/
|
||||
16
debian/postinst
vendored
16
debian/postinst
vendored
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
if [ "$1" = "configure" ]; then
|
||||
systemctl enable proxlb.service
|
||||
systemctl restart proxlb.service || true
|
||||
|
||||
# Create the 'plb' user if it does not exist
|
||||
if ! id "plb" &>/dev/null; then
|
||||
useradd --system --home /var/lib/proxlb --create-home --shell /usr/sbin/nologin --group nogroup plb
|
||||
echo "User 'plb' created."
|
||||
else
|
||||
echo "User 'plb' already exists, skipping creation."
|
||||
fi
|
||||
fi
|
||||
16
debian/prerm
vendored
16
debian/prerm
vendored
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
if [ "$1" = "remove" ]; then
|
||||
systemctl stop proxlb.service || true
|
||||
systemctl disable proxlb.service || true
|
||||
|
||||
# Remove the 'plb' user if it exists
|
||||
if id "plb" &>/dev/null; then
|
||||
userdel --remove plb
|
||||
echo "User 'plb' removed."
|
||||
else
|
||||
echo "User 'plb' does not exist, skipping removal."
|
||||
fi
|
||||
fi
|
||||
4
debian/rules
vendored
4
debian/rules
vendored
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@ --with python3 --buildsystem=pybuild
|
||||
|
||||
1
debian/source/format
vendored
1
debian/source/format
vendored
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
@@ -7,11 +7,6 @@ perform balancing actions based on the configuration provided. It also includes
|
||||
parser for handling command-line arguments and a custom logger for systemd integration.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import logging
|
||||
from utils.logger import SystemdLogger
|
||||
from utils.cli_parser import CliParser
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
"""
|
||||
The Balancing class is responsible for processing workloads on Proxmox clusters.
|
||||
It processes the previously generated data (held in proxlb_data) and moves guests
|
||||
and other supported types across Proxmox clusters based on the defined values by an operator.
|
||||
The balancing class is responsible for processing workloads on Proxmox clusters.
|
||||
The previously generated data (hold in proxlb_data) will processed and guests and
|
||||
other supported types will be moved across Proxmox clusters based on the defined
|
||||
values by an operator.
|
||||
"""
|
||||
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import proxmoxer
|
||||
import time
|
||||
from utils.logger import SystemdLogger
|
||||
@@ -24,23 +19,6 @@ class Balancing:
|
||||
The previously generated data (hold in proxlb_data) will processed and guests and
|
||||
other supported types will be moved across Proxmox clusters based on the defined
|
||||
values by an operator.
|
||||
|
||||
Methods:
|
||||
__init__(self, proxmox_api: any, proxlb_data: Dict[str, Any]):
|
||||
Initializes the Balancing class with the provided ProxLB data and initiates the rebalancing
|
||||
process for guests.
|
||||
|
||||
exec_rebalancing_vm(self, proxmox_api: any, proxlb_data: Dict[str, Any], guest_name: str) -> None:
|
||||
Executes the rebalancing of a virtual machine (VM) to a new node within the cluster. Logs the migration
|
||||
process and handles exceptions.
|
||||
|
||||
exec_rebalancing_ct(self, proxmox_api: any, proxlb_data: Dict[str, Any], guest_name: str) -> None:
|
||||
Executes the rebalancing of a container (CT) to a new node within the cluster. Logs the migration
|
||||
process and handles exceptions.
|
||||
|
||||
get_rebalancing_job_status(self, proxmox_api: any, proxlb_data: Dict[str, Any], guest_name: str, guest_current_node: str, job_id: int, retry_counter: int = 1) -> bool:
|
||||
Monitors the status of a rebalancing job on a Proxmox node until it completes or a timeout
|
||||
is reached. Returns True if the job completed successfully, False otherwise.
|
||||
"""
|
||||
|
||||
def __init__(self, proxmox_api: any, proxlb_data: Dict[str, Any]):
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
"""
|
||||
The Calculations class is responsible for handling the balancing of virtual machines (VMs)
|
||||
The calculation class is responsible for handling the balancing of virtual machines (VMs)
|
||||
and containers (CTs) across all available nodes in a Proxmox cluster. It provides methods
|
||||
to calculate the optimal distribution of VMs and CTs based on the provided data.
|
||||
"""
|
||||
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import sys
|
||||
from typing import Dict, Any
|
||||
from utils.logger import SystemdLogger
|
||||
@@ -22,37 +16,6 @@ class Calculations:
|
||||
The calculation class is responsible for handling the balancing of virtual machines (VMs)
|
||||
and containers (CTs) across all available nodes in a Proxmox cluster. It provides methods
|
||||
to calculate the optimal distribution of VMs and CTs based on the provided data.
|
||||
|
||||
Methods:
|
||||
__init__(proxlb_data: Dict[str, Any]):
|
||||
Initializes the Calculation class with the provided ProxLB data.
|
||||
|
||||
set_node_assignments(proxlb_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
Sets the assigned resources of the nodes based on the current assigned
|
||||
guest resources by their created groups as an initial base.
|
||||
|
||||
get_balanciness(proxlb_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
Gets the balanciness for further actions where the highest and lowest
|
||||
usage or assignments of Proxmox nodes are compared.
|
||||
|
||||
get_most_free_node(proxlb_data: Dict[str, Any], return_node: bool = False) -> Dict[str, Any]:
|
||||
Gets the name of the Proxmox node in the cluster with the most free resources based on
|
||||
the user-defined method (e.g., memory) and mode (e.g., used).
|
||||
|
||||
relocate_guests_on_maintenance_nodes(proxlb_data: Dict[str, Any]):
|
||||
Relocates guests that are currently on nodes marked for maintenance to
|
||||
nodes with the most available resources.
|
||||
|
||||
relocate_guests(proxlb_data: Dict[str, Any]):
|
||||
Relocates guests within the provided data structure to ensure affinity groups are
|
||||
placed on nodes with the most free resources.
|
||||
|
||||
val_anti_affinity(proxlb_data: Dict[str, Any], guest_name: str):
|
||||
Validates and assigns nodes to guests based on anti-affinity rules.
|
||||
|
||||
update_node_resources(proxlb_data):
|
||||
Updates the resource allocation and usage statistics for nodes when a guest
|
||||
is moved from one node to another.
|
||||
"""
|
||||
|
||||
def __init__(self, proxlb_data: Dict[str, Any]):
|
||||
@@ -207,13 +170,13 @@ class Calculations:
|
||||
None
|
||||
"""
|
||||
logger.debug("Starting: relocate_guests.")
|
||||
if proxlb_data["meta"]["balancing"]["balance"] or proxlb_data["meta"]["balancing"]["enforce_affinity"]:
|
||||
if proxlb_data["meta"]["balancing"]["balance"] or proxlb_data["meta"]["balancing"]["force"]:
|
||||
|
||||
if proxlb_data["meta"]["balancing"].get("balance", False):
|
||||
logger.debug("Balancing of guests will be performt. Reason: balanciness")
|
||||
|
||||
if proxlb_data["meta"]["balancing"].get("enforce_affinity", False):
|
||||
logger.debug("Balancing of guests will be performt. Reason: enforce affinity balancing")
|
||||
if proxlb_data["meta"]["balancing"].get("force", False):
|
||||
logger.debug("Balancing of guests will be performt. Reason: force balancing")
|
||||
|
||||
for group_name in proxlb_data["groups"]["affinity"]:
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
"""
|
||||
The Groups class is responsible for handling the correlations between the guests
|
||||
and their groups, such as affinity and anti-affinity groups. It ensures proper balancing
|
||||
by grouping guests and evaluating them for further balancing. The class provides methods
|
||||
to initialize with ProxLB data and to generate groups based on guest and node data.
|
||||
The groups class is responsible for handling the correlations between the guests
|
||||
and their groups like affinity and anti-affinity groups. To ensure a proper balancing
|
||||
guests will ge grouped and then evaluated for further balancing.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
from typing import Dict, Any
|
||||
from utils.logger import SystemdLogger
|
||||
from utils.helper import Helper
|
||||
@@ -22,14 +16,6 @@ class Groups:
|
||||
The groups class is responsible for handling the correlations between the guests
|
||||
and their groups like affinity and anti-affinity groups. To ensure a proper balancing
|
||||
guests will ge grouped and then evaluated for further balancing.
|
||||
|
||||
Methods:
|
||||
__init__(proxlb_data: Dict[str, Any]):
|
||||
Initializes the Groups class.
|
||||
|
||||
get_groups(guests: Dict[str, Any], nodes: Dict[str, Any]) -> Dict[str, Any]:
|
||||
Generates and returns a dictionary of affinity and anti-affinity groups
|
||||
based on the provided data.
|
||||
"""
|
||||
|
||||
def __init__(self, proxlb_data: Dict[str, Any]):
|
||||
|
||||
@@ -3,11 +3,6 @@ The Guests class retrieves all running guests on the Proxmox cluster across all
|
||||
It handles both VM and CT guest types, collecting their resource metrics.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
from typing import Dict, Any
|
||||
from utils.logger import SystemdLogger
|
||||
from models.tags import Tags
|
||||
@@ -19,14 +14,6 @@ class Guests:
|
||||
"""
|
||||
The Guests class retrieves all running guests on the Proxmox cluster across all available nodes.
|
||||
It handles both VM and CT guest types, collecting their resource metrics.
|
||||
|
||||
Methods:
|
||||
__init__:
|
||||
Initializes the Guests class.
|
||||
|
||||
get_guests(proxmox_api: any, nodes: Dict[str, Any]) -> Dict[str, Any]:
|
||||
Retrieves metrics for all running guests (both VMs and CTs) across all nodes in the Proxmox cluster.
|
||||
It collects resource metrics such as CPU, memory, and disk usage, as well as tags and affinity/anti-affinity groups.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
|
||||
@@ -1,26 +1,8 @@
|
||||
"""
|
||||
The Nodes class retrieves all running nodes in a Proxmox cluster
|
||||
and collects their resource metrics.
|
||||
|
||||
Methods:
|
||||
__init__:
|
||||
Initializes the Nodes class.
|
||||
|
||||
get_nodes(proxmox_api: any, proxlb_config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
Gets metrics of all nodes in a Proxmox cluster.
|
||||
|
||||
set_node_maintenance(proxlb_config: Dict[str, Any], node_name: str) -> Dict[str, Any]:
|
||||
Sets Proxmox nodes to a maintenance mode if required.
|
||||
|
||||
set_node_ignore(proxlb_config: Dict[str, Any], node_name: str) -> Dict[str, Any]:
|
||||
Sets Proxmox nodes to be ignored if requested.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
from typing import Dict, Any
|
||||
from utils.logger import SystemdLogger
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
"""
|
||||
The Tags class retrieves and processes tags from guests of type VM or CT running
|
||||
in a Proxmox cluster. It provides methods to fetch tags from the Proxmox API and
|
||||
evaluate them for affinity, anti-affinity, and ignore tags, which are used during
|
||||
balancing calculations.
|
||||
The Tags class retrieves all tags from guests of the type VM or CT running
|
||||
in a Proxmox cluster and validates for affinity, anti-affinity and ignore
|
||||
tags set for the guest in the Proxmox API.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import time
|
||||
from typing import List
|
||||
from utils.logger import SystemdLogger
|
||||
@@ -19,30 +13,13 @@ logger = SystemdLogger()
|
||||
|
||||
class Tags:
|
||||
"""
|
||||
The Tags class retrieves and processes tags from guests of type VM or CT running
|
||||
in a Proxmox cluster. It provides methods to fetch tags from the Proxmox API and
|
||||
evaluate them for affinity, anti-affinity, and ignore tags, which are used during
|
||||
balancing calculations.
|
||||
|
||||
Methods:
|
||||
__init__:
|
||||
Initializes the Tags class.
|
||||
|
||||
get_tags_from_guests(proxmox_api: any, node: str, guest_id: int, guest_type: str) -> List[str]:
|
||||
Retrieves all tags for a given guest from the Proxmox API.
|
||||
|
||||
get_affinity_groups(tags: List[str]) -> List[str]:
|
||||
Evaluates and returns all affinity tags from the provided list of tags.
|
||||
|
||||
get_anti_affinity_groups(tags: List[str]) -> List[str]:
|
||||
Evaluates and returns all anti-affinity tags from the provided list of tags.
|
||||
|
||||
get_ignore(tags: List[str]) -> bool:
|
||||
Evaluates and returns a boolean indicating whether the guest should be ignored based on the provided list of tags.
|
||||
The Tags class retrieves all tags from guests of the type VM or CT running
|
||||
in a Proxmox cluster and validates for affinity, anti-affinity and ignore
|
||||
tags set for the guest in the Proxmox API.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes the tags class.
|
||||
Initializes the Tags class.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
The CliParser class handles the parsing of command-line interface (CLI) arguments.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import argparse
|
||||
import utils.version
|
||||
from utils.logger import SystemdLogger
|
||||
|
||||
@@ -3,11 +3,6 @@ The ConfigParser class handles the parsing of configuration file
|
||||
from a given YAML file from any location.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
try:
|
||||
@@ -29,16 +24,8 @@ logger = SystemdLogger()
|
||||
|
||||
class ConfigParser:
|
||||
"""
|
||||
The ConfigParser class handles the parsing of a configuration file.
|
||||
|
||||
Methods:
|
||||
__init__(config_path: str)
|
||||
|
||||
test_config_path(config_path: str) -> None
|
||||
Checks if the configuration file is present at the given config path.
|
||||
|
||||
get_config() -> Dict[str, Any]
|
||||
Parses and returns the configuration data from the YAML file.
|
||||
The ConfigParser class handles the parsing of configuration file
|
||||
from a given YAML file from any location.
|
||||
"""
|
||||
def __init__(self, config_path: str):
|
||||
"""
|
||||
|
||||
@@ -3,11 +3,6 @@ The Helper class provides some basic helper functions to not mess up the code in
|
||||
classes.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import uuid
|
||||
import sys
|
||||
import time
|
||||
@@ -22,22 +17,6 @@ class Helper:
|
||||
"""
|
||||
The Helper class provides some basic helper functions to not mess up the code in other
|
||||
classes.
|
||||
|
||||
Methods:
|
||||
__init__():
|
||||
Initializes the general Helper class.
|
||||
|
||||
get_uuid_string() -> str:
|
||||
Generates a random uuid and returns it as a string.
|
||||
|
||||
log_node_metrics(proxlb_data: Dict[str, Any], init: bool = True) -> None:
|
||||
Logs the memory, CPU, and disk usage metrics of nodes in the provided proxlb_data dictionary.
|
||||
|
||||
get_version(print_version: bool = False) -> None:
|
||||
Returns the current version of ProxLB and optionally prints it to stdout.
|
||||
|
||||
get_daemon_mode(proxlb_config: Dict[str, Any]) -> None:
|
||||
Checks if the daemon mode is active and handles the scheduling accordingly.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
"""
|
||||
The SystemdLogger class provides a singleton logger that integrates with systemd's journal if available.
|
||||
It dynamically evaluates the environment and adjusts the logger accordingly.
|
||||
The SystemdLogger class provides the root logger support. It dynamically
|
||||
evaluates the further usage and imports of journald and adjusts
|
||||
the logger to the systems functionality where it gets executed
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
import logging
|
||||
try:
|
||||
from systemd.journal import JournalHandler
|
||||
@@ -18,36 +14,9 @@ except ImportError:
|
||||
|
||||
class SystemdLogger:
|
||||
"""
|
||||
The SystemdLogger class provides a singleton logger that integrates with systemd's journal if available.
|
||||
It dynamically evaluates the environment and adjusts the logger accordingly.
|
||||
|
||||
Attributes:
|
||||
instance (SystemdLogger): Singleton instance of the SystemdLogger class.
|
||||
|
||||
Methods:
|
||||
__new__(cls, name: str = "ProxLB", level: str = logging.INFO) -> 'SystemdLogger':
|
||||
Creates a new instance of the SystemdLogger class or returns the existing instance.
|
||||
|
||||
initialize_logger(self, name: str, level: str) -> None:
|
||||
Initializes the logger with the given name and log level. Adds a JournalHandler if systemd is present.
|
||||
|
||||
set_log_level(self, level: str) -> None:
|
||||
Sets the log level for the logger and all its handlers.
|
||||
|
||||
debug(self, msg: str) -> str:
|
||||
Logs a message with level DEBUG.
|
||||
|
||||
info(self, msg: str) -> str:
|
||||
Logs a message with level INFO.
|
||||
|
||||
warning(self, msg: str) -> str:
|
||||
Logs a message with level WARNING.
|
||||
|
||||
error(self, msg: str) -> str:
|
||||
Logs a message with level ERROR.
|
||||
|
||||
critical(self, msg: str) -> str:
|
||||
Logs a message with level CRITICAL.
|
||||
The SystemdLogger class provides the root logger support. It dynamically
|
||||
evaluates the further usage and imports of journald and adjusts
|
||||
the logger to the systems functionality where it gets executed.
|
||||
"""
|
||||
# Create a singleton instance variable
|
||||
instance = None
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
"""
|
||||
The proxmox_api class manages connections to the Proxmox API by parsing the required objects
|
||||
for the authentication which can be based on username/password or API tokens.
|
||||
|
||||
This class provides methods to initialize the Proxmox API connection, test connectivity to
|
||||
Proxmox hosts, and handle authentication using either username/password or API tokens.
|
||||
It also includes functionality to distribute load across multiple Proxmox API endpoints
|
||||
and manage SSL certificate validation.
|
||||
Module providing a function printing python version.
|
||||
"""
|
||||
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
|
||||
try:
|
||||
import proxmoxer
|
||||
PROXMOXER_PRESENT = True
|
||||
@@ -53,44 +42,20 @@ logger = SystemdLogger()
|
||||
|
||||
class ProxmoxApi:
|
||||
"""
|
||||
The proxmox_api class manages connections to the Proxmox API by parsing the required objects
|
||||
for the authentication which can be based on username/password or API tokens.
|
||||
|
||||
This class provides methods to initialize the Proxmox API connection, test connectivity to
|
||||
Proxmox hosts, and handle authentication using either username/password or API tokens.
|
||||
It also includes functionality to distribute load across multiple Proxmox API endpoints
|
||||
and manage SSL certificate validation.
|
||||
|
||||
Attributes:
|
||||
logger (SystemdLogger): Logger instance for logging messages.
|
||||
proxmox_api (proxmoxer.ProxmoxAPI): Authenticated ProxmoxAPI object.
|
||||
|
||||
Methods:
|
||||
__init__(proxlb_config: Dict[str, Any]) -> None:
|
||||
Initializes the ProxmoxApi instance with the provided configuration.
|
||||
__getattr__(name):
|
||||
Delegates attribute access to the proxmox_api object.
|
||||
api_connect_get_hosts(proxmox_api_endpoints: list) -> str:
|
||||
Determines a working Proxmox API host from a list of endpoints.
|
||||
test_api_proxmox_host(host: str) -> str:
|
||||
Tests connectivity to a Proxmox host by resolving its IP address.
|
||||
test_api_proxmox_host_ipv4(host: str, port: int = 8006, timeout: int = 1) -> bool:
|
||||
Tests reachability of a Proxmox host on its IPv4 address.
|
||||
test_api_proxmox_host_ipv6(host: str, port: int = 8006, timeout: int = 1) -> bool:
|
||||
Tests reachability of a Proxmox host on its IPv6 address.
|
||||
api_connect(proxlb_config: Dict[str, Any]) -> proxmoxer.ProxmoxAPI:
|
||||
Establishes a connection to the Proxmox API using the provided configuration.
|
||||
Handles command-line argument parsing for ProxLB.
|
||||
"""
|
||||
def __init__(self, proxlb_config: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Initializes the ProxmoxApi instance with the provided configuration.
|
||||
Initialize the ProxmoxApi instance.
|
||||
|
||||
This constructor method sets up the Proxmox API connection by calling the
|
||||
api_connect method with the given configuration dictionary. It logs the
|
||||
initialization process for debugging purposes.
|
||||
This method sets up the ProxmoxApi instance by testing the required module dependencies
|
||||
and establishing a connection to the Proxmox API using the provided configuration.
|
||||
|
||||
Args:
|
||||
proxlb_config (Dict[str, Any]): A dictionary containing the Proxmox API configuration.
|
||||
proxlb_config (Dict[str, Any]): Configuration dictionary containing Proxmox API connection details.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
logger.debug("Starting: ProxmoxApi initialization.")
|
||||
self.proxmox_api = self.api_connect(proxlb_config)
|
||||
@@ -98,48 +63,10 @@ class ProxmoxApi:
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Delegate attribute access to proxmox_api to the underlying proxmoxer module.
|
||||
Delegate attribute access to proxmox_api.
|
||||
"""
|
||||
return getattr(self.proxmox_api, name)
|
||||
|
||||
def validate_config(self, proxlb_config: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Validates the provided ProxLB configuration dictionary to ensure that it contains
|
||||
the necessary credentials for authentication and that the credentials are not
|
||||
mutually exclusive.
|
||||
|
||||
Args:
|
||||
proxlb_config (Dict[str, Any]): A dictionary containing the ProxLB configuration.
|
||||
It must include a "proxmox_api" key with a nested dictionary that contains
|
||||
either "user" and "password" keys for username/password authentication or
|
||||
"token_id" and "token_secret" keys for API token authentication.
|
||||
|
||||
Raises:
|
||||
SystemExit: If both username/password and API token authentication methods are
|
||||
provided, the function will log a critical error message and terminate
|
||||
the program.
|
||||
|
||||
Logs:
|
||||
Logs the start and end of the validation process. Logs a critical error if both
|
||||
authentication methods are provided.
|
||||
"""
|
||||
logger.debug("Starting: validate_config.")
|
||||
if not proxlb_config.get("proxmox_api", False):
|
||||
logger.critical(f"Config error. Please check your proxmox_api chapter in your config file.")
|
||||
print(f"Config error. Please check your proxmox_api chapter in your config file.")
|
||||
sys.exit(1)
|
||||
|
||||
proxlb_credentials = proxlb_config["proxmox_api"]
|
||||
present_auth_user = "user" in proxlb_credentials
|
||||
present_auth_token = "token_id" in proxlb_credentials
|
||||
|
||||
if present_auth_user and present_auth_token:
|
||||
logger.critical(f"Username/password and API token authentication are mutal exclusive. Please use only one!")
|
||||
print(f"Username/password and API token authentication are mutal exclusive. Please use only one!")
|
||||
sys.exit(1)
|
||||
|
||||
logger.debug("Finished: validate_config.")
|
||||
|
||||
def api_connect_get_hosts(self, proxmox_api_endpoints: list) -> str:
|
||||
"""
|
||||
Perform a connectivity test to determine a working host for the Proxmox API.
|
||||
@@ -220,15 +147,7 @@ class ProxmoxApi:
|
||||
bool: False if the Proxmox server is not reachable.
|
||||
"""
|
||||
logger.debug("Starting: test_api_proxmox_host.")
|
||||
|
||||
# Try resolving DNS to IP and log non-resolvable ones
|
||||
try:
|
||||
ip = socket.getaddrinfo(host, None, socket.AF_UNSPEC)
|
||||
except socket.gaierror:
|
||||
logger.warning(f"Could not resolve {host}.")
|
||||
return False
|
||||
|
||||
# Validate if given object is IPv4 or IPv6
|
||||
ip = socket.getaddrinfo(host, None, socket.AF_UNSPEC)
|
||||
for address_type in ip:
|
||||
if address_type[0] == socket.AF_INET:
|
||||
logger.debug(f"{host} is type ipv4.")
|
||||
@@ -337,9 +256,6 @@ class ProxmoxApi:
|
||||
requests.exceptions.ConnectionError: If the connection to the Proxmox API is refused.
|
||||
"""
|
||||
logger.debug("Starting: api_connect.")
|
||||
# Validate config
|
||||
self.validate_config(proxlb_config)
|
||||
|
||||
# Get a valid Proxmox API endpoint
|
||||
proxmox_api_endpoint = self.api_connect_get_hosts(proxlb_config.get("proxmox_api", {}).get("hosts", []))
|
||||
|
||||
@@ -351,24 +267,12 @@ class ProxmoxApi:
|
||||
|
||||
# Login into Proxmox API and create API object
|
||||
try:
|
||||
|
||||
if proxlb_config.get("proxmox_api").get("token_secret", False):
|
||||
proxmox_api = proxmoxer.ProxmoxAPI(
|
||||
proxmox_api_endpoint,
|
||||
user=proxlb_config.get("proxmox_api").get("user", True),
|
||||
token_name=proxlb_config.get("proxmox_api").get("token_id", True),
|
||||
token_value=proxlb_config.get("proxmox_api").get("token_secret", True),
|
||||
verify_ssl=proxlb_config.get("proxmox_api").get("ssl_verification", True),
|
||||
timeout=proxlb_config.get("proxmox_api").get("timeout", True))
|
||||
logger.debug("Using API token authentication.")
|
||||
else:
|
||||
proxmox_api = proxmoxer.ProxmoxAPI(
|
||||
proxmox_api_endpoint,
|
||||
user=proxlb_config.get("proxmox_api").get("user", True),
|
||||
password=proxlb_config.get("proxmox_api").get("pass", True),
|
||||
verify_ssl=proxlb_config.get("proxmox_api").get("ssl_verification", True),
|
||||
timeout=proxlb_config.get("proxmox_api").get("timeout", True))
|
||||
logger.debug("Using username/password authentication.")
|
||||
proxmox_api = proxmoxer.ProxmoxAPI(
|
||||
proxmox_api_endpoint,
|
||||
user=proxlb_config.get("proxmox_api").get("user", True),
|
||||
password=proxlb_config.get("proxmox_api").get("pass", True),
|
||||
verify_ssl=proxlb_config.get("proxmox_api").get("ssl_verification", True),
|
||||
timeout=proxlb_config.get("proxmox_api").get("timeout", True))
|
||||
except proxmoxer.backends.https.AuthenticationError as proxmox_api_error:
|
||||
logger.critical(f"Authentication failed. Please check the defined credentials: {proxmox_api_error}")
|
||||
sys.exit(2)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
__app_name__ = "ProxLB"
|
||||
__app_desc__ = "A DRS alike loadbalancer for Proxmox clusters."
|
||||
__author__ = "Florian Paul Azim Hoberg <gyptazy>"
|
||||
__copyright__ = "Copyright (C) 2025 Florian Paul Azim Hoberg (@gyptazy)"
|
||||
__license__ = "GPL-3.0"
|
||||
__version__ = "1.1.0-alpha"
|
||||
__url__ = "https://github.com/gyptazy/ProxLB"
|
||||
|
||||
Reference in New Issue
Block a user