Debugging Custom Ansible Modules with PyCharm

Ansible is an easy configuration management system that can be used to automate and organize system configuration tasks for a large network of computers. With Ansible, you only need to install a server component, called the Ansible Control Machine, and this node only requires SSH access to the client machines, which means no agents are required to be installed on the client machines.

Ansible, comes out of the box with a plethora of modules available for system configuration tasks like creating user, installing packages and also with numerous module for cloud vendors like AWS, DigitalOcean, etc. Though, there are times where, writing a custom module for certain tasks becomes necessary, for internal requirements. While writing custom modules, one would need to be able to debug modules, which would ease module development.

In this post, I would like to provide insight in to a way of debugging modules, using Ansible’s test-module script and PyCharm IDE, on a Ubuntu 14.04 Vagrant Instance.

Requirements:

  • A Workstation, with Pycharm Professional Edition, can be any Operating System
  • Ubuntu 14.04 Vagrant Machine, which will be the Ansible Controller Machine or the debugging machine

Ubuntu 14.04 Vagrant Machine Setup:

The minimum Vagrant file configuration required for the setup to work is as below. Note the line, where the private network is configured, this makes it possible for the vagrant machine to connect to the workstation over the IP address 192.168.33.1 for debugging purpose. Create a directory at any location of your choice on the workstation and place the below vagrant config as Vagrantfile.

Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.network "private_network", ip: "192.168.33.80"
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end

Now in the same directory as the Vagrantfile do the following,

# Create a directory on the folder for keeping the custom modules # Windows user's please use git bash for the following
mkdir custom_modules
# Clone ansible from git
# As we need the hacking/test-module script for debugging
git clone git://github.com/ansible/ansible.git --recursive
cd ansible/
git submodule init && git submodule update

Now, the final project tree would look like,

.
├── ansible
│ ├── ansible-core-sitemap.xml
│ ├── bin
│ ├── CHANGELOG.md
│ ├── CODING_GUIDELINES.md
│ ├── contrib
│ ├── CONTRIBUTING.md
│ ├── COPYING
│ ├── docs
│ ├── docs-api
│ ├── docsite
│ ├── examples
│ ├── hacking
│ ├── lib
│ ├── Makefile
│ ├── MANIFEST.in
│ ├── packaging
│ ├── README.md
│ ├── RELEASES.txt
│ ├── ROADMAP.rst
│ ├── setup.py
│ ├── shippable.yml
│ ├── test
│ ├── ticket_stubs
│ ├── tox.ini
│ └── VERSION
├── custom_modules
│ └── OSModuleChecker.py
└── Vagrantfile

The contents of the OSModuleChecker.py, which will be the custom module is as below

#!/usr/bin/python

try:
import json
except ImportError:
import simplejson as json

import os

DOCUMENTATION = '''
---
module: OSCheckModule
short_description: A custom module to check OS of target machine.
description:
- A custom module to check OS of target machine.
options:
os_name:
description:
- Name of OS to check
required: true
'''

EXAMPLES = '''
# Example
module_name:
os_name: Linux
'''


def main():
module = AnsibleModule(
argument_spec=dict(
os_name=dict(required=True),
)
)

# Retreive parameters
os_name = module.params['os_name']

# Check for OS
if os_name in os.uname():
# Successfull Exit
module.exit_json(changed=True, msg="OS Match")
else:
# Fail Exit
module.fail_json(msg="OS Mismatch. OS =" + os.uname()[0])


from ansible.module_utils.basic import AnsibleModule

if __name__ == '__main__':
main()

Finally, It’s time to fire-up the Vagrant machine, and configure it for ansible

vagrant up && vagrant ssh
sudo apt-get install python-pip build-essential python-dev
sudo pip install ansible paramiko PyYAML Jinja2 httplib2 six markupsafe pydevd

Configuring PyCharm

  1. First, we have to configure the default python interpreter for our ansible project. Open the folder containing Vagrantfile with PyCharm.
  • Go to File -> Setting -> Project -> Project Interpreter.
  • In the window, click on the gear icon on the right (Please refer to the screenshot below)
  • In the pop-up that appears, select vagrant and select the default python interpreter. After the vagrant remote is configured, change the python interpreter to python 2.7. And finally your screenshot should be similar to the below screenshot

2 . Secondly, after configuring the default interpreter for the project, now the Run/Debug settings for the project has to be configured,

  • Go to Run ->Edit Configurations
  • On the screen that appears click the + button to the left-top corner and select Python Remote Debug and enter details as in the below screenshot
  • Note: The IP address may vary if you are using a different IP address in the Vagrantfile
  • Give a name for the configuration, enter Localhost name as 192.168.33.1 and the port as 54655 and click Apply and Save.

Debugging Ansible

Now click the Debug icon on the top-right corner and you will get the Debug console at the bottom of the screen like below, with the required details

  • Add the the line “import pydevd” to the top of the file OSModuleChecker.py, for importing the pydevd module
  • In order to set the breakpoint in the file, add the below line, in the place where the breakpoint is to be set
pydevd.settrace(‘192.168.33.1’, port=54655, stdoutToServer=True, stderrToServer=True)
  • Login to the vagrant machine via ssh and execute the below command
  • Note: Make sure that the vagrant machine is able to ping the IP 192.168.33.1 and use netcat to check if the port is reachable too.
/vagrant/ansible/hacking/test-module -m /vagrant/custom_modules/OSModuleChecker.py -a os_name=Linux
  • Now, PyCharm should prompt with a screen like the following
  • Click on Download, optionally you can check the Remember for Debug session checkbox, and then you should see the module with the break point set as in the below screenshot.
Vagrant
  • After the Debugging session you should be able to see the output in the console tab of the debugging view
Happy Debugging !!!

Please do, throw in your questions, if you have any trouble setting this up.

I would like to thank Mumshad Mannambeth, for his github document which helped me in setting this up, and my Team for their support and encouragement.