Skip to content

Commit

Permalink
Update for Nautobot API 2.0 (#105)
Browse files Browse the repository at this point in the history
Update for Nautobot API 2.0
  • Loading branch information
pszulczewski authored Sep 14, 2023
1 parent 1964295 commit 6b298cc
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 160 deletions.
37 changes: 22 additions & 15 deletions docs/inventory/inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,34 @@ With the Nornir Nautobot Inventory plugin you have the option of using all of th
## Filtering Examples

For each of the examples the sites correspond to the first three letters of the devices below:
For each of the examples the locations correspond to the first three letters of the devices below:

```python
>>> nautobot.dcim.devices.all()
[den-rtr01, den-rtr02, grb-rtr01, msp-rtr01, msp-rtr02, nyc-rtr01, nyc-rtr02]
```

### Filtering Example: Select from single site
### Filtering Example: Select from single location

To filter from a single location you can use the filter location to get the devices at a single location based on the **Primary Key** for the location:

> NOTE: Location names do not guarantee uniqueness in Nautobot 2.0.
>
> See this document for more information:
>
> https://docs.nautobot.com/projects/core/en/next/development/apps/api/platform-features/uniquely-identify-objects/
To filter from a single site you can use the filter site to get the devices at a single site based on the **slug** for the site:

```python
site = "msp"
location = "db913e3b-cbe0-4463-addc-816ba6a20100"

my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site": site},
"filter_parameters": {"location": location},
"ssl_verify": False,
},
},
Expand All @@ -73,26 +80,26 @@ print(my_nornir.inventory.hosts.keys())
This results in:

```
root@2e8168a1c3e7:/local# python examples/filter_site.py
root@2e8168a1c3e7:/local# python examples/filter_location.py
Hosts found: 2
dict_keys(['msp-rtr01', 'msp-rtr02'])
```


### Filter Example: Multiple Sites
### Filter Example: Multiple Locations

To search within multiple sites, pass a list of site slugs. In the example below, it is the same as the previous example with a list passed in instead of a single string.
To search within multiple locations, pass a list of location Primary Keys. In the example below, it is the same as the previous example with a list passed in instead of a single string.

```python
site = ["msp", "grb"]
location = ["db913e3b-cbe0-4463-addc-816ba6a20100", "6f09aa66-96be-4b4d-955a-9c98e488f0e6"]

This comment has been minimized.

Copy link
@itdependsnetworks

itdependsnetworks Sep 14, 2023

Contributor

It's not clear if this is the only way this works, but if it is this is not a proper user experience imho. I think we need to think about the 80% solution, where location will be set unique to what the makes sense in their environment vs the 100% that is void of user experience.


my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site": site},
"filter_parameters": {"location": location},
"ssl_verify": False,
},
},
Expand All @@ -106,25 +113,25 @@ print(my_nornir.inventory.hosts.keys())
Results in:

```
root@2e8168a1c3e7:/local# python examples/filter_multiple_sites.py
root@2e8168a1c3e7:/local# python examples/filter_multiple_locations.py
Hosts found: 3
dict_keys(['grb-rtr01', 'msp-rtr01', 'msp-rtr02'])
```

### Filtering Example: Not at a site
### Filtering Example: Not at a location

The negative filters also are supported. These are all of the filters possible. Here we will search for devices **not** at _MSP_:

```python
not_site = "msp"
not_location = "db913e3b-cbe0-4463-addc-816ba6a20100"

my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site__n": not_site},
"filter_parameters": {"location__n": not_location},
"ssl_verify": False,
},
},
Expand All @@ -138,7 +145,7 @@ print(my_nornir.inventory.hosts.keys())
Results in:

```
root@2e8168a1c3e7:/local# python examples/filter_negate_site.py
root@2e8168a1c3e7:/local# python examples/filter_negate_location.py
Hosts found: 5
dict_keys(['den-rtr01', 'den-rtr02', 'grb-rtr01', 'nyc-rtr01', 'nyc-rtr02'])
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ def hello_world(task: Task) -> Result:

def main():
"""Nornir testing."""
site = ["msp", "grb"]
location = "db913e3b-cbe0-4463-addc-816ba6a20100"

my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site": site},
"filter_parameters": {"location": location},
"ssl_verify": False,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ def hello_world(task: Task) -> Result:

def main():
"""Nornir testing."""
site = "msp"
location = ["db913e3b-cbe0-4463-addc-816ba6a20100", "6f09aa66-96be-4b4d-955a-9c98e488f0e6"]

my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site": site},
"filter_parameters": {"location": location},
"ssl_verify": False,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ def hello_world(task: Task) -> Result:

def main():
"""Nornir testing."""
not_site = "msp"
not_location = "db913e3b-cbe0-4463-addc-816ba6a20100"

my_nornir = InitNornir(
inventory={
"plugin": "NautobotInventory",
"options": {
"nautobot_url": os.getenv("NAUTOBOT_URL"),
"nautobot_token": os.getenv("NAUTBOT_TOKEN"),
"filter_parameters": {"site__n": not_site},
"filter_parameters": {"location__n": not_location},
"ssl_verify": False,
},
},
Expand Down
13 changes: 9 additions & 4 deletions nornir_nautobot/plugins/inventory/nautobot.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@


def _set_host(data: Dict[str, Any], name: str, groups, host, defaults: Defaults) -> Host:
host_platform = getattr(data["pynautobot_object"].platform, "slug", None)
host_platform = getattr(data["pynautobot_object"].platform, "network_driver", None)
connection_option = {}
for key, value in data.get("connection_options", {}).items():
connection_option[key] = ConnectionOptions(
Expand Down Expand Up @@ -106,6 +106,7 @@ def pynautobot_obj(self) -> pynautobot.core.api.Api:
"""
if self._pynautobot_obj is None:
self._pynautobot_obj = pynautobot.api(self.nautobot_url, token=self.nautobot_token)
self.api_session.params = {"depth": 1}
self._pynautobot_obj.http_session = self.api_session

return self._pynautobot_obj
Expand All @@ -120,8 +121,8 @@ def devices(self) -> list:
else:
try:
self._devices = self.pynautobot_obj.dcim.devices.filter(**self.filter_parameters)
except pynautobot.core.query.RequestError:
print("Error in the query filters. Please verify the parameters.")
except pynautobot.core.query.RequestError as err:
print(f"Error in the query filters: {err.error}. Please verify the parameters.")
sys.exit(1)

return self._devices
Expand Down Expand Up @@ -151,7 +152,11 @@ def load(self) -> Inventory:

# Add Primary IP address, if found. Otherwise add hostname as the device name
host["hostname"] = (
str(ipaddress.IPv4Interface(device.primary_ip.address).ip) if device["primary_ip"] else device["name"]
str(ipaddress.IPv4Interface(device.primary_ip4.address).ip)
if device["primary_ip4"]
else str(ipaddress.IPv6Interface(device.primary_ip6.address).ip)
if device["primary_ip6"]
else device["name"]
)
host["name"] = device.name or str(device.id)
host["groups"] = []
Expand Down
5 changes: 3 additions & 2 deletions nornir_nautobot/plugins/tasks/dispatcher/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def compliance_config(
features (dict): A dictionary describing the configurations required.
backup_file (str): The file location of where the back configuration should be saved.
intended_file (str): The file location of where the intended configuration should be saved.
platform (str): The platform slug of the device.
platform (str): The platform network_driver of the device.
Returns:
Result: Nornir Result object with a feature_data key of the compliance data.
Expand Down Expand Up @@ -268,7 +268,8 @@ def get_config(
if result[0].failed:
# TODO: investigate this, is there a better way to handle? recursive function?
logger.log_error(
f"`get_config` nornir task failed with an unexpected issue: `{str(result.exception)}`", extra={"object": obj}
f"`get_config` nornir task failed with an unexpected issue: `{str(result.exception)}`",
extra={"object": obj},
)
return result

Expand Down
12 changes: 6 additions & 6 deletions nornir_nautobot/utils/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,32 @@ def __init__(self, name: str, nautobot_job=None, debug: bool = False, job_result
self.debug = debug
self.nautobot_job = nautobot_job or job_result

def log_debug(self, message: str, extra: Any=None):
def log_debug(self, message: str, extra: Any = None):
"""Debug, does not take obj, and only logs to jobs result when in global debug mode."""
if self.nautobot_job and self.debug:
self.nautobot_job.logging.debug(message, extra=extra)
self.logger.debug(message)

def log_info(self, message: str, extra: Any=None):
def log_info(self, message: str, extra: Any = None):
"""Log to Python logger and jogs results for info messages."""
if self.nautobot_job:
self.nautobot_job.logging.info(message, extra=extra)
self.logger.info("%s | %s", str(extra), message)

def log_warning(self, message: str, extra: Any=None):
def log_warning(self, message: str, extra: Any = None):
"""Log to Python logger and jogs results for warning messages."""
if self.nautobot_job:
self.nautobot_job.logging.warning(message, extra=extra)
self.logger.warning("%s | %s", str(extra), message)

def log_error(self, message: str, extra: Any=None):
def log_error(self, message: str, extra: Any = None):
"""Log to Python logger and jogs results for error messages."""
if self.nautobot_job:
self.nautobot_job.logging.error(message, extra=extra)
self.logger.error("%s | %s", str(extra), message)

def log_critical(self, message: str, extra: Any=None):
def log_critical(self, message: str, extra: Any = None):
"""Log to Python logger and jogs results for critical messages."""
if self.nautobot_job:
self.nautobot_job.logging.critical(message, extra=extra)
self.logger.critical("%s | %s", str(extra), message)
self.logger.critical("%s | %s", str(extra), message)
Loading

0 comments on commit 6b298cc

Please sign in to comment.