diff --git a/Packs/PAN-OS/Integrations/Panorama/Panorama.py b/Packs/PAN-OS/Integrations/Panorama/Panorama.py index 625ee7e91c3a..53b4b7d6fe37 100644 --- a/Packs/PAN-OS/Integrations/Panorama/Panorama.py +++ b/Packs/PAN-OS/Integrations/Panorama/Panorama.py @@ -169,7 +169,7 @@ 'questionable', 'real-estate', 'recreation-and-hobbies', - 'reference-and-research ', + 'reference-and-research', 'religion', 'search-engines', 'sex-education', @@ -283,7 +283,7 @@ def to_class(self, response, ignored_keys: set | None = None, illegal_chars: set def http_request(uri: str, method: str, headers: dict = {}, - body: dict = {}, params: dict = {}, files: dict | None = None, is_pcap: bool = False, + body: dict = {}, params: dict = {}, files: dict | None = None, is_file: bool = False, is_xml: bool = False) -> Any: """ Makes an API call with the given arguments @@ -303,7 +303,7 @@ def http_request(uri: str, method: str, headers: dict = {}, 'Request Failed. with status: ' + str(result.status_code) + '. Reason is: ' + str(result.reason)) # if pcap download - if is_pcap: + if is_file: return result if is_xml: return result.text @@ -832,6 +832,108 @@ def list_device_groups_names(): ) +def start_tsf_export(): + """ + Start export of tech support file (TSF) from PAN-OS: + https://docs.paloaltonetworks.com/pan-os/11-0/pan-os-panorama-api/pan-os-xml-api-request-types/export-files-api/export-technical-support-data + """ + params = { + 'type': 'export', + 'category': 'tech-support', + 'key': API_KEY + } + result = http_request( + URL, + 'GET', + params=params + ) + return result + + +def get_tsf_export_status(job_id: str): + """ + Get status of TSF export. + """ + params = { + 'type': 'export', + 'category': 'tech-support', + 'action': 'status', + 'job-id': job_id, + 'key': API_KEY + } + result = http_request( + URL, + 'GET', + params=params + ) + return result + + +def download_tsf(job_id: str): + """ + Download an exported TSF. + """ + params = { + 'type': 'export', + 'category': 'tech-support', + 'action': 'get', + 'job-id': job_id, + 'key': API_KEY + } + result = http_request( + URL, + 'GET', + params=params, + is_file=True + ) + return fileResult("tech_support_file.tar.gz", result.content) + + +@polling_function( + name=demisto.command(), + interval=arg_to_number(demisto.args().get('interval_in_seconds', 30)), + timeout=arg_to_number(demisto.args().get('timeout', 1200)), + requires_polling_arg=False +) +def export_tsf_command(args: dict): + """ + Export a TSF from PAN-OS. + """ + if job_id := args.get('job_id'): + job_status = dict_safe_get( + get_tsf_export_status(job_id), + ['response', 'result', 'job', 'status'] + ) + return PollResult( + response=download_tsf(job_id), + continue_to_poll=job_status != 'FIN', # continue polling if job isn't done + ) + else: # either no polling is required or this is the first run + result = start_tsf_export() + job_id = dict_safe_get(result, ['response', 'result', 'job'], '') + if job_id: + context_output = { + 'JobID': job_id, + 'Status': 'Pending' + } + continue_to_poll = True + else: # no job ID which is unexpected + raise DemistoException("Failed to start tech support file export.") + + return PollResult( + response=download_tsf(job_id), + continue_to_poll=continue_to_poll, + args_for_next_run={ + 'job_id': job_id, + 'interval_in_seconds': arg_to_number(args.get('interval_in_seconds')), + 'timeout': arg_to_number(args.get('timeout')) + }, + partial_result=CommandResults( + readable_output=f'Waiting for tech support file export with job ID {job_id} to finish...' + ) + ) + + def device_group_test(): """ Test module for the Device group specified @@ -887,23 +989,25 @@ def panorama_command(args: dict): Executes a command """ params = {} + for arg in args.keys(): params[arg] = args[arg] + + is_xml = argToBoolean(params.get("is_xml", "false")) params['key'] = API_KEY result = http_request( URL, 'POST', - body=params + body=params, + is_xml=is_xml ) - return_results({ - 'Type': entryTypes['note'], - 'ContentsFormat': formats['json'], - 'Contents': result, - 'ReadableContentsFormat': formats['text'], - 'HumanReadable': 'Command was executed successfully.', - }) + return_results(CommandResults( + outputs_prefix='Panorama.Command', + outputs=result, + readable_output='Command was executed successfully.' + )) @logger @@ -4136,7 +4240,7 @@ def panorama_list_pcaps_command(args: dict): elif serial_number: params['target'] = serial_number - result = http_request(URL, 'GET', params=params, is_pcap=True) + result = http_request(URL, 'GET', params=params, is_file=True) json_result = json.loads(xml2json(result.text))['response'] if json_result['@status'] != 'success': raise Exception('Request to get list of Pcaps Failed.\nStatus code: ' + str( @@ -4244,7 +4348,7 @@ def panorama_get_pcap_command(args: dict): if not file_name: file_name = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') - result = http_request(URL, 'GET', params=params, is_pcap=True) + result = http_request(URL, 'GET', params=params, is_file=True) # due to pcap file size limitation in the product. For more details, please see the documentation. if result.headers['Content-Type'] != 'application/octet-stream': @@ -14674,6 +14778,8 @@ def main(): # pragma: no cover return_results(pan_os_delete_tag_command(args)) elif command == 'pan-os-list-device-groups': return_results(list_device_groups_names()) + elif command == 'pan-os-export-tech-support-file': + return_results(export_tsf_command(args)) else: raise NotImplementedError(f'Command {command} is not implemented.') except Exception as err: diff --git a/Packs/PAN-OS/Integrations/Panorama/Panorama.yml b/Packs/PAN-OS/Integrations/Panorama/Panorama.yml index 6fde7055180b..af264dc94afa 100644 --- a/Packs/PAN-OS/Integrations/Panorama/Panorama.yml +++ b/Packs/PAN-OS/Integrations/Panorama/Panorama.yml @@ -3773,6 +3773,11 @@ script: name: device-group - description: Return raw XML. name: is_xml + auto: PREDEFINED + predefined: + - "false" + - "true" + defaultValue: "false" description: Runs any command supported in the API. name: pan-os - arguments: @@ -9341,7 +9346,20 @@ script: - contextPath: Panorama.DeviceGroupNames description: The list of device groups. type: string - dockerimage: demisto/pan-os-python:1.0.0.79261 + - name: pan-os-export-tech-support-file + arguments: + - name: interval_in_seconds + description: The polling interval (in seconds). + defaultValue: "30" + - name: timeout + description: The polling timeout (in seconds). + defaultValue: "1200" + - name: job_id + hidden: true + description: The job ID to use when polling. + description: Exports a tech support file (TSF). + polling: true + dockerimage: demisto/pan-os-python:1.0.0.80157 isfetch: true runonce: false script: '' diff --git a/Packs/PAN-OS/Integrations/Panorama/README.md b/Packs/PAN-OS/Integrations/Panorama/README.md index d8888b4e8723..9671d8767fa4 100644 --- a/Packs/PAN-OS/Integrations/Panorama/README.md +++ b/Packs/PAN-OS/Integrations/Panorama/README.md @@ -8985,3 +8985,30 @@ Returns a list of all device groups from Panorama. | **Path** | **Type** | **Description** | | --- | --- | --- | | Panorama.DeviceGroupNames | string | The list of device groups. | + +### pan-os-export-tech-support-file + +*** +Exports a tech support file (TSF). + +#### Base Command + +`pan-os-export-tech-support-file` + +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| interval_in_seconds | The polling interval (in seconds). Default is 30. | Optional | +| timeout | The polling timeout (in seconds). Default is 1200. | Optional | + +#### Context Output + +There is no context output for this command. + +#### Command example +```!pan-os-export-tech-support-file``` + +#### Human Readable Output + +>Waiting for tech support file export with job ID 101 to finish... diff --git a/Packs/PAN-OS/Integrations/Panorama/command_examples.txt b/Packs/PAN-OS/Integrations/Panorama/command_examples.txt index af745bb32569..38277e8b24f3 100644 --- a/Packs/PAN-OS/Integrations/Panorama/command_examples.txt +++ b/Packs/PAN-OS/Integrations/Panorama/command_examples.txt @@ -143,4 +143,5 @@ not_running_this!panorama-get-ssl-decryption-rules !pan-os-list-tag include_shared_tags=No !pan-os-create-tag name="testtag" comment="some comment" is_shared=false !pan-os-edit-tag name="testtag" new_name="newtesttag" comment="some comment" -!pan-os-delete-tag name="testtag" \ No newline at end of file +!pan-os-delete-tag name="testtag" +!pan-os-export-tech-support-file diff --git a/Packs/PAN-OS/ReleaseNotes/2_1_12.md b/Packs/PAN-OS/ReleaseNotes/2_1_12.md new file mode 100644 index 000000000000..0a17ff8127f0 --- /dev/null +++ b/Packs/PAN-OS/ReleaseNotes/2_1_12.md @@ -0,0 +1,8 @@ + +#### Integrations + +##### Palo Alto Networks PAN-OS + +- Added a new command **pan-os-export-tech-support-file** to export a tech support file (TSF) from PAN-OS. +- Fixed an issue where argument **is_xml** of command **pan-os** was not used. +- Updated the Docker image to: *demisto/pan-os-python:1.0.0.80157*. diff --git a/Packs/PAN-OS/pack_metadata.json b/Packs/PAN-OS/pack_metadata.json index cb6ad299ff8f..5f8c22943bf4 100644 --- a/Packs/PAN-OS/pack_metadata.json +++ b/Packs/PAN-OS/pack_metadata.json @@ -2,7 +2,7 @@ "name": "PAN-OS by Palo Alto Networks", "description": "Manage Palo Alto Networks Firewall and Panorama. Use this pack to manage Prisma Access through Panorama. For more information see Panorama documentation.", "support": "xsoar", - "currentVersion": "2.1.11", + "currentVersion": "2.1.12", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", @@ -37,4 +37,4 @@ "marketplacev2", "xpanse" ] -} \ No newline at end of file +}