Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ scripts_bank/logs/*
scripts_bank/textfiles/*
instance/settings.py
flask/
flaskenv/
flaskenv3/
tmp/*
venv/
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
language: python
python:
- "2.7"
before_install:
- pip install coveralls
install:
- pip install -r requirements.txt
script:
python -m unittest discover
after_success:
- coveralls
notifications:
slack: netconfig-group:gUDIl72WBF0iDG8gAVQxXjyD
5 changes: 5 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
from flask_script import Manager
from scripts_bank.netboxAPI import NetboxHost

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
app.config.from_pyfile('settings.py', silent=True)
db = SQLAlchemy(app)
Bootstrap(app)
try:
netbox = NetboxHost(app.config['NETBOXSERVER'])
except KeyError:
netbox = NetboxHost("''")

from app import views, models

Expand Down
2 changes: 1 addition & 1 deletion app/device_classes/deviceType.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from device_definitions.cisco import CiscoIOS, CiscoNXOS, CiscoASA
from .device_definitions.cisco import CiscoIOS, CiscoNXOS, CiscoASA

# The keys of this dictionary are the supported device_types
CLASS_MAPPER = {
Expand Down
6 changes: 3 additions & 3 deletions app/device_classes/device_definitions/cisco/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from cisco_ios import CiscoIOS
from cisco_nxos import CiscoNXOS
from cisco_asa import CiscoASA
from .cisco_ios import CiscoIOS
from .cisco_nxos import CiscoNXOS
from .cisco_asa import CiscoASA

__all__ = ['CiscoIOS', 'CiscoNXOS', 'CiscoASA']
5 changes: 4 additions & 1 deletion app/device_classes/device_definitions/cisco/cisco_nxos.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from app.device_classes.device_definitions.cisco_base_device import CiscoBaseDevice
import re
import xml.etree.cElementTree as ET
from StringIO import StringIO
try:
from StringIO import StringIO # Python 2
except ImportError:
from io import StringIO # Python 3
from app.scripts_bank.lib.functions import containsSkipped


Expand Down
3 changes: 1 addition & 2 deletions app/device_classes/device_definitions/cisco_base_device.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from base_device import BaseDevice
from .base_device import BaseDevice


class CiscoBaseDevice(BaseDevice):
Expand Down Expand Up @@ -55,7 +55,6 @@ def cleanup_ios_output(self, iosOutput):

def cleanup_nxos_output(self, nxosOutput):
"""Clean up returned NX-OS output from 'show ip interface brief'."""
# TODO cleanup like cleanup_ios_output
data = []

for line in nxosOutput.splitlines():
Expand Down
34 changes: 27 additions & 7 deletions app/scripts_bank/db_modifyDatabase.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
from app import app, db, models
from app import app, db, models, netbox
from sqlalchemy import asc, func
from netaddr import IPAddress, core
import netboxAPI
from ..device_classes import deviceType as dt


class Host(object):
"""Generic Host class that mimics Host db.model"""

def __init__(self, id, hostname, ipv4_addr, type, ios_type, local_creds=False):
"""Initilization method."""
self.id = id
self.hostname = hostname
self.ipv4_addr = ipv4_addr
self.type = type
self.ios_type = ios_type
self.local_creds = local_creds


def addHostToDB(hostname, ipv4_addr, type, ios_type, local_creds):
"""Add host to database. Returns True if successful."""
try:
Expand Down Expand Up @@ -137,14 +149,22 @@ def retrieveHostByID(x):
Does not return SSH session.
x = host id
"""

# TODO do we need to check this every single time we get a host?
# There should be a host wrapper that handles the location under the hood

if app.config['DATALOCATION'] == 'local':
host = models.Host.query.filter_by(id=x).first()
# TODO handle downstream to use a dictionary not a model
return models.Host.query.filter_by(id=x).first()
elif app.config['DATALOCATION'] == 'netbox':
host = netboxAPI.getHostByID(x)
# Local credentials with Netbox is not currently supported, so we manually set it to False here.
host.local_creds = False

return host
host = netbox.getHostByID(x)

return Host(str(x), host['name'],
host['primary_ip']['address'].split('/', 1)[0],
host['device_type']['model'],
# can we get this in the previous response?
netbox.getDeviceTypeOS(host['device_type']['id']))


def getHostByID(x):
Expand Down
219 changes: 89 additions & 130 deletions app/scripts_bank/netboxAPI.py
Original file line number Diff line number Diff line change
@@ -1,174 +1,133 @@
from app import app
from urllib2 import urlopen
from json import load

# TODO refactor with requests
import requests


class NetboxHost(object):
"""Class for storing device information pulled from Netbox via API calls."""

def __init__(self, id, hostname, ipv4_addr, type, ios_type):
"""Initilization method."""
self.id = id
self.hostname = hostname
self.ipv4_addr = ipv4_addr
self.type = type
self.ios_type = ios_type


def getDeviceType(x):
"""Input type of device (network, database, server, etc), returns ID in Netbox database."""
response = []

url = app.config['NETBOXSERVER']
url += "/api/dcim/device-roles/"
# url += "?limit=0"

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)

for device in json_obj['results']:
if device['name'] == x:
return device['id']
"""
Netbox Host class for making calls to Netbox host
"""
# TODO should be a database model

return False
def __init__(self, url):
self.url = url

def getDeviceType(self, x):
"""Input type of device (network, database, server, etc), returns ID in Netbox database."""
r = requests.get(self.url + '/api/dcim/device-roles/')

def getDeviceTypeOS(x):
"""Input type of device (network, database, server, etc), returns ID in Netbox database."""
response = []
if r.status_code == requests.codes.ok:
for device in r.json()['results']:
if device['name'] == x:
return device['id']
else:
return False

url = app.config['NETBOXSERVER']
url += "/api/dcim/device-types/"
url += str(x)
def getDeviceTypeOS(self, x):
"""Get Device Type of specific Netbox Device ID"""
r = requests.get(self.url + '/api/dcim/device-types/' + str(x))

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
if r.status_code == requests.codes.ok:

netconfigOS = str(json_obj['custom_fields']['Netconfig_OS']['label'])
# NOTE should probably put a try/catch around this
netconfigOS = r.json()['custom_fields']['Netconfig_OS']['label']

if netconfigOS == 'IOS':
return 'cisco_ios'
elif netconfigOS == 'IOS-XE':
return 'cisco_xe'
elif netconfigOS == 'NX-OS':
return 'cisco_nxos'
elif netconfigOS == 'ASA':
return 'cisco_asa'
else: # Default to simply cisco_ios
return 'cisco_ios'
if netconfigOS == 'IOS':
return 'cisco_ios'
elif netconfigOS == 'IOS-XE':
return 'cisco_xe'
elif netconfigOS == 'NX-OS':
return 'cisco_nxos'
elif netconfigOS == 'ASA':
return 'cisco_asa'
else: # Default to simply cisco_ios
return 'cisco_ios'

else:

def getHostByID(x):
"""Return host info in same formate as SQLAlchemy responses, for X-Compatibility with local DB choice.
# NOTE should this be False?
return 'cisco_ios'

x is host ID
"""
host = NetboxHost('', '', '', '', '')
def getHostByID(self, x):
"""Return host info in same format as SQLAlchemy responses, for X-Compatibility with local DB choice.

response = []
x is host ID
"""

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += str(x)
r = requests.get(self.url + '/api/dcim/devices/' + str(x))

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
if r.status_code == requests.codes.ok:
return r.json()

host.id = str(x)
host.hostname = str(json_obj['name'])
host.ipv4_addr = str(json_obj['primary_ip']['address'].split('/', 1)[0])
host.type = str(json_obj['device_type']['model'])
host.ios_type = str(getDeviceTypeOS(json_obj['device_type']['id']))
else:
return None

return host
def getHosts(self):
"""Return all devices stored in Netbox."""

r = requests.get(self.url + '/api/dcim/devices/?limit=0')

def getHosts():
"""Return all devices stored in Netbox."""
response = []
result = []
if r.status_code == requests.codes.ok:

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += "?limit=0"
# NOTE probably don't need to strip primary_ip cidr.
# Not seeing this as a problem connecting
result = [host for host in r.json()['results']
if host['custom_fields']['Netconfig'] and
host['custom_fields']['Netconfig']['label'] == 'Yes']

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
return result

for host in json_obj['results']:
if str(host['custom_fields']['Netconfig']) != 'None':
if str(host['custom_fields']['Netconfig']['label']) == 'Yes':
# Strip the CIDR notation from the end of the IP address, and store it back as the address for the returned host
host['primary_ip']['address'] = str(host['primary_ip']['address'].split('/', 1)[0])
result.append(host)
else:

return result
return None

def getHostID(self, x):
"""Input device name/hostname, returns id as stored in Netbox."""
r = requests.get(self.url + '/api/dcim/devices/?limit=0')

def getHostID(x):
"""Input device name/hostname, returns id as stored in Netbox."""
response = []
if r.status_code == requests.codes.ok:

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += "?limit=0"
for host in r.json()['results']:
if host['display_name'] == x: # Network
return host['id']

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
else:

for host in json_obj['results']:
if host['display_name'] == x: # Network
return host['id']
return None

def getHostName(self, x):
"""Input ID, return device name from Netbox."""
r = requests.get(self.url + '/api/dcim/devices/' + str(x))

def getHostName(id):
"""Input ID, return device name from Netbox."""
response = []
if r.status_code == requests.codes.ok:

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += str(id)
# TODO add try/catch here
return r.json()['name']

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
else:

return json_obj['name']
return None

def getHostIPAddr(self, x):
"""Input ID, return device IP address from Netbox."""
r = requests.get(self.url + '/api/dcim/devices/' + str(x))

def getHostIPAddr(id):
"""Input ID, return device IP address from Netbox."""
response = []
if r.status_code == requests.codes.ok:

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += str(id)
# TODO add try/catch here
return r.json()['primary_ip']['address'].split('/', 1)[0]

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
else:

# Return IP address with trailing CIDR notation stripped off
return str(json_obj['primary_ip']['address'].split('/', 1)[0])
return None

def getHostType(self, x):
"""Input ID, return device type from Netbox."""
r = requests.get(self.url + '/api/dcim/devices/' + str(x))

def getHostType(id):
"""Input ID, return device type from Netbox."""
response = []
if r.status_code == requests.codes.ok:

url = app.config['NETBOXSERVER']
url += "/api/dcim/devices/"
url += str(id)
# TODO add try/catch here
return r.json()['device_type']['model']

# Open our url, load the JSON
response = urlopen(url)
json_obj = load(response)
else:

return json_obj['device_type']['model']
return None
Loading