illumio-pylo 0.3.1__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 +3 -4
- 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 -38
- illumio_pylo/cli/commands/utils/misc.py +4 -0
- illumio_pylo/cli/commands/ven_upgrader.py +10 -78
- illumio_pylo/cli/commands/workload_export.py +7 -4
- illumio_pylo/cli/commands/workload_import.py +44 -13
- illumio_pylo/tmp.py +8 -4
- {illumio_pylo-0.3.1.dist-info → illumio_pylo-0.3.2.dist-info}/METADATA +1 -1
- {illumio_pylo-0.3.1.dist-info → illumio_pylo-0.3.2.dist-info}/RECORD +23 -23
- {illumio_pylo-0.3.1.dist-info → illumio_pylo-0.3.2.dist-info}/LICENSE +0 -0
- {illumio_pylo-0.3.1.dist-info → illumio_pylo-0.3.2.dist-info}/WHEEL +0 -0
- {illumio_pylo-0.3.1.dist-info → illumio_pylo-0.3.2.dist-info}/top_level.txt +0 -0
illumio_pylo/API/APIConnector.py
CHANGED
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
import time
|
|
3
3
|
import getpass
|
|
4
4
|
|
|
5
|
-
from .CredentialsManager import is_api_key_encrypted, decrypt_api_key
|
|
5
|
+
from .CredentialsManager import is_api_key_encrypted, decrypt_api_key, CredentialProfile
|
|
6
6
|
from .JsonPayloadTypes import LabelGroupObjectJsonStructure, LabelObjectCreationJsonStructure, \
|
|
7
7
|
LabelObjectJsonStructure, LabelObjectUpdateJsonStructure, PCEObjectsJsonStructure, \
|
|
8
8
|
LabelGroupObjectUpdateJsonStructure, IPListObjectCreationJsonStructure, IPListObjectJsonStructure, \
|
|
@@ -108,6 +108,12 @@ class APIConnector:
|
|
|
108
108
|
def get_all_object_types():
|
|
109
109
|
return all_object_types.copy()
|
|
110
110
|
|
|
111
|
+
@staticmethod
|
|
112
|
+
def create_from_credentials_object(credentials: CredentialProfile) -> Optional['APIConnector']:
|
|
113
|
+
return APIConnector(credentials.fqdn, credentials.port, credentials.api_user,
|
|
114
|
+
credentials.api_key, skip_ssl_cert_check=not credentials.verify_ssl,
|
|
115
|
+
org_id=credentials.org_id, name=credentials.name)
|
|
116
|
+
|
|
111
117
|
@staticmethod
|
|
112
118
|
def create_from_credentials_in_file(fqdn_or_profile_name: str, request_if_missing: bool = False,
|
|
113
119
|
credential_file: Optional[str] = None) -> Optional['APIConnector']:
|
|
@@ -115,9 +121,7 @@ class APIConnector:
|
|
|
115
121
|
credentials = pylo.get_credentials_from_file(fqdn_or_profile_name, credential_file)
|
|
116
122
|
|
|
117
123
|
if credentials is not None:
|
|
118
|
-
return APIConnector(credentials
|
|
119
|
-
credentials.api_key, skip_ssl_cert_check=not credentials.verify_ssl,
|
|
120
|
-
org_id=credentials.org_id, name=credentials.name)
|
|
124
|
+
return APIConnector.create_from_credentials_object(credentials)
|
|
121
125
|
|
|
122
126
|
if not request_if_missing:
|
|
123
127
|
return None
|
|
@@ -362,7 +366,7 @@ class APIConnector:
|
|
|
362
366
|
else:
|
|
363
367
|
raise pylo.PyloEx("Unsupported object type '{}'".format(object_type))
|
|
364
368
|
|
|
365
|
-
def get_pce_objects(self, include_deleted_workloads=False, list_of_objects_to_load: Optional[List[str]] = None):
|
|
369
|
+
def get_pce_objects(self, include_deleted_workloads=False, list_of_objects_to_load: Optional[List[str]] = None, force_async_mode=False):
|
|
366
370
|
|
|
367
371
|
objects_to_load = {}
|
|
368
372
|
if list_of_objects_to_load is not None:
|
|
@@ -389,7 +393,7 @@ class APIConnector:
|
|
|
389
393
|
errors = []
|
|
390
394
|
thread_queue = Queue()
|
|
391
395
|
|
|
392
|
-
def get_objects(q: Queue, thread_num: int):
|
|
396
|
+
def get_objects(q: Queue, thread_num: int, force_async_mode=False):
|
|
393
397
|
while True:
|
|
394
398
|
object_type, errors = q.get()
|
|
395
399
|
try:
|
|
@@ -397,7 +401,7 @@ class APIConnector:
|
|
|
397
401
|
q.task_done()
|
|
398
402
|
continue
|
|
399
403
|
if object_type == 'workloads':
|
|
400
|
-
if self.get_objects_count_by_type(object_type) > default_max_objects_for_sync_calls:
|
|
404
|
+
if self.get_objects_count_by_type(object_type) > default_max_objects_for_sync_calls or force_async_mode:
|
|
401
405
|
data['workloads'] = self.objects_workload_get(include_deleted=include_deleted_workloads)
|
|
402
406
|
else:
|
|
403
407
|
data['workloads'] = self.objects_workload_get(include_deleted=include_deleted_workloads, async_mode=False, max_results=default_max_objects_for_sync_calls)
|
|
@@ -456,7 +460,7 @@ class APIConnector:
|
|
|
456
460
|
q.task_done()
|
|
457
461
|
|
|
458
462
|
for i in range(threads_count):
|
|
459
|
-
worker = Thread(target=get_objects, args=(thread_queue, i))
|
|
463
|
+
worker = Thread(target=get_objects, args=(thread_queue, i, force_async_mode,))
|
|
460
464
|
worker.daemon = True
|
|
461
465
|
worker.start()
|
|
462
466
|
|
|
@@ -97,7 +97,7 @@ def get_all_credentials_from_file(credential_file: str ) -> List[CredentialProfi
|
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
def get_credentials_from_file(fqdn_or_profile_name: str = None,
|
|
100
|
-
credential_file: str = None) -> CredentialProfile:
|
|
100
|
+
credential_file: str = None, fail_with_an_exception=True) -> Optional[CredentialProfile]:
|
|
101
101
|
|
|
102
102
|
if fqdn_or_profile_name is None:
|
|
103
103
|
log.debug("No fqdn_or_profile_name provided, profile_name=default will be used")
|
|
@@ -121,9 +121,12 @@ def get_credentials_from_file(fqdn_or_profile_name: str = None,
|
|
|
121
121
|
if credential_profile.fqdn.lower() == fqdn_or_profile_name.lower():
|
|
122
122
|
return credential_profile
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
if fail_with_an_exception:
|
|
125
|
+
raise PyloEx("No profile found in credential file '{}' with fqdn: {}".
|
|
125
126
|
format(credential_file, fqdn_or_profile_name))
|
|
126
127
|
|
|
128
|
+
return None
|
|
129
|
+
|
|
127
130
|
|
|
128
131
|
def list_potential_credential_files() -> List[str]:
|
|
129
132
|
"""
|
|
@@ -204,9 +207,7 @@ def create_credential_in_default_file(data: CredentialFileEntry) -> str:
|
|
|
204
207
|
return file_path
|
|
205
208
|
|
|
206
209
|
|
|
207
|
-
def
|
|
208
|
-
|
|
209
|
-
|
|
210
|
+
def encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key: paramiko.AgentKey, api_key: str) -> str:
|
|
210
211
|
def encrypt(raw: str, key: bytes) -> bytes:
|
|
211
212
|
"""
|
|
212
213
|
|
|
@@ -220,7 +221,7 @@ def encrypt_api_key_with_paramiko_key(ssh_key: paramiko.AgentKey, api_key: str)
|
|
|
220
221
|
|
|
221
222
|
|
|
222
223
|
# generate a random 128bit key
|
|
223
|
-
session_key_to_sign = os.urandom(
|
|
224
|
+
session_key_to_sign = os.urandom(32)
|
|
224
225
|
|
|
225
226
|
signed_message = ssh_key.sign_ssh_data(session_key_to_sign)
|
|
226
227
|
|
|
@@ -236,7 +237,7 @@ def encrypt_api_key_with_paramiko_key(ssh_key: paramiko.AgentKey, api_key: str)
|
|
|
236
237
|
return api_key
|
|
237
238
|
|
|
238
239
|
|
|
239
|
-
def
|
|
240
|
+
def decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload: str) -> str:
|
|
240
241
|
def decrypt(token_b64_encoded: str, key: bytes):
|
|
241
242
|
f = Fernet(base64.urlsafe_b64encode(key))
|
|
242
243
|
return f.decrypt(token_b64_encoded).decode('utf-8')
|
|
@@ -277,7 +278,7 @@ def decrypt_api_key(encrypted_api_key_payload: str) -> str:
|
|
|
277
278
|
if not encrypted_api_key_payload.startswith("$encrypted$:"):
|
|
278
279
|
raise PyloEx("Invalid encrypted API key format")
|
|
279
280
|
if encrypted_api_key_payload.startswith("$encrypted$:ssh-Fernet:"):
|
|
280
|
-
return
|
|
281
|
+
return decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload)
|
|
281
282
|
|
|
282
283
|
raise PyloEx("Unsupported encryption method: {}".format(encrypted_api_key_payload.split(":")[1]))
|
|
283
284
|
|
illumio_pylo/Helpers/exports.py
CHANGED
|
@@ -255,10 +255,9 @@ class ArraysToExcel:
|
|
|
255
255
|
new_line.append('=HYPERLINK("{}", "{}")'.format(item,self._headers[item_index].url_text))
|
|
256
256
|
else:
|
|
257
257
|
new_line.append(item)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
columns_max_width[item_index] = length
|
|
258
|
+
length = find_length(new_line[item_index])
|
|
259
|
+
if length > columns_max_width[item_index]:
|
|
260
|
+
columns_max_width[item_index] = length
|
|
262
261
|
|
|
263
262
|
|
|
264
263
|
xls_data.append(new_line)
|
illumio_pylo/IPMap.py
CHANGED
|
@@ -71,6 +71,15 @@ class IP4Map:
|
|
|
71
71
|
self._entries.append(new_entry)
|
|
72
72
|
self.sort_and_recalculate()
|
|
73
73
|
|
|
74
|
+
def add_another_map(self, another_map: 'IP4Map', skip_recalculation=False):
|
|
75
|
+
for entry in another_map._entries:
|
|
76
|
+
self._entries.append(entry)
|
|
77
|
+
|
|
78
|
+
if skip_recalculation:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
self.sort_and_recalculate()
|
|
82
|
+
|
|
74
83
|
def intersection(self, another_map: 'IP4Map'):
|
|
75
84
|
|
|
76
85
|
inverted_map = IP4Map()
|
illumio_pylo/LabeledObject.py
CHANGED
illumio_pylo/Organization.py
CHANGED
|
@@ -135,7 +135,10 @@ class Organization:
|
|
|
135
135
|
object_to_load = pylo.APIConnector.get_all_object_types()
|
|
136
136
|
|
|
137
137
|
if self.pce_version is None:
|
|
138
|
-
|
|
138
|
+
if 'pce_version' in data:
|
|
139
|
+
self.pce_version = pylo.SoftwareVersion(data['pce_version'])
|
|
140
|
+
else:
|
|
141
|
+
raise pylo.PyloEx('Organization has no "version" specified')
|
|
139
142
|
|
|
140
143
|
self.LabelStore.load_label_dimensions(data.get('label_dimensions'))
|
|
141
144
|
|
illumio_pylo/Rule.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Optional, List, Union, Dict, Any, NewType
|
|
|
4
4
|
import illumio_pylo as pylo
|
|
5
5
|
from .API.JsonPayloadTypes import RuleServiceReferenceObjectJsonStructure, RuleDirectServiceReferenceObjectJsonStructure
|
|
6
6
|
from illumio_pylo import Workload, Label, LabelGroup, Ruleset, Referencer, SecurityPrincipal, PyloEx, \
|
|
7
|
-
Service, nice_json, string_list_to_text, find_connector_or_die, VirtualService, IPList
|
|
7
|
+
Service, nice_json, string_list_to_text, find_connector_or_die, VirtualService, IPList, PortMap
|
|
8
8
|
|
|
9
9
|
RuleActorsAcceptableTypes = NewType('RuleActorsAcceptableTypes', Union[Workload, Label, LabelGroup, IPList, VirtualService])
|
|
10
10
|
|
|
@@ -250,6 +250,7 @@ class RuleServiceContainer(pylo.Referencer):
|
|
|
250
250
|
self.owner = owner
|
|
251
251
|
self._items: Dict[Service, Service] = {}
|
|
252
252
|
self._direct_services: List[DirectServiceInRule] = []
|
|
253
|
+
self._cached_port_map: Optional[PortMap] = None
|
|
253
254
|
|
|
254
255
|
def load_from_json(self, data_list: List[RuleServiceReferenceObjectJsonStructure|RuleDirectServiceReferenceObjectJsonStructure]):
|
|
255
256
|
ss_store = self.owner.owner.owner.owner.ServiceStore # make it a local variable for fast lookups
|
|
@@ -295,6 +296,8 @@ class RuleServiceContainer(pylo.Referencer):
|
|
|
295
296
|
:param service:
|
|
296
297
|
:return: True if the service was removed, False if it was not found
|
|
297
298
|
"""
|
|
299
|
+
self._cached_port_map = None
|
|
300
|
+
|
|
298
301
|
for i in range(0, len(self._direct_services)):
|
|
299
302
|
if self._direct_services[i] is service:
|
|
300
303
|
del(self._direct_services[i])
|
|
@@ -302,6 +305,8 @@ class RuleServiceContainer(pylo.Referencer):
|
|
|
302
305
|
return False
|
|
303
306
|
|
|
304
307
|
def add_direct_service(self, service: DirectServiceInRule) -> bool:
|
|
308
|
+
self._cached_port_map = None
|
|
309
|
+
|
|
305
310
|
for member in self._direct_services:
|
|
306
311
|
if service is member:
|
|
307
312
|
return False
|
|
@@ -352,6 +357,27 @@ class RuleServiceContainer(pylo.Referencer):
|
|
|
352
357
|
|
|
353
358
|
self.owner.raw_json.update(data)
|
|
354
359
|
|
|
360
|
+
def get_port_map(self) -> PortMap:
|
|
361
|
+
"""
|
|
362
|
+
Get a PortMap object with all ports and protocols from all services in this container
|
|
363
|
+
:return:
|
|
364
|
+
"""
|
|
365
|
+
if self._cached_port_map is not None:
|
|
366
|
+
return self._cached_port_map
|
|
367
|
+
|
|
368
|
+
result = PortMap()
|
|
369
|
+
for service in self._items.values():
|
|
370
|
+
for entry in service.entries:
|
|
371
|
+
result.add(entry.protocol, entry.port, entry.to_port, skip_recalculation=True)
|
|
372
|
+
for direct in self._direct_services:
|
|
373
|
+
result.add(direct.protocol, direct.port, direct.to_port, skip_recalculation=True)
|
|
374
|
+
|
|
375
|
+
result.merge_overlapping_maps()
|
|
376
|
+
|
|
377
|
+
self._cached_port_map = result
|
|
378
|
+
|
|
379
|
+
return result
|
|
380
|
+
|
|
355
381
|
|
|
356
382
|
class RuleHostContainer(pylo.Referencer):
|
|
357
383
|
def __init__(self, owner: 'pylo.Rule', name: str):
|
illumio_pylo/Ruleset.py
CHANGED
|
@@ -101,29 +101,17 @@ class RulesetScopeEntry:
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
def to_string(self, label_separator = '|', use_href=False):
|
|
104
|
-
string = '
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
else:
|
|
114
|
-
if use_href:
|
|
115
|
-
string += self.env_label.href + label_separator
|
|
116
|
-
else:
|
|
117
|
-
string += self.env_label.name + label_separator
|
|
118
|
-
|
|
119
|
-
if self.loc_label is None:
|
|
120
|
-
string += 'All'
|
|
121
|
-
else:
|
|
122
|
-
if use_href:
|
|
123
|
-
string += self.loc_label.href
|
|
104
|
+
string = ''
|
|
105
|
+
for label_type in self.owner.owner.owner.owner.LabelStore.label_types:
|
|
106
|
+
label = self._labels.get(label_type)
|
|
107
|
+
if len(string) > 0:
|
|
108
|
+
string += label_separator
|
|
109
|
+
if label is None:
|
|
110
|
+
string += 'All'
|
|
111
|
+
elif use_href:
|
|
112
|
+
string += label.href
|
|
124
113
|
else:
|
|
125
|
-
string +=
|
|
126
|
-
|
|
114
|
+
string += label.name
|
|
127
115
|
return string
|
|
128
116
|
|
|
129
117
|
def is_all_all_all(self):
|
|
@@ -156,10 +144,6 @@ class RulesetScopeEntry:
|
|
|
156
144
|
|
|
157
145
|
class Ruleset:
|
|
158
146
|
|
|
159
|
-
name: str
|
|
160
|
-
href: Optional[str]
|
|
161
|
-
description: str
|
|
162
|
-
|
|
163
147
|
def __init__(self, owner: 'pylo.RulesetStore'):
|
|
164
148
|
self.owner: 'pylo.RulesetStore' = owner
|
|
165
149
|
self.href: Optional[str] = None
|
|
@@ -169,6 +153,7 @@ class Ruleset:
|
|
|
169
153
|
# must keep an ordered list of rules while the dict by href is there for quick searches
|
|
170
154
|
self._rules_by_href: Dict[str, 'pylo.Rule'] = {}
|
|
171
155
|
self._rules: List['pylo.Rule'] = []
|
|
156
|
+
self.disabled: bool = False
|
|
172
157
|
|
|
173
158
|
@property
|
|
174
159
|
def rules(self):
|
|
@@ -211,6 +196,9 @@ class Ruleset:
|
|
|
211
196
|
raise pylo.PyloEx("Cannot find Ruleset href in JSON data: \n" + pylo.Helpers.nice_json(data))
|
|
212
197
|
self.href = data['href']
|
|
213
198
|
|
|
199
|
+
if 'enabled' in data:
|
|
200
|
+
self.disabled = not data['enabled']
|
|
201
|
+
|
|
214
202
|
scopes_json = data.get('scopes')
|
|
215
203
|
if scopes_json is None:
|
|
216
204
|
raise pylo.PyloEx("Cannot find Ruleset scope in JSON data: \n" + pylo.Helpers.nice_json(data))
|
|
@@ -287,7 +275,7 @@ class Ruleset:
|
|
|
287
275
|
if pce_fqdn is None or pce_port is None:
|
|
288
276
|
connector = pylo.find_connector_or_die(self)
|
|
289
277
|
if pce_fqdn is None:
|
|
290
|
-
pce_fqdn = connector.
|
|
278
|
+
pce_fqdn = connector.fqdn
|
|
291
279
|
if pce_port is None:
|
|
292
280
|
pce_port = connector.port
|
|
293
281
|
|
illumio_pylo/Service.py
CHANGED
|
@@ -7,10 +7,20 @@ from typing import *
|
|
|
7
7
|
|
|
8
8
|
class PortMap:
|
|
9
9
|
def __init__(self):
|
|
10
|
-
self._tcp_map = []
|
|
11
|
-
self._udp_map = []
|
|
10
|
+
self._tcp_map: List[List[2]] = [] # [start, end]
|
|
11
|
+
self._udp_map: List[List[2]] = [] # [start, end]
|
|
12
12
|
self._protocol_map = {}
|
|
13
13
|
|
|
14
|
+
def copy(self) -> 'PortMap':
|
|
15
|
+
new_map = PortMap()
|
|
16
|
+
new_map._tcp_map = self._tcp_map.copy()
|
|
17
|
+
new_map._udp_map = self._udp_map.copy()
|
|
18
|
+
new_map._protocol_map = self._protocol_map.copy()
|
|
19
|
+
return new_map
|
|
20
|
+
|
|
21
|
+
def count(self) -> int:
|
|
22
|
+
return len(self._tcp_map) + len(self._udp_map) + len(self._protocol_map)
|
|
23
|
+
|
|
14
24
|
def add(self, protocol, start_port: int, end_port: int = None, skip_recalculation=False):
|
|
15
25
|
|
|
16
26
|
proto = None
|
|
@@ -31,67 +41,54 @@ class PortMap:
|
|
|
31
41
|
return
|
|
32
42
|
|
|
33
43
|
if start_port is None:
|
|
44
|
+
start_port = end_port
|
|
45
|
+
|
|
46
|
+
if end_port is None:
|
|
34
47
|
end_port = start_port
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
if proto == 6:
|
|
50
|
+
self._tcp_map.append([start_port, end_port])
|
|
51
|
+
else:
|
|
52
|
+
self._udp_map.append([start_port, end_port])
|
|
37
53
|
|
|
38
54
|
if not skip_recalculation:
|
|
39
55
|
self.merge_overlapping_maps()
|
|
40
56
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
cur_entry = None
|
|
47
|
-
|
|
48
|
-
for original_entry in self._tcp_map:
|
|
49
|
-
if cur_entry is None:
|
|
50
|
-
cur_entry = original_entry
|
|
51
|
-
continue
|
|
52
|
-
|
|
53
|
-
cur_start = cur_entry[0]
|
|
54
|
-
cur_end = cur_entry[1]
|
|
55
|
-
new_start = original_entry[0]
|
|
56
|
-
new_end = original_entry[1]
|
|
57
|
+
def to_list_of_objects(self) -> List[Dict]:
|
|
58
|
+
result = []
|
|
59
|
+
for entry in self._tcp_map:
|
|
60
|
+
result.append({'proto': 6, 'port': entry[0], 'to_port': entry[1]})
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
continue
|
|
62
|
+
for entry in self._udp_map:
|
|
63
|
+
result.append({'proto': 17, 'port': entry[0], 'to_port': entry[1]})
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
for proto in self._protocol_map:
|
|
66
|
+
result.append({'proto': proto})
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
self._tcp_map = []
|
|
67
|
-
else:
|
|
68
|
-
new_map.append(cur_entry)
|
|
69
|
-
self._tcp_map = new_map
|
|
70
|
-
|
|
71
|
-
new_map = []
|
|
72
|
-
|
|
73
|
-
for original_entry in self._udp_map:
|
|
74
|
-
if cur_entry is None:
|
|
75
|
-
cur_entry = original_entry
|
|
76
|
-
continue
|
|
77
|
-
|
|
78
|
-
cur_start = cur_entry[0]
|
|
79
|
-
cur_end = cur_entry[1]
|
|
80
|
-
new_start = original_entry[0]
|
|
81
|
-
new_end = original_entry[1]
|
|
82
|
-
|
|
83
|
-
if new_start > cur_end + 1:
|
|
84
|
-
new_map.append(cur_entry)
|
|
85
|
-
continue
|
|
68
|
+
return result
|
|
86
69
|
|
|
87
|
-
|
|
88
|
-
|
|
70
|
+
def merge_overlapping_maps(self):
|
|
71
|
+
self._sort_maps()
|
|
89
72
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
def merge_maps(map_list):
|
|
74
|
+
new_list = []
|
|
75
|
+
current = None
|
|
76
|
+
for entry in map_list:
|
|
77
|
+
if current is None:
|
|
78
|
+
current = entry
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
if entry[0] <= current[1] + 1:
|
|
82
|
+
current[1] = entry[1]
|
|
83
|
+
else:
|
|
84
|
+
new_list.append(current)
|
|
85
|
+
current = entry
|
|
86
|
+
if current is not None:
|
|
87
|
+
new_list.append(current)
|
|
88
|
+
return new_list
|
|
89
|
+
|
|
90
|
+
self._tcp_map = merge_maps(self._tcp_map)
|
|
91
|
+
self._udp_map = merge_maps(self._udp_map)
|
|
95
92
|
|
|
96
93
|
def _sort_maps(self):
|
|
97
94
|
def first_entry(my_list):
|
illumio_pylo/__init__.py
CHANGED
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,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='
|
|
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(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
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
|
|
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(
|
|
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-
|
|
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
|
|
|
@@ -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([
|
|
84
|
-
|
|
85
|
-
|
|
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 =
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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('
|
|
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
|
|
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,60 +1,60 @@
|
|
|
1
1
|
illumio_pylo/AgentStore.py,sha256=O4dz9m0KM0TQPZvvEMoD_5pCkMvQ70YVDaWqbqqhR4k,5091
|
|
2
2
|
illumio_pylo/Exception.py,sha256=3lxS-ANBaEvHAKhDb8UzNLj5IpQBmRHNs4YkHONmQjs,1033
|
|
3
3
|
illumio_pylo/IPList.py,sha256=XljZM4AHjM59PoR0bPLoJDVjLFJGVjewF91GwXV29UY,4501
|
|
4
|
-
illumio_pylo/IPMap.py,sha256=
|
|
4
|
+
illumio_pylo/IPMap.py,sha256=2xCU0BD6Zc5yWgemn6Q0jNgs0owzp7TCyh6sVRLMf5U,10232
|
|
5
5
|
illumio_pylo/Label.py,sha256=sn6qNFo8Etdl0lUYRoSp_VC0mJymuFJpQ99x9vAwKSY,835
|
|
6
6
|
illumio_pylo/LabelCommon.py,sha256=2HzljD2xJSBjf4Ywxk8JpBQQfbiZILCMV_Mj_EATIGk,1500
|
|
7
7
|
illumio_pylo/LabelGroup.py,sha256=bVXvONHwR44GkF-9S19Fs2cnc4g8IW42VjyKi7QXmvQ,2624
|
|
8
8
|
illumio_pylo/LabelStore.py,sha256=_feA5HdrfnOgcWStBToesu1S2ymHQKEKEG2Ygcadh9g,19809
|
|
9
|
-
illumio_pylo/LabeledObject.py,sha256=
|
|
10
|
-
illumio_pylo/Organization.py,sha256=
|
|
9
|
+
illumio_pylo/LabeledObject.py,sha256=zXIFcHtuvgxhu93htxiWtHoJ_mMWfSSdOBRNlEiXej0,1991
|
|
10
|
+
illumio_pylo/Organization.py,sha256=MeESA6HuU0HzzkIKbj1NW_wzkarq30ptsCiffD1lm2M,12379
|
|
11
11
|
illumio_pylo/Query.py,sha256=lgLjst41ma3HMVkngvTjL7zSLjh80RoXYL5vS3PWO7Q,13330
|
|
12
12
|
illumio_pylo/ReferenceTracker.py,sha256=HyY0NwZwOdAzSXJrs6i6dhB_kgn1tidZL5LFLLkPuSM,1070
|
|
13
|
-
illumio_pylo/Rule.py,sha256=
|
|
14
|
-
illumio_pylo/Ruleset.py,sha256=
|
|
13
|
+
illumio_pylo/Rule.py,sha256=jPU0hj8BDv2t-keL3NCK6NEd-kYSRkjwtzdo1xWiC5E,26352
|
|
14
|
+
illumio_pylo/Ruleset.py,sha256=G-YrxHRovECDV0TVOLOJyRbMtlgSNmqiq4jMwR3175A,11711
|
|
15
15
|
illumio_pylo/RulesetStore.py,sha256=wJLAKi32y-YiI8NLWWa_Ykrn54AQDcLz3rRlvTGUToI,3379
|
|
16
16
|
illumio_pylo/SecurityPrincipal.py,sha256=8c7VYirJvByjW4r8Vz4NYHXlGYawOVE6YHDpizSIIO4,2280
|
|
17
|
-
illumio_pylo/Service.py,sha256=
|
|
17
|
+
illumio_pylo/Service.py,sha256=CKj5VHjkvy7YL18afHqgJl-Qole7s1NWoah3rWHm6Zw,8280
|
|
18
18
|
illumio_pylo/SoftwareVersion.py,sha256=Wg8Mi7vcoL9JgFSRo3i5aiX_OkUIU3ahmgLUrzJ9JYw,4348
|
|
19
19
|
illumio_pylo/VirtualService.py,sha256=KAKE4iWuNmOumGradR2vwZxZ_3iiZg4pBST5ixCZSng,561
|
|
20
20
|
illumio_pylo/VirtualServiceStore.py,sha256=b6bcMixq0Vfv7WjQTQnFHGCv_Mld7sOfvGi4NtFAMQ4,3057
|
|
21
21
|
illumio_pylo/Workload.py,sha256=OIrP2TVFpCjQdjMYPdHkZHcKRszZ2hPnj4uRhI6d8EI,19677
|
|
22
22
|
illumio_pylo/WorkloadStore.py,sha256=08FKIk9fubsaHy_-m2a0Io5sO_V9jmTa40-L3vyemRc,10947
|
|
23
23
|
illumio_pylo/WorkloadStoreSubClasses.py,sha256=tK7s-7s5wRf9SHv3T00EOzgv4gX8gPIM77KBubCzBuo,6092
|
|
24
|
-
illumio_pylo/__init__.py,sha256=
|
|
25
|
-
illumio_pylo/tmp.py,sha256=
|
|
26
|
-
illumio_pylo/API/APIConnector.py,sha256=
|
|
24
|
+
illumio_pylo/__init__.py,sha256=0cUO16-EHeOd6bZL4Aap5BngagWLcGsCZAnvhoVSxdg,4192
|
|
25
|
+
illumio_pylo/tmp.py,sha256=7PIn11aYfoxed7tjG_I1iZmiacn4TxZPihs7qD7f4DI,3123
|
|
26
|
+
illumio_pylo/API/APIConnector.py,sha256=PM-7sNPmBaZ8fLM5KGcLd7nXBha4Il6fFGAHntToSdk,59433
|
|
27
27
|
illumio_pylo/API/AuditLog.py,sha256=p0mfjrE5S8qoJgA5LIP_XGFBP3iL86Nl6BQEmhMVrPA,1533
|
|
28
28
|
illumio_pylo/API/ClusterHealth.py,sha256=GdMpVQrHUW4zLM-409GcPHM8H8b3LAppO37ZcUZyT_Q,5122
|
|
29
|
-
illumio_pylo/API/CredentialsManager.py,sha256=
|
|
29
|
+
illumio_pylo/API/CredentialsManager.py,sha256=vPqekauA0twbcpbWx8GcGFX4DQlh0y0SWAE9M4HKBsE,11031
|
|
30
30
|
illumio_pylo/API/Explorer.py,sha256=fFAIF-67_uuKgJOP0eZPPJrOGuYmFl33GK75AyMjgJU,47590
|
|
31
31
|
illumio_pylo/API/JsonPayloadTypes.py,sha256=eUsK1ba8PLoV-VReRE6A3qar-N3xckXRZbY2ckSkmMk,7113
|
|
32
32
|
illumio_pylo/API/RuleSearchQuery.py,sha256=O0-MsUXhwmywoO0G-GXnLq6kkVC9LgmxMZwqVKc_oJE,5325
|
|
33
33
|
illumio_pylo/API/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
illumio_pylo/Helpers/__init__.py,sha256=6E2eTZe-4qfPKGjRRQNtYsPrBhJSAjbdvv_DpniV_rY,49
|
|
35
|
-
illumio_pylo/Helpers/exports.py,sha256=
|
|
35
|
+
illumio_pylo/Helpers/exports.py,sha256=mW8zGDqSyAsAC7WjC-WX1DDhDS-DCJ37cF3raZ_C6xg,21890
|
|
36
36
|
illumio_pylo/Helpers/functions.py,sha256=TjI_NmD3cm1Escp3h51SFEDgS1vT9dUa8PX9-e0siao,4680
|
|
37
37
|
illumio_pylo/cli/NativeParsers.py,sha256=nzDL54EV0J-Iz0P9EkeiPl6DWQBSbCu-MpEPRad3j6c,4055
|
|
38
|
-
illumio_pylo/cli/__init__.py,sha256=
|
|
38
|
+
illumio_pylo/cli/__init__.py,sha256=UuZ2KamgIpvfYE1FXilm_THHAvXimdUcvGb3Jrbjfhc,7737
|
|
39
39
|
illumio_pylo/cli/__main__.py,sha256=ll1gK8k1YL_kPsImI7WVlw2sCyNyhocnuCqko6mGaYI,223
|
|
40
40
|
illumio_pylo/cli/commands/__init__.py,sha256=yoVkXy-qBGiAAziWiayJdjcclx1WTayShXSPqHelcWA,1489
|
|
41
|
-
illumio_pylo/cli/commands/credential_manager.py,sha256=
|
|
41
|
+
illumio_pylo/cli/commands/credential_manager.py,sha256=nChEuE7jewCzVNH2dXn3VspsfPmplZO-TjXLt07h7MA,9613
|
|
42
42
|
illumio_pylo/cli/commands/iplist_analyzer.py,sha256=vOl6fNd0HrUKnKXtY1sj7jeyx2GtuDZENV7mF79O7yU,3714
|
|
43
43
|
illumio_pylo/cli/commands/iplist_import_from_file.py,sha256=XvC-GlJyjA4dDPzfvGoBFtwelI9g_rQqGzgKgqMYcsI,8284
|
|
44
|
-
illumio_pylo/cli/commands/ruleset_export.py,sha256=
|
|
44
|
+
illumio_pylo/cli/commands/ruleset_export.py,sha256=E64oYgGKI1Ry382DqV1gT4IlsLdd55QTqdInXlL9EPU,5896
|
|
45
45
|
illumio_pylo/cli/commands/update_pce_objects_cache.py,sha256=kNBAmPIbJyw97FRcNREy059A5FLOGm_poKJYGdTWqr4,1256
|
|
46
46
|
illumio_pylo/cli/commands/ven_compatibility_report_export.py,sha256=8ZpgGQfn5zmOslgeE1imwx4-gDHfukdgoXJGZUh9q3o,8120
|
|
47
47
|
illumio_pylo/cli/commands/ven_duplicate_remover.py,sha256=sPq1E9LqJ0lku8c23zwfeJbCOblkG_3umve5-zV3DXU,19579
|
|
48
48
|
illumio_pylo/cli/commands/ven_idle_to_visibility.py,sha256=OMcQSB82Pn50TS7OL1_TPOvvN9XFR3jUxVYYizUQinI,13387
|
|
49
|
-
illumio_pylo/cli/commands/ven_upgrader.py,sha256=
|
|
50
|
-
illumio_pylo/cli/commands/workload_export.py,sha256=
|
|
51
|
-
illumio_pylo/cli/commands/workload_import.py,sha256=
|
|
49
|
+
illumio_pylo/cli/commands/ven_upgrader.py,sha256=4cLUA7HSpskJpPOtEkQh41lE4gMGJJiQiKGCd0CKYJk,7222
|
|
50
|
+
illumio_pylo/cli/commands/workload_export.py,sha256=EcQR8AacJVe7rOqYH-HFxyTchwHHQ9ZTc-ALSMt9gDY,11421
|
|
51
|
+
illumio_pylo/cli/commands/workload_import.py,sha256=EJPjue9jnPR4UyLrh67LlXovvpmfa9Lb4DAfy6exAFA,18939
|
|
52
52
|
illumio_pylo/cli/commands/workload_reset_names_to_null.py,sha256=GaO2Th1rx8i7_7yqb9WkGpDz3sdbYNod5fABQnhogig,3383
|
|
53
53
|
illumio_pylo/cli/commands/workload_update.py,sha256=hDiYEeOwGaSUkoG4NK7EvKbMnChoYJAGDmqRWDtLD9A,28484
|
|
54
54
|
illumio_pylo/cli/commands/workload_used_in_rule_finder.py,sha256=2MO9jjYh-mkY6rkfvDyW0Ok6El_AqDWGXiFDYq5YgTk,3361
|
|
55
55
|
illumio_pylo/cli/commands/utils/LabelCreation.py,sha256=qaU7BeP4et2GJxHwgbDNRoQLCA-xWp_y1nJ-m8ANUwA,4883
|
|
56
56
|
illumio_pylo/cli/commands/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
-
illumio_pylo/cli/commands/utils/misc.py,sha256=
|
|
57
|
+
illumio_pylo/cli/commands/utils/misc.py,sha256=iV5CEEccfQ08eO0wSpTOxS9-32_GXSekShic6auuGPE,761
|
|
58
58
|
illumio_pylo/docs/Doxygen,sha256=AVvSIRYLHFWJ15YLGahhzhsW0ZUUFO5lVxd2-F3iWz8,74257
|
|
59
59
|
illumio_pylo/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
60
|
illumio_pylo/utilities/cli.py,sha256=7Wv7qhdc91-vsaxiu4j8k7LY7V9dcwWHnHV7PLpOBAI,320
|
|
@@ -65,8 +65,8 @@ illumio_pylo/utilities/resources/iplists-import-example.xlsx,sha256=VW-7CRr8NA2V
|
|
|
65
65
|
illumio_pylo/utilities/resources/workload-exporter-filter-example.csv,sha256=cn5IA8AGEPjvS-EsPXA_GJ-LFsdF9t_44rSzFTCmAzE,36
|
|
66
66
|
illumio_pylo/utilities/resources/workloads-import-example.csv,sha256=DEOGVikFjxQpMFFI0l0jb3hrxEEeZCpTGkmWkz6GUcY,91
|
|
67
67
|
illumio_pylo/utilities/resources/workloads-import-example.xlsx,sha256=U8Ac2BZidA6NlvBFAVPHqeY5zmg3rjmIAXp5b3b1P5w,17101
|
|
68
|
-
illumio_pylo-0.3.
|
|
69
|
-
illumio_pylo-0.3.
|
|
70
|
-
illumio_pylo-0.3.
|
|
71
|
-
illumio_pylo-0.3.
|
|
72
|
-
illumio_pylo-0.3.
|
|
68
|
+
illumio_pylo-0.3.2.dist-info/LICENSE,sha256=WYmcYJG1QFgu1hfo7qrEkZ3Jhcz8NUWe6XUraZvlIFs,10172
|
|
69
|
+
illumio_pylo-0.3.2.dist-info/METADATA,sha256=TQ6C78x4N8G9pvl48e9MKRErO1CIjV17ETRUM1tN824,12224
|
|
70
|
+
illumio_pylo-0.3.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
71
|
+
illumio_pylo-0.3.2.dist-info/top_level.txt,sha256=c5cu_ZMuSuxjq48ih58Kc0Tr8-JdQtV8GrKJicvWNFE,13
|
|
72
|
+
illumio_pylo-0.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|