├── README.md ├── axltoolkit ├── __init__.py ├── paws │ └── hardware_information_service.wsdl └── schema │ ├── 10.0 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 10.5 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 11.0 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 11.5 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 12.0 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 12.5 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ ├── 14.0 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd │ └── 15.0 │ ├── AXLAPI.wsdl │ ├── AXLEnums.xsd │ └── AXLSoap.xsd ├── requirements.txt └── samples ├── credentials.py ├── sample1_axl.py ├── sample2_ris.py ├── sample3_perfmon.py ├── sample4_paws.py └── sample5_certs.py /README.md: -------------------------------------------------------------------------------- 1 | # axltoolkit by Paul Giralt 2 | 3 | This is a Python-based Toolkit for Unified CM AXL and SXML Frameworks. It is very much incomplete, but should get anyone interested in leveraging these API's enough to get started and build upon. 4 | 5 | Modify the credentials.py file with your UCM AXL and Platform Credentials to be able to try out the scripts. For production usage, I would recommend using some more secure way of storing credentials. 6 | 7 | The samples folder contains four sample scripts that will show you how to use the toolkit: 8 | 9 | sample1_axl: Example of Thick and Thin AXL 10 | 11 | sample2_ris: Example of the RisPort SXML API to retrieve a list of IP addresses of registered phones 12 | 13 | sample3_perfmon: Example of Perfmon SXML API to retrieve some Perfmon Counters 14 | 15 | sample4_paws: Example of the Hardware Information PAWS API 16 | 17 | sample5_certs: Example us using Thin AXL to pull certificates and decode them. 18 | 19 | 20 | ## License 21 | 22 | These terms govern this Cisco Systems, Inc. (“Cisco”), example or demo source code and its associated documentation (together, the “Sample Code”). By downloading, copying, modifying, compiling, or redistributing the Sample Code, you accept and agree to be bound by the following terms and conditions (the “License”). If you are accepting the License on behalf of an entity, you represent that you have the authority to do so (either you or the entity, “you”). Sample Code is not supported by Cisco TAC and is not tested for quality or performance. This is your only license to the Sample Code and all rights not expressly granted are reserved. 23 | 24 | 1. LICENSE GRANT: Subject to the terms and conditions of this License, Cisco hereby grants to you a perpetual, worldwide, non-exclusive, non-transferable, non-sublicensable, royalty-free license to copy and modify the Sample Code in source code form, and compile and redistribute the Sample Code in binary/object code or other executable forms, in whole or in part, solely for use with Cisco products and services. For interpreted languages like Java and Python, the executable form of the software may include source code and compilation is not required. 25 | 26 | 2. CONDITIONS: You shall not use the Sample Code independent of, or to replicate or compete with, a Cisco product or service. Cisco products and services are licensed under their own separate terms and you shall not use the Sample Code in any way that violates or is inconsistent with those terms (for more information, please visit: www.cisco.com/go/terms ). 27 | 28 | 3. OWNERSHIP: Cisco retains sole and exclusive ownership of the Sample Code, including all intellectual property rights therein, except with respect to any third-party material that may be used in or by the Sample Code. Any such third-party material is licensed under its own separate terms (such as an open source license) and all use must be in full accordance with the applicable license. This License does not grant you permission to use any trade names, trademarks, service marks, or product names of Cisco. If you provide any feedback to Cisco regarding the Sample Code, you agree that Cisco, its partners, and its customers shall be free to use and incorporate such feedback into the Sample Code, and Cisco products and services, for any purpose, and without restriction, payment, or additional consideration of any kind. If you initiate or participate in any litigation against Cisco, its partners, or its customers (including cross-claims and counter-claims) alleging that the Sample Code and/or its use infringe any patent, copyright, or other intellectual property right, then all rights granted to you under this License shall terminate immediately without notice. 29 | 30 | 4. LIMITATION OF LIABILITY: CISCO SHALL HAVE NO LIABILITY IN CONNECTION WITH OR RELATING TO THIS LICENSE OR USE OF THE SAMPLE CODE, FOR DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DIRECT, INCIDENTAL, AND CONSEQUENTIAL DAMAGES, OR FOR ANY LOSS OF USE, DATA, INFORMATION, PROFITS, BUSINESS, OR GOODWILL, HOWEVER CAUSED, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 31 | 32 | 5. DISCLAIMER OF WARRANTY: SAMPLE CODE IS INTENDED FOR EXAMPLE PURPOSES ONLY AND IS PROVIDED BY CISCO “AS IS” WITH ALL FAULTS AND WITHOUT WARRANTY OR SUPPORT OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY LAW, ALL EXPRESS AND IMPLIED CONDITIONS, REPRESENTATIONS, AND WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OR CONDITION OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, SATISFACTORY QUALITY, NON-INTERFERENCE, AND ACCURACY, ARE HEREBY EXCLUDED AND EXPRESSLY DISCLAIMED BY CISCO. CISCO DOES NOT WARRANT THAT THE SAMPLE CODE IS SUITABLE FOR PRODUCTION OR COMMERCIAL USE, WILL OPERATE PROPERLY, IS ACCURATE OR COMPLETE, OR IS WITHOUT ERROR OR DEFECT. 33 | 34 | 6. GENERAL: This License shall be governed by and interpreted in accordance with the laws of the State of California, excluding its conflict of laws provisions. You agree to comply with all applicable United States export laws, rules, and regulations. If any provision of this License is judged illegal, invalid, or otherwise unenforceable, that provision shall be severed and the rest of the License shall remain in full force and effect. No failure by Cisco to enforce any of its rights related to the Sample Code or to a breach of this License in a particular situation will act as a waiver of such rights. In the event of any inconsistencies with any other terms, this License shall take precedence. 35 | 36 | -------------------------------------------------------------------------------- /axltoolkit/__init__.py: -------------------------------------------------------------------------------- 1 | from zeep import Client 2 | from zeep.cache import SqliteCache 3 | from zeep.transports import Transport 4 | from zeep.plugins import HistoryPlugin 5 | from requests import Session 6 | from requests.auth import HTTPBasicAuth 7 | import urllib3 8 | import logging.config 9 | import logging 10 | import os 11 | from lxml.etree import tostring 12 | from pathlib import Path 13 | import tempfile 14 | import re 15 | 16 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 17 | 18 | def enable_logging(): 19 | logging.config.dictConfig({ 20 | 'version': 1, 21 | 'formatters': { 22 | 'verbose': { 23 | 'format': '%(name)s: %(message)s' 24 | } 25 | }, 26 | 'handlers': { 27 | 'console': { 28 | 'level': 'DEBUG', 29 | 'class': 'logging.StreamHandler', 30 | 'formatter': 'verbose', 31 | }, 32 | }, 33 | 'loggers': { 34 | 'zeep.transports': { 35 | 'level': 'DEBUG', 36 | 'propagate': True, 37 | 'handlers': ['console'], 38 | }, 39 | } 40 | }) 41 | logging.basicConfig() 42 | logging.getLogger().setLevel(logging.DEBUG) 43 | requests_log = logging.getLogger("requests.packages.urllib3") 44 | requests_log.setLevel(logging.DEBUG) 45 | requests_log.propagate = True 46 | 47 | 48 | class AxlToolkit: 49 | 50 | wsdl = "" 51 | last_exception = None 52 | history = HistoryPlugin() 53 | 54 | ''' 55 | 56 | Constructor - Create new instance 57 | 58 | ''' 59 | 60 | def __init__(self, username, password, server_ip, version='12.5', tls_verify=True, timeout=10, logging_enabled=False): 61 | self.session = Session() 62 | self.session.auth = HTTPBasicAuth(username, password) 63 | self.session.verify = tls_verify 64 | filedir = os.path.dirname(__file__) 65 | 66 | self.cache = SqliteCache(path=Path(tempfile.gettempdir()+'/sqlite_{0}.db'.format(server_ip)), timeout=60) 67 | 68 | if version == '15.0': 69 | self.wsdl = os.path.join(filedir, 'schema/15.0/AXLAPI.wsdl') 70 | elif version == '14.0': 71 | self.wsdl = os.path.join(filedir, 'schema/14.0/AXLAPI.wsdl') 72 | elif version == '12.5': 73 | self.wsdl = os.path.join(filedir, 'schema/12.5/AXLAPI.wsdl') 74 | elif version == '12.0': 75 | self.wsdl = os.path.join(filedir, 'schema/12.0/AXLAPI.wsdl') 76 | elif version == '11.5': 77 | self.wsdl = os.path.join(filedir, 'schema/11.5/AXLAPI.wsdl') 78 | elif version == '11.0': 79 | self.wsdl = os.path.join(filedir, 'schema/11.0/AXLAPI.wsdl') 80 | elif version == '10.5': 81 | self.wsdl = os.path.join(filedir, 'schema/10.5/AXLAPI.wsdl') 82 | elif version == '10.0': 83 | self.wsdl = os.path.join(filedir, 'schema/10.0/AXLAPI.wsdl') 84 | else: 85 | self.wsdl = os.path.join(filedir, 'schema/12.5/AXLAPI.wsdl') 86 | 87 | self.client = Client(wsdl=self.wsdl, plugins=[self.history], transport=Transport(timeout=timeout, 88 | operation_timeout=timeout, 89 | cache=self.cache, 90 | session=self.session)) 91 | 92 | self.service = self.client.create_service("{http://www.cisco.com/AXLAPIService/}AXLAPIBinding", 93 | "https://{0}:8443/axl/".format(server_ip)) 94 | 95 | if logging_enabled: 96 | enable_logging() 97 | 98 | def get_service(self): 99 | return self.service 100 | 101 | def last_request_debug(self): 102 | request_env = tostring(self.history.last_sent['envelope']) 103 | request_headers = self.history.last_sent['http_headers'] 104 | response_env = tostring(self.history.last_received['envelope']) 105 | response_headers = self.history.last_received['http_headers'] 106 | 107 | return { 108 | 'request': { 109 | 'raw': self.history.last_sent, 110 | 'headers': request_headers, 111 | 'envelope': request_env 112 | }, 113 | 'response': { 114 | 'raw': self.history.last_received, 115 | 'headers': response_headers, 116 | 'envelope': response_env 117 | 118 | } 119 | } 120 | 121 | 122 | 123 | ''' 124 | 125 | Thin AXL (SQL Queries / Updates) 126 | 127 | ''' 128 | 129 | def run_sql_query(self, query): 130 | result = {'num_rows': 0, 131 | 'query': query} 132 | 133 | try: 134 | sql_result = self.service.executeSQLQuery(sql=query) 135 | except Exception as fault: 136 | sql_result = None 137 | self.last_exception = fault 138 | 139 | num_rows = 0 140 | result_rows = [] 141 | 142 | if sql_result is not None: 143 | if sql_result['return'] is not None: 144 | for row in sql_result['return']['row']: 145 | result_rows.append({}) 146 | for column in row: 147 | result_rows[num_rows][column.tag] = column.text 148 | num_rows += 1 149 | 150 | result['num_rows'] = num_rows 151 | if num_rows > 0: 152 | result['rows'] = result_rows 153 | 154 | return result 155 | 156 | 157 | def run_sql_update(self, query): 158 | result = {'rows_updated': 0, 159 | 'query': query} 160 | 161 | try: 162 | sql_result = self.service.executeSQLUpdate(sql=query) 163 | except Exception as fault: 164 | sql_result = None 165 | self.last_exception = fault 166 | 167 | if sql_result is not None: 168 | if sql_result['return'] is not None: 169 | result['rows_updated'] = sql_result['return']['rowsUpdated'] 170 | 171 | return result 172 | 173 | 174 | ''' 175 | 176 | UCM Group 177 | 178 | ''' 179 | 180 | def get_ucm_group(self, name): 181 | try: 182 | result = self.service.getCallManagerGroup(name=name) 183 | except Exception as fault: 184 | result = None 185 | self.last_exception = fault 186 | 187 | return result 188 | 189 | def update_ucm_group_members(self, name, members): 190 | 191 | member_data = [] 192 | member_count = 0 193 | 194 | for member in members: 195 | member_count += 1 196 | member_data.append({'priority': member_count, 'callManagerName': member}) 197 | 198 | try: 199 | result = self.service.updateCallManagerGroup(name=name, members={'member': member_data}) 200 | except Exception as fault: 201 | result = None 202 | self.last_exception = fault 203 | 204 | return result 205 | 206 | def add_ucm_group(self, name, members): 207 | 208 | member_data = [] 209 | member_count = 0 210 | 211 | for member in members: 212 | member_count += 1 213 | member_data.append({'priority': member_count, 'callManagerName': member}) 214 | 215 | try: 216 | result = self.service.addCallManagerGroup(callManagerGroup= 217 | {'name': name, 'members':{'member': member_data}}) 218 | except Exception as fault: 219 | result = None 220 | self.last_exception = fault 221 | 222 | return result 223 | 224 | def remove_ucm_group(self, name): 225 | try: 226 | result = self.service.removeCallManagerGroup(name=name) 227 | except Exception as fault: 228 | result = None 229 | self.last_exception = fault 230 | 231 | return result 232 | 233 | 234 | ''' 235 | 236 | Users 237 | 238 | ''' 239 | 240 | def get_user(self, userid): 241 | try: 242 | result = self.service.getUser(userid=userid) 243 | except Exception as fault: 244 | result = None 245 | self.last_exception = fault 246 | 247 | return result 248 | 249 | def list_users(self, **kwargs): 250 | 251 | allowed_tags = ['firstName', 'lastName', 'userid', 'department'] 252 | search_criteria = {} 253 | users = {} 254 | 255 | if kwargs is not None: 256 | for key, value in kwargs.items(): 257 | if key in allowed_tags: 258 | search_criteria[key] = value 259 | 260 | if len(search_criteria) == 0: 261 | search_criteria['userid'] = '%' 262 | 263 | returned_tags = {'firstName': '', 'lastName': '', 'userid': ''} 264 | 265 | try: 266 | result = self.service.listUser(searchCriteria=search_criteria, returnedTags=returned_tags) 267 | 268 | if result['return'] is not None: 269 | 270 | for user in result['return']['user']: 271 | 272 | users[user['userid']] = {} 273 | 274 | users[user['userid']]['uuid'] = user['uuid'] 275 | users[user['userid']]['firstName'] = user['firstName'] 276 | users[user['userid']]['lastName'] = user['lastName'] 277 | users[user['userid']]['userid'] = user['userid'] 278 | 279 | except Exception as fault: 280 | users = None 281 | self.last_exception = fault 282 | 283 | return users 284 | 285 | def get_registration_dynamic(self, device): 286 | try: 287 | result = self.service.getRegistrationDynamic(device=device) 288 | except Exception as fault: 289 | result = None 290 | self.last_exception = fault 291 | 292 | return result 293 | 294 | 295 | def list_registration_dynamic(self, **kwargs): 296 | 297 | allowed_tags = ['device', 'lastKnownIpAddress', 'lastKnownUcm', 'lastKnownConfigVersion', 'locationDetails', 298 | 'endpointConnection', 'portOrSsid', 'lastSeen'] 299 | 300 | search_criteria = {} 301 | registrations = {} 302 | 303 | if kwargs is not None: 304 | for key, value in kwargs.items(): 305 | if key in allowed_tags: 306 | search_criteria[key] = value 307 | 308 | if len(search_criteria) == 0: 309 | search_criteria['device'] = '%' 310 | 311 | returned_tags = {'device': '', 312 | 'lastKnownIpAddress': '', 313 | 'lastKnownUcm': '', 314 | 'lastKnownConfigVersion': '', 315 | 'locationDetails': '', 316 | 'endpointConnection': '', 317 | 'portOrSsid': '', 318 | 'lastSeen': ''} 319 | 320 | try: 321 | result = self.service.listRegistrationDynamic(searchCriteria=search_criteria, returnedTags=returned_tags) 322 | 323 | if result['return'] is not None: 324 | for registration in result['return']['registrationDynamic']: 325 | 326 | device = registration['device']['_value_1'] 327 | device_uuid = registration['device']['uuid'] 328 | 329 | registrations[device] = {} 330 | 331 | registrations[device]['device'] = device 332 | registrations[device]['device_uuid'] = device_uuid 333 | registrations[device]['lastKnownIpAddress'] = registration['lastKnownIpAddress'] 334 | registrations[device]['lastKnownUcm'] = registration['lastKnownUcm'] 335 | registrations[device]['lastKnownConfigVersion'] = registration['lastKnownConfigVersion'] 336 | registrations[device]['locationDetails'] = registration['locationDetails'] 337 | registrations[device]['endpointConnection'] = registration['endpointConnection'] 338 | registrations[device]['portOrSsid'] = registration['portOrSsid'] 339 | registrations[device]['lastSeen'] = registration['lastSeen'] 340 | registrations[device]['uuid'] = registration['uuid'] 341 | 342 | except Exception as fault: 343 | registrations = None 344 | self.last_exception = fault 345 | 346 | return registrations 347 | 348 | 349 | def update_user(self, user_data): 350 | try: 351 | result = self.service.updateUser(**user_data) 352 | except Exception as fault: 353 | result = None 354 | self.last_exception = fault 355 | 356 | return result 357 | 358 | def sql_get_device_pkid(self, device): 359 | 360 | sql_query = "select pkid from device where name = '{0}'".format(device) 361 | 362 | result = self.run_sql_query(sql_query) 363 | 364 | if result['num_rows'] > 0: 365 | pkid = result['rows'][0]['pkid'] 366 | else: 367 | pkid = None 368 | 369 | return pkid 370 | 371 | def sql_get_user_group_pkid(self, group_name): 372 | 373 | sql_query = "select pkid from dirgroup where name = '{0}'".format(group_name) 374 | 375 | result = self.run_sql_query(sql_query) 376 | 377 | if result['num_rows'] > 0: 378 | pkid = result['rows'][0]['pkid'] 379 | else: 380 | pkid = None 381 | 382 | return pkid 383 | 384 | def sql_get_enduser_pkid(self, userid): 385 | 386 | sql_query = "select pkid from enduser where userid = '{0}'".format(userid) 387 | 388 | result = self.run_sql_query(sql_query) 389 | 390 | if result['num_rows'] > 0: 391 | pkid = result['rows'][0]['pkid'] 392 | else: 393 | pkid = None 394 | 395 | return pkid 396 | 397 | def sql_associate_user_to_group(self, userid, group_name): 398 | 399 | user_group_pkid = self.sql_get_user_group_pkid(group_name) 400 | enduser_pkid = self.sql_get_enduser_pkid(userid) 401 | 402 | if user_group_pkid is not None and enduser_pkid is not None: 403 | query = "insert into enduserdirgroupmap (fkenduser, fkdirgroup) values ('{0}', '{1}')".format(enduser_pkid, 404 | user_group_pkid) 405 | 406 | sql_result = self.run_sql_update(query) 407 | 408 | if sql_result['rows_updated'] > 0: 409 | result = True 410 | else: 411 | result = False 412 | 413 | return result 414 | 415 | def sql_remove_user_from_group(self, userid, group_name): 416 | pass 417 | # TODO: Need to add this code 418 | 419 | ''' 420 | 421 | Lines 422 | 423 | ''' 424 | 425 | def get_line(self, dn, partition): 426 | try: 427 | result = self.service.getLine(pattern=dn, routePartitionName=partition) 428 | except Exception as fault: 429 | result = None 430 | self.last_exception = fault 431 | 432 | return result 433 | 434 | def add_line(self, line_data): 435 | try: 436 | result = self.service.addLine(line=line_data) 437 | except Exception as fault: 438 | result = None 439 | self.last_exception = fault 440 | 441 | return result 442 | 443 | def update_line(self, line_data): 444 | try: 445 | result = self.service.updateLine(**line_data) 446 | except Exception as fault: 447 | result = None 448 | self.last_exception = fault 449 | 450 | return result 451 | 452 | ''' 453 | 454 | LDAP Filter 455 | 456 | ''' 457 | 458 | def get_ldap_filter(self, name): 459 | try: 460 | result = self.service.getLdapFilter(name=name) 461 | except Exception as fault: 462 | result = None 463 | self.last_exception = fault 464 | 465 | return result 466 | 467 | def add_ldap_filter(self, name, filter_name): 468 | 469 | filter_data = { 470 | 'name': name, 471 | 'filter': filter_name 472 | } 473 | 474 | try: 475 | result = self.service.addLdapFilter(filter_data) 476 | except Exception as fault: 477 | result = None 478 | self.last_exception = fault 479 | 480 | return result 481 | 482 | def remove_ldap_filter(self, name): 483 | try: 484 | result = self.service.removeLdapFilter(name=name) 485 | except Exception as fault: 486 | result = None 487 | self.last_exception = fault 488 | 489 | return result 490 | 491 | 492 | ''' 493 | 494 | LDAP Directory 495 | 496 | ''' 497 | 498 | def get_ldap_directory(self, name): 499 | try: 500 | result = self.service.getLdapDirectory(name=name) 501 | except Exception as fault: 502 | result = None 503 | self.last_exception = fault 504 | 505 | return result 506 | 507 | def add_ldap_directory(self, ldap_dir_data): 508 | 509 | try: 510 | result = self.service.addLdapDirectory(ldapDirectory=ldap_dir_data) 511 | except Exception as fault: 512 | result = None 513 | self.last_exception = fault 514 | 515 | return result 516 | 517 | def remove_ldap_directory(self, name): 518 | try: 519 | result = self.service.removeLdapDirectory(name=name) 520 | except Exception as fault: 521 | result = None 522 | self.last_exception = fault 523 | 524 | return result 525 | 526 | def start_ldap_sync(self, ldap_name=None): 527 | query = "update directorypluginconfig set syncnow = '1'" 528 | if ldap_name is not None: 529 | query += " where name = '{0}'".format(ldap_name) 530 | 531 | sql_result = self.run_sql_update(query) 532 | 533 | if sql_result['rows_updated'] > 0: 534 | result = True 535 | else: 536 | result = False 537 | 538 | return result 539 | 540 | ''' 541 | 542 | LDAP System 543 | 544 | ''' 545 | 546 | def get_ldap_system(self): 547 | try: 548 | result = self.service.getLdapSystem() 549 | except Exception as fault: 550 | result = None 551 | self.last_exception = fault 552 | 553 | return result 554 | 555 | def update_ldap_system(self, sync_enabled, ldap_server, user_id_attribute): 556 | 557 | try: 558 | result = self.service.updateLdapSystem(syncEnabled=sync_enabled, 559 | ldapServer=ldap_server, 560 | userIdAttribute=user_id_attribute) 561 | except Exception as fault: 562 | result = None 563 | self.last_exception = fault 564 | 565 | return result 566 | 567 | 568 | ''' 569 | 570 | LDAP Authentication 571 | 572 | ''' 573 | 574 | def get_ldap_authentication(self): 575 | try: 576 | result = self.service.getLdapAuthentication() 577 | except Exception as fault: 578 | result = None 579 | self.last_exception = fault 580 | 581 | return result 582 | 583 | def update_ldap_authentication(self, enabled, dn, password, search_base, servers, port, ssl): 584 | 585 | server_data = [] 586 | 587 | for server in servers: 588 | server_data.append({'hostName': server, 589 | 'ldapPortNumber': port, 590 | 'sslEnabled': ssl}) 591 | 592 | try: 593 | result = self.service.updateLdapAuthentication(authenticateEndUsers=enabled, 594 | distinguishedName=dn, 595 | ldapPassword=password, 596 | userSearchBase=search_base, 597 | servers={'server': server_data}) 598 | except Exception as fault: 599 | result = None 600 | self.last_exception = fault 601 | 602 | return result 603 | 604 | ''' 605 | 606 | Phone 607 | 608 | ''' 609 | 610 | def get_phone(self, name): 611 | try: 612 | result = self.service.getPhone(name=name) 613 | except Exception as fault: 614 | result = None 615 | self.last_exception = fault 616 | 617 | return result 618 | 619 | def add_phone(self, phone_data, line_data=None): 620 | 621 | if line_data is not None: 622 | phone_data['lines'] = {'line': line_data} 623 | 624 | try: 625 | result = self.service.addPhone(phone=phone_data) 626 | except Exception as fault: 627 | result = None 628 | self.last_exception = fault 629 | 630 | return result 631 | 632 | def remove_phone(self, name): 633 | try: 634 | result = self.service.removePhone(name=name) 635 | except Exception as fault: 636 | result = None 637 | self.last_exception = fault 638 | 639 | return result 640 | 641 | def update_phone(self, phone_data, line_data=None): 642 | 643 | if line_data is not None: 644 | phone_data['lines'] = {'line': line_data} 645 | 646 | try: 647 | result = self.service.updatePhone(**phone_data) 648 | except Exception as fault: 649 | result = None 650 | self.last_exception = fault 651 | 652 | return result 653 | 654 | def list_phone(self, **kwargs): 655 | 656 | allowed_tags = ['name', 'description', 'protocol', 'callingSearchSpaceName', 'devicePoolName', 'securityProfileName'] 657 | search_criteria = {} 658 | phones = {} 659 | 660 | if kwargs is not None: 661 | for key, value in kwargs.items(): 662 | if key in allowed_tags: 663 | search_criteria[key] = value 664 | 665 | if len(search_criteria) == 0: 666 | search_criteria['name'] = '%' 667 | 668 | returned_tags = {'name': '', 'description': '', 'devicePoolName': ''} 669 | 670 | try: 671 | print(search_criteria) 672 | print(returned_tags) 673 | 674 | result = self.service.listPhone(searchCriteria=search_criteria, returnedTags=returned_tags) 675 | 676 | if result['return'] is not None: 677 | 678 | for phone in result['return']['phone']: 679 | 680 | phones[phone['name']] = {} 681 | 682 | phones[phone['name']]['uuid'] = phone['uuid'] 683 | phones[phone['name']]['name'] = phone['name'] 684 | phones[phone['name']]['description'] = phone['description'] 685 | phones[phone['name']]['devicePoolName'] = phone['devicePoolName'] 686 | 687 | except Exception as fault: 688 | phones = None 689 | self.last_exception = fault 690 | 691 | return phones 692 | 693 | 694 | 695 | ''' 696 | 697 | Partitions 698 | 699 | ''' 700 | 701 | def add_partition(self, name, description): 702 | 703 | partition_data = {'name': name, 704 | 'description': description} 705 | 706 | try: 707 | result = self.service.addRoutePartition(routePartition=partition_data) 708 | except Exception as fault: 709 | result = None 710 | self.last_exception = fault 711 | 712 | return result 713 | 714 | # Accepts a list of partitions, either as a list of strings with Partition names, or a list of dictionaries 715 | # containing the name and description for each partition. 716 | 717 | def add_partitions(self, partition_list): 718 | 719 | result = [] 720 | 721 | for partition in partition_list: 722 | 723 | if not isinstance(partition, dict): 724 | partition = {"name": partition, "description": ""} 725 | 726 | try: 727 | result.append(self.service.addRoutePartition(routePartition=partition)) 728 | except Exception as fault: 729 | result.append({'fault': fault}) 730 | self.last_exception = fault 731 | 732 | return result 733 | 734 | def get_partition(self, name, returned_tags=None): 735 | 736 | try: 737 | if returned_tags is not None: 738 | result = self.service.getRoutePartition(name=name, returnedTags=returned_tags) 739 | else: 740 | result = self.service.getRoutePartition(name=name) 741 | except Exception as fault: 742 | result = None 743 | self.last_exception = fault 744 | 745 | return result 746 | 747 | def remove_partition(self, name): 748 | 749 | try: 750 | result = self.service.removeRoutePartition(name=name) 751 | except Exception as fault: 752 | result = None 753 | self.last_exception = fault 754 | 755 | return result 756 | 757 | ''' 758 | 759 | Calling Search Space 760 | 761 | ''' 762 | 763 | def add_css(self, name, description, partition_list): 764 | 765 | css_data = {'name': name, 766 | 'description': description, 767 | 'members': 768 | {'member': []} 769 | } 770 | 771 | css_index = 1 772 | for partition in partition_list: 773 | partition_data = {'routePartitionName': partition, 774 | 'index': css_index} 775 | css_data['members']['member'].append(partition_data) 776 | css_index += 1 777 | 778 | try: 779 | result = self.service.addCss(css=css_data) 780 | except Exception as fault: 781 | result = None 782 | self.last_exception = fault 783 | 784 | return result 785 | 786 | def get_css(self, name): 787 | 788 | try: 789 | result = self.service.getCss(name=name) 790 | except Exception as fault: 791 | result = None 792 | self.last_exception = fault 793 | 794 | return result 795 | 796 | def remove_css(self, name): 797 | 798 | try: 799 | result = self.service.removeCss(name=name) 800 | except Exception as fault: 801 | result = None 802 | self.last_exception = fault 803 | 804 | return result 805 | 806 | def update_css(self, css_name, description, partition_list): 807 | 808 | members = {'member': []} 809 | 810 | css_index = 1 811 | 812 | for partition in partition_list: 813 | partition_data = {'routePartitionName': partition, 814 | 'index': css_index} 815 | members['member'].append(partition_data) 816 | css_index += 1 817 | 818 | try: 819 | result = self.service.updateCss(name=css_name, description=description, members=members) 820 | except Exception as fault: 821 | result = None 822 | self.last_exception = fault 823 | 824 | return result 825 | 826 | ''' 827 | 828 | Route Group 829 | 830 | ''' 831 | 832 | def add_route_group(self, name, distribution_algorithm, device_list): 833 | 834 | rg_data = { 835 | 'name': name, 836 | 'distributionAlgorithm': distribution_algorithm, 837 | 'members': { 838 | 'member': [] 839 | } 840 | } 841 | 842 | rg_index = 1 843 | for device in device_list: 844 | rg_data['members']['member'].append({ 845 | 'deviceSelectionOrder': rg_index, 846 | 'deviceName': device, 847 | 'port': 0 848 | }) 849 | rg_index += 1 850 | 851 | try: 852 | result = self.service.addRouteGroup(routeGroup=rg_data) 853 | except Exception as fault: 854 | result = None 855 | self.last_exception = fault 856 | 857 | return result 858 | 859 | def get_route_group(self, name): 860 | 861 | try: 862 | result = self.service.getRouteGroup(name=name) 863 | except Exception as fault: 864 | result = None 865 | self.last_exception = fault 866 | 867 | return result 868 | 869 | def remove_route_group(self, name): 870 | 871 | try: 872 | result = self.service.removeRouteGroup(name=name) 873 | except Exception as fault: 874 | result = None 875 | self.last_exception = fault 876 | 877 | return result 878 | 879 | def update_route_group(self, name): 880 | # TODO: Need to implement 881 | pass 882 | 883 | ''' 884 | 885 | Route List 886 | 887 | ''' 888 | 889 | def add_route_list(self, name, description, cm_group, enabled, roan, members, ddi=None): 890 | 891 | rl_data = { 892 | 'name': name, 893 | 'description': description, 894 | 'callManagerGroupName': cm_group, 895 | 'routeListEnabled': enabled, 896 | 'runOnEveryNode': roan, 897 | 'members': { 898 | 'member': [] 899 | } 900 | } 901 | 902 | rg_index = 1 903 | for member in members: 904 | rl_data['members']['member'].append({ 905 | 'selectionOrder': rg_index, 906 | 'routeGroupName': member, 907 | 'digitDiscardInstructionName': ddi 908 | }) 909 | rg_index += 1 910 | 911 | try: 912 | result = self.service.addRouteList(routeList=rl_data) 913 | except Exception as fault: 914 | result = None 915 | self.last_exception = fault 916 | 917 | return result 918 | 919 | def get_route_list(self, name): 920 | 921 | try: 922 | result = self.service.getRouteList(name=name) 923 | except Exception as fault: 924 | result = None 925 | self.last_exception = fault 926 | 927 | return result 928 | 929 | def remove_route_list(self, name): 930 | 931 | try: 932 | result = self.service.removeRouteList(name=name) 933 | except Exception as fault: 934 | result = None 935 | self.last_exception = fault 936 | 937 | return result 938 | 939 | def update_route_group(self, name): 940 | # TODO: Need to implement 941 | pass 942 | 943 | 944 | ''' 945 | 946 | Route Pattern 947 | 948 | ''' 949 | 950 | def add_route_pattern(self, pattern, partition, route_list, network_location, outside_dialtone): 951 | 952 | rp_data = { 953 | 'pattern': pattern, 954 | 'routePartitionName': partition, 955 | 'destination': { 956 | 'routeListName': route_list 957 | }, 958 | 'blockEnable': False, 959 | 'networkLocation': network_location, 960 | 'provideOutsideDialtone': outside_dialtone 961 | } 962 | 963 | try: 964 | result = self.service.addRoutePattern(routePattern=rp_data) 965 | except Exception as fault: 966 | result = None 967 | self.last_exception = fault 968 | 969 | return result 970 | 971 | def get_route_pattern(self, pattern, partition): 972 | 973 | try: 974 | result = self.service.getRoutePattern(pattern=pattern, routePartitionName=partition) 975 | except Exception as fault: 976 | result = None 977 | self.last_exception = fault 978 | 979 | return result 980 | 981 | def remove_route_pattern(self, pattern, partition): 982 | 983 | try: 984 | result = self.service.removeRoutePattern(pattern=pattern, routePartitionName=partition) 985 | except Exception as fault: 986 | result = None 987 | self.last_exception = fault 988 | 989 | return result 990 | 991 | def update_route_pattern(self, name): 992 | # TODO: Need to implement 993 | pass 994 | 995 | ''' 996 | 997 | Route Pattern 998 | 999 | ''' 1000 | 1001 | def add_translation_pattern(self, pattern, partition, route_list, network_location, outside_dialtone): 1002 | 1003 | rp_data = { 1004 | 'pattern': pattern, 1005 | 'routePartitionName': partition, 1006 | 'blockEnable': False, 1007 | 'networkLocation': network_location, 1008 | 'provideOutsideDialtone': outside_dialtone 1009 | } 1010 | 1011 | try: 1012 | result = self.service.addRoutePattern(routePattern=rp_data) 1013 | except Exception as fault: 1014 | result = None 1015 | self.last_exception = fault 1016 | 1017 | return result 1018 | 1019 | def get_translation_pattern(self, pattern, partition): 1020 | 1021 | try: 1022 | result = self.service.getRoutePattern(pattern=pattern, routePartitionName=partition) 1023 | except Exception as fault: 1024 | result = None 1025 | self.last_exception = fault 1026 | 1027 | return result 1028 | 1029 | def remove_translation_pattern(self, pattern, partition): 1030 | 1031 | try: 1032 | result = self.service.removeRoutePattern(pattern=pattern, routePartitionName=partition) 1033 | except Exception as fault: 1034 | result = None 1035 | self.last_exception = fault 1036 | 1037 | return result 1038 | 1039 | def update_translation_pattern(self, name): 1040 | # TODO: Need to implement 1041 | pass 1042 | 1043 | ''' 1044 | 1045 | SIP Route Pattern 1046 | 1047 | ''' 1048 | 1049 | def add_sip_route_pattern(self, pattern, partition, route_list): 1050 | 1051 | rp_data = { 1052 | 'pattern': pattern, 1053 | 'routePartitionName': partition, 1054 | 'sipTrunkName': route_list, 1055 | 'usage': 'Domain Routing', 1056 | } 1057 | 1058 | try: 1059 | result = self.service.addSipRoutePattern(sipRoutePattern=rp_data) 1060 | except Exception as fault: 1061 | result = None 1062 | self.last_exception = fault 1063 | 1064 | return result 1065 | 1066 | def get_sip_route_pattern(self, pattern, partition): 1067 | 1068 | try: 1069 | result = self.service.getRoutePattern(pattern=pattern, routePartitionName=partition) 1070 | except Exception as fault: 1071 | result = None 1072 | self.last_exception = fault 1073 | 1074 | return result 1075 | 1076 | def remove_sip_route_pattern(self, pattern, partition): 1077 | 1078 | try: 1079 | result = self.service.removeRoutePattern(pattern=pattern, routePartitionName=partition) 1080 | except Exception as fault: 1081 | result = None 1082 | self.last_exception = fault 1083 | 1084 | return result 1085 | 1086 | def update_sip_route_pattern(self, name): 1087 | # TODO: Need to implement 1088 | pass 1089 | 1090 | 1091 | ''' 1092 | 1093 | Conference Bridge 1094 | 1095 | ''' 1096 | 1097 | def add_cfb(self, cfb_data): 1098 | 1099 | try: 1100 | result = self.service.addConferenceBridge(conferenceBridge=cfb_data) 1101 | except Exception as fault: 1102 | result = None 1103 | self.last_exception = fault 1104 | 1105 | return result 1106 | 1107 | def add_cfb_cms(self, name, description, cfb_prefix, sip_trunk, 1108 | security_icon_control, override_dest, addresses, 1109 | username, password, port): 1110 | cms_data = { 1111 | 'name': name, 1112 | 'description': description, 1113 | 'product': 'Cisco Meeting Server', 1114 | 'conferenceBridgePrefix': cfb_prefix, 1115 | 'sipTrunkName': sip_trunk, 1116 | 'allowCFBControlOfCallSecurityIcon': security_icon_control, 1117 | 'overrideSIPTrunkAddress': override_dest, 1118 | 'addresses': { 1119 | 'address': addresses 1120 | }, 1121 | 'userName': username, 1122 | 'password': password, 1123 | 'httpPort': port 1124 | } 1125 | 1126 | result = self.add_cfb(cms_data) 1127 | 1128 | return result 1129 | 1130 | def get_cfb(self, name): 1131 | 1132 | try: 1133 | result = self.service.getConferenceBridge(name=name) 1134 | except Exception as fault: 1135 | result = None 1136 | self.last_exception = fault 1137 | 1138 | return result 1139 | 1140 | def remove_cfb(self, name): 1141 | pass 1142 | 1143 | # try: 1144 | # result = self.service.removeCss(name=name) 1145 | # except Exception as fault: 1146 | # result = None 1147 | # self.last_exception = fault 1148 | # 1149 | # return result 1150 | 1151 | def update_cfb(self, css_name, description, partition_list): 1152 | pass 1153 | 1154 | # members = {'member': []} 1155 | # 1156 | # css_index = 1 1157 | # 1158 | # for partition in partition_list: 1159 | # partition_data = {'routePartitionName': partition, 1160 | # 'index': css_index} 1161 | # members['member'].append(partition_data) 1162 | # css_index += 1 1163 | # 1164 | # try: 1165 | # result = self.service.updateCss(name=css_name, description=description, members=members) 1166 | # except Exception as fault: 1167 | # result = None 1168 | # self.last_exception = fault 1169 | # 1170 | # return result 1171 | 1172 | ''' 1173 | 1174 | Media Resource Group 1175 | 1176 | ''' 1177 | 1178 | def get_mrg(self, name): 1179 | 1180 | try: 1181 | result = self.service.getMediaResourceGroup(name=name) 1182 | except Exception as fault: 1183 | result = None 1184 | self.last_exception = fault 1185 | 1186 | return result 1187 | 1188 | ''' 1189 | 1190 | Media Resource Group List 1191 | 1192 | ''' 1193 | 1194 | def get_mrgl(self, name): 1195 | 1196 | try: 1197 | result = self.service.getMediaResourceList(name=name) 1198 | except Exception as fault: 1199 | result = None 1200 | self.last_exception = fault 1201 | 1202 | return result 1203 | 1204 | ''' 1205 | 1206 | Device Pool 1207 | 1208 | ''' 1209 | 1210 | def get_device_pool(self, name): 1211 | 1212 | try: 1213 | result = self.service.getDevicePool(name=name) 1214 | except Exception as fault: 1215 | result = None 1216 | self.last_exception = fault 1217 | 1218 | return result 1219 | 1220 | ''' 1221 | 1222 | Device Security Profile 1223 | 1224 | ''' 1225 | 1226 | def get_phone_security_profile(self, name): 1227 | 1228 | try: 1229 | result = self.service.getPhoneSecurityProfile(name=name) 1230 | except Exception as fault: 1231 | result = None 1232 | self.last_exception = fault 1233 | 1234 | return result 1235 | 1236 | def add_phone_security_profile(self, phone_type, protocol, name, description, device_security_mode, 1237 | authentication_mode, key_size, key_order, ec_key_size, tftp_encrypted_config, 1238 | nonce_validity_time, transport_type, sip_phone_port, enable_digest_auth): 1239 | 1240 | security_profile = {'phoneType': phone_type, 1241 | 'protocol': protocol, 1242 | 'name': name, 1243 | 'description': description, 1244 | 'deviceSecurityMode': device_security_mode, 1245 | 'authenticationMode': authentication_mode, 1246 | 'keySize': key_size, 1247 | 'keyOrder': key_order, 1248 | 'ecKeySize': ec_key_size, 1249 | 'tftpEncryptedConfig': tftp_encrypted_config, 1250 | 'nonceValidityTime': nonce_validity_time, 1251 | 'transportType': transport_type, 1252 | 'sipPhonePort': sip_phone_port, 1253 | 'enableDigestAuthentication': enable_digest_auth, 1254 | } 1255 | 1256 | try: 1257 | result = self.service.addPhoneSecurityProfile(phoneSecurityProfile=security_profile) 1258 | except Exception as fault: 1259 | result = None 1260 | self.last_exception = fault 1261 | 1262 | return result 1263 | 1264 | ''' 1265 | 1266 | SIP Trunk Security Profile 1267 | 1268 | ''' 1269 | 1270 | def get_sip_trunk_security_profile(self, name): 1271 | 1272 | try: 1273 | result = self.service.getSipTrunkSecurityProfile(name=name) 1274 | except Exception as fault: 1275 | result = None 1276 | self.last_exception = fault 1277 | 1278 | return result 1279 | 1280 | def add_sip_trunk_security_profile(self, name, description, security_mode, incoming_transport, outgoing_transport, 1281 | digest_auth, nonce_policy_time, x509_subject_name, incoming_port, 1282 | app_level_auth, accept_presence_subscription, accept_ood_refer, 1283 | accept_unsolicited_notify, allow_replaces, transmit_security_status, 1284 | sip_v150_outbound_offer_filter, allow_charging_header): 1285 | 1286 | security_profile = { 1287 | 'name': name, 1288 | 'description': description, 1289 | 'securityMode': security_mode, 1290 | 'incomingTransport': incoming_transport, 1291 | 'outgoingTransport': outgoing_transport, 1292 | 'digestAuthentication': digest_auth, 1293 | 'noncePolicyTime': nonce_policy_time, 1294 | 'x509SubjectName': x509_subject_name, 1295 | 'incomingPort': incoming_port, 1296 | 'applLevelAuthentication': app_level_auth, 1297 | 'acceptPresenceSubscription': accept_presence_subscription, 1298 | 'acceptOutOfDialogRefer': accept_ood_refer, 1299 | 'acceptUnsolicitedNotification': accept_unsolicited_notify, 1300 | 'allowReplaceHeader': allow_replaces, 1301 | 'transmitSecurityStatus': transmit_security_status, 1302 | 'sipV150OutboundSdpOfferFiltering': sip_v150_outbound_offer_filter, 1303 | 'allowChargingHeader': allow_charging_header, 1304 | } 1305 | 1306 | try: 1307 | result = self.service.addSipTrunkSecurityProfile(sipTrunkSecurityProfile=security_profile) 1308 | except Exception as fault: 1309 | result = None 1310 | self.last_exception = fault 1311 | 1312 | return result 1313 | 1314 | def remove_sip_trunk_security_profile(self, name): 1315 | 1316 | try: 1317 | result = self.service.removeSipTrunkSecurityProfile(name=name) 1318 | except Exception as fault: 1319 | result = None 1320 | self.last_exception = fault 1321 | 1322 | return result 1323 | 1324 | 1325 | 1326 | ''' 1327 | 1328 | SIP Profile 1329 | 1330 | ''' 1331 | 1332 | 1333 | def get_sip_profile(self, name): 1334 | 1335 | try: 1336 | result = self.service.getSipProfile(name=name) 1337 | except Exception as fault: 1338 | result = None 1339 | self.last_exception = fault 1340 | 1341 | return result 1342 | 1343 | def add_sip_profile(self, profile_data): 1344 | 1345 | try: 1346 | result = self.service.addSipProfile(sipProfile=profile_data) 1347 | except Exception as fault: 1348 | result = None 1349 | self.last_exception = fault 1350 | 1351 | return result 1352 | 1353 | def update_sip_profile(self, profile_data): 1354 | 1355 | try: 1356 | result = self.service.updateSipProfile(**profile_data) 1357 | except Exception as fault: 1358 | result = None 1359 | self.last_exception = fault 1360 | 1361 | return result 1362 | 1363 | ''' 1364 | 1365 | SIP Trunk 1366 | 1367 | ''' 1368 | 1369 | def get_sip_trunk(self, name): 1370 | 1371 | try: 1372 | result = self.service.getSipTrunk(name=name) 1373 | except Exception as fault: 1374 | result = None 1375 | self.last_exception = fault 1376 | 1377 | return result 1378 | 1379 | def remove_sip_trunk(self, name): 1380 | 1381 | try: 1382 | result = self.service.removeSipTrunk(name=name) 1383 | except Exception as fault: 1384 | result = None 1385 | self.last_exception = fault 1386 | 1387 | return result 1388 | 1389 | def add_sip_trunk(self, trunk_data): 1390 | 1391 | try: 1392 | result = self.service.addSipTrunk(sipTrunk=trunk_data) 1393 | except Exception as fault: 1394 | result = None 1395 | self.last_exception = fault 1396 | 1397 | return result 1398 | 1399 | def update_sip_trunk(self, trunk_data): 1400 | 1401 | try: 1402 | result = self.service.updateSipTrunk(**trunk_data) 1403 | except Exception as fault: 1404 | result = None 1405 | self.last_exception = fault 1406 | 1407 | return result 1408 | 1409 | ''' 1410 | 1411 | Reset / Restart Devices 1412 | 1413 | ''' 1414 | 1415 | def do_reset_restart_device(self, device, is_hard_reset, is_mgcp): 1416 | reset_data = { 1417 | 'deviceName': device, 1418 | 'isHardReset': is_hard_reset, 1419 | 'isMGCP': is_mgcp 1420 | } 1421 | 1422 | try: 1423 | result = self.service.doDeviceReset(**reset_data) 1424 | except Exception as fault: 1425 | result = None 1426 | self.last_exception = fault 1427 | 1428 | return result 1429 | 1430 | def reset_device(self, device): 1431 | result = self.do_reset_restart_device(device, True, False) 1432 | 1433 | return result 1434 | 1435 | def restart_device(self, device): 1436 | result = self.do_reset_restart_device(device, False, False) 1437 | 1438 | return result 1439 | 1440 | def reset_mgcp(self, device): 1441 | result = self.do_reset_restart_device(device, True, True) 1442 | 1443 | return result 1444 | 1445 | def restart_mgcp(self, device): 1446 | result = self.do_reset_restart_device(device, False, True) 1447 | 1448 | return result 1449 | 1450 | 1451 | ''' 1452 | 1453 | Service Parameters 1454 | 1455 | ''' 1456 | 1457 | def sql_update_service_parameter(self, name, value): 1458 | 1459 | query = "update processconfig set paramvalue = '{0}' where paramname = '{1}'".format(value, name) 1460 | 1461 | sql_result = self.run_sql_update(query) 1462 | 1463 | if sql_result['rows_updated'] > 0: 1464 | result = True 1465 | else: 1466 | result = False 1467 | 1468 | return result 1469 | 1470 | def sql_get_service_parameter(self, name): 1471 | 1472 | query = "select * from processconfig where paramname = '{0}'".format(name) 1473 | 1474 | sql_result = self.run_sql_query(query) 1475 | 1476 | if sql_result['num_rows'] > 0: 1477 | result = sql_result['rows'] 1478 | else: 1479 | result = None 1480 | 1481 | return result 1482 | 1483 | 1484 | ''' 1485 | 1486 | Device Association 1487 | 1488 | ''' 1489 | 1490 | def sql_associate_device_to_user(self, device, userid, association_type='1'): 1491 | 1492 | device_pkid = self.sql_get_device_pkid(device) 1493 | enduser_pkid = self.sql_get_enduser_pkid(userid) 1494 | 1495 | if device_pkid is not None and enduser_pkid is not None: 1496 | 1497 | query = "insert into enduserdevicemap (fkenduser, fkdevice, defaultprofile, tkuserassociation) " \ 1498 | "values ('{0}','{1}','f','{2}')".format(enduser_pkid, device_pkid, association_type) 1499 | 1500 | sql_result = self.run_sql_update(query) 1501 | 1502 | if sql_result['rows_updated'] > 0: 1503 | result = True 1504 | else: 1505 | result = False 1506 | 1507 | return result 1508 | 1509 | 1510 | ''' 1511 | 1512 | Remote Destinations 1513 | 1514 | ''' 1515 | 1516 | def get_remote_destination(self, destination): 1517 | try: 1518 | result = self.service.getRemoteDestination(destination=destination) 1519 | except Exception as fault: 1520 | result = None 1521 | self.last_exception = fault 1522 | 1523 | return result 1524 | 1525 | 1526 | def check_connectivity(self): 1527 | pass 1528 | 1529 | 1530 | class UcmServiceabilityToolkit: 1531 | last_exception = None 1532 | 1533 | ''' 1534 | 1535 | Constructor - Create new instance 1536 | 1537 | ''' 1538 | 1539 | def __init__(self, username, password, server_ip, tls_verify=True): 1540 | wsdl = 'https://{0}:8443/controlcenterservice2/services/ControlCenterServices?wsdl'.format(server_ip) 1541 | 1542 | self.session = Session() 1543 | self.session.auth = HTTPBasicAuth(username, password) 1544 | self.session.verify = tls_verify 1545 | 1546 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_serviceability.db', timeout=60) 1547 | 1548 | self.client = Client(wsdl=wsdl, transport=Transport(cache=self.cache, session=self.session)) 1549 | 1550 | self.service = self.client.service 1551 | 1552 | # enable_logging() 1553 | 1554 | def get_service(self): 1555 | return self.service 1556 | 1557 | 1558 | class UcmRisPortToolkit: 1559 | last_exception = None 1560 | 1561 | ''' 1562 | 1563 | Constructor - Create new instance 1564 | 1565 | ''' 1566 | 1567 | def __init__(self, username, password, server_ip, tls_verify=True): 1568 | wsdl = 'https://{0}:8443/realtimeservice2/services/RISService70?wsdl'.format(server_ip) 1569 | 1570 | self.session = Session() 1571 | self.session.auth = HTTPBasicAuth(username, password) 1572 | self.session.verify = tls_verify 1573 | 1574 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_risport.db', timeout=60) 1575 | 1576 | self.client = Client(wsdl=wsdl, transport=Transport(cache=self.cache, session=self.session)) 1577 | 1578 | self.service = self.client.create_service("{http://schemas.cisco.com/ast/soap}RisBinding", 1579 | "https://{0}:8443/realtimeservice2/services/RISService70".format(server_ip)) 1580 | 1581 | enable_logging() 1582 | 1583 | def get_service(self): 1584 | return self.service 1585 | 1586 | 1587 | class UcmPerfMonToolkit: 1588 | last_exception = None 1589 | compiled_re = None 1590 | history = HistoryPlugin() 1591 | 1592 | ''' 1593 | 1594 | Constructor - Create new instance 1595 | 1596 | ''' 1597 | 1598 | def __init__(self, username, password, server_ip, tls_verify=True): 1599 | wsdl = 'https://{0}:8443/perfmonservice2/services/PerfmonService?wsdl'.format(server_ip) 1600 | 1601 | self.session = Session() 1602 | self.session.auth = HTTPBasicAuth(username, password) 1603 | self.session.verify = tls_verify 1604 | 1605 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_risport.db', timeout=60) 1606 | 1607 | self.client = Client(wsdl=wsdl, plugins=[self.history], transport=Transport(cache=self.cache, session=self.session)) 1608 | 1609 | self.service = self.client.create_service("{http://schemas.cisco.com/ast/soap}PerfmonBinding", 1610 | "https://{0}:8443/perfmonservice2/services/PerfmonService".format(server_ip)) 1611 | 1612 | # enable_logging() 1613 | 1614 | def get_service(self): 1615 | return self.service 1616 | 1617 | def last_request_debug(self): 1618 | request_env = tostring(self.history.last_sent['envelope']) 1619 | request_headers = self.history.last_sent['http_headers'] 1620 | response_env = tostring(self.history.last_received['envelope']) 1621 | response_headers = self.history.last_received['http_headers'] 1622 | 1623 | return { 1624 | 'request': { 1625 | 'raw': self.history.last_sent, 1626 | 'headers': request_headers, 1627 | 'envelope': request_env 1628 | }, 1629 | 'response': { 1630 | 'raw': self.history.last_received, 1631 | 'headers': response_headers, 1632 | 'envelope': response_env 1633 | 1634 | } 1635 | } 1636 | 1637 | def decode_counter_name(self, counter_name_string): 1638 | # Converts string like \\\\vnt-cm1b.cisco.com\\Cisco Locations LBM(BranchRemote->Hub_None)\\BandwidthAvailable 1639 | # to an object 1640 | 1641 | decoded_counter = None 1642 | 1643 | if self.compiled_re is None: 1644 | self.compiled_re = re.compile(r"""\\\\([^\\]*)\\([^()\\]*)(\(([^\\]*)\))?\\([^\\]*)""") 1645 | 1646 | match_result = self.compiled_re.match(counter_name_string) 1647 | 1648 | if match_result is not None: 1649 | decoded_counter = { 1650 | 'host': match_result.group(1), 1651 | 'object': match_result.group(2), 1652 | 'instance': match_result.group(4), 1653 | 'counter': match_result.group(5) 1654 | } 1655 | 1656 | return decoded_counter 1657 | 1658 | 1659 | def perfmonOpenSession(self): 1660 | session_handle = self.service.perfmonOpenSession() 1661 | return session_handle 1662 | 1663 | def perfmonAddCounter(self, session_handle, counters): 1664 | ''' 1665 | :param session_handle: A session Handle returned from perfmonOpenSession() 1666 | :param counters: An array of counters or a single string for a single counter 1667 | :return: True for Success and False for Failure 1668 | ''' 1669 | 1670 | counter_data = [] 1671 | 1672 | if isinstance(counters, list): 1673 | counter_data = [ 1674 | { 1675 | 'Counter': [] 1676 | } 1677 | ] 1678 | 1679 | for counter in counters: 1680 | new_counter = { 1681 | 'Name': counter 1682 | } 1683 | counter_data[0]['Counter'].append(new_counter) 1684 | 1685 | elif counters is not None: 1686 | counter_data = [ 1687 | { 1688 | 'Counter': [ 1689 | { 1690 | 'Name': counters 1691 | } 1692 | ] 1693 | } 1694 | ] 1695 | 1696 | try: 1697 | self.service.perfmonAddCounter(SessionHandle=session_handle, ArrayOfCounter=counter_data) 1698 | result = True 1699 | except Exception as e: 1700 | result = False 1701 | 1702 | return result 1703 | 1704 | def perfmonRemoveCounter(self, session_handle, counters): 1705 | ''' 1706 | :param session_handle: A session Handle returned from perfmonOpenSession() 1707 | :param counters: An array of counters or a single string for a single counter 1708 | :return: True for Success and False for Failure 1709 | ''' 1710 | 1711 | counter_data = [] 1712 | 1713 | if isinstance(counters, list): 1714 | counter_data = [ 1715 | { 1716 | 'Counter': [] 1717 | } 1718 | ] 1719 | 1720 | for counter in counters: 1721 | new_counter = { 1722 | 'Name': counter 1723 | } 1724 | counter_data[0]['Counter'].append(new_counter) 1725 | 1726 | elif counters is not None: 1727 | counter_data = [ 1728 | { 1729 | 'Counter': [ 1730 | { 1731 | 'Name': counters 1732 | } 1733 | ] 1734 | } 1735 | ] 1736 | 1737 | try: 1738 | self.service.perfmonRemoveCounter(SessionHandle=session_handle, ArrayOfCounter=counter_data) 1739 | result = True 1740 | except Exception as e: 1741 | result = False 1742 | 1743 | return result 1744 | 1745 | 1746 | def perfmonCollectSessionData(self, session_handle): 1747 | try: 1748 | session_data = self.service.perfmonCollectSessionData(SessionHandle=session_handle) 1749 | 1750 | result_data = {} 1751 | 1752 | for data in session_data: 1753 | counter_name_data = self.decode_counter_name(data['Name']['_value_1']) 1754 | if counter_name_data is not None: 1755 | counter_host = counter_name_data['host'] 1756 | counter_object = counter_name_data['object'] 1757 | counter_instance = counter_name_data['instance'] 1758 | counter_name = counter_name_data['counter'] 1759 | counter_value = data['Value'] 1760 | counter_status = data['CStatus'] 1761 | if counter_status == 0: 1762 | if counter_host not in result_data: 1763 | result_data[counter_host] = {} 1764 | if counter_object not in result_data[counter_host]: 1765 | result_data[counter_host][counter_object] = {} 1766 | 1767 | if counter_instance is None: 1768 | result_data[counter_host][counter_object]['multi_instance'] = False 1769 | if 'counters' not in result_data[counter_host][counter_object]: 1770 | result_data[counter_host][counter_object]['counters'] = {} 1771 | result_data[counter_host][counter_object]['counters'][counter_name] = counter_value 1772 | else: 1773 | result_data[counter_host][counter_object]['multi_instance'] = True 1774 | if 'instances' not in result_data[counter_host][counter_object]: 1775 | result_data[counter_host][counter_object]['instances'] = {} 1776 | if counter_instance not in result_data[counter_host][counter_object]['instances']: 1777 | result_data[counter_host][counter_object]['instances'][counter_instance] = {} 1778 | result_data[counter_host][counter_object]['instances'][counter_instance][counter_name] = counter_value 1779 | else: 1780 | # TODO: Clean up the session and restart it if we're not getting valid data 1781 | pass 1782 | except Exception as e: 1783 | print(e) 1784 | result_data = None 1785 | 1786 | return result_data 1787 | 1788 | def perfmonCloseSession(self, session_handle): 1789 | try: 1790 | session_handle = self.service.perfmonCloseSession(SessionHandle=session_handle) 1791 | except Exception as e: 1792 | session_handle = None 1793 | return session_handle 1794 | 1795 | def perfmonListCounter(self, host): 1796 | try: 1797 | counter_list = {} 1798 | counter_data = self.service.perfmonListCounter(Host=host) 1799 | for object_data in counter_data: 1800 | object_name = object_data['Name']['_value_1'] 1801 | counter_list[object_name] = {} 1802 | counter_list[object_name]['multi_instance'] = object_data['MultiInstance'] 1803 | counter_list[object_name]['counters'] = [] 1804 | for counter in object_data['ArrayOfCounter']['item']: 1805 | counter_list[object_name]['counters'].append(counter['Name']['_value_1']) 1806 | except Exception as e: 1807 | counter_list = None 1808 | 1809 | return counter_list 1810 | 1811 | def perfmonListInstance(self, host, object_name): 1812 | try: 1813 | instance_data = self.service.perfmonListInstance(Host=host, Object=object_name) 1814 | 1815 | instances = [] 1816 | 1817 | for instance in instance_data: 1818 | instances.append(instance['Name']['_value_1']) 1819 | except Exception as e: 1820 | instances = None 1821 | 1822 | return instances 1823 | 1824 | 1825 | 1826 | class UcmLogCollectionToolkit: 1827 | 1828 | last_exception = None 1829 | 1830 | ''' 1831 | 1832 | Constructor - Create new instance 1833 | 1834 | ''' 1835 | 1836 | def __init__(self, username, password, server_ip, tls_verify=True): 1837 | wsdl = 'https://{0}:8443/logcollectionservice2/services/LogCollectionPortTypeService?wsdl'.format(server_ip) 1838 | 1839 | self.session = Session() 1840 | self.session.auth = HTTPBasicAuth(username, password) 1841 | self.session.verify = tls_verify 1842 | 1843 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_logcollection.db', timeout=60) 1844 | 1845 | self.client = Client(wsdl=wsdl, transport=Transport(cache=self.cache, session=self.session)) 1846 | 1847 | self.service = self.client.service 1848 | 1849 | enable_logging() 1850 | 1851 | def get_service(self): 1852 | return self.service 1853 | 1854 | 1855 | class UcmDimeGetFileToolkit: 1856 | 1857 | last_exception = None 1858 | 1859 | ''' 1860 | 1861 | Constructor - Create new instance 1862 | 1863 | ''' 1864 | 1865 | def __init__(self, username, password, server_ip, tls_verify=True): 1866 | wsdl = 'https://{0}:8443/logcollectionservice/services/DimeGetFileService?wsdl'.format(server_ip) 1867 | 1868 | self.session = Session() 1869 | self.session.auth = HTTPBasicAuth(username, password) 1870 | self.session.verify = tls_verify 1871 | 1872 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_logcollection.db', timeout=60) 1873 | 1874 | self.client = Client(wsdl=wsdl, transport=Transport(cache=self.cache, session=self.session)) 1875 | 1876 | self.service = self.client.service 1877 | 1878 | enable_logging() 1879 | 1880 | def get_service(self): 1881 | return self.service 1882 | 1883 | 1884 | class PawsToolkit: 1885 | 1886 | last_exception = None 1887 | 1888 | ''' 1889 | 1890 | Constructor - Create new instance 1891 | 1892 | ''' 1893 | 1894 | def __init__(self, username, password, server_ip, service, tls_verify=True): 1895 | 1896 | dir = os.path.dirname(__file__) 1897 | 1898 | if (service == 'HardwareInformationService'): 1899 | wsdl = os.path.join(dir, 'paws/hardware_information_service.wsdl') 1900 | binding = "{http://services.api.platform.vos.cisco.com}HardwareInformationServiceSoap11Binding" 1901 | endpoint = "https://{0}:8443/platform-services/services/HardwareInformationService.HardwareInformationServiceHttpsSoap11Endpoint/".format(server_ip) 1902 | elif service == 'VersionService': 1903 | wsdl = 'https://{0}:8443/platform-services/services/VersionService?wsdl'.format(server_ip) 1904 | binding = "{http://services.api.platform.vos.cisco.com}VersionServiceSoap12Binding" 1905 | endpoint = "https://{0}:8443/platform-services/services/VersionService.VersionServiceHttpsSoap12Endpoint/".format(server_ip) # nopep8 1906 | elif service == 'OptionsService': 1907 | wsdl = 'https://{0}:8443/platform-services/services/OptionsService?wsdl'.format(server_ip) 1908 | binding = "{http://services.api.platform.vos.cisco.com}OptionsServiceSoap12Binding" 1909 | endpoint = "https://{0}:8443/platform-services/services/OptionsService.OptionsServiceHttpsSoap12Endpoint/".format(server_ip) # nopep8 1910 | elif service == 'ProductService': 1911 | wsdl = 'https://{0}:8443/platform-services/services/ProductService?wsdl'.format(server_ip) 1912 | binding = "{http://services.api.platform.vos.cisco.com}ProductServiceSoap12Binding" 1913 | endpoint = "https://{0}:8443/platform-services/services/ProductService.ProductServiceHttpsSoap12Endpoint/".format(server_ip) # nopep8 1914 | elif service == 'VersionService': 1915 | wsdl = 'https://{0}:8443/platform-services/services/VersionService?wsdl'.format(server_ip) 1916 | binding = "{http://services.api.platform.vos.cisco.com}VersionServiceSoap12Binding" 1917 | endpoint = "https://{0}:8443/platform-services/services/VersionService.VersionServiceHttpsSoap12Endpoint/".format(server_ip) # nopep8 1918 | elif service == 'ClusterNodesService': 1919 | wsdl = 'https://{0}:8443/platform-services/services/ClusterNodesService?wsdl'.format(server_ip) 1920 | binding = "{http://services.api.platform.vos.cisco.com}ClusterNodesServiceSoap12Binding" 1921 | endpoint = "https://{0}:8443/platform-services/services/ClusterNodesService.ClusterNodesServiceHttpsSoap12Endpoint/".format(server_ip) # nopep8 1922 | 1923 | self.session = Session() 1924 | self.session.auth = HTTPBasicAuth(username, password) 1925 | self.session.verify = tls_verify 1926 | 1927 | self.cache = SqliteCache(path=tempfile.gettempdir()+'/sqlite_paws.db', timeout=60) 1928 | 1929 | self.client = Client(wsdl=wsdl, transport=Transport(cache=self.cache, session=self.session)) 1930 | 1931 | self.service = self.client.create_service(binding, endpoint) 1932 | 1933 | # enable_logging() 1934 | 1935 | def get_service(self): 1936 | return self.service 1937 | 1938 | def get_hardware_information(self): 1939 | hw_info = self.service.getHardwareInformation() 1940 | 1941 | return hw_info 1942 | 1943 | def get_active_version(self): 1944 | active_version = self.service.getActiveVersion() 1945 | 1946 | return active_version 1947 | 1948 | 1949 | -------------------------------------------------------------------------------- /axltoolkit/paws/hardware_information_service.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | HardwareInformationService 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /axltoolkit/schema/10.0/AXLEnums.xsd: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | Version 1.00 12 | Copyright (c) 2001, 2005-2013 Cisco Systems, Inc. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | 1430 | 1431 | 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | 1511 | 1512 | 1513 | 1514 | 1515 | 1516 | 1517 | 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | 1526 | 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 1654 | 1655 | 1656 | 1657 | 1658 | 1659 | 1660 | 1661 | 1662 | 1663 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | 1674 | 1675 | 1676 | 1677 | 1678 | 1679 | 1680 | 1681 | 1682 | 1683 | 1684 | 1685 | 1686 | 1687 | 1688 | 1689 | 1690 | 1691 | 1692 | 1693 | 1694 | 1695 | 1696 | 1697 | 1698 | 1699 | 1700 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | 1728 | 1729 | 1730 | 1731 | 1732 | 1733 | 1734 | 1735 | 1736 | 1737 | 1738 | 1739 | 1740 | 1741 | 1742 | 1743 | 1744 | 1745 | 1746 | 1747 | 1748 | 1749 | 1750 | 1751 | 1752 | 1753 | 1754 | 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | 1761 | 1762 | 1763 | 1764 | 1765 | 1766 | 1767 | 1768 | 1769 | 1770 | 1771 | 1772 | 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | 1779 | 1780 | 1781 | 1782 | 1783 | 1784 | 1785 | 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | 1795 | 1796 | 1797 | 1798 | 1799 | 1800 | 1801 | 1802 | 1803 | 1804 | 1805 | 1806 | 1807 | 1808 | 1809 | 1810 | 1811 | 1812 | 1813 | 1814 | 1815 | 1816 | 1817 | 1818 | 1819 | 1820 | 1821 | 1822 | 1823 | 1824 | 1825 | 1826 | 1827 | 1828 | 1829 | 1830 | 1831 | 1832 | 1833 | 1834 | 1835 | 1836 | 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1846 | 1847 | 1848 | 1849 | 1850 | 1851 | 1852 | 1853 | 1854 | 1855 | 1856 | 1857 | 1858 | 1859 | 1860 | 1861 | 1862 | 1863 | 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | 1870 | 1871 | 1872 | 1873 | 1874 | 1875 | 1876 | 1877 | 1878 | 1879 | 1880 | 1881 | 1882 | 1883 | 1884 | 1885 | 1886 | 1887 | 1888 | 1889 | 1890 | 1891 | 1892 | 1893 | 1894 | 1895 | 1896 | 1897 | 1898 | 1899 | 1900 | 1901 | 1902 | 1903 | 1904 | 1905 | 1906 | 1907 | 1908 | 1909 | 1910 | 1911 | 1912 | 1913 | 1914 | 1915 | 1916 | 1917 | 1918 | 1919 | 1920 | 1921 | 1922 | 1923 | 1924 | 1925 | 1926 | 1927 | 1928 | 1929 | 1930 | 1931 | 1932 | 1933 | 1934 | 1935 | 1936 | 1937 | 1938 | 1939 | 1940 | 1941 | 1942 | 1943 | 1944 | 1945 | 1946 | 1947 | 1948 | 1949 | 1950 | 1951 | 1952 | 1953 | 1954 | 1955 | 1956 | 1957 | 1958 | 1959 | 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1968 | 1969 | 1970 | 1971 | 1972 | 1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | 1980 | 1981 | 1982 | 1983 | 1984 | 1985 | 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 | 2030 | 2031 | 2032 | 2033 | 2034 | 2035 | 2036 | 2037 | 2038 | 2039 | 2040 | 2041 | 2042 | 2043 | 2044 | 2045 | 2046 | 2047 | 2048 | 2049 | 2050 | 2051 | 2052 | 2053 | 2054 | 2055 | 2056 | 2057 | 2058 | 2059 | 2060 | 2061 | 2062 | 2063 | 2064 | 2065 | 2066 | 2067 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | 2077 | 2078 | 2079 | 2080 | 2081 | 2082 | 2083 | 2084 | 2085 | 2086 | 2087 | 2088 | 2089 | 2090 | 2091 | 2092 | 2093 | 2094 | 2095 | 2096 | 2097 | 2098 | 2099 | 2100 | 2101 | 2102 | 2103 | 2104 | 2105 | 2106 | 2107 | 2108 | 2109 | 2110 | 2111 | 2112 | 2113 | 2114 | 2115 | 2116 | 2117 | 2118 | 2119 | 2120 | 2121 | 2122 | 2123 | 2124 | 2125 | 2126 | 2127 | 2128 | 2129 | 2130 | 2131 | 2132 | 2133 | 2134 | 2135 | 2136 | 2137 | 2138 | 2139 | 2140 | 2141 | 2142 | 2143 | 2144 | 2145 | 2146 | 2147 | 2148 | 2149 | 2150 | 2151 | 2152 | 2153 | 2154 | 2155 | 2156 | 2157 | 2158 | 2159 | 2160 | 2161 | 2162 | 2163 | 2164 | 2165 | 2166 | 2167 | 2168 | 2169 | 2170 | 2171 | 2172 | 2173 | 2174 | 2175 | 2176 | 2177 | 2178 | 2179 | 2180 | 2181 | 2182 | 2183 | 2184 | 2185 | 2186 | 2187 | 2188 | 2189 | 2190 | 2191 | 2192 | 2193 | 2194 | 2195 | 2196 | 2197 | 2198 | 2199 | 2200 | 2201 | 2202 | 2203 | 2204 | 2205 | 2206 | 2207 | 2208 | 2209 | 2210 | 2211 | 2212 | 2213 | 2214 | 2215 | 2216 | 2217 | 2218 | 2219 | 2220 | 2221 | 2222 | 2223 | 2224 | 2225 | 2226 | 2227 | 2228 | 2229 | 2230 | 2231 | 2232 | 2233 | 2234 | 2235 | 2236 | 2237 | 2238 | 2239 | 2240 | 2241 | 2242 | 2243 | 2244 | 2245 | 2246 | 2247 | 2248 | 2249 | 2250 | 2251 | 2252 | 2253 | 2254 | 2255 | 2256 | 2257 | 2258 | 2259 | 2260 | 2261 | 2262 | 2263 | 2264 | 2265 | 2266 | 2267 | 2268 | 2269 | 2270 | 2271 | 2272 | 2273 | 2274 | 2275 | 2276 | 2277 | 2278 | 2279 | 2280 | 2281 | 2282 | 2283 | 2284 | 2285 | 2286 | 2287 | 2288 | 2289 | 2290 | 2291 | 2292 | 2293 | 2294 | 2295 | 2296 | 2297 | 2298 | 2299 | 2300 | 2301 | 2302 | 2303 | 2304 | 2305 | 2306 | 2307 | 2308 | 2309 | 2310 | 2311 | 2312 | 2313 | 2314 | 2315 | 2316 | 2317 | 2318 | 2319 | 2320 | 2321 | 2322 | 2323 | 2324 | 2325 | 2326 | 2327 | 2328 | 2329 | 2330 | 2331 | 2332 | 2333 | 2334 | 2335 | 2336 | 2337 | 2338 | 2339 | 2340 | 2341 | 2342 | 2343 | 2344 | 2345 | 2346 | 2347 | 2348 | 2349 | 2350 | 2351 | 2352 | 2353 | 2354 | 2355 | 2356 | 2357 | 2358 | 2359 | 2360 | 2361 | 2362 | 2363 | 2364 | 2365 | 2366 | 2367 | 2368 | 2369 | 2370 | 2371 | 2372 | 2373 | 2374 | 2375 | 2376 | 2377 | 2378 | 2379 | 2380 | 2381 | 2382 | 2383 | 2384 | 2385 | 2386 | 2387 | 2388 | 2389 | 2390 | 2391 | 2392 | 2393 | 2394 | 2395 | 2396 | 2397 | 2398 | 2399 | 2400 | 2401 | 2402 | 2403 | 2404 | 2405 | 2406 | 2407 | 2408 | 2409 | 2410 | 2411 | 2412 | 2413 | 2414 | 2415 | 2416 | 2417 | 2418 | 2419 | 2420 | 2421 | 2422 | 2423 | 2424 | 2425 | 2426 | 2427 | 2428 | 2429 | 2430 | 2431 | 2432 | 2433 | 2434 | 2435 | 2436 | 2437 | 2438 | 2439 | 2440 | 2441 | 2442 | 2443 | 2444 | 2445 | 2446 | 2447 | 2448 | 2449 | 2450 | 2451 | 2452 | 2453 | 2454 | 2455 | 2456 | 2457 | 2458 | 2459 | 2460 | 2461 | 2462 | 2463 | 2464 | 2465 | 2466 | 2467 | 2468 | 2469 | 2470 | 2471 | 2472 | 2473 | 2474 | 2475 | 2476 | 2477 | 2478 | 2479 | 2480 | 2481 | 2482 | 2483 | 2484 | 2485 | 2486 | 2487 | 2488 | 2489 | 2490 | 2491 | 2492 | 2493 | 2494 | 2495 | 2496 | 2497 | 2498 | 2499 | 2500 | 2501 | 2502 | 2503 | 2504 | 2505 | 2506 | 2507 | 2508 | 2509 | 2510 | 2511 | 2512 | 2513 | 2514 | 2515 | 2516 | 2517 | 2518 | 2519 | 2520 | 2521 | 2522 | 2523 | 2524 | 2525 | 2526 | 2527 | 2528 | 2529 | 2530 | 2531 | 2532 | 2533 | 2534 | 2535 | 2536 | 2537 | 2538 | 2539 | 2540 | 2541 | 2542 | 2543 | 2544 | 2545 | 2546 | 2547 | 2548 | 2549 | 2550 | 2551 | 2552 | 2553 | 2554 | 2555 | 2556 | 2557 | 2558 | 2559 | 2560 | 2561 | 2562 | 2563 | 2564 | 2565 | 2566 | 2567 | 2568 | 2569 | 2570 | 2571 | 2572 | 2573 | 2574 | 2575 | 2576 | 2577 | 2578 | 2579 | 2580 | 2581 | 2582 | 2583 | 2584 | 2585 | 2586 | 2587 | 2588 | 2589 | 2590 | 2591 | 2592 | 2593 | 2594 | 2595 | 2596 | 2597 | 2598 | 2599 | 2600 | 2601 | 2602 | 2603 | 2604 | 2605 | 2606 | 2607 | 2608 | 2609 | 2610 | 2611 | 2612 | 2613 | 2614 | 2615 | 2616 | 2617 | 2618 | 2619 | 2620 | 2621 | 2622 | 2623 | 2624 | 2625 | 2626 | 2627 | 2628 | 2629 | 2630 | 2631 | 2632 | 2633 | 2634 | 2635 | 2636 | 2637 | 2638 | 2639 | 2640 | 2641 | 2642 | 2643 | 2644 | 2645 | 2646 | 2647 | 2648 | 2649 | 2650 | 2651 | 2652 | 2653 | 2654 | 2655 | 2656 | 2657 | 2658 | 2659 | 2660 | 2661 | 2662 | 2663 | 2664 | 2665 | 2666 | 2667 | 2668 | 2669 | 2670 | 2671 | 2672 | 2673 | 2674 | 2675 | 2676 | 2677 | 2678 | 2679 | 2680 | 2681 | 2682 | 2683 | 2684 | 2685 | 2686 | 2687 | 2688 | 2689 | 2690 | 2691 | 2692 | 2693 | 2694 | 2695 | 2696 | 2697 | 2698 | 2699 | 2700 | 2701 | 2702 | 2703 | 2704 | 2705 | 2706 | 2707 | 2708 | 2709 | 2710 | 2711 | 2712 | 2713 | 2714 | 2715 | 2716 | 2717 | 2718 | 2719 | 2720 | 2721 | 2722 | 2723 | 2724 | 2725 | 2726 | 2727 | 2728 | 2729 | 2730 | 2731 | 2732 | 2733 | 2734 | 2735 | 2736 | 2737 | 2738 | 2739 | 2740 | 2741 | 2742 | 2743 | 2744 | 2745 | 2746 | 2747 | 2748 | 2749 | 2750 | 2751 | 2752 | 2753 | 2754 | 2755 | 2756 | 2757 | 2758 | 2759 | 2760 | 2761 | 2762 | 2763 | 2764 | 2765 | 2766 | 2767 | 2768 | 2769 | 2770 | 2771 | 2772 | 2773 | 2774 | 2775 | 2776 | 2777 | 2778 | 2779 | 2780 | 2781 | 2782 | 2783 | 2784 | 2785 | 2786 | 2787 | 2788 | 2789 | 2790 | 2791 | 2792 | 2793 | 2794 | 2795 | 2796 | 2797 | 2798 | 2799 | 2800 | 2801 | 2802 | 2803 | 2804 | 2805 | 2806 | 2807 | 2808 | 2809 | 2810 | 2811 | 2812 | 2813 | 2814 | 2815 | 2816 | 2817 | 2818 | 2819 | 2820 | 2821 | 2822 | 2823 | 2824 | 2825 | 2826 | 2827 | 2828 | 2829 | 2830 | 2831 | 2832 | 2833 | 2834 | 2835 | 2836 | 2837 | 2838 | 2839 | 2840 | 2841 | 2842 | 2843 | 2844 | 2845 | 2846 | 2847 | 2848 | 2849 | 2850 | 2851 | 2852 | 2853 | 2854 | 2855 | 2856 | 2857 | 2858 | 2859 | 2860 | 2861 | 2862 | 2863 | 2864 | 2865 | 2866 | 2867 | 2868 | 2869 | 2870 | 2871 | 2872 | 2873 | 2874 | 2875 | 2876 | 2877 | 2878 | 2879 | 2880 | 2881 | 2882 | 2883 | 2884 | 2885 | 2886 | 2887 | 2888 | 2889 | 2890 | 2891 | 2892 | 2893 | 2894 | 2895 | 2896 | 2897 | 2898 | 2899 | 2900 | 2901 | 2902 | 2903 | 2904 | 2905 | 2906 | 2907 | 2908 | 2909 | 2910 | 2911 | 2912 | 2913 | 2914 | 2915 | 2916 | 2917 | 2918 | 2919 | 2920 | 2921 | 2922 | 2923 | 2924 | 2925 | 2926 | 2927 | 2928 | 2929 | 2930 | 2931 | 2932 | 2933 | 2934 | 2935 | 2936 | 2937 | 2938 | 2939 | 2940 | 2941 | 2942 | 2943 | 2944 | 2945 | 2946 | 2947 | 2948 | 2949 | 2950 | 2951 | 2952 | 2953 | 2954 | 2955 | 2956 | 2957 | 2958 | 2959 | 2960 | 2961 | 2962 | 2963 | 2964 | 2965 | 2966 | 2967 | 2968 | 2969 | 2970 | 2971 | 2972 | 2973 | 2974 | 2975 | 2976 | 2977 | 2978 | 2979 | 2980 | 2981 | 2982 | 2983 | 2984 | 2985 | 2986 | 2987 | 2988 | 2989 | 2990 | 2991 | 2992 | 2993 | 2994 | 2995 | 2996 | 2997 | 2998 | 2999 | 3000 | 3001 | 3002 | 3003 | 3004 | 3005 | 3006 | 3007 | 3008 | 3009 | 3010 | 3011 | 3012 | 3013 | 3014 | 3015 | 3016 | 3017 | 3018 | 3019 | 3020 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.3 2 | asn1crypto==1.3.0 3 | attrs==19.3.0 4 | cached-property==1.5.1 5 | certifi==2024.7.4 6 | chardet==3.0.4 7 | defusedxml==0.6.0 8 | idna==3.7 9 | isodate==0.6.0 10 | lxml==4.9.1 11 | pytz==2019.3 12 | requests==2.32.2 13 | requests-toolbelt==0.9.1 14 | six==1.14.0 15 | urllib3==1.26.19 16 | zeep==3.4.0 -------------------------------------------------------------------------------- /samples/credentials.py: -------------------------------------------------------------------------------- 1 | user = '' 2 | password = '' 3 | 4 | platform_user = '' 5 | platform_password = '' 6 | -------------------------------------------------------------------------------- /samples/sample1_axl.py: -------------------------------------------------------------------------------- 1 | from axltoolkit import AxlToolkit 2 | from credentials import user, password, platform_user, platform_password 3 | 4 | # Be sure to update the credentials.py file with your AXL User and Platform User credentials 5 | 6 | # Put the IP address of your UCM Publisher 7 | ucm_ip = '172.18.106.58' 8 | 9 | axl = AxlToolkit(username=user, password=password, server_ip=ucm_ip, tls_verify=False, version='12.5') 10 | 11 | 12 | # Example of using Thick AXL to retrieve User Info 13 | # Replace this with a valid User ID from your UCM cluster: 14 | userid = 'pgiralt' 15 | 16 | result = axl.get_user(userid) 17 | 18 | print(result) 19 | 20 | userdata = result['return']['user'] 21 | 22 | print("Your name is " + userdata['firstName']) 23 | 24 | 25 | 26 | # Example of using thin AXL to retrieve User Info: 27 | 28 | query = "select * from enduser where userid = 'pgiralt'" 29 | result = axl.run_sql_query(query) 30 | 31 | print(result) 32 | 33 | 34 | result = axl.list_phone(name='CSF%') 35 | print(result) 36 | 37 | -------------------------------------------------------------------------------- /samples/sample2_ris.py: -------------------------------------------------------------------------------- 1 | from axltoolkit import UcmRisPortToolkit 2 | from credentials import user, password, platform_user, platform_password 3 | 4 | ucm_ip = '172.18.106.58' 5 | 6 | axl = UcmRisPortToolkit(username=user, password=password, server_ip=ucm_ip, tls_verify=False) 7 | 8 | selection_criteria = { 9 | 'DeviceClass': 'Any', 10 | 'SelectBy': 'Name', 11 | 'MaxReturnedDevices': '1000', 12 | 'Model': 255, 13 | 'Status': "Any", 14 | 'SelectItems': [ 15 | { 16 | 'item': [ 17 | 'SEP*', # Replace these with the devices you want to retrieve 18 | ] 19 | } 20 | ] 21 | } 22 | 23 | result = axl.get_service().selectCmDevice(StateInfo='', CmSelectionCriteria=selection_criteria) 24 | 25 | for node in result['SelectCmDeviceResult']['CmNodes']['item']: 26 | server = node['Name'] 27 | devices = node['CmDevices']['item'] 28 | 29 | for device in devices: 30 | if 'IPAddress' in device: 31 | if device['IPAddress'] is not None and 'item' in device['IPAddress']: 32 | if len(device['IPAddress']['item']) > 0: 33 | print(device['IPAddress']['item'][0]['IP'] + ',' + device['Name']) 34 | 35 | 36 | -------------------------------------------------------------------------------- /samples/sample3_perfmon.py: -------------------------------------------------------------------------------- 1 | from axltoolkit import UcmPerfMonToolkit 2 | from credentials import user, password 3 | 4 | ucm_ip = '172.18.106.58' 5 | 6 | axl = UcmPerfMonToolkit(user, password, ucm_ip, False) 7 | 8 | session_handle = axl.perfmonOpenSession() 9 | 10 | counters = [ 11 | "\\\\vnt-cm1b.cisco.com\\Cisco Locations LBM(BranchRemote->Hub_None)\\BandwidthAvailable", 12 | "\\\\vnt-cm1b.cisco.com\\Cisco Locations LBM(BranchRemote->Hub_None)\\BandwidthMaximum", 13 | "\\\\vnt-cm1b.cisco.com\\Cisco Locations LBM(BranchRemote->Hub_None)\\CallsInProgress", 14 | "\\\\vnt-cm1b.cisco.com\\Cisco Locations LBM(Hub_None)\\CallsInProgress", 15 | "\\\\vnt-cm1b.cisco.com\\Cisco SIP(ecats-rtp-dmz-cube2)\\CallsAttempted", 16 | "\\\\vnt-cm1b.cisco.com\\Cisco CallManager\\CallsCompleted" 17 | ] 18 | 19 | result = axl.perfmonAddCounter(session_handle=session_handle, counters=counters) 20 | 21 | if result is True: 22 | result = axl.perfmonCollectSessionData(session_handle=session_handle) 23 | else: 24 | result = "Error adding perfmon counter" 25 | 26 | print(result) 27 | 28 | result = axl.perfmonCloseSession(session_handle=session_handle) 29 | 30 | # Change this to the hostnames in your cluster 31 | hosts = ['vnt-cm1a.cisco.com', 'vnt-cm1b.cisco.com', 'vnt-cm1c.cisco.com'] 32 | 33 | 34 | counters = [] 35 | counter = 'Active' 36 | for host in hosts: 37 | lines = axl.perfmonListInstance(host=host, object_name='Cisco Lines') 38 | 39 | for line in lines: 40 | counter_string = f'\\\\{host}\\({line})\\{counter}' 41 | 42 | counters.append(counter_string) 43 | 44 | print(counters) 45 | -------------------------------------------------------------------------------- /samples/sample4_paws.py: -------------------------------------------------------------------------------- 1 | from axltoolkit import PawsToolkit 2 | from credentials import user, password, platform_user, platform_password 3 | 4 | ucm_ip = '172.18.106.58' 5 | 6 | paws = PawsToolkit(platform_user, platform_password, ucm_ip, 'VersionService', False) 7 | 8 | result = paws.get_active_version() 9 | 10 | print(result) 11 | 12 | -------------------------------------------------------------------------------- /samples/sample5_certs.py: -------------------------------------------------------------------------------- 1 | from axltoolkit import AxlToolkit 2 | from asn1crypto import pem, x509 3 | from pprint import pprint 4 | from credentials import user, password 5 | 6 | 7 | def subject_to_string(subject): 8 | subject_string = '' 9 | 10 | for value, key in subject.items(): 11 | if subject_string != '': 12 | subject_string += ', ' 13 | 14 | subject_string += value + ' = ' + str(key) 15 | 16 | return subject_string 17 | 18 | 19 | ucm_ip = '172.18.106.58' 20 | 21 | axl = AxlToolkit(username=user, password=password, server_ip=ucm_ip, tls_verify=False, version='12.5') 22 | 23 | query = 'select * from certificate' 24 | 25 | result = axl.run_sql_query(query) 26 | 27 | for row in result['rows']: 28 | 29 | cert = str.encode(row['certificate'], 'utf-8') 30 | 31 | if pem.detect(cert): 32 | cert_type, cert_headers, der_bytes = pem.unarmor(cert) 33 | 34 | cert_data = x509.Certificate.load(der_bytes)['tbs_certificate'].native 35 | 36 | not_before = cert_data['validity']['not_before'] 37 | not_after = cert_data['validity']['not_after'] 38 | 39 | print('"' + subject_to_string(cert_data['subject']) + '",' + str(not_after)) 40 | 41 | pprint(dict(cert_data.items())) --------------------------------------------------------------------------------