From 5519a5edcf5270b11e81fb65caeea2c2a72bfbe6 Mon Sep 17 00:00:00 2001 From: Robert Romero Date: Wed, 20 Mar 2024 12:48:01 -0700 Subject: [PATCH 1/7] Truncate Serials Over 50 characters in inventory.py Ensured serial numbers don't exceed 50 characters. --- netbox_agent/inventory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/netbox_agent/inventory.py b/netbox_agent/inventory.py index 40e73d4..fa6e8f6 100644 --- a/netbox_agent/inventory.py +++ b/netbox_agent/inventory.py @@ -172,7 +172,7 @@ def create_netbox_interface(self, iface): discovered=True, tags=[{'name': INVENTORY_TAG['interface']['name']}], name="{}".format(iface['product']), - serial='{}'.format(iface['serial']), + serial='{}'.format(iface['serial'][:50]), description='{} {}'.format(iface['description'], iface['name']) ) @@ -256,7 +256,7 @@ def create_netbox_raid_card(self, raid_card): ) name = raid_card.get_product_name() - serial = raid_card.get_serial_number() + serial = raid_card.get_serial_number()[:50] nb_raid_card = nb.dcim.inventory_items.create( device=self.device_id, discovered=True, @@ -371,7 +371,7 @@ def create_netbox_disk(self, disk): desc = disk.get('description') name = '{} ({})'.format(disk['Model'], disk['Size']) description = disk['Type'] - sn = disk.get('SN', 'unknown') + sn = disk.get('SN', 'unknown')[:50] parms = { 'device': self.device_id, @@ -455,7 +455,7 @@ def create_netbox_memory(self, memory): tags=[{'name': INVENTORY_TAG['memory']['name']}], name=name, part_id=memory['product'], - serial=memory['serial'], + serial=memory['serial'][:50], description=memory['description'], ) From 51687dd9f0ec51ac435cf0fee2812006fc6a367d Mon Sep 17 00:00:00 2001 From: Robert Romero Date: Wed, 20 Mar 2024 13:09:06 -0700 Subject: [PATCH 2/7] Skips IP within 0.0.0.0/0 within network.py Fixed an issue with IPMI interfaces with a default IP of 0.0.0.0/0, which can not be assigned within netbox. Fixed a small typo: (assigned_object and assigned_object_id != interface.id): vs. (assigned_object and assigned_object.id != interface.id): --- netbox_agent/network.py | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/netbox_agent/network.py b/netbox_agent/network.py index 673dfc1..e581b4c 100644 --- a/netbox_agent/network.py +++ b/netbox_agent/network.py @@ -326,26 +326,38 @@ def create_or_update_netbox_ip_on_interface(self, ip, interface): * If IP exists and isn't assigned, take it * If IP exists and interface is wrong, change interface ''' - netbox_ips = nb.ipam.ip_addresses.filter( + netbox_ip = None + # Check if the IP is not 0.0.0.0/0 + if ip != '0.0.0.0/0': + netbox_ips = nb.ipam.ip_addresses.filter( address=ip, - ) - if not netbox_ips: - logging.info('Create new IP {ip} on {interface}'.format( - ip=ip, interface=interface)) - query_params = { - 'address': ip, - 'status': "active", - 'assigned_object_type': self.assigned_object_type, - 'assigned_object_id': interface.id - } + ) + if not netbox_ips: + logging.info('Create new IP {ip} on {interface}'.format( + ip=ip, interface=interface)) + query_params = { + 'address': ip, + 'status': "active", + 'assigned_object_type': self.assigned_object_type, + 'assigned_object_id': interface.id + } netbox_ip = nb.ipam.ip_addresses.create( **query_params ) return netbox_ip - - netbox_ip = list(netbox_ips)[0] - # If IP exists in anycast + else: + # IP already exists, log a warning and return None + logging.warning('IP {ip} already exists in NetBox'.format(ip=ip)) + return None + else: + # Log that this IP is not being added due to being 0.0.0.0/0 + logging.info('Skipping IP {ip} on {interface} as it is 0.0.0.0/0'.format( + ip=ip, interface=interface)) + + # Check if netbox_ip is not None before accessing its properties + if netbox_ip is not None: + # Proceed with further processing only if netbox_ip is created if netbox_ip.role and netbox_ip.role.label == 'Anycast': logging.debug('IP {} is Anycast..'.format(ip)) unassigned_anycast_ip = [x for x in netbox_ips if x.interface is None] @@ -377,7 +389,7 @@ def create_or_update_netbox_ip_on_interface(self, ip, interface): logging.info('Assigning existing IP {ip} to {interface}'.format( ip=ip, interface=interface)) elif (ip_interface and ip_interface.id != interface.id) or \ - (assigned_object and assigned_object_id != interface.id): + (assigned_object and assigned_object.id != interface.id): old_interface = getattr(netbox_ip, "assigned_object", "n/a") logging.info( From 15205bde97868674710157b3419c95aba0bee4d4 Mon Sep 17 00:00:00 2001 From: Robert Romero <1013414+NessieCanCode@users.noreply.github.com> Date: Fri, 22 Mar 2024 08:36:01 -0700 Subject: [PATCH 3/7] updated to skip ib interfaces --- .vscode/settings.json | 3 + netbox_agent/network.py | 174 ++++++++++++++++++++-------------------- 2 files changed, 92 insertions(+), 85 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..457f44d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.typeCheckingMode": "basic" +} \ No newline at end of file diff --git a/netbox_agent/network.py b/netbox_agent/network.py index e581b4c..a9e2e8a 100644 --- a/netbox_agent/network.py +++ b/netbox_agent/network.py @@ -54,6 +54,11 @@ def scan(self): logging.debug('Ignore interface {interface}'.format(interface=interface)) continue + # Skip if the interface is ib0 + if interface in ('ib0', 'ib1'): + logging.debug('Skipping ib interface') + continue + ip_addr = netifaces.ifaddresses(interface).get(netifaces.AF_INET, []) ip6_addr = netifaces.ifaddresses(interface).get(netifaces.AF_INET6, []) if config.network.ignore_ips: @@ -314,99 +319,98 @@ def create_netbox_nic(self, nic, mgmt=False): return interface def create_or_update_netbox_ip_on_interface(self, ip, interface): - ''' - Two behaviors: - - Anycast IP - * If IP exists and is in Anycast, create a new Anycast one - * If IP exists and isn't assigned, take it - * If server is decomissioned, then free IP will be taken - - - Normal IP (can be associated only once) - * If IP doesn't exist, create it - * If IP exists and isn't assigned, take it - * If IP exists and interface is wrong, change interface - ''' - netbox_ip = None - # Check if the IP is not 0.0.0.0/0 - if ip != '0.0.0.0/0': - netbox_ips = nb.ipam.ip_addresses.filter( - address=ip, - ) - if not netbox_ips: - logging.info('Create new IP {ip} on {interface}'.format( + ''' + Two behaviors: + - Anycast IP + * If IP exists and is in Anycast, create a new Anycast one + * If IP exists and isn't assigned, take it + * If server is decomissioned, then free IP will be taken + + - Normal IP (can be associated only once) + * If IP doesn't exist, create it + * If IP exists and isn't assigned, take it + * If IP exists and interface is wrong, change interface + ''' + netbox_ip = None + # Check if the IP is not 0.0.0.0/0 + if ip != '0.0.0.0/0': + netbox_ips = nb.ipam.ip_addresses.filter( + address=ip, + ) + if not netbox_ips: + logging.info('Create new IP {ip} on {interface}'.format( ip=ip, interface=interface)) - query_params = { + query_params = { 'address': ip, 'status': "active", 'assigned_object_type': self.assigned_object_type, 'assigned_object_id': interface.id - } + } + netbox_ip = nb.ipam.ip_addresses.create( + **query_params + ) + return netbox_ip + else: + # IP already exists, log a warning and return None + logging.warning('IP {ip} already exists in NetBox'.format(ip=ip)) + return None + else: + # Log that this IP is not being added due to being 0.0.0.0/0 + logging.info('Skipping IP {ip} on {interface} as it is 0.0.0.0/0'.format( + ip=ip, interface=interface)) - netbox_ip = nb.ipam.ip_addresses.create( - **query_params - ) - return netbox_ip - else: - # IP already exists, log a warning and return None - logging.warning('IP {ip} already exists in NetBox'.format(ip=ip)) - return None - else: - # Log that this IP is not being added due to being 0.0.0.0/0 - logging.info('Skipping IP {ip} on {interface} as it is 0.0.0.0/0'.format( - ip=ip, interface=interface)) - - # Check if netbox_ip is not None before accessing its properties - if netbox_ip is not None: - # Proceed with further processing only if netbox_ip is created - if netbox_ip.role and netbox_ip.role.label == 'Anycast': - logging.debug('IP {} is Anycast..'.format(ip)) - unassigned_anycast_ip = [x for x in netbox_ips if x.interface is None] - assigned_anycast_ip = [x for x in netbox_ips if - x.interface and x.interface.id == interface.id] + # Check if netbox_ip is not None before accessing its properties + if netbox_ip is not None: + # Proceed with further processing only if netbox_ip is created + if netbox_ip.role and netbox_ip.role.label == 'Anycast': + logging.debug('IP {} is Anycast..'.format(ip)) + unassigned_anycast_ip = [x for x in netbox_ips if x.interface is None] + assigned_anycast_ip = [x for x in netbox_ips if + x.interface and x.interface.id == interface.id] # use the first available anycast ip - if len(unassigned_anycast_ip): - logging.info('Assigning existing Anycast IP {} to interface'.format(ip)) - netbox_ip = unassigned_anycast_ip[0] - netbox_ip.interface = interface - netbox_ip.save() + if len(unassigned_anycast_ip): + logging.info('Assigning existing Anycast IP {} to interface'.format(ip)) + netbox_ip = unassigned_anycast_ip[0] + netbox_ip.interface = interface + netbox_ip.save() # or if everything is assigned to other servers - elif not len(assigned_anycast_ip): - logging.info('Creating Anycast IP {} and assigning it to interface'.format(ip)) - query_params = { - "address": ip, - "status": "active", - "role": self.ipam_choices['ip-address:role']['Anycast'], - "tenant": self.tenant.id if self.tenant else None, - "assigned_object_type": self.assigned_object_type, - "assigned_object_id": interface.id - } - netbox_ip = nb.ipam.ip_addresses.create(**query_params) - return netbox_ip - else: - ip_interface = getattr(netbox_ip, 'interface', None) - assigned_object = getattr(netbox_ip, 'assigned_object', None) - if not ip_interface or not assigned_object: - logging.info('Assigning existing IP {ip} to {interface}'.format( + elif not len(assigned_anycast_ip): + logging.info('Creating Anycast IP {} and assigning it to interface'.format(ip)) + query_params = { + "address": ip, + "status": "active", + "role": self.ipam_choices['ip-address:role']['Anycast'], + "tenant": self.tenant.id if self.tenant else None, + "assigned_object_type": self.assigned_object_type, + "assigned_object_id": interface.id + } + netbox_ip = nb.ipam.ip_addresses.create(**query_params) + return netbox_ip + else: + ip_interface = getattr(netbox_ip, 'interface', None) + assigned_object = getattr(netbox_ip, 'assigned_object', None) + if not ip_interface or not assigned_object: + logging.info('Assigning existing IP {ip} to {interface}'.format( ip=ip, interface=interface)) - elif (ip_interface and ip_interface.id != interface.id) or \ - (assigned_object and assigned_object.id != interface.id): - - old_interface = getattr(netbox_ip, "assigned_object", "n/a") - logging.info( - 'Detected interface change for ip {ip}: old interface is ' - '{old_interface} (id: {old_id}), new interface is {new_interface} ' - ' (id: {new_id})' - .format( - old_interface=old_interface, new_interface=interface, - old_id=netbox_ip.id, new_id=interface.id, ip=netbox_ip.address - )) - else: - return netbox_ip - - netbox_ip.assigned_object_type = self.assigned_object_type - netbox_ip.assigned_object_id = interface.id - netbox_ip.save() - + elif (ip_interface and ip_interface.id != interface.id) or \ + (assigned_object and assigned_object.id != interface.id): + + old_interface = getattr(netbox_ip, "assigned_object", "n/a") + logging.info( + 'Detected interface change for ip {ip}: old interface is ' + '{old_interface} (id: {old_id}), new interface is {new_interface} ' + ' (id: {new_id})' + .format( + old_interface=old_interface, new_interface=interface, + old_id=netbox_ip.id, new_id=interface.id, ip=netbox_ip.address + )) + else: + return netbox_ip + + netbox_ip.assigned_object_type = self.assigned_object_type + netbox_ip.assigned_object_id = interface.id + netbox_ip.save() + def create_or_update_netbox_network_cards(self): if config.update_all is None or config.update_network is None: return None @@ -507,7 +511,7 @@ def create_or_update_netbox_network_cards(self): class ServerNetwork(Network): def __init__(self, server, *args, **kwargs): - super(ServerNetwork, self).__init__(server, args, kwargs) + super(ServerNetwork, self).__init__(server, *args, **kwargs) if config.network.ipmi: self.ipmi = self.get_ipmi() From a07b56bddc3051d74f6b30dc947f4a038aa80d8f Mon Sep 17 00:00:00 2001 From: Robert Romero <1013414+NessieCanCode@users.noreply.github.com> Date: Fri, 22 Mar 2024 08:36:38 -0700 Subject: [PATCH 4/7] update --- netbox_agent/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_agent/server.py b/netbox_agent/server.py index c755a71..922cacc 100644 --- a/netbox_agent/server.py +++ b/netbox_agent/server.py @@ -517,4 +517,4 @@ def own_drive_expansion_slot(self): """ Indicates if the device hosts a drive expansion bay """ - return False + return False \ No newline at end of file From 0ae67f44e612dc19a3a907fac0ad4cac5f70dfbe Mon Sep 17 00:00:00 2001 From: Robert Romero <1013414+NessieCanCode@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:20:00 -0700 Subject: [PATCH 5/7] handle the case where nb_mgmt_ip is None gracefully and prevent the AttributeError from occurring. --- netbox_agent/network.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/netbox_agent/network.py b/netbox_agent/network.py index a9e2e8a..6f94c22 100644 --- a/netbox_agent/network.py +++ b/netbox_agent/network.py @@ -540,10 +540,8 @@ def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_int nb_mgmt_ip = nb.ipam.ip_addresses.get( address=switch_ip, ) - if not nb_mgmt_ip: - logging.error('Switch IP {} cannot be found in Netbox'.format(switch_ip)) - return nb_server_interface - + # Add the check here + if nb_mgmt_ip is not None: try: nb_switch = nb_mgmt_ip.assigned_object.device logging.info('Found a switch in Netbox based on LLDP infos: {} (id: {})'.format( @@ -557,6 +555,9 @@ def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_int ) ) return nb_server_interface + else: + logging.error('No NetBox IP found for switch IP {}'.format(switch_ip)) + return nb_server_interface switch_interface = self.lldp.get_switch_port(nb_server_interface.name) nb_switch_interface = nb.dcim.interfaces.get( From 78f85abd08ee660f983f41b68fd8211b5f4cc4ef Mon Sep 17 00:00:00 2001 From: Robert Romero <1013414+NessieCanCode@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:24:26 -0700 Subject: [PATCH 6/7] Fixed indentions --- netbox_agent/network.py | 69 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/netbox_agent/network.py b/netbox_agent/network.py index 6f94c22..06d6039 100644 --- a/netbox_agent/network.py +++ b/netbox_agent/network.py @@ -533,14 +533,15 @@ def get_ipmi(self): ipmi = IPMI().parse() return ipmi - def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_interface): - logging.info('Interface {} is not connected to switch, trying to connect..'.format( - nb_server_interface.name - )) - nb_mgmt_ip = nb.ipam.ip_addresses.get( - address=switch_ip, - ) - # Add the check here +def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_interface): + logging.info('Interface {} is not connected to switch, trying to connect..'.format( + nb_server_interface.name + )) + nb_mgmt_ip = nb.ipam.ip_addresses.get( + address=switch_ip, + ) + + # Add the check here if nb_mgmt_ip is not None: try: nb_switch = nb_mgmt_ip.assigned_object.device @@ -559,34 +560,34 @@ def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_int logging.error('No NetBox IP found for switch IP {}'.format(switch_ip)) return nb_server_interface - switch_interface = self.lldp.get_switch_port(nb_server_interface.name) - nb_switch_interface = nb.dcim.interfaces.get( - device=nb_switch, - name=switch_interface, - ) - if nb_switch_interface is None: - logging.error('Switch interface {} cannot be found'.format(switch_interface)) - return nb_server_interface + switch_interface = self.lldp.get_switch_port(nb_server_interface.name) + nb_switch_interface = nb.dcim.interfaces.get( + device=nb_switch, + name=switch_interface, + ) + if nb_switch_interface is None: + logging.error('Switch interface {} cannot be found'.format(switch_interface)) + return nb_server_interface - logging.info('Found interface {} on switch {}'.format( - switch_interface, - switch_ip, - )) - cable = nb.dcim.cables.create( - termination_a_id=nb_server_interface.id, - termination_a_type="dcim.interface", - termination_b_id=nb_switch_interface.id, - termination_b_type="dcim.interface", + logging.info('Found interface {} on switch {}'.format( + switch_interface, + switch_ip, + )) + cable = nb.dcim.cables.create( + termination_a_id=nb_server_interface.id, + termination_a_type="dcim.interface", + termination_b_id=nb_switch_interface.id, + termination_b_type="dcim.interface", + ) + nb_server_interface.cable = cable + logging.info( + 'Connected interface {interface} with {switch_interface} of {switch_ip}'.format( + interface=nb_server_interface.name, + switch_interface=switch_interface, + switch_ip=switch_ip, ) - nb_server_interface.cable = cable - logging.info( - 'Connected interface {interface} with {switch_interface} of {switch_ip}'.format( - interface=nb_server_interface.name, - switch_interface=switch_interface, - switch_ip=switch_ip, - ) - ) - return nb_server_interface + ) + return nb_server_interface def create_or_update_cable(self, switch_ip, switch_interface, nb_server_interface): update = False From 0db8a9be30589d2585166e3bd27fb45a91fa5067 Mon Sep 17 00:00:00 2001 From: Robert Romero <1013414+NessieCanCode@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:27:48 -0700 Subject: [PATCH 7/7] indention was still off --- netbox_agent/network.py | 100 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/netbox_agent/network.py b/netbox_agent/network.py index 06d6039..c87fd78 100644 --- a/netbox_agent/network.py +++ b/netbox_agent/network.py @@ -533,61 +533,61 @@ def get_ipmi(self): ipmi = IPMI().parse() return ipmi -def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_interface): - logging.info('Interface {} is not connected to switch, trying to connect..'.format( - nb_server_interface.name - )) - nb_mgmt_ip = nb.ipam.ip_addresses.get( - address=switch_ip, - ) - - # Add the check here - if nb_mgmt_ip is not None: - try: - nb_switch = nb_mgmt_ip.assigned_object.device - logging.info('Found a switch in Netbox based on LLDP infos: {} (id: {})'.format( - switch_ip, - nb_switch.id - )) - except KeyError: - logging.error( - 'Switch IP {} is found but not associated to a Netbox Switch Device'.format( - switch_ip + def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_interface): + logging.info('Interface {} is not connected to switch, trying to connect..'.format( + nb_server_interface.name + )) + nb_mgmt_ip = nb.ipam.ip_addresses.get( + address=switch_ip, + ) + + # Add the check here + if nb_mgmt_ip is not None: + try: + nb_switch = nb_mgmt_ip.assigned_object.device + logging.info('Found a switch in Netbox based on LLDP infos: {} (id: {})'.format( + switch_ip, + nb_switch.id + )) + except KeyError: + logging.error( + 'Switch IP {} is found but not associated to a Netbox Switch Device'.format( + switch_ip + ) ) - ) + return nb_server_interface + else: + logging.error('No NetBox IP found for switch IP {}'.format(switch_ip)) return nb_server_interface - else: - logging.error('No NetBox IP found for switch IP {}'.format(switch_ip)) - return nb_server_interface - switch_interface = self.lldp.get_switch_port(nb_server_interface.name) - nb_switch_interface = nb.dcim.interfaces.get( - device=nb_switch, - name=switch_interface, - ) - if nb_switch_interface is None: - logging.error('Switch interface {} cannot be found'.format(switch_interface)) - return nb_server_interface + switch_interface = self.lldp.get_switch_port(nb_server_interface.name) + nb_switch_interface = nb.dcim.interfaces.get( + device=nb_switch, + name=switch_interface, + ) + if nb_switch_interface is None: + logging.error('Switch interface {} cannot be found'.format(switch_interface)) + return nb_server_interface - logging.info('Found interface {} on switch {}'.format( - switch_interface, - switch_ip, - )) - cable = nb.dcim.cables.create( - termination_a_id=nb_server_interface.id, - termination_a_type="dcim.interface", - termination_b_id=nb_switch_interface.id, - termination_b_type="dcim.interface", - ) - nb_server_interface.cable = cable - logging.info( - 'Connected interface {interface} with {switch_interface} of {switch_ip}'.format( - interface=nb_server_interface.name, - switch_interface=switch_interface, - switch_ip=switch_ip, + logging.info('Found interface {} on switch {}'.format( + switch_interface, + switch_ip, + )) + cable = nb.dcim.cables.create( + termination_a_id=nb_server_interface.id, + termination_a_type="dcim.interface", + termination_b_id=nb_switch_interface.id, + termination_b_type="dcim.interface", ) - ) - return nb_server_interface + nb_server_interface.cable = cable + logging.info( + 'Connected interface {interface} with {switch_interface} of {switch_ip}'.format( + interface=nb_server_interface.name, + switch_interface=switch_interface, + switch_ip=switch_ip, + ) + ) + return nb_server_interface def create_or_update_cable(self, switch_ip, switch_interface, nb_server_interface): update = False