From 5f0ea9976fdcfc9055a362e4a8bd9a782a0d9904 Mon Sep 17 00:00:00 2001 From: Dom Date: Sun, 9 Jun 2024 18:22:26 +0000 Subject: [PATCH] Porting code over from Github --- .gitignore | 4 + README.md | 93 ++++- ndct/.___init__.py | Bin 0 -> 4096 bytes ndct/._cli | Bin 0 -> 4096 bytes ndct/._core | Bin 0 -> 4096 bytes ndct/._modules | Bin 0 -> 4096 bytes ndct/__init__.py | 0 ndct/cli/.___init__.py | Bin 0 -> 4096 bytes ndct/cli/._configuration.py | Bin 0 -> 4096 bytes ndct/cli/._crypt.py | Bin 0 -> 4096 bytes ndct/cli/._deployment.py | Bin 0 -> 4096 bytes ndct/cli/._device.py | Bin 0 -> 4096 bytes ndct/cli/._main.py | Bin 0 -> 4096 bytes ndct/cli/__init__.py | 0 ndct/cli/configuration.py | 93 +++++ ndct/cli/crypt.py | 37 ++ ndct/cli/deployment.py | 102 ++++++ ndct/cli/device.py | 106 ++++++ ndct/cli/main.py | 20 ++ ndct/core/.___init__.py | Bin 0 -> 4096 bytes ndct/core/._banner.py | Bin 0 -> 4096 bytes ndct/core/._configuration.py | Bin 0 -> 4096 bytes ndct/core/._configuration_files | Bin 0 -> 4096 bytes ndct/core/._connection.py | Bin 0 -> 4096 bytes ndct/core/._crypt.py | Bin 0 -> 4096 bytes ndct/core/._db | Bin 0 -> 4096 bytes ndct/core/._deployment.py | Bin 0 -> 4096 bytes ndct/core/._device.py | Bin 0 -> 4096 bytes ndct/core/._device_metadata | Bin 0 -> 4096 bytes ndct/core/._log.py | Bin 0 -> 4096 bytes ndct/core/._logs | Bin 0 -> 4096 bytes ndct/core/._paths.py | Bin 0 -> 4096 bytes ndct/core/__init__.py | 0 ndct/core/banner.py | 12 + ndct/core/configuration.py | 328 ++++++++++++++++++ .../._R1_custom_commands.txt | Bin 0 -> 4096 bytes .../configuration_files/._R1_generated.txt | Bin 0 -> 4096 bytes .../._R2_custom_commands.txt | Bin 0 -> 4096 bytes .../configuration_files/._R2_generated.txt | Bin 0 -> 4096 bytes ndct/core/configuration_files/.___init__.py | Bin 0 -> 4096 bytes .../R1_custom_commands.txt | 2 + .../core/configuration_files/R1_generated.txt | 14 + .../R2_custom_commands.txt | 1 + .../core/configuration_files/R2_generated.txt | 6 + ndct/core/configuration_files/__init__.py | 0 ndct/core/connection.py | 53 +++ ndct/core/crypt.py | 111 ++++++ ndct/core/db/.___init__.py | Bin 0 -> 4096 bytes ndct/core/db/._deployments | Bin 0 -> 4096 bytes ndct/core/db/._devices | Bin 0 -> 4096 bytes ndct/core/db/._example_devices_file.txt | Bin 0 -> 4096 bytes ndct/core/db/._key.key | Bin 0 -> 4096 bytes ndct/core/db/__init__.py | 0 ndct/core/db/deployments | 1 + ndct/core/db/devices | 1 + ndct/core/db/example_devices_file.txt | 15 + ndct/core/db/key.key | 1 + ndct/core/deployment.py | 103 ++++++ ndct/core/device.py | 86 +++++ ndct/core/device_metadata/._R1_metadata.yaml | Bin 0 -> 4096 bytes ndct/core/device_metadata/._R2_metadata.yaml | Bin 0 -> 4096 bytes ndct/core/device_metadata/.___init__.py | Bin 0 -> 4096 bytes .../device_metadata/._cisco_ios_example.yaml | Bin 0 -> 4096 bytes ndct/core/device_metadata/._vyos_example.yaml | Bin 0 -> 4096 bytes ndct/core/device_metadata/R1_metadata.yaml | 26 ++ ndct/core/device_metadata/R2_metadata.yaml | 25 ++ ndct/core/device_metadata/__init__.py | 0 .../device_metadata/cisco_ios_example.yaml | 26 ++ ndct/core/device_metadata/vyos_example.yaml | 25 ++ ndct/core/log.py | 42 +++ ndct/core/logs/.___init__.py | Bin 0 -> 4096 bytes ndct/core/logs/__init__.py | 0 ndct/core/paths.py | 19 + ndct/modules/.___init__.py | Bin 0 -> 4096 bytes ndct/modules/._cisco_ios | Bin 0 -> 4096 bytes ndct/modules/._vyos | Bin 0 -> 4096 bytes ndct/modules/__init__.py | 0 ndct/modules/cisco_ios/.___init__.py | Bin 0 -> 4096 bytes ndct/modules/cisco_ios/._commands.json | Bin 0 -> 4096 bytes ndct/modules/cisco_ios/._template.j2 | Bin 0 -> 4096 bytes ndct/modules/cisco_ios/__init__.py | 0 ndct/modules/cisco_ios/commands.json | 7 + ndct/modules/cisco_ios/template.j2 | 28 ++ ndct/modules/vyos/.___init__.py | Bin 0 -> 4096 bytes ndct/modules/vyos/._commands.json | Bin 0 -> 4096 bytes ndct/modules/vyos/._template.j2 | Bin 0 -> 4096 bytes ndct/modules/vyos/__init__.py | 0 ndct/modules/vyos/commands.json | 7 + ndct/modules/vyos/template.j2 | 20 ++ setup.py | 29 ++ 90 files changed, 1442 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 ndct/.___init__.py create mode 100755 ndct/._cli create mode 100755 ndct/._core create mode 100755 ndct/._modules create mode 100644 ndct/__init__.py create mode 100644 ndct/cli/.___init__.py create mode 100644 ndct/cli/._configuration.py create mode 100644 ndct/cli/._crypt.py create mode 100644 ndct/cli/._deployment.py create mode 100644 ndct/cli/._device.py create mode 100644 ndct/cli/._main.py create mode 100644 ndct/cli/__init__.py create mode 100644 ndct/cli/configuration.py create mode 100644 ndct/cli/crypt.py create mode 100644 ndct/cli/deployment.py create mode 100644 ndct/cli/device.py create mode 100644 ndct/cli/main.py create mode 100644 ndct/core/.___init__.py create mode 100644 ndct/core/._banner.py create mode 100644 ndct/core/._configuration.py create mode 100755 ndct/core/._configuration_files create mode 100644 ndct/core/._connection.py create mode 100644 ndct/core/._crypt.py create mode 100755 ndct/core/._db create mode 100644 ndct/core/._deployment.py create mode 100644 ndct/core/._device.py create mode 100755 ndct/core/._device_metadata create mode 100644 ndct/core/._log.py create mode 100755 ndct/core/._logs create mode 100644 ndct/core/._paths.py create mode 100644 ndct/core/__init__.py create mode 100644 ndct/core/banner.py create mode 100644 ndct/core/configuration.py create mode 100644 ndct/core/configuration_files/._R1_custom_commands.txt create mode 100644 ndct/core/configuration_files/._R1_generated.txt create mode 100644 ndct/core/configuration_files/._R2_custom_commands.txt create mode 100644 ndct/core/configuration_files/._R2_generated.txt create mode 100644 ndct/core/configuration_files/.___init__.py create mode 100644 ndct/core/configuration_files/R1_custom_commands.txt create mode 100644 ndct/core/configuration_files/R1_generated.txt create mode 100644 ndct/core/configuration_files/R2_custom_commands.txt create mode 100644 ndct/core/configuration_files/R2_generated.txt create mode 100644 ndct/core/configuration_files/__init__.py create mode 100644 ndct/core/connection.py create mode 100644 ndct/core/crypt.py create mode 100644 ndct/core/db/.___init__.py create mode 100644 ndct/core/db/._deployments create mode 100644 ndct/core/db/._devices create mode 100644 ndct/core/db/._example_devices_file.txt create mode 100644 ndct/core/db/._key.key create mode 100644 ndct/core/db/__init__.py create mode 100644 ndct/core/db/deployments create mode 100644 ndct/core/db/devices create mode 100644 ndct/core/db/example_devices_file.txt create mode 100644 ndct/core/db/key.key create mode 100644 ndct/core/deployment.py create mode 100644 ndct/core/device.py create mode 100644 ndct/core/device_metadata/._R1_metadata.yaml create mode 100644 ndct/core/device_metadata/._R2_metadata.yaml create mode 100644 ndct/core/device_metadata/.___init__.py create mode 100644 ndct/core/device_metadata/._cisco_ios_example.yaml create mode 100644 ndct/core/device_metadata/._vyos_example.yaml create mode 100644 ndct/core/device_metadata/R1_metadata.yaml create mode 100644 ndct/core/device_metadata/R2_metadata.yaml create mode 100644 ndct/core/device_metadata/__init__.py create mode 100644 ndct/core/device_metadata/cisco_ios_example.yaml create mode 100644 ndct/core/device_metadata/vyos_example.yaml create mode 100644 ndct/core/log.py create mode 100644 ndct/core/logs/.___init__.py create mode 100644 ndct/core/logs/__init__.py create mode 100644 ndct/core/paths.py create mode 100644 ndct/modules/.___init__.py create mode 100755 ndct/modules/._cisco_ios create mode 100755 ndct/modules/._vyos create mode 100644 ndct/modules/__init__.py create mode 100644 ndct/modules/cisco_ios/.___init__.py create mode 100644 ndct/modules/cisco_ios/._commands.json create mode 100644 ndct/modules/cisco_ios/._template.j2 create mode 100644 ndct/modules/cisco_ios/__init__.py create mode 100644 ndct/modules/cisco_ios/commands.json create mode 100644 ndct/modules/cisco_ios/template.j2 create mode 100644 ndct/modules/vyos/.___init__.py create mode 100644 ndct/modules/vyos/._commands.json create mode 100644 ndct/modules/vyos/._template.j2 create mode 100644 ndct/modules/vyos/__init__.py create mode 100644 ndct/modules/vyos/commands.json create mode 100644 ndct/modules/vyos/template.j2 create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25c945b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/* +dist/* +ndct.egg-info/* +*/.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 3225d06..bab4209 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,93 @@ -# NDCT +# NDCT (Network Device Configuration Tool) +A configuration tool for efficient network device management. +## Features + +###### Generate network device configuration (Juniper Junos, Cisco IOS, Cisco Nexus, Cisco ASA) + - Multivendor device configuration generation from a single YAML file + +###### Deployments + - Push configuration + - Pull configuration + - Get device information + +###### Interface + - CLI + +###### File encryption + - AES 128-bit + +###### Full operation logging + +# Libraries +> + +# Installation +``` + $ cd NDCT + $ python3 setup.py install +``` + +# Commands +``` + $ ndct _____ + + configuration Configuration actions + crypt Crypt actions + deployment Deployment actions + device Device actions +``` +Add single device + +ndct device add -n DeviceName -i 192.168.1.1 -u username -p password -o cisco_ios|vyos +*** + +Add multiple devices from a file + +ndct device add-from-file -f example.txt +*** + +Remove device + +ndct device remove -n DeviceName +*** + +View device + +ndct device view -n DeviceName +*** + +Add deployment + +ndct deployment add -n DeploymentName -t Target1 Target2 -a get|deploy_generated|deploy_custom +*** + +Remove deployment + +ndct deployment remove -n DeploymentName +*** + +View deployment + +ndct deployment view -n DeploymentName +*** + +Run deployment + +ndct deployment run -n DeploymentName +*** + +List stored configuration files + +ndct configuration stored +*** + +Config diff + +ndct configuration diff -c1 R1_generated.txt -c2 R2_generated.txt +*** + +Generate config + +ndct configuration generate -n MyDevice +*** diff --git a/ndct/.___init__.py b/ndct/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ee00ce55620549d5be7c7c9fd08f91b622d3573e GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xIc% z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rLM(khn# literal 0 HcmV?d00001 diff --git a/ndct/._cli b/ndct/._cli new file mode 100755 index 0000000000000000000000000000000000000000..ee2b6c2227168a59964b5ee6bf6bc6a7b51e0d30 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xIc( z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rLs94eUr literal 0 HcmV?d00001 diff --git a/ndct/._core b/ndct/._core new file mode 100755 index 0000000000000000000000000000000000000000..0d9b0f3e880df351177dc7ee3f1d9a34931e031b GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xI!> z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rL?pek7a literal 0 HcmV?d00001 diff --git a/ndct/._modules b/ndct/._modules new file mode 100755 index 0000000000000000000000000000000000000000..e1592e8b9f3bb8ab6da0de912597e4d846fd9383 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpS z(E*|vMA5V`LgYc>qRIKWdWi)EIjMSurHMs}c_o>7sSE}N7KYYlW@e^oMh2$V&KX7d zxvAD@hKzDYLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%#uIRrqRO%MhHxsZ&^ zVuhmA;{4L0$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpT z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33g-ohqXM literal 0 HcmV?d00001 diff --git a/ndct/cli/._configuration.py b/ndct/cli/._configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..93a51fadd3baf877e0f0151e1c04bf2e03ecdb50 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xIc) z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rMVuqvGZ literal 0 HcmV?d00001 diff --git a/ndct/cli/._crypt.py b/ndct/cli/._crypt.py new file mode 100644 index 0000000000000000000000000000000000000000..386ee7b74d71536a9098eb5ea283cc9dd740750a GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xIc& z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rM0W-6Ni literal 0 HcmV?d00001 diff --git a/ndct/cli/._deployment.py b/ndct/cli/._deployment.py new file mode 100644 index 0000000000000000000000000000000000000000..9c494d8f34213355d290d35b396ff868d241adaa GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpR z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33g92`Zle literal 0 HcmV?d00001 diff --git a/ndct/cli/._device.py b/ndct/cli/._device.py new file mode 100644 index 0000000000000000000000000000000000000000..b38e131802beaeb226572fbe78306448b0ef7d8c GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpV z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33geQ!1eV literal 0 HcmV?d00001 diff --git a/ndct/cli/._main.py b/ndct/cli/._main.py new file mode 100644 index 0000000000000000000000000000000000000000..f8f33669d22f5fd0b63246dfff4c4679b5ce6872 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpX z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33hH=PIQD literal 0 HcmV?d00001 diff --git a/ndct/cli/__init__.py b/ndct/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/cli/configuration.py b/ndct/cli/configuration.py new file mode 100644 index 0000000..d55c2f1 --- /dev/null +++ b/ndct/cli/configuration.py @@ -0,0 +1,93 @@ +import click +import os +import difflib +import yaml +import sys + +from jinja2 import Environment, FileSystemLoader +from ndct.core.device import Device +from ndct.core.log import log +from ndct.core.paths import METADATA_PATH, MODULE_PATH, CONFIG_PATH + +@click.command(short_help = 'Generate a configuration') +@click.option('-n', '--name', help='Name', required = True) +def generate(name): + ''' + Summary: + Generates a configuration file for a specified device. + + Takes: + name: Device to generate configuration for + ''' + Device.get_devices_from_file() + + if os.path.isfile(METADATA_PATH + name + '_metadata.yaml'): + with open(METADATA_PATH + name + '_metadata.yaml', 'r') as metadata: + device_metadata = (yaml.safe_load(metadata)) + else: + log('[{}] Metadata does not exist'.format(name), 'info') + sys.exit(1) + + device_os = Device.get_device_information(name)['os'] + + environment = MODULE_PATH + device_os + '/' + + j2_env = Environment(loader=FileSystemLoader(environment), trim_blocks=True, lstrip_blocks=True) + + for device, data in device_metadata.items(): + configuration = j2_env.get_template('template.j2').render(data) + + with open(CONFIG_PATH + name + '_generated.txt', 'w') as generated_config_file: + generated_config_file.write(configuration) + + log('[{}] Generated configuration'.format(name), 'info') + +@click.command(short_help = 'List configuration files') +def stored(): + ''' + Summary: + Lists all stored configuration files. + + Takes: + none + ''' + configurations = os.listdir(CONFIG_PATH) + + if configurations: + log('Stored configuration files:', 'info') + for configuration_file in configurations: + if configuration_file != '__init__.py': + log(configuration_file, 'info') + else: + log('No configuration files stored', 'info') + +@click.command(short_help = 'Show the difference between two configuration files') +@click.option('-c1', '--config1', help='Configuration file 1', required = True) +@click.option('-c2', '--config2', help='Configuration file 2', required = True) +def diff(config1, config2): + ''' + Summary: + Outputs the difference between two device configuration files by comparing them line by line. + + Takes: + config1: First configuration file + config2: Configuration file to compare with + ''' + config1_lines = open(CONFIG_PATH + config1).read().splitlines() + config2_lines = open(CONFIG_PATH + config2).read().splitlines() + diff = difflib.unified_diff(config1_lines, config2_lines) + + log('Diff for [{}] < > [{}]'.format(config1, config2), 'info') + for line in diff: + if line[0] == '+' and line[1] != '+': + log('\033[0;32m{}\033[m'.format(line), 'info') + elif line[0] == '-' and line[1] != '-': + log('\033[0;31m{}\033[m'.format(line), 'info') + +@click.group(short_help = 'Configuration commands') +def configuration(): + pass + +configuration.add_command(generate) +configuration.add_command(stored) +configuration.add_command(diff) \ No newline at end of file diff --git a/ndct/cli/crypt.py b/ndct/cli/crypt.py new file mode 100644 index 0000000..3817bf7 --- /dev/null +++ b/ndct/cli/crypt.py @@ -0,0 +1,37 @@ +import click +import json + +from ast import literal_eval +from cryptography.fernet import Fernet +from ndct.core.crypt import Crypt +from ndct.core.log import log +from ndct.core.paths import DB_PATH + +@click.command(short_help = 'Decrypt a file') +@click.option('-f', '--filename', type = click.Choice(['devices', 'deployments']), help = 'Filename', required = True) +def decrypt(filename): + ''' + Summary: + Create a decrypted copy of a file in JSON format. + + Takes: + filename: Name of decrypted file + ''' + key = Crypt.get_key() + + with open(DB_PATH + filename, 'rb') as encrypted_file: + data = encrypted_file.read() + + fernet = Fernet(key) + decrypted_data = literal_eval(fernet.decrypt(data).decode()) + + with open(DB_PATH + filename + '.decrypted', 'w') as decrypted_file: + json.dump(decrypted_data, decrypted_file, indent=4) + + log('Generated decrypted file {}.decrypted'.format(filename), 'info') + +@click.group(short_help = 'Crypt commands') +def crypt(): + pass + +crypt.add_command(decrypt) \ No newline at end of file diff --git a/ndct/cli/deployment.py b/ndct/cli/deployment.py new file mode 100644 index 0000000..6bda315 --- /dev/null +++ b/ndct/cli/deployment.py @@ -0,0 +1,102 @@ +import click +import sys + +from ndct.core.deployment import Deployment, deployments +from ndct.core.device import Device, devices +from ndct.core.log import log + +@click.command(short_help = 'Add a deployment') +@click.option('-n', '--name', help = 'Name', required = True) +@click.option('-t', '--targets', nargs = 0, help = 'Devices to deploy to', required = True) +@click.option('-a', '--action', type = click.Choice(['get', 'deploy_generated', 'deploy_custom']), help = 'Deployment action', required = True) +@click.argument('targets', nargs = -1) +def add(name, targets, action): + ''' + Summary: + Adds a deployment. + + Takes: + name: Name of deployment + targets: Devices to target with the deployment + action: Action to perform get|deploy_generated|deploy_custom + ''' + Deployment.get_deployments_from_file() + for deployment in deployments: + if name in deployment: + log('[{}] Deployment already exists'.format(name), 'info') + sys.exit(1) + deployment_object = Deployment(name, list(targets), action) + deployments.append({name: deployment_object}) + log('[{}] Deployment added successfully with ID {}'.format(name, deployment_object.deployment_id), 'info') + Deployment.save_deployments_to_file() + +@click.command(short_help = 'Remove a deployment') +@click.option('-n', '--name', help = 'Name', required = True) +def remove(name): + ''' + Summary: + Removes a deployment. + + Takes: + name: Name of deployment to remove + ''' + Deployment.get_deployments_from_file() + for deployment in deployments: + if name in deployment: + deployments.remove(deployment) + log('[{}] Deployment removed successfully'.format(name), 'info') + Deployment.save_deployments_to_file() + return + + log('[{}] Deployment does not exist'.format(name), 'error') + +@click.command(short_help = 'View a deployment') +@click.option('-n', '--name', help = 'Name', required = True) +def view(name): + ''' + Summary: + Prints attributes of a Deployment instance. + + Takes: + name: Name of deployment to view information about + ''' + Deployment.get_deployments_from_file() + for deployment in deployments: + if name in deployment: + deployment_dict = deployment[name].all() + log('Name: ' + str(deployment_dict['name']), 'info') + log('Targets: ' + str(deployment_dict['targets']), 'info') + log('Action: ' + str(deployment_dict['action']), 'info') + log('ID: ' + str(deployment_dict['deployment_id']), 'info') + log('Status: ' + str(deployment_dict['status']), 'info') + return + + log('[{}] Deployment does not exist'.format(name), 'error') + +@click.command(short_help = 'Run a deployment') +@click.option('-n', '--name', help = 'Name', required = True) +def run(name): + ''' + Summary: + Calls the run method on a Deployment object. + + Takes: + name: Name of deployment to run + ''' + Device.get_devices_from_file() + Deployment.get_deployments_from_file() + for deployment in deployments: + if name in deployment: + deployment[name].run() + Deployment.save_deployments_to_file() + return + log('[{}] Deployment does not exist'.format(name), 'error') + +@click.group(short_help = 'Deployment commands') +def deployment(): + pass + +deployment.add_command(add) +deployment.add_command(remove) +deployment.add_command(view) +deployment.add_command(run) \ No newline at end of file diff --git a/ndct/cli/device.py b/ndct/cli/device.py new file mode 100644 index 0000000..f74e15c --- /dev/null +++ b/ndct/cli/device.py @@ -0,0 +1,106 @@ +import click +import sys +import os + +from ndct.core.device import Device, devices +from ndct.core.log import log +from ndct.core.paths import DB_PATH + +@click.command(short_help = 'Add a device') +@click.option('-n', '--name', help = 'Name', required = True) +@click.option('-i', '--ip', help = 'IP address', required = True) +@click.option('-u', '--username', help = 'Username to authenticate against', required = True) +@click.option('-p', '--password', help = 'Password to authenticate with', required = True) +@click.option('-o', '--os', type = click.Choice(['cisco_ios', 'vyos']), help = 'Operating system', required = True) +def add(name, ip, username, password, os): + ''' + Summary: + Adds a device. + + Takes: + name: Name of device + ip: Management IP address of device + username: Username to authenticate against + password: Password to authenticate with + os: Operating system of device cisco_ios|vyos + ''' + Device.get_devices_from_file() + for device in devices: + if name in device: + log('[{}] Device already exists'.format(name), 'info') + sys.exit(1) + device_object = Device(name, ip, username, password, os) + devices.append({name: device_object}) + log('[{}] Device added successfully'.format(name), 'info') + Device.save_devices_to_file() + +@click.command(short_help = 'Add devices from a file') +@click.option('-f', '--filename', help = 'File to add devices from', required = True) +def add_from_file(filename): + Device.get_devices_from_file() + file_path = DB_PATH + filename + if os.path.isfile(file_path): + log("Adding devices from '{}'".format(file_path), 'info') + with open(file_path, 'r') as devices_file: + all_lines = [line.strip() for line in devices_file.readlines()] + for device_attribute in range(0, len(all_lines), 5): + device_exists = False + for device in devices: + if all_lines[device_attribute] in device: + log('[{}] Device already exists'.format(all_lines[device_attribute]), 'info') + device_exists = True + if device_exists == False: + device_object = Device(all_lines[device_attribute], all_lines[device_attribute+1], all_lines[device_attribute+2], all_lines[device_attribute+3], all_lines[device_attribute+4]) + devices.append({all_lines[device_attribute]: device_object}) + log('[{}] Device added successfully'.format(all_lines[device_attribute]), 'info') + Device.save_devices_to_file() + +@click.command(short_help = 'Remove a device') +@click.option('-n', '--name', help = 'Name', required = True) +def remove(name): + ''' + Summary: + Removes a device. + + Takes: + name: Name of device to remove + ''' + Device.get_devices_from_file() + for device in devices: + if name in device: + devices.remove(device) + log('[{}] Device removed successfully'.format(name), 'info') + Device.save_devices_to_file() + return + + log('[{}] Device does not exist'.format(name), 'error') + +@click.command(short_help = 'View a device') +@click.option('-n', '--name', help = 'Name', required = True) +def view(name): + ''' + Summary: + Prints attributes of a Device instance. + + Takes: + name: Name of device to view information about + ''' + Device.get_devices_from_file() + device_information = Device.get_device_information(name) + if device_information != None: + log('Name: ' + str(device_information['name']), 'info') + log('IP: ' + str(device_information['ip']), 'info') + log('Username: ' + str(device_information['username']), 'info') + log('Password: ' + str(device_information['password']), 'info') + log('OS: ' + str(device_information['os']), 'info') + else: + log('[{}] Device does not exist'.format(name), 'error') + +@click.group(short_help = 'Device commands') +def device(): + pass + +device.add_command(add) +device.add_command(add_from_file) +device.add_command(remove) +device.add_command(view) \ No newline at end of file diff --git a/ndct/cli/main.py b/ndct/cli/main.py new file mode 100644 index 0000000..8b36be7 --- /dev/null +++ b/ndct/cli/main.py @@ -0,0 +1,20 @@ +import click + +from ndct.cli import crypt +from ndct.cli import device +from ndct.cli import deployment +from ndct.cli import configuration +from ndct.core.banner import banner + +@click.group() +def main(): + ''' + Summary: + Network Device Configuration Tool. + ''' + banner() + +main.add_command(crypt.crypt, name = 'crypt') +main.add_command(device.device, name = 'device') +main.add_command(deployment.deployment, name = 'deployment') +main.add_command(configuration.configuration, name = 'configuration') \ No newline at end of file diff --git a/ndct/core/.___init__.py b/ndct/core/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cf50bb7ed72f1b86bc6089536e7a45a437df0f2d GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#Kul533GxF0|p_LSc{-3Y}c@h!F5+RN#RXAhG+63I?*7S##)#M z!~C#sVZLslGwebeU?Eamu5p94su}bsLV*62GWGo%K+B{0hh|B#q9^L*fD6@whNVS) zJs+g@;EUr?OaD{~CrptU2|+L&$59kbR2WR+`Oevcj2oYD%LJGJ6JP>NfC(@GCcp%k z025#WOyHA1b!=Agl$Y$X9G%Q<=~9Wxx;$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xD^P z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rK?h$@u; literal 0 HcmV?d00001 diff --git a/ndct/core/._configuration.py b/ndct/core/._configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..c059868911e35442ce7fd8eaa3d2af5809adb193 GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#Kul533CF8LV}P=tX0qywrkjskj-w~#KN0+4A106bfQ7j##)#M z!~C#sVZLslGwebeU@0P8Z*hl>>KXJXrU3m5W$OD^fR;n`56#1HLr+#u2b`%Md|6u5 zH}gSc^06oiCC7)OutHaSMExhnz4;OSG~yM;l3ujm4cDqR2o literal 0 HcmV?d00001 diff --git a/ndct/core/._configuration_files b/ndct/core/._configuration_files new file mode 100755 index 0000000000000000000000000000000000000000..d94909a4afa3d0d34e4dce364e025b551d694d03 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xF4u z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rL+|0-z! literal 0 HcmV?d00001 diff --git a/ndct/core/._connection.py b/ndct/core/._connection.py new file mode 100644 index 0000000000000000000000000000000000000000..1449296b5be8f77e15a6d85f7b2eddc5cb6dadcf GIT binary patch literal 4096 zcmeH~&kBM-5XPq~&?VcT2r*;Mt3=d#>#f<&*1~9O({-ISA?eIzZa=-;^t7jQt z-_5O97Y;8=DV$To4Oexh1i0>U5e7lvC!Xtv>(uB%guPFwB?3f%2oM1xKm>>Y5g-CY zfCvx)BJfF|JvQ5T%8t^kVn$TDF|lBYHaW{wE%5Bric5KBQY1AyO0IIeg*(?@r|$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xI!= z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rMM>MC0R literal 0 HcmV?d00001 diff --git a/ndct/core/._db b/ndct/core/._db new file mode 100755 index 0000000000000000000000000000000000000000..d848bb98a0fe37c17cf545d986eefe74d8c3701f GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xGG3 z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rLxy()$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xAJw zkPe0!KrD);g%Kexnw+1jmsn7cld4x(npl*WSCW~R%3xq%VQ6h;W@egZWMFFToKcjY zn`(__$S8L-1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E1fLjcs-1Yt0c3(3eV zRwzm>&Mz%WPE|?J3N)(cE67#ZwePO7sAx)wBAMOcyWB+Lmy3>bt|Vl9HENV|p&hOE1~#=@I;4A106;zWa}jkPcj zhWTON!hGF8ci4jtz(QoWTw{u@ni=#dLV&@QGWCNSfa6j9L$f4V(UbJ)fD6^`m&KvJ zox7RY`C`9U(m$Mp6}l?4gdmuX<0y(Ic^FLM`PQ19jK4nNmI*KcCcp%k025#WOn?b6 z0Vco%n7})M=GbiFAurh$N3`;2PBxR68(U(bD@kXk(L$*+Dw$WfQ9_q=3%9PnPM`a~ JRvCd;*LSkDDrW!y literal 0 HcmV?d00001 diff --git a/ndct/core/._device_metadata b/ndct/core/._device_metadata new file mode 100755 index 0000000000000000000000000000000000000000..607991938b3d5c3c36422f2bd7c8f084b353ef5a GIT binary patch literal 4096 zcmeH~&kBM-5XQ$KqDwbBh3pfA2}~&?VcT2uWklo3=d#(#f<&r24Fc@|IjLmcKAd+9dNU5e7lvC!Xtv>(uB%gkPUfO9Y4j5g-CYfCvx)B0vO) z01+SpMBtr3cWid?kQ1d@#f+$QV`9M)ZE}{WTHx8K6_@hNq)2LZlw9R_3-_+SPM`a~ JR+)j*ws#8wDt!O| literal 0 HcmV?d00001 diff --git a/ndct/core/._log.py b/ndct/core/._log.py new file mode 100644 index 0000000000000000000000000000000000000000..1bc77bf0dc06cffcea0dbc31b9b80e924a2f12f8 GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#3o&=B+LmU1`I+fu@*st*sfuNA)8%YW8qCahG+63I?*7S##)#M z!~C#sVZLsl)9*qXU@j6|tZ;+1su}bs0s!YqnR@32(DJDMp=lg1>B;)#fD6@b!_uO@ zp1X-DhGM^0a(FliD|DG@3BErWMPV3@)4(4`vz;}Cj2fSC%LJGJ6JP>NfC(@GCcp%k z025#WOyHA1b!=Aglo#)^BU*VhC!0u2jm)q-yf4}DrEow literal 0 HcmV?d00001 diff --git a/ndct/core/._logs b/ndct/core/._logs new file mode 100755 index 0000000000000000000000000000000000000000..dfef0c9db97ec790b58994050f0b216f7e86917e GIT binary patch literal 4096 zcmeH~Jqp4=5QQg#h>e|A66ORU3I-vSSc{-3Y}c^Cn9Z)PN#RXAhG+63aiT%g##)#M z!~C#sVZLslJ?KCSU?E~$u5p8n>KSw?#sIx5W$Jr30LP{Jhh|Z* z&AcC*+!Kd`lHRctR_G$t5`2F;3WFe+Bx8RP&Ue=2GW`04TPDB+m;e)C0!)AjFaajO z1egF5U;^(1>SMExhumnFp3usZDQzq4|tb;6k<6u(YVJ z=U!@ef!y!43Jxdfq$zWwAPgs?B#z^87KP(vwsm%=lEx?8G65#Q1egF5U;<2l2`~XB zzyz286Zj-h9h+4=<%?~8M5m7S$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xC&> z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rJ^>?&^n literal 0 HcmV?d00001 diff --git a/ndct/core/configuration_files/._R1_generated.txt b/ndct/core/configuration_files/._R1_generated.txt new file mode 100644 index 0000000000000000000000000000000000000000..dabcbe34b89f313c8a1baea93bef996506786c71 GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#KtaG66ORU3I-vSSc{-3Y}dFU!Od=5W8qCahG+63I?*7S##)#M z!~C#sVZLslGw4DaU?E~$u5p94su}bs#sK{*W$ODkfR;=356z-zMNf7w2VAIj8kQFI z_1uX~?unvM(mR}l6}rr{1mB;I!XOAH$=IKS^VFJLhK*0SWdclq2`~XBzyz286JP>N zfC(@GCh$q1IyS3#%8k-g=^Per|-t-yd#(Dsun; literal 0 HcmV?d00001 diff --git a/ndct/core/configuration_files/._R2_custom_commands.txt b/ndct/core/configuration_files/._R2_custom_commands.txt new file mode 100644 index 0000000000000000000000000000000000000000..176720bd278edbff64f5bc8c8cf17c328f390a55 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xF4v z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rJlqAG0w literal 0 HcmV?d00001 diff --git a/ndct/core/configuration_files/._R2_generated.txt b/ndct/core/configuration_files/._R2_generated.txt new file mode 100644 index 0000000000000000000000000000000000000000..83cc476032f49b75a307e984fe9601bddd677e8c GIT binary patch literal 4096 zcmeH~Jqp4=5QQg#h>cyWB+Lmy3>bt|Vl9HEu-$|WhOE1~#=@I;4A106;zWa}jkPcj zhWTON!hGF8ci4jtz(QoWT;m2?H8bc_gaCspW$Fhv0LP>Hhh|B#q9^Io0T-&>FN;Hc zJ9jg)^TmFzq<=UGD|A(82|+L&$59kb@-Udh^Q|>I8Gn7kEfZh@On?b60Vco%m;e)C z0!)AjFoAah&9T|ULte5ij%el4oNOjBH@3t=SCY<7qlHpuR5GtHRYI3^3%9PnPM`a~ JRvCd;*LP1gDscb+ literal 0 HcmV?d00001 diff --git a/ndct/core/configuration_files/.___init__.py b/ndct/core/configuration_files/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..210723c5185fbf6aae420d40318f749249eac952 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xF4t z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rMHNh)gq literal 0 HcmV?d00001 diff --git a/ndct/core/configuration_files/R1_custom_commands.txt b/ndct/core/configuration_files/R1_custom_commands.txt new file mode 100644 index 0000000..1b11dbf --- /dev/null +++ b/ndct/core/configuration_files/R1_custom_commands.txt @@ -0,0 +1,2 @@ +router ospf 1 +network 11.11.11.11 0.0.0.0 area 0 \ No newline at end of file diff --git a/ndct/core/configuration_files/R1_generated.txt b/ndct/core/configuration_files/R1_generated.txt new file mode 100644 index 0000000..43affe8 --- /dev/null +++ b/ndct/core/configuration_files/R1_generated.txt @@ -0,0 +1,14 @@ +hostname R1 +! +interface Loopback0 + description Loopback + ip address 11.11.11.11 255.255.255.255 + no shutdown +! +router bgp 65001 + neighbor 192.168.21.202 remote-as 65002 + network 11.11.11.11 mask 255.255.255.255 +! +router ospf 1 + network 192.168.21.0 0.0.0.255 area 0 +! \ No newline at end of file diff --git a/ndct/core/configuration_files/R2_custom_commands.txt b/ndct/core/configuration_files/R2_custom_commands.txt new file mode 100644 index 0000000..aed19be --- /dev/null +++ b/ndct/core/configuration_files/R2_custom_commands.txt @@ -0,0 +1 @@ +set protocols ospf area 0 network '12.12.12.12/32' \ No newline at end of file diff --git a/ndct/core/configuration_files/R2_generated.txt b/ndct/core/configuration_files/R2_generated.txt new file mode 100644 index 0000000..5937cd2 --- /dev/null +++ b/ndct/core/configuration_files/R2_generated.txt @@ -0,0 +1,6 @@ +set system host-name 'R2' +set interfaces loopback lo address '12.12.12.12/32' +set interfaces loopback lo description 'Loopback' +set protocols bgp 65002 neighbor 192.168.21.201 remote-as '65001' +set protocols bgp 65002 network '12.12.12.12/32' +set protocols ospf area 0 network '192.168.21.0/24' diff --git a/ndct/core/configuration_files/__init__.py b/ndct/core/configuration_files/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/core/connection.py b/ndct/core/connection.py new file mode 100644 index 0000000..cb534a6 --- /dev/null +++ b/ndct/core/connection.py @@ -0,0 +1,53 @@ +from netmiko import Netmiko +from pythonping import ping +from ndct.core.device import Device +from ndct.core.log import log + +class Connection(Device): + def __init__(self, name, ip, username, password, os): + ''' + Takes: + ip: IP address of the device + username: Username to authentication against + password: Password to authenticate with + os: Operating system of the device + ''' + super().__init__(name, ip, username, password, os) + + def get_connection(self): + ''' + Summary: + Tests device connectivity then creates an SSH connection if successful. + + Returns: + Connection object + ''' + ping_result = ping(self.ip, count=1) + + if 'Request timed out' not in str(ping_result): + log('[{}] Reachable, getting connection...'.format(self.name), 'info') + + connection = Netmiko( + self.ip, + username=self.username, + password=self.password, + device_type=self.os + ) + + log('[{}] Connected'.format(self.name), 'info') + return connection + else: + log('[{}] Not reachable'.format(self.name), 'info') + return + + def close_connection(self, connection): + ''' + Summary: + Closes an SSH connection to a device. + + Takes: + connection: Connection object to close + ''' + connection.disconnect() + + log('[{}] Disconnected'.format(self.name), 'info') \ No newline at end of file diff --git a/ndct/core/crypt.py b/ndct/core/crypt.py new file mode 100644 index 0000000..552494c --- /dev/null +++ b/ndct/core/crypt.py @@ -0,0 +1,111 @@ +import base64 +import os +import hashlib +import json + +from ast import literal_eval +from getpass import getpass +from cryptography.fernet import Fernet +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from ndct.core.paths import KEY_PATH, DB_PATH +from ndct.core.log import log + +class Crypt: + @staticmethod + def generate_key(): + ''' + Summary: + Generate a key used for symmetric encryption, using the hash of a user entered password for the salt. + + Returns: + Encryption key + + Encryption type: + 128-bit AES + ''' + if os.path.isfile(KEY_PATH): + log("Using existing key '{}' for encryption/decryption".format(KEY_PATH), 'info') + key = Crypt.get_key() + + return key + else: + log("Attempting to use '{}' but no key found, create a new key or add an existing one".format(KEY_PATH), 'info') + + password = getpass(prompt='New encryption key password: ') + password_bytes = password.encode() + salt = os.urandom(16) + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + salt=salt, + iterations=100000, + backend=default_backend() + ) + key = base64.urlsafe_b64encode(kdf.derive(password_bytes)) + + with open(KEY_PATH, 'wb') as encryption_key: + encryption_key.write(key) + + log("Stored encryption key as '{}'".format(KEY_PATH), 'info') + + return key + + @staticmethod + def get_key(): + ''' + Summary: + Get stored encryption key. + + Returns: + Encryption key + + Encryption type: + 128-bit AES + ''' + with open(KEY_PATH, 'rb') as encryption_key: + key = encryption_key.read() + + return key + + @staticmethod + def create_encrypted_file(filename, data): + ''' + Summary: + Create an encrypted file from data that is passed to the function. + + Takes: + filename: Name of encrypted file + data: Data to encrypt + ''' + key = Crypt.generate_key() + + fernet = Fernet(key) + encrypted_data = fernet.encrypt(str(data).encode()) + + with open(DB_PATH + filename, 'wb') as encrypted_file: + encrypted_file.write(encrypted_data) + + @staticmethod + def get_encrypted_file_contents(filename): + ''' + Summary: + Get the contents of an encrypted file to enable use of the stored data. + + Takes: + filename: Name of file to get decrypted contents of + + Returns: + Decrypted file contents + ''' + key = Crypt.generate_key() + + with open(DB_PATH + filename, 'rb') as encrypted_file: + data = encrypted_file.read() + + fernet = Fernet(key) + temp_data = fernet.decrypt(data).decode() + temp_data_list = literal_eval(temp_data) + + return temp_data_list \ No newline at end of file diff --git a/ndct/core/db/.___init__.py b/ndct/core/db/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..708f2ef4212da4d35fd160d7e86ef5196c9eef87 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xGG4 z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rJaU@DCO literal 0 HcmV?d00001 diff --git a/ndct/core/db/._deployments b/ndct/core/db/._deployments new file mode 100644 index 0000000000000000000000000000000000000000..74ab3571b4d221f339ecbd12ce5e9c39e82ca194 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xD^O z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rKD^eT}6 literal 0 HcmV?d00001 diff --git a/ndct/core/db/._devices b/ndct/core/db/._devices new file mode 100644 index 0000000000000000000000000000000000000000..0fc0775bcdbe04d0f2211a1762776f9397567c07 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xD^N z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rKjJ}Q#{ literal 0 HcmV?d00001 diff --git a/ndct/core/db/._example_devices_file.txt b/ndct/core/db/._example_devices_file.txt new file mode 100644 index 0000000000000000000000000000000000000000..349f92c3d16a4eaf5212bd321f9d4f2d5d1b0b30 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xD^M z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rJ(sw$5F literal 0 HcmV?d00001 diff --git a/ndct/core/db/._key.key b/ndct/core/db/._key.key new file mode 100644 index 0000000000000000000000000000000000000000..b38a595ee131871f9fa35aaf18a47806c9421923 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xGG2 z=wO%uWQ(F{VFdD_;-bmKf7%s{i3$ KkztVg{~rM62P%sI literal 0 HcmV?d00001 diff --git a/ndct/core/db/__init__.py b/ndct/core/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/core/db/deployments b/ndct/core/db/deployments new file mode 100644 index 0000000..ff85032 --- /dev/null +++ b/ndct/core/db/deployments @@ -0,0 +1 @@ +gAAAAABevSjq3Y7_nWkDlUWzLjMnKBkn29IiaA-GKTCL-dCdgSm0klcLCWLlYN_lpVTsrIY9QHQpGjZGCWGyMC3YFRDcZC4xxQ8nWgOU26xH5ypa2wBZrqGcCNpktcxRUNDl-NXOy9FOrYVzJFYMNMd_krmAya9NJcKX3AgKlBtKVZQGYpTYDOx4tBJ3roCOKRJEM_ZP5tPCvbI9FdBj4XjoeW1Rzz5i_wIY53k8aKhL8I6FDZXVFtg9s_5ph8I4St43kXVUuViGsbc2J_6R0Nw_xgs0vCMpxFZXgxxP_SAtG-Ux-ApVPjL6MibNbn-qgfzBqf3FaRp8o1N7-hT0FgD2Xv3dreXIbiYKKi8FFjdYtMkP0pB0ZUpES131QfSFoic5X4cQ7Rdi6ml6avVgjitsePLv-eBxDULrT0UrHPEyD_NjPugRNvYWlzaW0NBsyYkveXxsZuZdsQ9mlTcrnNcehCMAnzTvi7hnANhYU2gmraPskCmoe8CsmFNZC82S-jkLfHVo4CAsnnRmcGkJ_VaZWr659Uk9Txj0gBkBP5QVMTnJ1gFjzHjytKUNRVELk4rn4d0NixM3fRCxAGXIxiqnUKlsi-FFabOjrbOaWSmJIbDt64wLkSgGotfvz5j2ilTMArEzWO3xEGh9E4rbWg-b1cczdx2_IW5eh38NIpNImWAGmkhMHunGYGW6VoYRZpln-3X3Okt5eNOW4f1EeG0LroVzy71LgA== \ No newline at end of file diff --git a/ndct/core/db/devices b/ndct/core/db/devices new file mode 100644 index 0000000..52846ac --- /dev/null +++ b/ndct/core/db/devices @@ -0,0 +1 @@ +gAAAAABevR-IVmPaNA1MBwD9ukWIWMbf2axg4PgO0AxYxZAl35eaWEiU2Er8Y1Hk__cyc7nZ8l0KQHWy-iJRNuMs2b93_yfmN54cv0NZk9HwpOaWmGv4Ejp2iQLjXvcq_NrX8djlDkTyu73S98yzEjJ9HJR8o2fMbdotW37JCURQyBpn_IzXdfCuN6IXxGClTVK0TBvhVEESYrHOapUKB_qchya42jwTNuam7RgVOl8IkyrJqKsGICKtvNmCxLmm1yik_wO1QLwHUrBPuP1r5jtiFIfY090BobOuERky-Za7-ICkFt8x-YUJAGeimP_ynMNAXRh6l15FVemyHKxyju8tgawtcngAiw== \ No newline at end of file diff --git a/ndct/core/db/example_devices_file.txt b/ndct/core/db/example_devices_file.txt new file mode 100644 index 0000000..f637375 --- /dev/null +++ b/ndct/core/db/example_devices_file.txt @@ -0,0 +1,15 @@ +R7 +10.1.1.1 +admin +password +cisco_ios +R8 +10.2.2.2 +admin +password +cisco_ios +R9 +10.3.3.3 +admin +password +vyos \ No newline at end of file diff --git a/ndct/core/db/key.key b/ndct/core/db/key.key new file mode 100644 index 0000000..c6a0bc9 --- /dev/null +++ b/ndct/core/db/key.key @@ -0,0 +1 @@ +ENR0-Y8N5ZH7ho6DEAI9xRe17jwVAXsuekLOEfED6Ic= \ No newline at end of file diff --git a/ndct/core/deployment.py b/ndct/core/deployment.py new file mode 100644 index 0000000..2ca9e9e --- /dev/null +++ b/ndct/core/deployment.py @@ -0,0 +1,103 @@ +import uuid +import os + +from multiprocessing import Process +from ndct.core.configuration import Configuration +from ndct.core.crypt import Crypt +from ndct.core.log import log +from ndct.core.paths import DB_PATH + +deployments = [] + +class Deployment: + def __init__(self, name, targets, action, deployment_id=str(uuid.uuid4()), status='Not started'): + ''' + Takes: + name: Deployment name + targets: Target devices + action: Action of deployment + deployment_id: Unique identifier of deployment + status: Deployment status + attribute: Deployment attribute - for 'get' action deployments + ''' + self.name = name + self.targets = targets + self.action = action + self.deployment_id = deployment_id + self.status = status + + def all(self): + ''' + Summary: + Gets the contents of a Deployment instance. + + Returns: + Deployment instance contents in dictionairy form + ''' + return self.__dict__ + + def run(self): + ''' + Summary: + Runs a deployment. + ''' + log('[{}] Running deployment'.format(self.name), 'info') + + self.status = 'In progress' + log('[{}] Updated deployment status to In progress'.format(self.name), 'info') + + if self.action == 'get': + device_processes = [Process(target=Configuration.get_configuration, args=(target_device,)) for target_device in self.targets] + elif self.action == 'deploy_generated': + device_processes = [Process(target=Configuration.deploy_generated_configuration, args=(target_device,)) for target_device in self.targets] + elif self.action == 'deploy_custom': + device_processes = [Process(target=Configuration.deploy_custom_configuration, args=(target_device,)) for target_device in self.targets] + + for _process in device_processes: + _process.start() + + for _process in device_processes: + _process.join() + + self.status = 'Completed' + log('[{}] Updated deployment status to Completed'.format(self.name), 'info') + + log('[{}] Deployment completed'.format(self.name), 'info') + + @staticmethod + def get_deployments_from_file(): + ''' + Summary: + Gets deployment data from an encrypted file and creates an object stored in the deployments list. + ''' + if os.path.isfile(DB_PATH + 'deployments'): + deployments_temp_file = Crypt.get_encrypted_file_contents('deployments') + for deployment in deployments_temp_file: + deployment_object = Deployment( + deployment['name'], + deployment['targets'], + deployment['action'], + deployment_id=deployment['deployment_id'], + status=deployment['status'], + ) + deployments.append({deployment['name']: deployment_object}) + + log('Got deployments from file', 'info') + else: + log('No deployments to get from file', 'info') + + @staticmethod + def save_deployments_to_file(): + ''' + Summary: + Saves deployment data to an encrypted file. + ''' + deployments_to_save = [] + + for deployment in deployments: + for deployment_name, deployment_object in deployment.items(): + deployments_to_save.append(deployment_object.all()) + + Crypt.create_encrypted_file('deployments', deployments_to_save) + + log('Saved deployments to file', 'info') diff --git a/ndct/core/device.py b/ndct/core/device.py new file mode 100644 index 0000000..dcda296 --- /dev/null +++ b/ndct/core/device.py @@ -0,0 +1,86 @@ +import os + +from ndct.core.crypt import Crypt +from ndct.core.log import log +from ndct.core.paths import DB_PATH + +devices = [] + +class Device(): + def __init__(self, name, ip, username, password, os): + ''' + Takes: + name: Device name + ip: IP address of the device + user: Username to use for device connection + password: Password to use for device connection authentication + os: Operating system of the device + ''' + self.name = name + self.ip = ip + self.username = username + self.password = password + self.os = os + + def all(self): + ''' + Summary: + Gets the contents of a Device instance. + + Returns: + Device instance contents in dictionairy form + ''' + return self.__dict__ + + @staticmethod + def get_devices_from_file(): + ''' + Summary: + Gets device data from an encrypted file and creates an object stored in the devices list. + ''' + if os.path.isfile(DB_PATH + 'devices'): + devices_temp_file = Crypt.get_encrypted_file_contents('devices') + for device in devices_temp_file: + device_object = Device( + device['name'], + device['ip'], + device['username'], + device['password'], + device['os'] + ) + devices.append({device['name']: device_object}) + log('Got devices from file', 'info') + else: + log('No devices to get from file', 'info') + + @staticmethod + def save_devices_to_file(): + ''' + Summary: + Saves device data to an encrypted file. + ''' + devices_to_save = [] + + for device in devices: + for device_name, device_object in device.items(): + devices_to_save.append(device_object.all()) + + Crypt.create_encrypted_file('devices', devices_to_save) + + log('Saved devices to file', 'info') + + @staticmethod + def get_device_information(device_name): + ''' + Summary: + Gets the contents of a Device instance. + + Returns: + Device instance contents in dictionairy form. + ''' + for device in devices: + if device_name in device: + device_information = device[device_name].all() + return device_information + + return None diff --git a/ndct/core/device_metadata/._R1_metadata.yaml b/ndct/core/device_metadata/._R1_metadata.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dfe39fd9f9f620cdfc09103c0e84ab87fd00df52 GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#Kul533GxFg#;m$Sc{-3Y}c?M!F4xoqTo$DhG+63aiT%g##)#M z!~C#sVZLslGwebeU?E~$u5p8n>KXJXCII~_W$ODkfR;n`56z-zMNdvo2VAHgd|6u5 zH}gSk_O8ftCEdeOSfR^IOYpquI1GZoPbQup&QojlGW`04TPDB+m;e)C0!)AjFaajO z1egF5U;^(1>SMExhny(Q3be8?M;l8_jNM_TOG#&^(L$*+Dw&kHRYLFR7H(XBoj&(} Jtug|qs_uv>Dun<5 literal 0 HcmV?d00001 diff --git a/ndct/core/device_metadata/._R2_metadata.yaml b/ndct/core/device_metadata/._R2_metadata.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d73ae3ca515ace971e6dbe08a85d1705616124b3 GIT binary patch literal 4096 zcmeH~&kBM-5XQ$KqDwbBh3pfA2}~&?VcT2uWklo3=d#(#f<&r24Fc@|IjLmcKD=xI^cq}{bjMR z@8@=`i#ab#Ddwk!8?Ne132@!zA`F7SPdwKT*QwEk2){m|mIx35B0vO)01+SpM1Tko z0U|&Ih`>96?%3?&Aty?+iWyPq#>9dp+T<)#wZOAeD=y`kNs-j-D7nh<7Vcetoj&(} Jtuh0rZSN{eDt`a~ literal 0 HcmV?d00001 diff --git a/ndct/core/device_metadata/.___init__.py b/ndct/core/device_metadata/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d8900558becdbb5584bf37c9cf8d3cec14ec616b GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#Kul533GxF0|p_LSc{-3Y}c^C;JT}uP2o*EhG+63I?*7S##)#M z!~C#sVZLslGwebeU?Eamu5p94su}bsLV*62GWGo%K+B{0hh|B#q9^X`Ocbyj2oYD%LJGJ6JP>NfC(@GCcp%k z025#WOyHA1b!=Agl$Y%C0~&?VcT2uWklo3=d#(#f<&r24Fc@|IjLmcKF0R9dNU5e7lvC!Xtv>(uB%gkPUfO9Y4j5g-CYfCvx)B0vO) z01+SpMBtr3cWid?kQ1d@#f+$QV`9M)ZE}{WTHx8K6_@hNq)2LZlw9R_3-_+SPM`a~ JR+)j*ws%RDDuDn1 literal 0 HcmV?d00001 diff --git a/ndct/core/device_metadata/._vyos_example.yaml b/ndct/core/device_metadata/._vyos_example.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43813cfea91b1048ee6926f675c6535135daf32a GIT binary patch literal 4096 zcmeH~Jqp4=5QRrU#Kul533GxFg#;m$Sc{-3Y}c?M!F4xoBIr##hG+63aiT%g##)#M z!~C#sVZLslGwebeU?E~$u5p8n>KXJXCII~_W$ODkfR;n`56z-zMNdvo2VAHgd|6u5 zH}gSk_O8ftCEdeOSfR^IOYpquI1GZoPbQup&QojlGW`04TPDB+m;e)C0!)AjFaajO z1egF5U;^(1>SMExhny(Q3be8?M;l8_jNM_TOG#&^(L$*+Dw&kHRYLFR7H(XBoj&(} Jtug|qs_v(ADu)07 literal 0 HcmV?d00001 diff --git a/ndct/core/device_metadata/R1_metadata.yaml b/ndct/core/device_metadata/R1_metadata.yaml new file mode 100644 index 0000000..4efef2e --- /dev/null +++ b/ndct/core/device_metadata/R1_metadata.yaml @@ -0,0 +1,26 @@ +R1: + hostname: R1 + interfaces: + Loopback0: + description: Loopback + ip_address: 11.11.11.11 + subnet_mask: 255.255.255.255 + + bgp: + as: 65001 + peers: + R2: + neighbor_ip: 192.168.21.202 + neighbor_as: 65002 + networks: + network1: + subnet: 11.11.11.11 + subnet_mask: 255.255.255.255 + + ospf: + process: 1 + networks: + network1: + area: 0 + subnet: 192.168.21.0 + wildcard_mask: 0.0.0.255 \ No newline at end of file diff --git a/ndct/core/device_metadata/R2_metadata.yaml b/ndct/core/device_metadata/R2_metadata.yaml new file mode 100644 index 0000000..6cf1278 --- /dev/null +++ b/ndct/core/device_metadata/R2_metadata.yaml @@ -0,0 +1,25 @@ +R2: + hostname: R2 + interfaces: + lo: + description: Loopback + ip_address: 12.12.12.12 + subnet_mask: 32 + + bgp: + as: 65002 + peers: + R1: + neighbor_ip: 192.168.21.201 + neighbor_as: 65001 + networks: + network1: + subnet: 12.12.12.12 + subnet_mask: 32 + + ospf: + networks: + network1: + area: 0 + subnet: 192.168.21.0 + subnet_mask: 24 \ No newline at end of file diff --git a/ndct/core/device_metadata/__init__.py b/ndct/core/device_metadata/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/core/device_metadata/cisco_ios_example.yaml b/ndct/core/device_metadata/cisco_ios_example.yaml new file mode 100644 index 0000000..09dcab6 --- /dev/null +++ b/ndct/core/device_metadata/cisco_ios_example.yaml @@ -0,0 +1,26 @@ +ROUTER: + hostname: X + interfaces: + int1: + description: X + ip_address: X + subnet_mask: X + + bgp: + as: X + peers: + X: + neighbor_ip: X + neighbor_as: X + networks: + network1: + subnet: X + subnet_mask: X + + ospf: + process: X + networks: + network1: + area: X + subnet: X + wildcard_mask: X \ No newline at end of file diff --git a/ndct/core/device_metadata/vyos_example.yaml b/ndct/core/device_metadata/vyos_example.yaml new file mode 100644 index 0000000..25d9e9e --- /dev/null +++ b/ndct/core/device_metadata/vyos_example.yaml @@ -0,0 +1,25 @@ +ROUTER: + hostname: X + interfaces: + int1: + description: X + ip_address: X + subnet_mask: X + + bgp: + as: X + peers: + X: + neighbor_ip: X + neighbor_as: X + networks: + network1: + subnet: X + subnet_mask: X + + ospf: + networks: + network1: + area: X + subnet: X + subnet_mask: X \ No newline at end of file diff --git a/ndct/core/log.py b/ndct/core/log.py new file mode 100644 index 0000000..13806bb --- /dev/null +++ b/ndct/core/log.py @@ -0,0 +1,42 @@ +import logging +import sys +import os + +from datetime import datetime +from ndct.core.paths import LOGGING_PATH + +def log(log_message, level): + ''' + Summary: + Logs information to logfile at the specified level. + + Takes: + log_message: Information to log + level: Level of which to log the information at + ''' + logger = logging.getLogger('ndct-logger') + + log_message_types = { + 'debug': logger.debug, + 'info': logger.info, + 'warning': logger.warning, + 'error': logger.error, + 'critical': logger.critical + } + + if not logger.handlers: + logger.setLevel(logging.DEBUG) + + formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') + + file_handler = logging.FileHandler(LOGGING_PATH + 'log_' + datetime.now().strftime("%Y-%m-%d_%H:%M:%S") + '.log') + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(formatter) + + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.setFormatter(formatter) + + logger.addHandler(file_handler) + logger.addHandler(stream_handler) + + log_message_types[level](log_message) diff --git a/ndct/core/logs/.___init__.py b/ndct/core/logs/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..13e74b26fdd0a9b2611c9b18294c270e9617d2ba GIT binary patch literal 4096 zcmeH~&kBM-5XQ$KqDwbBh3pfA2}~&?VcT2uWklo3=d#(#f<&r24Fc@|IjLmcKAd-9dNU5e7lvC!Xtv>(uB%gkPUfO9Y4j5g-CYfCvx)B0vO) z01+SpMBtr3cWid?kQ1d@#f+$QV`9M)ZE}{WTHx8K6_@hNq)2LZlw9R_3-_+SPM`a~ JR+)j*ws-5ODtiC` literal 0 HcmV?d00001 diff --git a/ndct/core/logs/__init__.py b/ndct/core/logs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/core/paths.py b/ndct/core/paths.py new file mode 100644 index 0000000..d81f026 --- /dev/null +++ b/ndct/core/paths.py @@ -0,0 +1,19 @@ +import os + +current_path = os.path.dirname(__file__) + +#For compatability with any operating system +#KEY_PATH = current_path + '/db/key.key' +#MODULE_PATH = os.path.dirname(current_path) + '/modules/' +#CONFIG_PATH = current_path + '/configuration_files/' +#METADATA_PATH = current_path + '/device_metadata/' +#LOGGING_PATH = current_path + '/logs/' +#DB_PATH = current_path + '/db/' + +#For testing only +KEY_PATH = 'Documents/Python/NDCT/ndct/core/db/key.key' +MODULE_PATH = 'Documents/Python/NDCT/ndct/modules/' +CONFIG_PATH = 'Documents/Python/NDCT/ndct/core/configuration_files/' +METADATA_PATH = 'Documents/Python/NDCT/ndct/core/device_metadata/' +LOGGING_PATH = 'Documents/Python/NDCT/ndct/core/logs/' +DB_PATH = 'Documents/Python/NDCT/ndct/core/db/' \ No newline at end of file diff --git a/ndct/modules/.___init__.py b/ndct/modules/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ffc2a4f6dc85ec1e9450908fc5966303ee6cb9f0 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpU z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33iQ#VV@+ literal 0 HcmV?d00001 diff --git a/ndct/modules/._cisco_ios b/ndct/modules/._cisco_ios new file mode 100755 index 0000000000000000000000000000000000000000..5eae792d923f9401a9075a976ad0dd65f0d132e1 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSs z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33hCMk=)c literal 0 HcmV?d00001 diff --git a/ndct/modules/._vyos b/ndct/modules/._vyos new file mode 100755 index 0000000000000000000000000000000000000000..a1a70567665b6aa074bfbe00bd32a037eb448ed0 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xFpY z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33iw4=Swy literal 0 HcmV?d00001 diff --git a/ndct/modules/__init__.py b/ndct/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/modules/cisco_ios/.___init__.py b/ndct/modules/cisco_ios/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a54a5a89ae3160346f10ccceaf835e56d6291684 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSr z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33h=+A6sK literal 0 HcmV?d00001 diff --git a/ndct/modules/cisco_ios/._commands.json b/ndct/modules/cisco_ios/._commands.json new file mode 100644 index 0000000000000000000000000000000000000000..794283303cc4d0a2079391dbd2bb24d6fd2abaeb GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSp z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33iLBr3ZA literal 0 HcmV?d00001 diff --git a/ndct/modules/cisco_ios/._template.j2 b/ndct/modules/cisco_ios/._template.j2 new file mode 100644 index 0000000000000000000000000000000000000000..ecc9a597d73f4a93d488028ceceacab85c2a0fae GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSn z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33hhkSezT literal 0 HcmV?d00001 diff --git a/ndct/modules/cisco_ios/__init__.py b/ndct/modules/cisco_ios/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/modules/cisco_ios/commands.json b/ndct/modules/cisco_ios/commands.json new file mode 100644 index 0000000..9093d39 --- /dev/null +++ b/ndct/modules/cisco_ios/commands.json @@ -0,0 +1,7 @@ +{ + "commands": + { + "config": "show run", + "save_config": "copy run start" + } +} \ No newline at end of file diff --git a/ndct/modules/cisco_ios/template.j2 b/ndct/modules/cisco_ios/template.j2 new file mode 100644 index 0000000..bddfd24 --- /dev/null +++ b/ndct/modules/cisco_ios/template.j2 @@ -0,0 +1,28 @@ +hostname {{hostname}} +! +{% if interfaces %} +{% for interface, config in interfaces.items() %} +interface {{interface}} + description {{config['description']}} + ip address {{config['ip_address']}} {{config['subnet_mask']}} + no shutdown +! +{% endfor %} +{% endif %} +{% if bgp %} +router bgp {{bgp['as']}} +{% for peer, peer_config in bgp['peers'].items() %} + neighbor {{peer_config['neighbor_ip']}} remote-as {{peer_config['neighbor_as']}} +{% endfor %} +{% for network, network_config in bgp['networks'].items() %} + network {{network_config['subnet']}} mask {{network_config['subnet_mask']}} +{% endfor %} +{% endif %} +! +{% if ospf %} +router ospf {{ospf['process']}} +{% for network, network_config in ospf['networks'].items() %} + network {{network_config['subnet']}} {{network_config['wildcard_mask']}} area {{network_config['area']}} +{% endfor %} +{% endif %} +! \ No newline at end of file diff --git a/ndct/modules/vyos/.___init__.py b/ndct/modules/vyos/.___init__.py new file mode 100644 index 0000000000000000000000000000000000000000..95d311d70f3975fc79c03a85ed51a196945109a5 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSq z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33gYvMR9v literal 0 HcmV?d00001 diff --git a/ndct/modules/vyos/._commands.json b/ndct/modules/vyos/._commands.json new file mode 100644 index 0000000000000000000000000000000000000000..3e4b9439c3562b6f4bde414d89b9d9c1a5976cbd GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSo z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33g%{3^2m literal 0 HcmV?d00001 diff --git a/ndct/modules/vyos/._template.j2 b/ndct/modules/vyos/._template.j2 new file mode 100644 index 0000000000000000000000000000000000000000..5c3577a60a976319614820e4220be3cef166d122 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSm z(ZMhS$QDJ@!U*I;#YL0zbM+Dn3UX5Q3QH4<67xzj^HLcM3@i+-&CJY9(~JyEt(`N9 z@^e$I(F_^oj)uT!2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0CEU`I-4L2267=8 znZ*i4sm1xFMaiiOY57Ij3YmE&slaY$eqLfuPNhOlYFddxQchxCHn1-Y)itCkRR6=h KBEul}|33g3XezG& literal 0 HcmV?d00001 diff --git a/ndct/modules/vyos/__init__.py b/ndct/modules/vyos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ndct/modules/vyos/commands.json b/ndct/modules/vyos/commands.json new file mode 100644 index 0000000..45abd6c --- /dev/null +++ b/ndct/modules/vyos/commands.json @@ -0,0 +1,7 @@ +{ + "commands": + { + "config": "show configuration commands", + "save_config": ["commit", "save", "exit"] + } +} \ No newline at end of file diff --git a/ndct/modules/vyos/template.j2 b/ndct/modules/vyos/template.j2 new file mode 100644 index 0000000..5190831 --- /dev/null +++ b/ndct/modules/vyos/template.j2 @@ -0,0 +1,20 @@ +set system host-name '{{hostname}}' +{% if interfaces %} +{% for interface, interface_config in interfaces.items() %} +set interfaces {% if interface[0:2] == 'lo' %}loopback{% elif interface[0:3] == 'eth' %}ethernet{% endif %} {{interface}} address '{{interface_config['ip_address']}}/{{interface_config['subnet_mask']}}' +set interfaces {% if interface[0:2] == 'lo' %}loopback{% elif interface[0:3] == 'eth' %}ethernet{% endif %} {{interface}} description '{{interface_config['description']}}' +{% endfor %} +{% endif %} +{% if bgp %} +{% for peer, peer_config in bgp['peers'].items() %} +set protocols bgp {{bgp['as']}} neighbor {{peer_config['neighbor_ip']}} remote-as '{{peer_config['neighbor_as']}}' +{% endfor %} +{% for network, network_config in bgp['networks'].items() %} +set protocols bgp {{bgp['as']}} network '{{network_config['subnet']}}/{{network_config['subnet_mask']}}' +{% endfor %} +{% endif %} +{% if ospf %} +{% for network, network_config in ospf['networks'].items() %} +set protocols ospf area {{network_config['area']}} network '{{network_config['subnet']}}/{{network_config['subnet_mask']}}' +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c844969 --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +from setuptools import setup, find_packages + +setup( + name='ndct', + + # Information + version='1.0', + description='A configuration management tool for network device orchestration.', + long_description='A configuration management tool for network device orchestration through the use of deployments.', + + # Author details + author='Dominic James Macfarlane', + author_email='m021859g@student.staffs.ac.uk', + + # Packages + packages=find_packages(exclude=[""]), + + package_data={ + "": ["*.json", "*.j2", "*.yaml", "*.txt"], + }, + + # Dependencies + install_requires=['netmiko>=2.3.3', 'Jinja2>=2.10', 'cryptography>=2.6.1', 'Click>=7.0', 'PyYAML>=5.1.2', 'pythonping>=1.0.5'], + + # Entry points + entry_points={ + 'console_scripts': ['ndct = ndct.cli.main:main'] + } +) \ No newline at end of file