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.

Full Code
  1. __author__ = "Mitch Dawson"
  2. __contact__ = ""
  3. __copyright__ = "Copyright (C) 2017 - 2021 Ucdevops All Rights Reserved"
  4. __license__ = "GPLv3"
  5. """
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see
  16. """
  17. import requests
  18. import re
  19. from ipaddress import IPv4Network
  20. import time
  21. from lxml import etree
  22. # Define Networks
  23. nets = ['']
  24. # Create a list of IP's using the IPV4Network Class
  25. ips = [str(ip) for net in nets for ip in IPv4Network(net).hosts()]
  26. # Define Phone Base Configuration Statistics Url
  27. baseurl = '/CGI/Java/Serviceability?adapter=device.statistics.configuration'
  28. # Model Alternate TFTP OFF Combination
  29. combination = [
  30. 'Key:Applications', 'Key:KeyPad5', 'Key:KeyPad1',
  31. 'Key:KeyPad1', 'Key:KeyPad8', 'Key:Soft4', 'Key:Soft2',
  32. 'Key:Soft1', 'Key:Soft1'
  33. ]
  34. # Credentials
  35. u = 'username'
  36. p = 'password'
  37. # Handset API Http Headers
  38. headers = {'content-type': 'application/xml'}
  39. # Define Regex Search string
  40. regex = 'Alternate TFTP</B></TD><td width=20></TD><TD><B>Yes'
  41. def getHtml(address):
  42.     # Function makes an HTTP request
  43.     # to the required url and returns the HTML
  44.     url = 'http://' + address + baseurl
  45.     r = requests.get(url, timeout=1)
  46.     return r.text
  47. def checkHtml(html):
  48.     # Check if Alternate TFTP Value is True
  49.     # by running a regex test on the returned HTML
  50.     return, html)
  51. def buildXml():
  52.     # Function creates the xml messages
  53.     # required to turn Alternate TFTP off, returns a list
  54.     messages = []
  55.     for c in combination:
  56.         tag_1 = etree.Element('CiscoIPPhoneExecute')
  57.         tag_2 = etree.SubElement(tag_1, 'ExecuteItem')
  58.         tag_2.set('Priority', '0')
  59.         tag_2.set('URL', c)
  60.         msg = etree.tostring(tag_1, pretty_print=False)
  61.         data = {'XML': msg}
  62.         messages.append(data)
  63.     return messages
  64. def sendXml(messages, address):
  65.     # Function unpacks the messages from the supplied list
  66.     # and posts to the phone API
  67.     for message in messages:
  68.         url = 'http://' + address + '/CGI/Execute'
  69.         r =
  70.         url, data=message, headers=headers, auth=(u, p)
  71.         )
  72.         print(r.text)
  73.                 # Introduce a delay between messages, required for some devices.
  74.                 time.sleep(1)
  75. def main():
  76.     for ip in ips:
  77.     print(ip)
  78.     # Try and Except loop
  79.     # required to catch exception where there is no response from ip
  80.     try:
  81.         html = getHtml(ip)
  82.     except Exception as e:
  83.         # If there was no response go to next ip
  84.         continue
  85.     else:
  86.         # Check for presence of Alternate TFTP = YES
  87.         if checkHtml(html):
  88.             # Create the list of xml messages
  89.             phoneXml = buildXml()
  90.             # Call the sendXml function to post the messages
  91.             sendXml(phoneXml, ip)
  92. if __name__ =="__main__":
  93.     main()
Successful Responses
  1. <CiscoIPPhoneResponse>
  2. <ResponseItem URL="Key:Applications" Data="Success" Status="0" />
  3. </CiscoIPPhoneResponse>
  4. <?xml version="1.0" encoding="utf-8"?>
  5. <CiscoIPPhoneResponse>
  6. <ResponseItem URL="Key:KeyPad5" Data="Success" Status="0" />
  7. </CiscoIPPhoneResponse>
  8. <?xml version="1.0" encoding="utf-8"?>
  9. <CiscoIPPhoneResponse>
  10. <ResponseItem URL="Key:KeyPad1" Data="Success" Status="0" />
  11. </CiscoIPPhoneResponse>
  12. <?xml version="1.0" encoding="utf-8"?>
  13. <CiscoIPPhoneResponse>
  14. <ResponseItem URL="Key:KeyPad1" Data="Success" Status="0" />
  15. </CiscoIPPhoneResponse>
  16. <?xml version="1.0" encoding="utf-8"?>
  17. <CiscoIPPhoneResponse>
  18. <ResponseItem URL="Key:KeyPad8" Data="Success" Status="0" />
  19. </CiscoIPPhoneResponse>
  20. <?xml version="1.0" encoding="utf-8"?>
  21. <CiscoIPPhoneResponse>
  22. <ResponseItem URL="Key:Soft4" Data="Success" Status="0" />
  23. </CiscoIPPhoneResponse>
  24. <?xml version="1.0" encoding="utf-8"?>
  25. <CiscoIPPhoneResponse>
  26. <ResponseItem URL="Key:Soft2" Data="Success" Status="0" />
  27. </CiscoIPPhoneResponse>
  28. <?xml version="1.0" encoding="utf-8"?>
  29. <CiscoIPPhoneResponse>
  30. <ResponseItem URL="Key:Soft1" Data="Success" Status="0" />
  31. </CiscoIPPhoneResponse>
  32. <?xml version="1.0" encoding="utf-8"?>
  33. <CiscoIPPhoneResponse>
  34. <ResponseItem URL="Key:Soft1" Data="Success" Status="0" />
  35. </CiscoIPPhoneResponse>

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