Working with the Cisco Handset Api In Python3 – Disable alternate TFTP

By 27th October 2017Cisco, CiscoIpPhone API

I thought I would share the following application that I wrote whilst in the middle of a large scale migration of phones between CUCM clusters a few months ago, in the hope it maybe useful to others.

After enabling pre V8 rollback mode, resetting all devices, updating dhcp option 150 values and resetting all devices again I discovered a large number had not registered to the new cluster. These types of issues can be tricky to diagnose when working remotley and as I had no local support to assist in reporting on the state of the devices, I decided look at the web interface of an affected device to confirm if it was indeed receiving the correct option 150 values. On closer inspection I found that “Alternate TFTP” had been turned on and the previous TFTP servers had been hard configured on the handset, checking a few more devices it was clear that this was wide spread. At that point in most situations you would have no choice but to back out the migration until you could have someone visit each device to remove the setting, but it is possible to do remotley if you are familiar with the handset api. The handset api is authenticated using the credentials of a user or application user with ownership of the device.

I have refined the code to make it more presentable, but I basically created some functions to grab html from the phones webservice, check that html for the “Alternate TFTP = Yes” value within, in which case, build the xml key press combinations required to change the setting before finally posting these messages to the handset api.
Complete Code.

__author__ = "Mitch Dawson"
__contact__ = ""
__copyright__ = "Copyright (C) 2017 - 2018 Ucdevops All Rights Reserved"
__license__ = "GPLv3"

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see

import requests
import re
from ipaddress import IPv4Network
import time
from lxml import etree

# Define Networks
nets = ['']

# Create a list of IP's using the IPV4Network Class
ips = [str(ip) for net in nets for ip in IPv4Network(net).hosts()]

# Define Phone Base Configuration Statistics Url
baseurl = '/CGI/Java/Serviceability?adapter=device.statistics.configuration'

# Model Alternate TFTP OFF Combination
combination = [
'Key:Applications', 'Key:KeyPad5', 'Key:KeyPad1',
'Key:KeyPad1', 'Key:KeyPad8', 'Key:Soft4', 'Key:Soft2',
'Key:Soft1', 'Key:Soft1'

# Credentials
u = 'username'
p = 'password'

# Handset API Http Headers
headers = {'content-type': 'application/xml'}

# Define Regex Search string
regex = 'Alternate TFTP</B></TD><td width=20></TD><TD><B>Yes'

def getHtml(address):
	# Function makes an HTTP request
	# to the required url and returns the HTML
	url = 'http://' + address + baseurl
	r = requests.get(url, timeout=1)
	return r.text

def checkHtml(html):
	# Check if Alternate TFTP Value is True
	# by running a regex test on the returned HTML
	return, html)

def buildXml():
	# Function creates the xml messages
	# required to turn Alternate TFTP off, returns a list
	messages = []
	for c in combination:
		tag_1 = etree.Element('CiscoIPPhoneExecute')
		tag_2 = etree.SubElement(tag_1, 'ExecuteItem')
		tag_2.set('Priority', '0')
		tag_2.set('URL', c)
		msg = etree.tostring(tag_1, pretty_print=False)
		data = {'XML': msg}
	return messages

def sendXml(messages, address):
	# Function unpacks the messages from the supplied list
	# and posts to the phone API
	for message in messages:
		url = 'http://' + address + '/CGI/Execute'
		r =
		url, data=message, headers=headers, auth=(u, p)
# Introduce a delay between messages, required for some devices.

def run():
	for ip in ips:
	# Try and Except loop
	# required to catch exception where there is no response from ip
	html = getHtml(ip)
	except Exception as e:
	# If there was no response go to next ip
	# Check for presence of Alternate TFTP = YES
	if checkHtml(html):
	# Create the list of xml messages
	phoneXml = buildXml()
	# Call the sendXml function to post the messages
	sendXml(phoneXml, ip)


Successful Response.

<ResponseItem URL="Key:Applications" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:KeyPad5" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:KeyPad1" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:KeyPad1" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:KeyPad8" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:Soft4" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:Soft2" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:Soft1" Data="Success" Status="0" />
<?xml version="1.0" encoding="utf-8"?>
<ResponseItem URL="Key:Soft1" Data="Success" Status="0" />


Author Mitch

I am an independent IT Consultant with specialities in Software Development, Enterprise Unified Communication, Network and Security platforms. In addition to my day to day work, I develop bespoke applications and I hope that through I can build relationships with clients, business partners and fellow engineers by providing solutions to complex problems through the use of programming.

More posts by Mitch

Leave a Reply