illumio-pylo 0.3.12__py3-none-any.whl → 0.3.13__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 +61 -14
- illumio_pylo/API/CredentialsManager.py +130 -3
- illumio_pylo/API/Explorer.py +619 -14
- illumio_pylo/API/JsonPayloadTypes.py +64 -4
- illumio_pylo/FilterQuery.py +892 -0
- illumio_pylo/LabelCommon.py +13 -3
- illumio_pylo/LabelDimension.py +109 -0
- illumio_pylo/LabelStore.py +97 -38
- illumio_pylo/WorkloadStore.py +58 -0
- illumio_pylo/__init__.py +9 -3
- illumio_pylo/cli/__init__.py +5 -2
- illumio_pylo/cli/commands/__init__.py +1 -0
- illumio_pylo/cli/commands/credential_manager.py +176 -0
- illumio_pylo/cli/commands/traffic_export.py +358 -0
- illumio_pylo/cli/commands/ui/credential_manager_ui/app.js +191 -2
- illumio_pylo/cli/commands/ui/credential_manager_ui/index.html +50 -1
- illumio_pylo/cli/commands/ui/credential_manager_ui/styles.css +179 -28
- illumio_pylo/cli/commands/update_pce_objects_cache.py +1 -2
- illumio_pylo/cli/commands/workload_export.py +29 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/METADATA +1 -1
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/RECORD +24 -22
- illumio_pylo/Query.py +0 -331
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/WHEEL +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/licenses/LICENSE +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/top_level.txt +0 -0
illumio_pylo/LabelCommon.py
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
2
|
from .Exception import PyloEx
|
|
3
3
|
from .LabelStore import LabelStore
|
|
4
4
|
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .LabelDimension import LabelDimension
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
class LabelCommon:
|
|
7
10
|
|
|
8
11
|
__slots__ = ['owner', 'name', 'href', 'type']
|
|
9
12
|
|
|
10
|
-
def __init__(self, name: str, href: str, label_type: str, owner: LabelStore):
|
|
13
|
+
def __init__(self, name: str, href: str, label_type: str, owner: LabelStore) -> None:
|
|
11
14
|
self.owner: LabelStore = owner
|
|
12
15
|
self.name: str = name
|
|
13
16
|
self.href: str = href
|
|
14
|
-
self.type = label_type
|
|
17
|
+
self.type: str = label_type
|
|
18
|
+
|
|
19
|
+
def get_dimension(self) -> 'LabelDimension':
|
|
20
|
+
"""Get the LabelDimension object for this label's type from the owning LabelStore."""
|
|
21
|
+
dimension: 'LabelDimension | None' = self.owner.get_dimension(self.type)
|
|
22
|
+
if dimension is None:
|
|
23
|
+
raise PyloEx(f"Dimension '{self.type}' not found in LabelStore")
|
|
24
|
+
return dimension
|
|
15
25
|
|
|
16
26
|
def is_label(self) -> bool:
|
|
17
27
|
raise PyloEx("not implemented")
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from typing import Dict, Optional, Set, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from .API.JsonPayloadTypes import LabelDimensionObjectStructure
|
|
5
|
+
|
|
6
|
+
# Module constants for dimension keys
|
|
7
|
+
DIMENSION_KEY_ROLE: str = 'role'
|
|
8
|
+
DIMENSION_KEY_APP: str = 'app'
|
|
9
|
+
DIMENSION_KEY_ENV: str = 'env'
|
|
10
|
+
DIMENSION_KEY_LOC: str = 'loc'
|
|
11
|
+
|
|
12
|
+
# Set of built-in dimension keys
|
|
13
|
+
BUILTIN_DIMENSION_KEYS: Set[str] = {DIMENSION_KEY_ROLE, DIMENSION_KEY_APP, DIMENSION_KEY_ENV, DIMENSION_KEY_LOC}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LabelDimension:
|
|
17
|
+
"""
|
|
18
|
+
Represents a label dimension (type) in the Illumio PCE.
|
|
19
|
+
|
|
20
|
+
Label dimensions define the categories of labels (e.g., role, app, env, loc).
|
|
21
|
+
Built-in dimensions are the standard four dimensions, while custom dimensions
|
|
22
|
+
can be created by the user.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__slots__ = ['key', 'display_name', 'href', 'created_at', 'created_by', 'updated_at', 'updated_by', 'raw_json']
|
|
26
|
+
|
|
27
|
+
def __init__(self, key: str, display_name: str, href: Optional[str] = None,
|
|
28
|
+
created_at: Optional[str] = None, created_by: Optional[str] = None,
|
|
29
|
+
updated_at: Optional[str] = None, updated_by: Optional[str] = None,
|
|
30
|
+
raw_json: Optional['LabelDimensionObjectStructure'] = None) -> None:
|
|
31
|
+
self.key: str = key
|
|
32
|
+
self.display_name: str = display_name
|
|
33
|
+
self.href: Optional[str] = href
|
|
34
|
+
self.created_at: Optional[str] = created_at
|
|
35
|
+
self.created_by: Optional[str] = created_by
|
|
36
|
+
self.updated_at: Optional[str] = updated_at
|
|
37
|
+
self.updated_by: Optional[str] = updated_by
|
|
38
|
+
self.raw_json: Optional['LabelDimensionObjectStructure'] = raw_json
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def from_json(json_data: 'LabelDimensionObjectStructure') -> 'LabelDimension':
|
|
42
|
+
"""
|
|
43
|
+
Factory method to create a LabelDimension from JSON data returned by the API.
|
|
44
|
+
|
|
45
|
+
:param json_data: The JSON structure from the API
|
|
46
|
+
:return: A new LabelDimension instance
|
|
47
|
+
"""
|
|
48
|
+
created_by: Optional[str] = None
|
|
49
|
+
if json_data.get('created_by') is not None:
|
|
50
|
+
created_by = json_data['created_by'].get('href')
|
|
51
|
+
|
|
52
|
+
updated_by: Optional[str] = None
|
|
53
|
+
if json_data.get('updated_by') is not None:
|
|
54
|
+
updated_by = json_data['updated_by'].get('href')
|
|
55
|
+
|
|
56
|
+
return LabelDimension(
|
|
57
|
+
key=json_data['key'],
|
|
58
|
+
display_name=json_data['display_name'],
|
|
59
|
+
href=json_data.get('href'),
|
|
60
|
+
created_at=json_data.get('created_at'),
|
|
61
|
+
created_by=created_by,
|
|
62
|
+
updated_at=json_data.get('updated_at'),
|
|
63
|
+
updated_by=updated_by,
|
|
64
|
+
raw_json=json_data
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def create_builtin_dimensions() -> Dict[str, 'LabelDimension']:
|
|
69
|
+
"""
|
|
70
|
+
Create the four built-in label dimensions with default display names.
|
|
71
|
+
|
|
72
|
+
:return: A dictionary mapping dimension keys to LabelDimension objects
|
|
73
|
+
"""
|
|
74
|
+
return {
|
|
75
|
+
DIMENSION_KEY_ROLE: LabelDimension(key=DIMENSION_KEY_ROLE, display_name='Role'),
|
|
76
|
+
DIMENSION_KEY_APP: LabelDimension(key=DIMENSION_KEY_APP, display_name='Application'),
|
|
77
|
+
DIMENSION_KEY_ENV: LabelDimension(key=DIMENSION_KEY_ENV, display_name='Environment'),
|
|
78
|
+
DIMENSION_KEY_LOC: LabelDimension(key=DIMENSION_KEY_LOC, display_name='Location'),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def is_role(self) -> bool:
|
|
82
|
+
"""Check if this dimension is the Role dimension."""
|
|
83
|
+
return self.key == DIMENSION_KEY_ROLE
|
|
84
|
+
|
|
85
|
+
def is_application(self) -> bool:
|
|
86
|
+
"""Check if this dimension is the Application dimension."""
|
|
87
|
+
return self.key == DIMENSION_KEY_APP
|
|
88
|
+
|
|
89
|
+
def is_environment(self) -> bool:
|
|
90
|
+
"""Check if this dimension is the Environment dimension."""
|
|
91
|
+
return self.key == DIMENSION_KEY_ENV
|
|
92
|
+
|
|
93
|
+
def is_location(self) -> bool:
|
|
94
|
+
"""Check if this dimension is the Location dimension."""
|
|
95
|
+
return self.key == DIMENSION_KEY_LOC
|
|
96
|
+
|
|
97
|
+
def is_builtin(self) -> bool:
|
|
98
|
+
"""Check if this dimension is one of the four built-in dimensions."""
|
|
99
|
+
return self.key in BUILTIN_DIMENSION_KEYS
|
|
100
|
+
|
|
101
|
+
def is_custom(self) -> bool:
|
|
102
|
+
"""Check if this dimension is a custom (non-built-in) dimension."""
|
|
103
|
+
return self.key not in BUILTIN_DIMENSION_KEYS
|
|
104
|
+
|
|
105
|
+
def __repr__(self) -> str:
|
|
106
|
+
return f"LabelDimension(key='{self.key}', display_name='{self.display_name}')"
|
|
107
|
+
|
|
108
|
+
def __str__(self) -> str:
|
|
109
|
+
return f"{self.display_name} ({self.key})"
|
illumio_pylo/LabelStore.py
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
from hashlib import md5
|
|
2
2
|
import random
|
|
3
|
-
from typing import Set
|
|
3
|
+
from typing import Set, Union
|
|
4
4
|
# Pylo imports
|
|
5
5
|
from illumio_pylo import log
|
|
6
6
|
from .API.JsonPayloadTypes import LabelObjectJsonStructure, LabelGroupObjectJsonStructure, LabelDimensionObjectStructure
|
|
7
7
|
from .Helpers import *
|
|
8
|
+
from .LabelDimension import (
|
|
9
|
+
LabelDimension,
|
|
10
|
+
DIMENSION_KEY_ROLE, DIMENSION_KEY_APP, DIMENSION_KEY_ENV, DIMENSION_KEY_LOC
|
|
11
|
+
)
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
# Backward compatibility aliases
|
|
14
|
+
label_type_loc: str = DIMENSION_KEY_LOC
|
|
15
|
+
label_type_env: str = DIMENSION_KEY_ENV
|
|
16
|
+
label_type_app: str = DIMENSION_KEY_APP
|
|
17
|
+
label_type_role: str = DIMENSION_KEY_ROLE
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
class LabelStore:
|
|
@@ -47,32 +52,78 @@ class LabelStore:
|
|
|
47
52
|
result.append(label)
|
|
48
53
|
return result
|
|
49
54
|
|
|
50
|
-
__slots__ = ['owner', '_items_by_href', '
|
|
55
|
+
__slots__ = ['owner', '_items_by_href', '_dimensions', '_dimensions_dict', '_label_types_cache', '_label_types_as_set_cache', 'label_resolution_cache']
|
|
51
56
|
|
|
52
|
-
def __init__(self, owner: 'pylo.Organization'):
|
|
57
|
+
def __init__(self, owner: 'pylo.Organization') -> None:
|
|
53
58
|
self.owner: "pylo.Organization" = owner
|
|
54
59
|
self._items_by_href: Dict[str, Union[pylo.Label, pylo.LabelGroup]] = {}
|
|
55
|
-
self.
|
|
56
|
-
self.
|
|
60
|
+
self._dimensions: List[LabelDimension] = []
|
|
61
|
+
self._dimensions_dict: Dict[str, LabelDimension] = {}
|
|
62
|
+
self._label_types_cache: Optional[List[str]] = None
|
|
63
|
+
self._label_types_as_set_cache: Optional[Set[str]] = None
|
|
64
|
+
|
|
65
|
+
self.label_resolution_cache: Optional[Dict[str, List[pylo.Workload]]] = None
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def label_types(self) -> List[str]:
|
|
69
|
+
"""Backward-compatible property returning list of label type keys (strings)."""
|
|
70
|
+
if self._label_types_cache is None:
|
|
71
|
+
self._label_types_cache = [dim.key for dim in self._dimensions]
|
|
72
|
+
return self._label_types_cache
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def label_types_as_set(self) -> Set[str]:
|
|
76
|
+
"""Backward-compatible property returning set of label type keys (strings)."""
|
|
77
|
+
if self._label_types_as_set_cache is None:
|
|
78
|
+
self._label_types_as_set_cache = set(dim.key for dim in self._dimensions)
|
|
79
|
+
return self._label_types_as_set_cache
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def dimensions(self) -> List[LabelDimension]:
|
|
83
|
+
"""Return the list of LabelDimension objects, preserving order."""
|
|
84
|
+
return self._dimensions
|
|
85
|
+
|
|
86
|
+
def get_dimension(self, key: str) -> Optional[LabelDimension]:
|
|
87
|
+
"""Get a LabelDimension by its key."""
|
|
88
|
+
return self._dimensions_dict.get(key)
|
|
89
|
+
|
|
90
|
+
def _add_dimension(self, dimension: Union[str, LabelDimension]) -> None:
|
|
91
|
+
"""Add a dimension to the store. Accepts either a string key or a LabelDimension object."""
|
|
92
|
+
if isinstance(dimension, str):
|
|
93
|
+
# Create a basic LabelDimension from string key for backward compatibility
|
|
94
|
+
if dimension in self._dimensions_dict:
|
|
95
|
+
return
|
|
96
|
+
# Create built-in dimensions with proper display names
|
|
97
|
+
builtin_dims: Dict[str, LabelDimension] = LabelDimension.create_builtin_dimensions()
|
|
98
|
+
if dimension in builtin_dims:
|
|
99
|
+
dim_obj: LabelDimension = builtin_dims[dimension]
|
|
100
|
+
else:
|
|
101
|
+
# Custom dimension from string - use key as display name
|
|
102
|
+
dim_obj = LabelDimension(key=dimension, display_name=dimension)
|
|
103
|
+
else:
|
|
104
|
+
dim_obj = dimension
|
|
105
|
+
if dim_obj.key in self._dimensions_dict:
|
|
106
|
+
return
|
|
57
107
|
|
|
58
|
-
self.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
self.label_types.append(dimension)
|
|
108
|
+
self._dimensions.append(dim_obj)
|
|
109
|
+
self._dimensions_dict[dim_obj.key] = dim_obj
|
|
110
|
+
# Invalidate caches
|
|
111
|
+
self._label_types_cache = None
|
|
112
|
+
self._label_types_as_set_cache = None
|
|
64
113
|
|
|
65
|
-
def load_label_dimensions(self, json_list: Optional[List[LabelDimensionObjectStructure]]):
|
|
114
|
+
def load_label_dimensions(self, json_list: Optional[List[LabelDimensionObjectStructure]]) -> None:
|
|
66
115
|
if json_list is None or len(json_list) == 0:
|
|
67
|
-
# add the default built-in label types
|
|
68
|
-
|
|
69
|
-
self._add_dimension(
|
|
70
|
-
self._add_dimension(
|
|
71
|
-
self._add_dimension(
|
|
116
|
+
# add the default built-in label types in the standard order
|
|
117
|
+
builtin_dims: Dict[str, LabelDimension] = LabelDimension.create_builtin_dimensions()
|
|
118
|
+
self._add_dimension(builtin_dims[DIMENSION_KEY_ROLE])
|
|
119
|
+
self._add_dimension(builtin_dims[DIMENSION_KEY_APP])
|
|
120
|
+
self._add_dimension(builtin_dims[DIMENSION_KEY_ENV])
|
|
121
|
+
self._add_dimension(builtin_dims[DIMENSION_KEY_LOC])
|
|
72
122
|
return
|
|
73
123
|
|
|
74
|
-
for
|
|
75
|
-
|
|
124
|
+
for dimension_json in json_list:
|
|
125
|
+
dim_obj: LabelDimension = LabelDimension.from_json(dimension_json)
|
|
126
|
+
self._add_dimension(dim_obj)
|
|
76
127
|
|
|
77
128
|
def load_labels_from_json(self, json_list: List[LabelObjectJsonStructure]):
|
|
78
129
|
for json_label in json_list:
|
|
@@ -202,7 +253,7 @@ class LabelStore:
|
|
|
202
253
|
allow_label: bool = True,
|
|
203
254
|
raise_exception_if_not_found: bool = False) \
|
|
204
255
|
-> Optional[Union['pylo.Label', 'pylo.LabelGroup', List[Union['pylo.Label', 'pylo.LabelGroup']]]]:
|
|
205
|
-
"""Find a label by its name. If case_sensitive is False, the search is case-insensitive
|
|
256
|
+
"""Find a label by its name. If case_sensitive is False, the search is case-insensitive and will return a list of matches
|
|
206
257
|
If case_sensitive is False it will return a list of labels with the same name rather than a single object.
|
|
207
258
|
If missing_labels_names is not None, it will be filled with the names of the labels not found.
|
|
208
259
|
If raise_exception_if_not_found is True, an exception will be raised if a label is not found.
|
|
@@ -210,22 +261,30 @@ class LabelStore:
|
|
|
210
261
|
If a label is not found, None will be returned in the list.
|
|
211
262
|
"""
|
|
212
263
|
if not isinstance(name, list):
|
|
213
|
-
if case_sensitive
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
continue
|
|
222
|
-
if label.is_label() and allow_label: # ignore groups
|
|
223
|
-
if case_sensitive:
|
|
264
|
+
if case_sensitive:
|
|
265
|
+
for label in self._items_by_href.values():
|
|
266
|
+
if label_type is not None and label.type != label_type:
|
|
267
|
+
continue
|
|
268
|
+
if label.is_label() and allow_label:
|
|
269
|
+
if label.name == name:
|
|
270
|
+
return label
|
|
271
|
+
elif label.is_group() and allow_label_group: # ignore labels
|
|
224
272
|
if label.name == name:
|
|
225
273
|
return label
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
274
|
+
else:
|
|
275
|
+
matches = []
|
|
276
|
+
for label in self._items_by_href.values():
|
|
277
|
+
if label_type is not None and label.type != label_type:
|
|
278
|
+
continue
|
|
279
|
+
if label.is_label() and allow_label:
|
|
280
|
+
if label.name.lower() == name.lower():
|
|
281
|
+
matches.append(label)
|
|
282
|
+
elif label.is_group() and allow_label_group: # ignore labels
|
|
283
|
+
if label.name.lower() == name.lower():
|
|
284
|
+
matches.append(label)
|
|
285
|
+
if len(matches) > 0:
|
|
286
|
+
return matches
|
|
287
|
+
|
|
229
288
|
if raise_exception_if_not_found:
|
|
230
289
|
raise pylo.PyloEx("Label/group '%s' not found", name)
|
|
231
290
|
if missing_labels_names is not None:
|
illumio_pylo/WorkloadStore.py
CHANGED
|
@@ -6,6 +6,7 @@ from .Organization import Organization
|
|
|
6
6
|
from typing import Optional, List, Union, Set, Iterable
|
|
7
7
|
|
|
8
8
|
from .WorkloadStoreSubClasses import UnmanagedWorkloadDraft, UnmanagedWorkloadDraftMultiCreatorManager
|
|
9
|
+
from .FilterQuery import FilterQuery, get_workload_filter_registry
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class WorkloadStore:
|
|
@@ -297,5 +298,62 @@ class WorkloadStore:
|
|
|
297
298
|
def new_unmanaged_workload_multi_creator_manager(self) -> UnmanagedWorkloadDraftMultiCreatorManager:
|
|
298
299
|
return UnmanagedWorkloadDraftMultiCreatorManager(self)
|
|
299
300
|
|
|
301
|
+
def find_workloads_matching_query(self, query: str, include_deleted: bool = False) -> List['Workload']:
|
|
302
|
+
"""
|
|
303
|
+
Find all workloads matching a filter query expression.
|
|
304
|
+
|
|
305
|
+
Query syntax supports:
|
|
306
|
+
- Comparison operators: ==, !=, <, >, <=, >=
|
|
307
|
+
- String operators: contains, matches (regex)
|
|
308
|
+
- Logical operators: and, or, not
|
|
309
|
+
- Parentheses for grouping
|
|
310
|
+
|
|
311
|
+
Available fields:
|
|
312
|
+
- name, hostname, forced_name, href, description
|
|
313
|
+
- online, managed, unmanaged, deleted
|
|
314
|
+
- os_id, os_detail
|
|
315
|
+
- ip_address, ip (checks all interfaces)
|
|
316
|
+
- last_heartbeat, last_heartbeat_received
|
|
317
|
+
- agent.status, agent.mode, mode, agent.version
|
|
318
|
+
- label.<type> for any label type configured in the PCE (e.g., label.role, label.app, label.env, label.loc)
|
|
319
|
+
- Shorthand aliases for labels (e.g., role, app, env, loc)
|
|
320
|
+
- created_at
|
|
321
|
+
- reference_count
|
|
322
|
+
|
|
323
|
+
Examples:
|
|
324
|
+
"(name == 'SRV158' or name == 'SRV48889') and ip_address == '192.168.2.54'"
|
|
325
|
+
"last_heartbeat_received <= '2022-09-12' and online == true"
|
|
326
|
+
"hostname contains 'prod' and label.env == 'Production'"
|
|
327
|
+
"name matches 'SRV[0-9]+' and not deleted == true"
|
|
328
|
+
|
|
329
|
+
:param query: filter query expression
|
|
330
|
+
:param include_deleted: whether to include deleted workloads in the search
|
|
331
|
+
:return: list of matching Workload objects
|
|
332
|
+
"""
|
|
333
|
+
# Pass owner (Organization) to get registry with all configured label types
|
|
334
|
+
registry = get_workload_filter_registry(self.owner)
|
|
335
|
+
filter_query = FilterQuery(registry)
|
|
336
|
+
|
|
337
|
+
# Get workloads to filter
|
|
338
|
+
if include_deleted:
|
|
339
|
+
workloads = list(self.itemsByHRef.values())
|
|
340
|
+
else:
|
|
341
|
+
workloads = [w for w in self.itemsByHRef.values() if not w.deleted]
|
|
342
|
+
|
|
343
|
+
return filter_query.execute(query, workloads)
|
|
344
|
+
|
|
345
|
+
def find_workloads_matching_query_as_dict(self, query: str, include_deleted: bool = False) -> Dict[str, 'Workload']:
|
|
346
|
+
"""
|
|
347
|
+
Find all workloads matching a filter query expression, returned as a dict with HREF as key.
|
|
348
|
+
|
|
349
|
+
See find_workloads_matching_query() for query syntax and available fields.
|
|
350
|
+
|
|
351
|
+
:param query: filter query expression
|
|
352
|
+
:param include_deleted: whether to include deleted workloads in the search
|
|
353
|
+
:return: dict of matching Workload objects with HREF as key
|
|
354
|
+
"""
|
|
355
|
+
matching = self.find_workloads_matching_query(query, include_deleted)
|
|
356
|
+
return {w.href: w for w in matching}
|
|
357
|
+
|
|
300
358
|
|
|
301
359
|
|
illumio_pylo/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.13"
|
|
2
2
|
|
|
3
3
|
from typing import Callable
|
|
4
4
|
|
|
@@ -12,9 +12,11 @@ from .ReferenceTracker import ReferenceTracker, Referencer, Pathable
|
|
|
12
12
|
from .API.APIConnector import APIConnector, ObjectTypes
|
|
13
13
|
from .API.RuleSearchQuery import RuleSearchQuery, RuleSearchQueryResolvedResultSet
|
|
14
14
|
from .API.ClusterHealth import ClusterHealth
|
|
15
|
-
from .API.Explorer import ExplorerResultSetV1, RuleCoverageQueryManager, ExplorerFilterSetV1,
|
|
15
|
+
from .API.Explorer import (ExplorerResultSetV1, ExplorerResultSetV2, RuleCoverageQueryManager, ExplorerFilterSetV1,
|
|
16
|
+
ExplorerFilterSetV2, ExplorerQuery, ExplorerQueryV2, ExplorerResultV2)
|
|
16
17
|
from .API.AuditLog import AuditLogQuery, AuditLogQueryResultSet, AuditLogFilterSet
|
|
17
18
|
from .API.CredentialsManager import get_credentials_from_file
|
|
19
|
+
from .LabelDimension import LabelDimension, DIMENSION_KEY_ROLE, DIMENSION_KEY_APP, DIMENSION_KEY_ENV, DIMENSION_KEY_LOC
|
|
18
20
|
from .LabelCommon import LabelCommon
|
|
19
21
|
from .Label import Label
|
|
20
22
|
from .LabelGroup import LabelGroup
|
|
@@ -31,7 +33,11 @@ from .Ruleset import Ruleset, RulesetScope, RulesetScopeEntry
|
|
|
31
33
|
from .RulesetStore import RulesetStore
|
|
32
34
|
from .SecurityPrincipal import SecurityPrincipal, SecurityPrincipalStore
|
|
33
35
|
from .Organization import Organization
|
|
34
|
-
from .
|
|
36
|
+
from .FilterQuery import (
|
|
37
|
+
FilterQuery, FilterRegistry, FilterField, ValueType,
|
|
38
|
+
WorkloadFilterRegistry, get_workload_filter_registry,
|
|
39
|
+
QueryLexer, QueryParser, QueryNode, AndNode, OrNode, NotNode, ConditionNode
|
|
40
|
+
)
|
|
35
41
|
|
|
36
42
|
|
|
37
43
|
def get_organization(fqdn: str, port: int, api_user: str, api_key: str,
|
illumio_pylo/cli/__init__.py
CHANGED
|
@@ -38,13 +38,15 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
38
38
|
|
|
39
39
|
parser = argparse.ArgumentParser(description='PYLO-CLI: Illumio API&More Command Line Interface')
|
|
40
40
|
parser.add_argument('--pce', type=str, required=False,
|
|
41
|
-
help='
|
|
41
|
+
help='name of the CredentialProfile to use (as defined in the credentials file)')
|
|
42
42
|
parser.add_argument('--force-async-mode', action='store_true',
|
|
43
43
|
help='Forces the command to run async API queries when required (large PCEs which timeout on specific queries)')
|
|
44
44
|
parser.add_argument('--debug', action='store_true',
|
|
45
45
|
help='Enables extra debugging output in Pylo framework')
|
|
46
46
|
parser.add_argument('--use-cache', action='store_true',
|
|
47
47
|
help='For developers only')
|
|
48
|
+
parser.add_argument('--include-deleted-workloads', action='store_true',
|
|
49
|
+
help='Include deleted workloads when loading data from the PCE')
|
|
48
50
|
parser.add_argument('--version', action='store_true', help='Prints the version of the Pylo CLI')
|
|
49
51
|
|
|
50
52
|
selected_command = None
|
|
@@ -80,6 +82,7 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
80
82
|
|
|
81
83
|
credential_profile_name = args['pce']
|
|
82
84
|
settings_use_cache = args['use_cache']
|
|
85
|
+
include_deleted_workloads = args['include_deleted_workloads']
|
|
83
86
|
org: Optional[pylo.Organization] = None
|
|
84
87
|
|
|
85
88
|
# We are getting the command object associated to the command name if it was not already set (via forced_command_name)
|
|
@@ -111,7 +114,7 @@ def run(forced_command_name: Optional[str] = None):
|
|
|
111
114
|
print("OK!")
|
|
112
115
|
|
|
113
116
|
print(" * Downloading PCE objects from API... ".format(credential_profile_name), end="", flush=True)
|
|
114
|
-
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
|
+
config_data = connector.get_pce_objects(list_of_objects_to_load=selected_command.load_specific_objects_only, force_async_mode=args['force_async_mode'], include_deleted_workloads=include_deleted_workloads)
|
|
115
118
|
timer_download_finished = time.perf_counter()
|
|
116
119
|
print("OK! (execution time: {:.2f} seconds)".format(timer_download_finished - timer_start))
|
|
117
120
|
|