illumio-pylo 0.3.10__py3-none-any.whl → 0.3.12__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.
@@ -2,6 +2,7 @@ from typing import Dict, List, Literal, Optional
2
2
  import datetime
3
3
  import click
4
4
  import argparse
5
+ import os
5
6
 
6
7
  import illumio_pylo as pylo
7
8
  from illumio_pylo import ExcelHeader
@@ -10,7 +11,7 @@ from .utils.misc import make_filename_with_timestamp
10
11
  from . import Command
11
12
 
12
13
  command_name = 'ven-duplicate-remover'
13
- objects_load_filter = ['labels']
14
+ objects_load_filter = ['labels', 'workloads']
14
15
 
15
16
 
16
17
  def fill_parser(parser: argparse.ArgumentParser):
@@ -37,8 +38,18 @@ def fill_parser(parser: argparse.ArgumentParser):
37
38
  parser.add_argument('--limit-number-of-deleted-workloads', '-l', type=int, default=None,
38
39
  help='Limit the number of workloads to be deleted, for a limited test run for example.')
39
40
 
41
+ # New option: don't delete if labels mismatch across duplicates
42
+ parser.add_argument('--do-not-delete-if-labels-mismatch', action='store_true',
43
+ help='Do not delete workloads for a duplicated hostname if the workloads do not all have the same set of labels')
44
+
45
+ # New option: ignore PCE online status and allow online workloads to be considered for deletion
46
+ parser.add_argument('--ignore-pce-online-status', action='store_true',
47
+ help='Bypass the logic that keeps online workloads; when set online workloads will be treated like offline ones for deletion decisions')
48
+
40
49
  parser.add_argument('--output-dir', '-o', type=str, required=False, default="output",
41
50
  help='Directory where to write the report file(s)')
51
+ parser.add_argument('--output-filename', type=str, default=None,
52
+ help='Write report to the specified file (or basename) instead of using the default timestamped filename. If multiple formats are requested, the provided path\'s extension will be replaced/added per format.')
42
53
 
43
54
 
44
55
  def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
@@ -55,11 +66,16 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
55
66
  arg_do_not_delete_if_last_heartbeat_is_more_recent_than = args['do_not_delete_if_last_heartbeat_is_more_recent_than']
56
67
  arg_override_pce_offline_timer_to = args['override_pce_offline_timer_to']
57
68
  arg_limit_number_of_deleted_workloads = args['limit_number_of_deleted_workloads']
58
- arg_report_output_dir: str = args['output_dir']
59
-
60
- output_file_prefix = make_filename_with_timestamp('ven-duplicate-removal_', arg_report_output_dir)
61
- output_file_csv = output_file_prefix + '.csv'
62
- output_file_excel = output_file_prefix + '.xlsx'
69
+ arg_ignore_pce_online_status = args['ignore_pce_online_status'] is True
70
+ arg_do_not_delete_if_labels_mismatch = args['do_not_delete_if_labels_mismatch'] is True
71
+ arg_report_output_dir: str = args['output_dir']
72
+
73
+ # Determine output filename behavior: user provided filename/basename or use timestamped prefix
74
+ arg_output_filename: Optional[str] = args.get('output_filename')
75
+ if arg_output_filename is None:
76
+ output_file_prefix = make_filename_with_timestamp('ven-duplicate-removal_', arg_report_output_dir)
77
+ else:
78
+ output_file_prefix = None
63
79
 
64
80
  csv_report_headers = pylo.ExcelHeaderSet([
65
81
  ExcelHeader(name='name', max_width=40),
@@ -88,54 +104,18 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
88
104
  raise pylo.PyloEx("Cannot find label '{}' in the PCE".format(label_name))
89
105
  filter_labels.append(label)
90
106
 
91
- # <editor-fold desc="Download workloads from PCE">
92
- if not pce_cache_was_used:
93
- print("* Downloading Workloads data from the PCE (it may take moment for large amounts of workloads) ... ", flush=True, end='')
94
- if args['filter_label'] is None:
95
- workloads_json = org.connector.objects_workload_get(async_mode=True, max_results=1000000)
96
- else:
97
- filter_labels_list_of_list: List[List[pylo.Label]] = []
98
- # convert filter_labels dict to an array of arrays
99
- for label_type, label_list in org.LabelStore.Utils.list_to_dict_by_type(filter_labels).items():
100
- filter_labels_list_of_list.append(label_list)
101
-
102
- # convert filter_labels_list_of_list to a matrix of all possibilities
103
- # example: [[a,b],[c,d]] becomes [[a,c],[a,d],[b,c],[b,d]]
104
- filter_labels_matrix = [[]]
105
- for label_list in filter_labels_list_of_list:
106
- new_matrix = []
107
- for label in label_list:
108
- for row in filter_labels_matrix:
109
- new_row = row.copy()
110
- new_row.append(label.href)
111
- new_matrix.append(new_row)
112
- filter_labels_matrix = new_matrix
113
-
114
- workloads_json = org.connector.objects_workload_get(async_mode=False, max_results=1000000, filter_by_label=filter_labels_matrix)
115
-
116
- org.WorkloadStore.load_workloads_from_json(workloads_json)
117
-
118
- print("OK! {} workloads loaded".format(org.WorkloadStore.count_workloads()))
119
- # </editor-fold>
120
-
121
- all_workloads: List[pylo.Workload] # the list of all workloads to be processed
122
-
123
- if pce_cache_was_used:
124
- # if some filters were used, let's apply them now
125
- print("* Filtering workloads loaded from cache based on their labels... ", end='', flush=True)
126
- # if some label filters were used, we will apply them at later stage
127
- all_workloads: List[pylo.Workload] = list((org.WorkloadStore.find_workloads_matching_all_labels(filter_labels)).values())
128
- print("OK! {} workloads left after filtering".format(len(all_workloads)))
129
- else:
130
- # filter was already applied during the download from the PCE
131
- all_workloads = org.WorkloadStore.workloads
107
+ # if some filters were used, let's apply them now
108
+ print("* Filtering workloads loaded based on their labels... ", end='', flush=True)
109
+ # if some label filters were used, we will apply them at later stage
110
+ all_workloads: List[pylo.Workload] = list((org.WorkloadStore.find_workloads_matching_all_labels(filter_labels)).values())
111
+ print("OK! {} workloads left after filtering".format(len(all_workloads)))
132
112
 
133
113
  def add_workload_to_report(workload: pylo.Workload, action: str):
134
114
  url_link_to_pce = workload.get_pce_ui_url()
135
115
  new_row = {
136
116
  'hostname': workload.hostname,
137
- 'online': workload.online,
138
- 'last_heartbeat': workload.ven_agent.get_last_heartbeat_date().strftime('%Y-%m-%d %H:%M'),
117
+ 'online': workload.online if not workload.unmanaged else 'UNMANAGED',
118
+ 'last_heartbeat': workload.ven_agent.get_last_heartbeat_date().strftime('%Y-%m-%d %H:%M') if not workload.unmanaged else 'UNMANAGED',
139
119
  'created_at': workload.created_at_datetime().strftime('%Y-%m-%d %H:%M'),
140
120
  'href': workload.href,
141
121
  'link_to_pce': url_link_to_pce,
@@ -147,9 +127,8 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
147
127
 
148
128
  sheet.add_line_from_object(new_row)
149
129
 
150
- duplicated_hostnames = DuplicateRecordManager(arg_override_pce_offline_timer_to)
151
-
152
130
  print(" * Looking for VEN with duplicated hostname(s)")
131
+ duplicated_hostnames = DuplicateRecordManager(arg_override_pce_offline_timer_to)
153
132
 
154
133
  for workload in all_workloads:
155
134
  if workload.deleted:
@@ -161,10 +140,12 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
161
140
 
162
141
  print(" * Found {} duplicated hostnames".format(duplicated_hostnames.count_duplicates()))
163
142
 
164
- delete_tracker = org.connector.new_tracker_workload_multi_delete()
143
+ delete_tracker = org.connector.new_tracker_workload_multi_delete() # tracker to handle deletions, it will be executed later
165
144
 
145
+ # Process each duplicated hostname record
166
146
  for dup_hostname, dup_record in duplicated_hostnames._records.items():
167
- if not dup_record.has_duplicates():
147
+
148
+ if not dup_record.has_duplicates(): # no duplicates, skip
168
149
  continue
169
150
 
170
151
  print(" - hostname '{}' has duplicates. ({} online, {} offline, {} unmanaged)".format(dup_hostname,
@@ -172,48 +153,88 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
172
153
  len(dup_record.offline),
173
154
  len(dup_record.unmanaged)))
174
155
 
175
- latest_created_workload = dup_record.find_latest_created_at()
176
- latest_heartbeat_workload = dup_record.find_latest_heartbeat()
177
- print(" - Latest created at {} and latest heartbeat at {}".format(latest_created_workload.created_at, latest_heartbeat_workload.ven_agent.get_last_heartbeat_date()))
178
-
179
- if dup_record.count_online() == 0:
180
- print(" - IGNORED: there is no VEN online")
181
- for wkl in dup_record.offline:
182
- add_workload_to_report(wkl, "ignored (no VEN online)")
183
- continue
184
-
185
- if dup_record.count_online() > 1:
186
- print(" - WARNING: there are more than 1 VEN online")
187
-
188
- # Don't delete online workloads but still show them in the report
189
- for wkl in dup_record.online:
190
- add_workload_to_report(wkl, "ignored (VEN is online)")
191
-
192
- for wkl in dup_record.offline:
193
- if arg_do_not_delete_the_most_recent_workload and wkl is latest_created_workload:
194
- print(" - IGNORED: wkl {}/{} is the most recent".format(wkl.get_name_stripped_fqdn(), wkl.href))
195
- add_workload_to_report(wkl, "ignored (it is the most recently created)")
196
- elif arg_do_not_delete_the_most_recently_heartbeating_workload and wkl is latest_heartbeat_workload:
197
- print(" - IGNORED: wkl {}/{} is the most recently heartbeating".format(wkl.get_name_stripped_fqdn(), wkl.href))
198
- add_workload_to_report(wkl, "ignored (it is the most recently heartbeating)")
199
- elif arg_do_not_delete_if_last_heartbeat_is_more_recent_than is not None and wkl.ven_agent.get_last_heartbeat_date() > datetime.datetime.now() - datetime.timedelta(days=arg_do_not_delete_if_last_heartbeat_is_more_recent_than):
200
- print(" - IGNORED: wkl {}/{} has a last heartbeat more recent than {} days".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_do_not_delete_if_last_heartbeat_is_more_recent_than))
201
- add_workload_to_report(wkl, "ignored (last heartbeat is more recent than {} days)".format(arg_do_not_delete_if_last_heartbeat_is_more_recent_than))
156
+ # If the new flag was passed, ensure all workloads under this duplicate record have identical labels
157
+ if arg_do_not_delete_if_labels_mismatch:
158
+ label_strings = set()
159
+ for wkl in dup_record.all:
160
+ # Use workload.get_labels_str() to produce a stable representation across label types
161
+ lbl_str = wkl.get_labels_str()
162
+ label_strings.add(lbl_str)
163
+
164
+ if len(label_strings) > 1:
165
+ print(" - IGNORED: workloads for hostname '{}' have mismatching labels".format(dup_hostname))
166
+ for wkl in dup_record.all:
167
+ add_workload_to_report(wkl, "ignored (labels mismatch)")
168
+ continue
169
+
170
+ if not dup_record.has_no_managed_workloads():
171
+ latest_created_workload = dup_record.find_latest_managed_created_at()
172
+ latest_heartbeat_workload = dup_record.find_latest_heartbeat()
173
+
174
+ print(" - Latest created at {} and latest heartbeat at {}".format(latest_created_workload.created_at, latest_heartbeat_workload.ven_agent.get_last_heartbeat_date()))
175
+
176
+ if not arg_ignore_pce_online_status and dup_record.count_online() == 0:
177
+ print(" - IGNORED: there is no VEN online")
178
+ for wkl in dup_record.offline:
179
+ add_workload_to_report(wkl, "ignored (no VEN online)")
180
+ continue
181
+
182
+ if dup_record.count_online() > 1:
183
+ print(" - WARNING: there are more than 1 VEN online")
184
+
185
+ # Don't delete online workloads but still show them in the report
186
+ if not arg_ignore_pce_online_status:
187
+ for wkl in dup_record.online:
188
+ add_workload_to_report(wkl, "ignored (VEN is online)")
189
+
190
+ # Build the list of candidate workloads to consider for deletion. If --ignore-pce-online-status
191
+ # is passed, include online workloads among the candidates.
192
+ if arg_ignore_pce_online_status:
193
+ deletion_candidates = list(dup_record.offline) + list(dup_record.online)
202
194
  else:
195
+ deletion_candidates = list(dup_record.offline)
196
+
197
+ for wkl in deletion_candidates:
198
+ if arg_do_not_delete_the_most_recent_workload and wkl is latest_created_workload:
199
+ print(" - IGNORED: wkl {}/{} is the most recent".format(wkl.get_name_stripped_fqdn(), wkl.href))
200
+ add_workload_to_report(wkl, "ignored (it is the most recently created)")
201
+ elif arg_do_not_delete_the_most_recently_heartbeating_workload and wkl is latest_heartbeat_workload:
202
+ print(" - IGNORED: wkl {}/{} is the most recently heartbeating".format(wkl.get_name_stripped_fqdn(), wkl.href))
203
+ add_workload_to_report(wkl, "ignored (it is the most recently heartbeating)")
204
+ elif arg_do_not_delete_if_last_heartbeat_is_more_recent_than is not None and wkl.ven_agent.get_last_heartbeat_date() > datetime.datetime.now() - datetime.timedelta(days=arg_do_not_delete_if_last_heartbeat_is_more_recent_than):
205
+ print(" - IGNORED: wkl {}/{} has a last heartbeat more recent than {} days".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_do_not_delete_if_last_heartbeat_is_more_recent_than))
206
+ add_workload_to_report(wkl, "ignored (last heartbeat is more recent than {} days)".format(arg_do_not_delete_if_last_heartbeat_is_more_recent_than))
207
+ else:
208
+ if arg_limit_number_of_deleted_workloads is not None and delete_tracker.count_entries() >= arg_limit_number_of_deleted_workloads:
209
+ print(" - IGNORED: wkl {}/{} because the limit of {} workloads to be deleted was reached".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_limit_number_of_deleted_workloads))
210
+ add_workload_to_report(wkl, "ignored (limit of {} workloads to be deleted was reached)".format(arg_limit_number_of_deleted_workloads))
211
+ else:
212
+ delete_tracker.add_workload(wkl)
213
+ print(" - added wkl {}/{} to the delete list".format(wkl.get_name_stripped_fqdn(), wkl.href))
214
+
215
+ for wkl in dup_record.unmanaged:
203
216
  if arg_limit_number_of_deleted_workloads is not None and delete_tracker.count_entries() >= arg_limit_number_of_deleted_workloads:
204
217
  print(" - IGNORED: wkl {}/{} because the limit of {} workloads to be deleted was reached".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_limit_number_of_deleted_workloads))
205
218
  add_workload_to_report(wkl, "ignored (limit of {} workloads to be deleted was reached)".format(arg_limit_number_of_deleted_workloads))
206
219
  else:
207
220
  delete_tracker.add_workload(wkl)
208
- print(" - added offline wkl {}/{} to the delete list".format(wkl.get_name_stripped_fqdn(), wkl.href))
209
-
210
- for wkl in dup_record.unmanaged:
211
- if arg_limit_number_of_deleted_workloads is not None and delete_tracker.count_entries() >= arg_limit_number_of_deleted_workloads:
212
- print(" - IGNORED: wkl {}/{} because the limit of {} workloads to be deleted was reached".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_limit_number_of_deleted_workloads))
213
- add_workload_to_report(wkl, "ignored (limit of {} workloads to be deleted was reached)".format(arg_limit_number_of_deleted_workloads))
214
- else:
215
- delete_tracker.add_workload(wkl)
216
- print(" - added unmanaged wkl {}/{} to the delete list".format(wkl.get_name_stripped_fqdn(), wkl.href))
221
+ print(" - added unmanaged wkl {}/{} to the delete list".format(wkl.get_name_stripped_fqdn(), wkl.href))
222
+ else:
223
+ latest_created_workload = dup_record.find_latest_unmanaged_created_at()
224
+ if latest_created_workload is None:
225
+ raise pylo.PyloEx("Internal error: cannot find the latest created unmanaged workload for hostname '{}'".format(dup_hostname))
226
+ print(" - All workloads are unmanaged. Latest created at {} will be kept".format(latest_created_workload.created_at))
227
+ for wkl in dup_record.unmanaged:
228
+ if wkl is latest_created_workload:
229
+ print(" - IGNORED: wkl {}/{} is the most recent".format(wkl.get_name_stripped_fqdn(), wkl.href))
230
+ add_workload_to_report(wkl, "ignored (it is the most recently created)")
231
+ else:
232
+ if arg_limit_number_of_deleted_workloads is not None and delete_tracker.count_entries() >= arg_limit_number_of_deleted_workloads:
233
+ print(" - IGNORED: wkl {}/{} because the limit of {} workloads to be deleted was reached".format(wkl.get_name_stripped_fqdn(), wkl.href, arg_limit_number_of_deleted_workloads))
234
+ add_workload_to_report(wkl, "ignored (limit of {} workloads to be deleted was reached)".format(arg_limit_number_of_deleted_workloads))
235
+ else:
236
+ delete_tracker.add_workload(wkl)
237
+ print(" - added unmanaged wkl {}/{} to the delete list".format(wkl.get_name_stripped_fqdn(), wkl.href))
217
238
 
218
239
  print()
219
240
 
@@ -239,7 +260,8 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
239
260
  for wkl in delete_tracker.workloads:
240
261
  add_workload_to_report(wkl, "TO BE DELETED (aborted by user)")
241
262
  else:
242
- print(" * Executing deletion requests ... ".format(output_file_csv), end='', flush=True)
263
+ # execute deletions
264
+ print(" * Executing deletion requests ... ", end='', flush=True)
243
265
  delete_tracker.execute(unpair_agents=True)
244
266
  print("DONE")
245
267
 
@@ -259,13 +281,29 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
259
281
  for wkl in delete_tracker.workloads:
260
282
  add_workload_to_report(wkl, "TO BE DELETED (no confirm option used)")
261
283
 
284
+ # if report is not empty, write it to disk
262
285
  if sheet.lines_count() >= 1:
263
286
  if len(report_wanted_format) < 1:
264
287
  print(" * No report format was specified, no report will be generated")
265
288
  else:
266
289
  sheet.reorder_lines(['hostname']) # sort by hostname for better readability
267
290
  for report_format in report_wanted_format:
268
- output_filename = output_file_prefix + '.' + report_format
291
+ # Choose output filename depending on whether user provided --output-filename
292
+ if arg_output_filename is None:
293
+ output_filename = output_file_prefix + '.' + report_format
294
+ else:
295
+ # If only one format requested, use the provided filename as-is
296
+ if len(report_wanted_format) == 1:
297
+ output_filename = arg_output_filename
298
+ else:
299
+ base = os.path.splitext(arg_output_filename)[0]
300
+ output_filename = base + '.' + report_format
301
+
302
+ # Ensure parent directory exists
303
+ output_directory = os.path.dirname(output_filename)
304
+ if output_directory:
305
+ os.makedirs(output_directory, exist_ok=True)
306
+
269
307
  print(" * Writing report file '{}' ... ".format(output_filename), end='', flush=True)
270
308
  if report_format == 'csv':
271
309
  sheet.write_to_csv(output_filename)
@@ -324,7 +362,12 @@ class DuplicateRecordManager:
324
362
  return True
325
363
  return False
326
364
 
327
- def find_latest_created_at(self) -> 'pylo.Workload':
365
+ def has_no_managed_workloads(self):
366
+ if len(self.offline) + len(self.online) == 0:
367
+ return True
368
+ return False
369
+
370
+ def find_latest_managed_created_at(self) -> Optional['pylo.Workload']:
328
371
  latest: Optional[pylo.Workload] = None
329
372
  for wkl in self.all:
330
373
  if wkl.unmanaged:
@@ -333,7 +376,16 @@ class DuplicateRecordManager:
333
376
  latest = wkl
334
377
  return latest
335
378
 
336
- def find_latest_heartbeat(self) -> 'pylo.Workload':
379
+ def find_latest_unmanaged_created_at(self) -> Optional['pylo.Workload']:
380
+ latest: Optional[pylo.Workload] = None
381
+ for wkl in self.all:
382
+ if not wkl.unmanaged:
383
+ continue
384
+ if latest is None or wkl.created_at > latest.created_at:
385
+ latest = wkl
386
+ return latest
387
+
388
+ def find_latest_heartbeat(self) -> Optional['pylo.Workload']:
337
389
  latest: Optional[pylo.Workload] = None
338
390
  for wkl in self.all:
339
391
  if wkl.unmanaged:
@@ -4,7 +4,10 @@ import os
4
4
  import sys
5
5
 
6
6
  # in case user wants to run this utility while having a version of pylo already installed
7
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
7
+ if __name__ == "__main__":
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
9
+ pass
10
+
8
11
  import illumio_pylo.cli
9
12
 
10
13
  illumio_pylo.cli.run()
@@ -4,7 +4,11 @@ import argparse
4
4
  import math
5
5
  from datetime import datetime
6
6
 
7
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
7
+ # in case user wants to run this utility while having a version of pylo already installed
8
+ if __name__ == "__main__":
9
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
10
+ pass
11
+
8
12
  import illumio_pylo as pylo
9
13
 
10
14
 
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: illumio_pylo
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: A set of tools and library for working with Illumio PCE
5
5
  Home-page: https://github.com/cpainchaud/pylo
6
6
  Author: Christophe Painchaud
7
7
  Author-email: shellescape@gmail.com
8
- License: Apache License
8
+ License: Apache License
9
9
  Version 2.0, January 2004
10
10
  http://www.apache.org/licenses/
11
11
 
@@ -185,13 +185,20 @@ Project-URL: Homepage, https://github.com/cpainchaud/pylo
185
185
  Project-URL: Issues, https://github.com/cpainchaud/pylo/issues
186
186
  Requires-Python: >=3.11
187
187
  License-File: LICENSE
188
- Requires-Dist: click ==8.1.7
189
- Requires-Dist: colorama ~=0.4.4
190
- Requires-Dist: cryptography ==43.0.1
191
- Requires-Dist: openpyxl ~=3.1.3
192
- Requires-Dist: paramiko ~=3.4.0
193
- Requires-Dist: prettytable ~=3.10.0
194
- Requires-Dist: requests ~=2.32.0
195
- Requires-Dist: xlsxwriter ~=3.2.0
188
+ Requires-Dist: click==8.1.7
189
+ Requires-Dist: colorama~=0.4.4
190
+ Requires-Dist: cryptography==44.0.1
191
+ Requires-Dist: openpyxl~=3.1.3
192
+ Requires-Dist: paramiko~=3.4.0
193
+ Requires-Dist: prettytable~=3.10.0
194
+ Requires-Dist: requests~=2.32.0
195
+ Requires-Dist: xlsxwriter~=3.2.0
196
+ Requires-Dist: flask~=2.2.0
197
+ Dynamic: author
198
+ Dynamic: author-email
199
+ Dynamic: description
200
+ Dynamic: home-page
201
+ Dynamic: license-file
202
+ Dynamic: requires-python
196
203
 
197
204
  README.md
@@ -1,7 +1,7 @@
1
1
  illumio_pylo/AgentStore.py,sha256=7xWpjGJHK5Xp69M-WzqcDeC1WMLCeMii1D_hh6xVqb4,5312
2
2
  illumio_pylo/Exception.py,sha256=3lxS-ANBaEvHAKhDb8UzNLj5IpQBmRHNs4YkHONmQjs,1033
3
- illumio_pylo/IPList.py,sha256=ozCAkU3ZHJtcm0O-f_7_L_a8C0ZIqVGAlhsQMS4LvD4,4394
4
- illumio_pylo/IPMap.py,sha256=FC7v-qA9TjSum8OMP0tx-auU-rDBO4wRNkx3irjTYf4,10259
3
+ illumio_pylo/IPList.py,sha256=0fPsX6TYUQmmyWMQpl-QsMll9JwpvEiGN6lmbUtBwDU,4614
4
+ illumio_pylo/IPMap.py,sha256=XrC-qvnTUWCVq38NCRDebDnXWQYCAyTRtdVX6fk9DiE,10575
5
5
  illumio_pylo/Label.py,sha256=hGeU3zQW8GIfRekSVcRAiTQbezbjYTF1nGHoH9DxvyY,834
6
6
  illumio_pylo/LabelCommon.py,sha256=fgmzE8tztufAXXzLgmOqo74z66e8uAyJYnM917e8wRE,1486
7
7
  illumio_pylo/LabelGroup.py,sha256=ZPC3xBLJUEhStyBTMB3fTTcEi8Z4CDjHUwnq_T-8p5Y,2674
@@ -21,30 +21,31 @@ illumio_pylo/VirtualServiceStore.py,sha256=MNTwo1cvteUuID6JniWUk5oRHQHGY4OwrWvFa
21
21
  illumio_pylo/Workload.py,sha256=k6s420rExq_Nb453f9UYjsLyNfxcCGuhgX6sPDBx9kU,19813
22
22
  illumio_pylo/WorkloadStore.py,sha256=3C6SMU0wRlet6C6UVbjkYNsTY7vkyK_ZwqM1dlCBpsQ,10989
23
23
  illumio_pylo/WorkloadStoreSubClasses.py,sha256=P9dUqT4Hb_tTCW14RmfJU3kVp5Zx9ZcfzeuD2VdsPDs,6101
24
- illumio_pylo/__init__.py,sha256=c0Ig7Y0er4mMXtKAVlEkmyKRU1MQ8chUrxzJ_NnwzAc,4173
24
+ illumio_pylo/__init__.py,sha256=V0HyPA1VXx4jie4Vb4dNQIa74v9betxHervNCy05B5o,4173
25
25
  illumio_pylo/tmp.py,sha256=8WSnsdgnLgVS2m4lxc6qCpCfefADV77B8MqiwdqFkos,3914
26
- illumio_pylo/API/APIConnector.py,sha256=jfvl3-b-GhgLEWZ2VxUnl27BhgUT888eQkOmPDfvabs,62620
26
+ illumio_pylo/API/APIConnector.py,sha256=Uf_uYwl7qtCI8qXm6r-S5itXta0X7aLFXmwy6S05uPo,64281
27
27
  illumio_pylo/API/AuditLog.py,sha256=p0mfjrE5S8qoJgA5LIP_XGFBP3iL86Nl6BQEmhMVrPA,1533
28
28
  illumio_pylo/API/ClusterHealth.py,sha256=GdMpVQrHUW4zLM-409GcPHM8H8b3LAppO37ZcUZyT_Q,5122
29
- illumio_pylo/API/CredentialsManager.py,sha256=z-a8O67UwPnbAEmjv_t8C9Wb0eXbSql1PVhLZ8IEUms,12854
30
- illumio_pylo/API/Explorer.py,sha256=fFAIF-67_uuKgJOP0eZPPJrOGuYmFl33GK75AyMjgJU,47590
31
- illumio_pylo/API/JsonPayloadTypes.py,sha256=T2BVpQwcVgChXAwCeXNSvbKO_i_CyT8Gk7oDsdlHXTA,8274
29
+ illumio_pylo/API/CredentialsManager.py,sha256=noIs8IuOIjh0Obt4KZ2I0O05YoBv-2_wqkxwdGTpKNU,14263
30
+ illumio_pylo/API/Explorer.py,sha256=fZQDkjbbJTYYOAuVqsbb7STJDNKFVj2U5LsF4yQN6TE,49201
31
+ illumio_pylo/API/JsonPayloadTypes.py,sha256=wrKMW3xSTM-PQk6ABUI4lNu-HAQMtnfaZ6FjGA1yypw,8963
32
32
  illumio_pylo/API/RuleSearchQuery.py,sha256=O0-MsUXhwmywoO0G-GXnLq6kkVC9LgmxMZwqVKc_oJE,5325
33
33
  illumio_pylo/API/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  illumio_pylo/Helpers/__init__.py,sha256=6E2eTZe-4qfPKGjRRQNtYsPrBhJSAjbdvv_DpniV_rY,49
35
- illumio_pylo/Helpers/exports.py,sha256=7wXWTRmjkfApgeweJ3gJ4qUXJLOK0xyA49cY_EkkkCg,21898
35
+ illumio_pylo/Helpers/exports.py,sha256=3lme5g1UZYSlEkvfT3gU5dj86ndX0NIGQVFsAw2nZWQ,21896
36
36
  illumio_pylo/Helpers/functions.py,sha256=WmLCJjAHPLqJH6WM7bKSz4ZbP2zDAFJ73FCoJZRTIJE,4635
37
37
  illumio_pylo/cli/NativeParsers.py,sha256=nzDL54EV0J-Iz0P9EkeiPl6DWQBSbCu-MpEPRad3j6c,4055
38
38
  illumio_pylo/cli/__init__.py,sha256=hgp7KG4FZujnRnId3A1tAoaYpUDFkE_Fv2wXyF02GNM,7737
39
39
  illumio_pylo/cli/__main__.py,sha256=ll1gK8k1YL_kPsImI7WVlw2sCyNyhocnuCqko6mGaYI,223
40
- illumio_pylo/cli/commands/__init__.py,sha256=yoVkXy-qBGiAAziWiayJdjcclx1WTayShXSPqHelcWA,1489
41
- illumio_pylo/cli/commands/credential_manager.py,sha256=YUaIqTxl9F7HXpxC7urU2tnwZq3UROV4riV_YMKSJso,9354
40
+ illumio_pylo/cli/commands/__init__.py,sha256=_M2qTOOfCN3FVnL_0j6CgfwYmatvobzwierJDtlVtz0,1537
41
+ illumio_pylo/cli/commands/credential_manager.py,sha256=v7Jjbf7iFiUndN7ScKz1Wz1UBMDasqM0myjiV5LpYP4,27209
42
42
  illumio_pylo/cli/commands/iplist_analyzer.py,sha256=OJB8hSDzLhICCkFF5NaRC3a0uCUSfaEDuJb2CLUJgu4,3709
43
43
  illumio_pylo/cli/commands/iplist_import_from_file.py,sha256=yqB6VvDT72VxC8tIyu2wRixorwzarhWF6t8J-MvLGlM,9836
44
+ illumio_pylo/cli/commands/label_delete_unused.py,sha256=KRJDppSQFonKVet9kjrPOaHUiIhahfjHMs92KAE9mYQ,3434
44
45
  illumio_pylo/cli/commands/ruleset_export.py,sha256=MsT-jd0BzA_cpZcvbKtkD1Dvwqt6_9F5Q1vp8-qfEo0,6080
45
46
  illumio_pylo/cli/commands/update_pce_objects_cache.py,sha256=vSVSA9K9mXQhfiQPLoH7uEcSz5j1JddQW6UGGYvydKQ,1255
46
47
  illumio_pylo/cli/commands/ven_compatibility_report_export.py,sha256=Hp58Z0cj-hX8tpcD9IyT0c2c36CutT9rrfhTfgx9sbI,8070
47
- illumio_pylo/cli/commands/ven_duplicate_remover.py,sha256=e2j1p04e6l4jZRsk0d1IlLVwCfGlvpas6_aG3ZzLccc,19501
48
+ illumio_pylo/cli/commands/ven_duplicate_remover.py,sha256=zPLbuZ3m2sFP6IbeZeUGM3R63K-ih8Db3C6H-JSqPNo,23397
48
49
  illumio_pylo/cli/commands/ven_idle_to_visibility.py,sha256=ez8NbryK_I7motzkhLWHOdKl0Uh-zCkO4ASD1exsOkY,13352
49
50
  illumio_pylo/cli/commands/ven_upgrader.py,sha256=h0XCcI-kMzq9GC-ovIph7r0-BObo_4GHdyuMnVfRYZA,7158
50
51
  illumio_pylo/cli/commands/workload_export.py,sha256=EcQR8AacJVe7rOqYH-HFxyTchwHHQ9ZTc-ALSMt9gDY,11421
@@ -52,21 +53,24 @@ illumio_pylo/cli/commands/workload_import.py,sha256=mpcHISn5qxM13eSYCWusQRvkkNuZ
52
53
  illumio_pylo/cli/commands/workload_reset_names_to_null.py,sha256=j87jbnDxT3kABlqCHlqVd1_U1mfV7y0mgFwEEdFmt0Q,3331
53
54
  illumio_pylo/cli/commands/workload_update.py,sha256=nPlSCOAx0wmshVWy4WNp-15wwg-IWcPPQy66AWEDTic,28659
54
55
  illumio_pylo/cli/commands/workload_used_in_rule_finder.py,sha256=35t_HpAw_gk9SjmNoyPI3eyZT6doPhqcFF6XkuzbNII,3340
56
+ illumio_pylo/cli/commands/ui/credential_manager_ui/app.js,sha256=FROWIE-nIGv1X47hDgpL18t6lhgmc2kMNA8gIVbqdwQ,17221
57
+ illumio_pylo/cli/commands/ui/credential_manager_ui/index.html,sha256=MOfSDkGvq8zpGdWfU9d-aPfMfdYt3BfQ2yaQ-MF8q9Q,7501
58
+ illumio_pylo/cli/commands/ui/credential_manager_ui/styles.css,sha256=A8t4r1-rW5iEMyHT0ligUVWXwFgemR_5ONmsTPvBdB4,6765
55
59
  illumio_pylo/cli/commands/utils/LabelCreation.py,sha256=cO_MWJrAIgeZGZrm0Dix50isrGzhckZ_kLnjy1VWWRI,4885
56
60
  illumio_pylo/cli/commands/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
61
  illumio_pylo/cli/commands/utils/misc.py,sha256=dsFHrmCaQ1OrRGms8dHaHtE_rTJ0j4-Q3JXcFCKNC_I,775
58
62
  illumio_pylo/docs/Doxygen,sha256=AVvSIRYLHFWJ15YLGahhzhsW0ZUUFO5lVxd2-F3iWz8,74257
59
63
  illumio_pylo/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- illumio_pylo/utilities/cli.py,sha256=7Wv7qhdc91-vsaxiu4j8k7LY7V9dcwWHnHV7PLpOBAI,320
64
+ illumio_pylo/utilities/cli.py,sha256=AJl9NaMzAfKbEHwVy96bvXAZMV_jdcwvpUodWEEgoDQ,361
61
65
  illumio_pylo/utilities/credentials.example.json,sha256=CZcp3aAbdVljfyQzFbgxIZCU9Ln2eWZKfWcmN9VAeVc,439
62
- illumio_pylo/utilities/health_monitoring.py,sha256=sqe9gArMZm3s8y6Jg6F9SO_Rm2dj1aM99xpPi8WSFZc,3700
66
+ illumio_pylo/utilities/health_monitoring.py,sha256=_8IsDvOGxoqMvZhN8GQwHqrRb5qkh_x4ccENuECe19E,3831
63
67
  illumio_pylo/utilities/resources/iplists-import-example.csv,sha256=beM9OBWJQNiYXlGh1KYiHxCN8LpZk4uPpoiO14-CgOE,200
64
68
  illumio_pylo/utilities/resources/iplists-import-example.xlsx,sha256=VW-7CRr8NA2Vultv9jLGd8-_jzVp5ZtL3KgswjOUHeQ,16893
65
69
  illumio_pylo/utilities/resources/workload-exporter-filter-example.csv,sha256=cn5IA8AGEPjvS-EsPXA_GJ-LFsdF9t_44rSzFTCmAzE,36
66
70
  illumio_pylo/utilities/resources/workloads-import-example.csv,sha256=HBZj5e1TFnJTzNrcgO3ZVzqAwqBoyGABJtd9Y8W8j7g,115
67
71
  illumio_pylo/utilities/resources/workloads-import-example.xlsx,sha256=B5LRCFjqkZ0xbGel7qlmLepmhAysoq1Cu9eVFUiZLq0,17191
68
- illumio_pylo-0.3.10.dist-info/LICENSE,sha256=WYmcYJG1QFgu1hfo7qrEkZ3Jhcz8NUWe6XUraZvlIFs,10172
69
- illumio_pylo-0.3.10.dist-info/METADATA,sha256=jX3HP9RyjinYW5Sl5MlT__Aowb5av-6UTCLBjH2HkjE,12225
70
- illumio_pylo-0.3.10.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
71
- illumio_pylo-0.3.10.dist-info/top_level.txt,sha256=c5cu_ZMuSuxjq48ih58Kc0Tr8-JdQtV8GrKJicvWNFE,13
72
- illumio_pylo-0.3.10.dist-info/RECORD,,
72
+ illumio_pylo-0.3.12.dist-info/licenses/LICENSE,sha256=WYmcYJG1QFgu1hfo7qrEkZ3Jhcz8NUWe6XUraZvlIFs,10172
73
+ illumio_pylo-0.3.12.dist-info/METADATA,sha256=QnZMdnCzrIt0_B_2qoO0w8uyqU2hvDWeOWrhGuece7M,12403
74
+ illumio_pylo-0.3.12.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
75
+ illumio_pylo-0.3.12.dist-info/top_level.txt,sha256=c5cu_ZMuSuxjq48ih58Kc0Tr8-JdQtV8GrKJicvWNFE,13
76
+ illumio_pylo-0.3.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5