Showing results for 
Search instead for 
Did you mean: 
Cisco Employee
Cisco Employee


If you have been working with Network Plug and Play (PnP) for a while, you might find some of these tools useful.  I am going to assume you have seen my earlier blogs, so have a good understanding of the PnP process, the API and how they work.  The scripts are purely educational, and you should adapt them for your own environment.


The tools are published at the following repository

Instructions for installing on a MAC/Linux machine follow:

git clone

I have written each as a standalone application.  I recommend you use a virtual environment to install the requirements. Note: these scripts use the uniq library, which requires python3.

cd PnPTools

virtualenv -p python3 env

source env/bin/activate

Remember, if you exit your shell or logout, you will need to run the command "source env/bin/activate" each time to activate the virtual environment.

#1 PnPWatch

This utility shows you the steps and progress of the PnP process for a device.

To install and run do the following:

cd PnPWatch/

pip install -r requirements.txt

To run the script you need to add your controller and the credentials for it.  You can do this in two ways:

  1. edit the file
  2. use the APIC, APIC_USER and APIC_PASSWORD environment variables.  For example the shell command "export APIC=''" would set the controller to the DevNet cloud controller. Be very careful with quotes.

You also provide a serial number for the device you are watching.  You need to have a rule for the device, or it needs to be in the unclaimed devices list.

$ ./src/ FDO1732Q00B

Watching unclaimed for serial:FDO1732Q00B

19:49:35: Duration (0) Unclaimed

19:49:43: Duration (9) Waiting for Resource

19:50:02: Duration (28) Start Provisioning

19:51:08: Duration (94) Deploying Device Certificate

19:52:09: Duration (155) Deploying Config

19:54:09: Duration (275) Provisioned

19:54:09: Completed (275): PROVISIONED

each step of the process is shown, along with the number of seconds it took to execute.  For example, it took 155 seconds for the "Deploying Config" step.

#2 PnPSync

This utility keeps your library of configuration, template and images files synced up to APIC-EM. As you are not able to edit directly on the controller, you can edit the disk based version of these files then sync them up to the controller.  The SHA1 hash is checked first to ensure an upload is required.  Any missing files are uploaded (POST), and any existing files are updated (PUT)

You will need to perform the same steps as the first example above.

There is an extra variable which is the directory containing the folders for the configuration, template and image files.  Those folders are called "configs", "templates" and "images".

$ ./src/


Updated File:4451.txt (c0991ef7-2274-40a8-8b21-7a690fc73193)


Uploaded File:new-template.txt (967e44ab-bd66-40bb-9645-71d69b7db0a6)


Skipping File:c2960x-universalk9-mz.152-4.E.bin (2f4380b4-64f5-401c-9464-ad5925f783a9) SHA1hash:67ff12708d66c188e998e78641d4f2f18e29c539

Skipping File:cat3k_caa-universalk9.SPA.03.06.05.E.152-2.E5.bin (f2b2c594-73de-45cc-9c70-ed0315e5b0ff) SHA1hash:c87f5ea36cd66d4558af1729c9d41f5058da4123

This shows the file "configs/4451.txt" was updated.

File "templates/new-template.txt" was uploaded.

File "images /c2960x-universalk9-mz.152-4.E.bin" was left unchanged as the SHA1 hash was identical.

#3 PnPConfigTemplator

This is an "industrialized" version of the initial example I wrote about in earlier blogs. There are two main differences:

1) It does not append a 4 digit number onto the end of projects and filenames

2) It uses nested jinja2 templates to generate configuration files for switch stacks.  There is a base template, that gets extended through macros and loops.

You will need to do the same steps as #1 to install the requirements and update the variables for the APIC-EM, username and password.

$ ./ ./work_files/hosts.csv

Using device file: ./work_files/hosts.csv

Using template directory: work_files/templates


Variables: {'HOSTNAME': 'fourswitch', 'imageFile': 'cat3k_caa-universalk9.SPA.03.06.05.E.152-2.E5.bin', 'VoiceVlan': '300', 'ManagementIP': '', 'stackCount': '4', 'DISTRO': 'distribution2-2-2', 'site': 'Sydney', 'serialNumber': '12345678901', 'USERVLAN': '100', 'platformId': '3850-stack', 'management': '90', 'template': 'four_switch.jnja'}

created file: work_files/configs/fourswitch-config

creating project:Sydney

Configuration File_id: b01989ab-19e7-47ee-88ec-812a90fc479c

Creating Rule [


    "eulaAccepted": true,

    "serialNumber": "12345678901",

    "licenseLevel": "ipbase",

    "platformId": "3850-stack",

    "memberCount": "4",

    "imageId": "f2b2c594-73de-45cc-9c70-ed0315e5b0ff",

    "pkiEnabled": true,

    "configId": "b01989ab-19e7-47ee-88ec-812a90fc479c",

    "hostName": "fourswitch"



Rule Status: {"message":"Success creating new site device(rule)","ruleId":"1aff050c-ba3f-456f-976d-e9a3531f544c"}


The script has quite a lot of debugging in it to show you what is happening.

I can see:

  • variables used for the template: Variables: {'HOSTNAME'…
  • configuration file being created: created file: work_files/configs/fourswitch-config
  • - project being created: creating project:Sydney
  • - Configuration file ID:Configuration File_id: b01989ab-19e7-47ee-88ec-812a90fc479c
  • - The rule being uploaded: Creating Rule […..
  • - Rule status: Rule Status: {"
These rules assume that an image file "cat3k_caa-universalk9.SPA.03.06.05.E.152-2.E5.bin"is present on the controller.  You can either edit the file "work_files/hosts.csv" to remove it, upload a dummy file for testing, or a real file for production.

How do the templates work

These examples are using jinja2 templates.    A sample configuration file is below.  All of the base IOS commands are in the file "work_files/templates/base.jnja". There are "macros" defined for the different port types in the file "work_files/templates/macros.jnja".

The base file has a "block" in it called "interfaces" {% block interfaces %}.  The template fills out that block (you could have other blocks as well, if required).

Jinja2 supports for loops {% for stack_num in range(1,3) %} .  In this case the variable "stack_num" will be set to 1 and 2.

"stack_num" is used to define the interface range, and then the macro "user_port" has the configurations for a user port.

{% from "macros.jnja" import user_port, ap_port, uplink_trunk with context%}

{% extends "base.jnja" %}

{% block interfaces %}

{% for stack_num in range(1,3) %}
interface range g{{ stack_num }}/0/1 - 40
{{ user_port() }}

int range g{{ stack_num }}/0/45 - 48
{{ ap_port() }}

int ten{{ stack_num }}/1/4
{{ uplink_trunk() }}

{% endfor %}
{% endblock %}

There are many ways this could be optimized and extended.  This example is just to illustrate the basic concepts.



./  work_files/hosts.csv

will remove all of the files/projects/rules.  This is for testing purposes

What Next?

I will keep adding to these utilities as I get time.  They are not officially (or unofficially J) supported, just examples of tools to make life easier.

In the meantime, if you would like to learn more about this, you could come hang out with us in The Cisco Devnet DNA Community. We’ll have a continuous stream of blogs like this and you can ask questions and we’ll get you answers.

In addition, we have a Github repository where you can get examples related to PnP.

Thanks for reading,


Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community:

Recognize Your Peers