catocli 3.0.22__py3-none-any.whl → 3.0.25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of catocli might be problematic. Click here for more details.
- catocli/Utils/clidriver.py +2 -2
- catocli/Utils/formatter_app_stats.py +29 -16
- catocli/Utils/formatter_app_stats_timeseries.py +15 -23
- catocli/Utils/formatter_utils.py +1 -0
- catocli/__init__.py +1 -1
- catocli/parsers/customParserApiClient.py +6 -2
- catocli/parsers/query_appStatsTimeSeries/README.md +2 -0
- catocli/parsers/utils/export_utils.py +6 -2
- catocli-3.0.25.dist-info/METADATA +181 -0
- {catocli-3.0.22.dist-info → catocli-3.0.25.dist-info}/RECORD +160 -160
- models/mutation.policy.antiMalwareFileHash.addRule.json +0 -20
- models/mutation.policy.antiMalwareFileHash.addSection.json +0 -103
- models/mutation.policy.antiMalwareFileHash.createPolicyRevision.json +0 -123
- models/mutation.policy.antiMalwareFileHash.discardPolicyRevision.json +0 -123
- models/mutation.policy.antiMalwareFileHash.moveRule.json +0 -20
- models/mutation.policy.antiMalwareFileHash.moveSection.json +0 -103
- models/mutation.policy.antiMalwareFileHash.publishPolicyRevision.json +0 -123
- models/mutation.policy.antiMalwareFileHash.removeRule.json +0 -20
- models/mutation.policy.antiMalwareFileHash.removeSection.json +0 -103
- models/mutation.policy.antiMalwareFileHash.updatePolicy.json +0 -123
- models/mutation.policy.antiMalwareFileHash.updateRule.json +0 -20
- models/mutation.policy.antiMalwareFileHash.updateSection.json +0 -103
- models/mutation.policy.appTenantRestriction.addRule.json +0 -20
- models/mutation.policy.appTenantRestriction.addSection.json +0 -103
- models/mutation.policy.appTenantRestriction.createPolicyRevision.json +0 -123
- models/mutation.policy.appTenantRestriction.discardPolicyRevision.json +0 -123
- models/mutation.policy.appTenantRestriction.moveRule.json +0 -20
- models/mutation.policy.appTenantRestriction.moveSection.json +0 -103
- models/mutation.policy.appTenantRestriction.publishPolicyRevision.json +0 -123
- models/mutation.policy.appTenantRestriction.removeRule.json +0 -20
- models/mutation.policy.appTenantRestriction.removeSection.json +0 -103
- models/mutation.policy.appTenantRestriction.updatePolicy.json +0 -123
- models/mutation.policy.appTenantRestriction.updateRule.json +0 -20
- models/mutation.policy.appTenantRestriction.updateSection.json +0 -103
- models/mutation.policy.applicationControl.addRule.json +0 -20
- models/mutation.policy.applicationControl.addSection.json +0 -103
- models/mutation.policy.applicationControl.createPolicyRevision.json +0 -123
- models/mutation.policy.applicationControl.discardPolicyRevision.json +0 -123
- models/mutation.policy.applicationControl.moveRule.json +0 -20
- models/mutation.policy.applicationControl.moveSection.json +0 -103
- models/mutation.policy.applicationControl.publishPolicyRevision.json +0 -123
- models/mutation.policy.applicationControl.removeRule.json +0 -20
- models/mutation.policy.applicationControl.removeSection.json +0 -103
- models/mutation.policy.applicationControl.updatePolicy.json +0 -123
- models/mutation.policy.applicationControl.updateRule.json +0 -20
- models/mutation.policy.applicationControl.updateSection.json +0 -103
- models/mutation.policy.dynamicIpAllocation.addRule.json +0 -20
- models/mutation.policy.dynamicIpAllocation.addSection.json +0 -103
- models/mutation.policy.dynamicIpAllocation.createPolicyRevision.json +0 -123
- models/mutation.policy.dynamicIpAllocation.discardPolicyRevision.json +0 -123
- models/mutation.policy.dynamicIpAllocation.moveRule.json +0 -20
- models/mutation.policy.dynamicIpAllocation.moveSection.json +0 -103
- models/mutation.policy.dynamicIpAllocation.publishPolicyRevision.json +0 -123
- models/mutation.policy.dynamicIpAllocation.removeRule.json +0 -20
- models/mutation.policy.dynamicIpAllocation.removeSection.json +0 -103
- models/mutation.policy.dynamicIpAllocation.updatePolicy.json +0 -123
- models/mutation.policy.dynamicIpAllocation.updateRule.json +0 -20
- models/mutation.policy.dynamicIpAllocation.updateSection.json +0 -103
- models/mutation.policy.internetFirewall.addRule.json +0 -20
- models/mutation.policy.internetFirewall.addSection.json +0 -103
- models/mutation.policy.internetFirewall.createPolicyRevision.json +0 -123
- models/mutation.policy.internetFirewall.discardPolicyRevision.json +0 -123
- models/mutation.policy.internetFirewall.moveRule.json +0 -20
- models/mutation.policy.internetFirewall.moveSection.json +0 -103
- models/mutation.policy.internetFirewall.publishPolicyRevision.json +0 -123
- models/mutation.policy.internetFirewall.removeRule.json +0 -20
- models/mutation.policy.internetFirewall.removeSection.json +0 -103
- models/mutation.policy.internetFirewall.updatePolicy.json +0 -123
- models/mutation.policy.internetFirewall.updateRule.json +0 -20
- models/mutation.policy.internetFirewall.updateSection.json +0 -103
- models/mutation.policy.remotePortFwd.addRule.json +0 -20
- models/mutation.policy.remotePortFwd.addSection.json +0 -103
- models/mutation.policy.remotePortFwd.createPolicyRevision.json +0 -123
- models/mutation.policy.remotePortFwd.discardPolicyRevision.json +0 -123
- models/mutation.policy.remotePortFwd.moveRule.json +0 -20
- models/mutation.policy.remotePortFwd.moveSection.json +0 -103
- models/mutation.policy.remotePortFwd.publishPolicyRevision.json +0 -123
- models/mutation.policy.remotePortFwd.removeRule.json +0 -20
- models/mutation.policy.remotePortFwd.removeSection.json +0 -103
- models/mutation.policy.remotePortFwd.updatePolicy.json +0 -123
- models/mutation.policy.remotePortFwd.updateRule.json +0 -20
- models/mutation.policy.remotePortFwd.updateSection.json +0 -103
- models/mutation.policy.socketLan.addRule.json +0 -40
- models/mutation.policy.socketLan.addSection.json +0 -103
- models/mutation.policy.socketLan.createPolicyRevision.json +0 -143
- models/mutation.policy.socketLan.discardPolicyRevision.json +0 -143
- models/mutation.policy.socketLan.moveRule.json +0 -40
- models/mutation.policy.socketLan.moveSection.json +0 -103
- models/mutation.policy.socketLan.publishPolicyRevision.json +0 -143
- models/mutation.policy.socketLan.removeRule.json +0 -40
- models/mutation.policy.socketLan.removeSection.json +0 -103
- models/mutation.policy.socketLan.updatePolicy.json +0 -143
- models/mutation.policy.socketLan.updateRule.json +0 -40
- models/mutation.policy.socketLan.updateSection.json +0 -103
- models/mutation.policy.terminalServer.addRule.json +0 -20
- models/mutation.policy.terminalServer.addSection.json +0 -103
- models/mutation.policy.terminalServer.createPolicyRevision.json +0 -123
- models/mutation.policy.terminalServer.discardPolicyRevision.json +0 -123
- models/mutation.policy.terminalServer.moveRule.json +0 -20
- models/mutation.policy.terminalServer.moveSection.json +0 -103
- models/mutation.policy.terminalServer.publishPolicyRevision.json +0 -123
- models/mutation.policy.terminalServer.removeRule.json +0 -20
- models/mutation.policy.terminalServer.removeSection.json +0 -103
- models/mutation.policy.terminalServer.updatePolicy.json +0 -123
- models/mutation.policy.terminalServer.updateRule.json +0 -20
- models/mutation.policy.terminalServer.updateSection.json +0 -103
- models/mutation.policy.tlsInspect.addRule.json +0 -20
- models/mutation.policy.tlsInspect.addSection.json +0 -103
- models/mutation.policy.tlsInspect.createPolicyRevision.json +0 -123
- models/mutation.policy.tlsInspect.discardPolicyRevision.json +0 -123
- models/mutation.policy.tlsInspect.moveRule.json +0 -20
- models/mutation.policy.tlsInspect.moveSection.json +0 -103
- models/mutation.policy.tlsInspect.publishPolicyRevision.json +0 -123
- models/mutation.policy.tlsInspect.removeRule.json +0 -20
- models/mutation.policy.tlsInspect.removeSection.json +0 -103
- models/mutation.policy.tlsInspect.updatePolicy.json +0 -123
- models/mutation.policy.tlsInspect.updateRule.json +0 -20
- models/mutation.policy.tlsInspect.updateSection.json +0 -103
- models/mutation.policy.wanFirewall.addRule.json +0 -20
- models/mutation.policy.wanFirewall.addSection.json +0 -103
- models/mutation.policy.wanFirewall.createPolicyRevision.json +0 -123
- models/mutation.policy.wanFirewall.discardPolicyRevision.json +0 -123
- models/mutation.policy.wanFirewall.moveRule.json +0 -20
- models/mutation.policy.wanFirewall.moveSection.json +0 -103
- models/mutation.policy.wanFirewall.publishPolicyRevision.json +0 -123
- models/mutation.policy.wanFirewall.removeRule.json +0 -20
- models/mutation.policy.wanFirewall.removeSection.json +0 -103
- models/mutation.policy.wanFirewall.updatePolicy.json +0 -123
- models/mutation.policy.wanFirewall.updateRule.json +0 -20
- models/mutation.policy.wanFirewall.updateSection.json +0 -103
- models/mutation.policy.wanNetwork.addRule.json +0 -20
- models/mutation.policy.wanNetwork.addSection.json +0 -103
- models/mutation.policy.wanNetwork.createPolicyRevision.json +0 -123
- models/mutation.policy.wanNetwork.discardPolicyRevision.json +0 -123
- models/mutation.policy.wanNetwork.moveRule.json +0 -20
- models/mutation.policy.wanNetwork.moveSection.json +0 -103
- models/mutation.policy.wanNetwork.publishPolicyRevision.json +0 -123
- models/mutation.policy.wanNetwork.removeRule.json +0 -20
- models/mutation.policy.wanNetwork.removeSection.json +0 -103
- models/mutation.policy.wanNetwork.updatePolicy.json +0 -123
- models/mutation.policy.wanNetwork.updateRule.json +0 -20
- models/mutation.policy.wanNetwork.updateSection.json +0 -103
- models/query.appStats.json +216 -0
- models/query.appStatsTimeSeries.json +144 -0
- models/query.policy.antiMalwareFileHash.policy.json +0 -123
- models/query.policy.appTenantRestriction.policy.json +0 -123
- models/query.policy.applicationControl.policy.json +0 -123
- models/query.policy.dynamicIpAllocation.policy.json +0 -123
- models/query.policy.internetFirewall.policy.json +0 -123
- models/query.policy.remotePortFwd.policy.json +0 -123
- models/query.policy.socketLan.policy.json +0 -143
- models/query.policy.terminalServer.policy.json +0 -123
- models/query.policy.tlsInspect.policy.json +0 -123
- models/query.policy.wanFirewall.policy.json +0 -123
- models/query.policy.wanNetwork.policy.json +0 -123
- schema/catolib.py +64 -56
- catocli-3.0.22.dist-info/METADATA +0 -124
- {catocli-3.0.22.dist-info → catocli-3.0.25.dist-info}/WHEEL +0 -0
- {catocli-3.0.22.dist-info → catocli-3.0.25.dist-info}/entry_points.txt +0 -0
- {catocli-3.0.22.dist-info → catocli-3.0.25.dist-info}/licenses/LICENSE +0 -0
- {catocli-3.0.22.dist-info → catocli-3.0.25.dist-info}/top_level.txt +0 -0
catocli/Utils/clidriver.py
CHANGED
|
@@ -54,8 +54,8 @@ from ..parsers.query_policy import query_policy_parse
|
|
|
54
54
|
from ..parsers.query_groups import query_groups_parse
|
|
55
55
|
from ..parsers.mutation_xdr import mutation_xdr_parse
|
|
56
56
|
from ..parsers.mutation_site import mutation_site_parse
|
|
57
|
-
from ..parsers.mutation_sites import mutation_sites_parse
|
|
58
57
|
from ..parsers.mutation_policy import mutation_policy_parse
|
|
58
|
+
from ..parsers.mutation_sites import mutation_sites_parse
|
|
59
59
|
from ..parsers.mutation_container import mutation_container_parse
|
|
60
60
|
from ..parsers.mutation_admin import mutation_admin_parse
|
|
61
61
|
from ..parsers.mutation_accountManagement import mutation_accountManagement_parse
|
|
@@ -189,8 +189,8 @@ query_policy_parser = query_policy_parse(query_subparsers)
|
|
|
189
189
|
query_groups_parser = query_groups_parse(query_subparsers)
|
|
190
190
|
mutation_xdr_parser = mutation_xdr_parse(mutation_subparsers)
|
|
191
191
|
mutation_site_parser = mutation_site_parse(mutation_subparsers)
|
|
192
|
-
mutation_sites_parser = mutation_sites_parse(mutation_subparsers)
|
|
193
192
|
mutation_policy_parser = mutation_policy_parse(mutation_subparsers)
|
|
193
|
+
mutation_sites_parser = mutation_sites_parse(mutation_subparsers)
|
|
194
194
|
mutation_container_parser = mutation_container_parse(mutation_subparsers)
|
|
195
195
|
mutation_admin_parser = mutation_admin_parse(mutation_subparsers)
|
|
196
196
|
mutation_accountManagement_parser = mutation_accountManagement_parse(mutation_subparsers)
|
|
@@ -14,12 +14,12 @@ from typing import Dict, List, Any
|
|
|
14
14
|
|
|
15
15
|
# Import shared utility functions
|
|
16
16
|
try:
|
|
17
|
-
from .formatter_utils import convert_bytes_to_mb
|
|
17
|
+
from .formatter_utils import convert_bytes_to_mb, is_bytes_measure
|
|
18
18
|
except ImportError:
|
|
19
19
|
try:
|
|
20
|
-
from catocli.Utils.formatter_utils import convert_bytes_to_mb
|
|
20
|
+
from catocli.Utils.formatter_utils import convert_bytes_to_mb, is_bytes_measure
|
|
21
21
|
except ImportError:
|
|
22
|
-
from formatter_utils import convert_bytes_to_mb
|
|
22
|
+
from formatter_utils import convert_bytes_to_mb, is_bytes_measure
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def format_app_stats(response_data: Dict[str, Any], output_format: str = 'json') -> str:
|
|
@@ -89,24 +89,27 @@ def _format_app_stats_to_json(response_data: Dict[str, Any]) -> str:
|
|
|
89
89
|
record_data = {}
|
|
90
90
|
|
|
91
91
|
for i, (field, value) in enumerate(fields_map.items()):
|
|
92
|
-
#
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
# Check if this is a bytes field using both unit type and field name
|
|
93
|
+
unit_type = record_unit_types[i] if i < len(record_unit_types) else "unknown"
|
|
94
|
+
is_bytes_field = (unit_type == 'bytes') or is_bytes_measure(field, unit_type)
|
|
95
|
+
|
|
96
|
+
if is_bytes_field:
|
|
97
|
+
try:
|
|
98
|
+
formatted_mb = convert_bytes_to_mb(value)
|
|
96
99
|
record_data[field] = {
|
|
97
100
|
"value": value,
|
|
98
101
|
"formatted_mb": formatted_mb,
|
|
99
102
|
"unit_type": "bytes"
|
|
100
103
|
}
|
|
101
|
-
|
|
104
|
+
except (ValueError, ZeroDivisionError):
|
|
102
105
|
record_data[field] = {
|
|
103
106
|
"value": value,
|
|
104
|
-
"unit_type": "
|
|
107
|
+
"unit_type": "bytes_err"
|
|
105
108
|
}
|
|
106
109
|
else:
|
|
107
110
|
record_data[field] = {
|
|
108
111
|
"value": value,
|
|
109
|
-
"unit_type":
|
|
112
|
+
"unit_type": unit_type
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
organized_data["appStats"]["records"].append(record_data)
|
|
@@ -155,7 +158,10 @@ def _format_app_stats_to_csv(response_data: Dict[str, Any]) -> str:
|
|
|
155
158
|
# Create headers with _mb suffix for bytes fields
|
|
156
159
|
headers = []
|
|
157
160
|
for i, field_name in enumerate(field_names):
|
|
158
|
-
if i < len(field_unit_types)
|
|
161
|
+
unit_type = field_unit_types[i] if i < len(field_unit_types) else "unknown"
|
|
162
|
+
is_bytes_field = (unit_type == 'bytes') or is_bytes_measure(field_name, unit_type)
|
|
163
|
+
|
|
164
|
+
if is_bytes_field:
|
|
159
165
|
headers.append(f'{field_name}_mb')
|
|
160
166
|
else:
|
|
161
167
|
headers.append(field_name)
|
|
@@ -172,12 +178,19 @@ def _format_app_stats_to_csv(response_data: Dict[str, Any]) -> str:
|
|
|
172
178
|
for i, field in enumerate(field_names):
|
|
173
179
|
value = fields_map.get(field, '')
|
|
174
180
|
|
|
175
|
-
#
|
|
176
|
-
if
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
# Check if this is a bytes field using both unit type and field name
|
|
182
|
+
unit_type = record_unit_types[i] if i < len(record_unit_types) else "unknown"
|
|
183
|
+
is_bytes_field = (unit_type == 'bytes') or is_bytes_measure(field, unit_type)
|
|
184
|
+
|
|
185
|
+
# Convert bytes to MB if the field is a bytes field
|
|
186
|
+
if is_bytes_field:
|
|
187
|
+
try:
|
|
188
|
+
formatted_value = convert_bytes_to_mb(value) if value != '' else ''
|
|
189
|
+
row.append(formatted_value)
|
|
190
|
+
except (ValueError, ZeroDivisionError):
|
|
191
|
+
row.append(str(value) if value != '' else '')
|
|
179
192
|
else:
|
|
180
|
-
row.append(value)
|
|
193
|
+
row.append(str(value) if value != '' else '')
|
|
181
194
|
|
|
182
195
|
writer.writerow(row)
|
|
183
196
|
|
|
@@ -16,12 +16,12 @@ from typing import Dict, List, Any, Tuple
|
|
|
16
16
|
|
|
17
17
|
# Import shared utility functions
|
|
18
18
|
try:
|
|
19
|
-
from .formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure
|
|
19
|
+
from .formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure, is_bytes_measure, convert_bytes_to_mb
|
|
20
20
|
except ImportError:
|
|
21
21
|
try:
|
|
22
|
-
from catocli.Utils.formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure
|
|
22
|
+
from catocli.Utils.formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure, is_bytes_measure, convert_bytes_to_mb
|
|
23
23
|
except ImportError:
|
|
24
|
-
from formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure
|
|
24
|
+
from formatter_utils import format_timestamp, parse_label_for_dimensions_and_measure, is_bytes_measure, convert_bytes_to_mb
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def format_app_stats_timeseries(response_data: Dict[str, Any], output_format: str = 'json') -> str:
|
|
@@ -170,20 +170,18 @@ def _format_app_stats_timeseries_to_json(response_data: Dict[str, Any]) -> str:
|
|
|
170
170
|
for timestamp, value in measure_data['data'].items():
|
|
171
171
|
timestamp_str = format_timestamp(timestamp)
|
|
172
172
|
|
|
173
|
-
if measure
|
|
173
|
+
if is_bytes_measure(measure):
|
|
174
174
|
try:
|
|
175
|
-
|
|
176
|
-
# mb_value = float(value) / 1048576
|
|
177
|
-
formatted_value = f"{mb_value:.3f}".rstrip('0').rstrip('.')
|
|
175
|
+
converted_value = convert_bytes_to_mb(value)
|
|
178
176
|
formatted_data[timestamp_str] = {
|
|
179
177
|
'value': value,
|
|
180
|
-
'formatted_mb':
|
|
181
|
-
'unit_type': '
|
|
178
|
+
'formatted_mb': converted_value,
|
|
179
|
+
'unit_type': 'bytes'
|
|
182
180
|
}
|
|
183
181
|
except (ValueError, ZeroDivisionError):
|
|
184
182
|
formatted_data[timestamp_str] = {
|
|
185
183
|
'value': value,
|
|
186
|
-
'unit_type': '
|
|
184
|
+
'unit_type': 'bytes_err'
|
|
187
185
|
}
|
|
188
186
|
else:
|
|
189
187
|
formatted_data[timestamp_str] = {
|
|
@@ -322,20 +320,14 @@ def _format_app_stats_timeseries_to_csv(response_data: Dict[str, Any]) -> str:
|
|
|
322
320
|
value = data.get(timestamp, '')
|
|
323
321
|
|
|
324
322
|
# Convert bytes measures to MB and add appropriate suffix
|
|
325
|
-
if measure
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
formatted_value = f"{mb_value:.3f}".rstrip('0').rstrip('.')
|
|
332
|
-
row_data[f'{measure}_mb'] = formatted_value
|
|
333
|
-
except (ValueError, ZeroDivisionError):
|
|
334
|
-
row_data[f'{measure}_mb'] = value
|
|
335
|
-
else:
|
|
336
|
-
row_data[f'{measure}_mb'] = value
|
|
323
|
+
if is_bytes_measure(measure):
|
|
324
|
+
try:
|
|
325
|
+
converted_value = convert_bytes_to_mb(value) if value != '' else ''
|
|
326
|
+
row_data[f'{measure}_mb'] = converted_value
|
|
327
|
+
except (ValueError, ZeroDivisionError):
|
|
328
|
+
row_data[f'{measure}_mb'] = str(value) if value != '' else ''
|
|
337
329
|
else:
|
|
338
|
-
row_data[measure] = value
|
|
330
|
+
row_data[measure] = str(value) if value != '' else ''
|
|
339
331
|
|
|
340
332
|
rows.append(row_data)
|
|
341
333
|
|
catocli/Utils/formatter_utils.py
CHANGED
|
@@ -118,6 +118,7 @@ def is_bytes_measure(measure: str, units: str = "") -> bool:
|
|
|
118
118
|
"""
|
|
119
119
|
bytes_measures = {
|
|
120
120
|
'downstream', 'upstream', 'traffic', 'bytes', 'bytesDownstream',
|
|
121
|
+
'bytes_upstream', 'bytes_downstream', 'bytes_total',
|
|
121
122
|
'bytesUpstream', 'bytesTotal', 'throughput_downstream', 'throughput_upstream'
|
|
122
123
|
}
|
|
123
124
|
|
catocli/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "3.0.
|
|
1
|
+
__version__ = "3.0.25"
|
|
2
2
|
__cato_host__ = "https://api.catonetworks.com/api/v1/graphql2"
|
|
@@ -350,7 +350,9 @@ def createRequest(args, configuration):
|
|
|
350
350
|
print(f"Saved CSV report to: {output_path}")
|
|
351
351
|
|
|
352
352
|
# Return structured response similar to export functions
|
|
353
|
-
|
|
353
|
+
# Normalize path separators for better cross-platform display
|
|
354
|
+
display_path = output_path.replace(os.sep, '/')
|
|
355
|
+
return [{"success": True, "output_file": display_path, "operation": operation_name}]
|
|
354
356
|
elif csv_output is None:
|
|
355
357
|
# Formatter returned None, indicating we should fall back to raw response
|
|
356
358
|
print("INFO: No processable data found, returning raw API response")
|
|
@@ -1844,7 +1846,9 @@ def createPrivateRequest(args, configuration):
|
|
|
1844
1846
|
print(f"Saved CSV report to: {output_path}")
|
|
1845
1847
|
|
|
1846
1848
|
# Return structured response similar to export functions
|
|
1847
|
-
|
|
1849
|
+
# Normalize path separators for better cross-platform display
|
|
1850
|
+
display_path = output_path.replace(os.sep, '/')
|
|
1851
|
+
return [{"success": True, "output_file": display_path, "operation": csv_operation, "private_command": private_command}]
|
|
1848
1852
|
else:
|
|
1849
1853
|
print("WARNING: CSV formatter returned empty result")
|
|
1850
1854
|
return response
|
|
@@ -58,6 +58,7 @@ catocli query appStatsTimeSeries '{
|
|
|
58
58
|
"fieldName": "application_name"
|
|
59
59
|
}
|
|
60
60
|
],
|
|
61
|
+
"perSecond": false,
|
|
61
62
|
"measure": [
|
|
62
63
|
{
|
|
63
64
|
"aggType": "sum",
|
|
@@ -99,6 +100,7 @@ catocli query appStatsTimeSeries '{
|
|
|
99
100
|
"fieldName": "user_name"
|
|
100
101
|
}
|
|
101
102
|
],
|
|
103
|
+
"perSecond": false,
|
|
102
104
|
"measure": [
|
|
103
105
|
{
|
|
104
106
|
"aggType": "sum",
|
|
@@ -183,9 +183,11 @@ def write_json_export(
|
|
|
183
183
|
if verbose:
|
|
184
184
|
print(f"Successfully exported data to JSON: {filepath}")
|
|
185
185
|
|
|
186
|
+
# Normalize path separators for better cross-platform display
|
|
187
|
+
display_path = filepath.replace(os.sep, '/')
|
|
186
188
|
return {
|
|
187
189
|
'success': True,
|
|
188
|
-
'output_file':
|
|
190
|
+
'output_file': display_path,
|
|
189
191
|
'format': 'json',
|
|
190
192
|
'record_count': len(data) if isinstance(data, (list, dict)) else 1
|
|
191
193
|
}
|
|
@@ -232,9 +234,11 @@ def write_csv_export(
|
|
|
232
234
|
if verbose:
|
|
233
235
|
print(f"Successfully exported {len(data)} records to CSV: {filepath}")
|
|
234
236
|
|
|
237
|
+
# Normalize path separators for better cross-platform display
|
|
238
|
+
display_path = filepath.replace(os.sep, '/')
|
|
235
239
|
return {
|
|
236
240
|
'success': True,
|
|
237
|
-
'output_file':
|
|
241
|
+
'output_file': display_path,
|
|
238
242
|
'format': 'csv',
|
|
239
243
|
'record_count': len(data)
|
|
240
244
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: catocli
|
|
3
|
+
Version: 3.0.25
|
|
4
|
+
Summary: Cato Networks cli wrapper for the GraphQL API.
|
|
5
|
+
Home-page: https://github.com/Cato-Networks/cato-cli
|
|
6
|
+
Author: Cato Networks
|
|
7
|
+
Author-email: [email protected]
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.6
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: urllib3
|
|
22
|
+
Requires-Dist: certifi
|
|
23
|
+
Requires-Dist: six
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
Dynamic: summary
|
|
34
|
+
|
|
35
|
+
# Cato Networks GraphQL API CLI
|
|
36
|
+
|
|
37
|
+
The package provides a simple to use CLI that reflects industry standards (such as the AWS cli), and enables customers to manage Cato Networks configurations and processes via the [Cato Networks GraphQL API](https://api.catonetworks.com/api/v1/graphql2) easily integrating into configurations management, orchestration or automation frameworks to support the DevOps model.
|
|
38
|
+
|
|
39
|
+
## Overview
|
|
40
|
+
|
|
41
|
+
CatoCLI is a command-line interface that provides access to the Cato Networks GraphQL API, enabling you to:
|
|
42
|
+
- Generate detailed network and security reports
|
|
43
|
+
- Analyze user and application activity
|
|
44
|
+
- Monitor network performance and events
|
|
45
|
+
- Export data in multiple formats (JSON, CSV)
|
|
46
|
+
- Automate reporting and monitoring tasks
|
|
47
|
+
|
|
48
|
+
## Prerequisites
|
|
49
|
+
|
|
50
|
+
- Python 3.6 or higher
|
|
51
|
+
- CatoCLI installed (`pip3 install catocli`)
|
|
52
|
+
- Valid Cato Networks API token and Account ID
|
|
53
|
+
- Proper authentication configuration (see [Authentication Setup](#authentication-setup))
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
`pip3 install catocli`
|
|
58
|
+
|
|
59
|
+
## Authentication Setup
|
|
60
|
+
|
|
61
|
+
Configure your CatoCLI profile before using any query operations:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Interactive configuration
|
|
65
|
+
catocli configure set
|
|
66
|
+
|
|
67
|
+
# Non-interactive configuration
|
|
68
|
+
catocli configure set --cato-token "your-api-token" --account-id "12345"
|
|
69
|
+
|
|
70
|
+
# List configured profiles
|
|
71
|
+
catocli configure list
|
|
72
|
+
|
|
73
|
+
# Show current profile
|
|
74
|
+
catocli configure show
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Documentation
|
|
78
|
+
|
|
79
|
+
For detailed information about profile management, see [PROFILES.md](PROFILES.md).
|
|
80
|
+
|
|
81
|
+
[CLICK HERE](https://support.catonetworks.com/hc/en-us/articles/4413280536081-Generating-API-Keys-for-the-Cato-API) to see how create an API key to authenticate.
|
|
82
|
+
|
|
83
|
+
## Running the CLI
|
|
84
|
+
catocli -h
|
|
85
|
+
catocli query -h
|
|
86
|
+
catocli query entityLookup -h
|
|
87
|
+
catocli query entityLookup '{"type":"country"}`
|
|
88
|
+
|
|
89
|
+
// Override the accountID value as a cli argument
|
|
90
|
+
catocli query entityLookup -accountID=12345 '{"type":"country"}`
|
|
91
|
+
|
|
92
|
+
## Check out run locally not as pip package
|
|
93
|
+
git clone git@github.com:Cato-Networks/cato-cli.git
|
|
94
|
+
cd cato-cli
|
|
95
|
+
python3 -m catocli -h
|
|
96
|
+
|
|
97
|
+
## Query Operations
|
|
98
|
+
|
|
99
|
+
### Core Analytics Queries
|
|
100
|
+
|
|
101
|
+
| Operation | Description | Guide |
|
|
102
|
+
|-----------|-------------|--------|
|
|
103
|
+
| [Account Metrics](./catocli_user_guide/account-metrics.md) | Network performance metrics by site, user, or interface | 📊 |
|
|
104
|
+
| [Application Statistics](./catocli_user_guide/app-stats.md) | User activity and application usage analysis | 📱 |
|
|
105
|
+
| [Application Statistics Time Series](./catocli_user_guide/app-stats-timeseries.md) | Traffic analysis over time with hourly/daily breakdowns | 📈 |
|
|
106
|
+
| [Events Time Series](./catocli_user_guide/events-timeseries.md) | Security events, connectivity, and threat analysis | 🔒 |
|
|
107
|
+
| [Socket Port Metrics](./catocli_user_guide/socket-port-metrics.md) | Socket interface performance and traffic analysis | 🔌 |
|
|
108
|
+
| [Socket Port Time Series](./catocli_user_guide/socket-port-timeseries.md) | Socket performance metrics over time | ⏱️ |
|
|
109
|
+
|
|
110
|
+
### Advanced Topics
|
|
111
|
+
|
|
112
|
+
- [Common Patterns & Best Practices](./catocli_user_guide/common-patterns.md) - Output formats, time frames, filtering patterns
|
|
113
|
+
- [Python Integration - Windows](./catocli_user_guide/python-integration-windows.md) - Windows-specific Python automation examples
|
|
114
|
+
- [Python Integration - Unix/Linux/macOS](./catocli_user_guide/python-integration-unix.md) - Unix-based Python integration guide
|
|
115
|
+
- [SIEM Integration Guide](./catocli_user_guide/siem-integration.md) - Real-time security event streaming to SIEM platforms
|
|
116
|
+
|
|
117
|
+
## Quick Start Examples
|
|
118
|
+
|
|
119
|
+
### Basic Network Health Check
|
|
120
|
+
```bash
|
|
121
|
+
# Get last hour account metrics
|
|
122
|
+
catocli query accountMetrics '{"timeFrame":"last.PT1H"}'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### User Activity Report (csv format)
|
|
126
|
+
```bash
|
|
127
|
+
# Export user activity for the last month to CSV
|
|
128
|
+
catocli query appStats '{
|
|
129
|
+
"dimension": [{"fieldName": "user_name"}],
|
|
130
|
+
"measure": [{"aggType": "sum", "fieldName": "flows_created"}],
|
|
131
|
+
"timeFrame": "last.P1M"
|
|
132
|
+
}' -f csv --csv-filename user_activity_report.csv
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Security Events Analysis
|
|
136
|
+
```bash
|
|
137
|
+
# Weekly security events breakdown
|
|
138
|
+
catocli query eventsTimeSeries '{
|
|
139
|
+
"buckets": 7,
|
|
140
|
+
"eventsFilter": [{"fieldName": "event_type", "operator": "is", "values": ["Security"]}],
|
|
141
|
+
"eventsMeasure": [{"aggType": "sum", "fieldName": "event_count"}],
|
|
142
|
+
"timeFrame": "last.P7D"
|
|
143
|
+
}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Output Formats
|
|
147
|
+
|
|
148
|
+
CatoCLI supports multiple output formats:
|
|
149
|
+
|
|
150
|
+
- **Enhanced JSON** (default): Formatted with granularity adjustments
|
|
151
|
+
- **Raw JSON**: Original API response with `-raw` flag
|
|
152
|
+
- **CSV**: Structured data export with `-f csv`
|
|
153
|
+
- **Custom CSV**: Named files with `--csv-filename` and `--append-timestamp`
|
|
154
|
+
|
|
155
|
+
## Time Frame Options
|
|
156
|
+
|
|
157
|
+
Common time frame patterns:
|
|
158
|
+
- `last.PT1H` - Last hour
|
|
159
|
+
- `last.P1D` - Last day
|
|
160
|
+
- `last.P7D` - Last week
|
|
161
|
+
- `last.P1M` - Last month
|
|
162
|
+
- `utc.2023-02-{28/00:00:00--28/23:59:59}` - Custom UTC range
|
|
163
|
+
|
|
164
|
+
## Getting Help
|
|
165
|
+
|
|
166
|
+
- Use `-h` or `--help` with any command for detailed usage
|
|
167
|
+
- Check the [Cato API Documentation](https://api.catonetworks.com/documentation/)
|
|
168
|
+
- Review individual operation guides linked above
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
This CLI is a Python 3 application and has been tested with Python 3.6 -> 3.8
|
|
172
|
+
|
|
173
|
+
## Requirements:
|
|
174
|
+
python 3.6 or higher
|
|
175
|
+
|
|
176
|
+
## Confirm your version of python if installed:
|
|
177
|
+
Open a terminal
|
|
178
|
+
Enter: python -V or python3 -V
|
|
179
|
+
|
|
180
|
+
## Installing the correct version for environment:
|
|
181
|
+
https://www.python.org/downloads/
|