As the title suggests, this post will use Nautobot for something other than a Network Source of Truth. This post will use Nautobot to solve one of life’s most difficult questions, perhaps one of the most frequently asked questions. This question is known to ruin the evening, but with Nautobot’s help, this no longer has to be the case: The question Nautobot will answer for us is, “What’s for dinner?”
This post will walk through using one new feature introduced in Nautobot version 1.1 and one existing feature, in order to have each Site in Nautobot provide a recipe recommendation. The two features are independent, but often work hand in hand to provide dynamic data to a specific entry in Nautobot. The primary feature that will provide the Site with a recipe is Custom Links. The Custom Link will make use of a Custom Jinja2 Filter in order to generate a recommendation from a third-party API.
The API used by the Custom Jinja2 Filter will be Spoonacular. Spoonacular requires an account and token to use the API, but it allows for several API requests per day for free. The API also has several filtering capabilities, the two that will be used here are: cuisine and ingredients. A Nautobot Plugin needs to be created in order to give Nautobot access to the Custom Jinja2 Filter, which will also provide access to the Spoonacular API token using PLUGINS_CONFIG in nautbot_config.py.
The Custom Link will be added to the dcim | site
Content Type, and will use the Site object to pass the Jinja2 Filter a cuisine and an ingredient. The cuisine will be obtained from the Site’s parent Region’s slug
field, and the Site’s slug
field will be used to provide an ingredient. This means that:
A list of acceptable cuisines for the API can be found at Spoonacular Cuisines.
This Plugin will only provide a Jinja2 Filter, so the file structure is very simple:
nautobot_plugin_recipe_filter
directory for the Plugin Appnautobot_plugin_recipe_filter/__init__.py
file with the PluginConfig definitionnautobot_plugin_recipe_filter/recipe_filters.py
for providing the filter to get recipe recommendationspyproject.toml
, README.md
, and LICENSE
for installing the Plugin in Nautobot.$ tree nautobot-plugin-recipe-filter
.
├── nautobot_plugin_recipe_filter
│ ├── __init__.py
│ └── recipe_filters.py
├── LICENSE
├── pyproject.toml
└── README.md
"""Plugin declaration for nautobot_plugin_recipe_filter."""
__version__ = "0.1.0"
from nautobot.extras.plugins import PluginConfig
class NautobotPluginRecipeFilterConfig(PluginConfig):
"""Plugin configuration for the nautobot_plugin_recipe_filter plugin."""
name = "nautobot_plugin_recipe_filter"
verbose_name = "Nautobot Plugin Recipe Filter"
version = __version__
author = "Network to Code, LLC"
description = "Nautobot Plugin Jinja2 Filter for Recipe Recommendations"
required_settings = ["spoonacular_token"]
min_version = "1.1.0"
max_version = "1.9999"
default_settings = {}
caching_config = {}
jinja_filters = "recipe_filters.py"
config = NautobotPluginRecipeFilterConfig
[tool.poetry]
name = "nautobot-plugin-recipe-filter"
version = "0.1.0"
description = "Nautobot Plugin Jinja2 Filter for Recipe Recommendations"
authors = ["Network to Code, LLC <info@networktocode.com>"]
license = "Apache-2.0"
readme = "README.md"
homepage = "https://github.com/networktocode/nautobot-plugin-recipe-filter"
repository = "https://github.com/networktocode/nautobot-plugin-recipe-filter"
keywords = ["nautobot", "nautobot-plugin"]
include = ["LICENSE", "README.md"]
packages = [
{ include = "nautobot_plugin_recipe_filter" },
]
[tool.poetry.dependencies]
python = "^3.6"
nautobot = "^v1.1.0"
requests = "*"
Creating a Custom Jinja2 Filter that can be consumed within Nautobot follows the standard Jinja2 process, and is auto-registered to the Nautobot Environment when a Plugin is installed and enabled on the Nautobot instance. Each Plugin can define the name of the file where Jinja2 Filters should be loaded from using the PluginConfig; the default location is jinja_filters.py
. The above PluginConfig specified recipe_filters.py
, so the filters made available by this plugin will go in that file.
In order to register the Jinja2 Filter with Nautobot, the Filter function is decorated with @library.filter
, where library
is imported from the django_jinja
library. The django_jinja library is included with Nautobot in order to support the Custom Jinja2 Filters feature. This Filter function will use the requests library to make API calls to the Spoonacular API. The Jinja2 Filter will return the URL to the recipe recommended by Spoonacular. In order to interact with the Spoonacular API, the token will be retrieved using the spoonacular_token
setting in the PLUGINS_CONFIG
; this setting is marked as required in the above PluginConfig.
"""Custom filters for nautobot_plugin_recipe_filter."""
import requests
from django.conf import settings
from django_jinja import library
SPOONACULAR_TOKEN = settings.PLUGINS_CONFIG["nautobot_plugin_recipe_filter"]["spoonacular_token"]
SPOONACULAR_URL = "https://api.spoonacular.com/recipes"
SPOONACULAR_RECOMMENDATION_URL = f"{SPOONACULAR_URL}/complexSearch" # URL to get recommendations
SPOONACULAR_RECIPE_URL = f"{SPOONACULAR_URL}//information" # URL to get details of recommendation
@library.filter
def get_recipe_url(cuisine:str, ingredient:str) -> str:
"""
Get Recipe recommendation based on cuisine and single ingredient.
Args:
cuisine (str): The cuisine derived from Nautobot's Region.slug value.
ingredient (str): The ingredient to include in the recipe based on Nautobot's Site.slug value.
Returns:
str: The URL of the recommended recipe.
"""
recommendation_params = {
"apiKey": SPOONACULAR_TOKEN,
"number": 1, # Limit number of responses to a single recipe
"cuisine": cuisine,
"includeIngredients": ingredient,
}
# Get recipe recommendation
recommendation_response = requests.get(url=SPOONACULAR_RECOMMENDATION_URL, params=recommendation_params)
recommendation_response.raise_for_status()
recipe_id = recommendation_response.json()["results"][0]["id"]
recipe_params = {"apiKey": SPOONACULAR_TOKEN}
# Get recipe detailed information
recipe_response = requests.get(url=SPOONACULAR_RECIPE_URL.format(id=recipe_id), params=recipe_params)
recipe_response.raise_for_status()
recipe_json = recipe_response.json()
# Get URL to recipe on Spoonacular's website
recipe_url = recipe_json["spoonacularSourceUrl"]
return recipe_url
The Plugin is ready to be installed into the Nautobot environment. See the Plugin Installation Guide for details. The Database Migration and Collect Static Files steps can be skipped, since the Plugin does not use a database table and does not provide static files.
PLUGINS = ["nautobot_plugin_recipe_filter"]
PLUGINS_CONFIG = {
"nautobot_plugin_recipe_filter": {
"spoonacular_token": "abc123"
}
}
The Jinja2 Filters provided by Plugins are available in Nautobot wherever Jinja2 text fields are used. This post will showcase using the above Jinja2 Filter to create a Custom Link; however, the Filter could also be used by the new Computed Fields feature added in v1.1.
The reason for using Custom Links in this instance is that the goal is to provide a link to another site with the recipe and cooking instructions. Computed Fields are designed to be text fields, and have HTML escaped; this means that an HTML hyperlink would be rendered as plain-text and not be clickable.
Nautobot’s Custom Link feature allows hyperlinks and groups of hyperlinks to be added to the upper-right side of pages displaying a single entry (DetailViews). Creating a Custom Link has several configuration options:
There are two steps to creating most objects in Nautobot:
Creating a Custom Link is done in the Web UI by browsing to Extensibility > Custom Links, and clicking the + icon to bring up the form to create a new Custom Link.
The Custom Link used to provide a hyperlink to Spoonacular’s recipe recommendation specifies a Content Type of dcim | site
, and a Name and Text of Recipe Recommendation
. The URL uses the Jinja2 Filter defined above to obtain the URL to the recipe, and clicks will open the hyperlink in a new tab.
The Jinja2 expression for the URL makes use of the variable obj
, which is the Django ORM object for the database record. Since the Content Type for this Custom Link uses the Site model, it will be a record from the Site database table. Nautobot Site entries have foreign key fields to the Region, so both the Region’s slug (cuisine) and Site’s slug (ingredient) can be passed to the Custom Jinja2 Filter.
Once all required fields are filled out on the form, clicking Create will create the Custom Link.
The Custom Link created above is displayed on every Site page. If the Jinja2 expression fails to render an HTML link, then the Custom Link button will not be clickable. In order to showcase the Custom Link, a guide is provided below to create a Region and Site, which results in a valid URL being created for the Site.
In order for a valid link to be shown, each Site must belong to a Region that has a slug value with a valid cuisine.
Creating a Region is done by browsing to Organization > Regions, and clicking the + icon to bring up the form to create a new Region.
The Region used in this example is Spain
, which correlates to the spanish
cuisine used by the Spoonacular API.
Once all required fields have been filled out on the form, clicking Create will create the Region.
In addition to each Site being linked to a Region, the Site must also use an ingredient for the value of its slug field.
Creating a Site is done by browsing to Organization > Sites, and clicking the + icon to bring up the form to create a new Site.
The Site in this example is Madrid
, and is assigned to the Spain
Region created above. The slug (ingredient) value uses rice
. In Nautobot, Sites require the status to be set; this Site uses the built-in Active
Status.
The Site form is longer than the previous forms, but scrolling to the bottom of the form displays the familiar Create button to create the Site.
Clicking on the Create button above redirects to the newly created Site Page with the Custom Link added to it.
The Custom Link for the recipe recommendation is displayed in the top-right corner of the page.
Since the check box to open the hyperlink in a new tab was selected on the Create Custom Link form, clicking the Custom Link on the Site page opens the Spoonacular recipe page in a new tab. The recommendation returned in the example is Paella.
Nautobot is more than a Source of Truth application. The newly added Custom Jinja2 Filters provide an easy way to perform more complex operations for fields that are rendered through Jinja2. An example of a field that supports Jinja2 expressions is the URL field in Custom Links. Custom Links can be added to Pages dynamically, and provide a convenient way of giving Users access to related pages.
The recipe recommendation example used here is perhaps not practical, but something similar could be built for linking to other Source of Truth systems. Maybe Nautobot is not the System of Record for certain data points, but is instead used to aggregate multiple data points into a single place for read-only operations. The Jinja2 Filter could look up the URL to the data point in the Application used as the System of Record. It might be handy to have a link in Nautobot that is able to take users to that page in order to manage the data there.
Share details about yourself & someone from our team will reach out to you ASAP!