illumio-pylo 0.3.6__tar.gz → 0.3.8__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.6/illumio_pylo.egg-info → illumio_pylo-0.3.8}/PKG-INFO +4 -4
  2. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/CredentialsManager.py +22 -1
  3. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Helpers/exports.py +1 -1
  4. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/__init__.py +1 -1
  5. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/__init__.py +1 -1
  6. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/credential_manager.py +30 -35
  7. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/iplist_import_from_file.py +57 -33
  8. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/ruleset_export.py +0 -3
  9. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/utils/misc.py +2 -0
  10. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/ven_duplicate_remover.py +1 -0
  11. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/workload_import.py +6 -6
  12. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/workload_update.py +7 -4
  13. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8/illumio_pylo.egg-info}/PKG-INFO +4 -4
  14. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo.egg-info/requires.txt +3 -3
  15. illumio_pylo-0.3.8/requirements.txt +8 -0
  16. illumio_pylo-0.3.6/requirements.txt +0 -8
  17. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.devcontainer/Dockerfile +0 -0
  18. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.devcontainer/devcontainer.json +0 -0
  19. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.gitattributes +0 -0
  20. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.github/workflows/doxygen-publish.yml +0 -0
  21. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.github/workflows/make-binaries.yml +0 -0
  22. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.github/workflows/python-publish.yml +0 -0
  23. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/.gitignore +0 -0
  24. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/LICENSE +0 -0
  25. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/README.md +0 -0
  26. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/check_unique_hostnames.py +0 -0
  27. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/check_unique_services.py +0 -0
  28. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/delete_all_workloads.py +0 -0
  29. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/delete_unused_services.py +0 -0
  30. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/explorer_report_exporter.py +0 -0
  31. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/export_rules_to_firewall.py +0 -0
  32. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/generate-random-workloads.py +0 -0
  33. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/healthcheck_log.py +0 -0
  34. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/import-labels.py +0 -0
  35. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/import_workloads_placeholders.py +0 -0
  36. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/iplists_stats_duplicates_unused_finder.py +0 -0
  37. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/recalculate_explorer_logs.py +0 -0
  38. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/recalculate_explorer_logs_multithreaded.py +0 -0
  39. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/rules_exporter.py +0 -0
  40. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/rules_exporter_special.py +0 -0
  41. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/test.py +0 -0
  42. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/test_change_workload_desc.py +0 -0
  43. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/test_query.py +0 -0
  44. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/test_query2.py +0 -0
  45. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/test_securityprincipals.py +0 -0
  46. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/ven_idle_to_illumination.py +0 -0
  47. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/dev_playground/ven_reassign_pce.py +0 -0
  48. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/examples/explorer_query.py +0 -0
  49. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/examples/extend_cli.py +0 -0
  50. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/APIConnector.py +0 -0
  51. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/AuditLog.py +0 -0
  52. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/ClusterHealth.py +0 -0
  53. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/Explorer.py +0 -0
  54. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/JsonPayloadTypes.py +0 -0
  55. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/RuleSearchQuery.py +0 -0
  56. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/API/__init__.py +0 -0
  57. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/AgentStore.py +0 -0
  58. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Exception.py +0 -0
  59. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Helpers/__init__.py +0 -0
  60. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Helpers/functions.py +0 -0
  61. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/IPList.py +0 -0
  62. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/IPMap.py +0 -0
  63. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Label.py +0 -0
  64. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/LabelCommon.py +0 -0
  65. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/LabelGroup.py +0 -0
  66. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/LabelStore.py +0 -0
  67. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/LabeledObject.py +0 -0
  68. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Organization.py +0 -0
  69. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Query.py +0 -0
  70. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/ReferenceTracker.py +0 -0
  71. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Rule.py +0 -0
  72. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Ruleset.py +0 -0
  73. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/RulesetStore.py +0 -0
  74. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/SecurityPrincipal.py +0 -0
  75. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Service.py +0 -0
  76. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/SoftwareVersion.py +0 -0
  77. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/VirtualService.py +0 -0
  78. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/VirtualServiceStore.py +0 -0
  79. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/Workload.py +0 -0
  80. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/WorkloadStore.py +0 -0
  81. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/WorkloadStoreSubClasses.py +0 -0
  82. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/NativeParsers.py +0 -0
  83. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/__main__.py +0 -0
  84. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/__init__.py +0 -0
  85. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/iplist_analyzer.py +0 -0
  86. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/update_pce_objects_cache.py +0 -0
  87. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/utils/LabelCreation.py +0 -0
  88. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/utils/__init__.py +0 -0
  89. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/ven_compatibility_report_export.py +0 -0
  90. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/ven_idle_to_visibility.py +0 -0
  91. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/ven_upgrader.py +0 -0
  92. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/workload_export.py +0 -0
  93. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/workload_reset_names_to_null.py +0 -0
  94. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/cli/commands/workload_used_in_rule_finder.py +0 -0
  95. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/docs/Doxygen +0 -0
  96. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/tmp.py +0 -0
  97. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/__init__.py +0 -0
  98. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/cli.py +0 -0
  99. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/credentials.example.json +0 -0
  100. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/health_monitoring.py +0 -0
  101. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/resources/iplists-import-example.csv +0 -0
  102. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  103. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +0 -0
  104. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/resources/workloads-import-example.csv +0 -0
  105. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  106. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo.egg-info/SOURCES.txt +0 -0
  107. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo.egg-info/dependency_links.txt +0 -0
  108. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/illumio_pylo.egg-info/top_level.txt +0 -0
  109. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/pyproject.toml +0 -0
  110. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/setup.cfg +0 -0
  111. {illumio_pylo-0.3.6 → illumio_pylo-0.3.8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.6
3
+ Version: 0.3.8
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,11 +187,11 @@ 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.5
191
- Requires-Dist: openpyxl~=3.1.2
190
+ Requires-Dist: cryptography==42.0.7
191
+ Requires-Dist: openpyxl~=3.1.3
192
192
  Requires-Dist: paramiko~=3.4.0
193
193
  Requires-Dist: prettytable~=3.10.0
194
- Requires-Dist: requests~=2.31.0
194
+ Requires-Dist: requests~=2.32.0
195
195
  Requires-Dist: xlsxwriter~=3.2.0
196
196
 
197
197
  README.md
@@ -46,7 +46,6 @@ class CredentialProfile:
46
46
 
47
47
  self.raw_json: Optional[CredentialFileEntry] = None
48
48
 
49
-
50
49
  @staticmethod
51
50
  def from_credentials_file_entry(credential_file_entry: CredentialFileEntry, originating_file: Optional[str] = None):
52
51
  return CredentialProfile(credential_file_entry['name'],
@@ -303,3 +302,25 @@ def find_ssh_key_from_fingerprint(fingerprint: bytes) -> Optional[paramiko.Agent
303
302
  if key.get_fingerprint() == fingerprint:
304
303
  return key
305
304
  return None
305
+
306
+
307
+ def get_supported_keys_from_ssh_agent() -> list[paramiko.AgentKey]:
308
+ keys = paramiko.Agent().get_keys()
309
+ # filter out ECDSA NISTPXXX and sk-ssh-ed25519
310
+ # RSA and ED25519 keys are reported to be working
311
+ return [key for key in keys if not (key.get_name().startswith("ecdsa-sha2-nistp") or
312
+ key.get_name().startswith("sk-ssh-ed25519"))]
313
+
314
+
315
+ def is_encryption_available() -> bool:
316
+ if paramiko is None:
317
+ return False
318
+
319
+ # does paramiko have an agent module and does it have any supported key?
320
+ if hasattr(paramiko, 'Agent'):
321
+ keys = get_supported_keys_from_ssh_agent()
322
+ return len(keys) > 0
323
+
324
+ return False
325
+
326
+
@@ -436,7 +436,7 @@ class CsvExcelToObject:
436
436
  for col_index in range(1, source_worksheet.max_column+1):
437
437
  item = source_worksheet.cell(row_count, col_index).value
438
438
  if item is None or len(item) < 1:
439
- raise pylo.PyloEx('Excel headers has blank fields, this is not supported')
439
+ raise pylo.PyloEx('Excel headers has blank field at column #{}'.format(col_index))
440
440
  self._detected_headers.append(item.lower())
441
441
  if strict_headers and item.lower() not in mandatory_headers_dict:
442
442
  raise pylo.PyloEx("CSV/Excel headers have an unexpected header named '{}'".format(item))
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.6"
1
+ __version__ = "0.3.8"
2
2
 
3
3
  from typing import Callable
4
4
 
@@ -151,7 +151,7 @@ def run(forced_command_name: Optional[str] = None):
151
151
  print("**** END OF {} UTILITY ****".format(selected_command.name.upper()))
152
152
  print("Command Specific Execution time: {:.2f} seconds".format(time.perf_counter() - command_execution_time_start))
153
153
  print("CLI started at {} and finished at {}".format(cli_start_time, cli_end_time))
154
- print("CLI Total Execution time: {}".format(cli_end_time - cli_start_time))
154
+ print("CLI Total Execution time: {}".format((cli_end_time - cli_start_time)))
155
155
  print()
156
156
 
157
157
 
@@ -9,9 +9,9 @@ import paramiko
9
9
  import illumio_pylo as pylo
10
10
  import click
11
11
  from illumio_pylo.API.CredentialsManager import get_all_credentials, create_credential_in_file, CredentialFileEntry, \
12
- create_credential_in_default_file, \
12
+ create_credential_in_default_file, \
13
13
  get_credentials_from_file, encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305, \
14
- decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305, decrypt_api_key_with_paramiko_ssh_key_fernet
14
+ decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305, get_supported_keys_from_ssh_agent, is_encryption_available
15
15
 
16
16
  from illumio_pylo import log
17
17
  from . import Command
@@ -117,32 +117,33 @@ def __main(args, **kwargs):
117
117
  "api_key": api_key
118
118
  }
119
119
 
120
- encrypt_api_key = click.prompt('> Encrypt API (requires an SSH agent running and an RSA or Ed25519 key) ? Y/N', type=bool)
121
- if encrypt_api_key:
122
- print("Available keys (ECDSA NISTPXXX keys and a few others are not supported and will be filtered out):")
123
- ssh_keys = paramiko.Agent().get_keys()
124
- # filter out ECDSA NISTPXXX and sk-ssh-ed25519@openssh.com
125
- ssh_keys = get_supported_keys_from_ssh_agent()
126
-
127
- # display a table of keys
128
- print_keys(keys=ssh_keys, display_index=True)
129
- print()
130
-
131
- index_of_selected_key = click.prompt('> Select key by ID#', type=click.IntRange(0, len(ssh_keys)-1))
132
- selected_ssh_key = ssh_keys[index_of_selected_key]
133
- print("Selected key: {} | {} | {}".format(selected_ssh_key.get_name(),
134
- selected_ssh_key.get_fingerprint().hex(),
135
- selected_ssh_key.comment))
136
- print(" * encrypting API key with selected key (you may be prompted by your SSH agent for confirmation or PIN code) ...", flush=True, end="")
137
- # encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key=selected_ssh_key, api_key=api_key)
138
- encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(ssh_key=selected_ssh_key, api_key=api_key)
139
- print("OK!")
140
- print(" * trying to decrypt the encrypted API key...", flush=True, end="")
141
- decrypted_api_key = decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(encrypted_api_key_payload=encrypted_api_key)
142
- if decrypted_api_key != api_key:
143
- raise pylo.PyloEx("Decrypted API key does not match original API key")
144
- print("OK!")
145
- credentials_data["api_key"] = encrypted_api_key
120
+ if is_encryption_available():
121
+ encrypt_api_key = click.prompt('> Encrypt API (requires an SSH agent running and an RSA or Ed25519 key added to them) ? Y/N', type=bool)
122
+ if encrypt_api_key:
123
+ print("Available keys (ECDSA NISTPXXX keys and a few others are not supported and will be filtered out):")
124
+ ssh_keys = get_supported_keys_from_ssh_agent()
125
+
126
+ # display a table of keys
127
+ print_keys(keys=ssh_keys, display_index=True)
128
+ print()
129
+
130
+ index_of_selected_key = click.prompt('> Select key by ID#', type=click.IntRange(0, len(ssh_keys)-1))
131
+ selected_ssh_key = ssh_keys[index_of_selected_key]
132
+ print("Selected key: {} | {} | {}".format(selected_ssh_key.get_name(),
133
+ selected_ssh_key.get_fingerprint().hex(),
134
+ selected_ssh_key.comment))
135
+ print(" * encrypting API key with selected key (you may be prompted by your SSH agent for confirmation or PIN code) ...", flush=True, end="")
136
+ # encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key=selected_ssh_key, api_key=api_key)
137
+ encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(ssh_key=selected_ssh_key, api_key=api_key)
138
+ print("OK!")
139
+ print(" * trying to decrypt the encrypted API key...", flush=True, end="")
140
+ decrypted_api_key = decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(encrypted_api_key_payload=encrypted_api_key)
141
+ if decrypted_api_key != api_key:
142
+ raise pylo.PyloEx("Decrypted API key does not match original API key")
143
+ print("OK!")
144
+ credentials_data["api_key"] = encrypted_api_key
145
+ else:
146
+ print(" * encryption is not available (no SSH agent or compatible key found), storing API key in plain text...")
146
147
 
147
148
 
148
149
  cwd = os.getcwd()
@@ -187,15 +188,9 @@ def __main(args, **kwargs):
187
188
  else:
188
189
  raise pylo.PyloEx("Unknown sub-command '{}'".format(args['sub_command']))
189
190
 
190
- command_object = Command(command_name, __main, fill_parser, credentials_manager_mode=True)
191
191
 
192
+ command_object = Command(command_name, __main, fill_parser, credentials_manager_mode=True)
192
193
 
193
- def get_supported_keys_from_ssh_agent() -> list[paramiko.AgentKey]:
194
- keys = paramiko.Agent().get_keys()
195
- # filter out ECDSA NISTPXXX and sk-ssh-ed25519
196
- # RSA and ED25519 keys are reported to be working
197
- return [key for key in keys if not (key.get_name().startswith("ecdsa-sha2-nistp") or
198
- key.get_name().startswith("sk-ssh-ed25519"))]
199
194
 
200
195
  def print_keys(keys: list[paramiko.AgentKey], display_index = True) -> None:
201
196
 
@@ -1,8 +1,12 @@
1
+ from typing import List
2
+
1
3
  import illumio_pylo as pylo
2
4
  import argparse
3
5
  import sys
6
+ import click
4
7
  from .utils.misc import make_filename_with_timestamp
5
8
  from . import Command
9
+ from ...API.JsonPayloadTypes import IPListObjectCreationJsonStructure
6
10
 
7
11
  command_name = 'iplist-import'
8
12
  objects_load_filter = ['iplists']
@@ -14,20 +18,32 @@ def fill_parser(parser: argparse.ArgumentParser):
14
18
  parser.add_argument('--input-file-delimiter', type=str, required=False, default=',',
15
19
  help='CSV field delimiter')
16
20
 
21
+ parser.add_argument('--output-dir', '-o', type=str, required=False, default='output',
22
+ help='Directory where the output files will be saved')
23
+
17
24
  parser.add_argument('--ignore-if-iplist-exists', action='store_true',
18
25
  help='If an IPList with same same exists, ignore CSV entry')
19
26
 
20
27
  parser.add_argument('--network-delimiter', type=str, required=False, default=',', nargs='?', const=True,
21
28
  help='If an IPList with same same exists, ignore CSV entry')
22
29
 
30
+ parser.add_argument('--proceed', '-p', action='store_true',
31
+ help='If set, the script will proceed with the creation of iplists')
23
32
 
24
- def __main(args, org: pylo.Organization, **kwargs):
25
- input_file = args['input_file']
26
- input_file_delimiter = args['input_file_delimiter']
27
- ignore_if_iplist_exists = args['ignore_if_iplist_exists']
28
- network_delimiter = args['network_delimiter']
33
+ parser.add_argument('--no-confirmation-required', '-n', action='store_true',
34
+ help='If set, the script will proceed with the creation of iplists without asking for confirmation')
29
35
 
30
- output_file_prefix = make_filename_with_timestamp('import-iplists-results_')
36
+
37
+ def __main(args, org: pylo.Organization, **kwargs):
38
+ setting_input_file = args['input_file']
39
+ settings_input_file_delimiter = args['input_file_delimiter']
40
+ settings_ignore_if_iplist_exists = args['ignore_if_iplist_exists']
41
+ settings_network_delimiter = args['network_delimiter']
42
+ settings_proceed = args['proceed']
43
+ settings_no_confirmation_required = args['no_confirmation_required']
44
+ settings_output_dir: str = args['output_dir']
45
+
46
+ output_file_prefix = make_filename_with_timestamp('import-iplists-results_', settings_output_dir)
31
47
  output_file_csv = output_file_prefix + '.csv'
32
48
  output_file_excel = output_file_prefix + '.xlsx'
33
49
 
@@ -44,8 +60,8 @@ def __main(args, org: pylo.Organization, **kwargs):
44
60
  pylo.file_clean(output_file_csv)
45
61
  pylo.file_clean(output_file_excel)
46
62
 
47
- print(" * Loading CSV input file '{}'...".format(input_file), flush=True, end='')
48
- csv_data = pylo.CsvExcelToObject(input_file, expected_headers=csv_expected_fields, csv_delimiter=input_file_delimiter)
63
+ print(" * Loading CSV input file '{}'...".format(setting_input_file), flush=True, end='')
64
+ csv_data = pylo.CsvExcelToObject(setting_input_file, expected_headers=csv_expected_fields, csv_delimiter=settings_input_file_delimiter)
49
65
  print('OK')
50
66
  print(" - CSV has {} columns and {} lines (headers don't count)".format(csv_data.count_columns(), csv_data.count_lines()))
51
67
  # print(pylo.nice_json(csv_data._objects))
@@ -71,7 +87,7 @@ def __main(args, org: pylo.Organization, **kwargs):
71
87
  sys.exit(1)
72
88
  else:
73
89
  csv_object['**not_created_reason**'] = 'Found duplicated name in PCE'
74
- if not ignore_if_iplist_exists:
90
+ if not settings_ignore_if_iplist_exists:
75
91
  pylo.log.error("PCE contains iplists with duplicates name from CSV: '{}' at line #{}. Please fix CSV or look for --options to ignore it".format(lower_name, csv_object['*line*']))
76
92
  sys.exit(1)
77
93
  print(" - WARNING: CSV has an entry for iplist name '{}' at line #{} but it exists already in the PCE. It will be ignored.".format(lower_name, csv_object['*line*']))
@@ -89,28 +105,28 @@ def __main(args, org: pylo.Organization, **kwargs):
89
105
  ignored_objects_count += 1
90
106
 
91
107
  print(' * Preparing Iplist JSON data...')
92
- iplists_json_data = []
108
+ iplists_json_data: List[IPListObjectCreationJsonStructure] = []
93
109
  for data in csv_objects_to_create:
94
- new_iplist = {}
110
+ new_iplist: IPListObjectCreationJsonStructure = {}
95
111
  iplists_json_data.append(new_iplist)
96
112
 
97
113
  if len(data['name']) < 1:
98
114
  raise pylo.PyloEx('Iplist at line #{} is missing a name in CSV'.format(data['*line*']))
99
- else:
100
- new_iplist['name'] = data['name']
115
+
116
+ new_iplist['name'] = str(data['name'])
101
117
 
102
118
  if len(data['description']) > 0:
103
- new_iplist['description'] = data['description']
119
+ new_iplist['description'] = str(data['description'])
104
120
 
105
121
  if len(data['networks']) < 1:
106
122
  print('Iplist at line #{} has empty networks list'.format(data['*line*']))
107
123
  sys.exit(1)
108
124
 
109
- network_delimiter = network_delimiter.replace("\\n", "\n")
125
+ settings_network_delimiter = settings_network_delimiter.replace("\\n", "\n")
110
126
  ip_ranges = []
111
127
  new_iplist['ip_ranges'] = ip_ranges
112
128
 
113
- for network_string in data['networks'].rsplit(network_delimiter):
129
+ for network_string in data['networks'].rsplit(settings_network_delimiter):
114
130
  network_string = network_string.strip(" \r\n") # cleanup trailing characters
115
131
 
116
132
  exclusion = False
@@ -155,30 +171,38 @@ def __main(args, org: pylo.Organization, **kwargs):
155
171
 
156
172
  print(" * DONE")
157
173
 
158
- print(" * Creating {} IPLists".format(len(iplists_json_data)))
159
- total_created_count = 0
160
- total_failed_count = 0
174
+ if settings_proceed is False:
175
+ print(" * Dry-run mode, no changes will be made to the PCE. Use --proceed to actually create the iplists")
176
+
177
+ else:
178
+ if not settings_no_confirmation_required:
179
+ print(" * WARNING: You are about to create {} IPLists in the PCE. Are you sure you want to proceed?".format(len(iplists_json_data)))
180
+ click.confirm("Do you want to proceed with the creation of these labels?", abort=True)
181
+
182
+ print(" * Creating {} IPLists".format(len(iplists_json_data)))
183
+ total_created_count = 0
184
+ total_failed_count = 0
161
185
 
162
- for index in range(0, len(iplists_json_data)):
163
- json_blob = iplists_json_data[index]
164
- print(" - Pushing new iplist '{}' to PCE (#{} of {})... ".format(json_blob['name'], index+1, len(iplists_json_data)), end='', flush=True)
165
- result = org.connector.objects_iplist_create(json_blob)
166
- print("OK")
186
+ for index in range(0, len(iplists_json_data)):
187
+ json_blob: IPListObjectCreationJsonStructure = iplists_json_data[index]
188
+ print(" - Pushing new iplist '{}' to PCE (#{} of {})... ".format(json_blob['name'], index+1, len(iplists_json_data)), end='', flush=True)
189
+ result = org.connector.objects_iplist_create(json_blob)
190
+ print("OK")
167
191
 
168
- href = result.get('href')
169
- if href is None:
170
- raise pylo.PyloEx('API returned unexpected response which is missing a HREF:', result)
192
+ href = result.get('href')
193
+ if href is None:
194
+ raise pylo.PyloEx('API returned unexpected response which is missing a HREF:', result)
171
195
 
172
- total_created_count += 1
173
- csv_objects_to_create[index]['href'] = href
196
+ total_created_count += 1
197
+ csv_objects_to_create[index]['href'] = href
198
+
199
+ csv_data.save_to_csv(output_file_csv, csv_created_fields)
200
+ csv_data.save_to_excel(output_file_excel, csv_created_fields)
174
201
 
175
202
  csv_data.save_to_csv(output_file_csv, csv_created_fields)
176
203
  csv_data.save_to_excel(output_file_excel, csv_created_fields)
177
204
 
178
- csv_data.save_to_csv(output_file_csv, csv_created_fields)
179
- csv_data.save_to_excel(output_file_excel, csv_created_fields)
180
-
181
- print(" * DONE - {} created with success, {} failures and {} ignored. A report was created in {} and {}".format(total_created_count, total_failed_count, ignored_objects_count, output_file_csv, output_file_excel))
205
+ print(" * DONE - {} created with success, {} failures and {} ignored. A report was created in {} and {}".format(total_created_count, total_failed_count, ignored_objects_count, output_file_csv, output_file_excel))
182
206
 
183
207
 
184
208
  command_object = Command(command_name, __main, fill_parser, load_specific_objects_only=objects_load_filter)
@@ -25,7 +25,6 @@ def fill_parser(parser: argparse.ArgumentParser):
25
25
  "Public_NATed")
26
26
 
27
27
 
28
-
29
28
  def __main(args: Dict, org: pylo.Organization, **kwargs):
30
29
 
31
30
  setting_prefix_objects_with_type: bool|str = args['prefix_objects_with_type']
@@ -33,13 +32,11 @@ def __main(args: Dict, org: pylo.Organization, **kwargs):
33
32
  settings_output_file_format = args['format']
34
33
  settings_output_dir = args['output_dir']
35
34
 
36
-
37
35
  if setting_prefix_objects_with_type is False:
38
36
  print(" * Prefix for object types are disabled")
39
37
  else:
40
38
  print(" * Prefix for object types are enabled")
41
39
 
42
-
43
40
  if setting_object_types_as_section is False:
44
41
  print(" * Object types as section are disabled")
45
42
  else:
@@ -2,6 +2,8 @@ import os
2
2
  from datetime import datetime
3
3
  import illumio_pylo as pylo
4
4
 
5
+ default_label_columns_prefix = 'label:'
6
+
5
7
 
6
8
  def make_filename_with_timestamp(prefix: str, output_directory: str = './') -> str:
7
9
  # if output directory starts with a dot, we assume it's a relative path. Same if not it's not starting with a slash
@@ -288,6 +288,7 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
288
288
  # make this command available to the CLI system
289
289
  command_object = Command(command_name, __main, fill_parser, objects_load_filter)
290
290
 
291
+
291
292
  class DuplicateRecordManager:
292
293
  class DuplicatedRecord:
293
294
  def __init__(self, pce_offline_timer_override: Optional[int] = None):
@@ -8,7 +8,7 @@ import click
8
8
  import illumio_pylo as pylo
9
9
  from illumio_pylo import ArraysToExcel, ExcelHeaderSet, ExcelHeader
10
10
  from .utils.LabelCreation import generate_list_of_labels_to_create, create_labels
11
- from .utils.misc import make_filename_with_timestamp
11
+ from .utils.misc import make_filename_with_timestamp, default_label_columns_prefix
12
12
  from . import Command
13
13
 
14
14
  command_name = 'workload-import'
@@ -27,7 +27,7 @@ def fill_parser(parser: argparse.ArgumentParser):
27
27
  parser.add_argument('--ignore-missing-headers', action='store_true',
28
28
  help='Ignore missing headers in the CSV/Excel file for label types')
29
29
 
30
- parser.add_argument('--label-type-header-prefix', type=str, required=False, default='label_',
30
+ parser.add_argument('--label-type-header-prefix', type=str, required=False, default=default_label_columns_prefix,
31
31
  help='Prefix for the label type headers in the CSV/Excel file')
32
32
 
33
33
  parser.add_argument('--ignore-hostname-collision', action='store_true',
@@ -89,7 +89,7 @@ def __main(args, org: pylo.Organization, **kwargs):
89
89
 
90
90
  csv_report_headers = ExcelHeaderSet(['name', 'hostname', 'ip', 'description'])
91
91
  for label_type in org.LabelStore.label_types:
92
- csv_report_headers.append(f'label_{label_type}')
92
+ csv_report_headers.append(f'{settings_header_label_prefix}{label_type}')
93
93
 
94
94
  csv_report_headers.append(ExcelHeader(name='**not_created_reason**'))
95
95
  csv_report_headers.append(ExcelHeader(name='href', max_width=15))
@@ -124,7 +124,7 @@ def __main(args, org: pylo.Organization, **kwargs):
124
124
  if len(labels_to_be_created) > 0:
125
125
  print(" * {} Labels need to created before Workloads can be imported, listing:".format(len(labels_to_be_created)))
126
126
  for label in labels_to_be_created:
127
- print(" - Label: {} (type={})".format(label.name, label.type))
127
+ print(" - Label: {} (type={})".format(label['name'], label['type']))
128
128
  if not settings_no_confirmation_required:
129
129
  click.confirm("Do you want to proceed with the creation of these labels?", abort=True)
130
130
 
@@ -283,8 +283,8 @@ def detect_workloads_name_collisions(csv_data, org: pylo.Organization, ignore_al
283
283
  if csv_object['hostname'] is not None and len(csv_object['hostname']) > 0:
284
284
  lower_hostname = csv_object['hostname'].lower()
285
285
  if lower_name != lower_hostname:
286
- if csv_object['hostname'] not in name_cache:
287
- name_cache[csv_object['hostname']] = WorkloadCollisionItem(from_pce=False, csv_object=csv_object,
286
+ if lower_hostname not in name_cache:
287
+ name_cache[lower_hostname] = WorkloadCollisionItem(from_pce=False, csv_object=csv_object,
288
288
  managed=False)
289
289
  else:
290
290
  if not name_cache[lower_hostname].from_pce:
@@ -6,7 +6,7 @@ import sys
6
6
  import illumio_pylo as pylo
7
7
  from illumio_pylo import ArraysToExcel, ExcelHeader, ExcelHeaderSet
8
8
  from .utils.LabelCreation import generate_list_of_labels_to_create, create_labels
9
- from .utils.misc import make_filename_with_timestamp
9
+ from .utils.misc import make_filename_with_timestamp, default_label_columns_prefix
10
10
  from . import Command
11
11
 
12
12
 
@@ -25,7 +25,7 @@ def fill_parser(parser: argparse.ArgumentParser):
25
25
  parser.add_argument('--input-file-delimiter', type=str, required=False, default=',',
26
26
  help='CSV field delimiter')
27
27
 
28
- parser.add_argument('--label-type-header-prefix', type=str, required=False, default='label_',
28
+ parser.add_argument('--label-type-header-prefix', type=str, required=False, default=default_label_columns_prefix,
29
29
  help='Prefix for the label type headers in the CSV/Excel file')
30
30
 
31
31
  parser.add_argument('--output-dir', '-o', type=str, required=False, default='output',
@@ -49,6 +49,7 @@ def fill_parser(parser: argparse.ArgumentParser):
49
49
  parser.add_argument('--batch-size', type=int, required=False, default=500,
50
50
  help='Number of Workloads to update per API call')
51
51
 
52
+
52
53
  class ContextSingleton:
53
54
 
54
55
  def __init__(self, org: pylo.Organization):
@@ -128,6 +129,7 @@ def __main(args, org: pylo.Organization, **kwargs):
128
129
 
129
130
  print('OK')
130
131
  print(" - CSV has {} columns and {} lines (headers don't count)".format(csv_input_object.count_columns(), csv_input_object.count_lines()))
132
+ context.csv_data = csv_input_object.objects()
131
133
  # </editor-fold desc="CSV input file data extraction">
132
134
 
133
135
 
@@ -307,7 +309,7 @@ def check_and_sanitize_csv_input_data(context, input_match_on_hostname, input_ma
307
309
  if input_match_on_hostname:
308
310
  for csv_data in context.csv_data:
309
311
  name = csv_data['hostname']
310
- name = pylo.Workload.static_name_stripped_fqdn(name)
312
+ name = pylo.Workload.static_name_stripped_fqdn(name).lower()
311
313
  if name is None or len(name) < 1:
312
314
  print(" - ERROR: CSV line #{} has invalid hostname defined: '{}'".format(csv_data['*line*'],
313
315
  csv_data['hostname']),
@@ -332,7 +334,7 @@ def check_and_sanitize_csv_input_data(context, input_match_on_hostname, input_ma
332
334
  continue
333
335
 
334
336
  if href not in context.csv_href_index:
335
- context.csv_name_index[href] = csv_data
337
+ context.csv_href_index[href] = csv_data
336
338
  continue
337
339
 
338
340
  print(" - ERROR: CSV line #{} has duplicate href defined from a previous line: '{}'".format(
@@ -343,6 +345,7 @@ def check_and_sanitize_csv_input_data(context, input_match_on_hostname, input_ma
343
345
  "ERROR! Several ({}) inconsistencies were found in the CSV, please fix them before you continue!".format(
344
346
  csv_check_failed_count))
345
347
  sys.exit(1)
348
+
346
349
  print(" * Done")
347
350
 
348
351
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.6
3
+ Version: 0.3.8
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,11 +187,11 @@ 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.5
191
- Requires-Dist: openpyxl~=3.1.2
190
+ Requires-Dist: cryptography==42.0.7
191
+ Requires-Dist: openpyxl~=3.1.3
192
192
  Requires-Dist: paramiko~=3.4.0
193
193
  Requires-Dist: prettytable~=3.10.0
194
- Requires-Dist: requests~=2.31.0
194
+ Requires-Dist: requests~=2.32.0
195
195
  Requires-Dist: xlsxwriter~=3.2.0
196
196
 
197
197
  README.md
@@ -1,8 +1,8 @@
1
1
  click==8.1.7
2
2
  colorama~=0.4.4
3
- cryptography~=42.0.5
4
- openpyxl~=3.1.2
3
+ cryptography==42.0.7
4
+ openpyxl~=3.1.3
5
5
  paramiko~=3.4.0
6
6
  prettytable~=3.10.0
7
- requests~=2.31.0
7
+ requests~=2.32.0
8
8
  xlsxwriter~=3.2.0
@@ -0,0 +1,8 @@
1
+ click==8.1.7
2
+ colorama~=0.4.4
3
+ cryptography==42.0.7
4
+ openpyxl~=3.1.3
5
+ paramiko~=3.4.0
6
+ prettytable~=3.10.0
7
+ requests~=2.32.0
8
+ xlsxwriter~=3.2.0
@@ -1,8 +0,0 @@
1
- click==8.1.7
2
- colorama~=0.4.4
3
- cryptography~=42.0.5
4
- openpyxl~=3.1.2
5
- paramiko~=3.4.0
6
- prettytable~=3.10.0
7
- requests~=2.31.0
8
- xlsxwriter~=3.2.0
File without changes
File without changes
File without changes
File without changes
File without changes