POAP and Ansible integration part 4

In the last part of the series I will look at the boot process of a POAP installation
First thing to do is run the playbook to populate the tftpboot folder and create all the files.

There were no changes required for the DHCP server but as I removed all files from the tftp root all files were created or copied in case of the NXOS files.

At my dev system at home. I didn’t have the NXOS files available So i just created bogus files for demonstration purposes. The boot process below did use the correct software images.
Now all files are in place and the DHCP server is ready it is time to start the POAP process
To get a switch after it already has been configured back in poap mode a special boot option needs to be configured. Save the config and reboot the switch.

The system boots with the software 6.0.2.U2.2 (line 13) and POAP is enabled (line 26)

Obviously we do not want to abort POAP. So we wait until the device does a DHCP request on its management port which happens on line 9 and after about 25 seconds the switch decided on use this offer and continue the process (line 10).

The bootfile is downloaded and execution starts. Not sur why on line 23 it is stated that the MD5SUM is not verified because and incorrect MD5 in the file results in a failed boot process. All other messages are self explanatory.

The switch reboots after the succesfull POAP process and reboots with the specified software version and we are able to login with the username specified in the configuration file.


As you can see POAP is very powerfull to quickly upgrade and configure a large number of new switches. It would also be possible to modify the playbook to use the configuration of a failed switch. Imagine sending a replacement switch to the datacenter, the field engineer repalces the switch. You only need to change one line in a YAML file, run the playbook and the POAP files are prepared and the DHCP server is reconfigured and restarted.

POAP and Ansible integration part 3

The third part of the series will be about all the files required for the boot process. The boot process follows the diagram below.


All configuration files required for the boot process are generated by TFTPD role in the playbook. The tasks associated with this roles are defined in the YAML file.

The bootfile is a Python script based on an example which can be downloaded from CCO when you have correct entitlement. I have modified the Python script a bit and removed one bug which prevented the script to recognize the switch as a Nexus 3048. In the script all the details for the POAP process are specified.

  • software version
  • configuration file
  • download credentials
  • transfer method
  • download server

As I wanted to be flexible in the software version I used the templating system of Ansible to generate custom py files for booting.

The handler called when the py file changes is used to create the actual py file provided via the DHCP offer. In the actual file an extra line is added with the md5sum of the file without this extra line. When executed by the switch the python script will remove the line with the md5sum, calculate the md5sum and verify the script.  The handlers are specified in a separate YAML file

The handler for the py file is add md5 This handler executes a bash script to calculate the md5, add it to the file and store it as a new py file without the md5 suffix. This is the file downloaded by the switch during the POAP process and needs to be supplied by the DHCP server als Boot file.

When the md5 of the bootfile matches with the md5 included in the file the complete script is executed. In the script the transfer method is specified. If another method than tftp is used for transfer of the configuration and software credentials need to be specified. Please be aware that these credentials will be sent unencrypted to the switch when the py file is transferred. The files to be transferred are also specified in the script. In this example the name of the configuration file to be downloaded is derived from the serial number. This can be seen in line 8 of the roles/tftpd/tasks/main.yml file.

The next task is to create the actual configuration files. This is pretty straightforward. More about this can be found In a previous blog on this site. The only special thing is the handler generate md5 which calculates the md5sum of the configuration file and places this value in a textfile. This textfile has the same name as the configuration file with an .md5 suffix. The format of the string is md5sum=12345abcdef. The Python script executed by the POAP process will download these file automatically and verify the MD5SUM.

The last task in the playbook copies all the NXOS images to the TFTP server. Again a handler is called to create the md5 files like with the configuration files.

It is important to realize that Ansible is indempodent. It will always strive to keep everything in a consistent state regardless of how many times a playbook is run.  This also means that files generated by Ansible must not be changed by hand. The next time the playbook is run the changes made by hand will be lost.

In the last blog in the series I will show how everything works together and the switch will do a POAP.

Part 4

POAP and Ansible integration part 2

In this part of the serie I will discuss the isc-dhcpd server configuration. isc-dhcpd is a DHCP server which is available on most linux distributions. It has many options but for this setup only a minimal configuration is required.

The directory layout  for the ansible-playbook for the DHCPD role

The tasks for the DHCPD role are defined in roles/dhcpd/tasks/main.yml.

in role/dhcpd/vars/main.yml basic settings are configured for the DHCP server.

I my lab I used two scopes and one range to allocate addresses from. These settings are used in the dhcpd.conf.j2 template to create the main dhcpd.conf

At the end of the configuration an additional configuration file called static_clients has been included, in which the reservations for the statich (POAP) clients are defined. I have placed these in a separate file for a reason. In a normal environment there would be at least two DHCP servers. Each server would be responsible for a part of the subnet to allocate address from. Or there would be a master/slave relation between the two servers which requires different configurations on both. The reservations however must be the same on both servers.

This template is used by the task Generate dhcpd main config files. The handler is instructs Ansible to restart the DHCPD service but only when the configuration has changed.
The next task is to include an additional YAML file globals_poap_clients.yml with data about the various poap clients. The file is placed in a different directory than the normal vars directory belonging to the role because it will also be used by the TFTPD role.

This files specifies two Nexus devices. The data is being used in the task create client dhcpd config files and fed to the template for the POAP clients.

This configuration will provide for each poap client:

  • Hostname
  • Bootfile
  • Bootserver

Settings like the IP address/mask/gateway/DNS are provided via the global scope. The ip details specified in the YAML file will be used for the generation of the actual switch configuration files.

Normally reservations are made based on the MAC address. In this setup I have chosen to make the reservation based on the serial of switch. This is possible because the serial is used as the client-identifier in the DHCP request. The serial of a new switch is often more easilly obtained than the mac address and I hate entering mac addresses as each vendor/tool requires a different format.

It took a Wireshark capture to get it working because Cisco prepends the client- identifier with an ASCII NULL. That is why the \000 in front of the {{client.serial}} is required on line 5

Again when dhcp settings have changed like adding a POAP client the DHCPD service will be restarted by Ansible.

After running the playbook the configuration for the DHCP server is generated.

Overall the DHCP server configuration is pretty simple. In my lab the DHCP server is running on the same hosts as the ansible-scripts In a real world deployment this will most likely be different remote servers. How to configure Ansible to connect to remote DHCP servers is beyond the scope of this series but can be found on the internet easilly

This was part 2 of the series. In part 3 I will discuss about all the various files which need to be generated to make the POAP work.

Part 1

POAP and Ansible integration part 1

Everyone who has every installed a Nexus switch is familiar with the following message.

I always pressed y and be done with it. Since I have been using Ansible to create config files and to deploy Linux clients I have been wondering if I can do it all with Ansible. In a number of blogs I will describe  how to setup everything and never touch your console cable anymore. Please follow me on Twitter for the other blogs on this subject.

The flowchart for the setup is below


Everything is being specified in a number of YAML files. In the YAML files details about the POAP clients like, serial number, desired software version, hardware platform and ip details are specified. Also the basic DHCP server configuration parameters are specified in a YAML file.

The YAML files are used to create the following files via the templating system.

  • isc-dhcpd configuration files
  • bootfiles for the Nexus devices
  • configuration files for the Nexus devices

The creation of all these files has been split in two roles


In the next blog post I  will describe how the DHCPD role is responsible for the isc-dhcpd service.


Using Ansible to create config files

Recently I had to roll out a number of access switches. In the past I created the config files with either Excel/Word via a mailmerge or custom perl scripts. Both methods were not ideal. Mailmerge is inflexible and although I know my way around in Perl my colleagues often do not. After reading the excellent Ansible blog by Kirk Byers I gave it a try.

Ansible is primarily a tool like Chef and Puppet for server management. To make Ansible do something it has a concept named playbooks. A playbook defines which roles a specific host has. Each role has it specific tasks which need to be executed on that  hosts. For example a hosts has a role as DNS server . Tasks associated with this role could be make sure the latest version of Bind is installed and all the zone files are up to date. But also a task of creating the zone files by means of using a template system. This template system will be used to create the configuration files in this example

 Almost all files used by Ansible are written in the YAML format.
Below is the playbook used in this example.

Normally the tasks indicated by the roles would be executed on a remote host (remember the DNS server from above). For this example the files are generated on the same host as the Ansible script is being run this could also be a remote TFTP server for example.
The tasks belonging to the switch role of localhost are defined in a separate YAML file.

The task executed on the local host is creating files based on the Jinja2 template. The variables being used are also defined in a YAML file. The template is being completed by looping over item of the dictionary access_switches

The contents of the Jinja2 file.

This is a fairly simple Jinja2 file and is easy to read even without knowledge of the Jinja2 language. Everything between double curly brackets are variables which are being replaced with the actual value. Everything enclosed by a curly bracket and the percent sign is a function of the Jinja2 templating system. In this case a simple include for very static things like vlans and snmp stuff.
The directory layout for an Ansible script is very important. All files are expected to be found in specific directories. Below is the layout for this tutorial. Don’t worry about the router subdirectory for this moment.

The magic happens by running the playbook.

and the configuration files can be found in the config directory.

Although it might seem to be a lot of work to create all these YAML and Jinja2 files to generate a couple of configuration files it can save a lot of work later on. Imagine that you have generated 40 configurations and all of a sudden there is an additional vlan which needs to be included in all configurations. Now it is just a case of modifying one single file and generate all the configuration files by simply running the playbook again.