Refactor nbhelper

Change-Id: I69d10d164fac3eb319e072447a520905880c31dd
diff --git a/scripts/nbhelper/utils.py b/scripts/nbhelper/utils.py
new file mode 100644
index 0000000..5614ba9
--- /dev/null
+++ b/scripts/nbhelper/utils.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+
+# SPDX-FileCopyrightText: © 2021 Open Networking Foundation <support@opennetworking.org>
+# SPDX-License-Identifier: Apache-2.0
+
+# utils.py
+# The utility functions shared among nbhelper objects
+
+import re
+import logging
+import argparse
+import pynetbox
+import requests
+
+from ruamel import yaml
+
+# Initialize module-level variables
+netboxapi = None
+netbox_config = None
+netbox_version = None
+
+# create shared logger
+logging.basicConfig()
+logger = logging.getLogger("nbh")
+
+
+def initialize(extra_args):
+    """
+    Initialize the NBHelper module, the extra_args is required, it contains the
+    NetBox API url, API token, and the name of site
+    """
+    global netboxapi, netbox_config, netbox_version
+
+    args = parse_cli_args(extra_args)
+    netbox_config = yaml.safe_load(args.settings.read())
+
+    for require_args in ["api_endpoint", "token", "tenant_name"]:
+        if not netbox_config.get(require_args):
+            logger.error("The require argument: %s was not set. Stop." % require_args)
+            sys.exit(1)
+
+    netboxapi = pynetbox.api(
+        netbox_config["api_endpoint"], token=netbox_config["token"], threading=True,
+    )
+
+    if not netbox_config.get("validate_certs", True):
+        session = requests.Session()
+        session.verify = False
+        netboxapi.http_session = session
+
+    netbox_version = netboxapi.version
+
+    return args
+
+
+def parse_cli_args(extra_args={}):
+    """
+    parse CLI arguments.  Can add extra arguments with a option:kwargs dict
+    """
+
+    parser = argparse.ArgumentParser(description="Netbox")
+
+    # Positional args
+    parser.add_argument(
+        "settings",
+        type=argparse.FileType("r"),
+        help="YAML Ansible inventory file w/NetBox API token",
+    )
+
+    parser.add_argument(
+        "--debug", action="store_true", help="Print additional debugging information"
+    )
+
+    for ename, ekwargs in extra_args.items():
+        parser.add_argument(ename, **ekwargs)
+
+    args = parser.parse_args()
+    log_level = logging.DEBUG if args.debug else logging.INFO
+    logger.setLevel(log_level)
+
+    return args
+
+
+def check_name_dns(name):
+
+    badchars = re.search("[^a-z0-9.-]", name.lower(), re.ASCII)
+
+    if badchars:
+        logger.error(
+            "DNS name '%s' has one or more invalid characters: '%s'",
+            name,
+            badchars.group(0),
+        )
+        sys.exit(1)
+
+    return name.lower()
+
+
+def clean_name_dns(name):
+    return re.sub("[^a-z0-9.-]", "-", name.lower(), 0, re.ASCII)
+
+
+def apply_as_router(output_yaml):
+    """ Add the router-specific value for output structure """
+
+    output_yaml.setdefault("netprep_router", True)
+    output_yaml.setdefault("tftpd_files", ["undionly.kpxe"])
+    output_yaml.setdefault(
+        "vhosts", [{"name": "default", "default_server": True, "autoindex": True}]
+    )
+    output_yaml.setdefault("acme_username", "www-data")
+
+    return output_yaml
+
+
+class AttrDict(dict):
+    def __init__(self, *args, **kwargs):
+        super(AttrDict, self).__init__(*args, **kwargs)
+        self.__dict__ = self