illumio-pylo 0.3.1__py3-none-any.whl → 0.3.3__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.
@@ -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, encrypt_api_key_with_paramiko_key, decrypt_api_key_with_paramiko_key
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
- create_parser.add_argument('--name', required=True, type=str,
26
- help='Name of the credential')
27
- create_parser.add_argument('--fqdn', required=True, type=str,
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=True, type=int,
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=True, type=int,
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=True, type=str,
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=True, type=bool,
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
- print("Recap:")
58
- print("Name: {}".format(args['name']))
59
- print("FQDN: {}".format(args['fqdn']))
60
- print("Port: {}".format(args['port']))
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": args['name'],
78
- "fqdn": args['fqdn'],
79
- "port": args['port'],
80
- "org_id": args['org'],
81
- "api_user": args['api_user'],
82
- "verify_ssl": args['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 = encrypt_api_key_with_paramiko_key(ssh_key=selected_ssh_key, api_key=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 = decrypt_api_key_with_paramiko_key(encrypted_api_key_payload=encrypted_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,6 +1,6 @@
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
6
  from illumio_pylo import ArraysToExcel, ExcelHeader
@@ -12,7 +12,7 @@ command_name = 'rule-export'
12
12
 
13
13
  def fill_parser(parser: argparse.ArgumentParser):
14
14
  parser.add_argument('--format', '-f', required=False, default='excel',choices=['csv', 'excel'], help='Output file format')
15
- parser.add_argument('--output', '-o', required=False, default='.', help='Directory where to save the output file')
15
+ parser.add_argument('--output-dir', '-o', required=False, default='output', help='Directory where to save the output file')
16
16
  parser.add_argument('--prefix-objects-with-type', nargs='?', const=True, default=False,
17
17
  help='Prefix objects with their type (e.g. "label:mylabel")')
18
18
  parser.add_argument('--object-types-as-section', action='store_true', default=False,
@@ -26,44 +26,26 @@ def fill_parser(parser: argparse.ArgumentParser):
26
26
 
27
27
 
28
28
 
29
- def __main(options: Dict, org: pylo.Organization, **kwargs):
30
- csv_report_headers = pylo.ExcelHeaderSet(
31
- [ ExcelHeader(name = 'ruleset', max_width = 40),
32
- ExcelHeader(name = 'scope', max_width = 50),
33
- ExcelHeader(name = 'type', max_width = 10),
34
- ExcelHeader(name = 'consumers', max_width = 80),
35
- ExcelHeader(name = 'providers', max_width = 80),
36
- ExcelHeader(name = 'services', max_width = 30),
37
- ExcelHeader(name = 'options', max_width = 40),
38
- ExcelHeader(name = 'ruleset_url', max_width = 40, wrap_text = False),
39
- ExcelHeader(name = 'ruleset_href', max_width = 30, wrap_text = False)
40
- ])
41
-
42
- setting_prefix_objects_with_type: bool|str = options['prefix_objects_with_type']
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
+
36
+
43
37
  if setting_prefix_objects_with_type is False:
44
38
  print(" * Prefix for object types are disabled")
45
39
  else:
46
40
  print(" * Prefix for object types are enabled")
47
41
 
48
- setting_object_types_as_section: bool = options['prefix_objects_with_type']
42
+
49
43
  if setting_object_types_as_section is False:
50
44
  print(" * Object types as section are disabled")
51
45
  else:
52
46
  print(" * Object types as section are enabled")
53
47
 
54
- output_file_format = options.get('format')
55
- if output_file_format == "excel":
56
- output_file_extension = ".xlsx"
57
- elif output_file_format == "csv":
58
- output_file_extension = ".csv"
59
- else:
60
- raise Exception("Unknown output file format: %s" % output_file_format)
61
-
62
- output_file_name = options.get('output') + os.sep + make_filename_with_timestamp('rule_export_') + output_file_extension
63
- output_file_name = os.path.abspath(output_file_name)
64
-
65
- csv_report = pylo.ArraysToExcel()
66
- 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)
67
49
 
68
50
  for ruleset in org.RulesetStore.rulesets:
69
51
  for rule in ruleset.rules_ordered_by_type:
@@ -103,23 +85,47 @@ def __main(options: Dict, org: pylo.Organization, **kwargs):
103
85
  'services': rule.services.members_to_str("\n"),
104
86
  'options': pylo.string_list_to_text(rule_options, "\n"),
105
87
  'ruleset_href': ruleset.href,
106
- 'ruleset_url': ruleset.get_ruleset_url()}
107
- if rule.is_extra_scope():
108
- data['type'] = 'extra'
109
- else:
110
- data['type'] = 'intra'
88
+ 'ruleset_url': ruleset.get_ruleset_url(),
89
+ 'type': 'intra' if rule.is_intra_scope() else 'extra'
90
+ }
91
+
111
92
  sheet.add_line_from_object(data)
112
93
 
113
- if output_file_format == "csv":
94
+ if settings_output_file_format == "csv":
114
95
  print(" * Writing export file '{}' ... ".format(output_file_name), end='', flush=True)
115
96
  sheet.write_to_csv(output_file_name)
116
97
  print("DONE")
117
- elif output_file_format == "excel":
98
+ elif settings_output_file_format == "excel":
118
99
  print(" * Writing export file '{}' ... ".format(output_file_name), end='', flush=True)
119
100
  csv_report.write_to_excel(output_file_name)
120
101
  print("DONE")
121
102
  else:
122
- raise pylo.PyloEx("Unknown format: '{}'".format(options['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
123
129
 
124
130
 
125
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")
@@ -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-env-label', type=str, required=False, default=None,
15
- help='Filter agents by environment labels (separated by commas)')
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
- env_label_list = {}
72
- if args['filter_env_label'] is not None:
73
- print(" * Environment Labels specified")
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
- print("NOT FOUND!")
118
- raise pylo.PyloEx("Cannot find label named '{}'".format(raw_label_name))
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 len(env_label_list) > 0 and (workload.env_label is None or workload.env_label not in env_label_list):
153
- pylo.log.debug(" - workload '{}' does not match env_label filters, it's out!".format(workload.get_name()))
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
 
@@ -80,9 +80,11 @@ def __main(args, org: pylo.Organization, **kwargs):
80
80
  for label_type in org.LabelStore.label_types:
81
81
  csv_report_headers.append(f'label_{label_type}')
82
82
 
83
- csv_report_headers.extend(['online', 'managed', 'status', 'agent.last_heartbeat',
84
- 'agent.sec_policy_sync_state', 'agent.sec_policy_applied_at',
85
- 'href', 'agent.href'])
83
+ csv_report_headers.extend([
84
+ 'online', 'managed', 'status', 'agent.last_heartbeat',
85
+ 'agent.sec_policy_sync_state', 'agent.sec_policy_applied_at',
86
+ ExcelHeader(name='link_to_pce', wrap_text=False, url_text='See in PCE', is_url=True),
87
+ 'href', 'agent.href'])
86
88
 
87
89
  for extra_column in extra_columns:
88
90
  csv_report_headers.append(extra_column.column_description().name)
@@ -110,7 +112,7 @@ def __main(args, org: pylo.Organization, **kwargs):
110
112
  for field in filter_data._detected_headers:
111
113
  csv_report_headers.append('_' + field)
112
114
 
113
- csv_report = pylo.ArraysToExcel()
115
+ csv_report = ArraysToExcel()
114
116
  csv_sheet = csv_report.create_sheet('workloads', csv_report_headers, force_all_wrap_text=True)
115
117
 
116
118
  all_workloads = org.WorkloadStore.itemsByHRef.copy()
@@ -132,6 +134,7 @@ def __main(args, org: pylo.Organization, **kwargs):
132
134
  'online': wkl.online,
133
135
  'managed': not wkl.unmanaged,
134
136
  'status': wkl.get_status_string(),
137
+ 'link_to_pce': wkl.href,
135
138
  }
136
139
  for label_type in org.LabelStore.label_types:
137
140
  new_row[f'label_{label_type}'] = wkl.get_label_name(label_type)
@@ -3,6 +3,8 @@ from dataclasses import dataclass
3
3
  import sys
4
4
  import argparse
5
5
 
6
+ import click
7
+
6
8
  import illumio_pylo as pylo
7
9
  from illumio_pylo import ArraysToExcel, ExcelHeaderSet, ExcelHeader
8
10
  from .utils.LabelCreation import generate_list_of_labels_to_create, create_labels
@@ -43,6 +45,12 @@ def fill_parser(parser: argparse.ArgumentParser):
43
45
  parser.add_argument('--batch-size', type=int, required=False, default=500,
44
46
  help='Number of Workloads to create per API call')
45
47
 
48
+ parser.add_argument('--proceed-with-creation', '-p', action='store_true',
49
+ help='If set, the script will proceed with the creation of the workloads')
50
+
51
+ parser.add_argument('--no-confirmation-required', '-n', action='store_true',
52
+ help='If set, the script will proceed with the creation of the workloads and labels without asking for confirmation')
53
+
46
54
 
47
55
 
48
56
  def __main(args, org: pylo.Organization, **kwargs):
@@ -54,6 +62,8 @@ def __main(args, org: pylo.Organization, **kwargs):
54
62
  settings_header_label_prefix: str = args['label_type_header_prefix']
55
63
  settings_ignore_all_sorts_collisions: bool = args['ignore_all_sorts_collisions']
56
64
  settings_ignore_empty_ip_entries: bool = args['ignore_empty_ip_entries']
65
+ settings_proceed_with_creation: bool = args['proceed_with_creation']
66
+ settings_no_confirmation_required: bool = args['no_confirmation_required']
57
67
  settings_output_dir: str = args['output_dir']
58
68
 
59
69
  batch_size = args['batch_size']
@@ -113,6 +123,11 @@ def __main(args, org: pylo.Organization, **kwargs):
113
123
  # <editor-fold desc="Missing Labels creation">
114
124
  if len(labels_to_be_created) > 0:
115
125
  print(" * {} Labels need to created before Workloads can be imported, listing:".format(len(labels_to_be_created)))
126
+ for label in labels_to_be_created:
127
+ print(" - Label: {} (type={})".format(label.name, label.type))
128
+ if not settings_no_confirmation_required:
129
+ click.confirm("Do you want to proceed with the creation of these labels?", abort=True)
130
+
116
131
  create_labels(labels_to_be_created, org)
117
132
  # </editor-fold>
118
133
 
@@ -134,21 +149,37 @@ def __main(args, org: pylo.Organization, **kwargs):
134
149
  print(" * No Workloads to create, all were ignored due to collisions or missing data.")
135
150
  # still want to save the CSV/Excel files in the end so don't exit
136
151
  else:
137
- print(" * Creating {} Unmanaged Workloads in batches of {}".format(umw_creator_manager.count_drafts(), batch_size))
138
- total_created_count = 0
139
- total_failed_count = 0
140
-
141
- results = umw_creator_manager.create_all_in_pce(amount_created_per_batch=batch_size, retrieve_workloads_after_creation=False)
142
- for result in results:
143
- if result.success:
144
- total_created_count += 1
145
- result.external_tracker_id['href'] = result.workload_href
152
+ if not settings_proceed_with_creation is True:
153
+ print(" * No workload will be created because the --proceed-with-creation/-p flag was not set. Yet report will be generated")
154
+ for object_to_create in csv_objects_to_create:
155
+ if '**not_created_reason**' not in object_to_create:
156
+ object_to_create['**not_created_reason**'] = '--proceed-with-creation/-p flag was not set'
157
+ else:
158
+ confirmed = settings_no_confirmation_required
159
+ print(" * Creating {} Unmanaged Workloads in batches of {}".format(umw_creator_manager.count_drafts(), batch_size))
160
+ if not settings_no_confirmation_required:
161
+ confirmed = click.confirm("Do you want to proceed with the creation of these workloads?")
162
+
163
+ if not confirmed:
164
+ print(" * No Workloads will be created, user aborted the operation")
165
+ for object_to_create in csv_objects_to_create:
166
+ if '**not_created_reason**' not in object_to_create:
167
+ object_to_create['**not_created_reason**'] = 'user aborted the operation'
146
168
  else:
147
- total_failed_count += 1
148
- result.external_tracker_id['**not_created_reason**'] = result.message
169
+ total_created_count = 0
170
+ total_failed_count = 0
171
+
172
+ results = umw_creator_manager.create_all_in_pce(amount_created_per_batch=batch_size, retrieve_workloads_after_creation=False)
173
+ for result in results:
174
+ if result.success:
175
+ total_created_count += 1
176
+ result.external_tracker_id['href'] = result.workload_href
177
+ else:
178
+ total_failed_count += 1
179
+ result.external_tracker_id['**not_created_reason**'] = result.message
149
180
 
150
- print(" * DONE - {} created with success, {} failures and {} ignored.".format(
151
- total_created_count, total_failed_count, ignored_objects_count))
181
+ print(" * DONE - {} created with success, {} failures and {} ignored.".format(
182
+ total_created_count, total_failed_count, ignored_objects_count))
152
183
  # </editor-fold>
153
184
 
154
185
  print()
illumio_pylo/tmp.py CHANGED
@@ -3,12 +3,11 @@ from datetime import datetime
3
3
 
4
4
  import illumio_pylo as pylo
5
5
 
6
- log = logging.getLogger('Pylo')
7
-
6
+ log = logging.getLogger('PYLO')
8
7
 
9
8
  def init_logger():
10
9
  console_logger = logging.StreamHandler()
11
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10
+ formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s/%(filename)s:%(lineno)d - %(message)s')
12
11
  console_logger.setFormatter(formatter)
13
12
  log.addHandler(console_logger)
14
13
 
@@ -32,7 +31,12 @@ def get_logger():
32
31
  return log
33
32
 
34
33
 
35
- def find_connector_or_die(obj):
34
+ def find_connector_or_die(obj) -> 'pylo.APIConnector':
35
+ """
36
+ Find the APIConnector object in the object or its owner recursively. Will raise an exception if not found
37
+ :param obj:
38
+ :return:
39
+ """
36
40
  connector = obj.__dict__.get('connector') # type: pylo.APIConnector
37
41
  if connector is None:
38
42
  owner = obj.__dict__.get('owner')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.1
3
+ Version: 0.3.3
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