diff --git a/netztp/__init__.py b/netztp/__init__.py index adf8c29..35aeec5 100644 --- a/netztp/__init__.py +++ b/netztp/__init__.py @@ -10,8 +10,6 @@ def create_app(): netztp_env = os.getenv('NETZTP_ENV', 'DEFAULT').capitalize() app.config.from_object(f'netztp.config.{netztp_env}') - inventory.authenticate(app.config['INVENTORY_API_TOKEN']) - from netztp.eos import bp as eos app.register_blueprint(eos, url_prefix='/eos') diff --git a/netztp/config.py b/netztp/config.py index b98aa96..03d1e2d 100644 --- a/netztp/config.py +++ b/netztp/config.py @@ -10,7 +10,7 @@ class Default(object): 'sqlite:///{}'.format(os.path.join(basedir, 'netztp.db')) SQLALCHEMY_TRACK_MODIFICATIONS = False - INVENTORY_API_TOKEN = os.environ.get('INVENTORY_API_TOKEN') + JSONIFY_PRETTYPRINT_REGULAR = True LOG_DESTINATIONS = [{ 'destination': '192.168.50.2:514', @@ -18,6 +18,13 @@ class Default(object): }] FIRMWARE_SERVER = 'https://firmware.yzguy.io' + EOS_VERSIONS = { + '4.21.7.1M': 'vEOS-lab-4.21.7.1M.swi', + '4.22.2.1F': 'vEOS-lab-4.22.2.1F.swi', + '4.23.0.1F': 'vEOS-lab-4.23.0.1F.swi', + '4.24.2.1F': 'vEOS-lab-4.24.2.1F.swi', + 'default': '4.24.2.1F' + } class Development(Default): FLASK_ENV = 'development' diff --git a/netztp/eos/routes.py b/netztp/eos/routes.py index ce1a8d4..1dccd8a 100644 --- a/netztp/eos/routes.py +++ b/netztp/eos/routes.py @@ -1,7 +1,7 @@ from flask import make_response, jsonify, request, url_for, redirect, abort, \ render_template, current_app from netztp.util import response_with_content_type, generate_checksum -from netztp import inventory +from netztp import inventory as inv from netztp.eos import bp from datetime import datetime @@ -75,23 +75,30 @@ def ztp_nodes(): # copy_file - download and save file @bp.route('/ztp/nodes/') def ztp_nodes_serial(serialnum): - # Look for Device - actions = [] - # If not device, return unknown/no actions - # return jsonify({ - # 'name': 'Unknown Device', - # 'actions': [] - # }), 404 + device = inv.device(serialnum) + # Look for Device + if not device: + return jsonify({ + 'name': 'Unknown Device', + 'actions': [] + }), 404 + + # Get firmware version from inventory or use default + eos_versions = current_app.config['EOS_VERSIONS'] + version = device.custom_fields['firmware'] + if not version: + version = eos_versions['default'] + filename = eos_versions[version] # Upgrade Software Action actions.append({ 'name': 'Upgrade Operating System', 'action': 'install_image', 'attributes': { - 'url': f"{current_app.config['FIRMWARE_SERVER']}/eos/vEOS-lab-4.24.2.1F.swi", - 'version': '4.21.8M' + 'url': f"{current_app.config['FIRMWARE_SERVER']}/eos/{filename}", + 'version': version }, 'always_execute': True }) @@ -121,7 +128,7 @@ def ztp_nodes_serial(serialnum): 'name': 'Install static startup-config file', 'action': 'replace_config', 'attributes': { - 'url': '/nodes/{}/startup-config'.format(serialnum) + 'url': f'/nodes/{serialnum}/startup-config' }, 'always_execute': True }) @@ -131,7 +138,7 @@ def ztp_nodes_serial(serialnum): 'name': 'Signal ZTP Completion', 'action': 'copy_file', 'attributes': { - 'src_url': '/{}/ztp_finished'.format(serialnum), + 'src_url': f'/{serialnum}/ztp_finished', 'dst_url': '/mnt/flash', 'mode': '0644', 'overwrite': 'replace' @@ -166,14 +173,14 @@ def ztp_cli_commands(): # Node retreives device-specific startup-config @bp.route('/ztp/nodes//startup-config') def ztp_startup_config(serialnum): - device = inventory.device(serialnum) + device = inv.device(serialnum) return response_with_content_type(render_template('eos.j2', device=device), 'text/plain') # Node retrieves checksum for startup-config @bp.route('/ztp/meta/nodes//startup-config') def meta_serial_startup_config(serialnum): - device = inventory.device(serialnum) + device = inv.device(serialnum) return jsonify(generate_checksum(render_template('eos.j2', device=device))) # Node retrieves the time it finished ZTP diff --git a/netztp/eos/templates/eos.j2 b/netztp/eos/templates/eos.j2 index 3808362..ccd66fa 100644 --- a/netztp/eos/templates/eos.j2 +++ b/netztp/eos/templates/eos.j2 @@ -1,7 +1,7 @@ ! Command: show running-config -! device: {{ device.hostname }} +! device: {{ device.name }} ! -hostname {{ device.hostname }} +hostname {{ device.name }} ip domain-name {{ device.domain_name }} ! username admin privilege 15 role network-admin secret admin diff --git a/netztp/inventory.py b/netztp/inventory.py index 4ed7121..7b2c324 100644 --- a/netztp/inventory.py +++ b/netztp/inventory.py @@ -1,38 +1,28 @@ -class Inventory(object): - - DATA = { - 'abc123': { - 'hostname': 'abc123', - 'domain_name': 'yzguy.io', - 'ip_address': '192.168.50.200', - 'subnet_mask': '255.255.255.0', - 'gateway': '192.168.50.1' - }, - '123abc': { - 'hostname': '123abc', - 'domain_name': 'yzguy.io', - 'ip_address': '192.168.50.201', - 'subnet_mask': '255.255.255.0', - 'gateway': '192.168.50.1' - }, - '000c29a0de4d': { - 'hostname': '000c29a0de4d', - 'domain_name': 'yzguy.io', - 'ip_address': '192.168.50.202', - 'subnet_mask': '255.255.255.0', - 'gateway': '192.168.50.1' - } - } +import pynetbox, os +class Inventory(object): def __init__(self): - self.api_token = None + netbox = pynetbox.api( + os.getenv('NETBOX_URL'), + token=os.getenv('NETBOX_TOKEN'), + threading=True + ) + self.inv = netbox - def authenticate(self, api_token): - self.api_token = api_token + def devices(self): + devices = self.inv.dcim.devices.all() + return devices - def device(self, id): - if id in self.DATA: - return self.DATA[id] + def device(self, identifier): + device = self.inv.dcim.devices.filter(serial=identifier) + if device: + device = device[0] + device['interfaces'] = self._interfaces(device['id']) + return device + else: + return None - return {} + def _interfaces(self, device_id): + interfaces = self.inv.dcim.interfaces.filter(device_id=device_id) + return interfaces diff --git a/requirements.txt b/requirements.txt index 580ca19..b611329 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,14 @@ +certifi==2020.12.5 +chardet==4.0.0 click==7.1.2 Flask==1.1.2 gunicorn==20.0.4 +idna==2.10 itsdangerous==1.1.0 Jinja2==2.11.3 MarkupSafe==1.1.1 +pynetbox==5.3.1 +requests==2.25.1 +six==1.15.0 +urllib3==1.26.3 Werkzeug==1.0.1