Skip to content

Commit

Permalink
v1.3.4 Update (#58)
Browse files Browse the repository at this point in the history
* add travis integration to slack (#53)

* Add flaskenv to gitignore

* Added tracking .txt file for which functions have a test written already

* Add tests.  Begin Python3 conversion.

* Fix broken NXOS unit test

* Add TravisCI badge to Readme.  Updated docs

* Add coveralls support

* Update docs with coveralls badge

* Fix travis file type

* Fix #54 bug when adding new device with local credentials

* Fix #55 and #56 issues with underscores in iShell and interface edit Modal

* Cleanup code for issue #47.  Still WIP

* Fix #46 issue

* Add description display for NX-OS interfaces

* Use Requests for NetboxAPI (#57)

* Refactor all Netbox API calls to use Python requests.

* Convert NetboxAPI into its own object, define that object in __init__.py, and reference the object in views.py and db_modifyDatabase.py.

* Workaround for not having a NetboxServer defined (unittesting)

* Update to version 1.3.4
  • Loading branch information
matt852 authored Feb 10, 2018
1 parent f0906e6 commit 3286a75
Show file tree
Hide file tree
Showing 54 changed files with 10,669 additions and 240 deletions.
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

0 comments on commit 3286a75

Please sign in to comment.