Updates to scripts after refactor

- Run black to reformat all the scripts
- Update makefile test targets, pylint, and fix some of the issues found
- Update pxeconfig script for refactored nbhelper
- Add start of inventory script

Change-Id: I5f426ac2da840dc72f07f8a6844e199e47d49135
diff --git a/scripts/nbhelper/__init__.py b/scripts/nbhelper/__init__.py
index 7755623..85acea5 100644
--- a/scripts/nbhelper/__init__.py
+++ b/scripts/nbhelper/__init__.py
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: © 2021 Open Networking Foundation <support@opennetworking.org>
+# SPDX-License-Identifier: Apache-2.0
+
 from .utils import initialize
 from .tenant import Tenant
 from .device import Device, VirtualMachine
diff --git a/scripts/nbhelper/container.py b/scripts/nbhelper/container.py
index 397ee32..0e728a1 100644
--- a/scripts/nbhelper/container.py
+++ b/scripts/nbhelper/container.py
@@ -1,4 +1,5 @@
 import netaddr
+from .utils import logger
 
 
 class Singleton(type):
@@ -10,7 +11,7 @@
         return cls._instances[cls]
 
 
-class Container(object):
+class Container:
     def __init__(self):
         self.instances = dict()
 
@@ -36,16 +37,19 @@
         for instance in self.instances.values():
             if "dns" in list(map(str, instance.services)):
                 return instance
+        return None
 
     def getDHCPServer(self):
         for instance in self.instances.values():
             if "tftp" in list(map(str, instance.services)):
                 return instance
+        return None
 
     def getNTPServer(self):
         for instance in self.instances.values():
             if "ntp" in list(map(str, instance.services)):
                 return instance
+        return None
 
     def getRouters(self):
         """ Get a list of Devices/VMs which type is Router """
@@ -72,6 +76,7 @@
                         prefix.subnet
                     ):
                         return str(netaddr.IPNetwork(address).ip)
+        return None
 
 
 class DeviceContainer(AssignedObjectContainer, metaclass=Singleton):
@@ -87,9 +92,6 @@
 class PrefixContainer(Container, metaclass=Singleton):
     # PrefixContainer holds all prefixes fetch from Netbox, prefix(str) as key
 
-    def get(self, instance_id, name_segments=1):
-        return super().get(instance_id)
-
     def all(self):
         return self.instances.values()
 
@@ -221,7 +223,8 @@
                             #         "eno1": {
                             #             "mgmtOnly": False,
                             #             "macaddr": "ca:fe:ba:be:11:11",
-                            #             "ipaddr": [IPAddress("10.32.4.129"), IPAddress("10.32.4.130")]
+                            #             "ipaddr": [IPAddress("10.32.4.129"),
+                            #                        IPAddress("10.32.4.130")]
                             #         }
                             #    }
                             #  "mgmtswitch1": ...
@@ -242,14 +245,24 @@
                             ]
                             interfaceDict.setdefault("mgmtOnly", False)
 
-                            # Use interface["mac_address"] as the default value, but if the mac_address
-                            #  is None, that means we are dealing with a virtual interfaces
-                            #  so we can get the linked interface's mac_address instead
+                            # Use interface["mac_address"] as the default value, but if the
+                            # mac_address is None, that means we are dealing with a virtual
+                            # interfaces so we can get the linked interface's mac_address instead
 
-                            interfaceDict.setdefault(
-                                "mac_address", interface["mac_address"] or
-                                device.interfaces[interface["instance"].label]["mac_address"]
-                            )
+                            try:
+                                interfaceDict.setdefault(
+                                    "mac_address",
+                                    interface["mac_address"]
+                                    or device.interfaces[interface["instance"].label][
+                                        "mac_address"
+                                    ],
+                                )
+                            except KeyError:
+                                logger.error(
+                                    "Problem with MAC address on interface %s",
+                                    interface,
+                                )
+
                             interfaceDict.setdefault("ip_addresses", list())
                             interfaceDict["ip_addresses"].append(address)
 
diff --git a/scripts/nbhelper/device.py b/scripts/nbhelper/device.py
index 94b03df..32a2075 100644
--- a/scripts/nbhelper/device.py
+++ b/scripts/nbhelper/device.py
@@ -4,12 +4,11 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # device.py
-#
 
+import sys
 import netaddr
 
-from .utils import logger, clean_name_dns
-from .network import Prefix
+from .utils import logger
 from .container import DeviceContainer, VirtualMachineContainer, PrefixContainer
 
 
@@ -40,7 +39,7 @@
     objects = dict()
 
     def __init__(self, data):
-        from .utils import netboxapi, netbox_config
+        from .utils import netboxapi
 
         self.data = data
         self.nbapi = netboxapi
@@ -49,6 +48,7 @@
         self.id = self.data.id
         self.tenant = None
         self.primary_ip = None
+        self.primary_iface = None
 
         # In Netbox, we use FQDN as the Device name, but in the script,
         # we use the first segment to be the name of device.
@@ -108,8 +108,12 @@
             # ipam.ip_addresses doesn't have primary tag,
             # the primary tag is only available is only in the Device.
             # So we need to compare address to check which one is primary ip
-            if address.address == self.primary_ip.address:
-                interface["isPrimary"] = True
+            try:
+                if address.address == self.primary_ip.address:
+                    interface["isPrimary"] = True
+                    self.primary_iface = interface
+            except AttributeError:
+                logger.error("Error with primary address for device %s", self.fullname)
 
             # mgmt_only = False is a hack for VirtualMachine type
             if self.__class__ == VirtualMachine:
@@ -181,7 +185,7 @@
                 self.netplan_config["ethernets"].setdefault(intfName, {})
                 self.netplan_config["ethernets"][intfName].setdefault(
                     "addresses", []
-                ).append(address)
+                ).extend(interface["addresses"])
 
         # If the current selected device is a Server
         elif isinstance(self, Device) and self.data.device_role.name == "Server":
@@ -233,7 +237,9 @@
                     for dest_addr in destination.split(","):
 
                         # If interface address is in destination subnet, we don't need this route
-                        if netaddr.IPNetwork(address).ip in netaddr.IPNetwork(dest_addr):
+                        if netaddr.IPNetwork(address).ip in netaddr.IPNetwork(
+                            dest_addr
+                        ):
                             continue
 
                         new_route = {
@@ -298,7 +304,8 @@
                 }
             )
 
-        # Only management server needs to be configured the whitelist netrange of internal interface
+        # Only management server needs to be configured the whitelist netrange of
+        # internal interface
         if self.data.device_role.name == "Router":
 
             ret["interface_subnets"] = dict()
@@ -329,8 +336,13 @@
                         if prefix.subnet not in ret["interface_subnets"][intfName]:
                             ret["interface_subnets"][intfName].append(prefix.subnet)
                         for neighbor in prefix.neighbor:
-                            if neighbor.subnet not in ret["interface_subnets"][intfName]:
-                                ret["interface_subnets"][intfName].append(neighbor.subnet)
+                            if (
+                                neighbor.subnet
+                                not in ret["interface_subnets"][intfName]
+                            ):
+                                ret["interface_subnets"][intfName].append(
+                                    neighbor.subnet
+                                )
 
             for prefix in PrefixContainer().all():
 
@@ -361,8 +373,6 @@
         if self.extra_config:
             return self.extra_config
 
-        primary_ip = self.data.primary_ip.address if self.data.primary_ip else None
-
         service_names = list(map(lambda x: x.name, self.services))
 
         if "dns" in service_names:
diff --git a/scripts/nbhelper/network.py b/scripts/nbhelper/network.py
index 3a51219..9b5d824 100644
--- a/scripts/nbhelper/network.py
+++ b/scripts/nbhelper/network.py
@@ -10,7 +10,6 @@
 
 from .utils import logger, check_name_dns
 from .container import PrefixContainer
-from .container import DeviceContainer, VirtualMachineContainer
 
 
 class Prefix:
@@ -71,7 +70,6 @@
                     if self not in prefix.neighbor:
                         prefix.neighbor.append(self)
 
-
     def build_prefix(self):
         """
         find ip information for items (devices/vms, reserved_ips, dhcp_range) in prefix
diff --git a/scripts/nbhelper/service.py b/scripts/nbhelper/service.py
index 1ad992c..fe78a9f 100644
--- a/scripts/nbhelper/service.py
+++ b/scripts/nbhelper/service.py
@@ -4,12 +4,9 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # service.py
-#
 
-import re
 import netaddr
 
-from .utils import logger, AttrDict
 from .container import ServiceInfoContainer
 
 
@@ -66,7 +63,7 @@
             for host in domain["hosts"]:
                 if ip_address == host["ip_addr"]:
                     return f"{host['name']}.{domainName}."
-
+        return ""
 
     dnsForwardZoneConfigs = dict()
 
@@ -91,7 +88,9 @@
         if ntpServer:
             forwardZoneConfig["cname"]["ntp"] = getDomainNameByIP(ntpServer["address"])
         if dhcpServer:
-            forwardZoneConfig["cname"]["tftp"] = getDomainNameByIP(dhcpServer["address"])
+            forwardZoneConfig["cname"]["tftp"] = getDomainNameByIP(
+                dhcpServer["address"]
+            )
         if dnsServer:
             forwardZoneConfig["cname"]["ns"] = getDomainNameByIP(dnsServer["address"])
             forwardZoneConfig["ns"].append(getDomainNameByIP(dnsServer["address"]))
@@ -126,7 +125,7 @@
         if o1 == 10:
             o2 = o3 = o4 = 0
             cidr_plen = 8
-        elif (o1 == 172 and o2 >= 16 and o2 <= 31) or (o1 == 192 and o2 == 168):
+        elif (o1 == 172 and 16 <= o2 <= 31) or (o1 == 192 and o2 == 168):
             o3 = o4 = 0
             cidr_plen = 16
 
diff --git a/scripts/nbhelper/tenant.py b/scripts/nbhelper/tenant.py
index 4d529a7..a4d4048 100644
--- a/scripts/nbhelper/tenant.py
+++ b/scripts/nbhelper/tenant.py
@@ -6,7 +6,9 @@
 # tenant.py
 # The tenant abstract object of Netbox Object - Tenant
 
-from .utils import logger, netboxapi
+import sys
+
+from .utils import logger
 from .device import Device, VirtualMachine
 from .network import Prefix
 
@@ -58,9 +60,9 @@
         """
 
         for machine in self.devices + self.vms:
-            if name and machine.name == name:
-                return machine
-            elif machine.data["device_role"]["name"] == "Router":
+            if (name and machine.name == name) or machine.data["device_role"][
+                "name"
+            ] == "Router":
                 return machine
 
         ret_msg = (
@@ -71,10 +73,12 @@
         logger.error(ret_msg, name)
         sys.exit(1)
 
-    def get_devices(self, device_types=["server", "router"]):
+    def get_devices(self, device_types=None):
         """
         Get all devices (Router + Server) belong to this Tenant
         """
+        if not device_types:
+            device_types = ["server", "router"]
 
         if not device_types:
             return self.devices + self.vms
diff --git a/scripts/nbhelper/utils.py b/scripts/nbhelper/utils.py
index 5614ba9..d44bc96 100644
--- a/scripts/nbhelper/utils.py
+++ b/scripts/nbhelper/utils.py
@@ -6,11 +6,13 @@
 # utils.py
 # The utility functions shared among nbhelper objects
 
-import re
-import logging
 import argparse
-import pynetbox
+import logging
+import re
+import sys
+
 import requests
+import pynetbox
 
 from ruamel import yaml
 
@@ -36,7 +38,7 @@
 
     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)
+            logger.error("The require argument: %s was not set. Stop.", require_args)
             sys.exit(1)
 
     netboxapi = pynetbox.api(
@@ -53,7 +55,7 @@
     return args
 
 
-def parse_cli_args(extra_args={}):
+def parse_cli_args(extra_args):
     """
     parse CLI arguments.  Can add extra arguments with a option:kwargs dict
     """
@@ -115,5 +117,5 @@
 
 class AttrDict(dict):
     def __init__(self, *args, **kwargs):
-        super(AttrDict, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.__dict__ = self