From 4fca4a7fb4d703ac80c99bc73c79bfc41a18937d Mon Sep 17 00:00:00 2001 From: "Daniel W. Anner" Date: Fri, 3 Mar 2023 16:03:17 -0500 Subject: [PATCH] Cleanup (#75) * - Added exception handler function with easy map dictionary. - Added extra default values for ENV vars. - Moved parser args to Settings for global use. - Started implementing new exception handler - Moved git functions to gitcmd.py. - Implemented exception handler for git functions. - Removed extra imports where no longer needed. * Added how to fix ssl error in case it pops up * - Removed circular import of settings.py and gitcmd.py - Added exception_handler.py to handle exceptions (refer to above) - Made GitCMD a class with init and run methods - Removed git completely from nb-dt-import.py * Fixed missing arg * made exception handler a class to prevent circular import --- exception_handler.py | 27 ++++++++++++++++++++++ gitcmd.py | 46 ++++++++++++++++++++++++++++++++++++++ nb-dt-import.py | 53 +++++--------------------------------------- settings.py | 33 ++++++++++++++++++++++----- 4 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 exception_handler.py create mode 100644 gitcmd.py diff --git a/exception_handler.py b/exception_handler.py new file mode 100644 index 0000000..3bc0da4 --- /dev/null +++ b/exception_handler.py @@ -0,0 +1,27 @@ +from sys import exit as system_exit + + + + + +class ExceptionHandler: + def __new__(cls, *args, **kwargs): + return super().__new__(cls) + + def __init__(self, args): + self.args = args + + def exception(self, exception_type, exception, stack_trace=None): + exception_dict = { + "EnvironmentError": f'Environment variable "{exception}" is not set.', + "SSLError": f'SSL verification failed. IGNORE_SSL_ERRORS is {exception}. Set IGNORE_SSL_ERRORS to True if you want to ignore this error. EXITING.', + "GitCommandError": f'The repo "{exception}" is not a valid git repo.', + "GitInvalidRepositoryError": f'The repo "{exception}" is not a valid git repo.', + "Exception": f'An unknown error occurred: "{exception}"' + } + + if self.args.verbose and stack_trace: + print(stack_trace) + print(exception_dict[exception_type]) + system_exit(1) + diff --git a/gitcmd.py b/gitcmd.py new file mode 100644 index 0000000..0a6e05b --- /dev/null +++ b/gitcmd.py @@ -0,0 +1,46 @@ +from git import Repo, exc +from sys import exit as system_exit +import settings +import os + +class GitCMD: + def __new__(cls, *args, **kwargs): + return super().__new__(cls) + + def __init__(self, args, repo_path): + self.url = args.url + self.repo_path = repo_path + self.branch = args.branch + self.repo = Repo() + self.cwd = os.getcwd() + + if os.path.isdir(self.repo_path): + self.pull_repo() + else: + self.clone_repo() + + + def pull_repo(self): + try: + print("Package devicetype-library is already installed, " + + f"updating {os.path.join(self.cwd, self.repo_path)}") + self.repo = Repo(self.repo_path) + if not self.repo.remotes.origin.url.endswith('.git'): + settings.handle.exception("GitInvalidRepositoryError", self.repo.remotes.origin.url, f"Origin URL {self.repo.remotes.origin.url} does not end with .git") + self.repo.remotes.origin.pull() + self.repo.git.checkout(self.branch) + print(f"Pulled Repo {self.repo.remotes.origin.url}") + except exc.GitCommandError as git_error: + settings.handle.exception("GitCommandError", self.repo.remotes.origin.url, git_error) + except Exception as git_error: + settings.handle.exception("Exception", 'Git Repository Error', git_error) + + def clone_repo(self): + try: + self.repo = Repo.clone_from(self.url, os.path.join(self.cwd, self.repo_path), branch=self.branch) + print(f"Package Installed {self.repo.remotes.origin.url}") + except exc.GitCommandError as git_error: + settings.handle.exception("GitCommandError", self.url, git_error) + except Exception as git_error: + settings.handle.exception("Exception", 'Git Repository Error', git_error) + diff --git a/nb-dt-import.py b/nb-dt-import.py index f0ef8e1..7cfa93d 100755 --- a/nb-dt-import.py +++ b/nb-dt-import.py @@ -1,17 +1,14 @@ #!/usr/bin/env python3 -from git import Repo, exc, RemoteProgress from collections import Counter from datetime import datetime import yaml import pynetbox import glob -import argparse import os -import settings -import sys import re import requests +import settings counter = Counter( added=0, @@ -44,15 +41,7 @@ def determine_features(nb): if nb_ver[0] > 3 or (nb_ver[0] == 3 and nb_ver[1] >= 2): settings.NETBOX_FEATURES['modules'] = True -def update_package(path: str, branch: str): - try: - repo = Repo(path) - if repo.remotes.origin.url.endswith('.git'): - repo.remotes.origin.pull() - repo.git.checkout(branch) - print(f"Pulled Repo {repo.remotes.origin.url}") - except exc.InvalidGitRepositoryError: - pass + def slugFormat(name): @@ -769,25 +758,7 @@ def main(): cwd = os.getcwd() startTime = datetime.now() - - VENDORS = settings.VENDORS - REPO_URL = settings.REPO_URL - - SLUGS = settings.SLUGS - REPO_BRANCH = settings.REPO_BRANCH - - parser = argparse.ArgumentParser(description='Import Netbox Device Types') - parser.add_argument('--vendors', nargs='+', default=VENDORS, - help="List of vendors to import eg. apc cisco") - parser.add_argument('--url', '--git', default=REPO_URL, - help="Git URL with valid Device Type YAML files") - parser.add_argument('--slugs', nargs='+', default=SLUGS, - help="List of device-type slugs to import eg. ap4431 ws-c3850-24t-l") - parser.add_argument('--branch', default=REPO_BRANCH, - help="Git branch to use from repo") - parser.add_argument('--verbose', action='store_true', - help="Print verbose output") - args = parser.parse_args() + args = settings.args nbUrl = settings.NETBOX_URL nbToken = settings.NETBOX_TOKEN @@ -795,28 +766,14 @@ def main(): try: determine_features(nb) - except requests.exceptions.SSLError as e: - if args.verbose: - print(e) + except requests.exceptions.SSLError as ssl_exception: if not settings.IGNORE_SSL_ERRORS: - print("IGNORE_SSL_ERRORS is False. SSL verification failed, exiting.") - sys.exit(1) + settings.handle.exception("SSLError", settings.IGNORE_SSL_ERRORS, ssl_exception) print("IGNORE_SSL_ERRORS is True, catching exception and disabling SSL verification.") requests.packages.urllib3.disable_warnings() nb.http_session.verify = False determine_features(nb) - try: - if os.path.isdir('./repo'): - print(f"Package devicetype-library is already installed, " - + f"updating {os.path.join(cwd, 'repo')}") - update_package('./repo', branch=args.branch) - else: - repo = Repo.clone_from(args.url, os.path.join(cwd, 'repo'), branch=args.branch) - print(f"Package Installed {repo.remotes.origin.url}") - except exc.GitCommandError as error: - print("Couldn't clone {} ({})".format(args.url, error)) - if not args.vendors: print("No Vendors Specified, Gathering All Device-Types") files, vendors = getFiles() diff --git a/settings.py b/settings.py index bcbb855..4a4ef56 100644 --- a/settings.py +++ b/settings.py @@ -1,12 +1,18 @@ +from argparse import ArgumentParser +from sys import exit as system_exit import os +from exception_handler import ExceptionHandler +from gitcmd import GitCMD from dotenv import load_dotenv load_dotenv() -REPO_URL = os.getenv("REPO_URL") -REPO_BRANCH = os.getenv("REPO_BRANCH", "master") +REPO_URL = os.getenv("REPO_URL", + default="https://github.com/netbox-community/devicetype-library.git") +REPO_BRANCH = os.getenv("REPO_BRANCH", default="master") NETBOX_URL = os.getenv("NETBOX_URL") NETBOX_TOKEN = os.getenv("NETBOX_TOKEN") -IGNORE_SSL_ERRORS = (os.getenv("IGNORE_SSL_ERRORS", "False") == "True") +IGNORE_SSL_ERRORS = (os.getenv("IGNORE_SSL_ERRORS", default="False") == "True") +REPO_PATH = "./repo" # optionally load vendors through a comma separated list as env var VENDORS = list(filter(None, os.getenv("VENDORS", "").split(","))) @@ -18,8 +24,25 @@ NETBOX_FEATURES = { 'modules': False, } -MANDATORY_ENV_VARS = ["REPO_URL", "NETBOX_URL", "NETBOX_TOKEN"] +parser = ArgumentParser(description='Import Netbox Device Types') +parser.add_argument('--vendors', nargs='+', default=VENDORS, + help="List of vendors to import eg. apc cisco") +parser.add_argument('--url', '--git', default=REPO_URL, + help="Git URL with valid Device Type YAML files") +parser.add_argument('--slugs', nargs='+', default=SLUGS, + help="List of device-type slugs to import eg. ap4431 ws-c3850-24t-l") +parser.add_argument('--branch', default=REPO_BRANCH, + help="Git branch to use from repo") +parser.add_argument('--verbose', action='store_true', default=False, + help="Print verbose output") +args = parser.parse_args() + +handle = ExceptionHandler(args) +# Evaluate environment variables and exit if one of the mandatory ones are not set +MANDATORY_ENV_VARS = ["REPO_URL", "NETBOX_URL", "NETBOX_TOKEN"] for var in MANDATORY_ENV_VARS: if var not in os.environ: - raise EnvironmentError("Failed because {} is not set.".format(var)) + handle.exception("EnvironmentError", var, f'Environment variable "{var}" is not set.\n\nMANDATORY_ENV_VARS: {str(MANDATORY_ENV_VARS)}.\n\nCURRENT_ENV_VARS: {str(os.environ)}') + +git_repo = GitCMD(args, REPO_PATH)