illumio-pylo 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- illumio_pylo/API/APIConnector.py +12 -8
- illumio_pylo/API/CredentialsManager.py +9 -8
- illumio_pylo/Helpers/exports.py +77 -63
- illumio_pylo/IPMap.py +9 -0
- illumio_pylo/LabeledObject.py +1 -1
- illumio_pylo/Organization.py +4 -1
- illumio_pylo/Rule.py +27 -1
- illumio_pylo/Ruleset.py +15 -27
- illumio_pylo/Service.py +49 -52
- illumio_pylo/__init__.py +1 -1
- illumio_pylo/cli/__init__.py +19 -6
- illumio_pylo/cli/commands/credential_manager.py +91 -26
- illumio_pylo/cli/commands/ruleset_export.py +44 -37
- illumio_pylo/cli/commands/utils/misc.py +4 -0
- illumio_pylo/cli/commands/ven_compatibility_report_export.py +14 -8
- illumio_pylo/cli/commands/ven_duplicate_remover.py +18 -15
- illumio_pylo/cli/commands/ven_upgrader.py +10 -78
- illumio_pylo/cli/commands/workload_export.py +16 -12
- illumio_pylo/cli/commands/workload_import.py +50 -17
- illumio_pylo/cli/commands/workload_update.py +3 -2
- illumio_pylo/tmp.py +8 -4
- {illumio_pylo-0.3.0.dist-info → illumio_pylo-0.3.2.dist-info}/METADATA +1 -1
- {illumio_pylo-0.3.0.dist-info → illumio_pylo-0.3.2.dist-info}/RECORD +26 -30
- illumio_pylo/utilities/explorer_report_exporter.py +0 -86
- illumio_pylo/utilities/iplists_stats_duplicates_unused_finder.py +0 -75
- illumio_pylo/utilities/ven_idle_to_illumination.py +0 -344
- illumio_pylo/utilities/ven_reassign_pce.py +0 -183
- {illumio_pylo-0.3.0.dist-info → illumio_pylo-0.3.2.dist-info}/LICENSE +0 -0
- {illumio_pylo-0.3.0.dist-info → illumio_pylo-0.3.2.dist-info}/WHEEL +0 -0
- {illumio_pylo-0.3.0.dist-info → illumio_pylo-0.3.2.dist-info}/top_level.txt +0 -0
illumio_pylo/cli/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Optional, Dict
|
|
3
|
-
|
|
3
|
+
import time
|
|
4
|
+
import datetime
|
|
4
5
|
import sys
|
|
5
6
|
import argparse
|
|
6
7
|
from .NativeParsers import BaseParser
|
|
@@ -15,6 +16,8 @@ from illumio_pylo.cli import commands
|
|
|
15
16
|
|
|
16
17
|
def run(forced_command_name: Optional[str] = None):
|
|
17
18
|
|
|
19
|
+
cli_start_time = datetime.datetime.now()
|
|
20
|
+
|
|
18
21
|
def add_native_parser_to_argparse(parser: argparse.ArgumentParser, native_parsers: object):
|
|
19
22
|
# each property of the native parser is an extension of BaseParser, we need to iterate over them and add them to the argparse parser
|
|
20
23
|
for attr_name in dir(native_parsers):
|
|
@@ -33,9 +36,11 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
33
36
|
print(" * Native CLI arguments parsing...")
|
|
34
37
|
attr.execute(args[attr.get_arg_name()], org, padding=' ')
|
|
35
38
|
|
|
36
|
-
parser = argparse.ArgumentParser(description='
|
|
39
|
+
parser = argparse.ArgumentParser(description='PYLO-CLI: Illumio API&More Command Line Interface')
|
|
37
40
|
parser.add_argument('--pce', type=str, required=False,
|
|
38
41
|
help='hostname of the PCE')
|
|
42
|
+
parser.add_argument('--force-async-mode', action='store_true',
|
|
43
|
+
help='Forces the command to run async API queries when required (large PCEs which timeout on specific queries)')
|
|
39
44
|
parser.add_argument('--debug', action='store_true',
|
|
40
45
|
help='Enables extra debugging output in Pylo framework')
|
|
41
46
|
parser.add_argument('--use-cache', action='store_true',
|
|
@@ -89,14 +94,16 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
89
94
|
|
|
90
95
|
print("* Started Pylo CLI version {}".format(pylo.__version__))
|
|
91
96
|
|
|
97
|
+
|
|
92
98
|
if not selected_command.credentials_manager_mode:
|
|
99
|
+
timer_start = time.perf_counter()
|
|
93
100
|
# credential_profile_name is required for all commands except the credential manager
|
|
94
101
|
if credential_profile_name is None:
|
|
95
102
|
raise pylo.PyloEx("The --pce argument is required for this command")
|
|
96
103
|
if settings_use_cache:
|
|
97
104
|
print(" * Loading objects from cached PCE '{}' data... ".format(credential_profile_name), end="", flush=True)
|
|
98
105
|
org = pylo.Organization.get_from_cache_file(credential_profile_name)
|
|
99
|
-
print("OK!")
|
|
106
|
+
print("OK! (execution time: {:.2f} seconds)".format(time.perf_counter() - timer_start))
|
|
100
107
|
connector = pylo.APIConnector.create_from_credentials_in_file(credential_profile_name, request_if_missing=False)
|
|
101
108
|
if connector is not None:
|
|
102
109
|
org.connector = connector
|
|
@@ -106,8 +113,9 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
106
113
|
print("OK!")
|
|
107
114
|
|
|
108
115
|
print(" * Downloading PCE objects from API... ".format(credential_profile_name), end="", flush=True)
|
|
109
|
-
config_data = connector.get_pce_objects(list_of_objects_to_load=selected_command.load_specific_objects_only)
|
|
110
|
-
|
|
116
|
+
config_data = connector.get_pce_objects(list_of_objects_to_load=selected_command.load_specific_objects_only, force_async_mode=args['force_async_mode'])
|
|
117
|
+
timer_download_finished = time.perf_counter()
|
|
118
|
+
print("OK! (execution time: {:.2f} seconds)".format(timer_download_finished - timer_start))
|
|
111
119
|
|
|
112
120
|
org = pylo.Organization(1)
|
|
113
121
|
org.connector = connector
|
|
@@ -116,7 +124,7 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
116
124
|
print(" * Loading objects from PCE '{}' via API... ".format(credential_profile_name), end="", flush=True)
|
|
117
125
|
org.pce_version = connector.get_software_version()
|
|
118
126
|
org.load_from_json(config_data, list_of_objects_to_load=selected_command.load_specific_objects_only)
|
|
119
|
-
print("OK!")
|
|
127
|
+
print("OK! (execution time: {:.2f} seconds)".format(time.perf_counter() - timer_download_finished))
|
|
120
128
|
|
|
121
129
|
print()
|
|
122
130
|
if not selected_command.skip_pce_config_loading:
|
|
@@ -126,6 +134,7 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
126
134
|
print(flush=True)
|
|
127
135
|
|
|
128
136
|
print("**** {} UTILITY ****".format(selected_command.name.upper()), flush=True)
|
|
137
|
+
command_execution_time_start = time.perf_counter()
|
|
129
138
|
if selected_command.native_parsers is None:
|
|
130
139
|
native_parsers = None
|
|
131
140
|
else:
|
|
@@ -138,7 +147,11 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
138
147
|
commands.available_commands[selected_command.name].main(args, org=org, config_data=config_data, connector=connector, pce_cache_was_used=settings_use_cache)
|
|
139
148
|
|
|
140
149
|
print()
|
|
150
|
+
cli_end_time = datetime.datetime.now()
|
|
141
151
|
print("**** END OF {} UTILITY ****".format(selected_command.name.upper()))
|
|
152
|
+
print("Command Specific Execution time: {:.2f} seconds".format(time.perf_counter() - command_execution_time_start))
|
|
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))
|
|
142
155
|
print()
|
|
143
156
|
|
|
144
157
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
from prettytable import PrettyTable
|
|
2
4
|
import argparse
|
|
3
5
|
import os
|
|
@@ -7,7 +9,8 @@ import paramiko
|
|
|
7
9
|
import illumio_pylo as pylo
|
|
8
10
|
import click
|
|
9
11
|
from illumio_pylo.API.CredentialsManager import get_all_credentials, create_credential_in_file, CredentialFileEntry, \
|
|
10
|
-
create_credential_in_default_file,
|
|
12
|
+
create_credential_in_default_file, encrypt_api_key_with_paramiko_ssh_key_fernet, decrypt_api_key_with_paramiko_ssh_key_fernet, \
|
|
13
|
+
get_credentials_from_file
|
|
11
14
|
|
|
12
15
|
from illumio_pylo import log
|
|
13
16
|
from . import Command
|
|
@@ -20,19 +23,24 @@ objects_load_filter = None
|
|
|
20
23
|
def fill_parser(parser: argparse.ArgumentParser):
|
|
21
24
|
sub_parser = parser.add_subparsers(dest='sub_command', required=True)
|
|
22
25
|
list_parser = sub_parser.add_parser('list', help='List all credentials')
|
|
23
|
-
create_parser = sub_parser.add_parser('create', help='Create a new credential')
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
test_parser = sub_parser.add_parser('test', help='Test a credential')
|
|
28
|
+
test_parser.add_argument('--name', required=False, type=str, default=None,
|
|
29
|
+
help='Name of the credential profile to test')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
create_parser = sub_parser.add_parser('create', help='Create a new credential')
|
|
33
|
+
create_parser.add_argument('--name', required=False, type=str, default=None,
|
|
34
|
+
help='Name of the credential')
|
|
35
|
+
create_parser.add_argument('--fqdn', required=False, type=str, default=None,
|
|
28
36
|
help='FQDN of the PCE')
|
|
29
|
-
create_parser.add_argument('--port', required=
|
|
37
|
+
create_parser.add_argument('--port', required=False, type=int, default=None,
|
|
30
38
|
help='Port of the PCE')
|
|
31
|
-
create_parser.add_argument('--org', required=
|
|
39
|
+
create_parser.add_argument('--org', required=False, type=int, default=None,
|
|
32
40
|
help='Organization ID')
|
|
33
|
-
create_parser.add_argument('--api-user', required=
|
|
41
|
+
create_parser.add_argument('--api-user', required=False, type=str, default=None,
|
|
34
42
|
help='API user')
|
|
35
|
-
create_parser.add_argument('--verify-ssl', required=
|
|
43
|
+
create_parser.add_argument('--verify-ssl', required=False, type=bool, default=None,
|
|
36
44
|
help='Verify SSL')
|
|
37
45
|
|
|
38
46
|
|
|
@@ -54,14 +62,10 @@ def __main(args, **kwargs):
|
|
|
54
62
|
)
|
|
55
63
|
|
|
56
64
|
elif args['sub_command'] == 'create':
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
print("Org ID: {}".format(args['org']))
|
|
62
|
-
print("API User: {}".format(args['api_user']))
|
|
63
|
-
print("Verify SSL: {}".format(args['verify_ssl']))
|
|
64
|
-
print()
|
|
65
|
+
|
|
66
|
+
wanted_name = args['name']
|
|
67
|
+
if wanted_name is None:
|
|
68
|
+
wanted_name = click.prompt('> Input a Profile Name (ie: prod-pce)', type=str)
|
|
65
69
|
|
|
66
70
|
print("* Checking if a credential with the same name already exists...", flush=True, end="")
|
|
67
71
|
credentials = get_all_credentials()
|
|
@@ -70,20 +74,52 @@ def __main(args, **kwargs):
|
|
|
70
74
|
raise pylo.PyloEx("A credential named '{}' already exists".format(args['name']))
|
|
71
75
|
print("OK!")
|
|
72
76
|
|
|
77
|
+
wanted_fqdn = args['fqdn']
|
|
78
|
+
if wanted_fqdn is None:
|
|
79
|
+
wanted_fqdn = click.prompt('> PCE FQDN (ie: pce1.mycompany.com)', type=str)
|
|
80
|
+
|
|
81
|
+
wanted_port = args['port']
|
|
82
|
+
if wanted_port is None:
|
|
83
|
+
wanted_port = click.prompt('> PCE Port (ie: 8443)', type=int)
|
|
84
|
+
|
|
85
|
+
wanted_org = args['org']
|
|
86
|
+
if wanted_org is None:
|
|
87
|
+
wanted_org = click.prompt('> Organization ID', type=int)
|
|
88
|
+
|
|
89
|
+
wanted_api_user = args['api_user']
|
|
90
|
+
if wanted_api_user is None:
|
|
91
|
+
wanted_api_user = click.prompt('> API User', type=str)
|
|
92
|
+
|
|
93
|
+
wanted_verify_ssl = args['verify_ssl']
|
|
94
|
+
if wanted_verify_ssl is None:
|
|
95
|
+
wanted_verify_ssl = click.prompt('> Verify SSL/TLS certificate? Y/N', type=bool)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
print()
|
|
99
|
+
print("Recap:")
|
|
100
|
+
print("Name: {}".format(wanted_name))
|
|
101
|
+
print("FQDN: {}".format(wanted_fqdn))
|
|
102
|
+
print("Port: {}".format(wanted_port))
|
|
103
|
+
print("Org ID: {}".format(wanted_org))
|
|
104
|
+
print("API User: {}".format(wanted_api_user))
|
|
105
|
+
print("Verify SSL: {}".format(wanted_verify_ssl))
|
|
106
|
+
print()
|
|
107
|
+
|
|
108
|
+
|
|
73
109
|
# prompt of API key from user input, single line, hidden
|
|
74
110
|
api_key = click.prompt('> API Key', hide_input=True)
|
|
75
111
|
|
|
76
112
|
credentials_data: CredentialFileEntry = {
|
|
77
|
-
"name":
|
|
78
|
-
"fqdn":
|
|
79
|
-
"port":
|
|
80
|
-
"org_id":
|
|
81
|
-
"api_user":
|
|
82
|
-
"verify_ssl":
|
|
113
|
+
"name": wanted_name,
|
|
114
|
+
"fqdn": wanted_fqdn,
|
|
115
|
+
"port": wanted_port,
|
|
116
|
+
"org_id": wanted_org,
|
|
117
|
+
"api_user": wanted_api_user,
|
|
118
|
+
"verify_ssl": wanted_verify_ssl,
|
|
83
119
|
"api_key": api_key
|
|
84
120
|
}
|
|
85
121
|
|
|
86
|
-
encrypt_api_key = click.prompt('> Encrypt API? Y/N', type=bool)
|
|
122
|
+
encrypt_api_key = click.prompt('> Encrypt API (requires an SSH agent running and an RSA or Ed25519 key) ? Y/N', type=bool)
|
|
87
123
|
if encrypt_api_key:
|
|
88
124
|
print("Available keys (ECDSA NISTPXXX keys and a few others are not supported and will be filtered out):")
|
|
89
125
|
ssh_keys = paramiko.Agent().get_keys()
|
|
@@ -100,10 +136,10 @@ def __main(args, **kwargs):
|
|
|
100
136
|
selected_ssh_key.get_fingerprint().hex(),
|
|
101
137
|
selected_ssh_key.comment))
|
|
102
138
|
print(" * encrypting API key with selected key (you may be prompted by your SSH agent for confirmation or PIN code) ...", flush=True, end="")
|
|
103
|
-
encrypted_api_key =
|
|
139
|
+
encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key=selected_ssh_key, api_key=api_key)
|
|
104
140
|
print("OK!")
|
|
105
141
|
print(" * trying to decrypt the encrypted API key...", flush=True, end="")
|
|
106
|
-
decrypted_api_key =
|
|
142
|
+
decrypted_api_key = decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload=encrypted_api_key)
|
|
107
143
|
if decrypted_api_key != api_key:
|
|
108
144
|
raise pylo.PyloEx("Decrypted API key does not match original API key")
|
|
109
145
|
print("OK!")
|
|
@@ -122,6 +158,35 @@ def __main(args, **kwargs):
|
|
|
122
158
|
|
|
123
159
|
print("OK! ({})".format(file_path))
|
|
124
160
|
|
|
161
|
+
elif args['sub_command'] == 'test':
|
|
162
|
+
print("* Profile Tester command")
|
|
163
|
+
wanted_name = args['name']
|
|
164
|
+
if wanted_name is None:
|
|
165
|
+
wanted_name = click.prompt('> Input a Profile Name to test (ie: prod-pce)', type=str)
|
|
166
|
+
found_profile = get_credentials_from_file(wanted_name, fail_with_an_exception=False)
|
|
167
|
+
if found_profile is None:
|
|
168
|
+
print("Cannot find a profile named '{}'".format(wanted_name))
|
|
169
|
+
print("Available profiles:")
|
|
170
|
+
credentials = get_all_credentials()
|
|
171
|
+
for credential in credentials:
|
|
172
|
+
print(" - {}".format(credential.name))
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
175
|
+
print("Selected profile:")
|
|
176
|
+
print(" - Name: {}".format(found_profile.name))
|
|
177
|
+
print(" - FQDN: {}".format(found_profile.fqdn))
|
|
178
|
+
print(" - Port: {}".format(found_profile.port))
|
|
179
|
+
print(" - Org ID: {}".format(found_profile.org_id))
|
|
180
|
+
print(" - API User: {}".format(found_profile.api_user))
|
|
181
|
+
print(" - Verify SSL: {}".format(found_profile.verify_ssl))
|
|
182
|
+
|
|
183
|
+
print("* Testing credential...", flush=True, end="")
|
|
184
|
+
connector = pylo.APIConnector.create_from_credentials_object(found_profile)
|
|
185
|
+
connector.objects_label_dimension_get()
|
|
186
|
+
print("OK!")
|
|
187
|
+
|
|
188
|
+
else:
|
|
189
|
+
raise pylo.PyloEx("Unknown sub-command '{}'".format(args['sub_command']))
|
|
125
190
|
|
|
126
191
|
command_object = Command(command_name, __main, fill_parser, credentials_manager_mode=True)
|
|
127
192
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
|
-
from typing import Dict, List
|
|
3
|
+
from typing import Dict, List, Literal
|
|
4
4
|
|
|
5
5
|
import illumio_pylo as pylo
|
|
6
|
+
from illumio_pylo import ArraysToExcel, ExcelHeader
|
|
6
7
|
from .utils.misc import make_filename_with_timestamp
|
|
7
8
|
from . import Command
|
|
8
9
|
|
|
@@ -11,7 +12,7 @@ command_name = 'rule-export'
|
|
|
11
12
|
|
|
12
13
|
def fill_parser(parser: argparse.ArgumentParser):
|
|
13
14
|
parser.add_argument('--format', '-f', required=False, default='excel',choices=['csv', 'excel'], help='Output file format')
|
|
14
|
-
parser.add_argument('--output', '-o', required=False, default='
|
|
15
|
+
parser.add_argument('--output-dir', '-o', required=False, default='output', help='Directory where to save the output file')
|
|
15
16
|
parser.add_argument('--prefix-objects-with-type', nargs='?', const=True, default=False,
|
|
16
17
|
help='Prefix objects with their type (e.g. "label:mylabel")')
|
|
17
18
|
parser.add_argument('--object-types-as-section', action='store_true', default=False,
|
|
@@ -25,44 +26,26 @@ def fill_parser(parser: argparse.ArgumentParser):
|
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def __main(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{'name': 'services', 'max_width': 30},
|
|
36
|
-
{'name': 'options', 'max_width': 40},
|
|
37
|
-
{'name': 'ruleset_url', 'max_width': 40, 'wrap_text': False},
|
|
38
|
-
{'name': 'ruleset_href', 'max_width': 30, 'wrap_text': False}
|
|
39
|
-
]
|
|
29
|
+
def __main(args: Dict, org: pylo.Organization, **kwargs):
|
|
30
|
+
|
|
31
|
+
setting_prefix_objects_with_type: bool|str = args['prefix_objects_with_type']
|
|
32
|
+
setting_object_types_as_section: bool = args['prefix_objects_with_type']
|
|
33
|
+
settings_output_file_format = args['format']
|
|
34
|
+
settings_output_dir = args['output_dir']
|
|
35
|
+
|
|
40
36
|
|
|
41
|
-
setting_prefix_objects_with_type: bool|str = options['prefix_objects_with_type']
|
|
42
37
|
if setting_prefix_objects_with_type is False:
|
|
43
38
|
print(" * Prefix for object types are disabled")
|
|
44
39
|
else:
|
|
45
40
|
print(" * Prefix for object types are enabled")
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
|
|
48
43
|
if setting_object_types_as_section is False:
|
|
49
44
|
print(" * Object types as section are disabled")
|
|
50
45
|
else:
|
|
51
46
|
print(" * Object types as section are enabled")
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
if output_file_format == "excel":
|
|
55
|
-
output_file_extension = ".xlsx"
|
|
56
|
-
elif output_file_format == "csv":
|
|
57
|
-
output_file_extension = ".csv"
|
|
58
|
-
else:
|
|
59
|
-
raise Exception("Unknown output file format: %s" % output_file_format)
|
|
60
|
-
|
|
61
|
-
output_file_name = options.get('output') + os.sep + make_filename_with_timestamp('rule_export_') + output_file_extension
|
|
62
|
-
output_file_name = os.path.abspath(output_file_name)
|
|
63
|
-
|
|
64
|
-
csv_report = pylo.ArraysToExcel()
|
|
65
|
-
sheet = csv_report.create_sheet('rulesets', csv_report_headers, force_all_wrap_text=True, multivalues_cell_delimiter=',')
|
|
48
|
+
csv_report, output_file_name, sheet = prepare_csv_report_object(settings_output_file_format, settings_output_dir)
|
|
66
49
|
|
|
67
50
|
for ruleset in org.RulesetStore.rulesets:
|
|
68
51
|
for rule in ruleset.rules_ordered_by_type:
|
|
@@ -102,23 +85,47 @@ def __main(options: Dict, org: pylo.Organization, **kwargs):
|
|
|
102
85
|
'services': rule.services.members_to_str("\n"),
|
|
103
86
|
'options': pylo.string_list_to_text(rule_options, "\n"),
|
|
104
87
|
'ruleset_href': ruleset.href,
|
|
105
|
-
'ruleset_url': ruleset.get_ruleset_url()
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
data['type'] = 'intra'
|
|
88
|
+
'ruleset_url': ruleset.get_ruleset_url(),
|
|
89
|
+
'type': 'intra' if rule.is_intra_scope() else 'extra'
|
|
90
|
+
}
|
|
91
|
+
|
|
110
92
|
sheet.add_line_from_object(data)
|
|
111
93
|
|
|
112
|
-
if
|
|
94
|
+
if settings_output_file_format == "csv":
|
|
113
95
|
print(" * Writing export file '{}' ... ".format(output_file_name), end='', flush=True)
|
|
114
96
|
sheet.write_to_csv(output_file_name)
|
|
115
97
|
print("DONE")
|
|
116
|
-
elif
|
|
98
|
+
elif settings_output_file_format == "excel":
|
|
117
99
|
print(" * Writing export file '{}' ... ".format(output_file_name), end='', flush=True)
|
|
118
100
|
csv_report.write_to_excel(output_file_name)
|
|
119
101
|
print("DONE")
|
|
120
102
|
else:
|
|
121
|
-
raise pylo.PyloEx("Unknown format: '{}'".format(
|
|
103
|
+
raise pylo.PyloEx("Unknown format: '{}'".format(args['format']))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def prepare_csv_report_object(output_file_format: Literal['excel', 'csv'], settings_output_dir: str):
|
|
107
|
+
if output_file_format == "excel":
|
|
108
|
+
output_file_extension = ".xlsx"
|
|
109
|
+
elif output_file_format == "csv":
|
|
110
|
+
output_file_extension = ".csv"
|
|
111
|
+
else:
|
|
112
|
+
raise Exception("Unknown output file format: %s" % output_file_format)
|
|
113
|
+
csv_report_headers = pylo.ExcelHeaderSet(
|
|
114
|
+
[ExcelHeader(name='ruleset', max_width=40),
|
|
115
|
+
ExcelHeader(name='scope', max_width=50),
|
|
116
|
+
ExcelHeader(name='type', max_width=10),
|
|
117
|
+
ExcelHeader(name='consumers', max_width=80),
|
|
118
|
+
ExcelHeader(name='providers', max_width=80),
|
|
119
|
+
ExcelHeader(name='services', max_width=30),
|
|
120
|
+
ExcelHeader(name='options', max_width=40),
|
|
121
|
+
ExcelHeader(name='ruleset_url', max_width=40, wrap_text=False),
|
|
122
|
+
ExcelHeader(name='ruleset_href', max_width=30, wrap_text=False)
|
|
123
|
+
])
|
|
124
|
+
csv_report = ArraysToExcel()
|
|
125
|
+
sheet = csv_report.create_sheet('rulesets', csv_report_headers, force_all_wrap_text=True,
|
|
126
|
+
multivalues_cell_delimiter=',')
|
|
127
|
+
output_file_name = make_filename_with_timestamp('rule_export_', settings_output_dir) + output_file_extension
|
|
128
|
+
return csv_report, output_file_name, sheet
|
|
122
129
|
|
|
123
130
|
|
|
124
131
|
command_object = Command(command_name, __main, fill_parser)
|
|
@@ -8,5 +8,9 @@ def make_filename_with_timestamp(prefix: str, output_directory: str = './') -> s
|
|
|
8
8
|
if output_directory.startswith('.') or not output_directory.startswith('/') or not output_directory.startswith('\\'):
|
|
9
9
|
output_directory = os.path.realpath(os.getcwd() + os.path.sep + output_directory)
|
|
10
10
|
|
|
11
|
+
# if the directory does not exist, we will create it and its parents if needed
|
|
12
|
+
if not os.path.exists(output_directory):
|
|
13
|
+
os.makedirs(output_directory)
|
|
14
|
+
|
|
11
15
|
now = datetime.now()
|
|
12
16
|
return output_directory + os.path.sep + prefix + now.strftime("%Y%m%d-%H%M%S")
|
|
@@ -6,6 +6,7 @@ from typing import Dict, List
|
|
|
6
6
|
from prettytable import PrettyTable
|
|
7
7
|
|
|
8
8
|
import illumio_pylo as pylo
|
|
9
|
+
from illumio_pylo import ArraysToExcel, ExcelHeader, ExcelHeaderSet
|
|
9
10
|
from . import Command
|
|
10
11
|
from .utils.misc import make_filename_with_timestamp
|
|
11
12
|
|
|
@@ -39,17 +40,22 @@ def __main(args, org: pylo.Organization, **kwargs):
|
|
|
39
40
|
pylo.file_clean(output_filename_csv)
|
|
40
41
|
pylo.file_clean(output_filename_xls)
|
|
41
42
|
|
|
42
|
-
csv_report_headers = [
|
|
43
|
-
|
|
43
|
+
csv_report_headers = ExcelHeaderSet([
|
|
44
|
+
ExcelHeader(name='name', max_width=40),
|
|
45
|
+
ExcelHeader(name='hostname', max_width=40)
|
|
46
|
+
])
|
|
44
47
|
|
|
45
48
|
# insert all label dimensions
|
|
46
49
|
for label_type in org.LabelStore.label_types:
|
|
47
|
-
csv_report_headers.append(
|
|
48
|
-
|
|
49
|
-
csv_report_headers
|
|
50
|
-
'operating_system',
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
csv_report_headers.append(ExcelHeader(name= 'label_'+label_type, wrap_text= False))
|
|
51
|
+
|
|
52
|
+
csv_report_headers.extend([
|
|
53
|
+
'operating_system',
|
|
54
|
+
'report_failed',
|
|
55
|
+
'details',
|
|
56
|
+
ExcelHeader(name ='link_to_pce', max_width= 15, wrap_text=False, url_text='See in PCE',is_url= True),
|
|
57
|
+
ExcelHeader(name='href', max_width=15, wrap_text=False)
|
|
58
|
+
])
|
|
53
59
|
csv_report = pylo.ArraysToExcel()
|
|
54
60
|
sheet: pylo.ArraysToExcel.Sheet = csv_report.create_sheet('duplicates', csv_report_headers, force_all_wrap_text=True, multivalues_cell_delimiter=',')
|
|
55
61
|
# </editor-fold desc="Prepare the output files and CSV/Excel Object">
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from typing import Dict, List, Literal, Optional
|
|
1
2
|
import datetime
|
|
2
|
-
|
|
3
3
|
import click
|
|
4
|
+
import argparse
|
|
4
5
|
|
|
5
6
|
import illumio_pylo as pylo
|
|
6
|
-
import
|
|
7
|
-
|
|
7
|
+
from illumio_pylo import ArraysToExcel, ExcelHeader, ExcelHeaderSet
|
|
8
|
+
|
|
8
9
|
from .utils.misc import make_filename_with_timestamp
|
|
9
10
|
from . import Command
|
|
10
11
|
|
|
@@ -64,20 +65,22 @@ def __main(args, org: pylo.Organization, pce_cache_was_used: bool, **kwargs):
|
|
|
64
65
|
output_file_excel = output_file_prefix + '.xlsx'
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
csv_report_headers = [
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
csv_report_headers = pylo.ExcelHeaderSet([
|
|
69
|
+
ExcelHeader(name = 'name', max_width = 40),
|
|
70
|
+
ExcelHeader(name = 'hostname', max_width = 40)
|
|
71
|
+
])
|
|
70
72
|
# insert all label dimensions
|
|
71
73
|
for label_type in org.LabelStore.label_types:
|
|
72
|
-
csv_report_headers.append(
|
|
73
|
-
|
|
74
|
-
csv_report_headers
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
csv_report_headers.append(ExcelHeader(name= f'label_{label_type}', wrap_text= False))
|
|
75
|
+
|
|
76
|
+
csv_report_headers.extend([
|
|
77
|
+
'online',
|
|
78
|
+
ExcelHeader(name = 'last_heartbeat', max_width = 15, wrap_text = False),
|
|
79
|
+
ExcelHeader(name = 'created_at', max_width = 15, wrap_text = False),
|
|
80
|
+
'action',
|
|
81
|
+
ExcelHeader(name = 'link_to_pce', max_width = 15, wrap_text = False, url_text = 'See in PCE', is_url = True),
|
|
82
|
+
ExcelHeader(name = 'href', max_width = 15, wrap_text = False)
|
|
83
|
+
])
|
|
81
84
|
csv_report = pylo.ArraysToExcel()
|
|
82
85
|
sheet: pylo.ArraysToExcel.Sheet = csv_report.create_sheet('duplicates', csv_report_headers, force_all_wrap_text=True, multivalues_cell_delimiter=',')
|
|
83
86
|
|
|
@@ -11,14 +11,8 @@ objects_load_filter = ['workloads', 'labels']
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def fill_parser(parser: argparse.ArgumentParser):
|
|
14
|
-
parser.add_argument('--filter-
|
|
15
|
-
help='
|
|
16
|
-
parser.add_argument('--filter-loc-label', type=str, required=False, default=None,
|
|
17
|
-
help='Filter agents by location labels (separated by commas)')
|
|
18
|
-
parser.add_argument('--filter-app-label', type=str, required=False, default=None,
|
|
19
|
-
help='Filter agents by application labels (separated by commas)')
|
|
20
|
-
parser.add_argument('--filter-role-label', type=str, required=False, default=None,
|
|
21
|
-
help='Filter agents by role labels (separated by commas)')
|
|
14
|
+
parser.add_argument('--filter-label', '-fl', action='append',
|
|
15
|
+
help='Only look at workloads matching specified labels')
|
|
22
16
|
|
|
23
17
|
parser.add_argument('--filter-ven-versions', nargs='+', type=str, required=False, default=None,
|
|
24
18
|
help='Filter agents by versions (separated by spaces)')
|
|
@@ -67,58 +61,13 @@ def __main(args, org: pylo.Organization, **kwargs):
|
|
|
67
61
|
target_version = pylo.SoftwareVersion(target_version_string)
|
|
68
62
|
|
|
69
63
|
print(" * Parsing filters")
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
for raw_label_name in args['filter_env_label'].split(','):
|
|
75
|
-
print(" - label named '{}'".format(raw_label_name), end='', flush=True)
|
|
76
|
-
label = org.LabelStore.find_label_by_name_and_type(raw_label_name, pylo.label_type_env)
|
|
77
|
-
if label is None:
|
|
78
|
-
print("NOT FOUND!")
|
|
79
|
-
raise pylo.PyloEx("Cannot find label named '{}'".format(raw_label_name))
|
|
80
|
-
else:
|
|
81
|
-
print("found")
|
|
82
|
-
env_label_list[label] = label
|
|
83
|
-
|
|
84
|
-
loc_label_list = {}
|
|
85
|
-
if args['filter_loc_label'] is not None:
|
|
86
|
-
print(" * Location Labels specified")
|
|
87
|
-
for raw_label_name in args['filter_loc_label'].split(','):
|
|
88
|
-
print(" - label named '{}' ".format(raw_label_name), end='', flush=True)
|
|
89
|
-
label = org.LabelStore.find_label_by_name_and_type(raw_label_name, pylo.label_type_loc)
|
|
90
|
-
if label is None:
|
|
91
|
-
print("NOT FOUND!")
|
|
92
|
-
raise pylo.PyloEx("Cannot find label named '{}'".format(raw_label_name))
|
|
93
|
-
else:
|
|
94
|
-
print("found")
|
|
95
|
-
loc_label_list[label] = label
|
|
96
|
-
|
|
97
|
-
app_label_list = {}
|
|
98
|
-
if args['filter_app_label'] is not None:
|
|
99
|
-
print(" * Application Labels specified")
|
|
100
|
-
for raw_label_name in args['filter_app_label'].split(','):
|
|
101
|
-
print(" - label named '{}' ".format(raw_label_name), end='', flush=True)
|
|
102
|
-
label = org.LabelStore.find_label_by_name_and_type(raw_label_name, pylo.label_type_app)
|
|
103
|
-
if label is None:
|
|
104
|
-
print("NOT FOUND!")
|
|
105
|
-
raise pylo.PyloEx("Cannot find label named '{}'".format(raw_label_name))
|
|
106
|
-
else:
|
|
107
|
-
print("found")
|
|
108
|
-
app_label_list[label] = label
|
|
109
|
-
|
|
110
|
-
role_label_list = {}
|
|
111
|
-
if args['filter_role_label'] is not None:
|
|
112
|
-
print(" * Role Labels specified")
|
|
113
|
-
for raw_label_name in args['filter_role_label']:
|
|
114
|
-
print(" - label named '{}' ".format(raw_label_name), end='', flush=True)
|
|
115
|
-
label = org.LabelStore.find_label_by_name_and_type(raw_label_name, pylo.label_type_role)
|
|
64
|
+
filter_labels: List[pylo.Label] = [] # the list of labels to filter the workloads against
|
|
65
|
+
if args['filter_label'] is not None:
|
|
66
|
+
for label_name in args['filter_label']:
|
|
67
|
+
label = org.LabelStore.find_label_by_name(label_name)
|
|
116
68
|
if label is None:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
else:
|
|
120
|
-
print("found")
|
|
121
|
-
role_label_list[label] = label
|
|
69
|
+
raise pylo.PyloEx("Cannot find label '{}' in the PCE".format(label_name))
|
|
70
|
+
filter_labels.append(label)
|
|
122
71
|
|
|
123
72
|
filter_versions = {}
|
|
124
73
|
if args['filter_ven_versions'] is not None:
|
|
@@ -149,25 +98,8 @@ def __main(args, org: pylo.Organization, **kwargs):
|
|
|
149
98
|
del agents[agent_href]
|
|
150
99
|
continue
|
|
151
100
|
|
|
152
|
-
if
|
|
153
|
-
pylo.log.debug(" - workload '{}'
|
|
154
|
-
del agents[agent_href]
|
|
155
|
-
continue
|
|
156
|
-
if len(loc_label_list) > 0 and (workload.loc_label is None or workload.loc_label not in loc_label_list):
|
|
157
|
-
pylo.log.debug(" - workload '{}' does not match loc_label filters, it's out!".format(workload.get_name()))
|
|
158
|
-
del agents[agent_href]
|
|
159
|
-
continue
|
|
160
|
-
if len(app_label_list) > 0 and (workload.app_label is None or workload.app_label not in app_label_list):
|
|
161
|
-
pylo.log.debug(" - workload '{}' does not match app_label filters, it's out!".format(workload.get_name()))
|
|
162
|
-
del agents[agent_href]
|
|
163
|
-
continue
|
|
164
|
-
if len(role_label_list) > 0 and (workload.role_label is None or workload.role_label not in role_label_list):
|
|
165
|
-
pylo.log.debug(" - workload '{}' does not match role_label filters, it's out!".format(workload.get_name()))
|
|
166
|
-
del agents[agent_href]
|
|
167
|
-
continue
|
|
168
|
-
|
|
169
|
-
if agent.software_version.version_string not in filter_versions:
|
|
170
|
-
pylo.log.debug(" - workload '{}' does not match the version filter, it's out!".format(workload.get_name()))
|
|
101
|
+
if workload.uses_all_labels(filter_labels) is False:
|
|
102
|
+
pylo.log.debug(" - workload '{}' is not matching the labels filter".format(workload.get_name()))
|
|
171
103
|
del agents[agent_href]
|
|
172
104
|
continue
|
|
173
105
|
|