Skip to content

Commit

Permalink
cleanup scripts and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
sjhloco committed Apr 15, 2022
1 parent 15c49e7 commit e35de85
Show file tree
Hide file tree
Showing 18 changed files with 664 additions and 2,209 deletions.
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,3 @@ config.py
config.pyc
.DS_Store
__pycache__
hme_base.yml
devices.yml
vms.yml
info
250 changes: 60 additions & 190 deletions README.md

Large diffs are not rendered by default.

36 changes: 0 additions & 36 deletions devices.yml

This file was deleted.

83 changes: 83 additions & 0 deletions example_vm_device.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# ----------------------------------------------------------------------------
# Examples of adding a couple of VMs and physical devices
# ----------------------------------------------------------------------------

###################################### Create Virtual Machines ######################################
cluster:
- name: ESX Cluster # Mandatory name of the cluster
site: site1 # Mandatory, needed to get unique cluster
tenant: tenant1 # Optional, can be here or under vm
device_role: firewall # Optional, can be here or under vm
platform: ASA # Optional, can be here or under vm
vm: # Mandatory List of VMs
- name: asa1 # Mandatory name of the vm
cpu: 4 # Optional
mem: 2 # Optional
disk: 32 # Optional
comments: ASA firewall # Optional
tags: {fw: 9e9e9e} # Optional dictionary of {tag: tag_colour}
intf: # Optionally define interface for the VM
- name: eth0 # Mandatory name of the VM if 'intf' defined
vrf_ip: [INSIDE, 10.10.10.1/24] # Optional L3 {VRF: IP} of the VM if 'intf' defined
descr: Inside interface # Optional description
- name: eth1
grp_vl: [vl_grp1, [20, 30]] # Optional L2 trunk port with {VLAN_group: [vlan, vlan]}
- name: eth1
grp_vl: [vl_grp1, 20] # Optional L2 access port with {VLAN_group: vlan}
- name: server1
tenant: tenant2
device_role: compute
status: decommissioning
intf:
- name: eth1
vrf_ip: [RED, 10.99.99.1/24]
- name: eth0
vrf_ip: [MGMT, 10.10.30.10/24]
dns: [email protected] # Optional DNS name of the device
primary_ip: True # Optionally set this interface as the primary interface

###################################### Create Devices ######################################
device_type:
- name: Catalyst 3560-CX-12PC-S # Mandatory, will automatically workout manufacturer
site: site1 # Mandatory, can be here or under device
tenant: tenant1 # Optional, can be here or under vm
device_role: switch # Mandatory, can be here or under device
platform: IOS # Optional, can be here or under vm
# cluster: ESX Home # Optional, can be here or under vm
location: office1_floor5 # Optional, MUST be the slug. Can be here or under device,
rack: FL5-A1 # Optional, can be here or under vm
device: # Mandatory List of devices
- name: SWI01 # Mandatory name of the vm
cluster: cluster1 # Optional, can be here or under vm
position: 2 # Optional devices position in the rack
face: rear # Optional, front or rear facing, default is front
serial: FOC2414L6TJ # Optional
asset_tag: "12345" # Optional (If is an integer netbox always thinks changed)
comments: Core switch # Optional
tags: {swi: 9e9e9e} # Optional dictionary of {tag: tag_colour}
virtual_chassis: {stack2: [1, 1]} # Optional create/ add to virtual chassis {vc_name: [vc_position, vc_priority]}
intf:
- name: vlan30
vrf_ip: [COMPUTE, 10.10.50.1/24] # Optional L3 {VRF: IP} of the VM if 'intf' defined
- name: GigabitEthernet0/1
grp_vl: [vl_grp1, 30] # Optional L2 access port with {VLAN_group: vlan}
- name: GigabitEthernet0/2
grp_vl: [vl_grp1, [20, 30]] # Optional L2 trunk port with {VLAN_group: [vlan, vlan]}
- name: SWI02
site: site2
tenant: tenant3
platform: NXOS
device_role: router
location: dc_floor1
rack: FL1-A1
intf:
- name: vlan20
vrf_ip: [BLU, 10.10.20.11/24]
type: virtual # Optionally set the port type, if port doesn't exist will default to virtual
- name: vlan10
vrf_ip: [BLU, 10.10.10.10/24]
dns: sw2.stesworld.com
primary_ip: True
- name: GigabitEthernet0/1
grp_vl: [vl_grp1, [20, 30]]
type: 10gbase-t # Optionally set as 10 gig port (overrides whats in device-type)
64 changes: 32 additions & 32 deletions nbox_add_device.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
"""
###### Netbox blah, blah, blah ######
Creates VMs and devices from a YAML file that starts at either the clusters (VMs) or devices-types (devices) and ends at the interface.
-VMs: Cluster name, Site and VM name are mandatory
-Devices: Device-type name, Site, tenants, Device-role and device name are mandatory
Can be a file holding VMs, devices or both.
-VMs are under a parent dictionary of cluster
-Devices are under a parent dictionary of device_type
The only things that cant be inherited from cluster are cpu, mem, disk, comments
The only things that be inherited from device-type are asset and serial number, comments, position, face and virtual-chassis
VMs: Cluster name, Site and VM name are mandatory
Devices: Device-type name, Site, Device-role and device name are mandatory
ONLY thing that cant be inherited from cluster are cpu, mem, disk, comments
ONLY thing that cant be inherited from device-type are asset and serial numnber, comments, positin and face
location must be the slug as it cna be anested object.
To run the script reference the VM variable file
python nbox_add_device.py devices_and_vms.yml
"""


import config
from netbox import NboxApi
import yaml
from sys import argv
from collections import defaultdict
import copy
from rich.console import Console
from rich.theme import Theme

######################## Variables to change dependant on environment ########################
import config
from netbox import NboxApi

# ----------------------------------------------------------------------------
# Variables to change dependant on environment
# ----------------------------------------------------------------------------
netbox_url = config.netbox_url
api_token = config.api_token
ssl = False
Expand Down Expand Up @@ -68,25 +66,27 @@ def create_vm_dvc(self, obj_type, cltr_dtype, vm_dvc, vm_dvc_orig):
dm["site"] = vm_dvc["site"]
dm["cluster"] = vm_dvc.get("cluster")
dm["location"] = vm_dvc.get("location")
dm["rack"] = vm_dvc.get("rack")
dm["position"] = vm_dvc_orig.get("position", None)
dm["face"] = vm_dvc_orig.get("face", "front")
dm["serial"] = vm_dvc_orig.get("serial", None)
dm["asset_tag"] = vm_dvc_orig.get("asset_tag", None)
dm["virtual_chassis"] = vm_dvc_orig.get("virtual_chassis", None)
if vm_dvc.get("rack") != None:
dm["rack"] = vm_dvc.get("rack")
dm["position"] = vm_dvc_orig.get("position", None)
dm["face"] = vm_dvc_orig.get("face", "front")
return dm

## 1b. CREATE_INTF_DM: Creates the data-models to be used to create the VM interface (interface and IP)
def create_intf_dm(self, obj_type, vl_vrf, vm_dvc, each_intf):
intf = dict(
virtual_machine=dict(name=vm_dvc["name"]),
name=each_intf["name"],
description=each_intf.get("descr", ""),
)
# Only requiredfor device interface
# Only required for device interface
if obj_type == "device":
del intf["virtual_machine"]
intf["device"] = dict(name=vm_dvc["name"])
intf["type"] = each_intf.get("type", "virtual")
intf["type"] = each_intf.get("type", None)
# INTF_DM: Sets whether an access or trunk port
if vl_vrf.get("vlan") != None:
if isinstance(vl_vrf["vlan"], int):
Expand All @@ -109,7 +109,7 @@ def create_intf_dm(self, obj_type, vl_vrf, vm_dvc, each_intf):
)
return dict(intf=intf, ip=ip)

## REMOVE_EMPTY: Removes any empty attributes from the VM/DVC or INTF or IP DMs
## 1c. REMOVE_EMPTY: Removes any empty attributes from the VM/DVC, INTF or IP DMs
def rmv_empty_attr(self, attr_dict):
tmp_attr_dict = copy.deepcopy(attr_dict)

Expand All @@ -126,7 +126,7 @@ def rmv_empty_attr(self, attr_dict):
del attr_dict[each_attr]
return attr_dict

## PRIM_IP: Sets 1st IP as primary if not set on any other interface
## 1d. PRIM_IP: Sets 1st IP as primary if not set on any other interface
def set_primary_ip(self, ip):
primary_ip_set = False

Expand Down Expand Up @@ -194,7 +194,7 @@ def vm_device_info(self, parent_obj, obj, info, err):
all_obj[obj_type] = self.nbox.get_single_id(
api_attr, obj, {"name": inherit_obj}, err
)
# DVC_OPTIONAL: Device only attributes
# DVC_OPTIONAL: Device only optional attributes
inherit_cltr = obj.get("cluster", parent_obj.get("cluster"))
if info == "device" and inherit_cltr != None:
fltr = dict(name=inherit_cltr, site_id=all_obj.get("site"))
Expand Down Expand Up @@ -225,7 +225,7 @@ def mand_err_msg(self, obj_type, input_err):
# Group errors as {missing_key: [obj_names]} before printing
for name, err_obj, err in input_err:
tmp_err[err_obj].append(name)

# STDOUT for the differnet missing mandatory attribute errors
for dict_name, obj_name in tmp_err.items():
if dict_name == "cluster":
self.rc.print(
Expand All @@ -244,7 +244,7 @@ def mand_err_msg(self, obj_type, input_err):
f":x: {obj_type.capitalize()} mandatory dictionary '{dict_name}' is missing in {obj_type}s '{', '.join(list(obj_name))}'"
)

## 3b. OBJ_ERROR_MSG: Error messages if any VM or VM interface objects don't exist (groups interfaces together together to report in one line)
## 3b. OBJ_ERROR_MSG: Error messages if any VM/device or interface objects don't exist (groups interfaces together to report in one line)
def obj_err_msg(self, obj_type, vm_name, input_err):
tmp_err = defaultdict(dict)
mand_err = []
Expand Down Expand Up @@ -287,7 +287,7 @@ def engine(self, cltr_dtype, vm_dvc, vm_dvc_fname):
all_obj = []
cltr_dtype_err = []

## 4a. CLTR/DTYPE:: Based on parent object (cluster or device=type) creates the objects (VM or device) DM by its getting attributes IDs
## 4a. CLTR/DTYPE:: Based on parent object (cluster or device-type) creates the objects (VM or device) DM by its getting attributes IDs
if self.my_vars.get(cltr_dtype) == None:
cltr_dtype_err.append(("unknown", cltr_dtype, None))
else:
Expand Down Expand Up @@ -334,12 +334,12 @@ def engine(self, cltr_dtype, vm_dvc, vm_dvc_fname):
vl_vrf["vrf"] = self.nbox.get_single_id(
"ipam.vrfs", each_intf, fltr, intf_err
)
# CREATE_INTF_DM: If are no errors creates the data-models to be used to create the interface
# 4d. CREATE_INTF_DM: If are no errors creates the data-models to be used to create the interface
if len(intf_err) == 0:
tmp_intf_ip = self.create_intf_dm(
vm_dvc, vl_vrf, dm_vm_dvc, each_intf
)
# CLEAN_DM: Removes any None values or empty lists
# 4e. CLEAN_DM: Removes any None values or empty lists
intf.append(
self.rmv_empty_attr(tmp_intf_ip["intf"])
)
Expand All @@ -355,7 +355,7 @@ def engine(self, cltr_dtype, vm_dvc, vm_dvc_fname):
self.obj_err_msg(
vm_dvc_fname, each_vm_dvc.get("name"), intf_err
)
# CREATE_DM: If no VM/device errors and no interface/IP errors sets primary IP and creates the DM
# 4f. CREATE_DM: If no VM/device errors and no interface/IP errors sets primary IP and creates the DM
elif len(intf_err) == 0:
ip = self.set_primary_ip(ip)
all_obj.append(dict(vm_dvc=dm_vm_dvc, intf=intf, ip=ip))
Expand All @@ -377,18 +377,18 @@ def engine(self, cltr_dtype, vm_dvc, vm_dvc_fname):
# RUN: Runs the script
# ----------------------------------------------------------------------------
def main():
# 1. LOAD: Opens netbox connection and loads the variable file
## 1. LOAD: Opens netbox connection and loads the variable file
script, first = argv
my_theme = {"repr.ipv4": "none", "repr.number": "none", "repr.call": "none"}
rc = Console(theme=Theme(my_theme))
nbox = NboxApi(netbox_url, api_token, ssl, rc)

# 2. DM: Create Data-Model for API calls. Has catchall of exit if empty as no changes need to be made
## 2. DM: Create Data-Model for API calls. Has catchall of exit if empty as no changes need to be made
create_dm = CreateDm(nbox, rc, argv)
vm = create_dm.engine("cluster", "vm", "Virtual machine")
dvc = create_dm.engine("device_type", "device", "Device")

# 3. NBOX: Create or update VMs using nbox API
## 3. NBOX: Create or update VMs using nbox API
if len(vm) != 0:
nbox.engine("vm", "virtualization.virtual_machines", vm)
if len(dvc) != 0:
Expand Down
Loading

0 comments on commit e35de85

Please sign in to comment.