Nautobot ChatOps with Cisco Meraki

Blog Detail

Network to Code is releasing a new Nautobot app—a plugin to interact with Cisco Meraki using the existing Nautobot ChatOps framework! This app comes with prepackaged commands to gather data about your Meraki environment via chat commands. The Nautobot ChatOps app lowers the barrier of entry by providing interactions with chat platforms of Mattermost, Microsoft Teams, Slack, and Webex Teams. The amount of code needed to generate ChatOps commands is low, and the Meraki ChatOps app can be expanded to include new commands to fit any number of use cases.

The Nautobot ChatOps Meraki app extends the capabilities of the Nautobot ChatOps app to include a new chat command. This is done by registering to the Python entry point in Nautobot plugin ChatOps that provides functionality to the code written to interact with Meraki.

Visit Nautobot Chatops with Meraki Repository for more details.

This app introduces the following subcommands to the meraki command:

  • get-organizations
  • get-admins
  • get-devices
  • get-networks
  • get-switchports
  • get-switchports-status
  • get-firewall-performance
  • get-wlan-ssids Query Meraki for all SSIDs for a given Network.
  • get-camera-recent Query Meraki Recent Camera Analytics.
  • get-clients Query Meraki for List of Clients.
  • get-neighbors Query Meraki for List of LLDP or CDP Neighbors.
  • configure-basic-access-port Configure an access port with description, VLAN, and state.
  • cycle-port Cycles a port on a given switch.

Get Organizations

Gather all the Meraki Organizations based on the API Key used during setup.

Meraki Get Organizations

Get Admins

Based on an Organization Name, return the Meraki Admins.

Get Devices

Gathers devices from Meraki. Provides a device type option in order to limit scope.

Get Networks

Gathers names of networks from Meraki.

Meraki Get Networks

Get Switchports

Gathers switch ports configuration details from an MS(Meraki Switch Model) switch device.

Get Switchports Status

Gathers switch ports operating status from an MS(Meraki Switch Model) switch device.

Get Firewall Performance

Query Meraki with a firewall to device performance. This provides an integer value.

Get WLAN SSIDS

Query Meraki for all SSIDs for a given Network.

Get Camera Recent

Query Meraki Recent Camera Analytics.

Get Clients

Query Meraki for List of Clients.

Get Neighbors

Query Meraki for List of LLDP or CDP Neighbors based on a device.

Configure Basic Access Port

Configure an access port with description, VLAN, and state.

Meraki Configure Port 1
Meraki Configure Port 2

Cycle Port

Cycles a port on a given switch. Equivalent to a shutdown, no shutdown procedure.

Extending the App

Cisco Meraki is a cloud-managed infrastructure manager that includes multiple different products. Since Meraki can manage switches, security devices, cameras, and wireless, the initial plugin aims to provide some value in each of these product lines. Extending this plugin to include more commands is simple, and I encourage contributions back into this project.


Conclusion

This is just the start of what is possible in extending the Nautobot ChatOps ecosystem. Whether you want to write your own, or use one of the additional plugins that has been created by Network to Code, the ecosystem for ChatOps is going to continue to grow! Keep an eye out for additional ChatOps plugins to be announced here!

-Jeff



ntc img
ntc img

Contact Us to Learn More

Share details about yourself & someone from our team will reach out to you ASAP!

Arista CloudVision ChatOps with Nautobot

Blog Detail

In the spirit of Network to Code’s continuing development in the ChatOps space, we are releasing a new Nautobot app. It is an application to interact with Arista CloudVision using the existing Nautobot ChatOps framework. This ChatOps integration comes pre-packaged with commands to gather various data about CloudVision (CV). You can now get key information from CV directly from chat!

This app can work with either an on-prem instance of CloudVision or CloudVision as-a-Service. For installation steps, refer to the app’s README. To install the underlying Nautobot ChatOps framework, refer to the documentation found here.

Looking at the image below gives a visual representation of how this app works with the core ChatOps app & plugin.

ChatOps_flow

The Nautobot ChatOps CloudVision plugin extends the capabilities of the Nautobot ChatOps framework addding a new chat command. The new CloudVision plugin introduces the following commands:

  • get-devices-in-container
  • get-configlet
  • get-device-configuration
  • get-task-logs
  • get-applied-configlets
  • get-active-events
  • get-tags
  • get-device-cve
cloudvision_commands

Get Devices in Container

The get-devices-in-container sub-command will prompt the user to choose a container from the existing containers in CloudVision. It will then give you a list of all devices in the chosen container.

Get Configlet

The get-configlet sub-command will query CloudVision for the currently defined configlets, ask you to choose one, and give you the contents of the chosen configlet.

Get Device Configuration

The get-device-configuration sub-command queries CloudVision for all of its devices, prompts you to choose one, and retrieves the specified device configuration.

Get Task Logs

The get-task-logs sub-command allows you to choose a task by Task ID. It will then give you the logs of that task.

Get Applied Configlets

The get-applied-configlets sub-command allows you to get a list of configlets applied to either a container or a device.

Get Active Events

The get-active-events sub-command allows you to get active events filtered by device, type, or severity within a given time frame. When the sub-command is executed, a prompt will ask you for the filter you wish to apply. From there it will ask you for a start time. We configured it so that you may specify a start time relative to the current time by using an h for hours, d for days, and w for weeks. For example, when prompted for a start time, you’d enter the value -2w if you wanted to go back 2 weeks from the current time. For 2 days, you’d type -2d. The last prompt asks you for an end time. You can again use the same syntax mentioned before but you may also type the word now to use the current time.

Get Tags

The get-tags sub-command will present a list of all the tags assigned to a specific device.

Get Device CVE

The get-device-cve sub-command gets a list of all CVE’s discovered by CloudVision for a specific device.


Conclusion

These commands only handle a subset of the information that can be gathered by a CloudVision chatbot. You can contribute more commands with minimal Python code! Because the Nautobot ChatOps plugin lowers the barrier of entry by already handling the interaction between Nautobot and chat applications like Mattermost, Microsoft Teams, Slack, and Webex, creating new commands is extremely easy. We encourage you to create your own commands by building on top of existing commands and plugins we at NTC have created, or to create your own command to interact with something you use on a daily basis. We’re going to continue creating new plugins for ChatOps, so keep an eye out for additional announcements here!

-Adam



ntc img
ntc img

Contact Us to Learn More

Share details about yourself & someone from our team will reach out to you ASAP!

Creating Custom Chat Commands Using Nautobot ChatOps

Blog Detail

ChatOps and chat bots are becoming an ever more popular method to interact with network systems. Whether the command is to get operational status, to full-on operational configuration commands, interactions via chat is here. Nautobot not only is a Source of Truth, but is also a Network Automation Platform to build apps on top of! One app already released is the ChatOps plugin that you can use to accelerate the implementation of your individual chatbot needs! You can explore a public demo of the ChatOps plugin in the Network to Code (NTC) Slack channel #nautobot-chatops. The ChatOps plugin today has support for integration with Microsoft Teams, Cisco Webex, Slack, and Mattermost. To install and configure the plugin with your own particular chat environment, take a look at the docs.

Extending the ChatOps plugin to support your own commands is quite straightforward. This post provides a walk-through guide to getting up and started with your own custom chat commands. Underneath the entire plugin is Python, which allows your chat commands to interact with nearly any system you want. Want to get information from a third-party API and present it back? Want to kick off an Ansible playbook execution in AWX/Tower? Want to interact with a network device over SSH? All of these are absolutely possible!

Exploring ChatOps Connection to Nautobot

By default and out of the box, the Nautobot ChatOps plugin will provide the capability to query the Nautobot environment directly. This is what is enabled on the NTC Slack channel. It is tied to the demo instance of Nautobot. Take a look at our NFD talk introduction to ChatOps and demonstration for more examples of Nautobot ChatOps features available with installation.

Exploring Slack-Specific Commands

Slack Command Help

Within Slack specifically, to get started you probably want to see all of the commands that are available for the chatbot. In this case the bot name is nautobot, so when you send the command /nautobot to the channel you get the response with the following:

Some points to note as you start thinking about adding your own commands:

  • Ordering of the commands: This is controlled by the order of the functions as they get defined in the plugin definition file.
  • Help text: This is gathered from the first line of the docstring that accompanies the function.

Slack Command Example – Get Devices

First, looking at the help output for get-devices it shows /nautobot get-devices [filter-type] [filter-value]. If one were to issue the command with the filter-type and filter-value defined, then that is all that is needed to kick off the command. The second option is to just issue the command /nautobot get-devices and the bot will prompt back for those particular items. As the prompt gets answered, the next option continues on. So in this example you can get the data with either the command where you follow the prompts, or just issue the complete command /nautobot get-devices site nyc.

The result is a text entry that is:

           Name              Status   Tenant       Site        Rack     Role     Type   IP Address
==================================================================================================
nyc-bb-01.infra.ntc.com      Active            New York City          Backbone   vMX              
nyc-leaf-01.infra.ntc.com    Active            New York City          leaf       vEOS             
nyc-leaf-02.infra.ntc.com    Active            New York City          leaf       vEOS             
nyc-rtr-01.infra.ntc.com     Active            New York City          Router     vMX              
nyc-rtr-02.infra.ntc.com     Active            New York City          Router     vMX              
nyc-spine-01.infra.ntc.com   Active            New York City          spine      vEOS             
nyc-spine-02.infra.ntc.com   Active            New York City          spine      vEOS      

Command Output Observations

Some things from the code here:

  • Site nyc is a slug, as in the imagery it shows the full name New York City but in the output shortcut that gets added it shows nyc. This matches the slug since spaces are not allowed in the section of the commands. Each space is a separator for the command to process.
  • Shortcut text is specified and written in the code had as part of the response for the bot. The bot itself does not add this or any other text.

Creating Your Own Chat Command

This walk-through will be to write a new chat command (as a Nautobot ChatOps plugin) that will be named netchat within Slack. The focus here is on getting started writing your own code rather than the chat platform itself. What gets outlined here should work for any other chat platform with minimal modifications. The document for chat setup referenced earlier is your guide to walk through getting the chat platform ready.

Once you have written the code, the same code is designed to work with Slack, Microsoft Teams, Cisco Webex, and Mattermost. Nautobot ChatOps can interact with multiple platforms from the same API endpoint. Allowing for interactions from any of the platforms defined in the Nautobot configuration.

There is not a limitation to only these four chat platforms. This can be expanded with additional dispatchers to be added into the ChatOps plugin. Recently this was extended to support Mattermost as an example, which was not supported at the beginning of 2021.

In this walk-through, we will be adding a command to get the device inventory from a Meraki Organization by interacting with the Meraki Dashboard API.

Demo Setup

Demo Setup Assumptions

  • Nautobot is locally installed via the local installation methods
  • Nautobot ChatOps has been successfully installed
  • The Nautobot ChatOps Plugin is required for adding custom chat commands

Demo Setup – Nautobot Configuration

The first step is to set up the Nautobot configuration. Within (/opt/nautobot/nautobot_config.py) a dictionary is added to PLUGINS_CONFIG. It shows as follows:

PLUGINS_CONFIG = {
    'nautobot_chatops': {
        'enable_slack': True,
        'slack_api_token': os.getenv("SLACK_API_TOKEN"),
        'slack_signing_secret': os.getenv("SLACK_SIGNING_SECRET")
    }
}
  • nautobot_chatops is the required first key to indicate this is the plugin configuration for the ChatOps plugin.
  • The value of nautobot_chatops is another dictionary that has the keys:
    • enable_slack: Boolean field for having Slack enabled. There are enable_* values for each of the chat platforms there are dispatchers for. There can be multiple platforms enabled at one time.
    • slack_api_token: Token for the bot defined within the chatbot config on api.slack.com
    • slack_signing_secret: Signing secret as defined within the chatbot config on api.slack.com
  • There are other keys per platform, depending on the chat platform

Demo Setup – Nautobot Environment Setup

The Slack configuration is configured in the environment, not in the configuration file. The environment is read by the configuration file and thus loaded into Nautobot. In order to get the updated environment, the Nautobot service files are updated to reference the environment file /opt/nautobot.env.

  • Add EnvironmentFile=/opt/nautobot/.env to both /etc/systemd/system/nautobot.service and /etc/systemd/system/nautobot-worker.service files.
[Unit]
Description=Nautobot WSGI Service
Documentation=https://nautobot.readthedocs.io/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
Environment="NAUTOBOT_ROOT=/opt/nautobot"
EnvironmentFile=/opt/nautobot/.env

User=nautobot
Group=nautobot
PIDFile=/var/tmp/nautobot.pid
WorkingDirectory=/opt/nautobot

ExecStart=/opt/nautobot/bin/nautobot-server start --pidfile /var/tmp/nautobot.pid --ini /opt/nautobot/uwsgi.ini

Restart=on-failure
RestartSec=30
PrivateTmp=true

[Install]
WantedBy=multi-user.target
  • The environment file (here, /opt/nautobot/.env) is set up with the corresponding environment variables and the file permissions are set to 0600.
cat /opt/nautobot/.env
SLACK_API_TOKEN=xoxb-0000000000000-0000000000000-abcdefg00000ABCDEFG01238
SLACK_SIGNING_SECRET=00000000000000000000000000000000
MERAKI_DASHBOARD_API_KEY=0000000000000000000000000000000000000000
  • After modifying the systemd files, execute daemon-reload and restart the services. Once the service is restarted it is recommended to check the status of the bot by issuing a Slack command /nautobot to get the help context.
sudo daemon-reload
sudo systemctl restart nautobot nautobot-worker

Inside the environment file is the Meraki API key to put the API key into the environment as required by the Meraki SDK.

Using Existing Methods for Portability

The first recommendation is to look at the design docs that show how the plugin is designed. It is recommended, wherever possible, to use the Python methods provided, especially in the interaction back to the chat platform. For example, one could set up the mention of @username easily in the response to tag a user. However, there is a Python method within the dispatcher to get the user mention. This way, should a platform need to be migrated say from Slack to Microsoft Teams, updates are not needed to the code, just a change of the settings. This allows for the chat bot to interact with multiple chat platforms at the same time. Take a look at the code inside of GitHub for your specific platform to see what methods are available!

Creating the Custom Chat Commands

After setting up the settings mentioned above to load the configuration into the environment, I logged into the user account for nautobot with sudo -iu nautobot. Following the instructions for Nautobot v1.0.0b2 or later, this has the user root in /opt/nautobot. Here create a new directory plugins/netchat (/opt/nautobot/plugins/netchat/).

The directory structure that will be built out is:

$ tree /opt/nautobot/plugins/
/opt/nautobot/plugins/
└── netchat
    ├── netchat
    │   ├── __init__.py
    │   └── worker.py
    ├── poetry.lock
    └── pyproject.toml

First step in this is to build out the directory structure and adding the __init__.py file. This creates a new /opt/nautobot/plugins directory, as well as the directory structure for the package netchat.

cd /opt/nautobot
mkdir -p plugins/netchat/netchat
touch /opt/nautobot/plugins/netchat/netchat/__init__.py

This ChatOps plugin will use Python Poetry to handle the packaging and configuration. To install Poetry, take a look at the installation steps outlined by Poetry. This should follow their installation methods, and do not install with Python PIP. They were specifically chosen to help with the entry point configuration which is required to add onto the chatops plugin. The configuration pieces specific to the netchat plugin for the /opt/nautobot/plugins/netchat/pyproject.toml:

[tool.poetry]
name = "netchat"
version = "0.1.1"
description = ""
authors = ["Network to Code <opensource@networktocode.com>"]

[tool.poetry.plugins."nautobot.workers"]
"netchat" = "netchat.worker:netchat"

The first section of tool.poetry outlines the Python package itself, in this instance it is called netchat. The second section tool.poetry.plugins."nautobot.workers" defines the “entry point” to register the code as an extension of the ChatOps plugin.

The left-hand side of "netchat" = corresponds to the slash command that is being installed, and the right-hand side of the line = "netchat.worker:netchat is how to get to the function. In this case, netchat.worker refers to the file netchat/worker.py, and the netchat to the right of the : is the function name inside of this file.

Take a look at the gist, if preferred.

Netchat – Worker

In the netchat plugin, the code for the chat bot will be housed within /opt/nautobot/plugins/netchat/worker.py. This is from the reference in the pyproject.toml file in the configuration "netchat" = "netchat.worker:netchat". This could be any file name, something that meaningful to the plugin setup. The code for worker.py is:

"""Demo netchat addition to Nautobot."""
import logging

from django_rq import job
from nautobot_chatops.workers import subcommand_of, handle_subcommands
from nautobot_chatops.choices import CommandStatusChoices

import meraki

logger = logging.getLogger("rq.worker")


@job("default")
def netchat(subcommand, **kwargs):
    """Interact with netchat."""
    return handle_subcommands("netchat", subcommand, **kwargs)


def get_meraki_orgs():
    """Query the Meraki Dashboard API for a list of defined organizations."""
    dashboard = meraki.DashboardAPI(suppress_logging=True)
    return dashboard.organizations.getOrganizations()


def meraki_devices(org_name):
    """Query the Meraki Dashboard API for a list of devices in the given organization."""
    # Get the org ID from the Get Meraki Orgs
    org_list = get_meraki_orgs()
    dashboard = meraki.DashboardAPI(suppress_logging=True)

    for org in org_list:
        if org["name"] == org_name:
            device_list = dashboard.organizations.getOrganizationDevices(organizationId=org["id"])
            return device_list

        return []


@subcommand_of("netchat")
def get_meraki_devices(dispatcher, org_name=None):
    """Gathers devices from Meraki API endpoint."""
    logger.info(f"ORG NAME: {org_name}")
    if not org_name:
        # The user didn't specify an organization, so prompt them to pick one
        org_list = get_meraki_orgs()
        
        # Build the list of sites, each as a pair of (user-visible string, internal value) entries
        choices = [(x["name"], x["name"]) for x in org_list]
        dispatcher.prompt_from_menu(f"netchat get-meraki-devices", "Select Organization", choices)
        
        # Returning False indicates that the command needed to prompt the user for more information 
        return False

    # If gathering information from another system may take some time, it's useful to send the user
    dispatcher.send_markdown(
        f"Stand by {dispatcher.user_mention()}, I'm getting the devices at the Organization {org_name}!"
    )

    devices = meraki_devices(org_name)
    
    # Render the list of devices to Markdown for display to the user's chat client
    blocks = [
        dispatcher.markdown_block(f"{dispatcher.user_mention()} here are the devices at {org_name}"),
        dispatcher.markdown_block("\n".join([x["name"] for x in devices])),
    ]

    dispatcher.send_blocks(blocks)

    return CommandStatusChoices.STATUS_SUCCEEDED

The important parts are, first, the function registration of the chat command:

@job("default")
def netchat(subcommand, **kwargs):
    """Interact with netchat."""
    return handle_subcommands("netchat", subcommand, **kwargs)

This registers the netchat slash command to the ChatOps plugin. It corresponds with the entry point registration. Note the docstring of the function is what will be displayed to the chat user when listing available commands.

The chat command itself is then handled by the next defined function get_meraki_devices. This function registers itself to be a subcommand of netchat The first argument must always be dispatcher, which is an object used for all interactions with the chat platform. The remaining arguments are potential user inputs to the chat command; here we have just one, the org_name argument. This is the part of the chat command `/netchat get-meraki-devices `. When a user first sends the command `/netchat get-meraki-devices`, the org_name is `None`, which allows for the function to gather a list of choices and present it back.

@subcommand_of("netchat")
def get_meraki_devices(dispatcher, org_name=None):
    """Gathers devices from Meraki API endpoint."""
    if not org_name:
        <gather organizations>

    # When org_name is passed in
    <do more with code>
    ... # More Code 

Through the prompts, the chat applications will not sort the data. In the example, everything is alphabetized because the code called for it to be alphabetized. Slack itself does not alphabetize the options. The text boxes are something that you can type in to filter for yourself.

Installation via Poetry

The final step after writing the code is that the package must be installed into the Nautobot environment. In this case since the package has not been published to PyPI, we will use Poetry to install it directly from source into the venv. This does require the virtual environment to be activated. As the Nautobot user:

cd ~
source bin/activate

Once activated, move to the plugin directory, use Poetry to add requirements (in this case typing-extensions and meraki), then install the local package to the virtual environment.

pwd
/opt/nautobot

cd plugins/netchat/
poetry add typing-extensions
poetry add meraki
poetry install

Once the netchat package is installed, exit out of the Nautobot user. Then restart the Nautobot application and Nautobot worker process. If there are access grants that are restricting access to the chatbot, be sure to make the necessary updates in Nautobot to allow the new slash command.

sudo systemctl restart nautobot nautobot-worker

With the workers restarted, the slash command should be available on the chat application!


Conclusion

The ChatOps plugin is an excellent starting point for your own custom chat application. As demonstrated by its built-in commands, you can write chat commands that interact directly with Nautobot. But the ChatOps plugin can do far more than that. With Python, the capabilities to interact with other third-party services are endless!



ntc img
ntc img

Contact Us to Learn More

Share details about yourself & someone from our team will reach out to you ASAP!