catocli 2.1.2__py3-none-any.whl → 2.1.4__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 (98) hide show
  1. catocli/Utils/clidriver.py +18 -18
  2. catocli/Utils/cliutils.py +165 -0
  3. catocli/Utils/csv_formatter.py +652 -0
  4. catocli/__init__.py +1 -1
  5. catocli/parsers/custom/export_rules/__init__.py +0 -4
  6. catocli/parsers/custom/export_sites/__init__.py +4 -3
  7. catocli/parsers/custom/export_sites/export_sites.py +198 -55
  8. catocli/parsers/custom/import_sites_to_tf/import_sites_to_tf.py +473 -393
  9. catocli/parsers/customParserApiClient.py +444 -38
  10. catocli/parsers/custom_private/__init__.py +19 -13
  11. catocli/parsers/mutation_accountManagement/__init__.py +21 -0
  12. catocli/parsers/mutation_accountManagement_disableAccount/README.md +15 -0
  13. catocli/parsers/mutation_admin/__init__.py +12 -0
  14. catocli/parsers/mutation_container/__init__.py +18 -0
  15. catocli/parsers/mutation_enterpriseDirectory/__init__.py +8 -0
  16. catocli/parsers/mutation_groups/__init__.py +6 -0
  17. catocli/parsers/mutation_hardware/__init__.py +2 -0
  18. catocli/parsers/mutation_policy/__init__.py +378 -0
  19. catocli/parsers/mutation_policy_antiMalwareFileHash_addRule/README.md +20 -0
  20. catocli/parsers/mutation_policy_antiMalwareFileHash_addSection/README.md +20 -0
  21. catocli/parsers/mutation_policy_antiMalwareFileHash_createPolicyRevision/README.md +20 -0
  22. catocli/parsers/mutation_policy_antiMalwareFileHash_discardPolicyRevision/README.md +20 -0
  23. catocli/parsers/mutation_policy_antiMalwareFileHash_moveRule/README.md +20 -0
  24. catocli/parsers/mutation_policy_antiMalwareFileHash_moveSection/README.md +20 -0
  25. catocli/parsers/mutation_policy_antiMalwareFileHash_publishPolicyRevision/README.md +20 -0
  26. catocli/parsers/mutation_policy_antiMalwareFileHash_removeRule/README.md +20 -0
  27. catocli/parsers/mutation_policy_antiMalwareFileHash_removeSection/README.md +20 -0
  28. catocli/parsers/mutation_policy_antiMalwareFileHash_updatePolicy/README.md +20 -0
  29. catocli/parsers/mutation_policy_antiMalwareFileHash_updateRule/README.md +20 -0
  30. catocli/parsers/mutation_policy_antiMalwareFileHash_updateSection/README.md +20 -0
  31. catocli/parsers/mutation_sandbox/__init__.py +4 -0
  32. catocli/parsers/mutation_site/__init__.py +72 -0
  33. catocli/parsers/mutation_sites/__init__.py +72 -0
  34. catocli/parsers/mutation_xdr/__init__.py +6 -0
  35. catocli/parsers/query_accountBySubdomain/__init__.py +2 -0
  36. catocli/parsers/query_accountManagement/__init__.py +2 -0
  37. catocli/parsers/query_accountMetrics/__init__.py +6 -0
  38. catocli/parsers/query_accountRoles/__init__.py +2 -0
  39. catocli/parsers/query_accountSnapshot/__init__.py +2 -0
  40. catocli/parsers/query_admin/__init__.py +2 -0
  41. catocli/parsers/query_admins/__init__.py +2 -0
  42. catocli/parsers/query_appStats/__init__.py +6 -0
  43. catocli/parsers/query_appStatsTimeSeries/README.md +3 -0
  44. catocli/parsers/query_appStatsTimeSeries/__init__.py +6 -0
  45. catocli/parsers/query_auditFeed/__init__.py +2 -0
  46. catocli/parsers/query_catalogs/__init__.py +2 -0
  47. catocli/parsers/query_container/__init__.py +2 -0
  48. catocli/parsers/query_devices/README.md +1 -1
  49. catocli/parsers/query_devices/__init__.py +2 -0
  50. catocli/parsers/query_enterpriseDirectory/__init__.py +2 -0
  51. catocli/parsers/query_entityLookup/__init__.py +2 -0
  52. catocli/parsers/query_events/__init__.py +2 -0
  53. catocli/parsers/query_eventsFeed/__init__.py +2 -0
  54. catocli/parsers/query_eventsTimeSeries/__init__.py +2 -0
  55. catocli/parsers/query_groups/__init__.py +6 -0
  56. catocli/parsers/query_hardware/README.md +1 -1
  57. catocli/parsers/query_hardware/__init__.py +2 -0
  58. catocli/parsers/query_hardwareManagement/__init__.py +2 -0
  59. catocli/parsers/query_licensing/__init__.py +2 -0
  60. catocli/parsers/query_policy/__init__.py +37 -0
  61. catocli/parsers/query_policy_antiMalwareFileHash_policy/README.md +19 -0
  62. catocli/parsers/query_popLocations/__init__.py +2 -0
  63. catocli/parsers/query_sandbox/__init__.py +2 -0
  64. catocli/parsers/query_servicePrincipalAdmin/__init__.py +2 -0
  65. catocli/parsers/query_site/__init__.py +33 -0
  66. catocli/parsers/query_siteLocation/__init__.py +2 -0
  67. catocli/parsers/query_site_siteGeneralDetails/README.md +19 -0
  68. catocli/parsers/query_socketPortMetrics/__init__.py +2 -0
  69. catocli/parsers/query_socketPortMetricsTimeSeries/__init__.py +6 -0
  70. catocli/parsers/query_subDomains/__init__.py +2 -0
  71. catocli/parsers/query_xdr/__init__.py +4 -0
  72. catocli/parsers/raw/__init__.py +3 -1
  73. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/METADATA +1 -1
  74. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/RECORD +98 -66
  75. models/mutation.accountManagement.disableAccount.json +545 -0
  76. models/mutation.policy.antiMalwareFileHash.addRule.json +2068 -0
  77. models/mutation.policy.antiMalwareFileHash.addSection.json +1350 -0
  78. models/mutation.policy.antiMalwareFileHash.createPolicyRevision.json +1822 -0
  79. models/mutation.policy.antiMalwareFileHash.discardPolicyRevision.json +1758 -0
  80. models/mutation.policy.antiMalwareFileHash.moveRule.json +1552 -0
  81. models/mutation.policy.antiMalwareFileHash.moveSection.json +1251 -0
  82. models/mutation.policy.antiMalwareFileHash.publishPolicyRevision.json +1813 -0
  83. models/mutation.policy.antiMalwareFileHash.removeRule.json +1204 -0
  84. models/mutation.policy.antiMalwareFileHash.removeSection.json +954 -0
  85. models/mutation.policy.antiMalwareFileHash.updatePolicy.json +1834 -0
  86. models/mutation.policy.antiMalwareFileHash.updateRule.json +1757 -0
  87. models/mutation.policy.antiMalwareFileHash.updateSection.json +1105 -0
  88. models/mutation.site.updateSiteGeneralDetails.json +3 -3
  89. models/mutation.sites.updateSiteGeneralDetails.json +3 -3
  90. models/query.devices.json +249 -2
  91. models/query.hardware.json +224 -0
  92. models/query.policy.antiMalwareFileHash.policy.json +1583 -0
  93. models/query.site.siteGeneralDetails.json +899 -0
  94. schema/catolib.py +52 -14
  95. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/WHEEL +0 -0
  96. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/entry_points.txt +0 -0
  97. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/licenses/LICENSE +0 -0
  98. {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/top_level.txt +0 -0
@@ -45,7 +45,8 @@ def sanitize_name_for_terraform(name):
45
45
 
46
46
 
47
47
  def extract_socket_sites_data(sites_data):
48
- """Extract socket sites, WAN interfaces, and network ranges from the sites data"""
48
+ """Extract socket sites, WAN interfaces, and network ranges from the sites data.
49
+ Supports both legacy (camelCase) and new (snake_case) JSON formats."""
49
50
  sites = []
50
51
  lan_interfaces = []
51
52
  wan_interfaces = []
@@ -63,17 +64,16 @@ def extract_socket_sites_data(sites_data):
63
64
  'address': site_location.get('address', '')
64
65
  }
65
66
 
66
- # Transform native_range data
67
+ # Transform native_range data (handle both shapes)
67
68
  native_range = {
68
- 'native_network_range': site.get('native_network_range', ''),
69
- 'local_ip': site.get('local_ip', ''),
70
- 'translated_subnet': site.get('translated_subnet', ''),
71
- 'native_network_range_id': site.get('native_network_range_id', '')
69
+ 'native_network_range': site.get('native_network_range', site.get('native_range', {}).get('subnet', '')),
70
+ 'local_ip': site.get('local_ip', site.get('native_range', {}).get('local_ip', '')),
71
+ 'translated_subnet': site.get('translated_subnet', site.get('native_range', {}).get('translated_subnet', '')),
72
+ 'native_network_range_id': site.get('native_network_range_id', site.get('native_range', {}).get('range_id', ''))
72
73
  }
73
-
74
- # Handle dhcp_settings - ensure it's a proper object with required fields
75
- dhcp_settings = site.get('dhcp_settings')
76
- if dhcp_settings and isinstance(dhcp_settings, dict) and dhcp_settings.get('dhcp_type'):
74
+ # Optional DHCP
75
+ dhcp_settings = site.get('dhcp_settings', site.get('native_range', {}).get('dhcp_settings'))
76
+ if dhcp_settings and isinstance(dhcp_settings, dict) and (dhcp_settings.get('dhcp_type') or dhcp_settings.get('ip_range') or dhcp_settings.get('relay_group_id')):
77
77
  native_range['dhcp_settings'] = {
78
78
  'dhcp_type': dhcp_settings.get('dhcp_type', ''),
79
79
  'ip_range': dhcp_settings.get('ip_range', ''),
@@ -86,7 +86,7 @@ def extract_socket_sites_data(sites_data):
86
86
  'id': site['id'],
87
87
  'name': site['name'],
88
88
  'description': site.get('description', ''),
89
- 'connection_type': site.get('connectionType', ''),
89
+ 'connection_type': site.get('connectionType', site.get('connection_type', '')),
90
90
  'site_type': site.get('type', ''),
91
91
  'site_location': transformed_location,
92
92
  'native_range': native_range
@@ -94,48 +94,105 @@ def extract_socket_sites_data(sites_data):
94
94
 
95
95
  # Extract WAN interfaces for this site
96
96
  for wan_interface in site.get('wan_interfaces', []):
97
- if wan_interface.get('id') and wan_interface.get('name'):
97
+ # Accept both key styles
98
+ name = wan_interface.get('name')
99
+ wid = wan_interface.get('id')
100
+ index = wan_interface.get('index')
101
+ if wid and name and index:
102
+ # Apply the same index formatting logic as the Terraform module
103
+ try:
104
+ # If index is a number, format as INT_X
105
+ int(index)
106
+ formatted_index = f"INT_{index}"
107
+ except ValueError:
108
+ # If not a number, use as-is
109
+ formatted_index = index
110
+
98
111
  wan_interfaces.append({
99
112
  'site_id': site['id'],
100
113
  'site_name': site['name'],
101
- 'interface_id': wan_interface['id'],
102
- 'name': wan_interface['name'],
103
- 'upstream_bandwidth': wan_interface.get('upstreamBandwidth', 25),
104
- 'downstream_bandwidth': wan_interface.get('downstreamBandwidth', 25),
105
- 'dest_type': wan_interface.get('destType', 'CATO'),
106
- 'role': wan_interface.get('id', 'wan_1'), # Use interface ID as role
114
+ 'interface_id': wid, # Full ID for actual import
115
+ 'interface_index': formatted_index, # Formatted index for Terraform key
116
+ 'name': name,
117
+ 'upstream_bandwidth': wan_interface.get('upstreamBandwidth', wan_interface.get('upstream_bandwidth', 25)),
118
+ 'downstream_bandwidth': wan_interface.get('downstreamBandwidth', wan_interface.get('downstream_bandwidth', 25)),
119
+ 'dest_type': wan_interface.get('destType', wan_interface.get('dest_type', 'CATO')),
120
+ 'role': wan_interface.get('role', 'wan_1'),
107
121
  'precedence': 'ACTIVE'
108
122
  })
109
123
 
110
- # Extract network ranges for this site
124
+ # Extract network ranges for this site (through LAN interfaces)
111
125
  for lan_interface in site.get('lan_interfaces', []):
112
- interface_id = lan_interface.get('id', '')
113
- interface_name = lan_interface.get('name', '')
114
- lan_interfaces.append({
115
- 'site_id': site['id'],
116
- 'interface_id': lan_interface['id'],
117
- 'name': lan_interface['name'],
118
- 'dest_type': lan_interface.get('destType', 'CATO'),
119
- 'subnet': lan_interface.get('subnet', ''),
120
- 'local_ip': lan_interface.get('local_ip', ''),
121
- })
126
+ interface_id = lan_interface.get('id', None)
127
+ interface_name = lan_interface.get('name', None)
128
+ interface_index = lan_interface.get('index', None)
129
+ is_default_lan = lan_interface.get('default_lan', False)
130
+
131
+ # If this is a default_lan interface, get interface info from native_range
132
+ if is_default_lan:
133
+ native_range = site.get('native_range', {})
134
+ interface_id = native_range.get('interface_id')
135
+ interface_name = native_range.get('interface_name')
136
+ interface_index = native_range.get('index')
137
+
138
+ # print(f"Processing LAN interface: interface_name={interface_name}, interface_id={interface_id}, interface_index={interface_index}, default_lan={is_default_lan}")
139
+ # Add LAN interfaces that have valid interface_id and interface_index (including default_lan interfaces)
140
+ if interface_id!=None and interface_index!=None:
141
+ # For default_lan interfaces, get additional info from the interface itself or native_range
142
+ subnet = lan_interface.get('subnet', '')
143
+ local_ip = lan_interface.get('local_ip', '')
144
+
145
+ # If this is a default_lan interface and we don't have subnet/local_ip, get from native_range
146
+ if is_default_lan:
147
+ native_range_data = site.get('native_range', {})
148
+ if not subnet:
149
+ subnet = native_range_data.get('subnet', '')
150
+ if not local_ip:
151
+ local_ip = native_range_data.get('local_ip', '')
152
+
153
+ lan_interfaces.append({
154
+ 'site_id': site['id'],
155
+ 'id': interface_id,
156
+ 'index': interface_index,
157
+ 'name': interface_name,
158
+ 'dest_type': lan_interface.get('destType', lan_interface.get('dest_type', 'LAN')),
159
+ 'subnet': subnet,
160
+ 'local_ip': local_ip,
161
+ 'role': interface_index or interface_name,
162
+ 'site_name': site.get('name', ''),
163
+ })
122
164
 
123
165
  for network_range in lan_interface.get('network_ranges', []):
124
- if network_range.get('id') and network_range.get('subnet'):
166
+ subnet = network_range.get('subnet')
167
+ if network_range.get('id') and subnet and "native_range" not in network_range:
168
+ # Use the same interface info logic for network ranges
169
+ range_interface_id = interface_id
170
+ range_interface_index = interface_index
171
+ range_interface_name = interface_name
172
+
173
+ # If this is a default_lan interface, use native_range info
174
+ if is_default_lan:
175
+ native_range = site.get('native_range', {})
176
+ range_interface_id = native_range.get('interface_id')
177
+ range_interface_name = native_range.get('interface_name')
178
+ range_interface_index = native_range.get('index')
179
+
180
+ # print(f"Processing Network Range subnet={subnet}, interface_id={range_interface_id}, network_range_id={network_range['id']}, default_lan={is_default_lan}")
125
181
  network_ranges.append({
126
182
  'site_id': site['id'],
127
183
  'site_name': site['name'],
128
- 'interface_id': interface_id,
129
- 'interface_name': interface_name,
184
+ 'interface_id': range_interface_id, # Use actual interface ID, not index
185
+ 'interface_index': range_interface_index, # Also pass interface index separately
186
+ 'interface_name': range_interface_name,
130
187
  'network_range_id': network_range['id'],
131
- 'name': network_range.get('rangeName', ''),
132
- 'subnet': network_range['subnet'],
133
- 'vlan_tag': network_range.get('vlanTag', ''),
134
- 'range_type': 'VLAN' if network_range.get('vlanTag') and network_range.get('vlanTag') != '' else 'Native',
188
+ 'name': network_range.get('rangeName', network_range.get('name', '')),
189
+ 'subnet': subnet,
190
+ 'vlan_tag': network_range.get('vlanTag', network_range.get('vlan', '')),
191
+ 'range_type': 'VLAN' if (network_range.get('vlanTag') or network_range.get('vlan')) else 'Native',
135
192
  'microsegmentation': network_range.get('microsegmentation', False)
136
193
  })
137
194
 
138
- return sites, wan_interfaces, network_ranges
195
+ return sites, wan_interfaces, lan_interfaces, network_ranges
139
196
 
140
197
 
141
198
  def run_terraform_import(resource_address, resource_id, timeout=60, verbose=False):
@@ -185,340 +242,340 @@ def run_terraform_import(resource_address, resource_id, timeout=60, verbose=Fals
185
242
  return False, "", str(e)
186
243
 
187
244
 
188
- def find_rule_index(rules, rule_name):
189
- """Find rule index by name."""
190
- for index, rule in enumerate(rules):
191
- if rule['name'] == rule_name:
192
- return index
193
- return None
245
+ # def find_rule_index(rules, rule_name):
246
+ # """Find rule index by name."""
247
+ # for index, rule in enumerate(rules):
248
+ # if rule['name'] == rule_name:
249
+ # return index
250
+ # return None
194
251
 
195
252
 
196
- def import_sections(sections, module_name, resource_type,
197
- resource_name="sections", verbose=False):
198
- """Import all sections"""
199
- print("\nStarting section imports...")
200
- total_sections = len(sections)
201
- successful_imports = 0
202
- failed_imports = 0
253
+ # def import_sections(sections, module_name, resource_type,
254
+ # resource_name="sections", verbose=False):
255
+ # """Import all sections"""
256
+ # print("\nStarting section imports...")
257
+ # total_sections = len(sections)
258
+ # successful_imports = 0
259
+ # failed_imports = 0
203
260
 
204
- for i, section in enumerate(sections):
205
- section_id = section['section_id']
206
- section_name = section['section_name']
207
- section_index = section['section_index']
208
- resource_address = f'{module_name}.{resource_type}.{resource_name}["{section_name}"]'
209
- print(f"\n[{i+1}/{total_sections}] Section: {section_name} (index: {section_index})")
261
+ # for i, section in enumerate(sections):
262
+ # section_id = section['section_id']
263
+ # section_name = section['section_name']
264
+ # section_index = section['section_index']
265
+ # resource_address = f'{module_name}.{resource_type}.{resource_name}["{section_name}"]'
266
+ # print(f"\n[{i+1}/{total_sections}] Section: {section_name} (index: {section_index})")
210
267
 
211
- # For sections, we use the section name as the ID since that's how Cato identifies them
212
- success, stdout, stderr = run_terraform_import(resource_address, section_id, verbose=verbose)
268
+ # # For sections, we use the section name as the ID since that's how Cato identifies them
269
+ # success, stdout, stderr = run_terraform_import(resource_address, section_id, verbose=verbose)
213
270
 
214
- if success:
215
- successful_imports += 1
216
- else:
217
- failed_imports += 1
271
+ # if success:
272
+ # successful_imports += 1
273
+ # else:
274
+ # failed_imports += 1
218
275
 
219
- print(f"\nSection Import Summary: {successful_imports} successful, {failed_imports} failed")
220
- return successful_imports, failed_imports
276
+ # print(f"\nSection Import Summary: {successful_imports} successful, {failed_imports} failed")
277
+ # return successful_imports, failed_imports
221
278
 
222
279
 
223
- def import_rules(rules, module_name, verbose=False,
224
- resource_type="cato_if_rule", resource_name="rules",
225
- batch_size=10, delay_between_batches=2, auto_approve=False):
226
- """Import all rules in batches"""
227
- print("\nStarting rule imports...")
228
- successful_imports = 0
229
- failed_imports = 0
230
- total_rules = len(rules)
280
+ # def import_rules(rules, module_name, verbose=False,
281
+ # resource_type="cato_if_rule", resource_name="rules",
282
+ # batch_size=10, delay_between_batches=2, auto_approve=False):
283
+ # """Import all rules in batches"""
284
+ # print("\nStarting rule imports...")
285
+ # successful_imports = 0
286
+ # failed_imports = 0
287
+ # total_rules = len(rules)
231
288
 
232
- for i, rule in enumerate(rules):
233
- rule_id = rule['id']
234
- rule_name = rule['name']
235
- rule_index = find_rule_index(rules, rule_name)
236
- terraform_key = sanitize_name_for_terraform(rule_name)
237
-
238
- # Use array index syntax instead of rule ID
239
- resource_address = f'{module_name}.{resource_type}.{resource_name}["{str(rule_name)}"]'
240
- print(f"\n[{i+1}/{total_rules}] Rule: {rule_name} (index: {rule_index})")
241
-
242
- success, stdout, stderr = run_terraform_import(resource_address, rule_id, verbose=verbose)
243
-
244
- if success:
245
- successful_imports += 1
246
- else:
247
- failed_imports += 1
289
+ # for i, rule in enumerate(rules):
290
+ # rule_id = rule['id']
291
+ # rule_name = rule['name']
292
+ # rule_index = find_rule_index(rules, rule_name)
293
+ # terraform_key = sanitize_name_for_terraform(rule_name)
294
+
295
+ # # Use array index syntax instead of rule ID
296
+ # resource_address = f'{module_name}.{resource_type}.{resource_name}["{str(rule_name)}"]'
297
+ # print(f"\n[{i+1}/{total_rules}] Rule: {rule_name} (index: {rule_index})")
298
+
299
+ # success, stdout, stderr = run_terraform_import(resource_address, rule_id, verbose=verbose)
300
+
301
+ # if success:
302
+ # successful_imports += 1
303
+ # else:
304
+ # failed_imports += 1
248
305
 
249
- # Ask user if they want to continue on failure (unless auto-approved)
250
- if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
251
- response = input(f"\nContinue with remaining imports? (y/n): ").lower()
252
- if response == 'n':
253
- print("Import process stopped by user.")
254
- break
255
-
256
- # Delay between batches
257
- if (i + 1) % batch_size == 0 and i < total_rules - 1:
258
- print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
259
- time.sleep(delay_between_batches)
306
+ # # Ask user if they want to continue on failure (unless auto-approved)
307
+ # if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
308
+ # response = input(f"\nContinue with remaining imports? (y/n): ").lower()
309
+ # if response == 'n':
310
+ # print("Import process stopped by user.")
311
+ # break
312
+
313
+ # # Delay between batches
314
+ # if (i + 1) % batch_size == 0 and i < total_rules - 1:
315
+ # print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
316
+ # time.sleep(delay_between_batches)
260
317
 
261
- print(f"\n Rule Import Summary: {successful_imports} successful, {failed_imports} failed")
262
- return successful_imports, failed_imports
318
+ # print(f"\n Rule Import Summary: {successful_imports} successful, {failed_imports} failed")
319
+ # return successful_imports, failed_imports
263
320
 
264
321
 
265
- def import_if_rules_to_tf(args, configuration):
266
- """Main function to orchestrate the import process"""
267
- try:
268
- print(" Terraform Import Tool - Cato IFW Rules & Sections")
269
- print("=" * 60)
270
-
271
- # Load data
272
- print(f" Loading data from {args.json_file}...")
273
- policy_data = load_json_data(args.json_file)
274
-
275
- # Extract rules and sections
276
- rules, sections = extract_rules_and_sections(policy_data)
277
-
278
- if hasattr(args, 'verbose') and args.verbose:
279
- print(f"section_ids: {json.dumps(policy_data.get('section_ids', {}), indent=2)}")
280
-
281
- print(f" Found {len(rules)} rules")
282
- print(f" Found {len(sections)} sections")
283
-
284
- if not rules and not sections:
285
- print(" No rules or sections found. Exiting.")
286
- return [{"success": False, "error": "No rules or sections found"}]
287
-
288
- # Validate Terraform environment before proceeding
289
- validate_terraform_environment(args.module_name, verbose=args.verbose)
290
-
291
- # Ask for confirmation (unless auto-approved)
292
- if not args.rules_only and not args.sections_only:
293
- print(f"\n Ready to import {len(sections)} sections and {len(rules)} rules.")
294
- elif args.rules_only:
295
- print(f"\n Ready to import {len(rules)} rules only.")
296
- elif args.sections_only:
297
- print(f"\n Ready to import {len(sections)} sections only.")
298
-
299
- if hasattr(args, 'auto_approve') and args.auto_approve:
300
- print("\nAuto-approve enabled, proceeding with import...")
301
- else:
302
- confirm = input(f"\nProceed with import? (y/n): ").lower()
303
- if confirm != 'y':
304
- print("Import cancelled.")
305
- return [{"success": False, "error": "Import cancelled by user"}]
306
-
307
- total_successful = 0
308
- total_failed = 0
309
-
310
- # Import sections first (if not skipped)
311
- if not args.rules_only and sections:
312
- successful, failed = import_sections(sections, module_name=args.module_name, resource_type="cato_if_section", verbose=args.verbose)
313
- total_successful += successful
314
- total_failed += failed
315
-
316
- # Import rules (if not skipped)
317
- if not args.sections_only and rules:
318
- successful, failed = import_rules(rules, module_name=args.module_name,
319
- verbose=args.verbose, batch_size=args.batch_size,
320
- delay_between_batches=args.delay,
321
- auto_approve=getattr(args, 'auto_approve', False))
322
- total_successful += successful
323
- total_failed += failed
324
-
325
- # Final summary
326
- print("\n" + "=" * 60)
327
- print(" FINAL IMPORT SUMMARY")
328
- print("=" * 60)
329
- print(f" Total successful imports: {total_successful}")
330
- print(f" Total failed imports: {total_failed}")
331
- print(f" Overall success rate: {(total_successful / (total_successful + total_failed) * 100):.1f}%" if (total_successful + total_failed) > 0 else "N/A")
332
- print("\n Import process completed!")
333
-
334
- return [{
335
- "success": True,
336
- "total_successful": total_successful,
337
- "total_failed": total_failed,
338
- "module_name": args.module_name
339
- }]
340
-
341
- except Exception as e:
342
- print(f"ERROR: {str(e)}")
343
- return [{"success": False, "error": str(e)}]
322
+ # def import_if_rules_to_tf(args, configuration):
323
+ # """Main function to orchestrate the import process"""
324
+ # try:
325
+ # print(" Terraform Import Tool - Cato IFW Rules & Sections")
326
+ # print("=" * 60)
327
+
328
+ # # Load data
329
+ # print(f" Loading data from {args.json_file}...")
330
+ # policy_data = load_json_data(args.json_file)
331
+
332
+ # # Extract rules and sections
333
+ # rules, sections = extract_rules_and_sections(policy_data)
334
+
335
+ # if hasattr(args, 'verbose') and args.verbose:
336
+ # print(f"section_ids: {json.dumps(policy_data.get('section_ids', {}), indent=2)}")
337
+
338
+ # print(f" Found {len(rules)} rules")
339
+ # print(f" Found {len(sections)} sections")
340
+
341
+ # if not rules and not sections:
342
+ # print(" No rules or sections found. Exiting.")
343
+ # return [{"success": False, "error": "No rules or sections found"}]
344
+
345
+ # # Validate Terraform environment before proceeding
346
+ # validate_terraform_environment(args.module_name, verbose=args.verbose)
347
+
348
+ # # Ask for confirmation (unless auto-approved)
349
+ # if not args.rules_only and not args.sections_only:
350
+ # print(f"\n Ready to import {len(sections)} sections and {len(rules)} rules.")
351
+ # elif args.rules_only:
352
+ # print(f"\n Ready to import {len(rules)} rules only.")
353
+ # elif args.sections_only:
354
+ # print(f"\n Ready to import {len(sections)} sections only.")
355
+
356
+ # if hasattr(args, 'auto_approve') and args.auto_approve:
357
+ # print("\nAuto-approve enabled, proceeding with import...")
358
+ # else:
359
+ # confirm = input(f"\nProceed with import? (y/n): ").lower()
360
+ # if confirm != 'y':
361
+ # print("Import cancelled.")
362
+ # return [{"success": False, "error": "Import cancelled by user"}]
363
+
364
+ # total_successful = 0
365
+ # total_failed = 0
366
+
367
+ # # Import sections first (if not skipped)
368
+ # if not args.rules_only and sections:
369
+ # successful, failed = import_sections(sections, module_name=args.module_name, resource_type="cato_if_section", verbose=args.verbose)
370
+ # total_successful += successful
371
+ # total_failed += failed
372
+
373
+ # # Import rules (if not skipped)
374
+ # if not args.sections_only and rules:
375
+ # successful, failed = import_rules(rules, module_name=args.module_name,
376
+ # verbose=args.verbose, batch_size=args.batch_size,
377
+ # delay_between_batches=args.delay,
378
+ # auto_approve=getattr(args, 'auto_approve', False))
379
+ # total_successful += successful
380
+ # total_failed += failed
381
+
382
+ # # Final summary
383
+ # print("\n" + "=" * 60)
384
+ # print(" FINAL IMPORT SUMMARY")
385
+ # print("=" * 60)
386
+ # print(f" Total successful imports: {total_successful}")
387
+ # print(f" Total failed imports: {total_failed}")
388
+ # print(f" Overall success rate: {(total_successful / (total_successful + total_failed) * 100):.1f}%" if (total_successful + total_failed) > 0 else "N/A")
389
+ # print("\n Import process completed!")
390
+
391
+ # return [{
392
+ # "success": True,
393
+ # "total_successful": total_successful,
394
+ # "total_failed": total_failed,
395
+ # "module_name": args.module_name
396
+ # }]
397
+
398
+ # except Exception as e:
399
+ # print(f"ERROR: {str(e)}")
400
+ # return [{"success": False, "error": str(e)}]
344
401
 
345
402
 
346
- def load_wf_json_data(json_file):
347
- """Load WAN Firewall data from JSON file"""
348
- try:
349
- with open(json_file, 'r') as f:
350
- data = json.load(f)
351
- return data['data']['policy']['wanFirewall']['policy']
352
- except FileNotFoundError:
353
- print(f"Error: JSON file '{json_file}' not found")
354
- sys.exit(1)
355
- except json.JSONDecodeError as e:
356
- print(f"Error: Invalid JSON in '{json_file}': {e}")
357
- sys.exit(1)
358
- except KeyError as e:
359
- print(f"Error: Expected JSON structure not found in '{json_file}': {e}")
360
- sys.exit(1)
403
+ # def load_wf_json_data(json_file):
404
+ # """Load WAN Firewall data from JSON file"""
405
+ # try:
406
+ # with open(json_file, 'r') as f:
407
+ # data = json.load(f)
408
+ # return data['data']['policy']['wanFirewall']['policy']
409
+ # except FileNotFoundError:
410
+ # print(f"Error: JSON file '{json_file}' not found")
411
+ # sys.exit(1)
412
+ # except json.JSONDecodeError as e:
413
+ # print(f"Error: Invalid JSON in '{json_file}': {e}")
414
+ # sys.exit(1)
415
+ # except KeyError as e:
416
+ # print(f"Error: Expected JSON structure not found in '{json_file}': {e}")
417
+ # sys.exit(1)
361
418
 
362
419
 
363
- def import_wf_sections(sections, module_name, verbose=False,
364
- resource_type="cato_wf_section", resource_name="sections"):
365
- """Import all WAN Firewall sections"""
366
- print("\nStarting WAN Firewall section imports...")
367
- total_sections = len(sections)
368
- successful_imports = 0
369
- failed_imports = 0
420
+ # def import_wf_sections(sections, module_name, verbose=False,
421
+ # resource_type="cato_wf_section", resource_name="sections"):
422
+ # """Import all WAN Firewall sections"""
423
+ # print("\nStarting WAN Firewall section imports...")
424
+ # total_sections = len(sections)
425
+ # successful_imports = 0
426
+ # failed_imports = 0
370
427
 
371
- for i, section in enumerate(sections):
372
- section_id = section['section_id']
373
- section_name = section['section_name']
374
- section_index = section['section_index']
375
- # Add module. prefix if not present
376
- if not module_name.startswith('module.'):
377
- module_name = f'module.{module_name}'
378
- resource_address = f'{module_name}.{resource_type}.{resource_name}["{section_name}"]'
379
- print(f"\n[{i+1}/{total_sections}] Section: {section_name} (index: {section_index})")
428
+ # for i, section in enumerate(sections):
429
+ # section_id = section['section_id']
430
+ # section_name = section['section_name']
431
+ # section_index = section['section_index']
432
+ # # Add module. prefix if not present
433
+ # if not module_name.startswith('module.'):
434
+ # module_name = f'module.{module_name}'
435
+ # resource_address = f'{module_name}.{resource_type}.{resource_name}["{section_name}"]'
436
+ # print(f"\n[{i+1}/{total_sections}] Section: {section_name} (index: {section_index})")
380
437
 
381
- # For sections, we use the section name as the ID since that's how Cato identifies them
382
- success, stdout, stderr = run_terraform_import(resource_address, section_id, verbose=verbose)
438
+ # # For sections, we use the section name as the ID since that's how Cato identifies them
439
+ # success, stdout, stderr = run_terraform_import(resource_address, section_id, verbose=verbose)
383
440
 
384
- if success:
385
- successful_imports += 1
386
- else:
387
- failed_imports += 1
441
+ # if success:
442
+ # successful_imports += 1
443
+ # else:
444
+ # failed_imports += 1
388
445
 
389
- print(f"\nWAN Firewall Section Import Summary: {successful_imports} successful, {failed_imports} failed")
390
- return successful_imports, failed_imports
446
+ # print(f"\nWAN Firewall Section Import Summary: {successful_imports} successful, {failed_imports} failed")
447
+ # return successful_imports, failed_imports
391
448
 
392
449
 
393
- def import_wf_rules(rules, module_name, verbose=False,
394
- resource_type="cato_wf_rule", resource_name="rules",
395
- batch_size=10, delay_between_batches=2, auto_approve=False):
396
- """Import all WAN Firewall rules in batches"""
397
- print("\nStarting WAN Firewall rule imports...")
398
- successful_imports = 0
399
- failed_imports = 0
400
- total_rules = len(rules)
450
+ # def import_wf_rules(rules, module_name, verbose=False,
451
+ # resource_type="cato_wf_rule", resource_name="rules",
452
+ # batch_size=10, delay_between_batches=2, auto_approve=False):
453
+ # """Import all WAN Firewall rules in batches"""
454
+ # print("\nStarting WAN Firewall rule imports...")
455
+ # successful_imports = 0
456
+ # failed_imports = 0
457
+ # total_rules = len(rules)
401
458
 
402
- for i, rule in enumerate(rules):
403
- rule_id = rule['id']
404
- rule_name = rule['name']
405
- rule_index = find_rule_index(rules, rule_name)
406
- terraform_key = sanitize_name_for_terraform(rule_name)
407
-
408
- # Add module. prefix if not present
409
- if not module_name.startswith('module.'):
410
- module_name = f'module.{module_name}'
459
+ # for i, rule in enumerate(rules):
460
+ # rule_id = rule['id']
461
+ # rule_name = rule['name']
462
+ # rule_index = find_rule_index(rules, rule_name)
463
+ # terraform_key = sanitize_name_for_terraform(rule_name)
464
+
465
+ # # Add module. prefix if not present
466
+ # if not module_name.startswith('module.'):
467
+ # module_name = f'module.{module_name}'
411
468
 
412
- # Use array index syntax instead of rule ID
413
- resource_address = f'{module_name}.{resource_type}.{resource_name}["{str(rule_name)}"]'
414
- print(f"\n[{i+1}/{total_rules}] Rule: {rule_name} (index: {rule_index})")
469
+ # # Use array index syntax instead of rule ID
470
+ # resource_address = f'{module_name}.{resource_type}.{resource_name}["{str(rule_name)}"]'
471
+ # print(f"\n[{i+1}/{total_rules}] Rule: {rule_name} (index: {rule_index})")
415
472
 
416
- success, stdout, stderr = run_terraform_import(resource_address, rule_id, verbose=verbose)
473
+ # success, stdout, stderr = run_terraform_import(resource_address, rule_id, verbose=verbose)
417
474
 
418
- if success:
419
- successful_imports += 1
420
- else:
421
- failed_imports += 1
475
+ # if success:
476
+ # successful_imports += 1
477
+ # else:
478
+ # failed_imports += 1
422
479
 
423
- # Ask user if they want to continue on failure (unless auto-approved)
424
- if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
425
- response = input(f"\nContinue with remaining imports? (y/n): ").lower()
426
- if response == 'n':
427
- print("Import process stopped by user.")
428
- break
429
-
430
- # Delay between batches
431
- if (i + 1) % batch_size == 0 and i < total_rules - 1:
432
- print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
433
- time.sleep(delay_between_batches)
480
+ # # Ask user if they want to continue on failure (unless auto-approved)
481
+ # if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
482
+ # response = input(f"\nContinue with remaining imports? (y/n): ").lower()
483
+ # if response == 'n':
484
+ # print("Import process stopped by user.")
485
+ # break
486
+
487
+ # # Delay between batches
488
+ # if (i + 1) % batch_size == 0 and i < total_rules - 1:
489
+ # print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
490
+ # time.sleep(delay_between_batches)
434
491
 
435
- print(f"\nWAN Firewall Rule Import Summary: {successful_imports} successful, {failed_imports} failed")
436
- return successful_imports, failed_imports
492
+ # print(f"\nWAN Firewall Rule Import Summary: {successful_imports} successful, {failed_imports} failed")
493
+ # return successful_imports, failed_imports
437
494
 
438
495
 
439
- def import_wf_rules_to_tf(args, configuration):
440
- """Main function to orchestrate the WAN Firewall import process"""
441
- try:
442
- print(" Terraform Import Tool - Cato WF Rules & Sections")
443
- print("=" * 60)
444
-
445
- # Load data
446
- print(f" Loading data from {args.json_file}...")
447
- policy_data = load_wf_json_data(args.json_file)
448
-
449
- # Extract rules and sections
450
- rules, sections = extract_rules_and_sections(policy_data)
451
-
452
- if hasattr(args, 'verbose') and args.verbose:
453
- print(f"section_ids: {json.dumps(policy_data.get('section_ids', {}), indent=2)}")
454
-
455
- print(f" Found {len(rules)} rules")
456
- print(f" Found {len(sections)} sections")
457
-
458
- if not rules and not sections:
459
- print(" No rules or sections found. Exiting.")
460
- return [{"success": False, "error": "No rules or sections found"}]
461
-
462
- # Add module. prefix if not present
463
- module_name = args.module_name
464
- if not module_name.startswith('module.'):
465
- module_name = f'module.{module_name}'
466
- # Validate Terraform environment before proceeding
467
- validate_terraform_environment(module_name, verbose=args.verbose)
468
-
469
- # Ask for confirmation (unless auto-approved)
470
- if not args.rules_only and not args.sections_only:
471
- print(f"\n Ready to import {len(sections)} sections and {len(rules)} rules.")
472
- elif args.rules_only:
473
- print(f"\n Ready to import {len(rules)} rules only.")
474
- elif args.sections_only:
475
- print(f"\n Ready to import {len(sections)} sections only.")
476
-
477
- if hasattr(args, 'auto_approve') and args.auto_approve:
478
- print("\nAuto-approve enabled, proceeding with import...")
479
- else:
480
- confirm = input(f"\nProceed with import? (y/n): ").lower()
481
- if confirm != 'y':
482
- print("Import cancelled.")
483
- return [{"success": False, "error": "Import cancelled by user"}]
484
-
485
- total_successful = 0
486
- total_failed = 0
487
-
488
- # Import sections first (if not skipped)
489
- if not args.rules_only and sections:
490
- successful, failed = import_wf_sections(sections, module_name=args.module_name, verbose=args.verbose)
491
- total_successful += successful
492
- total_failed += failed
493
-
494
- # Import rules (if not skipped)
495
- if not args.sections_only and rules:
496
- successful, failed = import_wf_rules(rules, module_name=args.module_name,
497
- verbose=args.verbose, batch_size=args.batch_size,
498
- delay_between_batches=args.delay,
499
- auto_approve=getattr(args, 'auto_approve', False))
500
- total_successful += successful
501
- total_failed += failed
502
-
503
- # Final summary
504
- print("\n" + "=" * 60)
505
- print(" FINAL IMPORT SUMMARY")
506
- print("=" * 60)
507
- print(f" Total successful imports: {total_successful}")
508
- print(f" Total failed imports: {total_failed}")
509
- print(f" Overall success rate: {(total_successful / (total_successful + total_failed) * 100):.1f}%" if (total_successful + total_failed) > 0 else "N/A")
510
- print("\n Import process completed!")
511
-
512
- return [{
513
- "success": True,
514
- "total_successful": total_successful,
515
- "total_failed": total_failed,
516
- "module_name": args.module_name
517
- }]
518
-
519
- except Exception as e:
520
- print(f"ERROR: {str(e)}")
521
- return [{"success": False, "error": str(e)}]
496
+ # def import_wf_rules_to_tf(args, configuration):
497
+ # """Main function to orchestrate the WAN Firewall import process"""
498
+ # try:
499
+ # print(" Terraform Import Tool - Cato WF Rules & Sections")
500
+ # print("=" * 60)
501
+
502
+ # # Load data
503
+ # print(f" Loading data from {args.json_file}...")
504
+ # policy_data = load_wf_json_data(args.json_file)
505
+
506
+ # # Extract rules and sections
507
+ # rules, sections = extract_rules_and_sections(policy_data)
508
+
509
+ # if hasattr(args, 'verbose') and args.verbose:
510
+ # print(f"section_ids: {json.dumps(policy_data.get('section_ids', {}), indent=2)}")
511
+
512
+ # print(f" Found {len(rules)} rules")
513
+ # print(f" Found {len(sections)} sections")
514
+
515
+ # if not rules and not sections:
516
+ # print(" No rules or sections found. Exiting.")
517
+ # return [{"success": False, "error": "No rules or sections found"}]
518
+
519
+ # # Add module. prefix if not present
520
+ # module_name = args.module_name
521
+ # if not module_name.startswith('module.'):
522
+ # module_name = f'module.{module_name}'
523
+ # # Validate Terraform environment before proceeding
524
+ # validate_terraform_environment(module_name, verbose=args.verbose)
525
+
526
+ # # Ask for confirmation (unless auto-approved)
527
+ # if not args.rules_only and not args.sections_only:
528
+ # print(f"\n Ready to import {len(sections)} sections and {len(rules)} rules.")
529
+ # elif args.rules_only:
530
+ # print(f"\n Ready to import {len(rules)} rules only.")
531
+ # elif args.sections_only:
532
+ # print(f"\n Ready to import {len(sections)} sections only.")
533
+
534
+ # if hasattr(args, 'auto_approve') and args.auto_approve:
535
+ # print("\nAuto-approve enabled, proceeding with import...")
536
+ # else:
537
+ # confirm = input(f"\nProceed with import? (y/n): ").lower()
538
+ # if confirm != 'y':
539
+ # print("Import cancelled.")
540
+ # return [{"success": False, "error": "Import cancelled by user"}]
541
+
542
+ # total_successful = 0
543
+ # total_failed = 0
544
+
545
+ # # Import sections first (if not skipped)
546
+ # if not args.rules_only and sections:
547
+ # successful, failed = import_wf_sections(sections, module_name=args.module_name, verbose=args.verbose)
548
+ # total_successful += successful
549
+ # total_failed += failed
550
+
551
+ # # Import rules (if not skipped)
552
+ # if not args.sections_only and rules:
553
+ # successful, failed = import_wf_rules(rules, module_name=args.module_name,
554
+ # verbose=args.verbose, batch_size=args.batch_size,
555
+ # delay_between_batches=args.delay,
556
+ # auto_approve=getattr(args, 'auto_approve', False))
557
+ # total_successful += successful
558
+ # total_failed += failed
559
+
560
+ # # Final summary
561
+ # print("\n" + "=" * 60)
562
+ # print(" FINAL IMPORT SUMMARY")
563
+ # print("=" * 60)
564
+ # print(f" Total successful imports: {total_successful}")
565
+ # print(f" Total failed imports: {total_failed}")
566
+ # print(f" Overall success rate: {(total_successful / (total_successful + total_failed) * 100):.1f}%" if (total_successful + total_failed) > 0 else "N/A")
567
+ # print("\n Import process completed!")
568
+
569
+ # return [{
570
+ # "success": True,
571
+ # "total_successful": total_successful,
572
+ # "total_failed": total_failed,
573
+ # "module_name": args.module_name
574
+ # }]
575
+
576
+ # except Exception as e:
577
+ # print(f"ERROR: {str(e)}")
578
+ # return [{"success": False, "error": str(e)}]
522
579
 
523
580
 
524
581
  def import_socket_sites(sites, module_name, verbose=False,
@@ -567,7 +624,7 @@ def import_socket_sites(sites, module_name, verbose=False,
567
624
 
568
625
 
569
626
  def import_wan_interfaces(wan_interfaces, module_name, verbose=False,
570
- resource_type="cato_wan_interface", resource_name="wan_interfaces",
627
+ resource_type="cato_wan_interface", resource_name="wan",
571
628
  batch_size=10, delay_between_batches=2, auto_approve=False):
572
629
  """Import all WAN interfaces in batches"""
573
630
  print("\nStarting WAN interface imports...")
@@ -585,18 +642,17 @@ def import_wan_interfaces(wan_interfaces, module_name, verbose=False,
585
642
  if not module_name.startswith('module.'):
586
643
  module_name = f'module.{module_name}'
587
644
 
588
- # Use correct resource addressing for WAN interfaces
589
- # WAN interfaces use the interface role as the key
590
- wan_role = interface['role']
591
- resource_address = f'{module_name}.module.socket-site["{site_name}"].cato_wan_interface.wan["{wan_role}"]'
645
+ # In the module, cato_wan_interface.wan is now keyed by interface_index, which we
646
+ # format from the JSON "index" field. Use interface_index as the key.
647
+ wan_key = interface.get('interface_index', interface_id) # Use formatted index, fallback to ID
648
+ resource_address = f'{module_name}.module.socket-site["{site_name}"].cato_wan_interface.wan["{wan_key}"]'
592
649
 
593
- # WAN interface import needs site_id:interface_id format
594
- # Check if interface_id is already in the correct format (contains ':')
650
+ # WAN import id must be "site_id:interface_part"
595
651
  if ':' in interface_id:
596
- import_id = interface_id # Already in correct format
652
+ import_id = interface_id
597
653
  else:
598
- import_id = f"{site_id}:{interface_id}" # Need to add site_id
599
- print(f"\n[{i+1}/{total_interfaces}] WAN Interface: {interface_name} on {site_name} (ID: {import_id})")
654
+ import_id = f"{site_id}:{interface_id}"
655
+ print(f"\n[{i+1}/{total_interfaces}] WAN Interface: {interface_name} on {site_name} (Key: {wan_key})")
600
656
 
601
657
  success, stdout, stderr = run_terraform_import(resource_address, import_id, verbose=verbose)
602
658
 
@@ -605,14 +661,12 @@ def import_wan_interfaces(wan_interfaces, module_name, verbose=False,
605
661
  else:
606
662
  failed_imports += 1
607
663
 
608
- # Ask user if they want to continue on failure (unless auto-approved)
609
- if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
664
+ if failed_imports <= 3 and not auto_approve:
610
665
  response = input(f"\nContinue with remaining imports? (y/n): ").lower()
611
666
  if response == 'n':
612
667
  print("Import process stopped by user.")
613
668
  break
614
669
 
615
- # Delay between batches
616
670
  if (i + 1) % batch_size == 0 and i < total_interfaces - 1:
617
671
  print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
618
672
  time.sleep(delay_between_batches)
@@ -621,10 +675,10 @@ def import_wan_interfaces(wan_interfaces, module_name, verbose=False,
621
675
  return successful_imports, failed_imports
622
676
 
623
677
  def import_lan_interfaces(lan_interfaces, module_name, verbose=False,
624
- resource_type="cato_lan_interface", resource_name="lan_interfaces",
678
+ resource_type="cato_lan_interface", resource_name="interface",
625
679
  batch_size=10, delay_between_batches=2, auto_approve=False):
626
- """Import all WAN interfaces in batches"""
627
- print("\nStarting WAN interface imports...")
680
+ """Import all LAN interfaces in batches"""
681
+ print("\nStarting LAN interface imports...")
628
682
  successful_imports = 0
629
683
  failed_imports = 0
630
684
  total_interfaces = len(lan_interfaces)
@@ -640,12 +694,20 @@ def import_lan_interfaces(lan_interfaces, module_name, verbose=False,
640
694
  if not module_name.startswith('module.'):
641
695
  module_name = f'module.{module_name}'
642
696
 
643
- # Use correct resource addressing for WAN interfaces
644
- # WAN interfaces use the interface role as the key
645
- lan_role = interface['role']
646
- resource_address = f'{module_name}.module.socket-site["{site_name}"].cato_lan_interface.lan["{interface_index}"]'
697
+ # Updated addressing to use interface_index-based indexing:
698
+ # module.sites.module.socket-site[site].module.lan_interfaces[interface_index].cato_lan_interface.interface[interface_id]
699
+ # Apply the same index formatting logic as the Terraform module
700
+ try:
701
+ # If index is a number, format as INT_X
702
+ int(interface_index)
703
+ formatted_index = f"INT_{interface_index}"
704
+ except (ValueError, TypeError):
705
+ # If not a number or None, use as-is
706
+ formatted_index = interface_index if interface_index else interface_id
707
+
708
+ resource_address = f'{module_name}.module.socket-site["{site_name}"].module.lan_interfaces["{formatted_index}"].cato_lan_interface.interface["{formatted_index}"]'
647
709
 
648
- print(f"\n[{i+1}/{total_interfaces}] LAN Interface: {interface_name} on {site_name} (ID: {interface_id})")
710
+ print(f"\n[{i+1}/{total_interfaces}] LAN Interface: {interface_name} on {site_name} (Index: {interface_index}, ID: {interface_id})")
649
711
 
650
712
  success, stdout, stderr = run_terraform_import(resource_address, interface_id, verbose=verbose)
651
713
 
@@ -654,23 +716,21 @@ def import_lan_interfaces(lan_interfaces, module_name, verbose=False,
654
716
  else:
655
717
  failed_imports += 1
656
718
 
657
- # Ask user if they want to continue on failure (unless auto-approved)
658
- if failed_imports <= 3 and not auto_approve: # Only prompt for first few failures
719
+ if failed_imports <= 3 and not auto_approve:
659
720
  response = input(f"\nContinue with remaining imports? (y/n): ").lower()
660
721
  if response == 'n':
661
722
  print("Import process stopped by user.")
662
723
  break
663
724
 
664
- # Delay between batches
665
725
  if (i + 1) % batch_size == 0 and i < total_interfaces - 1:
666
726
  print(f"\n Batch complete. Waiting {delay_between_batches}s before next batch...")
667
727
  time.sleep(delay_between_batches)
668
728
 
669
- print(f"\nWAN Interface Import Summary: {successful_imports} successful, {failed_imports} failed")
729
+ print(f"\nLAN Interface Import Summary: {successful_imports} successful, {failed_imports} failed")
670
730
  return successful_imports, failed_imports
671
731
 
672
732
  def import_network_ranges(network_ranges, module_name, verbose=False,
673
- resource_type="cato_network_range", resource_name="network_ranges",
733
+ resource_type="cato_network_range", resource_name="network_range",
674
734
  batch_size=10, delay_between_batches=2, auto_approve=False):
675
735
  """Import all network ranges in batches"""
676
736
  print("\nStarting network range imports...")
@@ -688,19 +748,25 @@ def import_network_ranges(network_ranges, module_name, verbose=False,
688
748
  if not module_name.startswith('module.'):
689
749
  module_name = f'module.{module_name}'
690
750
 
691
- # Use correct resource addressing for network ranges
692
- # Based on terraform plan output, network ranges are structured as:
693
- # module.sites.module.socket-site["site_name"].module.lan_interfaces["interface_id"].module.network_range["subnet"].cato_network_range.no_dhcp[0]
694
- interface_id = network_range['interface_id']
695
- # Convert interface_id to the format used in terraform (e.g., "161570" -> "INT_5")
696
- if interface_id.isdigit():
697
- # This is a numeric interface ID, need to convert to INT_X format
698
- # For now, we'll use the interface_id as is and let terraform handle the mapping
699
- terraform_interface_id = interface_id
700
- else:
701
- terraform_interface_id = interface_id
751
+ # Use correct resource addressing for network ranges with interface_index-based addressing
752
+ # module.sites.module.socket-site["site_name"].module.lan_interfaces["interface_index"].module.network_ranges.module.network_range["range_key"].cato_network_range.no_dhcp[0]
753
+ interface_index = network_range['interface_index'] # Use interface index for addressing
754
+
755
+ # Apply the same index formatting logic as the Terraform module
756
+ try:
757
+ # If index is a number, format as INT_X
758
+ int(interface_index)
759
+ formatted_index = f"INT_{interface_index}"
760
+ except (ValueError, TypeError):
761
+ # If not a number or None, use as-is (fallback to interface_id if needed)
762
+ formatted_index = interface_index if interface_index else network_range['interface_id']
763
+
764
+ # Generate the same key format as the Terraform configuration:
765
+ # "${network_range.interface_index}-${replace(network_range.name, " ", "_")}"
766
+ sanitized_range_name = range_name.replace(" ", "_")
767
+ range_key = f"{formatted_index}-{sanitized_range_name}"
702
768
 
703
- resource_address = f'{module_name}.module.socket-site["{site_name}"].module.lan_interfaces["{terraform_interface_id}"].module.network_range["{network_range_id}"].cato_network_range.no_dhcp[0]'
769
+ resource_address = f'{module_name}.module.socket-site["{site_name}"].module.lan_interfaces["{formatted_index}"].module.network_ranges.module.network_range["{range_key}"].cato_network_range.no_dhcp[0]'
704
770
 
705
771
  print(f"\n[{i+1}/{total_ranges}] Network Range: {range_name} - {subnet} ({network_range_id}) on {site_name} (ID: {network_range_id})")
706
772
 
@@ -835,9 +901,14 @@ def import_socket_sites_to_tf(args, configuration):
835
901
  print(f" Loading data from {args.json_file}...")
836
902
  sites_data = load_json_data(args.json_file)
837
903
 
838
- # Extract sites, WAN interfaces, and network ranges
839
- sites, wan_interfaces, network_ranges = extract_socket_sites_data(sites_data)
840
-
904
+ # Extract sites, WAN interfaces, LAN interfaces, and network ranges
905
+ sites, wan_interfaces, lan_interfaces, network_ranges = extract_socket_sites_data(sites_data)
906
+ # print("\n==================== DEBUG =====================\n")
907
+ # print("sites",json.dumps( sites, indent=2))
908
+ # print("wan_interfaces",json.dumps( wan_interfaces, indent=2))
909
+ # print("lan_interfaces",json.dumps( lan_interfaces, indent=2))
910
+ # print("network_ranges",json.dumps( network_ranges, indent=2))
911
+ # print("\n==================== DEBUG =====================\n")
841
912
  if hasattr(args, 'verbose') and args.verbose:
842
913
  print(f"\nExtracted data summary:")
843
914
  print(f" Sites: {len(sites)}")
@@ -878,16 +949,25 @@ def import_socket_sites_to_tf(args, configuration):
878
949
  validate_terraform_environment(module_name, verbose=args.verbose)
879
950
 
880
951
  # Ask for confirmation (unless auto-approved)
952
+ # Determine which categories to import based on flags
953
+ sites_only = getattr(args, 'sites_only', False)
954
+ wan_only = getattr(args, 'wan_interfaces_only', False)
955
+ lan_only = getattr(args, 'lan_interfaces_only', False)
956
+ ranges_only = getattr(args, 'network_ranges_only', False)
957
+
881
958
  import_summary = []
882
- if not args.sites_only and not args.interfaces_only and not args.network_ranges_only:
959
+ if not (sites_only or wan_only or lan_only or ranges_only):
883
960
  import_summary.append(f"{len(sites)} sites")
884
961
  import_summary.append(f"{len(wan_interfaces)} WAN interfaces")
962
+ import_summary.append(f"{len(lan_interfaces)} LAN interfaces")
885
963
  import_summary.append(f"{len(network_ranges)} network ranges")
886
- elif args.sites_only:
964
+ elif sites_only:
887
965
  import_summary.append(f"{len(sites)} sites only")
888
- elif args.lan_interfaces_only:
966
+ elif wan_only:
967
+ import_summary.append(f"{len(wan_interfaces)} WAN interfaces only")
968
+ elif lan_only:
889
969
  import_summary.append(f"{len(lan_interfaces)} LAN interfaces only")
890
- elif args.network_ranges_only:
970
+ elif ranges_only:
891
971
  import_summary.append(f"{len(network_ranges)} network ranges only")
892
972
 
893
973
  print(f"\n Ready to import {', '.join(import_summary)}.")
@@ -903,8 +983,8 @@ def import_socket_sites_to_tf(args, configuration):
903
983
  total_successful = 0
904
984
  total_failed = 0
905
985
 
906
- # Import sites first (if not skipped)
907
- if not args.interfaces_only and not args.network_ranges_only and sites:
986
+ # Import sites first (if selected)
987
+ if (sites_only or not (wan_only or lan_only or ranges_only)) and sites:
908
988
  successful, failed = import_socket_sites(sites, module_name=args.module_name,
909
989
  verbose=args.verbose, batch_size=args.batch_size,
910
990
  delay_between_batches=args.delay,
@@ -912,8 +992,8 @@ def import_socket_sites_to_tf(args, configuration):
912
992
  total_successful += successful
913
993
  total_failed += failed
914
994
 
915
- # Import WAN interfaces (if not skipped)
916
- if not args.sites_only and not args.network_ranges_only and wan_interfaces:
995
+ # Import WAN interfaces (if selected)
996
+ if (wan_only or (not sites_only and not lan_only and not ranges_only)) and wan_interfaces:
917
997
  successful, failed = import_wan_interfaces(wan_interfaces, module_name=args.module_name,
918
998
  verbose=args.verbose, batch_size=args.batch_size,
919
999
  delay_between_batches=args.delay,
@@ -921,8 +1001,8 @@ def import_socket_sites_to_tf(args, configuration):
921
1001
  total_successful += successful
922
1002
  total_failed += failed
923
1003
 
924
- # Import LAN interfaces (if not skipped)
925
- if not args.sites_only and not args.network_ranges_only and lan_interfaces:
1004
+ # Import LAN interfaces (if selected)
1005
+ if (lan_only or (not sites_only and not wan_only and not ranges_only)) and lan_interfaces:
926
1006
  successful, failed = import_lan_interfaces(lan_interfaces, module_name=args.module_name,
927
1007
  verbose=args.verbose, batch_size=args.batch_size,
928
1008
  delay_between_batches=args.delay,
@@ -930,8 +1010,8 @@ def import_socket_sites_to_tf(args, configuration):
930
1010
  total_successful += successful
931
1011
  total_failed += failed
932
1012
 
933
- # Import network ranges (if not skipped)
934
- if not args.sites_only and not args.interfaces_only and network_ranges:
1013
+ # Import network ranges (if selected)
1014
+ if (ranges_only or (not sites_only and not wan_only and not lan_only)) and network_ranges:
935
1015
  successful, failed = import_network_ranges(network_ranges, module_name=args.module_name,
936
1016
  verbose=args.verbose, batch_size=args.batch_size,
937
1017
  delay_between_batches=args.delay,