illumio-pylo 0.2.5__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.
Files changed (73) hide show
  1. illumio_pylo/API/APIConnector.py +1308 -0
  2. illumio_pylo/API/AuditLog.py +42 -0
  3. illumio_pylo/API/ClusterHealth.py +136 -0
  4. illumio_pylo/API/CredentialsManager.py +286 -0
  5. illumio_pylo/API/Explorer.py +1077 -0
  6. illumio_pylo/API/JsonPayloadTypes.py +240 -0
  7. illumio_pylo/API/RuleSearchQuery.py +128 -0
  8. illumio_pylo/API/__init__.py +0 -0
  9. illumio_pylo/AgentStore.py +139 -0
  10. illumio_pylo/Exception.py +44 -0
  11. illumio_pylo/Helpers/__init__.py +3 -0
  12. illumio_pylo/Helpers/exports.py +508 -0
  13. illumio_pylo/Helpers/functions.py +166 -0
  14. illumio_pylo/IPList.py +135 -0
  15. illumio_pylo/IPMap.py +285 -0
  16. illumio_pylo/Label.py +25 -0
  17. illumio_pylo/LabelCommon.py +48 -0
  18. illumio_pylo/LabelGroup.py +68 -0
  19. illumio_pylo/LabelStore.py +403 -0
  20. illumio_pylo/LabeledObject.py +25 -0
  21. illumio_pylo/Organization.py +258 -0
  22. illumio_pylo/Query.py +331 -0
  23. illumio_pylo/ReferenceTracker.py +41 -0
  24. illumio_pylo/Rule.py +671 -0
  25. illumio_pylo/Ruleset.py +306 -0
  26. illumio_pylo/RulesetStore.py +101 -0
  27. illumio_pylo/SecurityPrincipal.py +62 -0
  28. illumio_pylo/Service.py +256 -0
  29. illumio_pylo/SoftwareVersion.py +125 -0
  30. illumio_pylo/VirtualService.py +17 -0
  31. illumio_pylo/VirtualServiceStore.py +75 -0
  32. illumio_pylo/Workload.py +506 -0
  33. illumio_pylo/WorkloadStore.py +289 -0
  34. illumio_pylo/__init__.py +82 -0
  35. illumio_pylo/cli/NativeParsers.py +96 -0
  36. illumio_pylo/cli/__init__.py +134 -0
  37. illumio_pylo/cli/__main__.py +10 -0
  38. illumio_pylo/cli/commands/__init__.py +32 -0
  39. illumio_pylo/cli/commands/credential_manager.py +168 -0
  40. illumio_pylo/cli/commands/iplist_import_from_file.py +185 -0
  41. illumio_pylo/cli/commands/misc.py +7 -0
  42. illumio_pylo/cli/commands/ruleset_export.py +129 -0
  43. illumio_pylo/cli/commands/update_pce_objects_cache.py +44 -0
  44. illumio_pylo/cli/commands/ven_duplicate_remover.py +366 -0
  45. illumio_pylo/cli/commands/ven_idle_to_visibility.py +287 -0
  46. illumio_pylo/cli/commands/ven_upgrader.py +226 -0
  47. illumio_pylo/cli/commands/workload_export.py +251 -0
  48. illumio_pylo/cli/commands/workload_import.py +423 -0
  49. illumio_pylo/cli/commands/workload_relabeler.py +510 -0
  50. illumio_pylo/cli/commands/workload_reset_names_to_null.py +83 -0
  51. illumio_pylo/cli/commands/workload_used_in_rule_finder.py +80 -0
  52. illumio_pylo/docs/Doxygen +1757 -0
  53. illumio_pylo/tmp.py +104 -0
  54. illumio_pylo/utilities/__init__.py +0 -0
  55. illumio_pylo/utilities/cli.py +10 -0
  56. illumio_pylo/utilities/credentials.example.json +20 -0
  57. illumio_pylo/utilities/explorer_report_exporter.py +86 -0
  58. illumio_pylo/utilities/health_monitoring.py +102 -0
  59. illumio_pylo/utilities/iplist_analyzer.py +148 -0
  60. illumio_pylo/utilities/iplists_stats_duplicates_unused_finder.py +75 -0
  61. illumio_pylo/utilities/resources/iplists-import-example.csv +3 -0
  62. illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  63. illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +3 -0
  64. illumio_pylo/utilities/resources/workloads-import-example.csv +2 -0
  65. illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  66. illumio_pylo/utilities/ven_compatibility_report_export.py +240 -0
  67. illumio_pylo/utilities/ven_idle_to_illumination.py +344 -0
  68. illumio_pylo/utilities/ven_reassign_pce.py +183 -0
  69. illumio_pylo-0.2.5.dist-info/LICENSE +176 -0
  70. illumio_pylo-0.2.5.dist-info/METADATA +197 -0
  71. illumio_pylo-0.2.5.dist-info/RECORD +73 -0
  72. illumio_pylo-0.2.5.dist-info/WHEEL +5 -0
  73. illumio_pylo-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,506 @@
1
+ from typing import Optional, List, Union
2
+
3
+ import illumio_pylo as pylo
4
+ from illumio_pylo.API.JsonPayloadTypes import WorkloadObjectJsonStructure
5
+ from illumio_pylo import log
6
+ from .AgentStore import VENAgent
7
+ from .Helpers import *
8
+ from .Exception import PyloEx
9
+ from .Label import Label
10
+ from .ReferenceTracker import ReferenceTracker, Referencer
11
+ from .IPMap import IP4Map
12
+ from .LabeledObject import LabeledObject
13
+
14
+
15
+ class WorkloadInterface:
16
+ def __init__(self, owner: 'pylo.Workload', name: str, ip: Optional[str], network: str, gateway: str, ignored: bool):
17
+ self.owner: Workload = owner
18
+ self.name: str = name
19
+ self.network: str = network
20
+ self.gateway: str = gateway
21
+ self.is_ignored: bool = ignored
22
+ self.ip: Optional[str] = ip
23
+ if ip is not None and len(ip) == 0:
24
+ self.ip = None
25
+
26
+
27
+ class WorkloadApiUpdateStack:
28
+ def __init__(self):
29
+ self.json_payload = {}
30
+
31
+ def add_payload(self, data: Dict[str, Any]):
32
+ for prop_name, prop_value in data.items():
33
+ self.json_payload[prop_name] = prop_value
34
+
35
+ def get_payload_and_reset(self) -> Dict[str, Any]:
36
+ data = self.json_payload
37
+ self.json_payload = {}
38
+ return data
39
+
40
+ def count_payloads(self) -> int:
41
+ return len(self.json_payload)
42
+
43
+
44
+ class Workload(pylo.ReferenceTracker, pylo.Referencer, LabeledObject):
45
+
46
+ def __init__(self, name: str, href: str, owner: 'pylo.WorkloadStore'):
47
+ ReferenceTracker.__init__(self)
48
+ Referencer.__init__(self)
49
+ LabeledObject.__init__(self)
50
+ self.owner = owner
51
+ self.name: str = name
52
+ self.href: str = href
53
+ self.forced_name: Optional[str] = None
54
+ self.hostname: Optional[str] = None
55
+
56
+ self.description: Optional[str] = None
57
+ self.interfaces: List[WorkloadInterface] = []
58
+
59
+ self.online = False
60
+
61
+ self.os_id: Optional[str] = None
62
+ self.os_detail: Optional[str] = None
63
+
64
+ self.ven_agent: Optional[VENAgent] = None
65
+
66
+ self.unmanaged = True
67
+
68
+ self.temporary = False
69
+ self.deleted = False
70
+
71
+ self.raw_json: Optional[WorkloadObjectJsonStructure] = None
72
+
73
+ self._batch_update_stack: Optional[WorkloadApiUpdateStack] = None
74
+
75
+ @property
76
+ def loc_label(self) -> Optional['pylo.Label']:
77
+ """ @deprecated use get_label() instead """
78
+ return self.get_label('loc')
79
+
80
+ @property
81
+ def env_label(self) -> Optional['pylo.Label']:
82
+ """ @deprecated use get_label() instead """
83
+ return self.get_label('env')
84
+
85
+ @property
86
+ def app_label(self) -> Optional['pylo.Label']:
87
+ """ @deprecated use get_label() instead """
88
+ return self.get_label('app')
89
+
90
+ @property
91
+ def role_label(self) -> Optional['pylo.Label']:
92
+ """ @deprecated use get_label() instead """
93
+ return self.get_label('role')
94
+
95
+ def load_from_json(self, data):
96
+ """
97
+ Parse and build workload properties from a PCE API JSON payload. Should be used internally by this library only.
98
+ """
99
+ label_store = self.owner.owner.LabelStore # forced_name quick access
100
+
101
+ self.raw_json = data
102
+
103
+ self.forced_name = data['name']
104
+
105
+ self.hostname = data['hostname']
106
+
107
+ self.deleted = data['deleted']
108
+
109
+ agent_json = data.get('agent')
110
+
111
+ if agent_json is None:
112
+ raise PyloEx("Workload named '%s' has no Agent record:\n%s" % (
113
+ self.name, nice_json(data)))
114
+
115
+ agent_href = agent_json.get('href')
116
+ if agent_href is None:
117
+ self.unmanaged = True
118
+ else:
119
+ self.unmanaged = False
120
+ self.ven_agent = self.owner.owner.AgentStore.create_ven_agent_from_workload_record(self, agent_json)
121
+ self.online = data['online']
122
+ self.os_id = data.get('os_id')
123
+ #if self.os_id is None:
124
+ # raise PyloEx("Workload named '{}' has no os_id record:\n%s".format(self.name), data)
125
+ self.os_detail = data.get('os_detail')
126
+ #if self.os_detail is None:
127
+ # raise PyloEx("Workload named '{}' has no os_detail record:\n%s".format(self.name), data)
128
+
129
+ self.description = data.get('description')
130
+
131
+ ignored_interfaces_index = {}
132
+ ignored_interfaces_json = data.get('ignored_interface_names')
133
+
134
+ if ignored_interfaces_json is not None:
135
+ for interface_name in ignored_interfaces_json:
136
+ ignored_interfaces_index[interface_name] = True
137
+
138
+ interfaces_json = data.get('interfaces')
139
+ if interfaces_json is not None:
140
+ for interface_json in interfaces_json:
141
+ if_object = WorkloadInterface(self, interface_json.get('name'), interface_json.get('address'),
142
+ interface_json.get('cidr_block'), interface_json.get('default_gateway_address'),
143
+ ignored=interface_json.get('name') in ignored_interfaces_index)
144
+ self.interfaces.append(if_object)
145
+
146
+ if 'labels' in data:
147
+ labels = data['labels']
148
+ for label in labels:
149
+ if 'href' not in label:
150
+ raise PyloEx("Workload named '%s' has labels in JSON but without any HREF:\n%s" % (
151
+ self.get_name(), nice_json(labels)))
152
+ href = label['href']
153
+ label_object = label_store.find_by_href(href)
154
+ if label_object is None:
155
+ if not self.deleted:
156
+ raise pylo.PyloObjectNotFound(
157
+ "Workload '%s'/'%s' is referencing label href '%s' which does not exist" % (
158
+ self.name, self.href, href))
159
+
160
+ self.set_label(label_object)
161
+ # print("Workload '%s'/'%s' is referencing label '%s'/'%s'" % (self.name, self.hostname, label_object.type, label_object.name))
162
+
163
+ label_object.add_reference(self)
164
+
165
+ def interfaces_to_string(self, separator: str = ',', show_ignored: bool = True, show_interface_name: bool = True) -> str:
166
+ """
167
+ Conveniently outputs all interface of this Workload to a string.
168
+
169
+ :param separator: string used to separate each interface in the string
170
+ :param show_ignored: whether or not ignored interfaces should be showing
171
+ :param show_interface_name:
172
+ :return: string with interfaces split by specified separator
173
+ """
174
+ tmp = []
175
+
176
+ for interface in self.interfaces:
177
+ if not show_ignored and interface.is_ignored:
178
+ continue
179
+ if show_interface_name:
180
+ tmp.append('{}:{}'.format(interface.name, interface.ip if interface.ip is not None else 'UnknownIP'))
181
+ else:
182
+ tmp.append(interface.ip if interface.ip is not None else 'UnknownIP')
183
+
184
+ return pylo.string_list_to_text(tmp, separator)
185
+
186
+ def get_ip4map_from_interfaces(self) -> pylo.IP4Map:
187
+ """
188
+ Calculate and return a map of all IP4 covered by the Workload interfaces
189
+ """
190
+ result = IP4Map()
191
+
192
+ for interface in self.interfaces:
193
+ if interface.ip is not None:
194
+ result.add_from_text(interface.ip)
195
+
196
+ return result
197
+
198
+ @property
199
+ def created_at(self) -> str:
200
+ return self.raw_json['created_at']
201
+
202
+ def is_using_label(self, label: Union['pylo.Label', 'pylo.LabelGroup']) -> bool:
203
+ """
204
+ Check if a label is used by this Workload
205
+ :param label: label to check for usage. If it's a label group then it will check that at least one label of the group is used
206
+ :return: true if label is used by this workload
207
+ """
208
+
209
+ # check for label class
210
+ if isinstance(label, pylo.Label):
211
+ if self.loc_label is label or self.env_label is label \
212
+ or self.app_label is label or self.app_label is label:
213
+ return True
214
+ else:
215
+ for member_label in label.get_members().values():
216
+ if self.is_using_label(member_label):
217
+ return True
218
+ return False
219
+
220
+ def api_update_description(self, new_description: str):
221
+ data = {'description': new_description}
222
+ if self._batch_update_stack is None:
223
+ connector = pylo.find_connector_or_die(self.owner)
224
+ connector.objects_workload_update(self.href, data=data)
225
+ else:
226
+ self._batch_update_stack.add_payload(data)
227
+ self.description = new_description
228
+
229
+ def api_update_hostname(self, new_hostname: str):
230
+ data = {'hostname': new_hostname}
231
+ if self._batch_update_stack is None:
232
+ connector = pylo.find_connector_or_die(self.owner)
233
+ connector.objects_workload_update(self.href, data=data)
234
+ else:
235
+ self._batch_update_stack.add_payload(data)
236
+
237
+ self.raw_json.update(data)
238
+ self.hostname = new_hostname
239
+
240
+ def api_update_forced_name(self, name: str):
241
+
242
+ data = {'name': name}
243
+ if self._batch_update_stack is None:
244
+ connector = pylo.find_connector_or_die(self.owner)
245
+ connector.objects_workload_update(self.href, data=data)
246
+ else:
247
+ self._batch_update_stack.add_payload(data)
248
+
249
+ self.raw_json.update(data)
250
+ self.forced_name = name
251
+
252
+ def api_update_labels(self, list_of_labels: Optional[List[Label]] = None, missing_label_type_means_no_change=False):
253
+ """
254
+ Push Workload's assigned Labels to the PCE.
255
+
256
+ :param list_of_labels: labels to replace currently assigned ones. If not specified it will push current labels instead.
257
+ :param missing_label_type_means_no_change: if a label type is missing and this is False then existing label of type in the Workload will be removed
258
+ :return:
259
+ """
260
+
261
+ if list_of_labels is not None:
262
+ # a list of labels were specified so are first going to change
263
+ if not self.update_labels(list_of_labels, missing_label_type_means_no_change):
264
+ return
265
+
266
+ label_data = []
267
+ if self.loc_label is not None:
268
+ label_data.append({'href': self.loc_label.href})
269
+ if self.env_label is not None:
270
+ label_data.append({'href': self.env_label.href})
271
+ if self.app_label is not None:
272
+ label_data.append({'href': self.app_label.href})
273
+ if self.role_label is not None:
274
+ label_data.append({'href': self.role_label.href})
275
+
276
+ data = {'labels': label_data}
277
+
278
+ if self._batch_update_stack is None:
279
+ connector = pylo.find_connector_or_die(self.owner)
280
+ connector.objects_workload_update(self.href, data)
281
+ else:
282
+ self._batch_update_stack.add_payload(data)
283
+
284
+ self.raw_json.update(data)
285
+
286
+ def api_stacked_updates_start(self):
287
+ """
288
+ Turns on 'updates stacking' mode for this Worklaod which will not push changes to API as you make them but only
289
+ when you trigger 'api_push_stacked_updates()' function
290
+ """
291
+ self._batch_update_stack = WorkloadApiUpdateStack()
292
+
293
+ def api_stacked_updates_push(self):
294
+ """
295
+ Push all stacked changed to API and turns off 'updates stacking' mode
296
+ """
297
+ if self._batch_update_stack is None:
298
+ raise PyloEx("Workload was not in 'update stacking' mode")
299
+
300
+ connector = pylo.find_connector_or_die(self.owner)
301
+ connector.objects_workload_update(self.href, self._batch_update_stack.get_payload_and_reset())
302
+ self._batch_update_stack = None
303
+
304
+ def api_stacked_updates_count(self) -> int:
305
+ """
306
+ Counts the number of stacked changed for this Workload
307
+ :return:
308
+ """
309
+ if self._batch_update_stack is None:
310
+ raise PyloEx("Workload was not in 'update stacking' mode")
311
+ return self._batch_update_stack.count_payloads()
312
+
313
+ def get_labels_str(self, separator: str = '|') -> str:
314
+ """
315
+ Conveniently returns a string with all labels names in RAEL order
316
+ :param separator: default separator is |
317
+ :return: example: *None*|AppA|EnvC|LocZ
318
+ """
319
+ labels = ''
320
+
321
+ first = True
322
+ for dimensions in self.owner.owner.LabelStore.label_types:
323
+ label = self.get_label(dimensions)
324
+ if not first:
325
+ labels += separator
326
+ if label is not None:
327
+ labels += label.name
328
+ else:
329
+ labels += '*None*'
330
+ first = False
331
+
332
+ return labels
333
+
334
+
335
+ def get_appgroup_str(self, separator: str = '|') -> str:
336
+ labels = ''
337
+
338
+ if self.app_label is None:
339
+ labels += '*None*' + separator
340
+ else:
341
+ labels += self.app_label.name + separator
342
+
343
+ if self.env_label is None:
344
+ labels += '*None*' + separator
345
+ else:
346
+ labels += self.env_label.name + separator
347
+
348
+ if self.loc_label is None:
349
+ labels += '*None*'
350
+ else:
351
+ labels += self.loc_label.name
352
+
353
+ return labels
354
+
355
+ def get_labels_str_list(self, missing_str: Optional[str] = '') -> List[str]:
356
+ """
357
+ Conveniently returns the list of Workload labels as a list of strings
358
+ :param missing_str: if a label type is missing then missing_str will be used to represent it
359
+ :return:
360
+ """
361
+ labels = []
362
+
363
+ if self.role_label is None:
364
+ labels.append(missing_str)
365
+ else:
366
+ labels.append(self.role_label.name)
367
+
368
+ if self.app_label is None:
369
+ labels.append(missing_str)
370
+ else:
371
+ labels.append(self.app_label.name)
372
+
373
+ if self.env_label is None:
374
+ labels.append(missing_str)
375
+ else:
376
+ labels.append(self.env_label.name)
377
+
378
+ if self.loc_label is None:
379
+ labels.append(missing_str)
380
+ else:
381
+ labels.append(self.loc_label.name)
382
+
383
+ return labels
384
+
385
+ def get_name(self) -> str:
386
+ """
387
+ Return forced name if it exists, hostname otherwise
388
+
389
+ :return:
390
+ """
391
+ if self.forced_name is not None:
392
+ return self.forced_name
393
+ if self.hostname is None:
394
+ pylo.get_logger().warning("workload with href '{}' has no name nor host name".format(self.href))
395
+ return "*unknown*"
396
+ return self.hostname
397
+
398
+ def get_name_stripped_fqdn(self):
399
+ name_split = self.get_name().split('.')
400
+ return name_split[0]
401
+
402
+ @staticmethod
403
+ def static_name_stripped_fqdn(name: str):
404
+ name_split = name.split('.')
405
+ return name_split[0]
406
+
407
+ def get_status_string(self) -> str:
408
+ if self.ven_agent is None:
409
+ return 'not-applicable'
410
+ return self.ven_agent.mode
411
+
412
+ def update_labels(self, list_of_labels: List[Label], missing_label_type_means_no_change=False) -> bool:
413
+ """
414
+ WARNING: this will not send updates to PCE API, use the 'api_' prefixed function for that
415
+
416
+ :param list_of_labels: labels to replace currently assigned ones
417
+ :param missing_label_type_means_no_change: if a label type is missing and this is False then existing label of type in the Workload will be removed
418
+ :return:
419
+ """
420
+ changes_occurred = False
421
+ role_label: Optional[Label] = None
422
+ app_label: Optional[Label] = None
423
+ env_label: Optional[Label] = None
424
+ loc_label: Optional[Label] = None
425
+
426
+ if len(list_of_labels) > 4:
427
+ raise PyloEx("More than 4 labels provided")
428
+
429
+ for label in list_of_labels:
430
+ if label.type_is_role():
431
+ if role_label is not None:
432
+ raise PyloEx("ROLE label specified more than once ('{}' vs '{}')".format(role_label.name, label.name))
433
+ role_label = label
434
+ elif label.type_is_application():
435
+ if app_label is not None:
436
+ raise PyloEx("APP label specified more than once ('{}' vs '{}')".format(app_label.name, label.name))
437
+ app_label = label
438
+ elif label.type_is_environment():
439
+ if env_label is not None:
440
+ raise PyloEx("ENV label specified more than once ('{}' vs '{}')".format(env_label.name, label.name))
441
+ env_label = label
442
+ elif label.type_is_location():
443
+ if loc_label is not None:
444
+ raise PyloEx("LOC label specified more than once ('{}' vs '{}')".format(loc_label.name, label.name))
445
+ loc_label = label
446
+
447
+ if role_label is None:
448
+ if not missing_label_type_means_no_change:
449
+ if self.role_label is not None:
450
+ changes_occurred = True
451
+ self.role_label.remove_reference(self)
452
+ self.role_label = None
453
+ elif role_label is not self.role_label:
454
+ changes_occurred = True
455
+ if self.role_label is not None:
456
+ self.role_label.remove_reference(self)
457
+ role_label.add_reference(self)
458
+ self.role_label = role_label
459
+
460
+ if app_label is None:
461
+ if not missing_label_type_means_no_change:
462
+ if self.app_label is not None:
463
+ changes_occurred = True
464
+ self.app_label.remove_reference(self)
465
+ self.app_label = None
466
+ elif app_label is not self.app_label:
467
+ changes_occurred = True
468
+ if self.app_label is not None:
469
+ self.app_label.remove_reference(self)
470
+ app_label.add_reference(self)
471
+ self.app_label = app_label
472
+
473
+ if env_label is None:
474
+ if not missing_label_type_means_no_change:
475
+ if self.env_label is not None:
476
+ changes_occurred = True
477
+ self.env_label.remove_reference(self)
478
+ self.env_label = None
479
+ elif env_label is not self.env_label:
480
+ changes_occurred = True
481
+ if self.env_label is not None:
482
+ self.env_label.remove_reference(self)
483
+ env_label.add_reference(self)
484
+ self.env_label = env_label
485
+
486
+ if loc_label is None:
487
+ if not missing_label_type_means_no_change:
488
+ if self.loc_label is not None:
489
+ changes_occurred = True
490
+ self.loc_label.remove_reference(self)
491
+ self.loc_label = None
492
+ elif loc_label is not self.loc_label:
493
+ changes_occurred = True
494
+ if self.loc_label is not None:
495
+ self.loc_label.remove_reference(self)
496
+ loc_label.add_reference(self)
497
+ self.loc_label = loc_label
498
+
499
+ return changes_occurred
500
+
501
+ def get_pce_ui_url(self) -> str:
502
+ """
503
+ generates a URL link for the Workload vs PCE UI
504
+ :return: url string
505
+ """
506
+ return self.owner.owner.connector.get_pce_ui_workload_url(self.href)