From 7b42ee27a1149e9a5c2a099e821a1ec9f4d27d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alo=C3=AFs=20Micard?= Date: Mon, 3 Feb 2025 22:54:18 +0100 Subject: [PATCH] Initial commit --- app/__init__.py | 0 app/__main__.py | 158 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 159 insertions(+) create mode 100644 app/__init__.py create mode 100644 app/__main__.py diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/__main__.py b/app/__main__.py new file mode 100644 index 0000000..c0ac497 --- /dev/null +++ b/app/__main__.py @@ -0,0 +1,158 @@ +import os +import sys + +import pynetbox +from proxmoxer import ProxmoxAPI +from rich import print + + +def _parse_pve_network_definition(_raw_network_definition: str) -> dict: + _network_definition = {} + + for _component in _raw_network_definition.split(','): + _component_parts = _component.split('=') + _network_definition[_component_parts[0]] = _component_parts[1] + + return _network_definition + + +def main(): + # Instantiate connection to the Proxmox VE API + pve_api = ProxmoxAPI( + host=os.environ['PVE_API_HOST'], + user=os.environ['PVE_API_USER'], + token_name=os.environ['PVE_API_TOKEN'], + token_value=os.environ['PVE_API_SECRET'], + verify_ssl=os.getenv('PVE_API_VERIFY_SSL', 'false').lower() == 'true', + ) + + # Instantiate connection to the Netbox API + nb_api = pynetbox.api( + url=os.environ['NB_API_URL'], + token=os.environ['NB_API_TOKEN'], + ) + + # Load NetBox devices + nb_devices = {} + for _nb_device in nb_api.dcim.devices.all(): + nb_devices[_nb_device.name.lower()] = _nb_device + + # Load NetBox virtual machines + nb_virtual_machines = {} + for _nb_virtual_machine in nb_api.virtualization.virtual_machines.all(): + nb_virtual_machines[_nb_virtual_machine.serial] = _nb_virtual_machine + + # Load NetBox interfaces + nb_virtual_machines_interfaces = {} + for _nb_interface in nb_api.virtualization.interfaces.all(): + if _nb_interface.virtual_machine.id not in nb_virtual_machines_interfaces: + nb_virtual_machines_interfaces[_nb_interface.virtual_machine.id] = {} + + nb_virtual_machines_interfaces[_nb_interface.virtual_machine.id][_nb_interface.name] = _nb_interface + + # Load NetBox mac addresses + nb_mac_addresses = {} + for _nb_mac_address in nb_api.dcim.mac_addresses.all(): + nb_mac_addresses[_nb_mac_address.mac_address] = _nb_mac_address + + # Load NetBox IP addresses + nb_ip_addresses = {} + for _nb_ip_address in nb_api.ipam.ip_addresses.all(): + nb_ip_addresses[_nb_ip_address['address']] = _nb_ip_address + + # Process Proxmox nodes + for pve_node in pve_api.nodes.get(): + # Process Proxmox virtual machines per node + for pve_virtual_machine in pve_api.nodes(pve_node["node"]).qemu.get(): + pve_virtual_machine_config = pve_api.nodes(pve_node['node']).qemu(pve_virtual_machine['vmid']).config.get() + + # This script does not create the hardware devices. + nb_device = nb_devices.get(pve_node['node'].lower()) + if nb_device is None: + print(f'The device {pve_node["node"]} is not created on NetBox. Exiting.') + sys.exit(1) + else: + pass + + # Create the virtual machine if it exists, update it otherwise + nb_virtual_machine = nb_virtual_machines.get(str(pve_virtual_machine['vmid'])) + if nb_virtual_machine is None: + nb_virtual_machine = nb_api.virtualization.virtual_machines.create( + serial=pve_virtual_machine['vmid'], + name=pve_virtual_machine['name'], + site=nb_device.site.id, + cluster=1, # TODO + device=nb_device.id, + vcpus=pve_virtual_machine_config['cores'], + memory=int(pve_virtual_machine_config['memory']), + ) + else: + nb_virtual_machine.name = pve_virtual_machine['name'] + nb_virtual_machine.site = nb_device.site.id + nb_virtual_machine.cluster = 1 + nb_virtual_machine.device = nb_device.id + nb_virtual_machine.vcpus = pve_virtual_machine_config['cores'] + nb_virtual_machine.memory = int(pve_virtual_machine_config['memory']) + nb_virtual_machine.save() + + # Handle the VM network interfaces + for (_config_key, _config_value) in pve_virtual_machine_config.items(): + if not _config_key.startswith('net'): + continue + + _network_definition = _parse_pve_network_definition(_config_value) + + # Determinate MAC address + network_mac_address = None + for _model in ['virtio', 'e1000']: + if _model in _network_definition: + network_mac_address = _network_definition[_model] + break + + if network_mac_address is None: + continue + + nb_virtual_machines_interface = nb_virtual_machines_interfaces \ + .get(nb_virtual_machine.id, {}) \ + .get(_config_key) + + if nb_virtual_machines_interface is None: + nb_virtual_machines_interface = nb_api.virtualization.interfaces.create( + virtual_machine=nb_virtual_machine.id, + name=_config_key, + description=network_mac_address, + ) + + if nb_virtual_machine.id not in nb_virtual_machines_interfaces: + nb_virtual_machines_interfaces[nb_virtual_machine.id] = {} + + nb_virtual_machines_interfaces[nb_virtual_machine.id][_config_key] = nb_virtual_machines_interface + + # Create the MAC address and link it to the VM + nb_mac_address = nb_mac_addresses.get(network_mac_address) + if nb_mac_address is None: + nb_mac_address = nb_api.dcim.mac_addresses.create( + mac_address=network_mac_address, + assigned_object_type='virtualization.vminterface', + assigned_object_id=nb_virtual_machines_interface.id, + ) + + nb_mac_addresses[network_mac_address] = nb_mac_address + + nb_virtual_machines_interface.primary_mac_address = nb_mac_address.id + nb_virtual_machines_interface.save() + + # TODO: Handle the disk + + # Then create the disks if not exists, update them otherwise + # Then create the network interface if not exists, update them otherwise + # Link the network interface to the range if not exists + + # print(pve_api.nodes(pve_node['node']).qemu(pve_virtual_machine['vmid']).config.get()) + + # if vm['status'] == 'running': + # print(pve_api.nodes(node['node']).qemu(vm['vmid']).agent('network-get-interfaces').get()) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt index 54fcba2..ea146ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pynetbox==7.4.1 proxmoxer==2.2.0 requests==2.32.3 +rich==13.9.4 \ No newline at end of file