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.
- catocli/Utils/clidriver.py +18 -18
- catocli/Utils/cliutils.py +165 -0
- catocli/Utils/csv_formatter.py +652 -0
- catocli/__init__.py +1 -1
- catocli/parsers/custom/export_rules/__init__.py +0 -4
- catocli/parsers/custom/export_sites/__init__.py +4 -3
- catocli/parsers/custom/export_sites/export_sites.py +198 -55
- catocli/parsers/custom/import_sites_to_tf/import_sites_to_tf.py +473 -393
- catocli/parsers/customParserApiClient.py +444 -38
- catocli/parsers/custom_private/__init__.py +19 -13
- catocli/parsers/mutation_accountManagement/__init__.py +21 -0
- catocli/parsers/mutation_accountManagement_disableAccount/README.md +15 -0
- catocli/parsers/mutation_admin/__init__.py +12 -0
- catocli/parsers/mutation_container/__init__.py +18 -0
- catocli/parsers/mutation_enterpriseDirectory/__init__.py +8 -0
- catocli/parsers/mutation_groups/__init__.py +6 -0
- catocli/parsers/mutation_hardware/__init__.py +2 -0
- catocli/parsers/mutation_policy/__init__.py +378 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_addRule/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_addSection/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_createPolicyRevision/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_discardPolicyRevision/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_moveRule/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_moveSection/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_publishPolicyRevision/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_removeRule/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_removeSection/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_updatePolicy/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_updateRule/README.md +20 -0
- catocli/parsers/mutation_policy_antiMalwareFileHash_updateSection/README.md +20 -0
- catocli/parsers/mutation_sandbox/__init__.py +4 -0
- catocli/parsers/mutation_site/__init__.py +72 -0
- catocli/parsers/mutation_sites/__init__.py +72 -0
- catocli/parsers/mutation_xdr/__init__.py +6 -0
- catocli/parsers/query_accountBySubdomain/__init__.py +2 -0
- catocli/parsers/query_accountManagement/__init__.py +2 -0
- catocli/parsers/query_accountMetrics/__init__.py +6 -0
- catocli/parsers/query_accountRoles/__init__.py +2 -0
- catocli/parsers/query_accountSnapshot/__init__.py +2 -0
- catocli/parsers/query_admin/__init__.py +2 -0
- catocli/parsers/query_admins/__init__.py +2 -0
- catocli/parsers/query_appStats/__init__.py +6 -0
- catocli/parsers/query_appStatsTimeSeries/README.md +3 -0
- catocli/parsers/query_appStatsTimeSeries/__init__.py +6 -0
- catocli/parsers/query_auditFeed/__init__.py +2 -0
- catocli/parsers/query_catalogs/__init__.py +2 -0
- catocli/parsers/query_container/__init__.py +2 -0
- catocli/parsers/query_devices/README.md +1 -1
- catocli/parsers/query_devices/__init__.py +2 -0
- catocli/parsers/query_enterpriseDirectory/__init__.py +2 -0
- catocli/parsers/query_entityLookup/__init__.py +2 -0
- catocli/parsers/query_events/__init__.py +2 -0
- catocli/parsers/query_eventsFeed/__init__.py +2 -0
- catocli/parsers/query_eventsTimeSeries/__init__.py +2 -0
- catocli/parsers/query_groups/__init__.py +6 -0
- catocli/parsers/query_hardware/README.md +1 -1
- catocli/parsers/query_hardware/__init__.py +2 -0
- catocli/parsers/query_hardwareManagement/__init__.py +2 -0
- catocli/parsers/query_licensing/__init__.py +2 -0
- catocli/parsers/query_policy/__init__.py +37 -0
- catocli/parsers/query_policy_antiMalwareFileHash_policy/README.md +19 -0
- catocli/parsers/query_popLocations/__init__.py +2 -0
- catocli/parsers/query_sandbox/__init__.py +2 -0
- catocli/parsers/query_servicePrincipalAdmin/__init__.py +2 -0
- catocli/parsers/query_site/__init__.py +33 -0
- catocli/parsers/query_siteLocation/__init__.py +2 -0
- catocli/parsers/query_site_siteGeneralDetails/README.md +19 -0
- catocli/parsers/query_socketPortMetrics/__init__.py +2 -0
- catocli/parsers/query_socketPortMetricsTimeSeries/__init__.py +6 -0
- catocli/parsers/query_subDomains/__init__.py +2 -0
- catocli/parsers/query_xdr/__init__.py +4 -0
- catocli/parsers/raw/__init__.py +3 -1
- {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/METADATA +1 -1
- {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/RECORD +98 -66
- models/mutation.accountManagement.disableAccount.json +545 -0
- models/mutation.policy.antiMalwareFileHash.addRule.json +2068 -0
- models/mutation.policy.antiMalwareFileHash.addSection.json +1350 -0
- models/mutation.policy.antiMalwareFileHash.createPolicyRevision.json +1822 -0
- models/mutation.policy.antiMalwareFileHash.discardPolicyRevision.json +1758 -0
- models/mutation.policy.antiMalwareFileHash.moveRule.json +1552 -0
- models/mutation.policy.antiMalwareFileHash.moveSection.json +1251 -0
- models/mutation.policy.antiMalwareFileHash.publishPolicyRevision.json +1813 -0
- models/mutation.policy.antiMalwareFileHash.removeRule.json +1204 -0
- models/mutation.policy.antiMalwareFileHash.removeSection.json +954 -0
- models/mutation.policy.antiMalwareFileHash.updatePolicy.json +1834 -0
- models/mutation.policy.antiMalwareFileHash.updateRule.json +1757 -0
- models/mutation.policy.antiMalwareFileHash.updateSection.json +1105 -0
- models/mutation.site.updateSiteGeneralDetails.json +3 -3
- models/mutation.sites.updateSiteGeneralDetails.json +3 -3
- models/query.devices.json +249 -2
- models/query.hardware.json +224 -0
- models/query.policy.antiMalwareFileHash.policy.json +1583 -0
- models/query.site.siteGeneralDetails.json +899 -0
- schema/catolib.py +52 -14
- {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/WHEEL +0 -0
- {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/entry_points.txt +0 -0
- {catocli-2.1.2.dist-info → catocli-2.1.4.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
75
|
-
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
|
-
|
|
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':
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
106
|
-
'
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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':
|
|
129
|
-
'
|
|
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':
|
|
133
|
-
'vlan_tag': network_range.get('vlanTag', ''),
|
|
134
|
-
'range_type': 'VLAN' if network_range.get('vlanTag')
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
212
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
271
|
+
# if success:
|
|
272
|
+
# successful_imports += 1
|
|
273
|
+
# else:
|
|
274
|
+
# failed_imports += 1
|
|
218
275
|
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
382
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
441
|
+
# if success:
|
|
442
|
+
# successful_imports += 1
|
|
443
|
+
# else:
|
|
444
|
+
# failed_imports += 1
|
|
388
445
|
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
473
|
+
# success, stdout, stderr = run_terraform_import(resource_address, rule_id, verbose=verbose)
|
|
417
474
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
475
|
+
# if success:
|
|
476
|
+
# successful_imports += 1
|
|
477
|
+
# else:
|
|
478
|
+
# failed_imports += 1
|
|
422
479
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
436
|
-
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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="
|
|
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
|
-
#
|
|
589
|
-
#
|
|
590
|
-
|
|
591
|
-
resource_address = f'{module_name}.module.socket-site["{site_name}"].cato_wan_interface.wan["{
|
|
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
|
|
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
|
|
652
|
+
import_id = interface_id
|
|
597
653
|
else:
|
|
598
|
-
import_id = f"{site_id}:{interface_id}"
|
|
599
|
-
print(f"\n[{i+1}/{total_interfaces}] WAN Interface: {interface_name} on {site_name} (
|
|
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
|
-
|
|
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="
|
|
678
|
+
resource_type="cato_lan_interface", resource_name="interface",
|
|
625
679
|
batch_size=10, delay_between_batches=2, auto_approve=False):
|
|
626
|
-
"""Import all
|
|
627
|
-
print("\nStarting
|
|
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
|
-
#
|
|
644
|
-
#
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
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"\
|
|
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="
|
|
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
|
-
#
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
#
|
|
696
|
-
|
|
697
|
-
#
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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["{
|
|
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
|
|
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
|
|
964
|
+
elif sites_only:
|
|
887
965
|
import_summary.append(f"{len(sites)} sites only")
|
|
888
|
-
elif
|
|
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
|
|
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
|
|
907
|
-
if not
|
|
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
|
|
916
|
-
if not
|
|
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
|
|
925
|
-
if not
|
|
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
|
|
934
|
-
if not
|
|
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,
|