illumio-pylo 0.3.8__tar.gz → 0.3.10__tar.gz

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 (111) hide show
  1. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.github/workflows/doxygen-publish.yml +1 -1
  2. {illumio_pylo-0.3.8/illumio_pylo.egg-info → illumio_pylo-0.3.10}/PKG-INFO +2 -2
  3. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/APIConnector.py +90 -54
  4. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/JsonPayloadTypes.py +10 -0
  5. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Helpers/functions.py +8 -13
  6. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/IPList.py +5 -9
  7. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/IPMap.py +3 -3
  8. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Label.py +0 -1
  9. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/LabelCommon.py +1 -1
  10. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/LabelStore.py +24 -25
  11. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/LabeledObject.py +4 -5
  12. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Organization.py +1 -3
  13. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/ReferenceTracker.py +0 -3
  14. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Rule.py +2 -2
  15. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Ruleset.py +7 -7
  16. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/RulesetStore.py +1 -1
  17. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/SecurityPrincipal.py +0 -5
  18. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Workload.py +4 -13
  19. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/WorkloadStoreSubClasses.py +7 -10
  20. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/__init__.py +1 -1
  21. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/__init__.py +0 -2
  22. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/credential_manager.py +7 -18
  23. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/iplist_analyzer.py +3 -9
  24. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/iplist_import_from_file.py +1 -2
  25. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/ruleset_export.py +16 -20
  26. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/update_pce_objects_cache.py +0 -1
  27. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/utils/LabelCreation.py +2 -2
  28. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/utils/misc.py +1 -2
  29. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/ven_compatibility_report_export.py +4 -14
  30. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/ven_duplicate_remover.py +26 -32
  31. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/ven_idle_to_visibility.py +2 -4
  32. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/ven_upgrader.py +1 -2
  33. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/workload_import.py +12 -14
  34. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/workload_reset_names_to_null.py +12 -14
  35. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/workload_update.py +25 -30
  36. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/workload_used_in_rule_finder.py +5 -5
  37. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/tmp.py +1 -0
  38. illumio_pylo-0.3.10/illumio_pylo/utilities/resources/workloads-import-example.csv +2 -0
  39. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  40. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10/illumio_pylo.egg-info}/PKG-INFO +2 -2
  41. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo.egg-info/requires.txt +1 -1
  42. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/requirements.txt +1 -1
  43. illumio_pylo-0.3.8/illumio_pylo/utilities/resources/workloads-import-example.csv +0 -2
  44. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.devcontainer/Dockerfile +0 -0
  45. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.devcontainer/devcontainer.json +0 -0
  46. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.gitattributes +0 -0
  47. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.github/workflows/make-binaries.yml +0 -0
  48. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.github/workflows/python-publish.yml +0 -0
  49. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/.gitignore +0 -0
  50. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/LICENSE +0 -0
  51. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/README.md +0 -0
  52. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/check_unique_hostnames.py +0 -0
  53. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/check_unique_services.py +0 -0
  54. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/delete_all_workloads.py +0 -0
  55. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/delete_unused_services.py +0 -0
  56. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/explorer_report_exporter.py +0 -0
  57. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/export_rules_to_firewall.py +0 -0
  58. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/generate-random-workloads.py +0 -0
  59. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/healthcheck_log.py +0 -0
  60. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/import-labels.py +0 -0
  61. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/import_workloads_placeholders.py +0 -0
  62. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/iplists_stats_duplicates_unused_finder.py +0 -0
  63. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/recalculate_explorer_logs.py +0 -0
  64. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/recalculate_explorer_logs_multithreaded.py +0 -0
  65. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/rules_exporter.py +0 -0
  66. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/rules_exporter_special.py +0 -0
  67. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/test.py +0 -0
  68. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/test_change_workload_desc.py +0 -0
  69. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/test_query.py +0 -0
  70. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/test_query2.py +0 -0
  71. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/test_securityprincipals.py +0 -0
  72. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/ven_idle_to_illumination.py +0 -0
  73. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/dev_playground/ven_reassign_pce.py +0 -0
  74. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/examples/explorer_query.py +0 -0
  75. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/examples/extend_cli.py +0 -0
  76. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/AuditLog.py +0 -0
  77. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/ClusterHealth.py +0 -0
  78. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/CredentialsManager.py +0 -0
  79. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/Explorer.py +0 -0
  80. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/RuleSearchQuery.py +0 -0
  81. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/API/__init__.py +0 -0
  82. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/AgentStore.py +0 -0
  83. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Exception.py +0 -0
  84. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Helpers/__init__.py +0 -0
  85. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Helpers/exports.py +0 -0
  86. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/LabelGroup.py +0 -0
  87. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Query.py +0 -0
  88. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/Service.py +0 -0
  89. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/SoftwareVersion.py +0 -0
  90. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/VirtualService.py +0 -0
  91. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/VirtualServiceStore.py +0 -0
  92. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/WorkloadStore.py +0 -0
  93. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/NativeParsers.py +0 -0
  94. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/__main__.py +0 -0
  95. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/__init__.py +0 -0
  96. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/utils/__init__.py +0 -0
  97. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/cli/commands/workload_export.py +0 -0
  98. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/docs/Doxygen +0 -0
  99. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/__init__.py +0 -0
  100. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/cli.py +0 -0
  101. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/credentials.example.json +0 -0
  102. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/health_monitoring.py +0 -0
  103. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/resources/iplists-import-example.csv +0 -0
  104. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  105. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +0 -0
  106. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo.egg-info/SOURCES.txt +0 -0
  107. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo.egg-info/dependency_links.txt +0 -0
  108. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/illumio_pylo.egg-info/top_level.txt +0 -0
  109. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/pyproject.toml +0 -0
  110. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/setup.cfg +0 -0
  111. {illumio_pylo-0.3.8 → illumio_pylo-0.3.10}/setup.py +0 -0
@@ -3,7 +3,7 @@ name: Publish API Docs on GitHub Pages
3
3
 
4
4
  on:
5
5
  release:
6
- types: [published]
6
+ types: [released]
7
7
  branches:
8
8
  - master
9
9
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.8
3
+ Version: 0.3.10
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
@@ -187,7 +187,7 @@ Requires-Python: >=3.11
187
187
  License-File: LICENSE
188
188
  Requires-Dist: click==8.1.7
189
189
  Requires-Dist: colorama~=0.4.4
190
- Requires-Dist: cryptography==42.0.7
190
+ Requires-Dist: cryptography==43.0.1
191
191
  Requires-Dist: openpyxl~=3.1.3
192
192
  Requires-Dist: paramiko~=3.4.0
193
193
  Requires-Dist: prettytable~=3.10.0
@@ -12,7 +12,8 @@ from .JsonPayloadTypes import LabelGroupObjectJsonStructure, LabelObjectCreation
12
12
  LabelDimensionObjectStructure, AuditLogApiReplyEventJsonStructure, WorkloadsGetQueryLabelFilterJsonStructure, \
13
13
  NetworkDeviceObjectJsonStructure, NetworkDeviceEndpointObjectJsonStructure, HrefReference, \
14
14
  WorkloadObjectCreateJsonStructure, WorkloadObjectMultiCreateJsonRequestPayload, \
15
- WorkloadBulkUpdateEntryJsonStructure, WorkloadBulkUpdateResponseEntry, VenObjectJsonStructure
15
+ WorkloadBulkUpdateEntryJsonStructure, WorkloadBulkUpdateResponseEntry, VenObjectJsonStructure, \
16
+ VENUnpairApiResponseObjectJsonStructure
16
17
 
17
18
  try:
18
19
  import requests as requests
@@ -44,19 +45,19 @@ def get_field_or_die(field_name: str, data):
44
45
 
45
46
 
46
47
  ObjectTypes = Literal['iplists', 'workloads', 'virtual_services', 'labels', 'labelgroups', 'services', 'rulesets',
47
- 'security_principals', 'label_dimensions']
48
+ 'security_principals', 'label_dimensions']
48
49
 
49
50
  all_object_types: Dict[ObjectTypes, ObjectTypes] = {
50
- 'iplists': 'iplists',
51
- 'workloads': 'workloads',
52
- 'virtual_services': 'virtual_services',
53
- 'labels': 'labels',
54
- 'labelgroups': 'labelgroups',
55
- 'services': 'services',
56
- 'rulesets': 'rulesets',
57
- 'security_principals': 'security_principals',
58
- 'label_dimensions': 'label_dimensions'
59
- }
51
+ 'iplists': 'iplists',
52
+ 'workloads': 'workloads',
53
+ 'virtual_services': 'virtual_services',
54
+ 'labels': 'labels',
55
+ 'labelgroups': 'labelgroups',
56
+ 'services': 'services',
57
+ 'rulesets': 'rulesets',
58
+ 'security_principals': 'security_principals',
59
+ 'label_dimensions': 'label_dimensions'
60
+ }
60
61
 
61
62
 
62
63
  class APIConnector:
@@ -69,7 +70,7 @@ class APIConnector:
69
70
  port = str(port)
70
71
  self.port: int = port
71
72
  self._api_key: str = api_key
72
- self._decrypted_api_key: str = None
73
+ self._decrypted_api_key: Optional[str] = None
73
74
  self.api_user: str = api_user
74
75
  self.org_id: int = org_id
75
76
  self.skipSSLCertCheck: bool = skip_ssl_cert_check
@@ -86,7 +87,6 @@ class APIConnector:
86
87
  return self._decrypted_api_key
87
88
  return self._api_key
88
89
 
89
-
90
90
  @staticmethod
91
91
  def get_all_object_types_names_except(exception_list: List[ObjectTypes]):
92
92
 
@@ -138,7 +138,7 @@ class APIConnector:
138
138
  connector = pylo.APIConnector(fqdn_or_profile_name, port, user, password, skip_ssl_cert_check=True, name=name)
139
139
  return connector
140
140
 
141
- def _make_base_url(self, path: str='') -> str:
141
+ def _make_base_url(self, path: str = '') -> str:
142
142
  # remove leading '/' from path if exists
143
143
  if len(path) > 0 and path[0] == '/':
144
144
  path = path[1:]
@@ -222,7 +222,7 @@ class APIConnector:
222
222
  raise pylo.PyloApiEx("PCE connectivity or low level issue: {}".format(e))
223
223
 
224
224
  answer_size = len(req.content) / 1024
225
- log.info("URL downloaded (size "+str( int(answer_size) )+"KB) Reply headers:\n" +
225
+ log.info("URL downloaded (size "+str(int(answer_size))+"KB) Reply headers:\n" +
226
226
  "HTTP " + method + " " + url + " STATUS " + str(req.status_code) + " " + req.reason)
227
227
  log.info(req.headers)
228
228
  # log.info("Request Body:" + pylo.nice_json(json_arguments))
@@ -247,7 +247,9 @@ class APIConnector:
247
247
  retry_loop_times = 0
248
248
 
249
249
  while True:
250
- log.info("Sleeping " + str(retry_interval) + " seconds before polling for job status, elapsed " + str(retry_interval*retry_loop_times) + " seconds so far" )
250
+ log.info(
251
+ "Sleeping " + str(retry_interval) + " seconds before polling for job status, elapsed " + str(
252
+ retry_interval * retry_loop_times) + " seconds so far")
251
253
  retry_loop_times += 1
252
254
  time.sleep(retry_interval)
253
255
  job_poll = self.do_get_call(job_location, include_org_id=False)
@@ -304,17 +306,18 @@ class APIConnector:
304
306
  continue
305
307
 
306
308
  if req.status_code == 403:
307
- raise pylo.PyloApiRequestForbiddenEx('API returned error status "' + str(req.status_code) + ' ' + req.reason
308
- + '" and error message: ' + req.text)
309
+ raise pylo.PyloApiRequestForbiddenEx(
310
+ 'API returned error status "' + str(req.status_code) + ' ' + req.reason
311
+ + '" and error message: ' + req.text)
309
312
 
310
313
  raise pylo.PyloApiEx('API returned error status "' + str(req.status_code) + ' ' + req.reason
311
- + '" and error message: ' + req.text)
314
+ + '" and error message: ' + req.text)
312
315
 
313
316
  if return_headers:
314
317
  return req.headers
315
318
 
316
319
  if json_output_expected:
317
- log.info("Parsing API answer to JSON (with a size of " + str( int(answer_size) ) + "KB)")
320
+ log.info("Parsing API answer to JSON (with a size of " + str(int(answer_size)) + "KB)")
318
321
  json_out = req.json()
319
322
  log.info("Done!")
320
323
  if answer_size < 5:
@@ -387,7 +390,6 @@ class APIConnector:
387
390
  if 'label_dimensions' in objects_to_load:
388
391
  del objects_to_load['label_dimensions']
389
392
 
390
-
391
393
  threads_count = 4
392
394
  data: PCEObjectsJsonStructure = pylo.Organization.create_fake_empty_config()
393
395
  errors = []
@@ -660,7 +662,7 @@ class APIConnector:
660
662
  def objects_workload_get(self,
661
663
  include_deleted=False,
662
664
  filter_by_ip: str = None,
663
- filter_by_label: WorkloadsGetQueryLabelFilterJsonStructure=None,
665
+ filter_by_label: WorkloadsGetQueryLabelFilterJsonStructure = None,
664
666
  filter_by_name: str = None,
665
667
  filter_by_managed: bool = None,
666
668
  filer_by_policy_health: Literal['active', 'warning', 'error'] = None,
@@ -787,12 +789,11 @@ class APIConnector:
787
789
 
788
790
  try:
789
791
  result = self.connector.objects_workload_delete_multi(list(self._hrefs.keys()))
790
- except Exception as ex: #global exception means something really bad happened we log errors for all workloads
792
+ except Exception as ex: # global exception means something really bad happened we log errors for all workloads
791
793
  for href in self._hrefs.keys():
792
794
  self._errors[href] = str(ex)
793
795
  return
794
796
 
795
-
796
797
  # print(pylo.nice_json(result))
797
798
  if not type(result) is list:
798
799
  raise pylo.PyloApiEx("API didnt return expected JSON format", result)
@@ -816,28 +817,65 @@ class APIConnector:
816
817
  if len(agents_to_unpair) > 0:
817
818
  self._unpair_agents(agents_to_unpair)
818
819
 
820
+ def _mark_error(self, href: str, error_msg: str):
821
+ # does href contains "/ven' ?
822
+ if href.find("/vens/") > -1:
823
+ # replace '/vens/' with '/workloads/'
824
+ other_href = href.replace('/vens/', '/workloads/')
825
+ else:
826
+ # replace '/workloads/' with '/vens/'
827
+ other_href = href.replace('/workloads/', '/vens/')
828
+
829
+ # is href in self._hrefs ?
830
+ if href in self._hrefs:
831
+ self._errors[href] = error_msg
832
+ return
833
+
834
+ # is other_href in self._hrefs ?
835
+ if other_href in self._hrefs:
836
+ self._errors[other_href] = error_msg
837
+ return
838
+
839
+ # may be it's in self._workloads
840
+ if href in self._workloads:
841
+ self._errors[href] = error_msg
842
+ return
843
+
844
+ if other_href in self._workloads:
845
+ self._errors[other_href] = error_msg
846
+ return
847
+
848
+ raise pylo.PyloEx("Error for unknown href '{}'".format(href))
849
+
819
850
  def _unpair_agents(self, workloads_hrefs: [str]):
820
- for href in workloads_hrefs:
851
+
852
+ # split in batches of 500 href
853
+ workloads_hrefs_batches = [workloads_hrefs[i:i + 500] for i in range(0, len(workloads_hrefs), 500)]
854
+
855
+ for workloads_hrefs_batch in workloads_hrefs_batches:
821
856
  retry_count = 5
822
- api_result = None
823
857
 
824
858
  while retry_count >= 0:
825
859
  retry_count -= 1
826
860
  try:
827
- api_result = self.connector.objects_workload_unpair_multi([href])
861
+ api_result = self.connector.objects_workload_unpair_multi(workloads_hrefs_batch)
862
+ for error in api_result['errors']:
863
+ for errored_href in error['hrefs']:
864
+ self._mark_error(errored_href, error['token'])
828
865
  break
829
866
 
830
867
  except pylo.PyloApiTooManyRequestsEx as ex:
831
868
  if retry_count <= 0:
832
- self._errors[href] = str(ex)
869
+ for href in workloads_hrefs_batch:
870
+ self._mark_error(href, str(ex))
833
871
  break
834
872
  time.sleep(6)
835
873
 
836
874
  except pylo.PyloApiEx as ex:
837
- self._errors[href] = str(ex)
875
+ for href in workloads_hrefs_batch:
876
+ self._mark_error(href, str(ex))
838
877
  break
839
878
 
840
-
841
879
  def count_entries(self):
842
880
  return len(self._hrefs)
843
881
 
@@ -847,7 +885,7 @@ class APIConnector:
847
885
  def new_tracker_workload_multi_delete(self):
848
886
  return APIConnector.WorkloadMultiDeleteTracker(self)
849
887
 
850
- def objects_workload_delete_multi(self, href_or_workload_array: Union[List['pylo.Workload'],List[str]]):
888
+ def objects_workload_delete_multi(self, href_or_workload_array: Union[List['pylo.Workload'], List[str]]):
851
889
  if len(href_or_workload_array) < 1:
852
890
  return
853
891
 
@@ -867,33 +905,33 @@ class APIConnector:
867
905
 
868
906
  return self.do_put_call(path=path, json_arguments=json_data, json_output_expected=True)
869
907
 
870
- def objects_workload_unpair_multi(self, href_or_workload_array):
871
- """
872
-
873
- :type href_or_workload_array: list[str]|list[pylo.Workload]
874
- """
908
+ def objects_workload_unpair_multi(self, href_or_workload_array: Union[List['pylo.Workload'], List[str]]) -> VENUnpairApiResponseObjectJsonStructure:
875
909
 
876
910
  if len(href_or_workload_array) < 1:
877
- return
911
+ raise pylo.PyloEx("HREF list of workloads/VENs to unpair is empty")
878
912
 
879
913
  json_data = {
880
- "ip_table_restore": "disable",
881
- "workloads": []
914
+ "firewall_restore": "saved",
915
+ "vens": []
882
916
  }
883
917
 
884
918
  if type(href_or_workload_array[0]) is str:
919
+ href: str
885
920
  for href in href_or_workload_array:
886
- json_data['workloads'].append({"href": href})
921
+ # href may contain '/workloads/', we want to replace it with '/vens/'
922
+ new_href = href.replace('/workloads/', '/vens/')
923
+ json_data['vens'].append({"href": new_href})
887
924
  else:
888
- href: 'pylo.Workload'
889
- for href in href_or_workload_array:
890
- json_data['workloads'].append({"href": href.href})
925
+ workload: 'pylo.Workload'
926
+ for workload in href_or_workload_array:
927
+ new_href = workload.href.replace('/workloads/', '/vens/')
928
+ json_data['vens'].append({"href": new_href})
891
929
 
892
930
  # print(json_data)
893
931
 
894
- path = "/workloads/unpair"
932
+ path = "/vens/unpair"
895
933
 
896
- return self.do_put_call(path=path, json_arguments=json_data, json_output_expected=False)
934
+ return self.do_put_call(path=path, json_arguments=json_data, json_output_expected=True)
897
935
 
898
936
  def objects_workload_create_single_unmanaged(self, json_object: WorkloadObjectCreateJsonStructure):
899
937
  path = '/workloads'
@@ -923,8 +961,7 @@ class APIConnector:
923
961
 
924
962
  return self.do_delete_call(path=path, json_output_expected=False, include_org_id=False)
925
963
 
926
- def objects_network_device_get(self,
927
- max_results: int = None) -> List[NetworkDeviceObjectJsonStructure]:
964
+ def objects_network_device_get(self, max_results: int = None) -> List[NetworkDeviceObjectJsonStructure]:
928
965
  path = '/network_devices'
929
966
  data = {}
930
967
 
@@ -1069,7 +1106,7 @@ class APIConnector:
1069
1106
  'enabled': enabled,
1070
1107
  'stateless': stateless,
1071
1108
  'consuming_security_principals': consuming_security_principals,
1072
- 'resolve_labels_as': {'providers': resolve_providers, 'consumers': resolve_consumers,},
1109
+ 'resolve_labels_as': {'providers': resolve_providers, 'consumers': resolve_consumers},
1073
1110
  'consumers': consumers_json,
1074
1111
  'providers': providers_json,
1075
1112
  'ingress_services': services_json
@@ -1270,7 +1307,7 @@ class APIConnector:
1270
1307
  # get current timestamp to ensure we don't wait too long
1271
1308
  start_time = time.time()
1272
1309
 
1273
- query_status = None # Json response from API for specific query
1310
+ query_status = None # Json response from API for specific query
1274
1311
 
1275
1312
  while True:
1276
1313
  # check that we don't wait too long
@@ -1325,12 +1362,12 @@ class APIConnector:
1325
1362
  check_for_update_interval_seconds: int = 10) -> 'pylo.ExplorerQuery':
1326
1363
  return pylo.ExplorerQuery(self, max_results, max_running_time_seconds, check_for_update_interval_seconds)
1327
1364
 
1328
-
1329
1365
  def new_audit_log_query(self, max_results: int = 10000, max_running_time_seconds: int = 1800,
1330
1366
  check_for_update_interval_seconds: int = 10) -> 'pylo.AuditLogQuery':
1331
- return pylo.AuditLogQuery(self, max_results, max_running_time_seconds )
1367
+ return pylo.AuditLogQuery(self, max_results, max_running_time_seconds)
1332
1368
 
1333
- def audit_log_query(self, max_results = 1000, event_type: Optional[str] = None) -> List[AuditLogApiReplyEventJsonStructure]:
1369
+ def audit_log_query(self, max_results=1000, event_type: Optional[str] = None) \
1370
+ -> List[AuditLogApiReplyEventJsonStructure]:
1334
1371
  url = '/events'
1335
1372
  args = {'max_results': max_results}
1336
1373
  if event_type is not None:
@@ -1338,7 +1375,6 @@ class APIConnector:
1338
1375
 
1339
1376
  return self.do_get_call(path=url, params=args)
1340
1377
 
1341
-
1342
1378
  def get_pce_ui_workload_url(self, href: str) -> str:
1343
1379
  # extract UUID from workload HREF:
1344
1380
  uuid = href.split('/')[-1]
@@ -150,6 +150,14 @@ class VenObjectJsonStructure(TypedDict):
150
150
  os_platform: Optional[str]
151
151
  uid: Optional[str]
152
152
 
153
+ class VENUnpairApiResponseSingleErrorObjectJsonStructure(TypedDict):
154
+ token: str
155
+ message: str
156
+ hrefs: List[str]
157
+
158
+ class VENUnpairApiResponseObjectJsonStructure(TypedDict):
159
+ errors: List[VENUnpairApiResponseSingleErrorObjectJsonStructure]
160
+
153
161
 
154
162
  class RuleServiceReferenceObjectJsonStructure(TypedDict):
155
163
  href: str
@@ -186,6 +194,7 @@ class RulesetObjectJsonStructure(TypedDict):
186
194
  scopes: List[List[RulesetScopeEntryLineJsonStructure]]
187
195
  updated_at: str
188
196
  updated_by: Optional[HrefReferenceWithName]
197
+ enabled: bool
189
198
 
190
199
 
191
200
  class RulesetObjectUpdateStructure(TypedDict):
@@ -258,6 +267,7 @@ class PCEObjectsJsonStructure(TypedDict):
258
267
  virtual_services: List[VirtualServiceObjectJsonStructure]
259
268
  workloads: List[WorkloadObjectJsonStructure]
260
269
  label_dimensions: List[LabelDimensionObjectStructure]
270
+ pce_version: str
261
271
 
262
272
 
263
273
  class PCECacheFileJsonStructure(TypedDict):
@@ -2,6 +2,7 @@ import json
2
2
  import os
3
3
  import re
4
4
  import time
5
+ import functools
5
6
 
6
7
 
7
8
  def nice_json(json_obj):
@@ -133,7 +134,7 @@ ___ipv6_pattern = re.compile(r"""
133
134
  def is_valid_ipv6(ip):
134
135
  """Validates IPv6 addresses.
135
136
  """
136
- #print("testing ({})".format(ip))
137
+ # print("testing ({})".format(ip))
137
138
  return ___ipv6_pattern.match(ip) is not None
138
139
 
139
140
 
@@ -145,22 +146,16 @@ __clocks_start = {}
145
146
  __clocks_end = {}
146
147
 
147
148
 
148
- def clock_start(name:str = 'default'):
149
+ def clock_start(name: str = 'default'):
149
150
  __clocks_start[name] = time.time()
150
151
 
151
152
 
152
- def clock_stop(name:str = 'default'):
153
+ def clock_stop(name: str = 'default'):
153
154
  __clocks_end[name] = time.time()
154
155
 
155
156
 
156
- import functools
157
-
158
-
159
- def clock_elapsed_str(name:str = 'default'):
160
- t = time.time()-__clocks_start[name]
157
+ def clock_elapsed_str(name: str = 'default'):
158
+ t = time.time() - __clocks_start[name]
161
159
  return "%d:%02d:%02d.%03d" % \
162
- functools.reduce(lambda ll,b : divmod(ll[0],b) + ll[1:],
163
- [(t*1000,),1000,60,60])
164
- return "{}".format(time.time()-__clocks_start[name])
165
-
166
-
160
+ functools.reduce(lambda ll, b: divmod(ll[0], b) + ll[1:],
161
+ [(t * 1000,), 1000, 60, 60])
@@ -1,6 +1,3 @@
1
- from typing import Dict, Optional
2
-
3
- import illumio_pylo as pylo
4
1
  from .API.JsonPayloadTypes import IPListObjectJsonStructure
5
2
  from illumio_pylo import log
6
3
  from .Helpers import *
@@ -59,15 +56,15 @@ class IPList(pylo.ReferenceTracker):
59
56
  self.raw_entries[entry] = entry
60
57
 
61
58
  def get_ip4map(self) -> pylo.IP4Map:
62
- map = pylo.IP4Map()
59
+ new_map = pylo.IP4Map()
63
60
 
64
61
  for entry in self.raw_entries:
65
62
  if entry[0] == '!':
66
- map.subtract_from_text(entry[1:], ignore_ipv6=True)
63
+ new_map.subtract_from_text(entry[1:], ignore_ipv6=True)
67
64
  else:
68
- map.add_from_text(entry, ignore_ipv6=True)
65
+ new_map.add_from_text(entry, ignore_ipv6=True)
69
66
 
70
- return map
67
+ return new_map
71
68
 
72
69
  def get_raw_entries_as_string_list(self, separator=',') -> str:
73
70
  return pylo.string_list_to_text(self.raw_entries.values(), separator=separator)
@@ -115,7 +112,7 @@ class IPListStore:
115
112
  def find_by_href(self, href: str) -> Optional['pylo.IPList']:
116
113
  return self.items_by_href.get(href)
117
114
 
118
- def find_by_name(self, name: str, case_sensitive: bool = True ) -> Optional['pylo.IPList']:
115
+ def find_by_name(self, name: str, case_sensitive: bool = True) -> Optional['pylo.IPList']:
119
116
  if case_sensitive:
120
117
  for iplist in self.items_by_href.values():
121
118
  if iplist.name == name:
@@ -126,4 +123,3 @@ class IPListStore:
126
123
  if iplist.name.lower() == lower_name:
127
124
  return iplist
128
125
  return None
129
-
@@ -2,7 +2,7 @@ from .Exception import PyloEx
2
2
  from .Helpers.functions import is_valid_ipv6, string_list_to_text
3
3
  import ipaddress
4
4
  import copy
5
- from typing import Optional, List, Dict
5
+ from typing import Optional, List
6
6
 
7
7
 
8
8
  def sort_first(val):
@@ -232,13 +232,13 @@ class IP4Map:
232
232
 
233
233
  for netmask in range(1, 32, 1):
234
234
  new_end = net_start | masks[netmask]
235
- #print("{}/{}/{}/{}".format(ipaddress.IPv4Address(net_start), ipaddress.IPv4Address(net_end), ipaddress.IPv4Address(new_end), 32 - netmask))
235
+ # print("{}/{}/{}/{}".format(ipaddress.IPv4Address(net_start), ipaddress.IPv4Address(net_end), ipaddress.IPv4Address(new_end), 32 - netmask))
236
236
 
237
237
  if new_end > net_end:
238
238
  result.append('{}/{}'.format(ipaddress.IPv4Address(net_start), 33 - netmask))
239
239
  net_start = previous_loop_end + 1
240
240
  previous_loop_end = net_start
241
- #print("breaking loop with {}/{}".format(ipaddress.IPv4Address(net_start), ipaddress.IPv4Address(previous_loop_end)))
241
+ # print("breaking loop with {}/{}".format(ipaddress.IPv4Address(net_start), ipaddress.IPv4Address(previous_loop_end)))
242
242
  break
243
243
 
244
244
  if new_end == net_end:
@@ -27,4 +27,3 @@ class Label(ReferenceTracker, LabelCommon):
27
27
 
28
28
  def get_api_reference_json(self) -> LabelHrefRef:
29
29
  return {'label': {'href': self.href}}
30
-
@@ -1,6 +1,6 @@
1
1
  from typing import Union
2
2
  from .Exception import PyloEx
3
- from .LabelStore import label_type_role, label_type_env, label_type_loc, label_type_app, LabelStore
3
+ from .LabelStore import LabelStore
4
4
 
5
5
 
6
6
  class LabelCommon:
@@ -1,6 +1,6 @@
1
1
  from hashlib import md5
2
2
  import random
3
- from typing import Union, Set, Iterable
3
+ from typing import Set
4
4
  # Pylo imports
5
5
  from illumio_pylo import log
6
6
  from .API.JsonPayloadTypes import LabelObjectJsonStructure, LabelGroupObjectJsonStructure, LabelDimensionObjectStructure
@@ -96,7 +96,7 @@ class LabelStore:
96
96
 
97
97
  def load_label_groups_from_json(self, json_list: List[LabelGroupObjectJsonStructure]):
98
98
  # groups cannot be loaded straight away : we need to extract of their principal properties (name, href and type)
99
- #then we can extract their members in case there are nested groups
99
+ # then we can extract their members in case there are nested groups
100
100
  created_groups = []
101
101
  for json_label in json_list:
102
102
  if 'name' not in json_label or 'href' not in json_label or 'key' not in json_label:
@@ -174,33 +174,34 @@ class LabelStore:
174
174
  data.append(label)
175
175
  return data
176
176
 
177
-
178
177
  def get_label_groups_as_dict_by_href(self, label_type: Optional[str] = None) -> Dict[str, 'pylo.LabelGroup']:
179
178
  label_list = self.get_label_groups(label_type)
180
179
  return self.Utils.list_to_dict_by_href(label_list)
181
180
 
182
-
183
- def get_both_labels_and_groups(self, label_type: Optional[str] = None) -> List[Union['pylo.Label','pylo.LabelGroup']]:
181
+ def get_both_labels_and_groups(self, label_type: Optional[str] = None) \
182
+ -> List[Union['pylo.Label', 'pylo.LabelGroup']]:
184
183
  data = []
185
184
  if label_type is not None:
186
185
  if label_type not in self.label_types_as_set:
187
- raise pylo.PyloEx("Invalid label type '%s'. Valid types are: %s" % (label_type, self.label_types_as_set))
186
+ raise pylo.PyloEx(
187
+ "Invalid label type '%s'. Valid types are: %s" % (label_type, self.label_types_as_set))
188
188
 
189
189
  for label in self._items_by_href.values():
190
190
  if label_type is None or label.type == label_type:
191
191
  data.append(label)
192
192
  return data
193
193
 
194
- def get_both_labels_and_groups_as_dict_by_href(self, label_type: Optional[str] = None) -> Dict[str, Union['pylo.Label','pylo.LabelGroup']]:
194
+ def get_both_labels_and_groups_as_dict_by_href(self, label_type: Optional[str] = None) \
195
+ -> Dict[str, Union['pylo.Label', 'pylo.LabelGroup']]:
195
196
  label_list = self.get_both_labels_and_groups(label_type)
196
197
  return self.Utils.list_to_dict_by_href(label_list)
197
198
 
198
-
199
- def find_object_by_name(self, name: str|List[str], label_type: Optional[str] = None, case_sensitive: bool = True,
200
- missing_labels_names: Optional[List[str]] = None,
201
- allow_label_group: bool = True,
202
- allow_label: bool = True,
203
- raise_exception_if_not_found: bool = False) -> Optional[Union['pylo.Label','pylo.LabelGroup',List[Union['pylo.Label','pylo.LabelGroup']]]]:
199
+ def find_object_by_name(self, name: str | List[str], label_type: Optional[str] = None, case_sensitive: bool = True,
200
+ missing_labels_names: Optional[List[str]] = None,
201
+ allow_label_group: bool = True,
202
+ allow_label: bool = True,
203
+ raise_exception_if_not_found: bool = False) \
204
+ -> Optional[Union['pylo.Label', 'pylo.LabelGroup', List[Union['pylo.Label', 'pylo.LabelGroup']]]]:
204
205
  """Find a label by its name. If case_sensitive is False, the search is case-insensitive.
205
206
  If case_sensitive is False it will return a list of labels with the same name rather than a single object.
206
207
  If missing_labels_names is not None, it will be filled with the names of the labels not found.
@@ -218,13 +219,13 @@ class LabelStore:
218
219
  for label in self._items_by_href.values():
219
220
  if label_type is not None and label.type != label_type:
220
221
  continue
221
- if label.is_label() and allow_label: # ignore groups
222
+ if label.is_label() and allow_label: # ignore groups
222
223
  if case_sensitive:
223
224
  if label.name == name:
224
225
  return label
225
226
  elif allow_label_group:
226
- if label.name.lower() == name.lower():
227
- return label
227
+ if label.name.lower() == name.lower():
228
+ return label
228
229
  if raise_exception_if_not_found:
229
230
  raise pylo.PyloEx("Label/group '%s' not found", name)
230
231
  if missing_labels_names is not None:
@@ -234,10 +235,10 @@ class LabelStore:
234
235
  results = []
235
236
  local_notfound_labels = []
236
237
  for name_to_find in name:
237
- result = self.find_object_by_name(name_to_find, label_type=label_type ,case_sensitive=case_sensitive,
238
- allow_label_group=allow_label_group, allow_label=allow_label)
238
+ result = self.find_object_by_name(name_to_find, label_type=label_type, case_sensitive=case_sensitive,
239
+ allow_label_group=allow_label_group, allow_label=allow_label)
239
240
  if result is None:
240
- local_notfound_labels.append(name_to_find)
241
+ local_notfound_labels.append(name_to_find)
241
242
  else:
242
243
  results.append(result)
243
244
  if raise_exception_if_not_found and len(local_notfound_labels) > 0:
@@ -246,9 +247,9 @@ class LabelStore:
246
247
  missing_labels_names.extend(local_notfound_labels)
247
248
  return results
248
249
 
249
- def find_label_by_name(self, name: str|List[str], label_type: Optional[str] = None, case_sensitive: bool = True,
250
- missing_labels_names: Optional[List[str]] = None,
251
- raise_exception_if_not_found: bool = False) -> Optional['pylo.Label'|List['pylo.Label']]:
250
+ def find_label_by_name(self, name: str | List[str], label_type: Optional[str] = None, case_sensitive: bool = True,
251
+ missing_labels_names: Optional[List[str]] = None,
252
+ raise_exception_if_not_found: bool = False) -> Optional['pylo.Label' | List['pylo.Label']]:
252
253
  """Find a label by its name.
253
254
  If case_sensitive is False it will return a list of labels with the same name rather than a single object.
254
255
  If missing_labels_names is not None, it will be filled with the names of the labels not found.
@@ -261,12 +262,10 @@ class LabelStore:
261
262
  allow_label_group=False, allow_label=True,
262
263
  raise_exception_if_not_found=raise_exception_if_not_found)
263
264
 
264
-
265
265
  def find_label_by_name_whatever_type(self, name: str, case_sensitive: bool = True) -> Optional[Union['pylo.Label', 'pylo.LabelGroup']]:
266
266
  pylo.log.warn("find_label_by_name_whatever_type is deprecated, use find_label_by_name instead")
267
267
  return self.find_label_by_name(name, case_sensitive=case_sensitive)
268
268
 
269
-
270
269
  def find_label_by_name_and_type(self, name: str, label_type: str, case_sensitive: bool = True) \
271
270
  -> Optional[Union['pylo.Label', 'pylo.LabelGroup']]:
272
271
  pylo.log.warn("find_label_by_name_and_type is deprecated, use find_label_by_name instead")
@@ -366,7 +365,7 @@ class LabelStore:
366
365
 
367
366
  new_label_name = name
368
367
  new_label_type = label_type
369
- new_label_href = '**fake-label-href**/{}'.format( md5(str(random.random()).encode('utf8')).digest() )
368
+ new_label_href = '**fake-label-href**/{}'.format(md5(str(random.random()).encode('utf8')).digest())
370
369
 
371
370
  new_label = pylo.Label(new_label_name, new_label_href, new_label_type, self)
372
371
 
@@ -18,10 +18,10 @@ class LabeledObject:
18
18
  def get_labels(self) -> Iterable['pylo.Label']:
19
19
  return self._labels.values()
20
20
 
21
- def set_label(self, label :'pylo.Label'):
21
+ def set_label(self, label: 'pylo.Label'):
22
22
  self._labels[label.type] = label
23
23
 
24
- def get_label_name(self, key: str, not_found_return_value = None):
24
+ def get_label_name(self, key: str, not_found_return_value=None):
25
25
  label = self.get_label(key)
26
26
  return label.name if label else not_found_return_value
27
27
 
@@ -53,7 +53,6 @@ class LabeledObject:
53
53
 
54
54
  def is_using_label(self, label: Union['pylo.Label', 'pylo.LabelGroup']) -> bool:
55
55
  if isinstance(label, pylo.Label):
56
- return label.type in self._labels
56
+ return label in self._labels
57
57
  else:
58
- return any([label.type in self._labels for label in label.labels])
59
-
58
+ return any([label in self._labels for label in label.get_members().values()])