Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PAN-OS] Add the capability to export a TSF #30717

Merged
merged 11 commits into from Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
132 changes: 119 additions & 13 deletions Packs/PAN-OS/Integrations/Panorama/Panorama.py
Expand Up @@ -169,7 +169,7 @@
'questionable',
'real-estate',
'recreation-and-hobbies',
'reference-and-research ',
'reference-and-research',
'religion',
'search-engines',
'sex-education',
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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':
Expand Down Expand Up @@ -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:
Expand Down
20 changes: 19 additions & 1 deletion Packs/PAN-OS/Integrations/Panorama/Panorama.yml
Expand Up @@ -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:
Expand Down Expand Up @@ -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: ''
Expand Down
27 changes: 27 additions & 0 deletions Packs/PAN-OS/Integrations/Panorama/README.md
Expand Up @@ -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...
3 changes: 2 additions & 1 deletion Packs/PAN-OS/Integrations/Panorama/command_examples.txt
Expand Up @@ -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"
!pan-os-delete-tag name="testtag"
!pan-os-export-tech-support-file
8 changes: 8 additions & 0 deletions 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*.
4 changes: 2 additions & 2 deletions Packs/PAN-OS/pack_metadata.json
Expand Up @@ -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": "",
Expand Down Expand Up @@ -37,4 +37,4 @@
"marketplacev2",
"xpanse"
]
}
}