├── 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()))
--------------------------------------------------------------------------------