Compare commits

..

66 Commits

Author SHA1 Message Date
gyptazy
360920433a fix 2025-03-02 16:55:44 +01:00
gyptazy
4e39c4cba2 fix 2025-03-02 16:54:00 +01:00
gyptazy
baaf01e39b fix 2025-03-02 16:51:37 +01:00
gyptazy
0acc822777 fix 2025-03-02 16:49:46 +01:00
gyptazy
53723a0fdd fix 2025-03-02 15:51:15 +01:00
gyptazy
f451afd6d7 fix 2025-03-02 15:49:19 +01:00
gyptazy
e2aeeffdde fiux 2025-03-02 15:45:38 +01:00
gyptazy
3d33d5db44 fix 2025-03-02 15:44:28 +01:00
gyptazy
12be935115 fix 2025-03-02 15:33:58 +01:00
gyptazy
038d1e0fc3 fix 2025-03-02 15:32:26 +01:00
gyptazy
b3dd4c7e3a fix 2025-03-02 15:30:26 +01:00
gyptazy
8de8a263db fix 2025-03-02 15:26:12 +01:00
gyptazy
cf357445c6 fix 2025-03-02 15:17:05 +01:00
gyptazy
9d34d5bcd5 fix 2025-03-02 15:13:41 +01:00
gyptazy
4a3f10ccc2 fix 2025-03-02 15:09:08 +01:00
gyptazy
968d49adb0 fifx 2025-03-02 15:05:43 +01:00
gyptazy
585f6d511b fiux 2025-03-02 15:02:55 +01:00
gyptazy
c0558dda3b fix 2025-03-02 14:59:18 +01:00
gyptazy
a8470aca52 fix 2025-03-02 14:57:04 +01:00
gyptazy
d5f1853d15 fix 2025-03-02 14:51:40 +01:00
gyptazy
3da8f0638e fix 2025-03-02 14:49:45 +01:00
gyptazy
513e74c2e2 fix 2025-03-02 13:22:31 +01:00
gyptazy
3841cd80ee fix 2025-03-02 13:18:33 +01:00
gyptazy
86c9134b10 fix 2025-03-02 12:19:23 +01:00
gyptazy
ae2e15c2ae fix 2025-03-02 12:06:08 +01:00
gyptazy
1230aa2ef4 fix 2025-03-02 11:36:05 +01:00
gyptazy
f00edb6387 fix 2025-03-02 11:26:26 +01:00
gyptazy
4402a8775a fix 2025-03-02 11:25:02 +01:00
gyptazy
8dc538eb1e fix 2025-03-02 11:24:32 +01:00
gyptazy
26add60e70 fix 2025-03-02 11:22:34 +01:00
gyptazy
5b6897d250 fix 2025-03-02 11:19:05 +01:00
gyptazy
df1b9cb179 fix 2025-03-02 11:13:53 +01:00
gyptazy
57282a0273 fix 2025-03-02 11:12:51 +01:00
gyptazy
e0ed3773af fix 2025-03-01 18:33:16 +01:00
gyptazy
fb50afe473 fix 2025-03-01 18:24:18 +01:00
gyptazy
5dc747eb31 fix 2025-03-01 18:15:10 +01:00
gyptazy
87b82cfccb fix 2025-03-01 13:32:53 +01:00
gyptazy
c73bbd1538 fix 2025-03-01 13:26:57 +01:00
gyptazy
19e2d9e3ae fix 2025-03-01 13:23:35 +01:00
gyptazy
bb990f024c fix 2025-03-01 13:22:01 +01:00
gyptazy
9413d46bc4 fix 2025-03-01 13:16:18 +01:00
gyptazy
b48fd07f83 fix 2025-03-01 13:09:26 +01:00
gyptazy
a08f088bbd fix 2025-03-01 12:52:39 +01:00
gyptazy
31c455c835 fix 2025-03-01 12:49:38 +01:00
gyptazy
82704ce887 fix 2025-03-01 12:48:25 +01:00
gyptazy
8a08dd5a51 fix 2025-03-01 12:47:05 +01:00
gyptazy
389950c150 fix 2025-03-01 12:44:44 +01:00
gyptazy
8d004efc03 fix 2025-03-01 12:42:02 +01:00
gyptazy
3b5f6da2a5 fix 2025-03-01 12:40:50 +01:00
gyptazy
2f7f83f68e fix 2025-03-01 12:38:11 +01:00
gyptazy
8d46ebf758 fix 2025-03-01 12:31:48 +01:00
gyptazy
dc499c3fb6 replace ubntu pkg with pip 2025-03-01 12:23:51 +01:00
gyptazy
17da45e0aa test stdeb package ubuntu 2025-03-01 12:21:04 +01:00
gyptazy
680124a23e Fix package building 2025-03-01 12:17:25 +01:00
gyptazy
b12cb96b29 fix 2025-03-01 12:15:06 +01:00
gyptazy
66b7e58bdc use sudo 2025-03-01 12:13:35 +01:00
gyptazy
e303bddf2e add pipeline jobs 2025-03-01 12:11:38 +01:00
gyptazy
7c0fd9d76a fix 2025-03-01 10:15:05 +01:00
gyptazy
5aa1e8ee04 fix 2025-03-01 10:12:31 +01:00
gyptazy
110afb3c5f fix 2025-03-01 10:05:50 +01:00
gyptazy
9eee27afa4 fix 2025-03-01 10:03:00 +01:00
gyptazy
b3f9a2b04b fix 2025-03-01 09:58:40 +01:00
gyptazy
6c38f9ea07 fix 2025-03-01 09:56:19 +01:00
gyptazy
4900cfb53b Refactor of code base for ProxLB
* Native Python3 project
 * Oop style
 * Native Debian packaging
   - Renamed package: python3-proxlb

Fixes: #114
2025-03-01 09:47:29 +01:00
Florian Paul Azim Hoberg (@gyptazy)
e2a33e9805 fix
fix
2025-02-28 11:52:23 +01:00
gyptazy
1caf628e96 refactor: Refactor of ProxLB code base.
Fixes: #114
2025-02-28 09:42:55 +01:00
29 changed files with 59 additions and 429 deletions

View File

@@ -1,2 +0,0 @@
feature:
- Add Proxmox API authentication support. [#125]

View File

@@ -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.\")'
"

View File

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

View File

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

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

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

@@ -1,2 +0,0 @@
proxlb /usr/lib/python3/dist-packages/
service/proxlb.service /lib/systemd/system/

16
debian/postinst vendored
View File

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

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

@@ -1,4 +0,0 @@
#!/usr/bin/make -f
%:
dh $@ --with python3 --buildsystem=pybuild

View File

@@ -1 +0,0 @@
3.0 (native)

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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