cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
2923
Views
0
Helpful
3
Replies

Python with AXL UpdatePhone fail

Keith Abbott
Level 1
Level 1

Hi All,

Im using Python 3.6.7 / Eclipse IDE 4.1.3.0 / CUCM 10.5 AXL

I am new to all of the above.

I am attempting to write a script to use 'updatephone' to just change the device description of a phone.

This is a learning step to help me become more familiar with the environments so I can ultimately create, basically, an axl-based supercopy.

But in the simple version I am only attempting to:

connect to call manager

pass a mac address (really the phone name)

return UUID and description of the device using 'listphone'

... all of the above works

then using the UUID from above, use 'updatephone' to change the device description.

However when it hits the updatephone, it bombs - I'll provide some test code and error msgs below.

Since this is supposed to be a learning experience, something (along with the vast majority of the universe) Im not grasping, is when I should be calling 'updatephone' vs 'updatephonereq'. Im finding references to both, and since its difficult finding simultaneous references to AXL and python, with good examples - Im having trouble grasping the basics. Can someone enlighten me on this point?

Also, where can I find a good schema reference for 10.5? I have the xsd but its somewhat difficult to use.

 

ok now for the code - it probably will look a little goofy cause its extracted from a larger code-set and Ive been dumping debug junk in and out of it, and trying different things:

from test.test_importlib.util import CASE_INSENSITIVE_FS
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

disable_warnings(InsecureRequestWarning)

username = 'administrator'
password = 'mysecretpassword'
# If you're not disabling SSL verification, host should be the FQDN of the server rather than IP
host = 'LabCM.corp.mycompany.com'

wsdl = 'file://C:/AXLdev/AXLAPI.wsdl'
location = 'https://{host}:8443/axl/'.format(host=host)
binding = "{http://www.cisco.com/AXLAPIService/}AXLAPIBinding"

mac = "SEP00AF1F9zzzzz"

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])
service = client.create_service(binding, location)

try:
resp = service.listPhone(searchCriteria={'name': mac}, 
returnedTags={'name': '', 'description': '', 
'callingSearchSpaceName': ''})

phone_list = resp['return'].phone
for phone in phone_list:
print(phone.name + " --- " + phone.description + " --- " + phone.uuid)
Tuuid = phone.uuid
except Fault:
show_history()

newDesc = "Jonus Grumby"
PHONEID = phone.name

phone_data = {
'name': PHONEID,
'newdescription': newDesc,
'Uuid': Tuuid
}
#
service = client.create_service(binding, location)
print("starting updatephone...")
try:
# resp = service.updatePhone(uuid=Tuuid,newDescription=newDesc)
# resp = service.updatePhone({'uuid':Tuuid},{'newDescription':newDesc})
resp = service.updatePhone({'Uuid': Tuuid},{'newdescription': newDesc})

print("after-update...")
except Fault:
print("in fault processing")
show_history()
def show_history():
for item in [history.last_sent, history.last_received]:
print(etree.tostring(item["envelope"], encoding="unicode", pretty_print=True))

Here are the error messages (and general output) I get for my efforts:

SEP00AF1F9zzzzz --- Captain Matticus --- {D58FB7A2-E0FF-8C5E-091A-6DAB641271AE}
starting updatephone...
Traceback (most recent call last):
  File "C:\Users\me\eclipsePython-workspace\AXL-Demo5\testcode.py", line 61, in <module>
    resp = service.updatePhone({'Uuid': Tuuid},{'newdescription': newDesc})
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\proxy.py", line 45, in __call__
    kwargs,
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\wsdl\bindings\soap.py", line 119, in send
    operation, args, kwargs, client=client, options=options
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\wsdl\bindings\soap.py", line 68, in _create
    serialized = operation_obj.create(*args, **kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\wsdl\definitions.py", line 215, in create
    return self.input.serialize(*args, **kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\wsdl\messages\soap.py", line 68, in serialize
    body_value = self.body(*args, **kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\xsd\elements\element.py", line 57, in __call__
    instance = self.type(*args, **kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\xsd\types\complex.py", line 49, in __call__
    return self._value_class(*args, **kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\xsd\valueobjects.py", line 95, in __init__
    items = _process_signature(self._xsd_type, args, kwargs)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\xsd\valueobjects.py", line 185, in _process_signature
    values, args, index = element.parse_args(args, index)
  File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\zeep\xsd\elements\indicators.py", line 129, in parse_args
    raise TypeError("Choice elements only work with keyword arguments")
TypeError: Choice elements only work with keyword arguments

can anyone enlighten me?

 

thanks!

1 Accepted Solution

Accepted Solutions

May I recommend this learning lab?

https://developer.cisco.com/learning/lab/python-zeep-axl-lab/step/1

In step 4, you'll see this:

line_data = {
    'line': {
        'pattern': LINEDN,
        'description': 'Test Line',
        'usage': 'Device',
        'routePartitionName': None
    }
}

# the ** before line_data tells the Python function to expect
# an unspecified number  of keyword/value pairs

try:
  line_resp = service.addLine(**line_data)
except Fault as err:
etc...

As you can see, I pass json as the argument, and prefix it with ** in order to tell the API to expect an unspecified number of keyword/value pairs.  There are probably other ways to pass values to the AXL API, but this always works best for me.

 

Check out the next page for a possible hiccup.  Sometimes there isn't a subkey name when an XML tag would have a number of sub-tags.  So I simply substitute '_value_1' for the name. A great way to predict what AXL/Zeep will understand is to run a query like getPhone or whatever, and look at what it returns.  Then use that as a general guide for what to specify.  Keep in mind, though, that you can't do a getPhone and then modify some values and then use it to do an updatePhone.  The two APIs are not mirrored - they take different json/XML structure. 

 

phone_data = {
    'phone': {
        'name': PHONEID,
        'description': PHONEID,
        'product': 'Cisco 8821',
        'class': 'Phone',
        'protocol': 'SIP',
        'devicePoolName': {
            '_value_1': 'Default'
        },
        'commonPhoneConfigName': {
            '_value_1': 'Standard Common Phone Profile'
        },

What I usually do is try a request in SoapUI, and when I have it working, I convert the XML (in my head) to what it would look like as JSON.  There are ways in Python to convert the working XML to working JSON and vice versa, but I don't use them.

 

View solution in original post

3 Replies 3

Keith Abbott
Level 1
Level 1

 

Hi Group,

 

I got the test code to run. The successful code is shown below - along with console output below that.

I found  a few errors:

first - in this case the correct action was 'updatePhone' not 'updatePhoneReq' - and you really have to pay attention to case for all of the AXL stuff - actions and passed data names

second, I was trying to pass the parameters as tuples - that didnt work. Im not sure what you call how they are actually sent - they look like normal variable assignments separated by commas.

Here is the working code:

from test.test_importlib.util import CASE_INSENSITIVE_FS
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

disable_warnings(InsecureRequestWarning)
 
username = 'administrator'
password = 'mySecretPassword'
# If you're not disabling SSL verification, host should be the FQDN of the server rather than IP
host = 'myServer.Mycompany.com'
 
wsdl = 'file://C:/AXLdev/AXLAPI.wsdl'
location = 'https://{host}:8443/axl/'.format(host=host)
binding = "{http://www.cisco.com/AXLAPIService/}AXLAPIBinding"

mac = "SEP00AF1F9Czzzz"

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])
service = client.create_service(binding, location)

try:
    resp = service.listPhone(searchCriteria={'name': mac}, 
                             returnedTags={'name': '', 'description': '', 
                                           'callingSearchSpaceName': ''})
    
    phone_list = resp['return'].phone
    for phone in phone_list:
        print(phone.name + " --- " + phone.description + " --- " + phone.uuid)
        Tuuid = phone.uuid
except Fault:
    show_history()
    
newDesc = "Ginger Grant"
PHONEID = phone.name

phone_data = {
    'name': PHONEID,
    'newdescription': newDesc,
    'Uuid': Tuuid
    }
#
service = client.create_service(binding, location)
print("starting updatephone...")
try:
#    resp = service.updatePhone({'Uuid': Tuuid},{'newdescription': newDesc})  THIS IS THE FAILED VERSION
    
    resp = service.updatePhone( 
        name = PHONEID,
        description = newDesc,
         )


    print("after-update...")
except Fault:
    print("in fault processing")
    show_history()
def show_history():
    for item in [history.last_sent, history.last_received]:
        print(etree.tostring(item["envelope"], encoding="unicode", pretty_print=True))

#============ output ===========
SEP00AF1F9Cxxxx --- Jonas Grumby --- {D58FB7A2-E0FF-8C5E-091A-6DAB641271AE}
starting updatephone...
after-update...

The format of sending the the data is really confusing to me because the full code Im using includes the following code:

    resp = service.listPhone(searchCriteria={'name': mac}, 
                             returnedTags={'name': '', 'description': '', 
                                           'callingSearchSpaceName': ''})

This code (for listPhone instead of updatePhone) works perfectly, and appears to use tuples - in any case the format is much different than with updatePhone.

 

How do you know what format to pass data in???

 

Also, the other questions remain - why do I find references to both updatePhone and updatePhoneReq?  Is this a difference in AXL versions?

 

and I still need to know where I can find an AXL schema reference for 10.5 (lab) and 11.5(live) - is this where I'll find answers to the above questions?

 

If anyone out there has AXL experience in Python, please chime in!

thanks

 

 

 

May I recommend this learning lab?

https://developer.cisco.com/learning/lab/python-zeep-axl-lab/step/1

In step 4, you'll see this:

line_data = {
    'line': {
        'pattern': LINEDN,
        'description': 'Test Line',
        'usage': 'Device',
        'routePartitionName': None
    }
}

# the ** before line_data tells the Python function to expect
# an unspecified number  of keyword/value pairs

try:
  line_resp = service.addLine(**line_data)
except Fault as err:
etc...

As you can see, I pass json as the argument, and prefix it with ** in order to tell the API to expect an unspecified number of keyword/value pairs.  There are probably other ways to pass values to the AXL API, but this always works best for me.

 

Check out the next page for a possible hiccup.  Sometimes there isn't a subkey name when an XML tag would have a number of sub-tags.  So I simply substitute '_value_1' for the name. A great way to predict what AXL/Zeep will understand is to run a query like getPhone or whatever, and look at what it returns.  Then use that as a general guide for what to specify.  Keep in mind, though, that you can't do a getPhone and then modify some values and then use it to do an updatePhone.  The two APIs are not mirrored - they take different json/XML structure. 

 

phone_data = {
    'phone': {
        'name': PHONEID,
        'description': PHONEID,
        'product': 'Cisco 8821',
        'class': 'Phone',
        'protocol': 'SIP',
        'devicePoolName': {
            '_value_1': 'Default'
        },
        'commonPhoneConfigName': {
            '_value_1': 'Standard Common Phone Profile'
        },

What I usually do is try a request in SoapUI, and when I have it working, I convert the XML (in my head) to what it would look like as JSON.  There are ways in Python to convert the working XML to working JSON and vice versa, but I don't use them.

 

npetrele,

 

THANKS!!!! that demo is the best help Ive found since I started working on this project!!! It includes a lot of the things Im looking for, in the format I wish to use (ie not going directly to xml)!

 

I grabbed the code, did some minor mods basically just to set for my environment, and when I run it I was getting some errors.

To reduce clutter, I opened a new thread showing the code and errors. Perhaps you can take a look and provide some input - in the meantime I'll keep digging.

 

https://community.cisco.com/t5/management/python-axl-zeep-failing-to-add-line/m-p/4035551#M3320

 

thanks again for the help