Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4d463fb
Update README with oVirt source information
ekrichbaum Oct 23, 2026
43b388e
Add OVirtConfig to source configuration list
ekrichbaum Oct 23, 2026
f378c1b
Add OVirtHandler to valid sources list
ekrichbaum Oct 23, 2026
3df2084
Implement object ownership check for synced sources
ekrichbaum Oct 23, 2026
2e45854
Add optional dependency for ovirt-engine-sdk-python
ekrichbaum Oct 23, 2026
37aada3
Add oVirt example configuration to settings
ekrichbaum Oct 23, 2026
b134b87
Update fmt.Println message from 'Hello' to 'Goodbye'
ekrichbaum Oct 23, 2026
925ee0b
Add oVirt source documentation
ekrichbaum Oct 23, 2026
5d29e29
Add OVirtConfig class for oVirt API integration
ekrichbaum Oct 23, 2026
774afac
Add Connection for Ovirt source
ekrichbaum Oct 23, 2026
9b99519
Add ovirt-engine-sdk-python requirement
ekrichbaum Oct 23, 2026
d6dbdca
Add tests for OVirt source functionality
ekrichbaum Oct 23, 2026
052afa5
Add INSTALL_OVIRT argument for conditional dependencies
ekrichbaum Oct 23, 2026
676f662
Remove object_synced_by_other_source method
ekrichbaum Oct 23, 2026
bca2b97
Update deprecation messages for sync_tags options
ekrichbaum Oct 27, 2026
1427c48
Refactor oVirt connection handling and IP resolution
ekrichbaum Oct 27, 2026
d58681f
Update ovirt dependency version in pyproject.toml
ekrichbaum Oct 27, 2026
2f9883b
Update ovirt-engine-sdk-python version constraint
ekrichbaum Oct 27, 2026
0246bd6
Update ovirt-engine-sdk-python version to 4.6.3
ekrichbaum Oct 27, 2026
3d027fb
Update guest IP discovery section in source_ovirt.md
ekrichbaum Oct 27, 2026
531189f
Enhance populate function and add new tests
ekrichbaum Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@ FROM python:3.14-slim AS builder
COPY requirements.txt .

ARG VENV=/opt/netbox-sync/venv
ARG INSTALL_OVIRT=false
COPY requirements-ovirt.txt .

# Install dependencies
RUN python3 -m venv $VENV && \
RUN if [ "$INSTALL_OVIRT" = "true" ]; then \
apt-get update && \
apt-get install -y --no-install-recommends gcc libc6-dev libxml2-dev libcurl4-openssl-dev && \
rm -rf /var/lib/apt/lists/*; \
fi && \
python3 -m venv $VENV && \
$VENV/bin/python3 -m pip install --upgrade pip && \
$VENV/bin/pip install -r requirements.txt && \
if [ "$INSTALL_OVIRT" = "true" ]; then $VENV/bin/pip install -r requirements-ovirt.txt; fi && \
$VENV/bin/pip install vmware-vcenter==9.1.1.0 && \
$VENV/bin/python3 -m pip uninstall -y pip && \
find $VENV -type d -name "__pycache__" -print0 | xargs -0 -n1 rm -rf

FROM python:3.14-slim AS netbox-sync

ARG VENV=/opt/netbox-sync/venv
ARG INSTALL_OVIRT=false

# Copy installed packages
COPY --from=builder $VENV $VENV
Expand All @@ -30,6 +39,9 @@ COPY . .
# can use it)
RUN apt-get update && \
apt-get dist-upgrade -y && \
if [ "$INSTALL_OVIRT" = "true" ]; then \
apt-get install -y --no-install-recommends libxml2 libcurl4t64; \
fi && \
rm -rf /var/lib/apt/lists/* && \
python3 -m pip uninstall -y --root-user-action=ignore pip && \
groupadd --gid 1000 netbox-sync && \
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This is a tool to sync data from different sources to a NetBox instance.

Available source types:
* VMware vCenter Server
* oVirt / Red Hat Virtualization (see [oVirt source](docs/source_ovirt.md))
* [bb-ricardo/check_redfish](https://github.com/bb-Ricardo/check_redfish) inventory files

**IMPORTANT: READ INSTRUCTIONS CAREFULLY BEFORE RUNNING THIS PROGRAM**
Expand Down Expand Up @@ -38,6 +39,9 @@ This ensures stale objects are removed from NetBox keeping an accurate current s
* VMWare vCenter >= 6.0
#### Source: check_redfish (if used)
* check_redfish >= 1.2.0
#### Source: oVirt (if used)
* oVirt / Red Hat Virtualization Engine v4 API
* Optional SDK and native libraries: see [installation and configuration](docs/source_ovirt.md)

# Installing
* here we assume we install in ```/opt```
Expand Down
64 changes: 64 additions & 0 deletions docs/source_ovirt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Source: Ovirt

## Installation

The SDK is optional: installations using other sources do not need it. Selecting
an oVirt source without the SDK logs an error and skips that source.

On Debian/Ubuntu, install the SDK's build dependencies before installing Python
packages:

```sh
sudo apt-get install gcc python3-dev libxml2-dev libcurl4-openssl-dev
pip install -r requirements-ovirt.txt
```

With uv, use `uv sync --extra ovirt` after installing the native libraries.
The SDK contains a native extension; a compiler and Python development headers
matching your Python version are needed when no wheel is available. The runtime
needs libxml2 and libcurl.

For Docker, enable the optional source in both build stages:

```sh
docker build --build-arg INSTALL_OVIRT=true -t netbox-sync:ovirt .
```

## Configuration

```ini
[source/production-ovirt]
type = ovirt
url = https://engine.example.com/ovirt-engine/api
username = inventory@internal
password = replace-me
validate_tls_certs = True
ca_file = /etc/pki/ovirt-engine/ca.pem
permitted_subnets = 10.0.0.0/8, fd00::/8
cluster_site_relation = DC/Production = Main Site
```

Use an account with read access to datacenters, clusters, hosts, VMs, disks,
interfaces, and reported devices. The existing source options remain supported,
including name filters, site/tenant/role/tag relations, domain stripping,
`set_vm_name_to_uuid`, `skip_vm_comments`, `skip_vm_platform`, and
`host_custom_object_attributes` / `vm_custom_object_attributes` (dot-separated
SDK attribute paths). Generate a full example with `python netbox-sync.py -g`.

Guest IP discovery requires the guest agent to report network devices. VMs
without agent data still receive their configured interfaces and disks. As with
other sources, bare guest IPs need a matching prefix in NetBox to determine their
prefix length. Addresses without a matching prefix are not imported; netbox-sync
does not guess a host-length prefix. Only addresses allowed by `permitted_subnets`
are imported.
`ip_tenant_inheritance_order`, `preserve_primary_ips`, `skip_fhrp_group_ips`, and
interface exclusion filters use the current shared network-sync behavior.

NetBox 4.2+ cluster sites use scoped relations and interfaces use MAC address
objects. NetBox 3.7+ disks are separate virtual disk objects, with the size units
appropriate to the NetBox version. Older versions retain aggregate VM disk size.
Missing CPU or disk size data is not replaced with fabricated values.

Begin with a dry run (`python netbox-sync.py -n`) and inspect the log before
allowing writes. Unit tests use a fake SDK and the real in-memory NetBox
inventory; they do not replace validation against your live oVirt Engine.
4 changes: 3 additions & 1 deletion module/config/file_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from module.sources.vmware.config import VMWareConfig
from module.sources.check_redfish.config import CheckRedfishConfig
from module.sources.hetzner.config import HetznerConfig
from module.sources.ovirt.config import OVirtConfig
from module.common.logging import get_logger
from module.config import default_config_file_path, source_config_section_name
from module.config.files import ConfigFile, ConfigFileINI, ConfigFileYAML
Expand All @@ -35,7 +36,8 @@ class ConfigFileOutput(DescriptionFormatterMixin):
source_config_list = [
VMWareConfig,
CheckRedfishConfig,
HetznerConfig
HetznerConfig,
OVirtConfig
]

header = f"Welcome to the {__description__} configuration file."
Expand Down
3 changes: 2 additions & 1 deletion module/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# define all available sources here
from module.sources.vmware.connection import VMWareHandler
from module.sources.hetzner.connection import HetznerHandler
from module.sources.ovirt.connection import OVirtHandler
from module.sources.check_redfish.import_inventory import CheckRedfish

from module.common.logging import get_logger
Expand All @@ -19,7 +20,7 @@
from module.config import source_config_section_name

# list of valid sources
valid_sources = [VMWareHandler, CheckRedfish, HetznerHandler]
valid_sources = [VMWareHandler, CheckRedfish, HetznerHandler, OVirtHandler]


def validate_source(source_class_object=None, state="pre"):
Expand Down
11 changes: 10 additions & 1 deletion module/sources/common/source_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ def implements(cls, source_type):
def finish(self):
pass

def object_synced_by_other_source(self, nb_object):
"""Check the current source or stored tags for ownership by another configured source."""
if nb_object.source is not None:
return nb_object.source is not self

other_source_tags = [x.source_tag for x in self.inventory.source_list if x is not self]
return len(set(nb_object.get_tags()).intersection(other_source_tags)) > 0

def ip_is_primary_ip_of_object(self, ip_object, device_vm_object) -> bool:
"""
Check if a NBIPAddress object is currently set as primary IPv4 or IPv6
Expand Down Expand Up @@ -435,7 +443,8 @@ def add_update_interface(self, interface_object, device_object, interface_data,

# skip handling of IPs for VMs with not installed/running guest tools
skip_ip_handling = False
if type(device_object) == NBVM and grab(vmware_object,'guest.toolsRunningStatus') != "guestToolsRunning":
if type(device_object) == NBVM and vmware_object is not None and \
grab(vmware_object, 'guest.toolsRunningStatus') != "guestToolsRunning":
log.debug(f"VM '{device_object.name}' guest tool status is 'NotRunning', skipping IP handling")
skip_ip_handling = True

Expand Down
Loading