catocli 2.0.1__py3-none-any.whl → 2.0.3__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.

Files changed (108) hide show
  1. catocli/Utils/clidriver.py +41 -6
  2. catocli/__init__.py +1 -1
  3. catocli/parsers/custom/__init__.py +7 -5
  4. catocli/parsers/custom/customLib.py +490 -1
  5. catocli/parsers/custom/export_rules/__init__.py +5 -1
  6. catocli/parsers/custom/export_rules/export_rules.py +32 -183
  7. catocli/parsers/custom/export_sites/__init__.py +20 -0
  8. catocli/parsers/custom/export_sites/export_sites.py +365 -0
  9. catocli/parsers/custom/import_rules_to_tf/__init__.py +3 -3
  10. catocli/parsers/custom/import_rules_to_tf/import_rules_to_tf.py +20 -146
  11. catocli/parsers/custom/import_sites_to_tf/__init__.py +45 -0
  12. catocli/parsers/custom/import_sites_to_tf/import_sites_to_tf.py +891 -0
  13. catocli/parsers/mutation_accountManagement/__init__.py +18 -21
  14. catocli/parsers/mutation_admin/__init__.py +18 -21
  15. catocli/parsers/mutation_container/__init__.py +6 -7
  16. catocli/parsers/mutation_hardware/__init__.py +6 -7
  17. catocli/parsers/mutation_policy/__init__.py +666 -588
  18. catocli/parsers/mutation_policy_terminalServer/README.md +7 -0
  19. catocli/parsers/mutation_policy_terminalServer_addRule/README.md +18 -0
  20. catocli/parsers/mutation_policy_terminalServer_addSection/README.md +18 -0
  21. catocli/parsers/mutation_policy_terminalServer_createPolicyRevision/README.md +18 -0
  22. catocli/parsers/mutation_policy_terminalServer_discardPolicyRevision/README.md +18 -0
  23. catocli/parsers/mutation_policy_terminalServer_moveRule/README.md +18 -0
  24. catocli/parsers/mutation_policy_terminalServer_moveSection/README.md +18 -0
  25. catocli/parsers/mutation_policy_terminalServer_publishPolicyRevision/README.md +18 -0
  26. catocli/parsers/mutation_policy_terminalServer_removeRule/README.md +18 -0
  27. catocli/parsers/mutation_policy_terminalServer_removeSection/README.md +18 -0
  28. catocli/parsers/mutation_policy_terminalServer_updatePolicy/README.md +18 -0
  29. catocli/parsers/mutation_policy_terminalServer_updateRule/README.md +18 -0
  30. catocli/parsers/mutation_policy_terminalServer_updateSection/README.md +18 -0
  31. catocli/parsers/mutation_sandbox/__init__.py +12 -14
  32. catocli/parsers/mutation_site/__init__.py +189 -175
  33. catocli/parsers/mutation_site_addSocketAddOnCard/README.md +17 -0
  34. catocli/parsers/mutation_site_removeSocketAddOnCard/README.md +17 -0
  35. catocli/parsers/mutation_site_startSiteUpgrade/README.md +17 -0
  36. catocli/parsers/mutation_sites/__init__.py +189 -175
  37. catocli/parsers/mutation_sites_addSocketAddOnCard/README.md +17 -0
  38. catocli/parsers/mutation_sites_removeSocketAddOnCard/README.md +17 -0
  39. catocli/parsers/mutation_sites_startSiteUpgrade/README.md +17 -0
  40. catocli/parsers/mutation_xdr/__init__.py +18 -21
  41. catocli/parsers/parserApiClient.py +36 -11
  42. catocli/parsers/query_accountBySubdomain/__init__.py +6 -7
  43. catocli/parsers/query_accountManagement/__init__.py +6 -7
  44. catocli/parsers/query_accountMetrics/__init__.py +6 -7
  45. catocli/parsers/query_accountRoles/__init__.py +6 -7
  46. catocli/parsers/query_accountSnapshot/__init__.py +6 -7
  47. catocli/parsers/query_admin/__init__.py +6 -7
  48. catocli/parsers/query_admins/__init__.py +6 -7
  49. catocli/parsers/query_appStats/__init__.py +6 -7
  50. catocli/parsers/query_appStatsTimeSeries/__init__.py +6 -7
  51. catocli/parsers/query_auditFeed/__init__.py +6 -7
  52. catocli/parsers/query_catalogs/__init__.py +6 -7
  53. catocli/parsers/query_container/__init__.py +6 -7
  54. catocli/parsers/query_devices/README.md +2 -1
  55. catocli/parsers/query_devices/__init__.py +6 -7
  56. catocli/parsers/query_entityLookup/__init__.py +6 -7
  57. catocli/parsers/query_events/__init__.py +6 -7
  58. catocli/parsers/query_eventsFeed/README.md +1 -1
  59. catocli/parsers/query_eventsFeed/__init__.py +6 -7
  60. catocli/parsers/query_eventsTimeSeries/__init__.py +6 -7
  61. catocli/parsers/query_hardware/__init__.py +6 -7
  62. catocli/parsers/query_hardwareManagement/__init__.py +6 -7
  63. catocli/parsers/query_licensing/__init__.py +6 -7
  64. catocli/parsers/query_policy/README.md +2 -1
  65. catocli/parsers/query_policy/__init__.py +6 -7
  66. catocli/parsers/query_sandbox/__init__.py +6 -7
  67. catocli/parsers/query_site/README.md +2 -1
  68. catocli/parsers/query_site/__init__.py +6 -7
  69. catocli/parsers/query_siteLocation/__init__.py +4 -8
  70. catocli/parsers/query_subDomains/__init__.py +6 -7
  71. catocli/parsers/query_xdr/__init__.py +12 -14
  72. catocli/parsers/raw/README.md +4 -0
  73. catocli/parsers/raw/__init__.py +5 -2
  74. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/METADATA +1 -1
  75. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/RECORD +108 -67
  76. graphql_client/api/call_api.py +12 -6
  77. models/mutation.policy.remotePortFwd.updateRule.json +6 -6
  78. models/mutation.policy.terminalServer.addRule.json +2403 -0
  79. models/mutation.policy.terminalServer.addSection.json +1358 -0
  80. models/mutation.policy.terminalServer.createPolicyRevision.json +1873 -0
  81. models/mutation.policy.terminalServer.discardPolicyRevision.json +1807 -0
  82. models/mutation.policy.terminalServer.moveRule.json +1605 -0
  83. models/mutation.policy.terminalServer.moveSection.json +1259 -0
  84. models/mutation.policy.terminalServer.publishPolicyRevision.json +1864 -0
  85. models/mutation.policy.terminalServer.removeRule.json +1253 -0
  86. models/mutation.policy.terminalServer.removeSection.json +958 -0
  87. models/mutation.policy.terminalServer.updatePolicy.json +1883 -0
  88. models/mutation.policy.terminalServer.updateRule.json +2096 -0
  89. models/mutation.policy.terminalServer.updateSection.json +1111 -0
  90. models/mutation.site.addSocketAddOnCard.json +1050 -0
  91. models/mutation.site.removeSocketAddOnCard.json +786 -0
  92. models/mutation.site.startSiteUpgrade.json +802 -0
  93. models/mutation.sites.addSocketAddOnCard.json +1050 -0
  94. models/mutation.sites.removeSocketAddOnCard.json +786 -0
  95. models/mutation.sites.startSiteUpgrade.json +802 -0
  96. models/query.devices.json +311 -2
  97. models/query.events.json +48 -0
  98. models/query.eventsFeed.json +12 -0
  99. models/query.eventsTimeSeries.json +36 -0
  100. models/query.licensing.json +21815 -10093
  101. models/query.policy.json +1898 -305
  102. models/query.site.json +225 -0
  103. models/query.siteLocation.json +97190 -295396
  104. schema/catolib.py +63 -30
  105. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/LICENSE +0 -0
  106. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/WHEEL +0 -0
  107. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/entry_points.txt +0 -0
  108. {catocli-2.0.1.dist-info → catocli-2.0.3.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ import json
3
3
  import sys
4
4
  from graphql_client.api.call_api import ApiClient, CallApi
5
5
  from graphql_client.api_client import ApiException
6
-
6
+ from ..customLib import writeDataToFile, makeCall, getAccountID
7
7
 
8
8
  def strip_ids_recursive(data):
9
9
  """Recursively strip id attributes from data structure, but only from objects that contain only 'id' and 'name' keys"""
@@ -37,40 +37,7 @@ def export_if_rules_to_json(args, configuration):
37
37
  Adapted from scripts/export_if_rules_to_json.py
38
38
  """
39
39
  try:
40
- # Get account ID from args, configuration, or environment variable
41
- account_id = None
42
- if hasattr(args, 'accountID') and args.accountID:
43
- account_id = args.accountID
44
- elif hasattr(configuration, 'accountID') and configuration.accountID:
45
- account_id = configuration.accountID
46
- else:
47
- account_id = os.getenv('CATO_ACCOUNT_ID')
48
-
49
- if not account_id:
50
- raise ValueError("Account ID is required. Provide it using the -accountID flag or set CATO_ACCOUNT_ID environment variable.")
51
-
52
- # Set up output file path
53
- if hasattr(args, 'output_file_path') and args.output_file_path:
54
- # Use output file path if provided
55
- output_file = args.output_file_path
56
- destination_dir = os.path.dirname(output_file)
57
- if hasattr(args, 'verbose') and args.verbose:
58
- print(f"Using output file path: {output_file}")
59
- else:
60
- # Use default path and filename
61
- destination_dir = 'config_data'
62
- json_output_file = f"all_ifw_rules_and_sections_{account_id}.json"
63
- output_file = os.path.join(destination_dir, json_output_file)
64
- if hasattr(args, 'verbose') and args.verbose:
65
- print(f"Using default path: {output_file}")
66
-
67
- # Create destination directory if it doesn't exist
68
- if destination_dir and not os.path.exists(destination_dir):
69
- if hasattr(args, 'verbose') and args.verbose:
70
- print(f"Creating directory: {destination_dir}")
71
- os.makedirs(destination_dir)
72
-
73
- # Define the GraphQL query
40
+ account_id = getAccountID(args, configuration)
74
41
  policy_query = {
75
42
  "query": "query policy ( $accountId:ID! ) { policy ( accountId:$accountId ) { internetFirewall { policy { enabled rules { audit { updatedTime updatedBy } rule { id name description index section { id name } enabled source { ip host { id name } site { id name } subnet ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } connectionOrigin country { id name } device { id name } deviceOS deviceAttributes { category type model manufacturer os osVersion } destination { application { id name } customApp { id name } appCategory { id name } customCategory { id name } sanctionedAppsCategory { id name } country { id name } domain fqdn ip subnet ipRange { from to } globalIpRange { id name } remoteAsn containers { fqdnContainer { id name } ipAddressRangeContainer { id name } } } service { standard { id name } custom { port portRange { from to } protocol } } action tracking { event { enabled } alert { enabled frequency subscriptionGroup { id name } webhook { id name } mailingList { id name } } } schedule { activeOn customTimeframePolicySchedule: customTimeframe { from to } customRecurringPolicySchedule: customRecurring { from to days } } exceptions { name source { ip host { id name } site { id name } subnet ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } deviceOS country { id name } device { id name } deviceAttributes { category type model manufacturer os osVersion } destination { application { id name } customApp { id name } appCategory { id name } customCategory { id name } sanctionedAppsCategory { id name } country { id name } domain fqdn ip subnet ipRange { from to } globalIpRange { id name } remoteAsn containers { fqdnContainer { id name } ipAddressRangeContainer { id name } } } service { standard { id name } custom { port portRangeCustomService: portRange { from to } protocol } } connectionOrigin } } properties } sections { audit { updatedTime updatedBy } section { id name } properties } audit { publishedTime publishedBy } revision { id name description changes createdTime updatedTime } } } } }",
76
43
  "variables": {
@@ -78,56 +45,7 @@ def export_if_rules_to_json(args, configuration):
78
45
  },
79
46
  "operationName": "policy"
80
47
  }
81
-
82
- # Create API client instance with params
83
- instance = CallApi(ApiClient(configuration))
84
- params = {
85
- 'v': hasattr(args, 'verbose') and args.verbose, # verbose mode
86
- 'f': 'json', # format
87
- 'p': False, # pretty print
88
- 't': False # test mode
89
- }
90
-
91
- try:
92
- # Call the API directly
93
- # NOTE: The API client (graphql_client/api_client_types.py lines 106-108)
94
- # automatically prints error responses and exits on GraphQL errors.
95
- # This means our custom error handling below may not be reached if there are GraphQL errors.
96
- response = instance.call_api(policy_query, params)
97
- all_ifw_rules = response[0] if response else {}
98
-
99
- # Show raw API response in verbose mode
100
- if hasattr(args, 'verbose') and args.verbose:
101
- print("\n" + "=" * 80)
102
- print("RAW API RESPONSE:")
103
- print("=" * 80)
104
- print(json.dumps(all_ifw_rules, indent=2))
105
- print("=" * 80 + "\n")
106
-
107
- # Check for GraphQL errors first (may not be reached due to API client behavior)
108
- if 'errors' in all_ifw_rules:
109
- error_messages = [error.get('message', 'Unknown error') for error in all_ifw_rules['errors']]
110
- raise Exception(f"API returned errors: {', '.join(error_messages)}")
111
-
112
- if not all_ifw_rules or 'data' not in all_ifw_rules:
113
- raise ValueError("Failed to retrieve data from API")
114
-
115
- except ApiException as e:
116
- raise Exception(f"API call failed - {e}")
117
- except Exception as e:
118
- raise Exception(f"Unexpected error during API call - {e}")
119
-
120
- # IMPORTANT: Preserve section IDs BEFORE stripping them
121
- section_id_map = {}
122
- section_to_start_after_id = None
123
- sections_with_ids = all_ifw_rules['data']['policy']['internetFirewall']['policy']['sections']
124
- for index, section_data in enumerate(sections_with_ids):
125
- section_name = section_data['section']['name']
126
- section_id = section_data['section']['id']
127
- if index == 0:
128
- section_to_start_after_id = section_id
129
- else:
130
- section_id_map[section_name] = section_id
48
+ all_ifw_rules = makeCall(args, configuration, policy_query)
131
49
 
132
50
  # Processing data to strip id attributes
133
51
  processed_data = strip_ids_recursive(all_ifw_rules)
@@ -173,26 +91,28 @@ def export_if_rules_to_json(args, configuration):
173
91
 
174
92
  # Reformat sections array to have index, section_id and section_name structure
175
93
  # Exclude the first section from export
94
+ sections_with_ids = all_ifw_rules['data']['policy']['internetFirewall']['policy']['sections']
176
95
  processed_sections = []
177
- for index, section_data in enumerate(processed_data['data']['policy']['internetFirewall']['policy']['sections']):
96
+ for index, section_data in enumerate(sections_with_ids):
97
+ # print("sections_with_ids",json.dumps(section_data, indent=2))
178
98
  if index > 0: # Skip the first section (index 0)
179
99
  processed_sections.append({
180
100
  "section_index": index,
181
- "section_name": section_data['section']['name']
101
+ "section_name": section_data['section']['name'],
102
+ "section_id": section_data['section']['id']
182
103
  })
183
-
184
- # Add preserved section IDs and section_to_start_after_id
185
- processed_data['data']['policy']['internetFirewall']['policy']['section_ids'] = section_id_map
186
- if section_to_start_after_id:
187
- processed_data['data']['policy']['internetFirewall']['policy']['section_to_start_after_id'] = section_to_start_after_id
188
104
 
189
105
  # Replace the original sections array with the reformatted one
190
106
  processed_data['data']['policy']['internetFirewall']['policy']['sections'] = processed_sections
191
107
 
192
- # Write the processed data to the new file
193
- with open(output_file, 'w', encoding='utf-8') as f:
194
- json.dump(processed_data, f, indent=4, ensure_ascii=False)
195
-
108
+ output_file = writeDataToFile(
109
+ data=processed_data,
110
+ args=args,
111
+ account_id=account_id,
112
+ default_filename_template="all_ifw_rules_and_sections_{account_id}.json",
113
+ default_directory="config_data"
114
+ )
115
+
196
116
  return [{"success": True, "output_file": output_file, "account_id": account_id}]
197
117
 
198
118
  except Exception as e:
@@ -206,40 +126,8 @@ def export_wf_rules_to_json(args, configuration):
206
126
  Adapted from scripts/export_wf_rules_to_json.py
207
127
  """
208
128
  try:
209
- # Get account ID from args, configuration, or environment variable
210
- account_id = None
211
- if hasattr(args, 'accountID') and args.accountID:
212
- account_id = args.accountID
213
- elif hasattr(configuration, 'accountID') and configuration.accountID:
214
- account_id = configuration.accountID
215
- else:
216
- account_id = os.getenv('CATO_ACCOUNT_ID')
217
-
218
- if not account_id:
219
- raise ValueError("Account ID is required. Provide it using the -accountID flag or set CATO_ACCOUNT_ID environment variable.")
129
+ account_id = getAccountID(args, configuration)
220
130
 
221
- # Set up output file path
222
- if hasattr(args, 'output_file_path') and args.output_file_path:
223
- # Use output file path if provided
224
- output_file = args.output_file_path
225
- destination_dir = os.path.dirname(output_file)
226
- if hasattr(args, 'verbose') and args.verbose:
227
- print(f"Using output file path: {output_file}")
228
- else:
229
- # Use default path and filename
230
- destination_dir = 'config_data'
231
- json_output_file = f"all_wf_rules_and_sections_{account_id}.json"
232
- output_file = os.path.join(destination_dir, json_output_file)
233
- if hasattr(args, 'verbose') and args.verbose:
234
- print(f"Using default path: {output_file}")
235
-
236
- # Create destination directory if it doesn't exist
237
- if destination_dir and not os.path.exists(destination_dir):
238
- if hasattr(args, 'verbose') and args.verbose:
239
- print(f"Creating directory: {destination_dir}")
240
- os.makedirs(destination_dir)
241
-
242
- # Define the GraphQL query for WAN Firewall
243
131
  policy_query = {
244
132
  "query": "query policy ( $accountId:ID! ) { policy ( accountId:$accountId ) { wanFirewall { policy { enabled rules { audit { updatedTime updatedBy } rule { id name description index section { id name } enabled source { host { id name } site { id name } subnet ip ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } connectionOrigin country { id name } device { id name } deviceOS deviceAttributes { category type model manufacturer os osVersion } destination { host { id name } site { id name } subnet ip ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } application { application { id name } appCategory { id name } customApp { id name } customCategory { id name } sanctionedAppsCategory { id name } domain fqdn ip subnet ipRange { from to } globalIpRange { id name } } service { standard { id name } custom { port portRange { from to } protocol } } action tracking { event { enabled } alert { enabled frequency subscriptionGroup { id name } webhook { id name } mailingList { id name } } } schedule { activeOn customTimeframePolicySchedule: customTimeframe { from to } customRecurringPolicySchedule: customRecurring { from to days } } direction exceptions { name source { host { id name } site { id name } subnet ip ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } deviceOS destination { host { id name } site { id name } subnet ip ipRange { from to } globalIpRange { id name } networkInterface { id name } siteNetworkSubnet { id name } floatingSubnet { id name } user { id name } usersGroup { id name } group { id name } systemGroup { id name } } country { id name } device { id name } deviceAttributes { category type model manufacturer os osVersion } application { application { id name } appCategory { id name } customApp { id name } customCategory { id name } sanctionedAppsCategory { id name } domain fqdn ip subnet ipRange { from to } globalIpRange { id name } } service { standard { id name } custom { port portRangeCustomService: portRange { from to } protocol } } connectionOrigin direction } } properties } sections { audit { updatedTime updatedBy } section { id name } properties } audit { publishedTime publishedBy } revision { id name description changes createdTime updatedTime } } } } }",
245
133
  "variables": {
@@ -247,51 +135,11 @@ def export_wf_rules_to_json(args, configuration):
247
135
  },
248
136
  "operationName": "policy"
249
137
  }
250
-
251
- # Create API client instance with params
252
- instance = CallApi(ApiClient(configuration))
253
- params = {
254
- 'v': hasattr(args, 'verbose') and args.verbose, # verbose mode
255
- 'f': 'json', # format
256
- 'p': False, # pretty print
257
- 't': False # test mode
258
- }
259
-
260
- try:
261
- # Call the API directly
262
- response = instance.call_api(policy_query, params)
263
- all_wf_rules = response[0] if response else {}
264
-
265
- # Show raw API response in verbose mode
266
- if hasattr(args, 'verbose') and args.verbose:
267
- print("\n" + "=" * 80)
268
- print("RAW API RESPONSE:")
269
- print("=" * 80)
270
- print(json.dumps(all_wf_rules, indent=2))
271
- print("=" * 80 + "\n")
272
-
273
- # Check for GraphQL errors first
274
- if 'errors' in all_wf_rules:
275
- error_messages = [error.get('message', 'Unknown error') for error in all_wf_rules['errors']]
276
- raise Exception(f"API returned errors: {', '.join(error_messages)}")
277
-
278
- if not all_wf_rules or 'data' not in all_wf_rules:
279
- raise ValueError("Failed to retrieve data from API")
138
+ all_wf_rules = makeCall(args, configuration, policy_query)
280
139
 
281
- except ApiException as e:
282
- raise Exception(f"API call failed - {e}")
283
- except Exception as e:
284
- raise Exception(f"Unexpected error during API call - {e}")
140
+ if not all_wf_rules or 'data' not in all_wf_rules:
141
+ raise ValueError("Failed to retrieve data from API")
285
142
 
286
- # IMPORTANT: Preserve section IDs BEFORE stripping them
287
- section_id_map = {}
288
- section_to_start_after_id = None
289
- sections_with_ids = all_wf_rules['data']['policy']['wanFirewall']['policy']['sections']
290
- for index, section_data in enumerate(sections_with_ids):
291
- section_name = section_data['section']['name']
292
- section_id = section_data['section']['id']
293
- section_id_map[section_name] = section_id
294
-
295
143
  # Processing data to strip id attributes
296
144
  processed_data = strip_ids_recursive(all_wf_rules)
297
145
 
@@ -335,24 +183,25 @@ def export_wf_rules_to_json(args, configuration):
335
183
 
336
184
  # Reformat sections array to have index, section_id and section_name structure
337
185
  # Exclude the first section from export
186
+ sections_with_ids = all_wf_rules['data']['policy']['wanFirewall']['policy']['sections']
338
187
  processed_sections = []
339
- for index, section_data in enumerate(processed_data['data']['policy']['wanFirewall']['policy']['sections']):
188
+ for index, section_data in enumerate(sections_with_ids):
340
189
  processed_sections.append({
341
- "section_index": index,
342
- "section_name": section_data['section']['name']
190
+ "section_index": index+1,
191
+ "section_name": section_data['section']['name'],
192
+ "section_id": section_data['section']['id']
343
193
  })
344
-
345
- # Add preserved section IDs and section_to_start_after_id
346
- processed_data['data']['policy']['wanFirewall']['policy']['section_ids'] = section_id_map
347
- if section_to_start_after_id:
348
- processed_data['data']['policy']['wanFirewall']['policy']['section_to_start_after_id'] = section_to_start_after_id
349
194
 
350
195
  # Replace the original sections array with the reformatted one
351
196
  processed_data['data']['policy']['wanFirewall']['policy']['sections'] = processed_sections
352
197
 
353
- # Write the processed data to the new file
354
- with open(output_file, 'w', encoding='utf-8') as f:
355
- json.dump(processed_data, f, indent=4, ensure_ascii=False)
198
+ output_file = writeDataToFile(
199
+ data=processed_data,
200
+ args=args,
201
+ account_id=account_id,
202
+ default_filename_template="all_wf_rules_and_sections_{account_id}.json",
203
+ default_directory="config_data"
204
+ )
356
205
 
357
206
  return [{"success": True, "output_file": output_file, "account_id": account_id}]
358
207
 
@@ -0,0 +1,20 @@
1
+ import catocli.parsers.custom.export_sites.export_sites as export_sites
2
+
3
+ def export_sites_parse(subparsers):
4
+ """Create export_sites command parsers"""
5
+
6
+ # Create the socket_sites parser (direct command, no subparsers)
7
+ socket_sites_parser = subparsers.add_parser(
8
+ 'socket_sites',
9
+ help='Export consolidated site and socket data to JSON format',
10
+ usage='catocli export socket_sites [-accountID <account_id>] [options]'
11
+ )
12
+
13
+ socket_sites_parser.add_argument('-accountID', help='Account ID to export data from (uses CATO_ACCOUNT_ID environment variable if not specified)', required=False)
14
+ socket_sites_parser.add_argument('-siteIDs', help='Comma-separated list of site IDs to export (e.g., "132606,132964,133511")', required=False)
15
+ socket_sites_parser.add_argument('--output-file-path', help='Full path including filename and extension for output file. If not specified, uses default: config_data/socket_site_data_{account_id}.json')
16
+ socket_sites_parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
17
+
18
+ socket_sites_parser.set_defaults(func=export_sites.export_socket_site_to_json)
19
+
20
+ return socket_sites_parser