PaloAltoFirewall:部分api
1 import datetime 2 import time 3 import logging 4 5 import requests 6 7 8 class PaloAltoFirewallAPI(object): 9 error_codes = { 10 '400': 'Bad request: A required parameter is missing, an illegal parameter value is used.', 11 '403': ('Forbidden: ' 12 'Authentication or authorization errors including invalid key or insufficient admin access rights. ' 13 'Learn how to Get Your API Key.' 14 ), 15 '1': 'Unknown command: The specific config or operational command is not recognized.', 16 '2': 'Unknown command: The specific config or operational command is not recognized.', 17 '3': 'Unknown command: The specific config or operational command is not recognized.', 18 '4': 'Unknown command: The specific config or operational command is not recognized.', 19 '5': 'Unknown command: The specific config or operational command is not recognized.', 20 '6': ('Bad Xpath: ' 21 'The xpath specified in one or more attributes of the command is invalid. ' 22 'Check the API browser for proper xpath values.' 23 ), 24 '7': ('Object not present: ' 25 'Object specified by the xpath is not present. ' 26 'For example, entry[@name="value"] where no object with name "value" is present.' 27 ), 28 '8': 'Object not unique: For commands that operate on a single object, the specified object is not unique.', 29 '10': ('Reference count not zero: ' 30 'Object cannot be deleted as there are other objects that refer to it. ' 31 'For example, address object still in use in policy.' 32 ), 33 '11': 'Internal error: Check with technical support when seeing these errors.', 34 '12': 'Invalid object: Xpath or element values provided are not complete.', 35 '14': ('Operation not possible: ' 36 'Operation is allowed but not possible in this case. ' 37 'For example, moving a rule up one position when it is already at the top.' 38 ), 39 '15': ('Operation denied: ' 40 'Operation is allowed. For example, Admin not allowed to delete own account, ' 41 'Running a command that is not allowed on a passive device.' 42 ), 43 '16': 'Unauthorized: The API role does not have access rights to run this query.', 44 '17': 'Invalid command: Invalid command or parameters.', 45 '18': 'Malformed command: The XML is malformed.', 46 '19': 'Success: Command completed successfully.', 47 '20': 'Success: Check with technical support when seeing these errors.', 48 '21': 'Internal error: ', 49 '22': 'Session timed out: The session for this query timed out.', 50 } 51 52 def __init__(self, ip, username='', password='', api_key=''): 53 """ 54 :param ip: 55 :param username: 56 :param password: 57 :param api_key: if exist, ignore username and password 58 """ 59 self.ip = ip 60 if api_key: 61 self.key = api_key 62 else: 63 key_url = 'https://{ip}/api/?type=keygen&user={username}&password={password}'.format( 64 ip=ip, username=username, password=password 65 ) 66 self.key = xmltodict.parse( 67 requests.post(key_url, verify=False).content 68 ).get('response').get('result').get('key') 69 self.url = 'https://{ip}/api'.format(ip=ip) 70 71 def _request(self, params, method='post'): 72 """ 73 :param params: 74 { 75 'type': 'config', 76 'action': 'get', 77 'key': self.key, 78 'xpath': xpath, 79 } 80 :return: 81 { 82 'code': '19', 83 'msg': 'Success: Command completed successfully.', 84 'success': True, 85 'result': Dict, 86 } 87 """ 88 response = xmltodict.parse( 89 requests.request( 90 method=method, url=self.url, params=params, verify=False 91 ).text 92 ).get('response') 93 code = response.get('@code') 94 if code in ('19', '20', None): 95 msg = response.get('msg') 96 else: 97 msg = self.error_codes.get(code) 98 result = { 99 'code': code, 100 'msg': msg, 101 'success': False, 102 'result': None, 103 } 104 if response.get('@status') == 'success': 105 result['success'] = True 106 result['result'] = response.get('result') 107 else: 108 result['success'] = False 109 return result 110 111 def get_report(self, report_name, period='last-hour', top_n=5, wait_for_result=30): 112 """ 113 :param report_name: 114 acc-summary 115 custom-dynamic-report 116 top-app-summary 117 top-application-categories-summary 118 top-application-risk-summary 119 top-application-subcategories-summary 120 top-application-tech-summary 121 top-applications-summary 122 top-applications-trsum 123 top-attacker-countries-summary 124 top-attackers-summary 125 top-attacks-acc 126 top-blocked-url-categories-summary 127 top-blocked-url-summary 128 top-blocked-url-user-behavior-summary 129 top-data-dst-countries-summary 130 top-data-dst-summary 131 top-data-egress-zones-summary 132 top-data-filename-summary 133 top-data-filetype-summary 134 top-data-ingress-zones-summary 135 top-data-src-countries-summary 136 top-data-src-summary 137 top-data-type-summary 138 top-dst-countries-summary 139 top-dst-summary 140 top-egress-zones-summary 141 top-hip-objects-details 142 top-hip-objects-summary 143 top-hip-profiles-details 144 top-hip-profiles-summary 145 top-hip-report-links 146 top-hr-applications-summary 147 top-ingress-zones-summary 148 top-rule-summary 149 top-spyware-phonehome-summary 150 top-spyware-threats-summary 151 top-src-countries-summary 152 top-src-summary 153 top-threat-egress-zones-summary 154 top-threat-ingress-zones-summary 155 top-threats-type-summary 156 top-url-categories-summary 157 top-url-summary 158 top-url-user-behavior-summary 159 top-victim-countries-summary 160 top-victims-summary 161 top-viruses-summary 162 top-vulnerabilities-summary 163 :param period: 164 last-60-seconds 165 last-15-minutes 166 last-hour 167 last-12-hrs 168 last-7-days, 169 last-calendar-day 170 :param top_n: 171 :param wait_for_result: Return report-result within 30 seconds, otherwise return job id 172 :return: Dict 173 """ 174 params = { 175 'reportname': report_name, 176 'type': 'report', 177 'reporttype': 'dynamic', 178 'period': period, 179 'topn': top_n, 180 'key': self.key, 181 } 182 183 res = self._request(params=params) 184 result = { 185 'job_id': None, 186 'report': {}, 187 'success': True 188 } 189 data = res.get('result') or {} 190 if not res.get('success'): 191 result['success'] = False 192 result['msg'] = data.get('msg') 193 return result 194 else: 195 result['job_id'] = job_id = data.get('job') 196 if wait_for_result: 197 report = {} 198 while not report and wait_for_result > 0: 199 time.sleep(1) 200 wait_for_result -= 1 201 res = self.get_job(job_id, job_type='report') 202 if res.get('success'): 203 report = res.get('result') 204 result['success'] = True 205 else: 206 result['success'] = False 207 208 result.update(**report) 209 if not result['report']: 210 result['report'] = {} 211 return result 212 213 def get_job(self, job_id, job_type='report'): 214 """ 215 :param job_id: def get_report: 216 :param job_type: default = report 217 :return: 218 """ 219 params = { 220 'action': 'get', 221 'type': job_type, 222 'job-id': job_id, 223 'key': self.key, 224 } 225 return self._request(params=params) 226 227 def _sp_config(self, _type, rel_path, entry_name='', action='get', **kwargs): 228 """ 229 :param _type: 230 :param rel_path: 231 :param entry_name: 232 :param action: 233 :param kwargs: extra params 234 :return: 235 """ 236 limit_types = ('deviceconfig', 'network', 'platform', 'vsys') 237 if _type not in limit_types: 238 raise Exception('"_type" must be one of {limit_types}'.format(limit_types=limit_types)) 239 rel_path = rel_path.strip('/') 240 if entry_name and not isinstance(entry_name, (list, tuple)): 241 entry_name = [entry_name] 242 if _type in ('deviceconfig', 'network', 'platform'): 243 rel_path = os.path.join(_type, rel_path).replace('\\', '/') 244 if entry_name: 245 entries = ' or '.join(["@name='{name}'".format(name=name) for name in entry_name]) 246 xpath = ( 247 "/config/devices/entry[@name='localhost.localdomain']/{rel_path}/entry[{entries}]" 248 ).format(rel_path=rel_path, entries=entries) 249 else: 250 xpath = "/config/devices/entry[@name='localhost.localdomain']/{rel_path}".format(rel_path=rel_path) 251 else: # 'vsys': 252 if entry_name: 253 entries = ' or '.join(["@name='{name}'".format(name=name) for name in entry_name]) 254 xpath = ( 255 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}/" 256 "entry[{entries}]" 257 ).format(rel_path=rel_path, entries=entries) 258 else: 259 xpath = ( 260 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}" 261 ).format(rel_path=rel_path) 262 params = { 263 'type': 'config', 264 'action': action, 265 'key': self.key, 266 'xpath': xpath, 267 } 268 params.update(**kwargs) 269 return self._request(params=params) 270 271 def set_config(self, _type, rel_path, element='', action='set'): 272 """ 273 :param _type: 274 deviceconfig 275 network 276 platform 277 vsys 278 :param rel_path: 'interface/ethernet' 279 :param element: <xml> 280 <entry name="ethernet1/1"> 281 <layer3> 282 <lldp> 283 <profile>lenovo_lldp</profile> 284 <enable>yes</enable> 285 </lldp> 286 </layer3> 287 </entry> 288 :param action: Should be one of : [ 289 clone, complete, delete, edit, get, multi-clone, multi-move, override, rename, set, show 290 ] 291 :return: { 292 'code': '19', 293 'msg': 'Success: Command completed successfully.', 294 'success': True, 295 'result': Dict, 296 } 297 Usage: 298 >>> pa = PaloAltoFirewallAPI(api_key='x') 299 >>> element = 300 <entry name="ethernet1/1"> 301 <layer3><lldp><enable>yes</enable><profile>lenovo_lldp</profile></lldp></layer3> 302 </entry> 303 >>> pa.set_config(_type='network', rel_path='interface/ethernet', element=element) 304 >>> 305 >>> element = 306 <entry name="SDWAN-Vedge-02-Public-IP"> 307 <ip-netmask>192.168.1.2</ip-netmask> 308 <description>SDWAN-Vedge-02-Public-IP</description> 309 </entry> 310 >>> pa.set_config(_type='vsys', rel_path='address', element=element) 311 >>> 312 >>> element = 313 <entry name="SDWAN-Vedge-02-NAT"> 314 <to> 315 <member>Internet_zone</member> 316 </to> 317 <from> 318 <member>Office_zone</member> 319 </from> 320 <source> 321 <member>SDWAN-Vedge-02-Int-Port-IP</member> 322 </source> 323 <destination> 324 <member>any</member> 325 </destination> 326 <service>any</service> 327 <description>SDWAN-Ser2-vEdge</description> 328 <to-interface>ethernet1/2</to-interface> 329 <disabled>no</disabled> 330 <source-translation> 331 <static-ip> 332 <bi-directional>yes</bi-directional> 333 <translated-address>SDWAN-Vedge-02-Public-IP</translated-address> 334 </static-ip> 335 </source-translation> 336 </entry> 337 >>> pa.set_config(_type='vsys', rel_path='rulebase/nat/rules', element=element) 338 """ 339 limit_types = ('deviceconfig', 'network', 'platform', 'vsys') 340 if _type not in limit_types: 341 raise Exception('"_type" must be one of {limit_types}'.format(limit_types=limit_types)) 342 rel_path = rel_path.strip('/') 343 344 if _type in ('deviceconfig', 'network', 'platform'): 345 rel_path = os.path.join(_type, rel_path).replace('\\', '/') 346 xpath = "/config/devices/entry[@name='localhost.localdomain']/{rel_path}".format( 347 rel_path=rel_path 348 ) 349 else: # 'vsys': 350 xpath = ( 351 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}" 352 ).format(rel_path=rel_path) 353 params = { 354 'type': 'config', 355 'action': action, 356 'key': self.key, 357 'xpath': xpath, 358 'element': element 359 } 360 return self._request(params=params) 361 362 def get_config(self, _type, rel_path, entry_name=''): 363 """ 364 :param _type: 365 deviceconfig 366 network 367 platform 368 vsys 369 :param rel_path: 'interface/ethernet' 370 :param entry_name: 'ethernet1/1' 371 :return: { 372 'code': '19', 373 'msg': 'Success: Command completed successfully.', 374 'success': True, 375 'result': Dict, 376 } 377 Usage: 378 >>> pa = PaloAltoFirewallAPI(api_key='x') 379 >>> pa.get_config(_type='network', rel_path='interface/ethernet', entry_name='ethernet name') 380 >>> pa.get_config(_type='vsys', rel_path='address', entry_name='address name') 381 >>> pa.get_config(_type='vsys', rel_path='rulebase/nat/rules', entry_name='rule name') 382 """ 383 return self._sp_config(_type=_type, rel_path=rel_path, entry_name=entry_name, action='get') 384 385 def get_entry_members(self, _type, rel_path, entry_name, member_names=None): 386 """ 387 :param _type: 388 deviceconfig 389 network 390 platform 391 vsys 392 :param rel_path: 'interface/ethernet' 393 :param entry_name: 'CT-Address-Group' 394 :param member_names: ['ethernet1/1', ] 395 :return: { 396 'code': '19', 397 'msg': 'Success: Command completed successfully.', 398 'success': True, 399 'result': Dict, 400 } 401 Usage: 402 >>> pa = PaloAltoFirewallAPI(api_key='x') 403 >>> pa.get_entry_members(_type='vsys', rel_path='address-group', entry_name='CT-Address-Group', member_names=['CT-Address-0060', 'CT-Address-0061']) 404 """ 405 limit_types = ('deviceconfig', 'network', 'platform', 'vsys') 406 if _type not in limit_types: 407 raise Exception('"_type" must be one of {limit_types}'.format(limit_types=limit_types)) 408 rel_path = rel_path.strip('/') 409 410 if _type in ('deviceconfig', 'network', 'platform'): 411 rel_path = os.path.join(_type, rel_path).replace('\\', '/') 412 if member_names: 413 members = ' or '.join(["text()='{name}'".format(name=name) for name in member_names]) 414 xpath = ( 415 "/config/devices/entry[@name='localhost.localdomain']/{rel_path}/entry[@name='{entry_name}']/" 416 "static/member[{members}]" 417 ).format(rel_path=rel_path, entry_name=entry_name, members=members) 418 else: 419 xpath = ( 420 "/config/devices/entry[@name='localhost.localdomain']/{rel_path}/entry[@name='{entry_name}']" 421 ).format(rel_path=rel_path, entry_name=entry_name) 422 else: # 'vsys': 423 if member_names: 424 members = ' or '.join(["text()='{name}'".format(name=name) for name in member_names]) 425 xpath = ( 426 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}/" 427 "entry[@name='{entry_name}']/static/member[{members}]" 428 ).format(rel_path=rel_path, entry_name=entry_name, members=members) 429 else: 430 xpath = ( 431 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}/" 432 "entry[@name='{entry_name}']/static/member" 433 ).format(rel_path=rel_path, entry_name=entry_name) 434 params = { 435 'type': 'config', 436 'action': 'get', 437 'key': self.key, 438 'xpath': xpath, 439 } 440 return self._request(params=params) 441 442 def delete_entry_members(self, _type, rel_path, entry_name, member_names=None): 443 """ 444 :param _type: 445 deviceconfig 446 network 447 platform 448 vsys 449 :param rel_path: 'address-group' 450 :param entry_name: 'CT-Address-Group' 451 :param member_names: ['ethernet1/1', ] 452 :return: { 453 'code': '19', 454 'msg': 'Success: Command completed successfully.', 455 'success': True, 456 'result': Dict, 457 } 458 Usage: 459 >>> pa = PaloAltoFirewallAPI(api_key='x') 460 >>> pa.get_config( 461 >>> _type='vsys', rel_path='address-group', entry_name='CT-Address-Group', 462 >>> member_names=['CT-Address-0060', 'CT-Address-0061'] 463 >>> ) 464 >>> 465 """ 466 limit_types = ('deviceconfig', 'network', 'platform', 'vsys') 467 if _type not in limit_types: 468 raise Exception('"_type" must be one of {limit_types}'.format(limit_types=limit_types)) 469 rel_path = rel_path.strip('/') 470 471 if _type in ('deviceconfig', 'network', 'platform'): 472 rel_path = os.path.join(_type, rel_path).replace('\\', '/') 473 if member_names: 474 members = ' or '.join(["text()='{name}'".format(name=name) for name in member_names]) 475 xpath = ( 476 "/config/devices/entry[@name='localhost.localdomain']/{rel_path}/entry[@name='{entry_name}']/" 477 "static/member[{members}]" 478 ).format(rel_path=rel_path, entry_name=entry_name, members=members) 479 else: 480 xpath = ( 481 "/config/devices/entry[@name='localhost.localdomain']/{rel_path}/entry[@name='{entry_name}']" 482 ).format(rel_path=rel_path, entry_name=entry_name) 483 else: # 'vsys': 484 if member_names: 485 members = ' or '.join(["text()='{name}'".format(name=name) for name in member_names]) 486 xpath = ( 487 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}/" 488 "entry[@name='{entry_name}']/static/member[{members}]" 489 ).format(rel_path=rel_path, entry_name=entry_name, members=members) 490 else: 491 xpath = ( 492 "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/{rel_path}/" 493 "entry[@name='{entry_name}']/static/member" 494 ).format(rel_path=rel_path, entry_name=entry_name) 495 params = { 496 'type': 'config', 497 'action': 'delete', 498 'key': self.key, 499 'xpath': xpath, 500 } 501 return self._request(params=params) 502 503 def delete_config(self, _type, rel_path, entry_name): 504 """ 505 :param _type: 506 deviceconfig 507 network 508 platform 509 vsys 510 :param rel_path: 'interface/ethernet' 511 :param entry_name: 'ethernet1/1' or ['ethernet1/1', 'ethernet1/2'] 512 :return: { 513 'code': '19', 514 'msg': 'Success: Command completed successfully.', 515 'success': True, 516 'result': Dict, 517 } 518 # Usage: 519 >>> pa = PaloAltoFirewallAPI(api_key='x') 520 >>> pa.delete_config(_type='network', rel_path='interface/ethernet', entry_name='ethernet name') 521 >>> pa.delete_config(_type='vsys', rel_path='service', entry_name='service name') 522 >>> pa.delete_config(_type='vsys', rel_path='address', entry_name='address name') 523 >>> pa.delete_config(_type='vsys', rel_path='address-group', entry_name='address-group name') 524 >>> pa.delete_config(_type='vsys', rel_path='rulebase/nat/rules', entry_name='rule name') 525 """ 526 return self._sp_config(_type=_type, rel_path=rel_path, entry_name=entry_name, action='delete') 527 528 def move_config_position(self, rel_path, entry_name, where='top', _type='vsys'): 529 """ 530 :param rel_path: 531 :param entry_name: 532 :param where: 533 :param _type: 534 :return: 535 """ 536 return self._sp_config( 537 _type=_type, rel_path=rel_path, entry_name=entry_name, action='move', where=where 538 ) 539 540 def is_backup(self): 541 cmd = '<show><high-availability><state></state></high-availability></show>' 542 params = { 543 'type': 'op', 544 'key': self.key, 545 'cmd': cmd, 546 } 547 try: 548 res = requests.post(url=self.url, params=params, verify=False) 549 state = xmltodict.parse(res.content).get('response').get('result').get('group').get('local-info').get( 550 'state') 551 if state == 'passive': 552 return True 553 except: 554 pass 555 return False 556 557 def revert(self, command='<revert><config></config></revert>'): 558 """ 559 :param command: <revert><config></config></revert> 560 :return: 561 """ 562 return self.operational_commands(command=command) 563 564 def commit(self, command='<commit></commit>'): 565 """ 566 :param command: '<commit></commit>' 567 :return: str 568 """ 569 params = { 570 'type': 'commit', 571 'key': self.key, 572 'cmd': command 573 } 574 return self._request(params=params) 575 576 def acquire_lock(self, comment): 577 command = ''' 578 <request> 579 <config-lock> 580 <add><comment>{comment}</comment></add> 581 </config-lock> 582 </request> 583 '''.format(comment=comment) 584 return self.operational_commands(command) 585 586 def release_lock(self, command='<request><config-lock><remove></remove></config-lock></request>'): 587 return self.operational_commands(command) 588 589 def operational_commands(self, command): 590 """ 591 :param command: '<show><high-availability><state></state></high-availability></show>' 592 :return: 593 """ 594 params = { 595 'type': 'op', 596 'key': self.key, 597 'cmd': command, 598 } 599 return self._request(params=params) 600 601 def export_configuration(self): 602 """ 603 :return: xml 604 """ 605 params = { 606 'type': 'export', 607 'key': self.key, 608 'category': 'configuration' 609 } 610 result = { 611 'success': False, 612 } 613 try: 614 xml = requests.get(url=self.url, params=params, verify=False).text 615 result.update(success=True, result=xml) 616 except Exception as e: 617 result.update(success=False, error=str(e)) 618 return result 619 620 def show_job_status(self, job_id): 621 command = '<show><jobs><id>{}</id></jobs></show>'.format(job_id) 622 return self.operational_commands(command) 623 624 625 def now_time(): 626 return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') 627 628 629 class PAChangeBase(object): 630 def __init__(self, ip, api_key, lock_comment='PYTHON automation deployment lock(object updates)'): 631 self.ip = ip 632 self.api_key = api_key 633 self._pa = PaloAltoFirewallAPI(ip, api_key=api_key) 634 self.lock_comment = lock_comment 635 636 def has_pending_changes(self): 637 command = '<check><pending-changes></pending-changes></check>' 638 res = self._pa.operational_commands(command) 639 if not res.get('success') or res.get('result') != 'no': 640 return True 641 return False 642 643 def has_lock(self): 644 command = '<show><config-locks></config-locks></show>' 645 res = self._pa.operational_commands(command) 646 if not res.get('success'): 647 return False 648 result = res.get('result') 649 locks = result.get('config-locks') 650 if not locks: 651 return False 652 653 # 如果锁是本程序的锁, 则释放锁 654 if locks.get('entry').get('comment') == self.lock_comment: 655 self.release_lock() 656 return False 657 else: 658 return True 659 660 def acquire_lock(self): 661 res = self._pa.acquire_lock(comment=self.lock_comment) 662 if not res.get('success'): 663 return False 664 if 'Successfully acquired lock' in res.get('result'): 665 return True 666 return False 667 668 def release_lock(self): 669 res = self._pa.release_lock() 670 if not res.get('success'): 671 return False 672 if 'Config lock released' in res.get('result'): 673 return True 674 return False 675 676 def commit(self, callback_res): 677 res = self._pa.commit() 678 if not res.get('success'): 679 return False 680 job_id = res.get('result').get('job') 681 while True: 682 res = self._pa.show_job_status(job_id=job_id) 683 if not res.get('success'): 684 return False 685 job = res.get('result').get('job') 686 job_details = job.get('details') 687 if not job_details: 688 time.sleep(1) 689 continue 690 if 'Commit failed' in str(job_details): 691 for line in job_details.get('line'): 692 callback_res['msg'].append('{} {}'.format(now_time(), line)) 693 return False 694 return True 695 return True 696 697 def revert(self): 698 res = self._pa.revert() 699 if not res.get('success'): 700 return False 701 return True 702 703 def do_change(self, **kwargs): 704 raise Exception('Must be overwrite this function') 705 706 def run(self, **kwargs): 707 result = {'success': True, 'ip': self.ip, 'msg': []} 708 try: 709 # 如果有pending changes, 则直接返回 710 if self.has_pending_changes(): 711 result['success'] = False 712 result['msg'].append('{} There are pending changes'.format(now_time())) 713 return result 714 result['msg'].append('{} There are no pending changes'.format(now_time())) 715 716 if self.has_lock(): 717 result['success'] = False 718 result['msg'].append('{} Config lock was not released'.format(now_time())) 719 return result 720 721 if not self.acquire_lock(): 722 result['success'] = False 723 result['msg'].append('{} Acquire config lock failed'.format(now_time())) 724 return result 725 result['msg'].append('{} Acquire config lock successful'.format(now_time())) 726 try: 727 self.do_change(result=result, **kwargs) 728 if self.commit(result): 729 result['msg'].append('{} Commit successful'.format(now_time())) 730 else: 731 result['success'] = False 732 result['msg'].append('{} Commit failed'.format(now_time())) 733 except Exception as e: 734 logging.error(e) 735 result['success'] = False 736 result['msg'].append('{} {}'.format(now_time(), str(e))) 737 finally: 738 self.release_lock() 739 result['msg'].append('{} Release config lock successful'.format(now_time())) 740 except Exception as e: 741 result['success'] = False 742 result['msg'].append('{} {}'.format(now_time(), str(e))) 743 return result 744 745 746 def test(): 747 # get top app 748 result = { 749 'top_app_src': [], 750 'top_app_dst': [], 751 } 752 try: 753 ip = '1.1.1.1' 754 username = 'username' 755 password = 'password' 756 api_key = 'api_key' 757 # PaloAltoFirewallAPI(ip=ip, username=username, password=password) 758 pa = PaloAltoFirewallAPI(ip=ip, api_key='key') 759 for direction in ['src', 'dst']: 760 res = pa.get_report( 761 report_name='top-%s-summary' % direction, period='last-hour', top_n=10, 762 wait_for_result=timeout 763 ) 764 if res.get('success'): 765 entries = res.get('report', {}).get('entry', []) 766 if not entries: 767 continue 768 temp = [] 769 user_key = '%ssuer' % direction 770 for entry in entries: 771 user = entry.get(user_key) or 'unknown' 772 volume_bytes = int(entry.get('bytes')) 773 volume_mb = volume_bytes / 1000.0 / 1000.0 774 temp.append({ 775 'Name': user, 776 'volume': '%2s MB' % round(volume_mb, 2), 777 'traffic': '%2s %%' % round( 778 volume_mb * 8 / link_bandwidth / (60 * 60 * 24) * 100, 2 779 ) 780 }) 781 result['top_app_%s' % direction] = temp 782 except Exception as e: 783 logging.error(e) 784 785 786 def test2(): 787 class PBRObjectChanger(PAChangeBase): 788 def do_change(self, **kwargs): 789 # Business code 790 pass 791 792 793 if __name__ == '__main__': 794 test() 795 test2()