From e8429084afd0faf79809ce48eb51d0c7b4b4bde4 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Fri, 11 Oct 2024 21:37:33 +0000 Subject: [PATCH] Greatly improving GUI Log Messages by coloring and adding object Netbox linking --- netbox_proxbox/backend/exception.py | 17 +- netbox_proxbox/backend/logging.py | 20 +- .../routes/netbox/dcim/device_types.py | 2 +- .../backend/routes/netbox/dcim/devices.py | 8 +- .../backend/routes/netbox/dcim/interfaces.py | 2 +- .../backend/routes/netbox/generic.py | 108 ++++---- .../routes/netbox/virtualization/cluster.py | 2 +- .../netbox/virtualization/interfaces.py | 2 +- .../netbox/virtualization/virtual_machines.py | 2 +- .../routes/proxbox/clusters/__init__.py | 232 +++++++++++------- .../templates/netbox_proxbox/home.html | 7 +- 11 files changed, 240 insertions(+), 162 deletions(-) diff --git a/netbox_proxbox/backend/exception.py b/netbox_proxbox/backend/exception.py index 136278b..2c42e87 100755 --- a/netbox_proxbox/backend/exception.py +++ b/netbox_proxbox/backend/exception.py @@ -1,5 +1,6 @@ -from netbox_proxbox.backend.logging import logger +from fastapi import WebSocket + class ProxboxException(Exception): def __init__( self, @@ -18,8 +19,14 @@ def __init__( if self.python_exception: log_message+=f"\n > Python Exception: {self.python_exception}" - - - logger.error(log_message) + - \ No newline at end of file +async def exception_log( + logger, + message: str, + detail: str | None = None, + python_exception: str | None = None, + websocket: WebSocket | None = None, +): + log = logger + await log(websocket=websocket, msg=message, level="ERROR") diff --git a/netbox_proxbox/backend/logging.py b/netbox_proxbox/backend/logging.py index 1a779f6..0862ecf 100755 --- a/netbox_proxbox/backend/logging.py +++ b/netbox_proxbox/backend/logging.py @@ -1,5 +1,6 @@ import logging from logging.handlers import TimedRotatingFileHandler +from netbox_proxbox.backend.exception import ProxboxException # ANSI escape sequences for colors class AnsiColorCodes: @@ -70,4 +71,21 @@ def setup_logger(): return logger -logger = setup_logger() \ No newline at end of file +logger = setup_logger() + +from fastapi import WebSocket + +async def log(websocket: WebSocket, msg, level = None): + if websocket: + await websocket.send_text(msg) + + if level == "debug": logger.debug(msg) + + if level == "ERROR" or level == "error": + logger.error(msg) + #raise ProxboxException( + # message=msg, + # python_exception=python_exception + #) + + else: logger.info(msg) \ No newline at end of file diff --git a/netbox_proxbox/backend/routes/netbox/dcim/device_types.py b/netbox_proxbox/backend/routes/netbox/dcim/device_types.py index 3ccb3c2..6e371e4 100755 --- a/netbox_proxbox/backend/routes/netbox/dcim/device_types.py +++ b/netbox_proxbox/backend/routes/netbox/dcim/device_types.py @@ -15,7 +15,7 @@ class DeviceType(NetboxBase): object_name = "Device Types" async def get_base_dict(self): - manufacturer = await Manufacturer(nb = self.nb).get() + manufacturer = await Manufacturer(nb = self.nb, websocket = self.websocket).get() return { "model": self.default_name, diff --git a/netbox_proxbox/backend/routes/netbox/dcim/devices.py b/netbox_proxbox/backend/routes/netbox/dcim/devices.py index e7aa667..57c658f 100755 --- a/netbox_proxbox/backend/routes/netbox/dcim/devices.py +++ b/netbox_proxbox/backend/routes/netbox/dcim/devices.py @@ -18,10 +18,10 @@ class Device(NetboxBase): object_name = "Device" async def get_base_dict(self): - site = await Site(nb = self.nb).get() - role = await DeviceRole(nb = self.nb).get() - device_type = await DeviceType(nb = self.nb).get() - cluster = await Cluster(nb = self.nb).get() + site = await Site(nb = self.nb, websocket = self.websocket).get() + role = await DeviceRole(nb = self.nb, websocket = self.websocket).get() + device_type = await DeviceType(nb = self.nb, websocket = self.websocket).get() + cluster = await Cluster(nb = self.nb, websocket = self.websocket).get() return { "name": self.default_name, diff --git a/netbox_proxbox/backend/routes/netbox/dcim/interfaces.py b/netbox_proxbox/backend/routes/netbox/dcim/interfaces.py index 8caac4c..947b63b 100755 --- a/netbox_proxbox/backend/routes/netbox/dcim/interfaces.py +++ b/netbox_proxbox/backend/routes/netbox/dcim/interfaces.py @@ -19,7 +19,7 @@ class Interface(NetboxBase): object_name = "Interface" async def get_base_dict(self): - device = await Device(nb = self.nb).get() + device = await Device(nb = self.nb, websocket = self.websocket).get() return { "device": device.id, diff --git a/netbox_proxbox/backend/routes/netbox/generic.py b/netbox_proxbox/backend/routes/netbox/generic.py index 222f37c..9cb9e02 100755 --- a/netbox_proxbox/backend/routes/netbox/generic.py +++ b/netbox_proxbox/backend/routes/netbox/generic.py @@ -6,7 +6,8 @@ from netbox_proxbox.backend.session.netbox import NetboxSessionDep from netbox_proxbox.backend.exception import ProxboxException -from netbox_proxbox.backend.logging import logger +from netbox_proxbox.backend.logging import logger, log +from fastapi import WebSocket from netbox_proxbox.backend.cache import cache @@ -64,8 +65,11 @@ def __init__( ), ] = False, primary_field_value: str = None, + websocket: WebSocket = None, ): + self.nb = nb + self.websocket = websocket self.id = id self.all = all self.default = default @@ -117,7 +121,7 @@ async def get( #base_dict = await self.get_base_dict() print(kwargs) - logger.info(f"[GET] Getting '{self.object_name}' from Netbox.") + await log(self.websocket, f" [GET] Getting '{self.object_name}' from Netbox.") if self.id: return await self._get_by_id() if self.all: return await self._get_all() @@ -127,18 +131,18 @@ async def get( if self.pynetbox_path.count() == 0: - logger.info(f"[GET] There's no '{self.object_name}' registered on Netbox. Creating a DEFAULT ONE.") + await log(self.websocket, f" [GET] There's no '{self.object_name}' registered on Netbox. Creating a DEFAULT ONE.") self.default = True create_default_object = await self.post() if create_default_object != None: - logger.info(f"[GET] Default '{self.object_name}' created successfully. {self.object_name} ID: {create_default_object.id}") + await log(self.websocket, f" [GET] Default '{self.object_name}' created successfully. {self.object_name} ID: {create_default_object.id}") return create_default_object else: raise ProxboxException( - message=f"[GET] Error trying to create default '{self.object_name}' on Netbox.", + message=f" [GET] Error trying to create default '{self.object_name}' on Netbox.", detail=f"No objects found. Default '{self.object_name}' could not be created." ) @@ -150,7 +154,7 @@ async def get( # 2.2 # 2.2.1 If there's any 'Object' registered on Netbox, check if is Proxbox one by checking tag and name. try: - logger.info(f"[GET] '{self.object_name}' found on Netbox. Checking if it's 'Proxbox' one...") + await log(self.websocket, f" [GET] '{self.object_name}' found on Netbox. Checking if it's 'Proxbox' one...") get_object = await asyncio.to_thread(self.pynetbox_path.get, name=self.default_name, slug=self.default_slug, @@ -168,11 +172,11 @@ async def get( ) if get_object != None: - logger.info(f"[GET] The '{self.object_name}' found is from 'Proxbox' (because it has the tag). Returning it.") + await log(self.websocket, f" [GET] The '{self.object_name}' found is from 'Proxbox' (because it has the tag). Returning it.") return get_object # 2.2.2. If it's not Proxbox one, create a default one. - logger.info(f"[GET] The '{self.object_name}' object found IS NOT from 'Proxbox'. Creating a default one.") + await log(self.websocket, f" [GET] The '{self.object_name}' object found IS NOT from 'Proxbox'. Creating a default one.") self.default = True default_object = await self.post() return default_object @@ -187,27 +191,27 @@ async def get( async def _get_by_kwargs(self, **kwargs): - logger.info(f"[GET] Searching '{self.object_name}' by kwargs {kwargs}.") + await log(self.websocket, f"[GET] Searching '{self.object_name}' by kwargs {kwargs}.") try: try: response = await asyncio.to_thread(self.pynetbox_path.get, **kwargs) return response except Exception as error: if "get() returned more than one result." in f"{error}": - logger.info(f"[CHECK DUPLICATE] Object '{self.object_name}' with the same name already found. Checking with '.filter' method") + await log(self.websocket, f"[CHECK DUPLICATE] Object '{self.object_name}' with the same name already found. Checking with '.filter' method") if self.endpoint == "interfaces" and self.primary_field == "device": - logger.info("[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the DEVICE.") + await log(self.websocket, "[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the DEVICE.") result_by_primary = await asyncio.to_thread(self.pynetbox_path.get, virtual_machine=self.primary_field_value) if result_by_primary: if result_by_primary.virtual_machine == self.primary_field_value: - logger.info("[CHECK DUPLICATE] Interface with the same Device found. Duplicated object, returning it.") + await log(self.websocket, "[CHECK DUPLICATE] Interface with the same Device found. Duplicated object, returning it.") return result_by_primary else: - logger.info("[CHECK DUPLICATE] If interface equal, but different devices, return as NOT duplicated.") + await log(self.websocket, "[CHECK DUPLICATE] If interface equal, but different devices, return as NOT duplicated.") return None @@ -227,7 +231,7 @@ async def _get_by_id(self): If Query Parameter 'id' provided, use it to get the object from Netbox. """ - logger.info(f"[GET] Searching '{self.object_name}' by ID {self.id}.") + await log(self.websocket, f"[GET] Searching '{self.object_name}' by ID {self.id}.") response = None @@ -243,7 +247,7 @@ async def _get_by_id(self): # 1.1. Return found object. if response != None: - logger.info(f"[GET] '{self.object_name}' with ID '{self.id}' found on Netbox. Returning it.") + await log(self.websocket, f"[GET] '{self.object_name}' with ID '{self.id}' found on Netbox. Returning it.") return response # 1.2. Raise ProxboxException if object is not found. @@ -308,7 +312,7 @@ async def post( cache.set(self.endpoint, self.base_dict) if data: - logger.info(f"[POST] Creating '{self.object_name}' object on Netbox.") + await log(self.websocket, f" [POST] Creating '{self.object_name}' object on Netbox.") if isinstance(data, dict) == False: try: @@ -316,14 +320,14 @@ async def post( data = data.model_dump(exclude_unset=True) except Exception as error: raise ProxboxException( - message=f"[POST] Error parsing Pydantic model to Dict.", + message=f" [POST] Error parsing Pydantic model to Dict.", python_exception=f"{error}", ) # If no explicit slug was provided by the payload, create one based on the name. if data.get("slug") == None: if not self.primary_field: - logger.info("[POST] SLUG field not provided on the payload. Creating one based on the NAME or MODEL field.") + await log(self.websocket, " [POST] SLUG field not provided on the payload. Creating one based on the NAME or MODEL field.") try: data["slug"] = data.get("name").replace(" ", "-").lower() except AttributeError: @@ -332,11 +336,11 @@ async def post( data["slug"] = data.get("model").replace(" ", "-").lower() except AttributeError: raise ProxboxException( - message=f"[POST] No 'name' or 'model' field provided on the payload. Please provide one of them.", + message=f" [POST] No 'name' or 'model' field provided on the payload. Please provide one of them.", ) if self.default or data == None: - logger.info(f"[POST] Creating DEFAULT '{self.object_name}' object on Netbox.") + await log(self.websocket, f" [POST] Creating DEFAULT '{self.object_name}' object on Netbox.") data = self.base_dict try: @@ -358,7 +362,7 @@ async def post( data["tags"].append(self.nb.tag.id) try: - logger.info(f"[POST] Trying to create {self.object_name} object on Netbox.") + await log(self.websocket, f" [POST] Trying to create {self.object_name} object on Netbox.") response = await asyncio.to_thread(self.pynetbox_path.create, data) @@ -375,18 +379,18 @@ async def post( else: raise ProxboxException( - message=f"[POST] Error trying to create '{self.object_name}' object on Netbox.", + message=f" [POST] Error trying to create '{self.object_name}' object on Netbox.", python_exception=error ) if response: - logger.info(f"[POST] '{self.object_name}' object created successfully. {self.object_name} ID: {response.id}") + await log(self.websocket, f"[POST] '{self.object_name}' object created successfully. {self.object_name} ID: {response.id}") return response else: - logger.error(f"[POST] '{self.object_name}' object could not be created.") + logger.error(f" [POST] '{self.object_name}' object could not be created.") else: - logger.info(f"[POST] '{self.object_name}' object already exists on Netbox. Returning it.") + await log(self.websocket, f" [POST] '{self.object_name}' object already exists on Netbox. Returning it.") return check_duplicate_result #except ProxboxException as error: raise error @@ -410,10 +414,10 @@ async def post( async def _check_duplicate(self, search_params: dict = None, object: dict = None): - logger.info(f"[CHECK DUPLICATE] Checking if '{self.object_name}' exists on Netbox before creating it.") + await log(self.websocket, f"[CHECK DUPLICATE] Checking if '{self.object_name}' exists on Netbox before creating it.") if self.default: - logger.info("[CHECK DUPLICATE] Checking default object.") + await log(self.websocket, "[CHECK DUPLICATE] Checking default object.") try: result = await asyncio.to_thread(self.pynetbox_path.get, name=self.default_name, @@ -448,21 +452,21 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None except Exception as error: raise ProxboxException( - message=f"[POST] Error trying to create default {self.object_name} on Netbox.", + message=f" [POST] Error trying to create default {self.object_name} on Netbox.", python_exception=f"{error}" ) if object: try: if (self.primary_field): - logger.info("[CHECK DUPLICATE] (0.5) Checking object using only custom PRIMARY FIELD and Proxbox TAG provided by the class attribute.") + await log(self.websocket, "[CHECK DUPLICATE] (0.5) Checking object using only custom PRIMARY FIELD and Proxbox TAG provided by the class attribute.") print(f"primary field: {self.primary_field} - primary_field_value: {self.primary_field_value}") print(f'self.primary_field = {self.primary_field} / {self.endpoint}') if self.primary_field == "address": - logger.info("[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the ADDRESS.") + await log(self.websocket, "[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the ADDRESS.") try: result_by_primary = await asyncio.to_thread(self.pynetbox_path.get, address=self.primary_field_value) @@ -485,11 +489,11 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None print(f"self.primary_field_value = {self.primary_field_value}") if result_by_primary: - logger.info("[CHECK DUPLICATE] IP Address with the same network found. Returning it.") + await log(self.websocket, "[CHECK DUPLICATE] IP Address with the same network found. Returning it.") return result_by_primary if self.primary_field == "virtual_machine" and self.endpoint == "interfaces": - logger.info("[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the DEVICE.") + await log(self.websocket, "[CHECK DUPLICATE] Checking duplicate device using as PRIMARY FIELD the DEVICE.") result_by_primary = None @@ -517,10 +521,10 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None return None except Exception as error: - logger.info(f"[CHECK DUPLICATE] Error trying to get interface using only 'virtual_machine' field as parameter.\n >{error}") + await log(self.websocket, f"[CHECK DUPLICATE] Error trying to get interface using only 'virtual_machine' field as parameter.\n >{error}") if "get() returned more than one result" in f"{error}": # FILTER - logger.info("[CHECK DUPLICATE] Found more than one VM INTERFACE object with the same 'virtual_machine' field. Trying to use '.filter' pynetbox method now.") + await log(self.websocket, "[CHECK DUPLICATE] Found more than one VM INTERFACE object with the same 'virtual_machine' field. Trying to use '.filter' pynetbox method now.") try: @@ -562,30 +566,30 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None if result_by_primary: if self.endpoint == "interfaces": - logger.info("[CHECK DUPLICATE] If duplicate interface found, check if the devices are the same.") + await log(self.websocket, "[CHECK DUPLICATE] If duplicate interface found, check if the devices are the same.") if result_by_primary.device == self.primary_field_value: - logger.info("[CHECK DUPLICATE] Interface with the same Device found. Duplicated object, returning it.") + await log(self.websocket, "[CHECK DUPLICATE] Interface with the same Device found. Duplicated object, returning it.") return result_by_primary else: - logger.info("[CHECK DUPLICATE] If interface equal, but different devices, return as NOT duplicated.") + await log(self.websocket, "[CHECK DUPLICATE] If interface equal, but different devices, return as NOT duplicated.") return None - logger.info(f"[CHECK_DUPLICATE] Object found on Netbox. Returning it.") + await log(self.websocket, f"[CHECK_DUPLICATE] Object found on Netbox. Returning it.") print(f'result_by_primary: {result_by_primary}') return result_by_primary return None - logger.info("[CHECK DUPLICATE] (1) First attempt: Checking object making EXACT MATCH with the Payload provided...") + await log(self.websocket, " [CHECK DUPLICATE] (1) First attempt: Checking object making EXACT MATCH with the Payload provided...") result = await asyncio.to_thread(self.pynetbox_path.get, dict(object)) if result: - logger.info(f"[CHECK DUPLICATE] Object found on Netbox. Returning it.") + await log(self.websocket, f" [CHECK DUPLICATE] Object found on Netbox. Returning it.") return result else: - logger.info("[CHECK DUPLICATE] (1.5) Checking object using NAME and DEVICE provided by the Payload and also the PROXBOX TAG. If found, return it.") + await log(self.websocket, " [CHECK DUPLICATE] (1.5) Checking object using NAME and DEVICE provided by the Payload and also the PROXBOX TAG. If found, return it.") result_by_device = None @@ -594,7 +598,7 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None print(f"object: {object}") device_obj = None try: - logger.info("[CHECK DUPLICATE] (1.5.1) Checking duplicate using Device Object as parameter.") + await log(self.websocket, " [CHECK DUPLICATE] (1.5.1) Checking duplicate using Device Object as parameter.") device_obj = self.nb.session.dcim.devices.get(int(device_id)) print(f"device_obj: {device_obj}") @@ -612,7 +616,7 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None except: - logger.info("[CHECK DUPLICATE] (1.5.1) Device Object NOT found when checking for duplicated using Device as parameter.") + await log(self.websocket, " [CHECK DUPLICATE] (1.5.1) Device Object NOT found when checking for duplicated using Device as parameter.") if result_by_device: @@ -629,16 +633,16 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None if int(object.get("device")) != int(result_by_device.device.id): return None - logger.info("[CHECK DUPLICATE] (1.5.1) Object found on Netbox. Returning it.") + await log(self.websocket, " [CHECK DUPLICATE] (1.5.1) Object found on Netbox. Returning it.") return result_by_device - logger.info("[CHECK DUPLICATE] (2) Checking object using only NAME and SLUG provided by the Payload and also the PROXBOX TAG. If found, return it.") + await log(self.websocket, " [CHECK DUPLICATE] (2) Checking object using only NAME and SLUG provided by the Payload and also the PROXBOX TAG. If found, return it.") result_by_tag = None try: - logger.info("[CHECK DUPLICATE] (2.1) Searching object using 'get' method") + await log(self.websocket, " [CHECK DUPLICATE] (2.1) Searching object using 'get' method") result_by_tag = await asyncio.to_thread(self.pynetbox_path.get, name=object.get("name"), slug=object.get("slug"), @@ -656,13 +660,13 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None ) if result_by_tag: - logger.info("[CHECK DUPLICATE] (2) More than one object found.") + await log(self.websocket, " [CHECK DUPLICATE] (2) More than one object found.") for obj in result_by_tag: print(f"obj.id: {obj.device.id} / device_obj.id: {device_obj.id}") if int(obj.device.id) == int(device_obj.id): - logger.info("[CHECK DUPLICATE] (2) More than one object found, but returning with the same ID.") + await log(self.websocket, " [CHECK DUPLICATE] (2) More than one object found, but returning with the same ID.") return obj return None print(f"filter: {result_by_tag}") @@ -672,11 +676,11 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None if result_by_tag: - logger.info(f"[CHECK DUPLICATE] Object found on Netbox. Returning it.") + await log(self.websocket, f" [CHECK DUPLICATE] Object found on Netbox. Returning it.") return result_by_tag - logger.info(f"[CHECK DUPLICATE] (3) Checking duplicate object using only NAME and SLUG") + await log(self.websocket, f" [CHECK DUPLICATE] (3) Checking duplicate object using only NAME and SLUG") result_by_name_and_slug = await asyncio.to_thread(self.pynetbox_path.get, name=object.get("name"), slug=object.get("slug"), @@ -684,7 +688,7 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None if result_by_name_and_slug: raise ProxboxException( - message=f"[CHECK DUPLICATE] '{self.object_name}' with ID '{result_by_name_and_slug.id}' found on Netbox, but without Proxbox tag. Please delete it (or add the tag) and try again.", + message=f" [CHECK DUPLICATE] '{self.object_name}' with ID '{result_by_name_and_slug.id}' found on Netbox, but without Proxbox tag. Please delete it (or add the tag) and try again.", detail="Netbox does not allow duplicated names and/or slugs." ) @@ -703,7 +707,7 @@ async def _check_duplicate(self, search_params: dict = None, object: dict = None # name = search_params.get("name") # slug = search_params.get("slug") - # logger.info(f"Checking if {name} exists on Netbox.") + # await log(self.websocket, f"Checking if {name} exists on Netbox.") # """ # Check if object exists on Netbox based on the dict provided. # The fields used to distinguish duplicates are: diff --git a/netbox_proxbox/backend/routes/netbox/virtualization/cluster.py b/netbox_proxbox/backend/routes/netbox/virtualization/cluster.py index dba161a..9f10026 100755 --- a/netbox_proxbox/backend/routes/netbox/virtualization/cluster.py +++ b/netbox_proxbox/backend/routes/netbox/virtualization/cluster.py @@ -15,7 +15,7 @@ class Cluster(NetboxBase): async def get_base_dict(self): - type = await ClusterType(nb = self.nb).get() + type = await ClusterType(nb = self.nb, websocket = self.websocket).get() return { "name": self.default_name, diff --git a/netbox_proxbox/backend/routes/netbox/virtualization/interfaces.py b/netbox_proxbox/backend/routes/netbox/virtualization/interfaces.py index 8f033b2..07be6ad 100755 --- a/netbox_proxbox/backend/routes/netbox/virtualization/interfaces.py +++ b/netbox_proxbox/backend/routes/netbox/virtualization/interfaces.py @@ -15,7 +15,7 @@ class VMInterface(NetboxBase): async def get_base_dict(self): - virtual_machine = await VirtualMachine(nb = self.nb).get() + virtual_machine = await VirtualMachine(nb = self.nb, websocket = self.websocket).get() return { "virtual_machine": virtual_machine.id, diff --git a/netbox_proxbox/backend/routes/netbox/virtualization/virtual_machines.py b/netbox_proxbox/backend/routes/netbox/virtualization/virtual_machines.py index 970c893..0316211 100755 --- a/netbox_proxbox/backend/routes/netbox/virtualization/virtual_machines.py +++ b/netbox_proxbox/backend/routes/netbox/virtualization/virtual_machines.py @@ -13,7 +13,7 @@ class VirtualMachine(NetboxBase): async def get_base_dict(self): - cluster = await Cluster(nb = self.nb).get() + cluster = await Cluster(nb = self.nb, websocket = self.websocket).get() return { "name": self.default_name, "slug": self.default_slug, diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index d0537ab..3aab53c 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -6,9 +6,9 @@ from netbox_proxbox.backend.session.proxmox import ProxmoxSessionsDep from netbox_proxbox.backend.session.netbox import NetboxSessionDep -from netbox_proxbox.backend.logging import logger +from netbox_proxbox.backend.logging import logger, log -from netbox_proxbox.backend.exception import ProxboxException +from netbox_proxbox.backend.exception import ProxboxException, exception_log from netbox_proxbox.backend import ( ClusterType, @@ -65,20 +65,21 @@ async def proxbox_get_clusters( # Create Cluster Type object before the Cluster itself try: - await log(websocket, "Creating the Cluster Type before Cluster...") - cluster_type_obj = await ClusterType(nb = nb).post( + await log(websocket, "Creating the Cluster Type before Cluster...") + cluster_type_obj = await ClusterType(nb = nb, websocket = websocket).post( data = { "name": cluster_type_name, "slug": cluster_type_slug, "description": description } ) - except Exception as error: raise ProxboxException(message="Error trying to create the cluster type.", python_exception=error) + #except Exception as error: raise ProxboxException(message="Error trying to create the cluster type.", python_exception=error) + except Exception as error: await exception_log(logger=log,websocket=websocket, message="Error trying to create the cluster type.", python_exception=error) # Create the Cluster try: await log(websocket, "Creating the Cluster...") - cluster_obj = await Cluster(nb = nb).post( + cluster_obj = await Cluster(nb = nb, websocket = websocket).post( data = { "name": px.name, "slug": px.name, @@ -86,7 +87,8 @@ async def proxbox_get_clusters( "status": "active", } ) - except Exception as error: raise ProxboxException(message="Error trying to create the cluster.", python_exception=error) + #except Exception as error: raise ProxboxException(message="Error trying to create the cluster.", python_exception=error) + except Exception as error: await exception_log(logger=log,websocket=websocket, message="Error trying to create the cluster.", python_exception=error) result.append( @@ -119,12 +121,6 @@ def find_interface_type( from fastapi import WebSocket -async def log(websocket, msg, level = None): - if level == "debug": logger.debug(msg) - else: logger.info(msg) - - await websocket.send_text(msg) - @router.get("/nodes") async def get_nodes( nb: NetboxSessionDep, @@ -139,7 +135,7 @@ async def get_nodes( for px in pxs: # Get Cluster from Netbox based on Proxmox Cluster Name - get_cluster_from_netbox = await Cluster(nb = nb).get(name = px.name) + get_cluster_from_netbox = await Cluster(nb = nb, websocket = websocket).get(name = px.name) # Get Proxmox Nodes from the current Proxmox Cluster proxmox_nodes = px.session.nodes.get() @@ -151,10 +147,10 @@ async def get_nodes( for node in proxmox_nodes: try: - msg = f"Creating Device '{node.get("node")}' related with the Virtual Machine(s)" + msg = f" Creating Device {node.get("node")} related with the Virtual Machine(s)" await log(websocket, msg) - current_node = await Device(nb=nb).post( + current_node = await Device(nb=nb, websocket=websocket).post( data = { "name": node.get("node"), "cluster": get_cluster_from_netbox.id, @@ -162,16 +158,22 @@ async def get_nodes( } ) - msg = f"Device '{current_node}' created successfully." + if current_node: + nodes.append(current_node) + + msg = f" Device {current_node} created successfully." await log(websocket, msg) except Exception as error: - raise ProxboxException( - message="Error trying to create Netbox Device object.", + #raise ProxboxException( + await exception_log( + logger=log, + websocket=websocket, + message=" Error trying to create Netbox Device object.", python_exception=error ) - nodes.append(current_node) + print(node) node_interfaces = px.session.nodes(node.get("node")).network.get() @@ -195,9 +197,11 @@ async def get_nodes( enabled = True else: enabled = False + create_interface = None + try: - await log(websocket, f"Creating Netbox '{interface_name}' Interface on '{current_node.name}' Device...") - create_interface = await Interface(nb=nb, primary_field_value=current_node.id).post(data={ + await log(websocket, f" Creating Netbox {interface_name} Interface on {current_node.name} Device...") + create_interface = await Interface(nb=nb, websocket=websocket, primary_field_value=current_node.id).post(data={ "device": current_node.id, "name": interface_name, "enabled": enabled, @@ -205,13 +209,21 @@ async def get_nodes( "mtu": interface.get("mtu", None), "description": interface.get("comments", "") }) + + if create_interface: + print(f'create_interface: {create_interface}') + await log(websocket, f" Netbox Interface {create_interface.name} created successfully.") + except Exception as error: - raise ProxboxException( - message="Error trying to create Netbox interface.", + #raise ProxboxException( + await exception_log( + logger=log, + websocket=websocket, + message=" Error trying to create Netbox interface.", python_exception=error ) - print(f'create_interface: {create_interface}') + print(f"interface value type: {type(interface)}") @@ -220,17 +232,24 @@ async def get_nodes( if create_interface and cidr: try: - await log(websocket, "Interface with CIDR/Network. Creating the IP Address object on Netbox...") + await log(websocket, f" Interface with CIDR/Network. Creating the IP Address {cidr} object of Interface {create_interface.name} on Netbox...") # If interface with network configured, create IP Address and attach interface to it. - create_ipaddress = await IPAddress(nb=nb, primary_field_value=cidr).post(data={ + create_ipaddress = await IPAddress(nb=nb, websocket=websocket, primary_field_value=cidr).post(data={ "address": cidr, "assigned_object_id": create_interface.id, "assigned_object_type": "dcim.interface" }) + + if create_ipaddress: + log(websocket, f"IP Address {create_ipaddress.address} of Interface {create_interface.name}created successfully.") + print(f'create_ipaddress: {create_ipaddress}') except Exception as error: - raise ProxboxException( - message="Error trying to create IP Address of Interface on Netbox.", + #raise ProxboxException( + await exception_log( + logger=log, + websocket=websocket, + message=" Error trying to create IP Address of Interface on Netbox.", python_exception=error ) @@ -245,23 +264,34 @@ async def get_nodes( for port in bridge_ports: - print(f'current_node: {current_node}') + # print(f'current_node: {current_node}') + + netbox_port = None try: - await log(websocket, "Searching children interface of a bridge.") + await log(websocket, f" Searching children interface of bridge interface {create_interface.name}.") print(f"current_node.id: {current_node.id} / current_node: {current_node} / current_node.name: {current_node.name}") - netbox_port = await Interface(nb=nb, primary_field_value=current_node.id).get( + + log(websocket, f"Searching child interface {port} with Device ID {current_node.id}") + netbox_port = await Interface(nb=nb, websocket=websocket, primary_field_value=current_node.id).get( name=port ) + + if netbox_port: + print(f"netbox_port: {netbox_port}") + except Exception as error: - raise ProxboxException( - message="Error trying to search bridge child interface.", + #raise ProxboxException( + await exception_log( + logger=log, + websocket=websocket, + message=" Error trying to search bridge child interface.", python_exception=f"{error}" ) print(f"port: {port}") - print(f"netbox_port: {netbox_port}") + if not netbox_port: proxmox_port = px.session.nodes(node.get("node")).network(port).get() @@ -275,60 +305,73 @@ async def get_nodes( # Interface and Bridge Interface must belong to the same Device - await log(websocket, "Creating child interface of a bridge. Bridge interface and child must belong to the same device.") - if create_interface.device == current_node.id: - - await log(websocket, f"Creating interface '{port}'...") + await log(websocket, " Creating child interface of a bridge. Bridge interface and child must belong to the same device.") + #print(f"create_interface.device: {create_interface.device}\ncurrent_node.id: {current_node.id}") + if create_interface: + if create_interface.device == current_node.id: - try: - new_netbox_port = await Interface(nb=nb, primary_field_value=current_node.id).post(data={ - "device": current_node.id, - "name": port, - "enabled": enabled, - "type": interface_type, - "mtu": proxmox_port.get("mtu", None), - "description": proxmox_port.get("comments", ""), - "bridge": create_interface.id - }) + await log(websocket, f"Creating interface '{port}'...") - await log(websocket, f"Interface '{port}' created successfully.") - - except Exception as error: - raise ProxboxException( - message="Error trying to create child interface of bridge interface.", - python_exception=error - ) - - cidr = proxmox_port.get("cidr") - print(f"[2] cidr: {cidr}") - - if cidr: - await log(websocket, "If interface with network configured, create IP Address and attach interface to it.") try: - create_ipaddress = await IPAddress(nb=nb, primary_field_value=cidr).post(data={ - "address": cidr, - "assigned_object_id": new_netbox_port.id, - "assigned_object_type": "dcim.interface" + new_netbox_port = await Interface(nb=nb, websocket=websocket, primary_field_value=current_node.id).post(data={ + "device": current_node.id, + "name": port, + "enabled": enabled, + "type": interface_type, + "mtu": proxmox_port.get("mtu", None), + "description": proxmox_port.get("comments", ""), + "bridge": create_interface.id }) - except Exception as error: raise ProxboxException(message="Error trying to create IP Address on Netbox", python_exception=error) + + if new_netbox_port: + await log(websocket, f" Child Bridge Interface {port} of {create_interface.name} created successfully.") + + except Exception as error: + + #raise ProxboxException( + await log_exception( + websocket=websocket, + message=" Error trying to create child interface of bridge interface.", + python_exception=error + ) + + cidr = proxmox_port.get("cidr") + print(f"[2] cidr: {cidr}") + + if cidr: + await log(websocket, f" If interface with network configured, create IP Address {cidr} and attach interface to it.") + try: + create_ipaddress = await IPAddress(nb=nb, websocket=websocket, primary_field_value=cidr).post(data={ + "address": cidr, + "assigned_object_id": new_netbox_port.id, + "assigned_object_type": "dcim.interface" + }) + + if create_ipaddress: + log(websocket, f" IP Address {create_ipaddress.address} of Interface {new_netbox_port.name} created successfully.") + + #except Exception as error: raise ProxboxException(message=" Error trying to create IP Address on Netbox", python_exception=error) + except Exception as error: await exception_log(logger=log,websocket=websocket,message=" Error trying to create IP Address on Netbox", python_exception=error) - else: - await log(websocket, "Interface already exists. Attaching Bridge to Interface") - print(f'create_interface: {create_interface}') - # Interface and Bridge Interface must belong to the same Device - if create_interface.device == current_node.id: - netbox_port.bridge = create_interface.id - netbox_port.device = current_node.id - netbox_port.save() + else: + print(f"netbox_port {netbox_port}\ncreate_interface.device {create_interface.device}\ncurrent_node.id: {current_node.id}") + + await log(websocket, f" Interface already exists. Attaching Bridge to Interface {create_interface.name}") + print(f'create_interface: {create_interface}') + # Interface and Bridge Interface must belong to the same Device + if create_interface.device == current_node.id: + netbox_port.bridge = create_interface.id + netbox_port.device = current_node.id + netbox_port.save() print(f'interface: {interface}') print("\n") - await log(websocket, f"Nodes: {nodes}", "debug") + await log(websocket, f" Nodes: {nodes}", "debug") result.append({ @@ -389,7 +432,7 @@ class VirtualMachineStatus(Enum): This way we are able to minimize the number of requests to Netbox API """ if devices.get(vm_node) == None: - devices[vm_node] = await Device(nb = nb).get(name = vm.get("node")) + devices[vm_node] = await Device(nb = nb, websocket = websocket).get(name = vm.get("node")) device = devices[vm_node] print(f"devices[vm_node]: {devices[vm_node]} | {device}") @@ -401,13 +444,13 @@ class VirtualMachineStatus(Enum): This way we are able to minimize the number of requests to Netbox API """ if clusters.get(px.name) == None: - clusters[px.name] = await Cluster(nb = nb).get(name = px.name) + clusters[px.name] = await Cluster(nb = nb, websocket = websocket).get(name = px.name) cluster = clusters[px.name] - role = await DeviceRole(nb = nb).get(slug = vm.get("type")) + role = await DeviceRole(nb = nb, websocket = websocket).get(slug = vm.get("type")) if role == None: vm_type = vm.get("type") @@ -426,7 +469,7 @@ class VirtualMachineStatus(Enum): color = "7fffd4" description = "Proxmox Container" - role = await DeviceRole(nb = nb).post(data = { + role = await DeviceRole(nb = nb, websocket = websocket).post(data = { "name": vm_name, "slug": vm_type, "vm_role": True, @@ -637,26 +680,29 @@ class VirtualMachineStatus(Enum): } try: - await log(websocket, "Creating Virtual Machine on Netbox...") - new_virtual_machine = await VirtualMachine(nb = nb).post(data = virtual_machine_data) + await log(websocket, f" Creating Virtual Machine {vm.get("name")} on Netbox...") + new_virtual_machine = await VirtualMachine(nb = nb, websocket = websocket).post(data = virtual_machine_data) + + if new_virtual_machine: + log(websocket, f"Virtual Machine {new_virtual_machine.name} created successfully.") except Exception as error: - if "Virtual machine name must be unique per cluster." in str(error.python_exception): + if "Virtual machine name must be unique per cluster." in str(error): print("\nDUPLICATED VIRTUAL MACHINE NAME\n") - logger.warning("Duplicated virtual machine NAME found within the same cluster. Appending '(2)' to the name") + log(websocket, f" Duplicated virtual machine NAME {virtual_machine_data["name"]} found within the same cluster. Appending '(2)' to the name") virtual_machine_data["name"] = f"{virtual_machine_data["name"]} (2)" - duplicated_virtual_machine = await VirtualMachine(nb = nb).post(data = virtual_machine_data) + duplicated_virtual_machine = await VirtualMachine(nb = nb, websocket = websocket).post(data = virtual_machine_data) if duplicated_virtual_machine: new_virtual_machine = duplicated_virtual_machine print(f"error: {error} / {type(error)}") raise ProxboxException( - message=f"[CHECK DUPLICATE] Error trying to create Virtual Machine '{vm.get("name")}' on Netbox.", + message=f" [CHECK DUPLICATE] Error trying to create Virtual Machine '{vm.get("name")}' on Netbox.", python_exception=f"{error}" ) @@ -669,7 +715,7 @@ class VirtualMachineStatus(Enum): vm_networks = [] network_id = 0 - await log(websocket, "Getting network info and parsing data into JSON (dict)") + await log(websocket, " Getting network info and parsing data into JSON (dict)") while True: network_name = f"net{network_id}" @@ -712,18 +758,18 @@ class VirtualMachineStatus(Enum): hwaddr = v.get("hwaddr", None) if hwaddr: mac_address=hwaddr - await log(websocket, "Try creating VirtualMachine Interface on Netbox...") + await log(websocket, f" Try creating VirtualMachine Interface {str(k)} on Netbox...") try: - #vm_already_exists = await VMInterface(nb=nb).get({ + #vm_already_exists = await VMInterface(nb=nb, websocket=websocket).get({ # "virtual_machine": new_virtual_machine.id, # "name": str(k) #}) #if not vm_already_exists: - netbox_interface = await VMInterface(nb=nb).post(data={ + netbox_interface = await VMInterface(nb=nb, websocket=websocket).post(data={ "virtual_machine": new_virtual_machine.id, "name": str(k), "enabled": True, @@ -731,15 +777,15 @@ class VirtualMachineStatus(Enum): }) if netbox_interface: - await log(websocket, f"Virtual Machine Interface created successfully. {netbox_interface.id} - {netbox_interface.name}") + await log(websocket, f" Virtual Machine Interface created successfully.") except Exception as error: raise ProxboxException( - message="Error trying to create VM interface on Netbox", + message=" Error trying to create VM interface on Netbox", ) except Exception as error: raise ProxboxException( - message=f"Error trying to create {new_virtual_machine.name} VM Network Interfaces", + message=f" Error trying to create {new_virtual_machine.name} VM Network Interfaces", ) result.append({ diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 12c540c..6003356 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -16,8 +16,11 @@

Log Messages

ws.onmessage = function(event) { var messages = document.getElementById('messages') var message = document.createElement('li') - var content = document.createTextNode(event.data) - message.appendChild(content) + + message.innerHTML = event.data + + // var content = document.createTextNode(event.data) + // message.appendChild(content) messages.appendChild(message) }; function sendMessage(event) {