Getting Started with AXL Programming in Python using python-zeep

I’ve seen a lot more folks interested in working on UC automation and custom scripting against CUCM using AXL.  This is great news.  Unfortunately, Python, unlike other languages like Java, has no standard lib support for SOAP.  This means that most samples online end up being one of:

  • DIY with requests
  • Suds (I’m not going to share a link)


Requests is excellent for REST, but as SOAP is a standard, re-inventing the wheel is just plain dumb.  Copypasta of static xml strings is horrible as well.  We want to automate stuff on CallManager – not screw around with xml tags!  We need a library that does the heavy lifting for us.

Suds is not it.  Everyone starts here because the only samples available online use suds.  Suds has not been actively maintained for a number of years.  It has been forked, and the most recent fork is also now not maintained.  And it’s slow.  Very, very slow.

Please use python-zeep.  It is several orders of magnitude faster than suds.  It is under active development.  It abstracts away xml entirely and lets you work with native Python objects.  It supports serialization.  It’s a great piece of software.


So, since its the one thing that all UC engineers want when they get started…  here is a Hello World for a simple add operation to CUCM.  It was tested with Python 3.6.

# -*- coding: utf-8 -*-

from zeep import Client
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault
from zeep.plugins import HistoryPlugin
from requests import Session
from requests.auth import HTTPBasicAuth
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
from lxml import etree


USERNAME = 'axl_magician'
PASSWORD = '99problemsbutsudsaint1'
FQDN = ""
WSDL = 'file://C://path//to//AXLAPI.wsdl'
ADDRESS = "https://{fqdn}:8443/axl/".format(fqdn=FQDN)

def main():
    session = Session()
    session.verify = False
    session.auth = HTTPBasicAuth(USERNAME, PASSWORD)
    transport = Transport(cache=SqliteCache(), session=session, timeout=20)
    history = HistoryPlugin()
    client = Client(wsdl=WSDL, transport=transport, plugins=[history])
    axl = client.create_service(BINDING_NAME, ADDRESS)

    kwargs = {
       "name": "Hello World AAR Group"
    except Fault:
        for hist in [history.last_sent, history.last_received]:
            print(etree.tostring(hist["envelope"], encoding="unicode", pretty_print=True))

if __name__ == '__main__':

I’ve included the xml dump on failures.


Happy coding, and pouring over the AXL API docs!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s