Virtual Machine feature (#122)
Add ability to create Virtual Machine in netbox with an auto-detecting feature (currently VirtualBox, Xen, Hyper-V, VMWare, GCP) One caveat, I made the choice not to report the disk as Netbox model allow only one size but VM often have multiples. Also, lot of code refactoring to be able to use create_or_update function style, fixing a lot of corner case
This commit is contained in:
parent
996c10d95b
commit
f06da32fc3
10 changed files with 367 additions and 315 deletions
16
README.md
16
README.md
|
|
@ -6,7 +6,7 @@ The goal is to generate an existing infrastructure on Netbox and have the abilit
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
* Create servers, chassis and blade through standard tools (`dmidecode`)
|
* Create virtual machines, servers, chassis and blade through standard tools (`dmidecode`)
|
||||||
* Create physical, bonding and vlan network interfaces with IPs (IPv4 & IPv6)
|
* Create physical, bonding and vlan network interfaces with IPs (IPv4 & IPv6)
|
||||||
* Create IPMI interface if found
|
* Create IPMI interface if found
|
||||||
* Create or get existing VLAN and associate it to interfaces
|
* Create or get existing VLAN and associate it to interfaces
|
||||||
|
|
@ -52,6 +52,12 @@ network:
|
||||||
# enable auto-cabling
|
# enable auto-cabling
|
||||||
lldp: true
|
lldp: true
|
||||||
|
|
||||||
|
# virtual:
|
||||||
|
# # not mandatory, can be guessed
|
||||||
|
# enabled: True
|
||||||
|
# # see https://netbox.company.com/virtualization/clusters/
|
||||||
|
# cluster_name: my_vm_cluster
|
||||||
|
|
||||||
datacenter_location:
|
datacenter_location:
|
||||||
driver: "cmd:cat /etc/qualification | tr [a-z] [A-Z]"
|
driver: "cmd:cat /etc/qualification | tr [a-z] [A-Z]"
|
||||||
regex: "DATACENTER: (?P<datacenter>[A-Za-z0-9]+)"
|
regex: "DATACENTER: (?P<datacenter>[A-Za-z0-9]+)"
|
||||||
|
|
@ -94,6 +100,14 @@ In order to handle this case, user need to set Netbox IP's mode to `Anycast` so
|
||||||
|
|
||||||
Tested on:
|
Tested on:
|
||||||
|
|
||||||
|
## Virtual Machines
|
||||||
|
|
||||||
|
* Hyper-V
|
||||||
|
* VMWare
|
||||||
|
* VirtualBox
|
||||||
|
* AWS
|
||||||
|
* GCP
|
||||||
|
|
||||||
## Dell Inc.
|
## Dell Inc.
|
||||||
|
|
||||||
### Blades
|
### Blades
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from netbox_agent.vendors.generic import GenericHost
|
||||||
from netbox_agent.vendors.hp import HPHost
|
from netbox_agent.vendors.hp import HPHost
|
||||||
from netbox_agent.vendors.qct import QCTHost
|
from netbox_agent.vendors.qct import QCTHost
|
||||||
from netbox_agent.vendors.supermicro import SupermicroHost
|
from netbox_agent.vendors.supermicro import SupermicroHost
|
||||||
|
from netbox_agent.virtualmachine import VirtualMachine, is_vm
|
||||||
|
|
||||||
MANUFACTURERS = {
|
MANUFACTURERS = {
|
||||||
'Dell Inc.': DellHost,
|
'Dell Inc.': DellHost,
|
||||||
|
|
@ -19,8 +20,13 @@ MANUFACTURERS = {
|
||||||
|
|
||||||
def run(config):
|
def run(config):
|
||||||
dmi = dmidecode.parse()
|
dmi = dmidecode.parse()
|
||||||
manufacturer = dmidecode.get_by_type(dmi, 'Chassis')[0].get('Manufacturer')
|
|
||||||
|
|
||||||
|
if config.virtual.enabled or is_vm(dmi):
|
||||||
|
if not config.virtual.cluster_name:
|
||||||
|
raise Exception('virtual.cluster_name parameter is mandatory because it\'s a VM')
|
||||||
|
server = VirtualMachine(dmi=dmi)
|
||||||
|
else:
|
||||||
|
manufacturer = dmidecode.get_by_type(dmi, 'Chassis')[0].get('Manufacturer')
|
||||||
try:
|
try:
|
||||||
server = MANUFACTURERS[manufacturer](dmi=dmi)
|
server = MANUFACTURERS[manufacturer](dmi=dmi)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -28,11 +34,9 @@ def run(config):
|
||||||
|
|
||||||
if config.debug:
|
if config.debug:
|
||||||
server.print_debug()
|
server.print_debug()
|
||||||
if config.register:
|
if config.register or config.update_all or config.update_network or config.update_location or \
|
||||||
server.netbox_create(config)
|
|
||||||
if config.update_all or config.update_network or config.update_location or \
|
|
||||||
config.update_inventory or config.update_psu:
|
config.update_inventory or config.update_psu:
|
||||||
server.netbox_update(config)
|
server.netbox_create_or_update(config)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ def get_config():
|
||||||
p.add_argument('--log_level', default='debug')
|
p.add_argument('--log_level', default='debug')
|
||||||
p.add_argument('--netbox.url', help='Netbox URL')
|
p.add_argument('--netbox.url', help='Netbox URL')
|
||||||
p.add_argument('--netbox.token', help='Netbox API Token')
|
p.add_argument('--netbox.token', help='Netbox API Token')
|
||||||
|
p.add_argument('--virtual.enabled', action='store_true', help='Is a virtual machine or not')
|
||||||
|
p.add_argument('--virtual.cluster_name', help='Cluster name of VM')
|
||||||
p.add_argument('--hostname_cmd', default=None,
|
p.add_argument('--hostname_cmd', default=None,
|
||||||
help="Command to output hostname, used as Device's name in netbox")
|
help="Command to output hostname, used as Device's name in netbox")
|
||||||
p.add_argument('--datacenter_location.driver',
|
p.add_argument('--datacenter_location.driver',
|
||||||
|
|
|
||||||
|
|
@ -438,18 +438,7 @@ class Inventory():
|
||||||
if memory.get('serial') not in [x.serial for x in nb_memories]:
|
if memory.get('serial') not in [x.serial for x in nb_memories]:
|
||||||
self.create_netbox_memory(memory)
|
self.create_netbox_memory(memory)
|
||||||
|
|
||||||
def create(self):
|
def create_or_update(self):
|
||||||
if config.inventory is None:
|
|
||||||
return False
|
|
||||||
self.do_netbox_cpus()
|
|
||||||
self.do_netbox_memories()
|
|
||||||
self.do_netbox_raid_cards()
|
|
||||||
self.do_netbox_disks()
|
|
||||||
self.do_netbox_interfaces()
|
|
||||||
self.do_netbox_motherboard()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if config.inventory is None or config.update_inventory is None:
|
if config.inventory is None or config.update_inventory is None:
|
||||||
return False
|
return False
|
||||||
self.do_netbox_cpus()
|
self.do_netbox_cpus()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
|
|
||||||
class IPMI():
|
class IPMI():
|
||||||
"""
|
"""
|
||||||
|
|
@ -40,11 +42,27 @@ class IPMI():
|
||||||
logging.error('Cannot get ipmi info: {}'.format(self.output))
|
logging.error('Cannot get ipmi info: {}'.format(self.output))
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
ret = {}
|
_ipmi = {}
|
||||||
if self.ret != 0:
|
if self.ret != 0:
|
||||||
return ret
|
return _ipmi
|
||||||
|
|
||||||
for line in self.output.splitlines():
|
for line in self.output.splitlines():
|
||||||
key = line.split(':')[0].strip()
|
key = line.split(':')[0].strip()
|
||||||
|
if key not in ['802.1q VLAN ID', 'IP Address', 'Subnet Mask', 'MAC Address']:
|
||||||
|
continue
|
||||||
value = ':'.join(line.split(':')[1:]).strip()
|
value = ':'.join(line.split(':')[1:]).strip()
|
||||||
ret[key] = value
|
_ipmi[key] = value
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
ret['name'] = 'IPMI'
|
||||||
|
ret['bonding'] = False
|
||||||
|
ret['mac'] = _ipmi['MAC Address']
|
||||||
|
ret['vlan'] = int(_ipmi['802.1q VLAN ID']) \
|
||||||
|
if _ipmi['802.1q VLAN ID'] != 'Disabled' else None
|
||||||
|
ip = _ipmi['IP Address']
|
||||||
|
netmask = _ipmi['Subnet Mask']
|
||||||
|
address = str(IPNetwork('{}/{}'.format(ip, netmask)))
|
||||||
|
|
||||||
|
ret['ip'] = [address]
|
||||||
|
ret['ipmi'] = True
|
||||||
return ret
|
return ret
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ import logging
|
||||||
from netbox_agent.config import config
|
from netbox_agent.config import config
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
if config.log_level.lower() == 'debug':
|
||||||
if config.log_level == 'debug':
|
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
from shutil import which
|
from shutil import which
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,3 +31,9 @@ def get_vendor(name):
|
||||||
if name.upper().startswith(key):
|
if name.upper().startswith(key):
|
||||||
return value
|
return value
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def get_hostname(config):
|
||||||
|
if config.hostname_cmd is None:
|
||||||
|
return '{}'.format(socket.gethostname())
|
||||||
|
return subprocess.getoutput(config.hostname_cmd)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import re
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
import netifaces
|
import netifaces
|
||||||
from netaddr import IPAddress, IPNetwork
|
from netaddr import IPAddress
|
||||||
|
|
||||||
from netbox_agent.config import config
|
from netbox_agent.config import config
|
||||||
from netbox_agent.config import netbox_instance as nb
|
from netbox_agent.config import netbox_instance as nb
|
||||||
|
|
@ -13,15 +13,13 @@ from netbox_agent.ipmi import IPMI
|
||||||
from netbox_agent.lldp import LLDP
|
from netbox_agent.lldp import LLDP
|
||||||
|
|
||||||
|
|
||||||
class Network():
|
class Network(object):
|
||||||
def __init__(self, server, *args, **kwargs):
|
def __init__(self, server, *args, **kwargs):
|
||||||
self.nics = []
|
self.nics = []
|
||||||
|
|
||||||
self.server = server
|
|
||||||
self.device = self.server.get_netbox_server()
|
|
||||||
self.lldp = LLDP() if config.network.lldp else None
|
self.lldp = LLDP() if config.network.lldp else None
|
||||||
self.scan()
|
self.nics = self.scan()
|
||||||
|
self.ipmi = None
|
||||||
self.dcim_choices = {}
|
self.dcim_choices = {}
|
||||||
dcim_c = nb.dcim.choices()
|
dcim_c = nb.dcim.choices()
|
||||||
|
|
||||||
|
|
@ -38,7 +36,11 @@ class Network():
|
||||||
for c in ipam_c[choice]:
|
for c in ipam_c[choice]:
|
||||||
self.ipam_choices[choice][c['label']] = c['value']
|
self.ipam_choices[choice][c['label']] = c['value']
|
||||||
|
|
||||||
|
def get_network_type():
|
||||||
|
return NotImplementedError
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
|
nics = []
|
||||||
for interface in os.listdir('/sys/class/net/'):
|
for interface in os.listdir('/sys/class/net/'):
|
||||||
# ignore if it's not a link (ie: bonding_masters etc)
|
# ignore if it's not a link (ie: bonding_masters etc)
|
||||||
if not os.path.islink('/sys/class/net/{}'.format(interface)):
|
if not os.path.islink('/sys/class/net/{}'.format(interface)):
|
||||||
|
|
@ -102,7 +104,8 @@ class Network():
|
||||||
'bonding': bonding,
|
'bonding': bonding,
|
||||||
'bonding_slaves': bonding_slaves,
|
'bonding_slaves': bonding_slaves,
|
||||||
}
|
}
|
||||||
self.nics.append(nic)
|
nics.append(nic)
|
||||||
|
return nics
|
||||||
|
|
||||||
def _set_bonding_interfaces(self):
|
def _set_bonding_interfaces(self):
|
||||||
bonding_nics = (x for x in self.nics if x['bonding'])
|
bonding_nics = (x for x in self.nics if x['bonding'])
|
||||||
|
|
@ -130,25 +133,30 @@ class Network():
|
||||||
|
|
||||||
def get_netbox_network_card(self, nic):
|
def get_netbox_network_card(self, nic):
|
||||||
if nic['mac'] is None:
|
if nic['mac'] is None:
|
||||||
interface = nb.dcim.interfaces.get(
|
interface = self.nb_net.interfaces.get(
|
||||||
device_id=self.device.id,
|
|
||||||
name=nic['name'],
|
name=nic['name'],
|
||||||
|
**self.custom_arg_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
interface = nb.dcim.interfaces.get(
|
interface = self.nb_net.interfaces.get(
|
||||||
device_id=self.device.id,
|
|
||||||
mac_address=nic['mac'],
|
mac_address=nic['mac'],
|
||||||
name=nic['name'],
|
name=nic['name'],
|
||||||
|
**self.custom_arg_id,
|
||||||
)
|
)
|
||||||
return interface
|
return interface
|
||||||
|
|
||||||
def get_netbox_network_cards(self):
|
def get_netbox_network_cards(self):
|
||||||
return nb.dcim.interfaces.filter(
|
return self.nb_net.interfaces.filter(
|
||||||
device_id=self.device.id,
|
**self.custom_arg_id,
|
||||||
mgmt_only=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_netbox_type_for_nic(self, nic):
|
def get_netbox_type_for_nic(self, nic):
|
||||||
|
if self.get_network_type() == 'virtual':
|
||||||
|
return self.dcim_choices['interface:type']['Virtual']
|
||||||
|
|
||||||
|
if nic.get('bonding'):
|
||||||
|
return self.dcim_choices['interface:type']['Link Aggregation Group (LAG)']
|
||||||
|
|
||||||
if nic.get('bonding'):
|
if nic.get('bonding'):
|
||||||
return self.dcim_choices['interface:type']['Link Aggregation Group (LAG)']
|
return self.dcim_choices['interface:type']['Link Aggregation Group (LAG)']
|
||||||
if nic.get('ethtool') is None:
|
if nic.get('ethtool') is None:
|
||||||
|
|
@ -165,17 +173,6 @@ class Network():
|
||||||
return self.dcim_choices['interface:type']['1000BASE-T (1GE)']
|
return self.dcim_choices['interface:type']['1000BASE-T (1GE)']
|
||||||
return self.dcim_choices['interface:type']['Other']
|
return self.dcim_choices['interface:type']['Other']
|
||||||
|
|
||||||
def get_ipmi(self):
|
|
||||||
ipmi = IPMI().parse()
|
|
||||||
return ipmi
|
|
||||||
|
|
||||||
def get_netbox_ipmi(self):
|
|
||||||
ipmi = self.get_ipmi()
|
|
||||||
mac = ipmi['MAC Address']
|
|
||||||
return nb.dcim.interfaces.get(
|
|
||||||
mac=mac
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_or_create_vlan(self, vlan_id):
|
def get_or_create_vlan(self, vlan_id):
|
||||||
# FIXME: we may need to specify the datacenter
|
# FIXME: we may need to specify the datacenter
|
||||||
# since users may have same vlan id in multiple dc
|
# since users may have same vlan id in multiple dc
|
||||||
|
|
@ -206,9 +203,10 @@ class Network():
|
||||||
# if it's a vlan interface
|
# if it's a vlan interface
|
||||||
elif vlan_id and (
|
elif vlan_id and (
|
||||||
interface.mode is None or
|
interface.mode is None or
|
||||||
interface.mode.value != self.dcim_choices['interface:mode']['Access'] or
|
type(interface.mode) is not int and (
|
||||||
|
interface.mode.value == self.dcim_choices['interface:mode']['Access'] or
|
||||||
len(interface.tagged_vlans) != 1 or
|
len(interface.tagged_vlans) != 1 or
|
||||||
interface.tagged_vlans[0].vid != vlan_id):
|
interface.tagged_vlans[0].vid != vlan_id)):
|
||||||
logging.info('Resetting tagged VLAN(s) on interface {interface}'.format(
|
logging.info('Resetting tagged VLAN(s) on interface {interface}'.format(
|
||||||
interface=interface))
|
interface=interface))
|
||||||
update = True
|
update = True
|
||||||
|
|
@ -232,42 +230,6 @@ class Network():
|
||||||
interface.untagged_vlan = nb_vlan.id
|
interface.untagged_vlan = nb_vlan.id
|
||||||
return update, interface
|
return update, interface
|
||||||
|
|
||||||
def create_or_update_ipmi(self):
|
|
||||||
ipmi = self.get_ipmi()
|
|
||||||
mac = ipmi['MAC Address']
|
|
||||||
ip = ipmi['IP Address']
|
|
||||||
netmask = ipmi['Subnet Mask']
|
|
||||||
vlan = int(ipmi['802.1q VLAN ID']) if ipmi['802.1q VLAN ID'] != 'Disabled' else None
|
|
||||||
address = str(IPNetwork('{}/{}'.format(ip, netmask)))
|
|
||||||
|
|
||||||
interface = nb.dcim.interfaces.get(
|
|
||||||
device_id=self.device.id,
|
|
||||||
mgmt_only=True,
|
|
||||||
)
|
|
||||||
nic = {
|
|
||||||
'name': 'IPMI',
|
|
||||||
'mac': mac,
|
|
||||||
'vlan': vlan,
|
|
||||||
'ip': [address],
|
|
||||||
}
|
|
||||||
if interface is None:
|
|
||||||
interface = self.create_netbox_nic(nic, mgmt=True)
|
|
||||||
self.create_or_update_netbox_ip_on_interface(address, interface)
|
|
||||||
else:
|
|
||||||
# let the user chose the name of mgmt ?
|
|
||||||
# guess it with manufacturer (IDRAC, ILO, ...) ?
|
|
||||||
update = False
|
|
||||||
self.create_or_update_netbox_ip_on_interface(address, interface)
|
|
||||||
update, interface = self.reset_vlan_on_interface(nic, interface)
|
|
||||||
if mac.upper() != interface.mac_address:
|
|
||||||
logging.info('IPMI mac changed from {old_mac} to {new_mac}'.format(
|
|
||||||
old_mac=interface.mac_address, new_mac=mac.upper()))
|
|
||||||
interface.mac_address = mac
|
|
||||||
update = True
|
|
||||||
if update:
|
|
||||||
interface.save()
|
|
||||||
return interface
|
|
||||||
|
|
||||||
def create_netbox_nic(self, nic, mgmt=False):
|
def create_netbox_nic(self, nic, mgmt=False):
|
||||||
# TODO: add Optic Vendor, PN and Serial
|
# TODO: add Optic Vendor, PN and Serial
|
||||||
type = self.get_netbox_type_for_nic(nic)
|
type = self.get_netbox_type_for_nic(nic)
|
||||||
|
|
@ -275,12 +237,12 @@ class Network():
|
||||||
name=nic['name'], mac=nic['mac'], device=self.device.name))
|
name=nic['name'], mac=nic['mac'], device=self.device.name))
|
||||||
|
|
||||||
nb_vlan = None
|
nb_vlan = None
|
||||||
interface = nb.dcim.interfaces.create(
|
interface = self.nb_net.interfaces.create(
|
||||||
device=self.device.id,
|
|
||||||
name=nic['name'],
|
name=nic['name'],
|
||||||
mac_address=nic['mac'],
|
mac_address=nic['mac'],
|
||||||
type=type,
|
type=type,
|
||||||
mgmt_only=mgmt,
|
mgmt_only=mgmt,
|
||||||
|
**self.custom_arg,
|
||||||
)
|
)
|
||||||
|
|
||||||
if nic['vlan']:
|
if nic['vlan']:
|
||||||
|
|
@ -380,6 +342,112 @@ class Network():
|
||||||
netbox_ip.save()
|
netbox_ip.save()
|
||||||
return netbox_ip
|
return netbox_ip
|
||||||
|
|
||||||
|
def create_or_update_netbox_network_cards(self):
|
||||||
|
if config.update_all is None or config.update_network is None:
|
||||||
|
return None
|
||||||
|
logging.debug('Creating/Updating NIC...')
|
||||||
|
|
||||||
|
# delete unknown interface
|
||||||
|
nb_nics = self.get_netbox_network_cards()
|
||||||
|
local_nics = [x['name'] for x in self.nics]
|
||||||
|
for nic in nb_nics[:]:
|
||||||
|
if nic.name not in local_nics:
|
||||||
|
logging.info('Deleting netbox interface {name} because not present locally'.format(
|
||||||
|
name=nic.name
|
||||||
|
))
|
||||||
|
nb_nics.remove(nic)
|
||||||
|
nic.delete()
|
||||||
|
|
||||||
|
# delete IP on netbox that are not known on this server
|
||||||
|
if len(nb_nics):
|
||||||
|
netbox_ips = nb.ipam.ip_addresses.filter(
|
||||||
|
interface_id=[x.id for x in nb_nics],
|
||||||
|
)
|
||||||
|
all_local_ips = list(chain.from_iterable([
|
||||||
|
x['ip'] for x in self.nics if x['ip'] is not None
|
||||||
|
]))
|
||||||
|
for netbox_ip in netbox_ips:
|
||||||
|
if netbox_ip.address not in all_local_ips:
|
||||||
|
logging.info('Unassigning IP {ip} from {interface}'.format(
|
||||||
|
ip=netbox_ip.address, interface=netbox_ip.interface))
|
||||||
|
netbox_ip.interface = None
|
||||||
|
netbox_ip.save()
|
||||||
|
|
||||||
|
# update each nic
|
||||||
|
for nic in self.nics:
|
||||||
|
interface = self.get_netbox_network_card(nic)
|
||||||
|
if not interface:
|
||||||
|
logging.info('Interface {mac_address} not found, creating..'.format(
|
||||||
|
mac_address=nic['mac'])
|
||||||
|
)
|
||||||
|
interface = self.create_netbox_nic(nic)
|
||||||
|
|
||||||
|
nic_update = 0
|
||||||
|
if nic['name'] != interface.name:
|
||||||
|
logging.info('Updating interface {interface} name to: {name}'.format(
|
||||||
|
interface=interface, name=nic['name']))
|
||||||
|
interface.name = nic['name']
|
||||||
|
nic_update += 1
|
||||||
|
|
||||||
|
ret, interface = self.reset_vlan_on_interface(nic, interface)
|
||||||
|
nic_update += ret
|
||||||
|
|
||||||
|
_type = self.get_netbox_type_for_nic(nic)
|
||||||
|
if not interface.type or \
|
||||||
|
_type != interface.type.value:
|
||||||
|
logging.info('Interface type is wrong, resetting')
|
||||||
|
interface.type = _type
|
||||||
|
nic_update += 1
|
||||||
|
|
||||||
|
if hasattr(interface, 'lag') and interface.lag is not None:
|
||||||
|
local_lag_int = next(
|
||||||
|
item for item in self.nics if item['name'] == interface.lag.name
|
||||||
|
)
|
||||||
|
if nic['name'] not in local_lag_int['bonding_slaves']:
|
||||||
|
logging.info('Interface has no LAG, resetting')
|
||||||
|
nic_update += 1
|
||||||
|
interface.lag = None
|
||||||
|
|
||||||
|
# cable the interface
|
||||||
|
if config.network.lldp:
|
||||||
|
switch_ip = self.lldp.get_switch_ip(interface.name)
|
||||||
|
switch_interface = self.lldp.get_switch_port(interface.name)
|
||||||
|
if switch_ip and switch_interface:
|
||||||
|
ret, interface = self.create_or_update_cable(
|
||||||
|
switch_ip, switch_interface, interface
|
||||||
|
)
|
||||||
|
nic_update += ret
|
||||||
|
|
||||||
|
if nic['ip']:
|
||||||
|
# sync local IPs
|
||||||
|
for ip in nic['ip']:
|
||||||
|
self.create_or_update_netbox_ip_on_interface(ip, interface)
|
||||||
|
if nic_update > 0:
|
||||||
|
interface.save()
|
||||||
|
|
||||||
|
self._set_bonding_interfaces()
|
||||||
|
logging.debug('Finished updating NIC!')
|
||||||
|
|
||||||
|
|
||||||
|
class ServerNetwork(Network):
|
||||||
|
def __init__(self, server, *args, **kwargs):
|
||||||
|
super(ServerNetwork, self).__init__(server, args, kwargs)
|
||||||
|
self.ipmi = self.get_ipmi()
|
||||||
|
if self.ipmi:
|
||||||
|
self.nics.append(self.ipmi)
|
||||||
|
self.server = server
|
||||||
|
self.device = self.server.get_netbox_server()
|
||||||
|
self.nb_net = nb.dcim
|
||||||
|
self.custom_arg = {'device': self.device.id}
|
||||||
|
self.custom_arg_id = {'device_id': self.device.id}
|
||||||
|
|
||||||
|
def get_network_type(self):
|
||||||
|
return 'server'
|
||||||
|
|
||||||
|
def get_ipmi(self):
|
||||||
|
ipmi = IPMI().parse()
|
||||||
|
return ipmi
|
||||||
|
|
||||||
def connect_interface_to_switch(self, switch_ip, switch_interface, nb_server_interface):
|
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(
|
logging.info('Interface {} is not connected to switch, trying to connect..'.format(
|
||||||
nb_server_interface.name
|
nb_server_interface.name
|
||||||
|
|
@ -483,105 +551,22 @@ class Network():
|
||||||
)
|
)
|
||||||
return update, nb_server_interface
|
return update, nb_server_interface
|
||||||
|
|
||||||
def create_netbox_network_cards(self):
|
|
||||||
logging.debug('Creating NIC...')
|
|
||||||
for nic in self.nics:
|
|
||||||
interface = self.get_netbox_network_card(nic)
|
|
||||||
# if network doesn't exist we create it
|
|
||||||
if not interface:
|
|
||||||
new_interface = self.create_netbox_nic(nic)
|
|
||||||
if nic['ip']:
|
|
||||||
# for each ip, we try to find it
|
|
||||||
# assign the device's interface to it
|
|
||||||
# or simply create it
|
|
||||||
for ip in nic['ip']:
|
|
||||||
self.create_or_update_netbox_ip_on_interface(ip, new_interface)
|
|
||||||
self._set_bonding_interfaces()
|
|
||||||
self.create_or_update_ipmi()
|
|
||||||
logging.debug('Finished creating NIC!')
|
|
||||||
|
|
||||||
def update_netbox_network_cards(self):
|
class VirtualNetwork(Network):
|
||||||
if config.update_all is None or config.update_network is None:
|
def __init__(self, server, *args, **kwargs):
|
||||||
return None
|
super(VirtualNetwork, self).__init__(server, args, kwargs)
|
||||||
logging.debug('Updating NIC...')
|
self.server = server
|
||||||
|
self.device = self.server.get_netbox_vm()
|
||||||
|
self.nb_net = nb.virtualization
|
||||||
|
self.custom_arg = {'virtual_machine': self.device.id}
|
||||||
|
self.custom_arg_id = {'virtual_machine_id': self.device.id}
|
||||||
|
|
||||||
# delete unknown interface
|
dcim_c = nb.virtualization.choices()
|
||||||
nb_nics = self.get_netbox_network_cards()
|
|
||||||
local_nics = [x['name'] for x in self.nics]
|
|
||||||
for nic in nb_nics:
|
|
||||||
if nic.name not in local_nics:
|
|
||||||
logging.info('Deleting netbox interface {name} because not present locally'.format(
|
|
||||||
name=nic.name
|
|
||||||
))
|
|
||||||
nic.delete()
|
|
||||||
|
|
||||||
# delete IP on netbox that are not known on this server
|
for choice in dcim_c:
|
||||||
netbox_ips = nb.ipam.ip_addresses.filter(
|
self.dcim_choices[choice] = {}
|
||||||
device_id=self.device.id,
|
for c in dcim_c[choice]:
|
||||||
interface_id=[x.id for x in nb_nics],
|
self.dcim_choices[choice][c['label']] = c['value']
|
||||||
)
|
|
||||||
all_local_ips = list(chain.from_iterable([
|
|
||||||
x['ip'] for x in self.nics if x['ip'] is not None
|
|
||||||
]))
|
|
||||||
for netbox_ip in netbox_ips:
|
|
||||||
if netbox_ip.address not in all_local_ips:
|
|
||||||
logging.info('Unassigning IP {ip} from {interface}'.format(
|
|
||||||
ip=netbox_ip.address, interface=netbox_ip.interface))
|
|
||||||
netbox_ip.interface = None
|
|
||||||
netbox_ip.save()
|
|
||||||
|
|
||||||
# update each nic
|
def get_network_type(self):
|
||||||
for nic in self.nics:
|
return 'virtual'
|
||||||
interface = self.get_netbox_network_card(nic)
|
|
||||||
if not interface:
|
|
||||||
logging.info('Interface {mac_address} not found, creating..'.format(
|
|
||||||
mac_address=nic['mac'])
|
|
||||||
)
|
|
||||||
interface = self.create_netbox_nic(nic)
|
|
||||||
|
|
||||||
nic_update = 0
|
|
||||||
if nic['name'] != interface.name:
|
|
||||||
logging.info('Updating interface {interface} name to: {name}'.format(
|
|
||||||
interface=interface, name=nic['name']))
|
|
||||||
interface.name = nic['name']
|
|
||||||
nic_update += 1
|
|
||||||
|
|
||||||
ret, interface = self.reset_vlan_on_interface(nic, interface)
|
|
||||||
nic_update += ret
|
|
||||||
|
|
||||||
type = self.get_netbox_type_for_nic(nic)
|
|
||||||
if not interface.type or \
|
|
||||||
type != interface.type.value:
|
|
||||||
logging.info('Interface type is wrong, resetting')
|
|
||||||
interface.type = type
|
|
||||||
nic_update += 1
|
|
||||||
|
|
||||||
if interface.lag is not None:
|
|
||||||
local_lag_int = next(
|
|
||||||
item for item in self.nics if item['name'] == interface.lag.name
|
|
||||||
)
|
|
||||||
if nic['name'] not in local_lag_int['bonding_slaves']:
|
|
||||||
logging.info('Interface has no LAG, resetting')
|
|
||||||
nic_update += 1
|
|
||||||
interface.lag = None
|
|
||||||
|
|
||||||
# cable the interface
|
|
||||||
if config.network.lldp:
|
|
||||||
switch_ip = self.lldp.get_switch_ip(interface.name)
|
|
||||||
switch_interface = self.lldp.get_switch_port(interface.name)
|
|
||||||
if switch_ip and switch_interface:
|
|
||||||
ret, interface = self.create_or_update_cable(
|
|
||||||
switch_ip, switch_interface, interface
|
|
||||||
)
|
|
||||||
nic_update += ret
|
|
||||||
|
|
||||||
if nic['ip']:
|
|
||||||
# sync local IPs
|
|
||||||
for ip in nic['ip']:
|
|
||||||
self.create_or_update_netbox_ip_on_interface(ip, interface)
|
|
||||||
if nic_update > 0:
|
|
||||||
interface.save()
|
|
||||||
|
|
||||||
self._set_bonding_interfaces()
|
|
||||||
self.create_or_update_ipmi()
|
|
||||||
logging.debug('Finished updating NIC!')
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from netbox_agent.config import config
|
||||||
from netbox_agent.config import netbox_instance as nb
|
from netbox_agent.config import netbox_instance as nb
|
||||||
from netbox_agent.inventory import Inventory
|
from netbox_agent.inventory import Inventory
|
||||||
from netbox_agent.location import Datacenter, Rack
|
from netbox_agent.location import Datacenter, Rack
|
||||||
from netbox_agent.network import Network
|
from netbox_agent.network import ServerNetwork
|
||||||
from netbox_agent.power import PowerSupply
|
from netbox_agent.power import PowerSupply
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ class ServerBase():
|
||||||
def get_power_consumption(self):
|
def get_power_consumption(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _netbox_create_blade_chassis(self, datacenter, rack):
|
def _netbox_create_chassis(self, datacenter, rack):
|
||||||
device_type = get_device_type(self.get_chassis())
|
device_type = get_device_type(self.get_chassis())
|
||||||
device_role = get_device_role('Server Chassis')
|
device_role = get_device_role('Server Chassis')
|
||||||
serial = self.get_chassis_service_tag()
|
serial = self.get_chassis_service_tag()
|
||||||
|
|
@ -172,27 +172,6 @@ class ServerBase():
|
||||||
)
|
)
|
||||||
return new_blade
|
return new_blade
|
||||||
|
|
||||||
def _netbox_set_blade_slot(self, chassis, server):
|
|
||||||
slot = self.get_blade_slot()
|
|
||||||
# Find the slot and update it with our blade
|
|
||||||
device_bays = nb.dcim.device_bays.filter(
|
|
||||||
device_id=chassis.id,
|
|
||||||
name=slot,
|
|
||||||
)
|
|
||||||
if len(device_bays) > 0:
|
|
||||||
logging.info(
|
|
||||||
'Setting device ({serial}) new slot on {slot} '
|
|
||||||
'(Chassis {chassis_serial})..'.format(
|
|
||||||
serial=server.serial, slot=slot, chassis_serial=chassis.serial
|
|
||||||
))
|
|
||||||
device_bay = device_bays[0]
|
|
||||||
device_bay.installed_device = server
|
|
||||||
device_bay.save()
|
|
||||||
else:
|
|
||||||
logging.error('Could not find slot {slot} for chassis'.format(
|
|
||||||
slot=slot
|
|
||||||
))
|
|
||||||
|
|
||||||
def _netbox_create_server(self, datacenter, rack):
|
def _netbox_create_server(self, datacenter, rack):
|
||||||
device_role = get_device_role('Server')
|
device_role = get_device_role('Server')
|
||||||
device_type = get_device_type(self.get_product_name())
|
device_type = get_device_type(self.get_product_name())
|
||||||
|
|
@ -215,108 +194,89 @@ class ServerBase():
|
||||||
def get_netbox_server(self):
|
def get_netbox_server(self):
|
||||||
return nb.dcim.devices.get(serial=self.get_service_tag())
|
return nb.dcim.devices.get(serial=self.get_service_tag())
|
||||||
|
|
||||||
def netbox_create(self, config):
|
def _netbox_set_or_update_blade_slot(self, server, chassis, datacenter):
|
||||||
logging.debug('Creating Server..')
|
# before everything check if right chassis
|
||||||
|
actual_device_bay = server.parent_device.device_bay if server.parent_device else None
|
||||||
|
actual_chassis = actual_device_bay.device if actual_device_bay else None
|
||||||
|
slot = self.get_blade_slot()
|
||||||
|
if actual_chassis and \
|
||||||
|
actual_chassis.serial == chassis.serial and \
|
||||||
|
actual_device_bay.name == slot:
|
||||||
|
return
|
||||||
|
|
||||||
|
real_device_bays = nb.dcim.device_bays.filter(
|
||||||
|
device_id=chassis.id,
|
||||||
|
name=slot,
|
||||||
|
)
|
||||||
|
if len(real_device_bays) > 0:
|
||||||
|
logging.info(
|
||||||
|
'Setting device ({serial}) new slot on {slot} '
|
||||||
|
'(Chassis {chassis_serial})..'.format(
|
||||||
|
serial=server.serial, slot=slot, chassis_serial=chassis.serial
|
||||||
|
))
|
||||||
|
# reset actual device bay if set
|
||||||
|
if actual_device_bay:
|
||||||
|
actual_device_bay.installed_device = None
|
||||||
|
actual_device_bay.save()
|
||||||
|
# setup new device bay
|
||||||
|
real_device_bay = real_device_bays[0]
|
||||||
|
real_device_bay.installed_device = server
|
||||||
|
real_device_bay.save()
|
||||||
|
else:
|
||||||
|
logging.error('Could not find slot {slot} for chassis'.format(
|
||||||
|
slot=slot
|
||||||
|
))
|
||||||
|
|
||||||
|
def netbox_create_or_update(self, config):
|
||||||
|
"""
|
||||||
|
Netbox method to create or update info about our server/blade
|
||||||
|
|
||||||
|
Handle:
|
||||||
|
* new chassis for a blade
|
||||||
|
* new slot for a blade
|
||||||
|
* hostname update
|
||||||
|
* Network infos
|
||||||
|
* Inventory management
|
||||||
|
* PSU management
|
||||||
|
"""
|
||||||
datacenter = self.get_netbox_datacenter()
|
datacenter = self.get_netbox_datacenter()
|
||||||
rack = self.get_netbox_rack()
|
rack = self.get_netbox_rack()
|
||||||
|
|
||||||
if self.is_blade():
|
if self.is_blade():
|
||||||
# let's find the blade
|
|
||||||
serial = self.get_service_tag()
|
|
||||||
blade = nb.dcim.devices.get(serial=serial)
|
|
||||||
chassis = nb.dcim.devices.get(serial=self.get_chassis_service_tag())
|
|
||||||
# if it doesn't exist, create it
|
|
||||||
if not blade:
|
|
||||||
# check if the chassis exist before
|
|
||||||
# if it doesn't exist, create it
|
|
||||||
chassis = nb.dcim.devices.get(
|
chassis = nb.dcim.devices.get(
|
||||||
serial=self.get_chassis_service_tag()
|
serial=self.get_chassis_service_tag()
|
||||||
)
|
)
|
||||||
|
# Chassis does not exist
|
||||||
if not chassis:
|
if not chassis:
|
||||||
chassis = self._netbox_create_blade_chassis(datacenter, rack)
|
chassis = self._netbox_create_chassis(datacenter, rack)
|
||||||
|
|
||||||
blade = self._netbox_create_blade(chassis, datacenter, rack)
|
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
||||||
|
if not server:
|
||||||
|
server = self._netbox_create_blade(chassis, datacenter, rack)
|
||||||
|
|
||||||
# Set slot for blade
|
# Set slot for blade
|
||||||
self._netbox_set_blade_slot(chassis, blade)
|
self._netbox_set_or_update_blade_slot(server, chassis, datacenter)
|
||||||
else:
|
else:
|
||||||
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
||||||
if not server:
|
if not server:
|
||||||
self._netbox_create_server(datacenter, rack)
|
self._netbox_create_server(datacenter, rack)
|
||||||
|
|
||||||
self.network = Network(server=self)
|
logging.debug('Updating Server...')
|
||||||
self.network.create_netbox_network_cards()
|
# check network cards
|
||||||
|
if config.register or config.update_all or config.update_network:
|
||||||
|
self.network = ServerNetwork(server=self)
|
||||||
|
self.network.create_or_update_netbox_network_cards()
|
||||||
|
# update inventory if feature is enabled
|
||||||
|
if config.inventory and (config.register or config.update_all or config.update_inventory):
|
||||||
|
self.inventory = Inventory(server=self)
|
||||||
|
self.inventory.create_or_update()
|
||||||
|
# update psu
|
||||||
|
if config.register or config.update_all or config.update_psu:
|
||||||
self.power = PowerSupply(server=self)
|
self.power = PowerSupply(server=self)
|
||||||
self.power.create_or_update_power_supply()
|
self.power.create_or_update_power_supply()
|
||||||
|
self.power.report_power_consumption()
|
||||||
|
|
||||||
if config.inventory:
|
|
||||||
self.inventory = Inventory(server=self)
|
|
||||||
self.inventory.create()
|
|
||||||
logging.debug('Server created!')
|
|
||||||
|
|
||||||
def _netbox_update_chassis_for_blade(self, server, datacenter):
|
|
||||||
chassis = server.parent_device.device_bay.device
|
|
||||||
device_bay = nb.dcim.device_bays.get(
|
|
||||||
server.parent_device.device_bay.id
|
|
||||||
)
|
|
||||||
|
|
||||||
parent_chassis = nb.dcim.devices.get(
|
|
||||||
chassis.id
|
|
||||||
)
|
|
||||||
|
|
||||||
netbox_chassis_serial = parent_chassis.serial
|
|
||||||
move_device_bay = False
|
|
||||||
|
|
||||||
# check chassis serial with dmidecode
|
|
||||||
if netbox_chassis_serial != self.get_chassis_service_tag():
|
|
||||||
move_device_bay = True
|
|
||||||
# try to find the new netbox chassis
|
|
||||||
chassis = nb.dcim.devices.get(
|
|
||||||
serial=self.get_chassis_service_tag()
|
|
||||||
)
|
|
||||||
if not chassis:
|
|
||||||
chassis = self._netbox_create_blade_chassis(datacenter)
|
|
||||||
if move_device_bay or device_bay.name != self.get_blade_slot():
|
|
||||||
logging.info('Device ({serial}) seems to have moved, reseting old slot..'.format(
|
|
||||||
serial=server.serial))
|
|
||||||
device_bay.installed_device = None
|
|
||||||
device_bay.save()
|
|
||||||
|
|
||||||
# Set slot for blade
|
|
||||||
self._netbox_set_blade_slot(chassis, server)
|
|
||||||
|
|
||||||
def netbox_update(self, config):
|
|
||||||
"""
|
|
||||||
Netbox method to update info about our server/blade
|
|
||||||
|
|
||||||
Handle:
|
|
||||||
* new chasis for a blade
|
|
||||||
* new slot for a bblade
|
|
||||||
* hostname update
|
|
||||||
* new network infos
|
|
||||||
"""
|
|
||||||
logging.debug('Updating Server...')
|
|
||||||
|
|
||||||
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
|
||||||
if not server:
|
|
||||||
raise Exception("The server (Serial: {}) isn't yet registered in Netbox, register"
|
|
||||||
'it before updating it'.format(self.get_service_tag()))
|
|
||||||
update = 0
|
update = 0
|
||||||
if self.is_blade():
|
|
||||||
datacenter = self.get_netbox_datacenter()
|
|
||||||
# if it's already linked to a chassis
|
|
||||||
if server.parent_device:
|
|
||||||
self._netbox_update_chassis_for_blade(server, datacenter)
|
|
||||||
else:
|
|
||||||
logging.info('Blade is not in a chassis, fixing...')
|
|
||||||
chassis = nb.dcim.devices.get(
|
|
||||||
serial=self.get_chassis_service_tag()
|
|
||||||
)
|
|
||||||
if not chassis:
|
|
||||||
chassis = self._netbox_create_blade_chassis(datacenter)
|
|
||||||
# Set slot for blade
|
|
||||||
self._netbox_set_blade_slot(chassis, server)
|
|
||||||
|
|
||||||
# for every other specs
|
# for every other specs
|
||||||
# check hostname
|
# check hostname
|
||||||
if server.name != self.get_hostname():
|
if server.name != self.get_hostname():
|
||||||
|
|
@ -327,25 +287,12 @@ class ServerBase():
|
||||||
ret, server = self.update_netbox_location(server)
|
ret, server = self.update_netbox_location(server)
|
||||||
update += ret
|
update += ret
|
||||||
|
|
||||||
# check network cards
|
|
||||||
if config.update_all or config.update_network:
|
|
||||||
self.network = Network(server=self)
|
|
||||||
self.network.update_netbox_network_cards()
|
|
||||||
# update inventory
|
|
||||||
if config.update_all or config.update_inventory:
|
|
||||||
self.inventory = Inventory(server=self)
|
|
||||||
self.inventory.update()
|
|
||||||
# update psu
|
|
||||||
if config.update_all or config.update_psu:
|
|
||||||
self.power = PowerSupply(server=self)
|
|
||||||
self.power.create_or_update_power_supply()
|
|
||||||
self.power.report_power_consumption()
|
|
||||||
if update:
|
if update:
|
||||||
server.save()
|
server.save()
|
||||||
logging.debug('Finished updating Server!')
|
logging.debug('Finished updating Server!')
|
||||||
|
|
||||||
def print_debug(self):
|
def print_debug(self):
|
||||||
self.network = Network(server=self)
|
self.network = ServerNetwork(server=self)
|
||||||
print('Datacenter:', self.get_datacenter())
|
print('Datacenter:', self.get_datacenter())
|
||||||
print('Netbox Datacenter:', self.get_netbox_datacenter())
|
print('Netbox Datacenter:', self.get_netbox_datacenter())
|
||||||
print('Rack:', self.get_rack())
|
print('Rack:', self.get_rack())
|
||||||
|
|
|
||||||
86
netbox_agent/virtualmachine.py
Normal file
86
netbox_agent/virtualmachine.py
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
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.logging import logging # NOQA
|
||||||
|
from netbox_agent.misc import get_hostname
|
||||||
|
from netbox_agent.network import VirtualNetwork
|
||||||
|
|
||||||
|
|
||||||
|
def is_vm(dmi):
|
||||||
|
bios = dmidecode.get_by_type(dmi, 'BIOS')
|
||||||
|
system = dmidecode.get_by_type(dmi, 'System')
|
||||||
|
|
||||||
|
if 'Hyper-V' in bios[0]['Version'] or \
|
||||||
|
'Xen' in bios[0]['Version'] or \
|
||||||
|
'Google Compute Engine' in system[0]['Product Name'] or \
|
||||||
|
'VirtualBox' in bios[0]['Version'] or \
|
||||||
|
'VMware' in system[0]['Manufacturer']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachine(object):
|
||||||
|
def __init__(self, dmi=None):
|
||||||
|
if dmi:
|
||||||
|
self.dmi = dmi
|
||||||
|
else:
|
||||||
|
self.dmi = dmidecode.parse()
|
||||||
|
self.network = None
|
||||||
|
|
||||||
|
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
|
||||||
|
return int(mem_gib)
|
||||||
|
|
||||||
|
def get_vcpus(self):
|
||||||
|
return os.cpu_count()
|
||||||
|
|
||||||
|
def get_netbox_vm(self):
|
||||||
|
hostname = get_hostname(config)
|
||||||
|
vm = nb.virtualization.virtual_machines.get(
|
||||||
|
name=hostname
|
||||||
|
)
|
||||||
|
return vm
|
||||||
|
|
||||||
|
def get_netbox_cluster(self, name):
|
||||||
|
cluster = nb.virtualization.clusters.get(
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
|
return cluster
|
||||||
|
|
||||||
|
def netbox_create_or_update(self, config):
|
||||||
|
logging.debug('It\'s a virtual machine')
|
||||||
|
created = False
|
||||||
|
updated = 0
|
||||||
|
|
||||||
|
hostname = get_hostname(config)
|
||||||
|
vm = self.get_netbox_vm()
|
||||||
|
|
||||||
|
vcpus = self.get_vcpus()
|
||||||
|
memory = self.get_memory()
|
||||||
|
if not vm:
|
||||||
|
logging.debug('Creating Virtual machine..')
|
||||||
|
cluster = self.get_netbox_cluster(config.virtual.cluster_name)
|
||||||
|
|
||||||
|
vm = nb.virtualization.virtual_machines.create(
|
||||||
|
name=hostname,
|
||||||
|
cluster=cluster.id,
|
||||||
|
vcpus=vcpus,
|
||||||
|
memory=memory,
|
||||||
|
)
|
||||||
|
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 updated:
|
||||||
|
vm.save()
|
||||||
Loading…
Add table
editor.link_modal.header
Reference in a new issue