illumio-pylo 0.3.0__tar.gz → 0.3.2__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 (110) hide show
  1. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.github/workflows/doxygen-publish.yml +1 -1
  2. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.github/workflows/make-binaries.yml +21 -20
  3. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.github/workflows/python-publish.yml +1 -1
  4. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/PKG-INFO +1 -1
  5. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/examples/explorer_query.py +1 -1
  6. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/APIConnector.py +12 -8
  7. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/CredentialsManager.py +9 -8
  8. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Helpers/exports.py +77 -63
  9. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/IPMap.py +9 -0
  10. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/LabeledObject.py +1 -1
  11. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Organization.py +4 -1
  12. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Rule.py +27 -1
  13. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Ruleset.py +15 -27
  14. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Service.py +49 -52
  15. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/__init__.py +1 -1
  16. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/__init__.py +19 -6
  17. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/credential_manager.py +91 -26
  18. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/ruleset_export.py +44 -37
  19. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/utils/misc.py +4 -0
  20. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/ven_compatibility_report_export.py +14 -8
  21. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/ven_duplicate_remover.py +18 -15
  22. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/ven_upgrader.py +10 -78
  23. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/workload_export.py +16 -12
  24. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/workload_import.py +50 -17
  25. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/workload_update.py +3 -2
  26. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/tmp.py +8 -4
  27. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo.egg-info/PKG-INFO +1 -1
  28. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo.egg-info/SOURCES.txt +4 -4
  29. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.devcontainer/Dockerfile +0 -0
  30. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.devcontainer/devcontainer.json +0 -0
  31. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.gitattributes +0 -0
  32. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/.gitignore +0 -0
  33. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/LICENSE +0 -0
  34. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/README.md +0 -0
  35. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/check_unique_hostnames.py +0 -0
  36. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/check_unique_services.py +0 -0
  37. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/delete_all_workloads.py +0 -0
  38. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/delete_unused_services.py +0 -0
  39. {illumio_pylo-0.3.0/illumio_pylo/utilities → illumio_pylo-0.3.2/dev_playground}/explorer_report_exporter.py +0 -0
  40. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/export_rules_to_firewall.py +0 -0
  41. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/generate-random-workloads.py +0 -0
  42. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/healthcheck_log.py +0 -0
  43. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/import-labels.py +0 -0
  44. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/import_workloads_placeholders.py +0 -0
  45. {illumio_pylo-0.3.0/illumio_pylo/utilities → illumio_pylo-0.3.2/dev_playground}/iplists_stats_duplicates_unused_finder.py +0 -0
  46. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/recalculate_explorer_logs.py +0 -0
  47. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/recalculate_explorer_logs_multithreaded.py +0 -0
  48. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/rules_exporter.py +0 -0
  49. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/rules_exporter_special.py +0 -0
  50. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/test.py +0 -0
  51. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/test_change_workload_desc.py +0 -0
  52. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/test_query.py +0 -0
  53. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/test_query2.py +0 -0
  54. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/dev_playground/test_securityprincipals.py +0 -0
  55. {illumio_pylo-0.3.0/illumio_pylo/utilities → illumio_pylo-0.3.2/dev_playground}/ven_idle_to_illumination.py +0 -0
  56. {illumio_pylo-0.3.0/illumio_pylo/utilities → illumio_pylo-0.3.2/dev_playground}/ven_reassign_pce.py +0 -0
  57. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/examples/extend_cli.py +0 -0
  58. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/AuditLog.py +0 -0
  59. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/ClusterHealth.py +0 -0
  60. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/Explorer.py +0 -0
  61. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/JsonPayloadTypes.py +0 -0
  62. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/RuleSearchQuery.py +0 -0
  63. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/API/__init__.py +0 -0
  64. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/AgentStore.py +0 -0
  65. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Exception.py +0 -0
  66. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Helpers/__init__.py +0 -0
  67. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Helpers/functions.py +0 -0
  68. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/IPList.py +0 -0
  69. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Label.py +0 -0
  70. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/LabelCommon.py +0 -0
  71. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/LabelGroup.py +0 -0
  72. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/LabelStore.py +0 -0
  73. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Query.py +0 -0
  74. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/ReferenceTracker.py +0 -0
  75. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/RulesetStore.py +0 -0
  76. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/SecurityPrincipal.py +0 -0
  77. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/SoftwareVersion.py +0 -0
  78. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/VirtualService.py +0 -0
  79. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/VirtualServiceStore.py +0 -0
  80. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/Workload.py +0 -0
  81. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/WorkloadStore.py +0 -0
  82. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/WorkloadStoreSubClasses.py +0 -0
  83. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/NativeParsers.py +0 -0
  84. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/__main__.py +0 -0
  85. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/__init__.py +0 -0
  86. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/iplist_analyzer.py +0 -0
  87. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/iplist_import_from_file.py +0 -0
  88. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/update_pce_objects_cache.py +0 -0
  89. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/utils/LabelCreation.py +0 -0
  90. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/utils/__init__.py +0 -0
  91. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/ven_idle_to_visibility.py +0 -0
  92. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/workload_reset_names_to_null.py +0 -0
  93. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/cli/commands/workload_used_in_rule_finder.py +0 -0
  94. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/docs/Doxygen +0 -0
  95. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/__init__.py +0 -0
  96. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/cli.py +0 -0
  97. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/credentials.example.json +0 -0
  98. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/health_monitoring.py +0 -0
  99. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/resources/iplists-import-example.csv +0 -0
  100. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  101. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +0 -0
  102. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/resources/workloads-import-example.csv +0 -0
  103. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  104. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo.egg-info/dependency_links.txt +0 -0
  105. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo.egg-info/requires.txt +0 -0
  106. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/illumio_pylo.egg-info/top_level.txt +0 -0
  107. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/pyproject.toml +0 -0
  108. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/requirements.txt +0 -0
  109. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/setup.cfg +0 -0
  110. {illumio_pylo-0.3.0 → illumio_pylo-0.3.2}/setup.py +0 -0
@@ -12,7 +12,7 @@ jobs:
12
12
  deploy:
13
13
  runs-on: ubuntu-20.04
14
14
  steps:
15
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v4
16
16
  with:
17
17
  submodules: true # Fetch Hugo themes (true OR recursive)
18
18
  fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
@@ -15,7 +15,7 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v2
18
+ - uses: actions/checkout@v4
19
19
 
20
20
  - name: Change Pylo Version if this is a DEV build
21
21
  if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
@@ -39,18 +39,6 @@ jobs:
39
39
  run: |
40
40
  mv dist/windows/cli.exe dist/windows/pylo-cli.exe
41
41
 
42
- - name: Publish executables for Master branch with 'latest' tag
43
- # only if this is a TAG type
44
- if: github.ref == 'refs/heads/master' && github.event_name == 'push'
45
-
46
- uses: "marvinpinto/action-automatic-releases@latest"
47
- with:
48
- repo_token: "${{ secrets.GITHUB_TOKEN }}"
49
- automatic_release_tag: "latest"
50
- prerelease: true
51
- title: "Latest Binaries"
52
- files: |
53
- dist/windows/*
54
42
 
55
43
  - name: Publish executables along with the tag for a release
56
44
  # only if this is a TAG type
@@ -64,14 +52,27 @@ jobs:
64
52
  draft: false
65
53
  prerelease: false
66
54
 
55
+
56
+ - name: Publish executables for Master branch with 'latest' tag
57
+ # only if this is a TAG type
58
+ if: github.ref == 'refs/heads/master' && github.event_name == 'push'
59
+ uses: "crowbarmaster/GH-Automatic-Releases@latest"
60
+ with:
61
+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
62
+ automatic_release_tag: "latest"
63
+ prerelease: true
64
+ title: "Latest Binaries"
65
+ files: |
66
+ dist/windows/*
67
+
68
+
67
69
  - name: Publish executables for Dev branch
68
70
  if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
69
- uses: softprops/action-gh-release@975c1b265e11dd76618af1c374e7981f9a6ff44a # v0.1.16
71
+ uses: "crowbarmaster/GH-Automatic-Releases@latest"
70
72
  with:
71
- files: dist/windows/*
72
- token: ${{ secrets.GITHUB_TOKEN }}
73
- draft: false
73
+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
74
+ automatic_release_tag: "dev-latest"
74
75
  prerelease: false
75
- tag_name: "dev-build"
76
- name: "Latest DEV/Unstable Binaries"
77
-
76
+ title: "Latest Binaries for DEV/Unstable branch"
77
+ files: |
78
+ dist/windows/*
@@ -19,7 +19,7 @@ jobs:
19
19
  id-token: write
20
20
 
21
21
  steps:
22
- - uses: actions/checkout@v2
22
+ - uses: actions/checkout@v4
23
23
  - name: Set up Python
24
24
  uses: actions/setup-python@v2
25
25
  with:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -60,7 +60,7 @@ explorer_query.filters.set_time_from_x_days_ago(5)
60
60
  # Query the PCE for traffic logs matching the filter
61
61
  print("Querying PCE for traffic logs matching the filter... ", end='', flush=True)
62
62
  traffic_logs = explorer_query.execute()
63
- print("OK! found {} traffic logs".format(traffic_logs.count_results()))
63
+ print("OK! found {} traffic logs".format(traffic_logs.count_records()))
64
64
 
65
65
  # Print the results
66
66
  records = traffic_logs.get_all_records()
@@ -2,7 +2,7 @@ import json
2
2
  import time
3
3
  import getpass
4
4
 
5
- from .CredentialsManager import is_api_key_encrypted, decrypt_api_key
5
+ from .CredentialsManager import is_api_key_encrypted, decrypt_api_key, CredentialProfile
6
6
  from .JsonPayloadTypes import LabelGroupObjectJsonStructure, LabelObjectCreationJsonStructure, \
7
7
  LabelObjectJsonStructure, LabelObjectUpdateJsonStructure, PCEObjectsJsonStructure, \
8
8
  LabelGroupObjectUpdateJsonStructure, IPListObjectCreationJsonStructure, IPListObjectJsonStructure, \
@@ -108,6 +108,12 @@ class APIConnector:
108
108
  def get_all_object_types():
109
109
  return all_object_types.copy()
110
110
 
111
+ @staticmethod
112
+ def create_from_credentials_object(credentials: CredentialProfile) -> Optional['APIConnector']:
113
+ return APIConnector(credentials.fqdn, credentials.port, credentials.api_user,
114
+ credentials.api_key, skip_ssl_cert_check=not credentials.verify_ssl,
115
+ org_id=credentials.org_id, name=credentials.name)
116
+
111
117
  @staticmethod
112
118
  def create_from_credentials_in_file(fqdn_or_profile_name: str, request_if_missing: bool = False,
113
119
  credential_file: Optional[str] = None) -> Optional['APIConnector']:
@@ -115,9 +121,7 @@ class APIConnector:
115
121
  credentials = pylo.get_credentials_from_file(fqdn_or_profile_name, credential_file)
116
122
 
117
123
  if credentials is not None:
118
- return APIConnector(credentials.fqdn, credentials.port, credentials.api_user,
119
- credentials.api_key, skip_ssl_cert_check=not credentials.verify_ssl,
120
- org_id=credentials.org_id, name=credentials.name)
124
+ return APIConnector.create_from_credentials_object(credentials)
121
125
 
122
126
  if not request_if_missing:
123
127
  return None
@@ -362,7 +366,7 @@ class APIConnector:
362
366
  else:
363
367
  raise pylo.PyloEx("Unsupported object type '{}'".format(object_type))
364
368
 
365
- def get_pce_objects(self, include_deleted_workloads=False, list_of_objects_to_load: Optional[List[str]] = None):
369
+ def get_pce_objects(self, include_deleted_workloads=False, list_of_objects_to_load: Optional[List[str]] = None, force_async_mode=False):
366
370
 
367
371
  objects_to_load = {}
368
372
  if list_of_objects_to_load is not None:
@@ -389,7 +393,7 @@ class APIConnector:
389
393
  errors = []
390
394
  thread_queue = Queue()
391
395
 
392
- def get_objects(q: Queue, thread_num: int):
396
+ def get_objects(q: Queue, thread_num: int, force_async_mode=False):
393
397
  while True:
394
398
  object_type, errors = q.get()
395
399
  try:
@@ -397,7 +401,7 @@ class APIConnector:
397
401
  q.task_done()
398
402
  continue
399
403
  if object_type == 'workloads':
400
- if self.get_objects_count_by_type(object_type) > default_max_objects_for_sync_calls:
404
+ if self.get_objects_count_by_type(object_type) > default_max_objects_for_sync_calls or force_async_mode:
401
405
  data['workloads'] = self.objects_workload_get(include_deleted=include_deleted_workloads)
402
406
  else:
403
407
  data['workloads'] = self.objects_workload_get(include_deleted=include_deleted_workloads, async_mode=False, max_results=default_max_objects_for_sync_calls)
@@ -456,7 +460,7 @@ class APIConnector:
456
460
  q.task_done()
457
461
 
458
462
  for i in range(threads_count):
459
- worker = Thread(target=get_objects, args=(thread_queue, i))
463
+ worker = Thread(target=get_objects, args=(thread_queue, i, force_async_mode,))
460
464
  worker.daemon = True
461
465
  worker.start()
462
466
 
@@ -97,7 +97,7 @@ def get_all_credentials_from_file(credential_file: str ) -> List[CredentialProfi
97
97
 
98
98
 
99
99
  def get_credentials_from_file(fqdn_or_profile_name: str = None,
100
- credential_file: str = None) -> CredentialProfile:
100
+ credential_file: str = None, fail_with_an_exception=True) -> Optional[CredentialProfile]:
101
101
 
102
102
  if fqdn_or_profile_name is None:
103
103
  log.debug("No fqdn_or_profile_name provided, profile_name=default will be used")
@@ -121,9 +121,12 @@ def get_credentials_from_file(fqdn_or_profile_name: str = None,
121
121
  if credential_profile.fqdn.lower() == fqdn_or_profile_name.lower():
122
122
  return credential_profile
123
123
 
124
- raise PyloEx("No profile found in credential file '{}' with fqdn: {}".
124
+ if fail_with_an_exception:
125
+ raise PyloEx("No profile found in credential file '{}' with fqdn: {}".
125
126
  format(credential_file, fqdn_or_profile_name))
126
127
 
128
+ return None
129
+
127
130
 
128
131
  def list_potential_credential_files() -> List[str]:
129
132
  """
@@ -204,9 +207,7 @@ def create_credential_in_default_file(data: CredentialFileEntry) -> str:
204
207
  return file_path
205
208
 
206
209
 
207
- def encrypt_api_key_with_paramiko_key(ssh_key: paramiko.AgentKey, api_key: str) -> str:
208
-
209
-
210
+ def encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key: paramiko.AgentKey, api_key: str) -> str:
210
211
  def encrypt(raw: str, key: bytes) -> bytes:
211
212
  """
212
213
 
@@ -220,7 +221,7 @@ def encrypt_api_key_with_paramiko_key(ssh_key: paramiko.AgentKey, api_key: str)
220
221
 
221
222
 
222
223
  # generate a random 128bit key
223
- session_key_to_sign = os.urandom(16)
224
+ session_key_to_sign = os.urandom(32)
224
225
 
225
226
  signed_message = ssh_key.sign_ssh_data(session_key_to_sign)
226
227
 
@@ -236,7 +237,7 @@ def encrypt_api_key_with_paramiko_key(ssh_key: paramiko.AgentKey, api_key: str)
236
237
  return api_key
237
238
 
238
239
 
239
- def decrypt_api_key_with_paramiko_key(encrypted_api_key_payload: str) -> str:
240
+ def decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload: str) -> str:
240
241
  def decrypt(token_b64_encoded: str, key: bytes):
241
242
  f = Fernet(base64.urlsafe_b64encode(key))
242
243
  return f.decrypt(token_b64_encoded).decode('utf-8')
@@ -277,7 +278,7 @@ def decrypt_api_key(encrypted_api_key_payload: str) -> str:
277
278
  if not encrypted_api_key_payload.startswith("$encrypted$:"):
278
279
  raise PyloEx("Invalid encrypted API key format")
279
280
  if encrypted_api_key_payload.startswith("$encrypted$:ssh-Fernet:"):
280
- return decrypt_api_key_with_paramiko_key(encrypted_api_key_payload)
281
+ return decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload)
281
282
 
282
283
  raise PyloEx("Unsupported encryption method: {}".format(encrypted_api_key_payload.split(":")[1]))
283
284
 
@@ -1,29 +1,56 @@
1
- from typing import Dict, Any, List, Optional, TypedDict, NotRequired, Union
2
-
3
- try:
4
- import xlsxwriter
5
- except ImportError:
6
- import xlsxwriter
7
-
8
- try:
9
- import openpyxl
10
- except ImportError:
11
- import openpyxl
1
+ from typing import Dict, Any, List, Optional, TypedDict, NotRequired, Union, Iterable
12
2
 
3
+ import xlsxwriter
4
+ import openpyxl
13
5
  import csv
14
- import illumio_pylo as pylo
15
6
  import os
16
7
 
8
+ import illumio_pylo as pylo
9
+
10
+
11
+ class ExcelHeader:
12
+ def __init__(self, name: str, nice_name: Optional[str] = None, max_width: Optional[int] = None, wrap_text: Optional[bool] = None, is_url: [bool] = False, url_text: str = 'Link'):
13
+ self.name = name
14
+ self.nice_name:str = nice_name if nice_name is not None else name
15
+ self.max_width = max_width
16
+ self.wrap_text = wrap_text
17
+ self.url_text = url_text
18
+ self.is_url = is_url
19
+
20
+
21
+ class ExcelHeaderSet(list[ExcelHeader]):
22
+ def __init__(self, headers: Optional[Iterable[str|ExcelHeader]]):
23
+ super(ExcelHeaderSet, self).__init__()
24
+ if headers is not None:
25
+ for header in headers:
26
+ self.append(header)
27
+
28
+ def append(self, value: str|ExcelHeader):
29
+ if type(value) is str:
30
+ super(ExcelHeaderSet, self).append(ExcelHeader(name=value))
31
+ elif type(value) is ExcelHeader:
32
+ super(ExcelHeaderSet, self).append(value)
33
+ else:
34
+ raise pylo.PyloEx("ExcelHeaderSet.append() must be a string or a dict (ExcelHeader)")
35
+
36
+ def _check_unique(self, new_header_name: str):
37
+ if new_header_name in self:
38
+ raise pylo.PyloEx("Header '{}' is already in the set".format(new_header_name))
17
39
 
18
- class ExcelHeader(TypedDict):
19
- name: str
20
- nice_name: NotRequired[str]
21
- max_width: NotRequired[int]
22
- wrap_text: NotRequired[bool]
40
+ def extend(self, iterable):
41
+ for item in iterable:
42
+ self.append(item)
43
+
44
+ def get_header(self, header_name: str) -> Optional[ExcelHeader]:
45
+ for header in self:
46
+ if header.name == header_name:
47
+ return header
48
+ return None
23
49
 
24
50
 
25
51
  class ArrayToExport:
26
- def __init__(self, headers):
52
+
53
+ def __init__(self, headers: List[str]):
27
54
  self._headers = headers
28
55
  self._columns_count = len(headers)
29
56
  self._lines = []
@@ -106,8 +133,8 @@ class ArraysToExcel:
106
133
  _sheets: Dict[str, 'ArraysToExcel.Sheet']
107
134
 
108
135
  class Sheet:
109
- def __init__(self, headers: List[str|ExcelHeader], force_all_wrap_text=True, sheet_color: Optional[str] = None, order_by: Optional[List[str]] = None, multivalues_cell_delimiter=' '):
110
- self._headers: List[str|ExcelHeader] = headers
136
+ def __init__(self, headers: ExcelHeaderSet, force_all_wrap_text=True, sheet_color: Optional[str] = None, order_by: Optional[List[str]] = None, multivalues_cell_delimiter=' '):
137
+ self._headers: ExcelHeaderSet = headers
111
138
  self._columns_count = len(headers)
112
139
  self._lines = []
113
140
  self._columns_wrap = []
@@ -121,20 +148,18 @@ class ArraysToExcel:
121
148
  self._headers_index_to_name = []
122
149
  index = 0
123
150
 
124
- for header_name in headers:
151
+ for header in headers:
125
152
  self._columns_wrap.append(force_all_wrap_text)
126
153
 
127
- if type(header_name) is str:
128
- self._headers_index_to_name.append(header_name)
129
- self._headers_name_to_index[header_name] = index
130
- else:
131
- header_name['nice_name'] = header_name.get('nice_name', header_name['name'])
132
- self._headers_index_to_name.append(header_name['name'])
133
- self._headers_name_to_index[header_name['name']] = index
154
+ if header.nice_name is not None:
155
+ header.nice_name = header.name
156
+
157
+ self._headers_index_to_name.append(header.name)
158
+ self._headers_name_to_index[header.name] = index
134
159
 
135
- wrap = header_name.get('wrap_text')
136
- if wrap is not None and not wrap:
137
- self._columns_wrap[len(self._columns_wrap)-1] = False
160
+ wrap = header.wrap_text
161
+ if wrap is not None and not wrap:
162
+ self._columns_wrap[len(self._columns_wrap)-1] = False
138
163
 
139
164
  index += 1
140
165
 
@@ -142,20 +167,11 @@ class ArraysToExcel:
142
167
  ):
143
168
  headers: List[str] = []
144
169
  for header in self._headers:
145
- if type(header) is str:
146
- headers.append(header)
147
- continue
148
- if 'nice_name' in header:
149
- headers.append(header['nice_name'])
150
- continue
151
- headers.append(header['name'])
170
+ headers.append(header.name)
152
171
 
153
172
  headers_id: List[str] = []
154
173
  for header in self._headers:
155
- if type(header) is str:
156
- headers_id.append(header)
157
- continue
158
- headers_id.append(header['name'])
174
+ headers_id.append(header.name)
159
175
 
160
176
 
161
177
  exporter = ArrayToExport(headers)
@@ -180,10 +196,7 @@ class ArraysToExcel:
180
196
  def add_line_from_object(self, record):
181
197
  new_line = []
182
198
  for header in self._headers:
183
- if type(header) is str:
184
- new_line.append(record.get(header))
185
- else:
186
- new_line.append(record.get(header['name']))
199
+ new_line.append(record.get(header.name))
187
200
 
188
201
  self._lines.append(new_line)
189
202
 
@@ -226,21 +239,26 @@ class ArraysToExcel:
226
239
 
227
240
  columns_max_width = []
228
241
  for header in self._headers:
229
- columns_max_width.append(0)
242
+ columns_max_width.append(0)
230
243
 
244
+
245
+
246
+ # for each line, find the max length of string for each column and also add it to the data array
231
247
  for line in self._lines:
232
248
  new_line = []
233
- item_index = 0
234
- for item in line:
249
+
250
+ for item_index, item in enumerate(line):
235
251
  if type(item) is list:
236
252
  new_line.append(pylo.string_list_to_text(item, self._multivalues_cell_delimiter))
237
253
  else:
238
- new_line.append(item)
254
+ if self._headers[item_index].is_url:
255
+ new_line.append('=HYPERLINK("{}", "{}")'.format(item,self._headers[item_index].url_text))
256
+ else:
257
+ new_line.append(item)
258
+ length = find_length(new_line[item_index])
259
+ if length > columns_max_width[item_index]:
260
+ columns_max_width[item_index] = length
239
261
 
240
- length = find_length(new_line[item_index])
241
- if length > columns_max_width[item_index]:
242
- columns_max_width[item_index] = length
243
- item_index += 1
244
262
 
245
263
  xls_data.append(new_line)
246
264
 
@@ -252,14 +270,10 @@ class ArraysToExcel:
252
270
 
253
271
  header_max_width_setting = None
254
272
 
255
- if type(header) is str:
256
- xls_headers.append({'header': header, 'format': cell_format})
257
- column_name_length = len(header) + 2 # add 2 for dropdown menus
258
- else:
259
- column_name = header.get('nice_name') or header.get('name')
260
- xls_headers.append({'header': column_name, 'format': cell_format})
261
- column_name_length = len(column_name) + 2 # add 2 for dropdown menus
262
- header_max_width_setting = header.get('max_width')
273
+ column_name = header.nice_name
274
+ xls_headers.append({'header': column_name, 'format': cell_format})
275
+ column_name_length = len(column_name) + 2 # add 2 for dropdown menus
276
+ header_max_width_setting = header.max_width
263
277
 
264
278
  # default is to use width=longest string
265
279
  column_width = columns_max_width[header_index]+1
@@ -296,7 +310,7 @@ class ArraysToExcel:
296
310
  def __init__(self):
297
311
  self._sheets = {}
298
312
 
299
- def create_sheet(self, name: str, headers, force_all_wrap_text: bool = True, sheet_color: Optional[str] = None,
313
+ def create_sheet(self, name: str, headers: ExcelHeaderSet, force_all_wrap_text: bool = True, sheet_color: Optional[str] = None,
300
314
  order_by: Optional[List[str]] = None, multivalues_cell_delimiter: str = ' ') -> Sheet:
301
315
  if name in self._sheets:
302
316
  pylo.PyloEx("A sheet named '{}' already exists".format(name))
@@ -71,6 +71,15 @@ class IP4Map:
71
71
  self._entries.append(new_entry)
72
72
  self.sort_and_recalculate()
73
73
 
74
+ def add_another_map(self, another_map: 'IP4Map', skip_recalculation=False):
75
+ for entry in another_map._entries:
76
+ self._entries.append(entry)
77
+
78
+ if skip_recalculation:
79
+ return
80
+
81
+ self.sort_and_recalculate()
82
+
74
83
  def intersection(self, another_map: 'IP4Map'):
75
84
 
76
85
  inverted_map = IP4Map()
@@ -13,7 +13,7 @@ class LabeledObject:
13
13
  def get_labels_dict(self):
14
14
  return self._labels.copy()
15
15
 
16
- def get_labels(self):
16
+ def get_labels(self) -> Iterable['pylo.Label']:
17
17
  return self._labels.values()
18
18
 
19
19
  def set_label(self, label :'pylo.Label'):
@@ -135,7 +135,10 @@ class Organization:
135
135
  object_to_load = pylo.APIConnector.get_all_object_types()
136
136
 
137
137
  if self.pce_version is None:
138
- raise pylo.PyloEx('Organization has no "version" specified')
138
+ if 'pce_version' in data:
139
+ self.pce_version = pylo.SoftwareVersion(data['pce_version'])
140
+ else:
141
+ raise pylo.PyloEx('Organization has no "version" specified')
139
142
 
140
143
  self.LabelStore.load_label_dimensions(data.get('label_dimensions'))
141
144
 
@@ -4,7 +4,7 @@ from typing import Optional, List, Union, Dict, Any, NewType
4
4
  import illumio_pylo as pylo
5
5
  from .API.JsonPayloadTypes import RuleServiceReferenceObjectJsonStructure, RuleDirectServiceReferenceObjectJsonStructure
6
6
  from illumio_pylo import Workload, Label, LabelGroup, Ruleset, Referencer, SecurityPrincipal, PyloEx, \
7
- Service, nice_json, string_list_to_text, find_connector_or_die, VirtualService, IPList
7
+ Service, nice_json, string_list_to_text, find_connector_or_die, VirtualService, IPList, PortMap
8
8
 
9
9
  RuleActorsAcceptableTypes = NewType('RuleActorsAcceptableTypes', Union[Workload, Label, LabelGroup, IPList, VirtualService])
10
10
 
@@ -250,6 +250,7 @@ class RuleServiceContainer(pylo.Referencer):
250
250
  self.owner = owner
251
251
  self._items: Dict[Service, Service] = {}
252
252
  self._direct_services: List[DirectServiceInRule] = []
253
+ self._cached_port_map: Optional[PortMap] = None
253
254
 
254
255
  def load_from_json(self, data_list: List[RuleServiceReferenceObjectJsonStructure|RuleDirectServiceReferenceObjectJsonStructure]):
255
256
  ss_store = self.owner.owner.owner.owner.ServiceStore # make it a local variable for fast lookups
@@ -295,6 +296,8 @@ class RuleServiceContainer(pylo.Referencer):
295
296
  :param service:
296
297
  :return: True if the service was removed, False if it was not found
297
298
  """
299
+ self._cached_port_map = None
300
+
298
301
  for i in range(0, len(self._direct_services)):
299
302
  if self._direct_services[i] is service:
300
303
  del(self._direct_services[i])
@@ -302,6 +305,8 @@ class RuleServiceContainer(pylo.Referencer):
302
305
  return False
303
306
 
304
307
  def add_direct_service(self, service: DirectServiceInRule) -> bool:
308
+ self._cached_port_map = None
309
+
305
310
  for member in self._direct_services:
306
311
  if service is member:
307
312
  return False
@@ -352,6 +357,27 @@ class RuleServiceContainer(pylo.Referencer):
352
357
 
353
358
  self.owner.raw_json.update(data)
354
359
 
360
+ def get_port_map(self) -> PortMap:
361
+ """
362
+ Get a PortMap object with all ports and protocols from all services in this container
363
+ :return:
364
+ """
365
+ if self._cached_port_map is not None:
366
+ return self._cached_port_map
367
+
368
+ result = PortMap()
369
+ for service in self._items.values():
370
+ for entry in service.entries:
371
+ result.add(entry.protocol, entry.port, entry.to_port, skip_recalculation=True)
372
+ for direct in self._direct_services:
373
+ result.add(direct.protocol, direct.port, direct.to_port, skip_recalculation=True)
374
+
375
+ result.merge_overlapping_maps()
376
+
377
+ self._cached_port_map = result
378
+
379
+ return result
380
+
355
381
 
356
382
  class RuleHostContainer(pylo.Referencer):
357
383
  def __init__(self, owner: 'pylo.Rule', name: str):
@@ -101,29 +101,17 @@ class RulesetScopeEntry:
101
101
 
102
102
 
103
103
  def to_string(self, label_separator = '|', use_href=False):
104
- string = 'All' + label_separator
105
- if self.app_label is not None:
106
- if use_href:
107
- string = self.app_label.href + label_separator
108
- else:
109
- string = self.app_label.name + label_separator
110
-
111
- if self.env_label is None:
112
- string += 'All' + label_separator
113
- else:
114
- if use_href:
115
- string += self.env_label.href + label_separator
116
- else:
117
- string += self.env_label.name + label_separator
118
-
119
- if self.loc_label is None:
120
- string += 'All'
121
- else:
122
- if use_href:
123
- string += self.loc_label.href
104
+ string = ''
105
+ for label_type in self.owner.owner.owner.owner.LabelStore.label_types:
106
+ label = self._labels.get(label_type)
107
+ if len(string) > 0:
108
+ string += label_separator
109
+ if label is None:
110
+ string += 'All'
111
+ elif use_href:
112
+ string += label.href
124
113
  else:
125
- string += self.loc_label.name
126
-
114
+ string += label.name
127
115
  return string
128
116
 
129
117
  def is_all_all_all(self):
@@ -156,10 +144,6 @@ class RulesetScopeEntry:
156
144
 
157
145
  class Ruleset:
158
146
 
159
- name: str
160
- href: Optional[str]
161
- description: str
162
-
163
147
  def __init__(self, owner: 'pylo.RulesetStore'):
164
148
  self.owner: 'pylo.RulesetStore' = owner
165
149
  self.href: Optional[str] = None
@@ -169,6 +153,7 @@ class Ruleset:
169
153
  # must keep an ordered list of rules while the dict by href is there for quick searches
170
154
  self._rules_by_href: Dict[str, 'pylo.Rule'] = {}
171
155
  self._rules: List['pylo.Rule'] = []
156
+ self.disabled: bool = False
172
157
 
173
158
  @property
174
159
  def rules(self):
@@ -211,6 +196,9 @@ class Ruleset:
211
196
  raise pylo.PyloEx("Cannot find Ruleset href in JSON data: \n" + pylo.Helpers.nice_json(data))
212
197
  self.href = data['href']
213
198
 
199
+ if 'enabled' in data:
200
+ self.disabled = not data['enabled']
201
+
214
202
  scopes_json = data.get('scopes')
215
203
  if scopes_json is None:
216
204
  raise pylo.PyloEx("Cannot find Ruleset scope in JSON data: \n" + pylo.Helpers.nice_json(data))
@@ -287,7 +275,7 @@ class Ruleset:
287
275
  if pce_fqdn is None or pce_port is None:
288
276
  connector = pylo.find_connector_or_die(self)
289
277
  if pce_fqdn is None:
290
- pce_fqdn = connector.hostname
278
+ pce_fqdn = connector.fqdn
291
279
  if pce_port is None:
292
280
  pce_port = connector.port
293
281