Important Note: This post is very outdated now. I’d recommend highly against using suds-jurko and instead use Zeep as the SOAP Library for Python. I’ve updated my original AXL Programming post, however due to lack of spare time I haven’t had a chance to update this one.
The fundamentals of the post are perhaps still relevant but the code likely won’t even work due to API Changes in more recent versions of CUCM.
——————————
About one year ago I wrote a post Getting Started with Python CUCM AXL API Programming, consolidating some of the information I’d gathered in using a popular SOAP library for Python “suds-jurko”. Since I’ve done quite a bit more over the last year with CUCM AXL and suds-jurko, I thought I’d write a follow up post to demonstrate a few more practical scenarios that might give people ideas for their own custom scripts / applications.
Update Directory Numbers
from suds.client import Client
from suds.xsd.doctor import Import
from suds.xsd.doctor import ImportDoctor
wsdl = 'file:///C:/Development/axlsqltoolkit/schema/current/AXLAPI.wsdl'
location = 'https://cucmpub.lab.local:8443/axl/'
username = 'admin'
password = 'Cisco123'
tns = 'http://schemas.cisco.com/ast/soap/'
imp = Import('http://schemas.xmlsoap.org/soap/encoding/',
'http://schemas.xmlsoap.org/soap/encoding/')
imp.filter.add(tns)
client = Client(wsdl,location=location,faults=False,plugins=[ImportDoctor(imp)],
username=username,password=password)
resp = client.service.listLine(searchCriteria={'pattern' : '5%',
'routePartitionName' : 'dCloud_PT'},
returnedTags={'pattern' : '', 'routePartitionName' : ''})
print resp
(200, (reply){
return =
(return){
line[] =
(LLine){
_uuid = "{0BD5A715-60A9-BB1E-58F1-440ECE619E13}"
pattern = "5211"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
},
(LLine){
_uuid = "{A9D7208A-B9D6-8247-5B79-E0DAFD1500FF}"
pattern = "5212"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
},
(LLine){
_uuid = "{A8EA818F-0B86-995E-8D4B-31E078CE881E}"
pattern = "5213"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
},
}
})
lines = resp[1]['return'].line
print lines
[(LLine){
_uuid = "{0BD5A715-60A9-BB1E-58F1-440ECE619E13}"
pattern = "5211"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
}, (LLine){
_uuid = "{A9D7208A-B9D6-8247-5B79-E0DAFD1500FF}"
pattern = "5212"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
}, (LLine){
_uuid = "{A8EA818F-0B86-995E-8D4B-31E078CE881E}"
pattern = "5213"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
}]
for line in lines:
print line.pattern, line.routePartitionName.value
5211 dCloud_PT
5212 dCloud_PT
5213 dCloud_PT
for line in lines:
new_pattern = "+4930555" + line.pattern
resp = client.service.updateLine(pattern=line.pattern,
routePartitionName=line.routePartitionName.value,
newPattern=new_pattern)
if resp[0] == 200:
print "Successfully Updated DN {} to {}".format(line.pattern, new_pattern)
else:
print "Error encountered updating DN {} to {}".format(line.pattern, new_pattern)
Successfully Updated DN 5211 to +49305555211
Successfully Updated DN 5212 to +49305555212
Successfully Updated DN 5213 to +49305555213
from suds.client import Client
from suds.xsd.doctor import Import
from suds.xsd.doctor import ImportDoctor
wsdl = 'file:///C:/Development/axlsqltoolkit/schema/current/AXLAPI.wsdl'
location = 'https://cucm1.dcloud.cisco.com:8443/axl/'
username = 'axl_admin'
password = 'dCloud12345!'
tns = 'http://schemas.cisco.com/ast/soap/'
imp = Import('http://schemas.xmlsoap.org/soap/encoding/',
'http://schemas.xmlsoap.org/soap/encoding/')
imp.filter.add(tns)
client = Client(wsdl,location=location,faults=False,plugins=[ImportDoctor(imp)],
username=username,password=password)
resp = client.service.listLine(searchCriteria={'pattern' : '5%',
'routePartitionName' : 'dCloud_PT'},
returnedTags={'pattern' : '', 'routePartitionName' : ''})
for line in lines:
new_pattern = "+4930555" + line.pattern
resp = client.service.updateLine(pattern=line.pattern,
routePartitionName=line.routePartitionName.value,
newPattern=new_pattern)
if resp[0] == 200:
print "Successfully Updated DN {} to {}".format(line.pattern, new_pattern)
else:
print "Error encountered updating DN {} to {}".format(line.pattern, new_pattern)
This is just an example for changing the pattern itself, but in the AXL Schema documentation you can find many other attributes that you might want to modify (like CFW Calling Search Space, AAR Masks, External Phone Number Masks, etc.)
Update Telephone, Line and Associations
The next example will be a script that takes an existing Device and User as input, and performs a number of actions:
User:
- Adds the Device as a “Controlled CTI Device”
- Adds the user groups Standard CCM End User and Standard CTI Enabled
- Updates the “Primary Directory Number” to the DN assigned to Line 1 of the Device
Device:
- Updates the “Owner UserID” on the Device
- Updates the Device Description with the first & last names of the user and the directory number
Line 1 on the Device:
- Updates the Line Text Label (Note: this is actually the Line Appearance from the Device)
- Updates the Line Description
- Updates the Alerting Name
- Updates the Display Name (Note: this is also the Line Appearance from the Device)
Firstly, let’s define the User and the Device we’re dealing with, then get the Device Details:
phone = 'SEP123456654320'
userid = 'john.doe'
get_phone_resp = client.service.getPhone(name='SEP123456654320')
print get_phone_resp
(200, (reply){
return =
(return){
phone =
(RPhone){
_ctiid = 179
_uuid = "{20DBA4D8-0055-A1CE-706D-7C7237021F21}"
name = "SEP123456654320"
description = "Tanya Adams - 8861 MRA - X6024"
product = "Cisco 8861"
model = "Cisco 8861"
cls = "Phone"
protocol = "SIP"
protocolSide = "User"
callingSearchSpaceName =
(callingSearchSpaceName){
value = "dCloud_CSS"
_uuid = "{3A321D3D-0919-0C97-AF0C-F80E3CFB6695}"
}
devicePoolName =
(devicePoolName){
value = "dCloud_DP"
_uuid = "{1B1B9EB6-7803-11D3-BDF0-00108302EAD1}"
}
commonDeviceConfigName =
(commonDeviceConfigName){
value = "dCloud Common Device Configuration"
_uuid = "{9F0F7D5F-89A4-136C-20C0-90F3E54D9BCB}"
}
commonPhoneConfigName =
(commonPhoneConfigName){
value = "Standard Common Phone Profile"
_uuid = "{AC243D17-98B4-4118-8FEB-5FF2E1B781AC}"
}
networkLocation = "Use System Default"
locationName =
(locationName){
value = "dCloud_Location"
_uuid = "{B59F63DC-F9FE-16CD-6C9B-9AB84F7AC572}"
}
mediaResourceListName =
(mediaResourceListName){
value = "dCloud_MRGL"
_uuid = "{2A76E338-7EF1-9B12-4BDE-E1DC7A04BE5C}"
}
networkHoldMohAudioSourceId = "1"
userHoldMohAudioSourceId = "1"
automatedAlternateRoutingCssName = ""
aarNeighborhoodName = ""
loadInformation =
(loadInformation){
value = "sip88xx.11-5-1-18"
_special = "false"
}
vendorConfig =
(XVendorConfig){
disableSpeaker[] =
"false",
disableSpeakerAndHeadset[] =
"false",
pcPort[] =
"0",
voiceVlanAccess[] =
"0",
webAccess[] =
"0",
spanToPCPort[] =
"1",
recordingTone[] =
"0",
recordingToneLocalVolume[] =
"100",
recordingToneRemoteVolume[] =
"50",
powerPriority[] =
"0",
minimumRingVolume[] =
"0",
ehookEnable[] =
"0",
headsetWidebandUIControl[] =
"0",
headsetWidebandEnable[] =
"0",
garp[] =
"1",
allCallsOnPrimary[] =
"0",
g722CodecSupport[] =
"0",
webAdmin[] =
"0",
}
versionStamp = "{1453772260-43933CAB-193A-4C47-8A6F-773C5B65CCE1}"
traceFlag = "false"
mlppDomainId = ""
mlppIndicationStatus = "Default"
preemption = "Default"
useTrustedRelayPoint = "Default"
retryVideoCallAsAudio = "true"
securityProfileName =
(securityProfileName){
value = "Cisco 8861 - Standard SIP Non-Secure Profile"
_uuid = "{C7D7F171-28C8-4196-94A3-C9C5471DD5AA}"
}
sipProfileName =
(sipProfileName){
value = "dCloud Standard SIP Profile"
_uuid = "{7E2A3A02-A350-6A17-2967-BDF2998929E3}"
}
cgpnTransformationCssName = ""
useDevicePoolCgpnTransformCss = "true"
geoLocationName = ""
geoLocationFilterName = ""
sendGeoLocation = "false"
lines =
(lines){
line[] =
(RPhoneLine){
_uuid = "{8A34879B-4587-29EC-9788-249762960A89}"
index = "1"
label = "Tanya Adams - X6024"
display = "Tanya Adams - X6024"
dirn =
(RDirn){
_uuid = "{A7C50E64-6B3F-2C1F-611A-CCC24ED80D05}"
pattern = "+19725556024"
routePartitionName =
(routePartitionName){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
}
ringSetting = "Use System Default"
consecutiveRingSetting = "Use System Default"
ringSettingIdlePickupAlert = "Use System Default"
ringSettingActivePickupAlert = "Use System Default"
displayAscii = "Tanya Adams - X6024"
e164Mask = "+1972555XXXX"
dialPlanWizardId = ""
mwlPolicy = "Use System Policy"
maxNumCalls = "6"
busyTrigger = "2"
callInfoDisplay =
(callInfoDisplay){
callerName = "true"
callerNumber = "false"
redirectedNumber = "false"
dialedNumber = "true"
}
recordingProfileName =
(recordingProfileName){
value = "MediaSense"
_uuid = "32c8f8a7-1387-dc6e-5293-2c4b4572a4bb"
}
monitoringCssName = ""
recordingFlag = "Selective Call Recording Enabled"
audibleMwi = "Default"
speedDial = None
partitionUsage = "General"
associatedEndusers =
(associatedEndusers){
enduser[] =
(REnduserMember){
userId = "tadams"
},
}
missedCallLogging = "true"
recordingMediaSource = "Gateway Preferred"
},
}
numberOfButtons = "10"
phoneTemplateName =
(phoneTemplateName){
value = "Standard 8861 SIP"
_uuid = "{3987DE44-582C-4E18-9691-7B07FBF30BCF}"
}
speeddials = ""
busyLampFields = ""
primaryPhoneName = ""
ringSettingIdleBlfAudibleAlert = "Default"
ringSettingBusyBlfAudibleAlert = "Default"
blfDirectedCallParks = ""
addOnModules = ""
userLocale = "English United States"
networkLocale = "United States"
idleTimeout = ""
authenticationUrl = None
directoryUrl = None
idleUrl = None
informationUrl = None
messagesUrl = None
proxyServerUrl = None
servicesUrl = None
services = ""
softkeyTemplateName = ""
loginUserId = ""
defaultProfileName = ""
enableExtensionMobility = "true"
currentProfileName = ""
loginTime = ""
loginDuration = ""
currentConfig =
(currentConfig){
userHoldMohAudioSourceId = "1"
phoneTemplateName =
(phoneTemplateName){
value = "Standard 8861 SIP"
_uuid = "{3987DE44-582C-4E18-9691-7B07FBF30BCF}"
}
mlppDomainId = ""
mlppIndicationStatus = "Default"
preemption = "Default"
softkeyTemplateName = ""
ignorePresentationIndicators = "false"
singleButtonBarge = "Off"
joinAcrossLines = "Off"
callInfoPrivacyStatus = "Default"
dndStatus = ""
dndRingSetting = ""
dndOption = "Use Common Phone Profile Setting"
alwaysUsePrimeLine = "Default"
alwaysUsePrimeLineForVoiceMessage = "Default"
emccCallingSearchSpaceName =
(XFkType){
_uuid = ""
}
deviceName = ""
model = ""
product = ""
deviceProtocol = ""
cls = ""
addressMode = ""
allowAutoConfig = ""
remoteSrstOption = ""
remoteSrstIp = ""
remoteSrstPort = ""
remoteSipSrstIp = ""
remoteSipSrstPort = ""
geolocationInfo = ""
remoteLocationName = ""
}
singleButtonBarge = "Off"
joinAcrossLines = "Off"
builtInBridgeStatus = "On"
callInfoPrivacyStatus = "Default"
hlogStatus = "On"
ownerUserName =
(ownerUserName){
value = "tadams"
_uuid = "61a1d409-6ff5-449d-5988-33f1722888b9"
}
ignorePresentationIndicators = "false"
packetCaptureMode = "None"
packetCaptureDuration = "0"
subscribeCallingSearchSpaceName = ""
rerouteCallingSearchSpaceName = ""
allowCtiControlFlag = "true"
presenceGroupName =
(presenceGroupName){
value = "Standard Presence group"
_uuid = "{AD243D17-98B4-4118-8FEB-5FF2E1B781AC}"
}
unattendedPort = "false"
requireDtmfReception = "false"
rfc2833Disabled = "false"
certificateOperation = "No Pending Operation"
certificateStatus = "None"
upgradeFinishTime = None
deviceMobilityMode = "Default"
remoteDevice = "false"
dndOption = "Use Common Phone Profile Setting"
dndRingSetting = ""
dndStatus = "false"
isActive = "true"
isDualMode = "false"
mobilityUserIdName = ""
phoneSuite = "Default"
phoneServiceDisplay = "Default"
isProtected = "false"
mtpRequired = "false"
mtpPreferedCodec = "711ulaw"
dialRulesName = ""
sshUserId = ""
digestUser = ""
outboundCallRollover = "No Rollover"
hotlineDevice = "false"
secureInformationUrl = ""
secureDirectoryUrl = ""
secureMessageUrl = ""
secureServicesUrl = ""
secureAuthenticationUrl = ""
secureIdleUrl = ""
alwaysUsePrimeLine = "Default"
alwaysUsePrimeLineForVoiceMessage = "Default"
featureControlPolicy = ""
deviceTrustMode = "Not Trusted"
confidentialAccess =
(confidentialAccess){
confidentialAccessMode = ""
confidentialAccessLevel = "-1"
}
requireOffPremiseLocation = "false"
cgpnIngressDN = ""
useDevicePoolCgpnIngressDN = "true"
msisdn = ""
enableCallRoutingToRdWhenNoneIsActive = "false"
wifiHotspotProfile = ""
wirelessLanProfileGroup = ""
}
}
})
Next we can update the user, to add the CTI Device Association, the user groups, and update the Primary Directory Number to that of the associated Device.
# Define the list of associated devices.
# This is a list of 0 or more SEPXXXXXXXX tags
phone = resp[1]['return'].phone.name
associated_devices = [{'device' : resp[1]['return'].phone.name}]
# Define the Groups to be added to the user.
# Under "userGroup" is a list of GroupName tags.
associated_groups = {'userGroup' : [{'name' : 'Standard CCM End Users'},
{'name' : 'Standard CTI Enabled'}]}
# Get the DN Pattern and Partition of the first line from the getPhone query
dn_pattern = resp[1]['return'].phone.lines.line[0].dirn.pattern
dn_partition = resp[1]['return'].phone.lines.line[0].dirn.routePartitionName.value
# Update the user
update_user_resp = client.service.updateUser(userid=userid,
associatedDevices=associated_devices,
associatedGroups=associated_groups,
primaryExtension={'pattern' : dn_pattern,
'routePartitionName' : dn_partition})
print update_user_resp
(200, (reply){
return = "{86EFA455-4753-0DF4-2501-477E8EAC68FE}"
})
Next we need to gather the details of the user to be able to update the Device / Line.
# Get the User Details
get_user_resp = client.service.getUser(userid=userid)
print get_user_resp
(200, (reply){
return =
(return){
user =
(RUser){
_uuid = "{86EFA455-4753-0DF4-2501-477E8EAC68FE}"
firstName = "John"
middleName = None
lastName = "Doe"
userid = "john.doe"
password = None
pin = None
mailid = "john.doe@cisco.com"
department = None
manager = None
userLocale = ""
associatedDevices =
(associatedDevices){
device[] =
"SEP123456654320",
}
primaryExtension =
(primaryExtension){
pattern = "+19725556024"
routePartitionName = "dCloud_PT"
}
associatedPc = None
associatedGroups =
(associatedGroups){
userGroup[] =
(userGroup){
name = "Standard CCM End Users"
userRoles =
(userRoles){
userRole[] =
"Standard CCM End Users",
"Standard CCMUSER Administration",
}
},
(userGroup){
name = "Standard CTI Enabled"
userRoles =
(userRoles){
userRole[] =
"Standard CTI Enabled",
}
},
}
enableCti = "true"
digestCredentials = None
phoneProfiles = ""
defaultProfile = ""
presenceGroupName =
(presenceGroupName){
value = "Standard Presence group"
_uuid = "{AD243D17-98B4-4118-8FEB-5FF2E1B781AC}"
}
subscribeCallingSearchSpaceName = ""
enableMobility = "false"
enableMobileVoiceAccess = "false"
maxDeskPickupWaitTime = "10000"
remoteDestinationLimit = "4"
associatedRemoteDestinationProfiles = ""
passwordCredentials =
(passwordCredentials){
pwdCredPolicyName = "dCloud Credential Policy"
pwdCredUserCantChange = "false"
pwdCredUserMustChange = "false"
pwdCredDoesNotExpire = "true"
pwdCredTimeChanged = "May 27, 2017 08:14:28 CDT"
pwdCredTimeAdminLockout = None
pwdCredLockedByAdministrator = "false"
}
pinCredentials =
(pinCredentials){
pinCredPolicyName = "dCloud Credential Policy"
pinCredUserCantChange = "false"
pinCredUserMustChange = "false"
pinCredDoesNotExpire = "true"
pinCredTimeChanged = "May 27, 2017 08:14:27 CDT"
pinCredTimeAdminLockout = None
pinCredLockedByAdministrator = "false"
}
associatedTodAccess = ""
status = "1"
enableEmcc = "false"
associatedCapfProfiles = ""
ctiControlledDeviceProfiles = ""
patternPrecedence = ""
numericUserId = None
mlppPassword = None
customUserFields = ""
homeCluster = "true"
imAndPresenceEnable = "false"
serviceProfile =
(serviceProfile){
value = "dCloud_Service-Profile"
_uuid = "{4B5DE118-6185-A12B-6164-EF523DE23753}"
}
lineAppearanceAssociationForPresences =
(lineAppearanceAssociationForPresences){
lineAppearanceAssociationForPresence[] =
(RLineAppearanceAssociationForPresence){
_uuid = "{8A34879B-4587-29EC-9788-249762960A89}"
laapAssociate = "t"
laapProductType = "Cisco 8861"
laapDeviceName = "SEP123456654320"
laapDirectory = "+19725556024"
laapPartition = "dCloud_PT"
laapDescription = "Tanya Adams - 8861 MRA - X6024"
},
}
directoryUri = "john.doe@cisco.com"
telephoneNumber = None
title = None
mobileNumber = None
homeNumber = None
pagerNumber = None
extensionsInfo =
(extensionsInfo){
extension[] =
(RExtension){
_uuid = "{840FB340-0348-3BD2-55CA-B4704987DF48}"
sortOrder = "0"
pattern =
(pattern){
value = "+19725556024"
_uuid = "{A7C50E64-6B3F-2C1F-611A-CCC24ED80D05}"
}
routePartition = "dCloud_PT"
linePrimaryUri = "tadams@sip.dcloud.cisco.com"
partition =
(partition){
value = "dCloud_PT"
_uuid = "{99C5A8FE-DD48-6E6A-CDEC-42262292EB98}"
}
},
}
selfService = "19725556024"
userProfile = ""
calendarPresence = "false"
ldapDirectoryName = ""
userIdentity = None
nameDialing = "DoeJohn"
ipccExtension = ""
convertUserAccount = ""
}
}
})
Here, using the attributes of the user, we can update the Device Description
# Gather the attributes needed to update the Telephone and Line
first_name = get_user_resp[1]['return'].user.firstName
last_name = get_user_resp[1]['return'].user.lastName
# Define Description in the format "FirstName LastName - DirectoryNumber"
# Because the DN in this case is +E.164 with a
# we have to skip the otherwise it's not in a valid format.
description = "{} {} - {}".format(first_name, last_name, dn_pattern[1:])
# First, get the line appearance from the getDevice query initiated earlier
line = get_phone_resp[1]['return'].phone.lines.line[0]
# Next define the updated line appearance variables
# For the Label, we'll use "LastName - 5-Digit-Ext"
line.label = last_name + " - " + dn_pattern[-5:]
# For the Display, it'll be "FirstName LastName"
line.display = first_name + " " + last_name
line.displayAscii = first_name + " " + last_name
# Update the Phone with a Description consisting of the First Name,
# Last Name and Directory Number
update_phone_resp = client.service.updatePhone(name=phone,
description=description,
ownerUserName=userid,
lines={'line' : line})
print update_phone_resp
(200, (reply){
return = "{20DBA4D8-0055-A1CE-706D-7C7237021F21}"
})
And then the Line Description, Alerting Name, Display Name, Line Text Label, and Line Association.
# Next update the Line itself for non-line appearance settings such as the Alerting Name and Description
update_line_resp = client.service.updateLine(pattern=dn_pattern,
routePartitionName=dn_partition,
description=description,
alertingName=first_name + " " + last_name,
asciiAlertingName=first_name + " " + last_name)
print update_line_resp
(200, (reply){
return = "{A7C50E64-6B3F-2C1F-611A-CCC24ED80D05}"
})
And there you have it. Here is the summarised script:
from suds.client import Client
from suds.xsd.doctor import Import
from suds.xsd.doctor import ImportDoctor
wsdl = 'file:///C:/Development/axlsqltoolkit/schema/current/AXLAPI.wsdl'
location = 'https://cucm1.dcloud.cisco.com:8443/axl/'
username = 'axl_admin'
password = 'dCloud12345!'
tns = 'http://schemas.cisco.com/ast/soap/'
imp = Import('http://schemas.xmlsoap.org/soap/encoding/',
'http://schemas.xmlsoap.org/soap/encoding/')
imp.filter.add(tns)
client = Client(wsdl,location=location,faults=False,plugins=[ImportDoctor(imp)],
username=username,password=password)
# Specify the Phone Name and UserID to associate with one another.
# You could also have these input as arguments at the command line rather than static in the script.
phone = 'SEP123456654320'
userid = 'john.doe'
# Get the Phone
get_phone_resp = client.service.getPhone(name=phone)
# Define the list of associated devices for the user.
# This is a list of 0 or more SEPXXXXXXXX tags
phone = get_phone_resp[1]['return'].phone.name
associated_devices = [{'device' : phone}]
# Define the Groups to be added to the user.
# Under "userGroup" is a list of GroupName tags.
associated_groups = {'userGroup' : [{'name' : 'Standard CCM End Users'},
{'name' : 'Standard CTI Enabled'}]}
# Get the DN Pattern and Partition of the first line from the getPhone query
dn_pattern = get_phone_resp[1]['return'].phone.lines.line[0].dirn.pattern
dn_partition = get_phone_resp[1]['return'].phone.lines.line[0].dirn.routePartitionName.value
# Update the user
update_user_resp = client.service.updateUser(userid=userid,
associatedDevices=associated_devices,
associatedGroups=associated_groups,
primaryExtension={'pattern' : dn_pattern,
'routePartitionName' : dn_partition})
# Get the User Details
get_user_resp = client.service.getUser(userid=userid)
# Gather the attributes needed to update the Telephone and Line
first_name = get_user_resp[1]['return'].user.firstName
last_name = get_user_resp[1]['return'].user.lastName
# Define Description in the format "FirstName LastName - DirectoryNumber"
# Because the DN in this case is +E.164 with a
# we have to skip the otherwise it's not in a valid format.
description = "{} {} - {}".format(first_name, last_name, dn_pattern[1:])
# First, get the line appearance from the getDevice query initiated earlier
line = get_phone_resp[1]['return'].phone.lines.line[0]
# Next define the updated line appearance variables
# For the Label, we'll use "LastName - 5-Digit-Ext"
line.label = last_name + " - " + dn_pattern[-5:]
# For the Display, it'll be "FirstName LastName"
line.display = first_name + " " + last_name
line.displayAscii = first_name + " " + last_name
# Update the Phone with a Description consisting of the First Name,
# Last Name and Directory Number
update_phone_resp = client.service.updatePhone(name=phone,
description=description,
ownerUserName=userid,
lines={'line' : line})
print update_phone_resp
# Next update the Line itself for non-line appearance settings
# such as the Alerting Name and Description
update_line_resp = client.service.updateLine(pattern=dn_pattern,
routePartitionName=dn_partition,
description=description,
alertingName=first_name + " " + last_name,
asciiAlertingName=first_name + " " + last_name)
Wrap-Up / Tips
-
- If you’re getting syntax errors, check the CUCM AXL Schema pages and try and get a better understanding of what inputs it requires for updates / adds. Sometimes it’s a little more convoluted than you think it will be. A good example was the “associatedGroups” attribute in the updateUser method. There I find it helps to think of how suds will take your dict data and transform it into XML.For example:
<associatedGroups>
<userGroup>
<name>Standard CCM End Users</name>
</userGroup>
<userGroup>
<name>Standard CTI Enabled</name>
</userGroup>
</associatedGroups>May look like this when represented as keyword arguments with a Python dictionary as the value:
associatedGroups={‘userGroup’ : [{‘name’ : ‘Standard CCM End User’}, {‘name’ : ‘Standard CCM End User’}]}suds-jurko supports the logging module, so you can also turn this on and view all of your requests, and there the mistakes might appear obvious.
- If you’re getting syntax errors, check the CUCM AXL Schema pages and try and get a better understanding of what inputs it requires for updates / adds. Sometimes it’s a little more convoluted than you think it will be. A good example was the “associatedGroups” attribute in the updateUser method. There I find it helps to think of how suds will take your dict data and transform it into XML.For example:
-
- If you’re not getting errors, but you don’t see any actual changes, you’ve probably screwed up your syntax. It can be a little trial and error (in fact, it was for me writing this post too, especially with the user groups).
- These scripts won’t work as written if your CUCM has SAML SSO activated. I used a dCloud instance and deactivated SSO. For SSO you’ll need to modify the boilerplate code to be the following. This ensures the authentication header is included in each request suds sends. (https://stackoverflow.com/questions/11742494/python-soap-client-wsdl-call-with-suds-gives-transport-error-401-unauthorized-f)
from suds.client import Client
from suds.xsd.doctor import Import
from suds.xsd.doctor import ImportDoctor
from suds.plugin import MessagePlugin
import base64
wsdl = 'file:///C:/Development/axlsqltoolkit/schema/current/AXLAPI.wsdl'
location = 'https://cucm1.dcloud.cisco.com:8443/axl/'
username = 'amckenzie'
password = 'dCloud12345!'
base64string = base64.encodestring('%s:%s' % (username, password)).replace('n', '')
authenticationHeader = {
"SOAPAction" : "ActionName",
"Authorization" : "Basic %s" % base64string
}
tns = 'http://schemas.cisco.com/ast/soap/'
imp = Import('http://schemas.xmlsoap.org/soap/encoding/',
'http://schemas.xmlsoap.org/soap/encoding/')
imp.filter.add(tns)
client = Client(wsdl,location=location,faults=False,plugins=[ImportDoctor(imp)],
headers=authenticationHeader)
This comment has been removed by the author.
Hello, nice information. Thanks!
Maybe you can help me here. I'm trying to achieve the following:
For every phone in a list, I need to know which user is controlling that device.
I already know how to get phone lists and user lists. I have been able also to get all the details of a particular phone and user (i haven't been able of extracting only two or three fields with getPhone or getUser).
I guess one method is looping through the phones in the list, and looping inside through all the users. Inside this loop, we could calculate the length of the controlled devices list for the user, and finally loop again through all the controlled devices of the user. If the devicename in controlled devices matches phone devicename, that's it.
But I found this method quite intensive, since I have previously had to get all the info for all the phones and all the users from CUCM. Additionally I have had to loop through all the users for every phone in the list. I dont know if its a more efficient way to accomplish this.
Thanks and best regards.
Hi Floyder,
With this you're best off using the "executeSQLQuery" method as this is much more easily pulled direct from the database. I tend to build up a query and test it on the command line before I use it in a script.
Check out this link:
https://www.cisco.com/c/en/us/support/docs/unified-communications/unified-communications-manager-callmanager/117780-technote-cucm-00.html
The first query they list there is with an application user, but if you change it to enduser you'll likely get what you want. Google for the CUCM Database dictionary to get more details about the table relationships(i.e. enduser to enduserdevicemap, etc).
Or even better with this link (final query in the list):
https://www.cisco.com/c/en/us/support/docs/unified-communications/unified-communications-manager-callmanager/117726-technote-cucm-00.html#anc11