Cisco CUCM AXL Python3 – Changing DN Partitions

By 12th December 2016AXL, Cisco, CUCM

We recently wrote the following script as part of some tools for client who required a phased migration of users to a new version 11.0 cluster. They were routing pstn calls through the new cluster so could not place extensions of the users into live partitions until the users phones had been cutover to the new platform.

All extensions were placed into a standalone holding partition to facilitate verification of the user and device configuration prior to migration, which created the following requirements on cutover.

1) New Cluster – Requirement to move the migrated users extensions from holding to live.
2) Old Cluster – Requirement to move the migrated users extensions from live to holding.

When migrating hundreds of users changing the partition individually across two clusters would be quite alot of work, so it was imperitive to automate the process.

SQL Query
 
  1. q1 = """select dnorpattern from numplan where fkroutepartition = (select pkid from routepartition where name = "PAR_HOLDING")
  2. and tkpatternusage = '2' and dnorpattern like '%+442078%'"""

We use the following query to select all directory numbers which contain “+442078” from the relevant partition

XML
 
  1. msg = """
  2. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/11.0">
  3. <soapenv:Header/>
  4. <soapenv:Body>
  5. <ns:executeSQLQuery sequence="?">
  6. <sql>{0}
  7. </sql>
  8. </ns:executeSQLQuery>
  9. </soapenv:Body>
  10. </soapenv:Envelope>""".format(q1)

Format of the XML message body

Post
 
  1. post = requests.post(url11, data=msg, headers=headers11query, verify=False, auth=('username', 'password'))
  2. response = ElementTree.fromstring(post.content)
  3. result = response.iterfind(".//row/*")
  4.  

We create a connection, post the message to the API and parse the returned response object.
We then use an xpath query on the result of the parsed post response object to locate the returned rows.

Iterate Results
 
  1. for r in result:
  2.         if r.tag == 'dnorpattern':
  3.             newdn = r.text
  4.             # Take e164 number and take last 4 digits
  5.             olddn = r.text[-4:]
  6.             # Pass e164 number to cluster11 function
  7.             cluster11(newdn)
  8.             # Pass 4 digit number to cluster86 function
  9.             cluster86(olddn)

Once we have the data we iterate through and match the xml tag ‘dnorpattern’ before returning its text value and storing this in a value called “newdn”. We then create another variable called “olddn” which is made up of the last four digits of the dnorpattern text value, before calling the functions “cluster11” and “cluster86” and passing the variables in. The “cluster11” and “cluster86” functions do the work of actually changing the value on the respective clusters.

Complete Code
 
  1. __author__ = "Mitch Dawson"
  2. __contact__ = "info@ucdevops.com"
  3. __copyright__ = "Copyright (C) 2017 - 2018 Ucdevops All Rights Reserved"
  4. __license__ = "GPLv3"
  5. <br/>
  6. """
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation, either version 3 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see http://www.gnu.org/licenses.
  17. """
  18. <br/>
  19. import requests
  20. from xml.etree import ElementTree
  21. import time
  22. # CUCM URL's
  23. url11 = 'https://V11CUCM:8443/axl/'
  24. url86 = 'https://V86CUCM:8443/axl/'
  25. # V11 CUCM Headers
  26. headers11query = {'Content-Type''text/xml',
  27.                   'SOAPAction''CUCM:DB ver=11.0 executeSQLQuery'}
  28. headers11update = {'Content-Type''text/xml',
  29.                    'SOAPAction''CUCM:DB ver=11.0 executeSQLUpdate'}
  30. # V8.6 CUCM Headers
  31. headers86update = {'Content-Type''text/xml',
  32.                    'SOAPAction''CUCM:DB ver=8.5 executeSQLUpdate'}
  33. q1 = """select dnorpattern from numplan where fkroutepartition = (select pkid from routepartition where name = "PAR_HOLDING")
  34. and tkpatternusage = '2' and dnorpattern like '%+442078%'"""
  35. def findextensions():
  36.     """
  37.     Find numbers on cucm 11 cluster in PAR_HOLDING
  38.     :return:
  39.     """
  40.     msg = """
  41.         <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/11.0">
  42.            <soapenv:Header/>
  43.            <soapenv:Body>
  44.               <ns:executeSQLQuery sequence="?">
  45.                 <sql>{0}
  46.                 </sql>
  47.               </ns:executeSQLQuery>
  48.            </soapenv:Body>
  49.         </soapenv:Envelope>""".format(q1)
  50.     # Create the Requests Connection
  51.     post = requests.post(url11, data=msg, headers=headers11query, verify=False, auth=('username', 'password'))
  52.  
  53.     # Parse the response string
  54.     response = ElementTree.fromstring(post.content)
  55.     # Find returned rows
  56.     result = response.iterfind(".//row/*")
  57.     for r in result:
  58.         time.sleep(1.5)
  59.         if r.tag == 'dnorpattern':
  60.             newdn = r.text
  61.             # Take e164 number and take last 4 digits
  62.             olddn = r.text[-4:]
  63.             # Pass e164 number to cluster11 function
  64.             cluster11(newdn)
  65.             # Pass 4 digit number to cluster86 function
  66.             cluster86(olddn)
  67. def cluster11(dn):
  68.     """
  69.     Move patterns to PAR_RESOURCES
  70.     :param dn:
  71.     :return:
  72.     """
  73.     # Message to Post
  74.     msg = """
  75.         <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/11.0">
  76.            <soapenv:Header/>
  77.            <soapenv:Body>
  78.               <ns:executeSQLUpdate sequence="?">
  79.                 <sql>update numplan set fkroutepartition = 'd6f7e81b-4364-4403-876a-10cae8d59df8'
  80.                 where dnorpattern = "{0}"
  81.                 </sql>
  82.               </ns:executeSQLUpdate>
  83.            </soapenv:Body>
  84.         </soapenv:Envelope>""".format(dn)
  85.     # Create the Requests Connection
  86.     post = requests.post(url11, data=msg, headers=headers11update, verify=False, auth=('username', 'password'))
  87.     # Parse the response string
  88.     response = ElementTree.fromstring(post.content)
  89.     # Find returned rows
  90.     result = response.iterfind(".//return/*")
  91.     for i in result:
  92.         if i.tag == 'rowsUpdated':
  93.             if i.text == '1':
  94.                 print('#### Successfully moved ' + str(dn) + ' to PAR_RESOURCES ####')
  95.             else:
  96.                 print('#### The response indicates no rows were updated for dn ' + str(dn) + ' ####')
  97. def cluster86(dn):
  98.     """
  99.     Move numbers to staging partition on 8.6 cluster
  100.     :param dn:
  101.     :return:
  102.     """
  103.     # Message to Post
  104.     msg = """
  105.             <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/8.5">
  106.                <soapenv:Header/>
  107.                <soapenv:Body>
  108.                   <ns:executeSQLUpdate sequence="?">
  109.                     <sql>update numplan set fkroutepartition = '61de9659-bc10-fca0-aa5e-81dc4b7e2fe4'
  110.                     where dnorpattern = "{0}"
  111.                     </sql>
  112.                   </ns:executeSQLUpdate>
  113.                </soapenv:Body>
  114.             </soapenv:Envelope>""".format(dn)
  115.     # Create the Requests Connection
  116.     post = requests.post(url86, data=msg, headers=headers86update, verify=False, auth=('username', 'password'))
  117.     # Parse the response string
  118.     response = ElementTree.fromstring(post.content)
  119.     # Find returned rows
  120.     result = response.iterfind(".//return/*")
  121.     for i in result:
  122.         if i.tag == 'rowsUpdated':
  123.             if i.text == '1':
  124.                 print('#### Successfully moved ' + str(dn) + ' to staging partition ####')
  125.             else:
  126.                 print('#### The response indicates no rows were updated for dn ' + str(dn) + ' ####')
  127. findextensions()
Mitch

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 ucdevops.com 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

"