Add LLDP auto cabling
This commit is contained in:
parent
0ef0e1c698
commit
1b9bf46762
3 changed files with 154 additions and 4 deletions
|
@ -33,6 +33,8 @@ if config.get('rack_location'):
|
|||
|
||||
NETWORK_IGNORE_INTERFACES = None
|
||||
NETWORK_IGNORE_IPS = None
|
||||
NETWORK_LLDP = None
|
||||
if config.get('network'):
|
||||
NETWORK_IGNORE_INTERFACES = config['network'].get('ignore_interfaces')
|
||||
NETWORK_IGNORE_IPS = config['network'].get('ignore_ips')
|
||||
NETWORK_LLDP = config['network'].get('lldp') is True
|
||||
|
|
42
netbox_agent/lldp.py
Normal file
42
netbox_agent/lldp.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import subprocess
|
||||
|
||||
|
||||
class LLDP():
|
||||
def __init__(self):
|
||||
self.output = subprocess.getoutput('lldpctl -f keyvalue')
|
||||
self.data = self.parse()
|
||||
|
||||
def parse(self):
|
||||
output_dict = {}
|
||||
for entry in self.output.splitlines():
|
||||
path, value = entry.strip().split("=", 1)
|
||||
path = path.split(".")
|
||||
path_components, final = path[:-1], path[-1]
|
||||
|
||||
current_dict = output_dict
|
||||
for path_component in path_components:
|
||||
current_dict[path_component] = current_dict.get(path_component, {})
|
||||
current_dict = current_dict[path_component]
|
||||
current_dict[final] = value
|
||||
return output_dict
|
||||
|
||||
def get_switch_ip(self, interface):
|
||||
# lldp.eth0.chassis.mgmt-ip=100.66.7.222
|
||||
if self.data['lldp'].get(interface) is None:
|
||||
return None
|
||||
return self.data['lldp'][interface]['chassis']['mgmt-ip']
|
||||
|
||||
def get_switch_port(self, interface):
|
||||
# lldp.eth0.port.descr=GigabitEthernet1/0/1
|
||||
if self.data['lldp'].get(interface) is None:
|
||||
return None
|
||||
return self.data['lldp'][interface]['port']['descr']
|
||||
|
||||
def get_switch_vlan(self, interface):
|
||||
# lldp.eth0.vlan.vlan-id=296
|
||||
if self.data['lldp'].get(interface) is None:
|
||||
return None
|
||||
|
||||
if self.data['lldp'][interface].get('vlan'):
|
||||
return int(self.data['lldp'][interface]['vlan'].replace('vlan-', ''))
|
||||
return None
|
|
@ -7,9 +7,10 @@ from netaddr import IPAddress, IPNetwork
|
|||
import netifaces
|
||||
|
||||
from netbox_agent.config import netbox_instance as nb
|
||||
from netbox_agent.config import NETWORK_IGNORE_INTERFACES, NETWORK_IGNORE_IPS
|
||||
from netbox_agent.config import NETWORK_IGNORE_INTERFACES, NETWORK_IGNORE_IPS, NETWORK_LLDP
|
||||
from netbox_agent.ethtool import Ethtool
|
||||
from netbox_agent.ipmi import IPMI
|
||||
from netbox_agent.lldp import LLDP
|
||||
|
||||
IFACE_TYPE_100ME_FIXED = 800
|
||||
IFACE_TYPE_1GE_FIXED = 1000
|
||||
|
@ -44,6 +45,7 @@ class Network():
|
|||
|
||||
self.server = server
|
||||
self.device = self.server.get_netbox_server()
|
||||
self.lldp = LLDP()
|
||||
self.scan()
|
||||
|
||||
def scan(self):
|
||||
|
@ -297,6 +299,105 @@ class Network():
|
|||
self.create_or_update_ipmi()
|
||||
logging.debug('Finished creating NIC!')
|
||||
|
||||
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,
|
||||
)
|
||||
if not nb_mgmt_ip:
|
||||
logging.error('Switch IP {} cannot be found in Netbox'.format(switch_ip))
|
||||
return nb_server_interface
|
||||
|
||||
try:
|
||||
nb_switch = nb_mgmt_ip.interface.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
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
return nb_server_interface
|
||||
|
||||
def create_or_update_cable(self, switch_ip, switch_interface, nb_server_interface):
|
||||
if nb_server_interface.cable is None:
|
||||
update = True
|
||||
nb_server_interface = self.connect_interface_to_switch(
|
||||
switch_ip, switch_interface, nb_server_interface
|
||||
)
|
||||
else:
|
||||
update = False
|
||||
nb_sw_int = nb_server_interface.cable.termination_b
|
||||
nb_sw = nb_sw_int.device
|
||||
nb_mgmt_int = nb.dcim.interfaces.get(
|
||||
device_id=nb_sw.id,
|
||||
mgmt_only=True
|
||||
)
|
||||
nb_mgmt_ip = nb.ipam.ip_addresses.get(
|
||||
interface_id=nb_mgmt_int.id
|
||||
)
|
||||
if nb_mgmt_ip is None:
|
||||
pass
|
||||
|
||||
# Netbox IP is always IP/Netmask
|
||||
nb_mgmt_ip = nb_mgmt_ip.address.split('/')[0]
|
||||
if nb_mgmt_ip != switch_ip or \
|
||||
nb_sw_int.name != switch_interface:
|
||||
logging.info('Netbox cable is not connected to correct ports, fixing..')
|
||||
logging.info(
|
||||
'Deleting cable {cable_id} from {interface} to {switch_interface} of '
|
||||
'{switch_ip}'.format(
|
||||
cable_id=nb_server_interface.cable.id,
|
||||
interface=nb_server_interface.name,
|
||||
switch_interface=nb_sw_int.name,
|
||||
switch_ip=nb_mgmt_ip,
|
||||
)
|
||||
)
|
||||
cable = nb.dcim.cables.get(
|
||||
nb_server_interface.cable.id
|
||||
)
|
||||
print(cable)
|
||||
cable.delete()
|
||||
update = True
|
||||
nb_server_interface = self.connect_interface_to_switch(
|
||||
switch_ip, switch_interface, nb_server_interface
|
||||
)
|
||||
return update, nb_server_interface
|
||||
|
||||
def update_netbox_network_cards(self):
|
||||
logging.debug('Updating NIC...')
|
||||
|
||||
|
@ -359,12 +460,17 @@ class Network():
|
|||
nic_update = True
|
||||
interface.lag = None
|
||||
|
||||
# cable the interface
|
||||
if NETWORK_LLDP:
|
||||
switch_ip = self.lldp.get_switch_ip(interface.name)
|
||||
switch_interface = self.lldp.get_switch_port(interface.name)
|
||||
nic_update, interface = self.create_or_update_cable(
|
||||
switch_ip, switch_interface, interface
|
||||
)
|
||||
|
||||
if nic['ip']:
|
||||
# sync local IPs
|
||||
for ip in nic['ip']:
|
||||
netbox_ip = nb.ipam.ip_addresses.get(
|
||||
address=ip,
|
||||
)
|
||||
self.create_or_update_netbox_ip_on_interface(ip, interface)
|
||||
if nic_update:
|
||||
interface.save()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue