Add tags,device roles,tenants.. (#110)
* Adds support for: * sets the tenant for IP addresses and Devices. * setting device tags * setting the blade, chassis, and server roles. Co-authored-by: Thomas Davis <tdavis@nersc.gov> Co-authored-by: Solvik <solvik@solvik.fr>
This commit is contained in:
parent
bd5037996b
commit
a60c0cd70c
8 changed files with 165 additions and 35 deletions
17
README.md
17
README.md
|
@ -99,6 +99,23 @@ network:
|
|||
# enable auto-cabling by parsing LLDP answers
|
||||
lldp: true
|
||||
|
||||
#
|
||||
# You can use these to change the Netbox roles.
|
||||
# These are the defaults.
|
||||
#
|
||||
#device:
|
||||
# chassis_role: "Server Chassis"
|
||||
# blade_role: "Blade"
|
||||
# server_role: "Server"
|
||||
# tags: server, blade, ,just a comma,delimited,list
|
||||
#
|
||||
#
|
||||
# Can use this to set the tenant
|
||||
#
|
||||
#tenant:
|
||||
# driver: "file:/tmp/tenant"
|
||||
# regex: "(.*)"
|
||||

|
||||
## Enable virtual machine support
|
||||
# virtual:
|
||||
# # not mandatory, can be guessed
|
||||
|
|
|
@ -8,6 +8,22 @@ network:
|
|||
# enable auto-cabling
|
||||
lldp: true
|
||||
|
||||
#
|
||||
# You can use these to change the roles.
|
||||
#
|
||||
#device:
|
||||
# chassis_role: "Server Chassis"
|
||||
# blade_role: "Blade"
|
||||
# server_role: "Server"
|
||||
# tags: server, blade, ,just a comma,delimited,list
|
||||
|
||||
#
|
||||
# Use this to set the tenant
|
||||
#
|
||||
#tenant:
|
||||
# driver: "file:/tmp/tenant"
|
||||
# regex: "(.*)"
|
||||

|
||||
datacenter_location:
|
||||
driver: "cmd:cat /etc/qualification | tr [a-z] [A-Z]"
|
||||
regex: "DATACENTER: (?P<datacenter>[A-Za-z0-9]+)"
|
||||
|
|
|
@ -34,6 +34,20 @@ def get_config():
|
|||
p.add_argument('--virtual.cluster_name', help='Cluster name of VM')
|
||||
p.add_argument('--hostname_cmd', default=None,
|
||||
help="Command to output hostname, used as Device's name in netbox")
|
||||
p.add_argument('--device.tags', default=r'',
|
||||
help='tags to use for a host')
|
||||
p.add_argument('--device.blade_role', default=r'Blade',
|
||||
help='role to use for a blade server')
|
||||
p.add_argument('--device.chassis_role', default=r'Server Chassis',
|
||||
help='role to use for a chassis')
|
||||
p.add_argument('--device.server_role', default=r'Server',
|
||||
help='role to use for a server')
|
||||
p.add_argument('--tenant.driver',
|
||||
help='tenant driver, ie cmd, file')
|
||||
p.add_argument('--tenant.driver_file',
|
||||
help='tenant driver custom driver file path')
|
||||
p.add_argument('--tenant.regex',
|
||||
help='tenant regex to extract Netbox tenant slug')
|
||||
p.add_argument('--datacenter_location.driver',
|
||||
help='Datacenter location driver, ie: cmd, file')
|
||||
p.add_argument('--datacenter_location.driver_file',
|
||||
|
|
|
@ -50,6 +50,17 @@ class LocationBase():
|
|||
return getattr(self.driver, 'get')(self.driver_value, self.regex)
|
||||
|
||||
|
||||
class Tenant(LocationBase):
|
||||
def __init__(self):
|
||||
driver = config.tenant.driver.split(':')[0] if \
|
||||
config.tenant.driver else None
|
||||
driver_value = ':'.join(config.tenant.driver.split(':')[1:]) if \
|
||||
config.tenant.driver else None
|
||||
driver_file = config.tenant.driver_file
|
||||
regex = config.tenant.regex
|
||||
super().__init__(driver, driver_value, driver_file, regex)
|
||||
|
||||
|
||||
class Datacenter(LocationBase):
|
||||
def __init__(self):
|
||||
driver = config.datacenter_location.driver.split(':')[0] if \
|
||||
|
|
|
@ -2,12 +2,32 @@ import socket
|
|||
import subprocess
|
||||
from shutil import which
|
||||
|
||||
from netbox_agent.config import netbox_instance as nb
|
||||
|
||||
|
||||
def is_tool(name):
|
||||
'''Check whether `name` is on PATH and marked as executable.'''
|
||||
return which(name) is not None
|
||||
|
||||
|
||||
def get_device_role(role):
|
||||
device_role = nb.dcim.device_roles.get(
|
||||
name=role
|
||||
)
|
||||
if device_role is None:
|
||||
raise Exception('DeviceRole "{}" does not exist, please create it'.format(role))
|
||||
return device_role
|
||||
|
||||
|
||||
def get_device_type(type):
|
||||
device_type = nb.dcim.device_types.get(
|
||||
model=type
|
||||
)
|
||||
if device_type is None:
|
||||
raise Exception('DeviceType "{}" does not exist, please create it'.format(type))
|
||||
return device_type
|
||||
|
||||
|
||||
def get_vendor(name):
|
||||
vendors = {
|
||||
'PERC': 'Dell',
|
||||
|
@ -37,3 +57,15 @@ def get_hostname(config):
|
|||
if config.hostname_cmd is None:
|
||||
return '{}'.format(socket.gethostname())
|
||||
return subprocess.getoutput(config.hostname_cmd)
|
||||
|
||||
|
||||
def create_netbox_tags(tags):
|
||||
for tag in tags:
|
||||
nb_tag = nb.extras.tags.get(
|
||||
name=tag
|
||||
)
|
||||
if not nb_tag:
|
||||
nb_tag = nb.extras.tags.create(
|
||||
name=tag,
|
||||
slug=tag,
|
||||
)
|
||||
|
|
|
@ -17,6 +17,9 @@ class Network(object):
|
|||
def __init__(self, server, *args, **kwargs):
|
||||
self.nics = []
|
||||
|
||||
self.server = server
|
||||
self.tenant = self.server.get_netbox_tenant()
|
||||
|
||||
self.lldp = LLDP() if config.network.lldp else None
|
||||
self.nics = self.scan()
|
||||
self.ipmi = None
|
||||
|
@ -337,6 +340,7 @@ class Network(object):
|
|||
interface=interface.id,
|
||||
status=1,
|
||||
role=self.ipam_choices['ip-address:role']['Anycast'],
|
||||
tenant=self.tenant.id if self.tenant else None,
|
||||
)
|
||||
return netbox_ip
|
||||
else:
|
||||
|
|
|
@ -8,29 +8,12 @@ import netbox_agent.dmidecode as dmidecode
|
|||
from netbox_agent.config import config
|
||||
from netbox_agent.config import netbox_instance as nb
|
||||
from netbox_agent.inventory import Inventory
|
||||
from netbox_agent.location import Datacenter, Rack
|
||||
from netbox_agent.location import Datacenter, Rack, Tenant
|
||||
from netbox_agent.misc import create_netbox_tags, get_device_role, get_device_type
|
||||
from netbox_agent.network import ServerNetwork
|
||||
from netbox_agent.power import PowerSupply
|
||||
|
||||
|
||||
def get_device_role(role):
|
||||
device_role = nb.dcim.device_roles.get(
|
||||
name=role
|
||||
)
|
||||
if device_role is None:
|
||||
raise Exception('DeviceRole "{}" does not exist, please create it'.format(role))
|
||||
return device_role
|
||||
|
||||
|
||||
def get_device_type(type):
|
||||
device_type = nb.dcim.device_types.get(
|
||||
model=type
|
||||
)
|
||||
if device_type is None:
|
||||
raise Exception('DeviceType "{}" does not exist, please create it'.format(type))
|
||||
return device_type
|
||||
|
||||
|
||||
class ServerBase():
|
||||
def __init__(self, dmi=None):
|
||||
if dmi:
|
||||
|
@ -45,6 +28,20 @@ class ServerBase():
|
|||
|
||||
self.network = None
|
||||
|
||||
self.tags = list(set(config.device.tags.split(','))) if config.device.tags else []
|
||||
if self.tags and len(self.tags):
|
||||
create_netbox_tags(self.tags)
|
||||
|
||||
def get_tenant(self):
|
||||
tenant = Tenant()
|
||||
return tenant.get()
|
||||
|
||||
def get_netbox_tenant(self):
|
||||
tenant = nb.tenancy.tenants.get(
|
||||
slug=self.get_tenant()
|
||||
)
|
||||
return tenant
|
||||
|
||||
def get_datacenter(self):
|
||||
dc = Datacenter()
|
||||
return dc.get()
|
||||
|
@ -153,9 +150,9 @@ class ServerBase():
|
|||
def get_power_consumption(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _netbox_create_chassis(self, datacenter, rack):
|
||||
def _netbox_create_chassis(self, datacenter, tenant, rack):
|
||||
device_type = get_device_type(self.get_chassis())
|
||||
device_role = get_device_role('Server Chassis')
|
||||
device_role = get_device_role(config.device.chassis_role)
|
||||
serial = self.get_chassis_service_tag()
|
||||
logging.info('Creating chassis blade (serial: {serial})'.format(
|
||||
serial=serial))
|
||||
|
@ -165,12 +162,14 @@ class ServerBase():
|
|||
serial=serial,
|
||||
device_role=device_role.id,
|
||||
site=datacenter.id if datacenter else None,
|
||||
tenant=tenant.id if tenant else None,
|
||||
rack=rack.id if rack else None,
|
||||
tags=self.tags,
|
||||
)
|
||||
return new_chassis
|
||||
|
||||
def _netbox_create_blade(self, chassis, datacenter, rack):
|
||||
device_role = get_device_role('Blade')
|
||||
def _netbox_create_blade(self, chassis, datacenter, tenant, rack):
|
||||
device_role = get_device_role(config.device.blade_role)
|
||||
device_type = get_device_type(self.get_product_name())
|
||||
serial = self.get_service_tag()
|
||||
hostname = self.get_hostname()
|
||||
|
@ -185,12 +184,14 @@ class ServerBase():
|
|||
device_type=device_type.id,
|
||||
parent_device=chassis.id,
|
||||
site=datacenter.id if datacenter else None,
|
||||
tenant=tenant.id if tenant else None,
|
||||
rack=rack.id if rack else None,
|
||||
tags=self.tags,
|
||||
)
|
||||
return new_blade
|
||||
|
||||
def _netbox_create_server(self, datacenter, rack):
|
||||
device_role = get_device_role('Server')
|
||||
def _netbox_create_server(self, datacenter, tenant, rack):
|
||||
device_role = get_device_role(config.device.server_role)
|
||||
device_type = get_device_type(self.get_product_name())
|
||||
if not device_type:
|
||||
raise Exception('Chassis "{}" doesn\'t exist'.format(self.get_chassis()))
|
||||
|
@ -204,7 +205,9 @@ class ServerBase():
|
|||
device_role=device_role.id,
|
||||
device_type=device_type.id,
|
||||
site=datacenter.id if datacenter else None,
|
||||
tenant=tenant.id if tenant else None,
|
||||
rack=rack.id if rack else None,
|
||||
tags=self.tags,
|
||||
)
|
||||
return new_server
|
||||
|
||||
|
@ -258,6 +261,7 @@ class ServerBase():
|
|||
"""
|
||||
datacenter = self.get_netbox_datacenter()
|
||||
rack = self.get_netbox_rack()
|
||||
tenant = self.get_netbox_tenant()
|
||||
|
||||
if self.is_blade():
|
||||
chassis = nb.dcim.devices.get(
|
||||
|
@ -265,18 +269,18 @@ class ServerBase():
|
|||
)
|
||||
# Chassis does not exist
|
||||
if not chassis:
|
||||
chassis = self._netbox_create_chassis(datacenter, rack)
|
||||
chassis = self._netbox_create_chassis(datacenter, tenant, rack)
|
||||
|
||||
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
||||
if not server:
|
||||
server = self._netbox_create_blade(chassis, datacenter, rack)
|
||||
server = self._netbox_create_blade(chassis, datacenter, tenant, rack)
|
||||
|
||||
# Set slot for blade
|
||||
self._netbox_set_or_update_blade_slot(server, chassis, datacenter)
|
||||
else:
|
||||
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
||||
if not server:
|
||||
self._netbox_create_server(datacenter, rack)
|
||||
self._netbox_create_server(datacenter, tenant, rack)
|
||||
|
||||
logging.debug('Updating Server...')
|
||||
# check network cards
|
||||
|
@ -300,6 +304,10 @@ class ServerBase():
|
|||
update += 1
|
||||
server.name = self.get_hostname()
|
||||
|
||||
if sorted(set(server.tags)) != sorted(set(self.tags)):
|
||||
server.tags = self.tags
|
||||
update += 1
|
||||
|
||||
if config.update_all or config.update_location:
|
||||
ret, server = self.update_netbox_location(server)
|
||||
update += ret
|
||||
|
|
|
@ -3,8 +3,9 @@ import os
|
|||
import netbox_agent.dmidecode as dmidecode
|
||||
from netbox_agent.config import config
|
||||
from netbox_agent.config import netbox_instance as nb
|
||||
from netbox_agent.location import Tenant
|
||||
from netbox_agent.logging import logging # NOQA
|
||||
from netbox_agent.misc import get_hostname
|
||||
from netbox_agent.misc import create_netbox_tags, get_hostname
|
||||
from netbox_agent.network import VirtualNetwork
|
||||
|
||||
|
||||
|
@ -29,6 +30,10 @@ class VirtualMachine(object):
|
|||
self.dmi = dmidecode.parse()
|
||||
self.network = None
|
||||
|
||||
self.tags = list(set(config.device.tags.split(','))) if config.device.tags else []
|
||||
if self.tags and len(self.tags):
|
||||
create_netbox_tags(self.tags)
|
||||
|
||||
def get_memory(self):
|
||||
mem_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') # e.g. 4015976448
|
||||
mem_gib = mem_bytes / (1024.**2) # e.g. 3.74
|
||||
|
@ -50,6 +55,22 @@ class VirtualMachine(object):
|
|||
)
|
||||
return cluster
|
||||
|
||||
def get_netbox_datacenter(self, name):
|
||||
cluster = self.get_netbox_cluster()
|
||||
if cluster.datacenter:
|
||||
return cluster.datacenter
|
||||
return None
|
||||
|
||||
def get_tenant(self):
|
||||
tenant = Tenant()
|
||||
return tenant.get()
|
||||
|
||||
def get_netbox_tenant(self):
|
||||
tenant = nb.tenancy.tenants.get(
|
||||
slug=self.get_tenant()
|
||||
)
|
||||
return tenant
|
||||
|
||||
def netbox_create_or_update(self, config):
|
||||
logging.debug('It\'s a virtual machine')
|
||||
created = False
|
||||
|
@ -60,6 +81,7 @@ class VirtualMachine(object):
|
|||
|
||||
vcpus = self.get_vcpus()
|
||||
memory = self.get_memory()
|
||||
tenant = self.get_netbox_tenant()
|
||||
if not vm:
|
||||
logging.debug('Creating Virtual machine..')
|
||||
cluster = self.get_netbox_cluster(config.virtual.cluster_name)
|
||||
|
@ -69,18 +91,24 @@ class VirtualMachine(object):
|
|||
cluster=cluster.id,
|
||||
vcpus=vcpus,
|
||||
memory=memory,
|
||||
tenant=tenant.id if tenant else None,
|
||||
tags=self.tags,
|
||||
)
|
||||
created = True
|
||||
|
||||
self.network = VirtualNetwork(server=self)
|
||||
self.network.create_or_update_netbox_network_cards()
|
||||
|
||||
if not created and vm.vcpus != vcpus:
|
||||
vm.vcpus = vcpus
|
||||
updated += 1
|
||||
elif not created and vm.memory != memory:
|
||||
vm.memory = memory
|
||||
updated += 1
|
||||
if not created:
|
||||
if vm.vcpus != vcpus:
|
||||
vm.vcpus = vcpus
|
||||
updated += 1
|
||||
if vm.memory != memory:
|
||||
vm.memory = memory
|
||||
updated += 1
|
||||
if sorted(set(vm.tags)) != sorted(set(self.tags)):
|
||||
vm.tags = self.tags
|
||||
updated += 1
|
||||
|
||||
if updated:
|
||||
vm.save()
|
||||
|
|
Loading…
Reference in a new issue