|
| 1 | +"""AXL <addLine>, <addPhone>, <addUser>, <updatePhone>, <getUser> sample script, using the zeep library |
| 2 | +
|
| 3 | +Install Python 2.7 or 3.7 |
| 4 | +On Windows, choose the option to add to PATH environment variable |
| 5 | +
|
| 6 | +If this is a fresh installation, update pip |
| 7 | +
|
| 8 | + $ python -m pip install --upgrade pip |
| 9 | +
|
| 10 | +Script Dependencies: |
| 11 | + lxml |
| 12 | + requests |
| 13 | + zeep |
| 14 | +
|
| 15 | +Depencency Installation: |
| 16 | +
|
| 17 | + $ pip install zeep |
| 18 | +
|
| 19 | +This will install automatically all of zeep dependencies, including lxml, requests |
| 20 | +
|
| 21 | +Copyright (c) 2018 Cisco and/or its affiliates. |
| 22 | +Permission is hereby granted, free of charge, to any person obtaining a copy |
| 23 | +of this software and associated documentation files (the "Software"), to deal |
| 24 | +in the Software without restriction, including without limitation the rights |
| 25 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 26 | +copies of the Software, and to permit persons to whom the Software is |
| 27 | +furnished to do so, subject to the following conditions: |
| 28 | +The above copyright notice and this permission notice shall be included in all |
| 29 | +copies or substantial portions of the Software. |
| 30 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 31 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 32 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 33 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 34 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 35 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 36 | +SOFTWARE. |
| 37 | +""" |
| 38 | + |
| 39 | + |
| 40 | +from lxml import etree |
| 41 | +from requests import Session |
| 42 | +from requests.auth import HTTPBasicAuth |
| 43 | + |
| 44 | +from zeep import Client, Settings, Plugin |
| 45 | +from zeep.transports import Transport |
| 46 | +from zeep.cache import SqliteCache |
| 47 | +from zeep.plugins import HistoryPlugin |
| 48 | + |
| 49 | +WSDL_URL = 'AXLAPI.wsdl' |
| 50 | + |
| 51 | +# These are sample values for DevNet sandbox |
| 52 | +# replace them with values for your own CUCM, if needed |
| 53 | + |
| 54 | +CUCM_URL = 'https://10.10.20.1:8443/axl/' |
| 55 | +USERNAME = 'administrator' |
| 56 | +PASSWD = 'ciscopsdt' |
| 57 | + |
| 58 | +# If you have a pem file certificate for CUCM, uncomment and define it here |
| 59 | + |
| 60 | +#CERT = 'some.pem' |
| 61 | + |
| 62 | +# These values should work with a DevNet sandbox |
| 63 | +# You may need to change them if you are working with your own CUCM server |
| 64 | + |
| 65 | +LINEDN = '1111' |
| 66 | +PHONEID = 'SEP151515151515' |
| 67 | +USERFNAME = 'johnq' |
| 68 | +USERLNAME = 'public' |
| 69 | +USERPASS = 'public' |
| 70 | + |
| 71 | +# history shows http_headers |
| 72 | +history = HistoryPlugin() |
| 73 | + |
| 74 | +# This class lets you view the incoming and outgoing http headers and/or XML |
| 75 | +class MyLoggingPlugin(Plugin): |
| 76 | + |
| 77 | + def ingress(self, envelope, http_headers, operation): |
| 78 | +# uncomment the next line to see incoming XML |
| 79 | +# print(etree.tostring(envelope, pretty_print=True)) |
| 80 | + return envelope, http_headers |
| 81 | + |
| 82 | + def egress(self, envelope, http_headers, operation, binding_options): |
| 83 | +# uncomment the next line to see outgoing XML |
| 84 | +# print(etree.tostring(envelope, pretty_print=True)) |
| 85 | + return envelope, http_headers |
| 86 | + |
| 87 | +# This is where the meat of the application starts |
| 88 | +# The first step is to create a SOAP client session |
| 89 | + |
| 90 | +session = Session() |
| 91 | + |
| 92 | +# We avoid certificate verification by default, but you can uncomment and set |
| 93 | +# your certificate here, and comment out the False setting |
| 94 | + |
| 95 | +#session.verify = CERT |
| 96 | +session.verify = False |
| 97 | +session.auth = HTTPBasicAuth(USERNAME, PASSWD) |
| 98 | + |
| 99 | +transport = Transport(session=session, timeout=10, cache=SqliteCache()) |
| 100 | + |
| 101 | +# strict=False is not always necessary, but it allows zeep to parse imperfect XML |
| 102 | +settings = Settings(strict=False, xml_huge_tree=True) |
| 103 | + |
| 104 | +client = Client(WSDL_URL, settings=settings, transport=transport, plugins=[MyLoggingPlugin(),history]) |
| 105 | + |
| 106 | +service = client.create_service("{http://www.cisco.com/AXLAPIService/}AXLAPIBinding", CUCM_URL) |
| 107 | + |
| 108 | +line_data = { |
| 109 | + 'line': { |
| 110 | + 'pattern': LINEDN, |
| 111 | + 'description': 'Test Line', |
| 112 | + 'usage': 'Device', |
| 113 | + 'routePartitionName': None |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +line_resp = service.addLine(**line_data) |
| 118 | + |
| 119 | +l = line_resp |
| 120 | +print("\naddLine response:\n") |
| 121 | +print(l,"\n") |
| 122 | +# uncomment these lines to see headers sent and received |
| 123 | +#print(history.last_sent) |
| 124 | +#print(history.last_received) |
| 125 | + |
| 126 | +phone_data = { |
| 127 | + 'phone': { |
| 128 | + 'name': PHONEID, |
| 129 | + 'description': PHONEID, |
| 130 | + 'product': 'Cisco 8821', |
| 131 | + 'class': 'Phone', |
| 132 | + 'protocol': 'SIP', |
| 133 | + 'devicePoolName': { |
| 134 | + '_value_1': 'Default' |
| 135 | + }, |
| 136 | + 'commonPhoneConfigName': { |
| 137 | + '_value_1': 'Standard Common Phone Profile' |
| 138 | + }, |
| 139 | + 'networkLocation': 'Use System Default', |
| 140 | + 'locationName': { |
| 141 | + '_value_1': 'Hub_None' |
| 142 | + }, |
| 143 | + 'mlppIndicationStatus': 'Default', |
| 144 | + 'preemption': 'Default', |
| 145 | + 'useTrustedRelayPoint': 'Default', |
| 146 | + 'retryVideoCallAsAudio': 'true', |
| 147 | + 'securityProfileName': { |
| 148 | + '_value_1': 'Cisco 8821 - Standard SIP Non-Secure Profile' |
| 149 | + }, |
| 150 | + 'sipProfileName': { |
| 151 | + '_value_1': 'Standard SIP Profile' |
| 152 | + }, |
| 153 | + 'lines': { |
| 154 | + 'line': [ |
| 155 | + { |
| 156 | + 'index': 1, |
| 157 | + 'dirn': { |
| 158 | + 'pattern': LINEDN, |
| 159 | + 'routePartitionName': None |
| 160 | + }, |
| 161 | + 'ringSetting': 'Use System Default', |
| 162 | + 'consecutiveRingSetting': 'Use System Default', |
| 163 | + 'ringSettingIdlePickupAlert': 'Use System Default', |
| 164 | + 'ringSettingActivePickupAlert': 'Use System Default', |
| 165 | + 'missedCallLogging': 'true', |
| 166 | + 'recordingMediaSource': 'Gateway Preferred', |
| 167 | + } |
| 168 | + ], |
| 169 | + }, |
| 170 | + 'phoneTemplateName': { |
| 171 | + '_value_1': 'Standard 8821 SIP' |
| 172 | + }, |
| 173 | + 'ringSettingIdleBlfAudibleAlert': 'Default', |
| 174 | + 'ringSettingBusyBlfAudibleAlert': 'Default', |
| 175 | + 'enableExtensionMobility': 'false', |
| 176 | + 'singleButtonBarge': 'Off', |
| 177 | + 'joinAcrossLines': 'Off', |
| 178 | + 'builtInBridgeStatus': 'Default', |
| 179 | + 'callInfoPrivacyStatus': 'Default', |
| 180 | + 'hlogStatus': 'On', |
| 181 | + 'ignorePresentationIndicators': 'false', |
| 182 | + 'allowCtiControlFlag': 'true', |
| 183 | + 'presenceGroupName': { |
| 184 | + '_value_1': 'Standard Presence group' |
| 185 | + }, |
| 186 | + 'unattendedPort': 'false', |
| 187 | + 'requireDtmfReception': 'false', |
| 188 | + 'rfc2833Disabled': 'false', |
| 189 | + 'certificateOperation': 'No Pending Operation', |
| 190 | + 'dndOption': 'Use Common Phone Profile Setting', |
| 191 | + 'dndStatus': 'false', |
| 192 | + 'isActive': 'true', |
| 193 | + 'isDualMode': 'false', |
| 194 | + 'phoneSuite': 'Default', |
| 195 | + 'phoneServiceDisplay': 'Default', |
| 196 | + 'isProtected': 'false', |
| 197 | + 'mtpRequired': 'false', |
| 198 | + 'mtpPreferedCodec': '711ulaw', |
| 199 | + 'outboundCallRollover': 'No Rollover', |
| 200 | + 'hotlineDevice': 'false', |
| 201 | + 'alwaysUsePrimeLine': 'Default', |
| 202 | + 'alwaysUsePrimeLineForVoiceMessage': 'Default', |
| 203 | + 'deviceTrustMode': 'Not Trusted', |
| 204 | + 'earlyOfferSupportForVoiceCall': 'false' |
| 205 | + } |
| 206 | +} |
| 207 | + |
| 208 | +phone_resp = service.addPhone(**phone_data) |
| 209 | + |
| 210 | +p = phone_resp |
| 211 | +print("\naddPhone response:\n") |
| 212 | +print(p,"\n") |
| 213 | +#print(history.last_sent) |
| 214 | +#print(history.last_received) |
| 215 | + |
| 216 | +user_data = { |
| 217 | + 'user': { |
| 218 | + 'firstName': USERFNAME, |
| 219 | + 'lastName': USERLNAME, |
| 220 | + 'userid': USERFNAME, |
| 221 | + 'password': USERPASS, |
| 222 | + 'pin': '5555', |
| 223 | + 'userLocale': 'English United States', |
| 224 | + 'associatedDevices': { |
| 225 | + 'device': [ |
| 226 | + PHONEID |
| 227 | + ] |
| 228 | + }, |
| 229 | + 'primaryExtension': { |
| 230 | + 'pattern': LINEDN, |
| 231 | + 'routePartitionName': None |
| 232 | + }, |
| 233 | + 'associatedGroups': { |
| 234 | + 'userGroup': [ |
| 235 | + { |
| 236 | + 'name': 'Standard CCM End Users', |
| 237 | + 'userRoles': { |
| 238 | + 'userRole': [ |
| 239 | + 'Standard CCM End Users', |
| 240 | + 'Standard CCMUSER Administration' |
| 241 | + ] |
| 242 | + } |
| 243 | + }, |
| 244 | + { |
| 245 | + 'name': 'Standard CTI Enabled', |
| 246 | + 'userRoles': { |
| 247 | + 'userRole': [ |
| 248 | + 'Standard CTI Enabled' |
| 249 | + ] |
| 250 | + } |
| 251 | + }, |
| 252 | + { |
| 253 | + 'name': 'Third Party Application Users' |
| 254 | + }, |
| 255 | + { |
| 256 | + 'name': 'Application Client Users' |
| 257 | + } |
| 258 | + ] |
| 259 | + }, |
| 260 | + 'enableCti': 'true', |
| 261 | + 'presenceGroupName': { |
| 262 | + '_value_1': 'Standard Presence group' |
| 263 | + }, |
| 264 | + 'enableMobility': 'true', |
| 265 | + 'enableMobileVoiceAccess': 'true', |
| 266 | + 'maxDeskPickupWaitTime': 10000, |
| 267 | + 'remoteDestinationLimit': 4, |
| 268 | + 'passwordCredentials': { |
| 269 | + 'pwdCredPolicyName': { |
| 270 | + '_value_1': 'Default Credential Policy' |
| 271 | + } |
| 272 | + }, |
| 273 | + 'enableEmcc': 'false', |
| 274 | + 'homeCluster': 'true', |
| 275 | + 'imAndPresenceEnable': 'true', |
| 276 | + 'calendarPresence': 'false' |
| 277 | + } |
| 278 | +} |
| 279 | + |
| 280 | +user_resp = service.addUser(**user_data) |
| 281 | + |
| 282 | +u = user_resp |
| 283 | +print("\naddUser response:\n") |
| 284 | +print(u,"\n") |
| 285 | +#print(history.last_sent) |
| 286 | +#print(history.last_received) |
| 287 | + |
| 288 | +phone_data = { |
| 289 | + 'name': PHONEID, |
| 290 | + 'ownerUserName': USERFNAME, |
| 291 | + 'lines': { |
| 292 | + 'line': [ |
| 293 | + { |
| 294 | + 'index': 1, |
| 295 | + 'dirn': { |
| 296 | + 'pattern': LINEDN, |
| 297 | + 'routePartitionName': None |
| 298 | + }, |
| 299 | + 'associatedEndusers': { |
| 300 | + 'enduser': [ |
| 301 | + { |
| 302 | + 'userId': 'johnq' |
| 303 | + } |
| 304 | + ] |
| 305 | + } |
| 306 | + } |
| 307 | + ], |
| 308 | + } |
| 309 | +} |
| 310 | + |
| 311 | +phone_resp = service.updatePhone(**phone_data) |
| 312 | + |
| 313 | +p = phone_resp |
| 314 | +print("\nupdatePhone Response\n") |
| 315 | +print(p,"\n") |
| 316 | +#print(history.last_sent) |
| 317 | +#print(history.last_received) |
| 318 | + |
| 319 | +user_data = { |
| 320 | + 'userid': USERFNAME |
| 321 | +} |
| 322 | + |
| 323 | +# You'd think you could use returnedTags, but... |
| 324 | +# returnedTags doesn't work as expected using zeep |
| 325 | +# zeep will return much more than you request |
| 326 | +#user_data = { |
| 327 | +# 'userid': 'dstaudt', |
| 328 | +# 'returnedTags' : { |
| 329 | +# 'firstName' : '', |
| 330 | +# 'lastName' : '' |
| 331 | +# } |
| 332 | +#} |
| 333 | + |
| 334 | +user_resp = service.getUser(**user_data) |
| 335 | + |
| 336 | +fname = user_resp['return']['user']['firstName'] |
| 337 | +lname = user_resp['return']['user']['lastName'] |
| 338 | +print("\ngetUser response:\n") |
| 339 | +print(fname, lname, "\n") |
| 340 | +#print(history.last_sent) |
| 341 | +#print(history.last_received) |
0 commit comments