illumio-pylo 0.3.12__tar.gz → 0.3.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {illumio_pylo-0.3.12/illumio_pylo.egg-info → illumio_pylo-0.3.13}/PKG-INFO +1 -1
- illumio_pylo-0.3.13/docs/ENV_CREDENTIALS.md +205 -0
- illumio_pylo-0.3.13/examples/example_env_credentials.py +77 -0
- illumio_pylo-0.3.13/examples/explorer_query.py +101 -0
- illumio_pylo-0.3.13/examples/filter_query_example.py +122 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/APIConnector.py +61 -14
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/CredentialsManager.py +130 -3
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/Explorer.py +619 -14
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/JsonPayloadTypes.py +64 -4
- illumio_pylo-0.3.13/illumio_pylo/FilterQuery.py +892 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/LabelCommon.py +13 -3
- illumio_pylo-0.3.13/illumio_pylo/LabelDimension.py +109 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/LabelStore.py +97 -38
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/WorkloadStore.py +58 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/__init__.py +9 -3
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/__init__.py +5 -2
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/__init__.py +1 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/credential_manager.py +176 -0
- illumio_pylo-0.3.13/illumio_pylo/cli/commands/traffic_export.py +358 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ui/credential_manager_ui/app.js +191 -2
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ui/credential_manager_ui/index.html +50 -1
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ui/credential_manager_ui/styles.css +179 -28
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/update_pce_objects_cache.py +1 -2
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/workload_export.py +29 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13/illumio_pylo.egg-info}/PKG-INFO +1 -1
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo.egg-info/SOURCES.txt +10 -2
- illumio_pylo-0.3.13/tests/test_env_credentials.py +198 -0
- illumio_pylo-0.3.13/tests/test_filter_query.py +344 -0
- illumio_pylo-0.3.13/tests/test_nested_queries.py +34 -0
- illumio_pylo-0.3.12/examples/explorer_query.py +0 -84
- illumio_pylo-0.3.12/illumio_pylo/Query.py +0 -331
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.devcontainer/Dockerfile +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.devcontainer/devcontainer.json +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.gitattributes +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.github/workflows/doxygen-publish.yml +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.github/workflows/make-binaries.yml +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.github/workflows/python-publish.yml +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/.gitignore +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/LICENSE +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/README.md +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/check_unique_hostnames.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/check_unique_services.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/delete_all_workloads.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/delete_unused_services.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/explorer_report_exporter.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/export_rules_to_firewall.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/generate-random-workloads.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/healthcheck_log.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/import-labels.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/import_workloads_placeholders.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/iplists_stats_duplicates_unused_finder.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/recalculate_explorer_logs.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/recalculate_explorer_logs_multithreaded.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/rules_exporter.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/rules_exporter_special.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/test.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/test_change_workload_desc.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/test_query.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/test_query2.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/test_securityprincipals.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/ven_idle_to_illumination.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/dev_playground/ven_reassign_pce.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/examples/extend_cli.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/AuditLog.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/ClusterHealth.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/RuleSearchQuery.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/API/__init__.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/AgentStore.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Exception.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Helpers/__init__.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Helpers/exports.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Helpers/functions.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/IPList.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/IPMap.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Label.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/LabelGroup.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/LabeledObject.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Organization.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/ReferenceTracker.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Rule.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Ruleset.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/RulesetStore.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/SecurityPrincipal.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Service.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/SoftwareVersion.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/VirtualService.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/VirtualServiceStore.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/Workload.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/WorkloadStoreSubClasses.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/NativeParsers.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/__main__.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/iplist_analyzer.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/iplist_import_from_file.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/label_delete_unused.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ruleset_export.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/utils/LabelCreation.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/utils/__init__.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/utils/misc.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ven_compatibility_report_export.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ven_duplicate_remover.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ven_idle_to_visibility.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/ven_upgrader.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/workload_import.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/workload_reset_names_to_null.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/workload_update.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/cli/commands/workload_used_in_rule_finder.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/docs/Doxygen +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/tmp.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/__init__.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/cli.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/credentials.example.json +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/health_monitoring.py +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/resources/iplists-import-example.csv +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/resources/workloads-import-example.csv +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo.egg-info/dependency_links.txt +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo.egg-info/requires.txt +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/illumio_pylo.egg-info/top_level.txt +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/pyproject.toml +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/requirements.txt +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/setup.cfg +0 -0
- {illumio_pylo-0.3.12 → illumio_pylo-0.3.13}/setup.py +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Environment Variable Credentials
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `CredentialProfile.from_environment_variables()` feature allows you to provide Illumio PCE credentials via environment variables instead of storing them in credential files on disk. This is particularly useful for:
|
|
6
|
+
|
|
7
|
+
- CI/CD pipelines
|
|
8
|
+
- Docker containers
|
|
9
|
+
- Security-conscious environments where credentials shouldn't be written to disk
|
|
10
|
+
- Temporary credential usage
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
To use environment variable credentials, simply specify the profile name `'ENV'` (case-insensitive) when loading credentials:
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import illumio_pylo as pylo
|
|
18
|
+
|
|
19
|
+
# Load credentials from environment variables
|
|
20
|
+
credentials = pylo.get_credentials_from_file('ENV')
|
|
21
|
+
|
|
22
|
+
# Or use with APIConnector
|
|
23
|
+
connector = pylo.APIConnector.create_from_credentials_in_file('ENV')
|
|
24
|
+
|
|
25
|
+
# Or use with Organization
|
|
26
|
+
org = pylo.Organization.get_from_api_using_credential_file('ENV')
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Required Environment Variables
|
|
30
|
+
|
|
31
|
+
- **`PYLO_FQDN`**: Fully qualified domain name of the PCE (e.g., `pce.example.com`)
|
|
32
|
+
- **`PYLO_API_USER`**: API username
|
|
33
|
+
- **`PYLO_API_KEY`**: API key (can be encrypted with `$encrypted$:` prefix)
|
|
34
|
+
|
|
35
|
+
## Optional Environment Variables
|
|
36
|
+
|
|
37
|
+
- **`PYLO_PORT`**: Port number
|
|
38
|
+
- Default: `8443` for standard PCE
|
|
39
|
+
- Default: `443` for illum.io hosted domains (SaaS)
|
|
40
|
+
- Valid range: 1-65535
|
|
41
|
+
|
|
42
|
+
- **`PYLO_ORG_ID`**: Organization ID
|
|
43
|
+
- Default: `1` for standard PCE
|
|
44
|
+
- **Required** for illum.io hosted domains (no default)
|
|
45
|
+
- Must be a positive integer
|
|
46
|
+
|
|
47
|
+
- **`PYLO_VERIFY_SSL`**: Verify SSL certificate
|
|
48
|
+
- Default: `true`
|
|
49
|
+
- Accepts: `true`, `false`, `1`, `0`, `yes`, `no`, `y`, `n` (case-insensitive)
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### Basic Example (Standard PCE)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Set environment variables
|
|
57
|
+
export PYLO_FQDN="pce.example.com"
|
|
58
|
+
export PYLO_API_USER="api_12345"
|
|
59
|
+
export PYLO_API_KEY="your_api_key_here"
|
|
60
|
+
|
|
61
|
+
# Run your Python script
|
|
62
|
+
python your_script.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import illumio_pylo as pylo
|
|
67
|
+
|
|
68
|
+
# Load from environment
|
|
69
|
+
org = pylo.Organization.get_from_api_using_credential_file('ENV')
|
|
70
|
+
print(f"Connected to {org.connector.fqdn}")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Illumio SaaS Example
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# For illum.io domains, ORG_ID is required
|
|
77
|
+
export PYLO_FQDN="mycompany.illum.io"
|
|
78
|
+
export PYLO_API_USER="api_12345"
|
|
79
|
+
export PYLO_API_KEY="your_api_key_here"
|
|
80
|
+
export PYLO_ORG_ID="5"
|
|
81
|
+
|
|
82
|
+
# Port defaults to 443 for illum.io domains
|
|
83
|
+
python your_script.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Custom Configuration
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Custom port and disable SSL verification
|
|
90
|
+
export PYLO_FQDN="pce.internal.local"
|
|
91
|
+
export PYLO_API_USER="api_12345"
|
|
92
|
+
export PYLO_API_KEY="your_api_key_here"
|
|
93
|
+
export PYLO_PORT="9443"
|
|
94
|
+
export PYLO_ORG_ID="2"
|
|
95
|
+
export PYLO_VERIFY_SSL="false"
|
|
96
|
+
|
|
97
|
+
python your_script.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Docker Container Example
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
docker run -e PYLO_FQDN="pce.example.com" \
|
|
104
|
+
-e PYLO_API_USER="api_12345" \
|
|
105
|
+
-e PYLO_API_KEY="your_api_key" \
|
|
106
|
+
your-container:latest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Encrypted API Key
|
|
110
|
+
|
|
111
|
+
If you have an encrypted API key (generated with the `cred-manager` tool), you can use it directly:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
export PYLO_FQDN="pce.example.com"
|
|
115
|
+
export PYLO_API_USER="api_12345"
|
|
116
|
+
export PYLO_API_KEY='$encrypted$:ssh-ChaCha20Poly1305:...'
|
|
117
|
+
|
|
118
|
+
python your_script.py
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The API key will be automatically decrypted using your SSH agent.
|
|
122
|
+
|
|
123
|
+
## Checking Availability
|
|
124
|
+
|
|
125
|
+
You can check if required environment variables are set before attempting to load:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from illumio_pylo.API.CredentialsManager import is_env_credentials_available
|
|
129
|
+
|
|
130
|
+
if is_env_credentials_available():
|
|
131
|
+
print("Environment credentials are available")
|
|
132
|
+
credentials = pylo.get_credentials_from_file('ENV')
|
|
133
|
+
else:
|
|
134
|
+
print("Missing required environment variables")
|
|
135
|
+
# Fall back to file-based credentials
|
|
136
|
+
credentials = pylo.get_credentials_from_file('default')
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Validation and Error Handling
|
|
140
|
+
|
|
141
|
+
The implementation includes comprehensive validation:
|
|
142
|
+
|
|
143
|
+
- **Missing required variables**: Clear error message listing which variables are missing
|
|
144
|
+
- **Invalid port**: Must be a valid integer between 1-65535
|
|
145
|
+
- **Invalid org_id**: Must be a positive integer
|
|
146
|
+
- **Invalid verify_ssl**: Must be a boolean-like value
|
|
147
|
+
- **illum.io domains**: Automatically defaults port to 443 and requires ORG_ID
|
|
148
|
+
|
|
149
|
+
Example error messages:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
Missing required environment variables for ENV profile: PYLO_FQDN, PYLO_API_KEY.
|
|
153
|
+
Required: PYLO_FQDN, PYLO_API_USER, PYLO_API_KEY.
|
|
154
|
+
Optional: PYLO_PORT, PYLO_ORG_ID, PYLO_VERIFY_SSL
|
|
155
|
+
|
|
156
|
+
Invalid PYLO_PORT value 'abc': must be a valid port number (1-65535)
|
|
157
|
+
|
|
158
|
+
PYLO_ORG_ID is required for illum.io domains (no default available)
|
|
159
|
+
|
|
160
|
+
Invalid PYLO_VERIFY_SSL value 'maybe': must be true/false/1/0/yes/no/y/n (case-insensitive)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Security Considerations
|
|
164
|
+
|
|
165
|
+
### Advantages
|
|
166
|
+
- Credentials never written to disk
|
|
167
|
+
- Easier to rotate in CI/CD environments
|
|
168
|
+
- No risk of accidentally committing credentials to version control
|
|
169
|
+
- Better integration with secrets management tools (AWS Secrets Manager, HashiCorp Vault, etc.)
|
|
170
|
+
|
|
171
|
+
### Best Practices
|
|
172
|
+
1. **Never log environment variables** - Be careful not to print or log the actual API key
|
|
173
|
+
2. **Use encrypted keys when possible** - Leverage SSH agent encryption for additional security
|
|
174
|
+
3. **Rotate credentials regularly** - Environment-based approach makes rotation easier
|
|
175
|
+
4. **Use secrets management** - In production, use proper secrets management tools to inject environment variables
|
|
176
|
+
5. **Limit scope** - Set environment variables only for the specific process that needs them
|
|
177
|
+
|
|
178
|
+
### Example with AWS Secrets Manager
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import boto3
|
|
182
|
+
import json
|
|
183
|
+
import os
|
|
184
|
+
import illumio_pylo as pylo
|
|
185
|
+
|
|
186
|
+
# Fetch credentials from AWS Secrets Manager
|
|
187
|
+
client = boto3.client('secretsmanager')
|
|
188
|
+
secret = client.get_secret_value(SecretId='pylo/pce/credentials')
|
|
189
|
+
creds = json.loads(secret['SecretString'])
|
|
190
|
+
|
|
191
|
+
# Set environment variables
|
|
192
|
+
os.environ['PYLO_FQDN'] = creds['fqdn']
|
|
193
|
+
os.environ['PYLO_API_USER'] = creds['api_user']
|
|
194
|
+
os.environ['PYLO_API_KEY'] = creds['api_key']
|
|
195
|
+
|
|
196
|
+
# Use ENV profile
|
|
197
|
+
org = pylo.Organization.get_from_api_using_credential_file('ENV')
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Notes
|
|
201
|
+
|
|
202
|
+
- The profile name is always `'ENV'` (case-insensitive: `'env'`, `'Env'`, `'ENV'` all work)
|
|
203
|
+
- The `originating_file` attribute is set to `'environment'` for env-based profiles
|
|
204
|
+
- Environment credentials are **never** included in `get_all_credentials()` - they must be explicitly requested
|
|
205
|
+
- Environment credentials don't require any credential files to exist on disk
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Practical example of using environment variable credentials with Illumio Pylo
|
|
4
|
+
This demonstrates how to connect to a PCE using only environment variables
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import illumio_pylo as pylo
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
# Check if environment credentials are available
|
|
11
|
+
from illumio_pylo.API.CredentialsManager import is_env_credentials_available
|
|
12
|
+
|
|
13
|
+
if not is_env_credentials_available():
|
|
14
|
+
print("ERROR: Required environment variables are not set!")
|
|
15
|
+
print("Please set: PYLO_FQDN, PYLO_API_USER, PYLO_API_KEY")
|
|
16
|
+
print("\nExample:")
|
|
17
|
+
print(" export PYLO_FQDN='pce.example.com'")
|
|
18
|
+
print(" export PYLO_API_USER='api_12345'")
|
|
19
|
+
print(" export PYLO_API_KEY='your_api_key_here'")
|
|
20
|
+
return 1
|
|
21
|
+
|
|
22
|
+
print("Environment credentials detected!")
|
|
23
|
+
print("-" * 60)
|
|
24
|
+
|
|
25
|
+
# Load credentials from environment
|
|
26
|
+
try:
|
|
27
|
+
credentials = pylo.get_credentials_from_file('ENV')
|
|
28
|
+
print(f"Credentials loaded:")
|
|
29
|
+
print(f" FQDN: {credentials.fqdn}")
|
|
30
|
+
print(f" Port: {credentials.port}")
|
|
31
|
+
print(f" Org ID: {credentials.org_id}")
|
|
32
|
+
print(f" API User: {credentials.api_user}")
|
|
33
|
+
print(f" Verify SSL: {credentials.verify_ssl}")
|
|
34
|
+
print(f" Source: {credentials.originating_file}")
|
|
35
|
+
print("-" * 60)
|
|
36
|
+
except pylo.PyloEx as e:
|
|
37
|
+
print(f"ERROR loading credentials: {e}")
|
|
38
|
+
return 1
|
|
39
|
+
|
|
40
|
+
# Example 1: Create an APIConnector
|
|
41
|
+
print("\nExample 1: Creating APIConnector from ENV profile")
|
|
42
|
+
try:
|
|
43
|
+
connector = pylo.APIConnector.create_from_credentials_in_file('ENV')
|
|
44
|
+
print(f"✓ APIConnector created for {connector.fqdn}:{connector.port}")
|
|
45
|
+
|
|
46
|
+
# Test connection by getting software version
|
|
47
|
+
version = connector.get_software_version()
|
|
48
|
+
print(f"✓ Connected successfully! PCE Version: {version}")
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(f"✗ Connection failed: {e}")
|
|
52
|
+
print(" (This is expected if the PCE is not accessible)")
|
|
53
|
+
|
|
54
|
+
# Example 2: Load Organization data
|
|
55
|
+
print("\nExample 2: Loading Organization from ENV profile")
|
|
56
|
+
try:
|
|
57
|
+
org = pylo.Organization.get_from_api_using_credential_file(
|
|
58
|
+
'ENV',
|
|
59
|
+
list_of_objects_to_load=[
|
|
60
|
+
pylo.ObjectTypes.LABEL,
|
|
61
|
+
pylo.ObjectTypes.WORKLOAD
|
|
62
|
+
]
|
|
63
|
+
)
|
|
64
|
+
print(f"✓ Organization loaded successfully!")
|
|
65
|
+
print(f" Labels: {len(org.LabelStore.itemsByHref)}")
|
|
66
|
+
print(f" Workloads: {len(org.WorkloadStore.itemsByHref)}")
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
print(f"✗ Failed to load organization: {e}")
|
|
70
|
+
print(" (This is expected if the PCE is not accessible)")
|
|
71
|
+
|
|
72
|
+
print("\n" + "=" * 60)
|
|
73
|
+
print("Example complete!")
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
if __name__ == '__main__':
|
|
77
|
+
exit(main())
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
In this example we will show how to query traffic logs from the PCE using the Explorer V2 APIs.
|
|
3
|
+
We will make a query for all traffic logs matching the following conditions:
|
|
4
|
+
- Source (consumer) has a label 'E-PRODUCTION' or 'E-PREPROD' or is part of IPList 'I-Prod-Networks'
|
|
5
|
+
- Destination (provider) can be any workload or IP
|
|
6
|
+
- Service is TCP port 80 or 443 or ICMP protocol
|
|
7
|
+
- Policy decision is 'allowed'
|
|
8
|
+
- Traffic was detected within the last 5 days
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) # only required if you run this script from the examples folder without installing pylo
|
|
15
|
+
import illumio_pylo as pylo
|
|
16
|
+
|
|
17
|
+
# PCE connection parameters
|
|
18
|
+
pce_hostname = 'pce1.company.com'
|
|
19
|
+
pce_port = 9443
|
|
20
|
+
pce_api_user = 'api_xxxxxxxxx'
|
|
21
|
+
pce_api_key = 'xxxxxxxxxxxxxxxxxxxx'
|
|
22
|
+
pce_org_id = 1
|
|
23
|
+
pce_verify_ssl = True
|
|
24
|
+
|
|
25
|
+
print("Loading organization from PCE '{}'... ".format(pce_hostname), end='', flush=True)
|
|
26
|
+
organization = pylo.get_organization(pce_hostname, pce_port, pce_api_user, pce_api_key, pce_org_id, pce_verify_ssl)
|
|
27
|
+
print("OK!")
|
|
28
|
+
|
|
29
|
+
# Create a new V2 Explorer query with a max of 1500 results
|
|
30
|
+
explorer_query = organization.connector.new_explorer_query_v2(max_results=1500)
|
|
31
|
+
|
|
32
|
+
# Define source filter criteria
|
|
33
|
+
source_labels_names = ['E-PRODUCTION', 'E-PREPROD']
|
|
34
|
+
source_ip_list_name = 'I-Prod-Networks'
|
|
35
|
+
|
|
36
|
+
# Create a source filter that combines labels and IPList (treated with OR logic)
|
|
37
|
+
source_filter = explorer_query.filters.new_source_filter()
|
|
38
|
+
|
|
39
|
+
# Lookup and add Label objects to the source filter
|
|
40
|
+
for label_name in source_labels_names:
|
|
41
|
+
label_search_result = organization.LabelStore.find_label_by_name(label_name, raise_exception_if_not_found=False, case_sensitive=False)
|
|
42
|
+
if len(label_search_result) == 0:
|
|
43
|
+
raise pylo.PyloEx("Label '{}' not found in PCE!".format(label_name))
|
|
44
|
+
elif len(label_search_result) > 1:
|
|
45
|
+
raise pylo.PyloEx("Multiple labels found for name '{}', please use a more specific name!".format(label_name))
|
|
46
|
+
source_filter.add_label(label_search_result[0])
|
|
47
|
+
|
|
48
|
+
# Lookup and add IPList object to the source filter
|
|
49
|
+
source_iplist = organization.IPListStore.find_by_name(source_ip_list_name)
|
|
50
|
+
if source_iplist is None:
|
|
51
|
+
raise pylo.PyloEx("IPList '{}' not found in PCE!".format(source_ip_list_name))
|
|
52
|
+
source_filter.add_iplist(source_iplist)
|
|
53
|
+
|
|
54
|
+
# Filter by services (ICMP, HTTP, HTTPS)
|
|
55
|
+
explorer_query.filters.service_include_add_protocol(1) # ICMP
|
|
56
|
+
explorer_query.filters.service_include_add('tcp/80') # HTTP
|
|
57
|
+
explorer_query.filters.service_include_add('tcp/443') # HTTPS
|
|
58
|
+
|
|
59
|
+
# Filter by policy decision (only allowed traffic)
|
|
60
|
+
explorer_query.filters.filter_on_policy_decision_allowed()
|
|
61
|
+
|
|
62
|
+
# Filter by time range (last 5 days)
|
|
63
|
+
explorer_query.filters.set_time_from_x_days_ago(5)
|
|
64
|
+
|
|
65
|
+
# Execute the query and retrieve traffic logs
|
|
66
|
+
print("Querying PCE for traffic logs matching the filter... ", end='', flush=True)
|
|
67
|
+
traffic_logs = explorer_query.execute()
|
|
68
|
+
records = traffic_logs.get_all_records()
|
|
69
|
+
print("OK! Found {} traffic log(s)".format(len(records)))
|
|
70
|
+
|
|
71
|
+
# Print the results
|
|
72
|
+
for record in records:
|
|
73
|
+
# Format source information
|
|
74
|
+
if record.source_is_workload():
|
|
75
|
+
workload = record.get_source_workload(organization)
|
|
76
|
+
# Get labels as a formatted string
|
|
77
|
+
label_values = [record.source_workload_labels_by_type.get(lt) for lt in organization.LabelStore.label_types]
|
|
78
|
+
label_values = [lv for lv in label_values if lv is not None]
|
|
79
|
+
labels_str = '|'.join(label_values) if label_values else 'unlabeled'
|
|
80
|
+
consumer_text = "Workload '{}' ({})".format(workload.name if workload else record.source_workload_hostname, labels_str)
|
|
81
|
+
else:
|
|
82
|
+
consumer_text = "IP '{}'".format(record.source_ip)
|
|
83
|
+
|
|
84
|
+
# Format destination information
|
|
85
|
+
if record.destination_is_workload():
|
|
86
|
+
workload = record.get_destination_workload(organization)
|
|
87
|
+
# Get labels as a formatted string
|
|
88
|
+
label_values = [record.destination_workload_labels_by_type.get(lt) for lt in organization.LabelStore.label_types]
|
|
89
|
+
label_values = [lv for lv in label_values if lv is not None]
|
|
90
|
+
labels_str = '|'.join(label_values) if label_values else 'unlabeled'
|
|
91
|
+
provider_text = "Workload '{}' ({})".format(workload.name if workload else record.destination_workload_hostname, labels_str)
|
|
92
|
+
else:
|
|
93
|
+
provider_text = "IP '{}'".format(record.destination_ip)
|
|
94
|
+
|
|
95
|
+
# Format service information
|
|
96
|
+
service_text = record.service_to_str()
|
|
97
|
+
|
|
98
|
+
# Print the traffic log entry
|
|
99
|
+
print("Traffic log: {} -> {} via {} (policy decision: {})".format(
|
|
100
|
+
consumer_text, provider_text, service_text, record.policy_decision_string))
|
|
101
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Using the Filter Query feature to search workloads
|
|
3
|
+
|
|
4
|
+
This example demonstrates how to use the find_workloads_matching_query() method
|
|
5
|
+
to filter workloads using a SQL-like query syntax.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
12
|
+
|
|
13
|
+
import illumio_pylo as pylo
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
# Load organization from PCE (using credentials file or cached data)
|
|
18
|
+
# Replace 'your-pce-hostname' with your actual PCE hostname
|
|
19
|
+
pce_hostname = 'your-pce-hostname'
|
|
20
|
+
|
|
21
|
+
print(f"Loading PCE configuration from {pce_hostname}...")
|
|
22
|
+
org = pylo.Organization(1)
|
|
23
|
+
|
|
24
|
+
# Option 1: Load from API using saved credentials
|
|
25
|
+
# org.load_from_api_using_credential_file(pce_hostname)
|
|
26
|
+
|
|
27
|
+
# Option 2: Load from cache (if you've previously cached the data)
|
|
28
|
+
try:
|
|
29
|
+
org.load_from_cache_or_saved_credentials(pce_hostname)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"Failed to load PCE data: {e}")
|
|
32
|
+
print("\nTo use this example, you need to either:")
|
|
33
|
+
print(" 1. Configure credentials in ~/.pylo/credentials.json")
|
|
34
|
+
print(" 2. Have cached PCE data available")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
print(f"Loaded {len(org.WorkloadStore.workloads)} workloads from PCE\n")
|
|
38
|
+
|
|
39
|
+
# Example queries
|
|
40
|
+
example_queries = [
|
|
41
|
+
# Find workloads by name
|
|
42
|
+
"name == 'web-server-01'",
|
|
43
|
+
|
|
44
|
+
# Find workloads by partial name match
|
|
45
|
+
"name contains 'web'",
|
|
46
|
+
|
|
47
|
+
# Find workloads by name using regex
|
|
48
|
+
"name matches 'web-.*-[0-9]+'",
|
|
49
|
+
|
|
50
|
+
# Find workloads by IP address
|
|
51
|
+
"ip_address == '192.168.1.100'",
|
|
52
|
+
|
|
53
|
+
# Find online workloads
|
|
54
|
+
"online == true",
|
|
55
|
+
|
|
56
|
+
# Find workloads by label
|
|
57
|
+
"env == 'Production'",
|
|
58
|
+
"label.app == 'WebApp' and label.env == 'Production'",
|
|
59
|
+
|
|
60
|
+
# Complex query with OR
|
|
61
|
+
"(name == 'server-01' or name == 'server-02') and online == true",
|
|
62
|
+
|
|
63
|
+
# Find workloads with old heartbeat
|
|
64
|
+
"last_heartbeat <= '2024-01-01'",
|
|
65
|
+
|
|
66
|
+
# Find workloads in a specific mode
|
|
67
|
+
"mode == 'enforced'",
|
|
68
|
+
|
|
69
|
+
# Find deleted workloads (need to pass include_deleted=True)
|
|
70
|
+
"deleted == true",
|
|
71
|
+
|
|
72
|
+
# Combine multiple conditions
|
|
73
|
+
"hostname contains 'prod' and env == 'Production' and not deleted == true",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
print("=" * 60)
|
|
77
|
+
print("Filter Query Examples")
|
|
78
|
+
print("=" * 60)
|
|
79
|
+
|
|
80
|
+
for query in example_queries:
|
|
81
|
+
print(f"\nQuery: {query}")
|
|
82
|
+
try:
|
|
83
|
+
results = org.WorkloadStore.find_workloads_matching_query(query)
|
|
84
|
+
print(f"Found {len(results)} workload(s)")
|
|
85
|
+
for wkl in results[:5]: # Show first 5 results
|
|
86
|
+
print(f" - {wkl.get_name()} ({wkl.href})")
|
|
87
|
+
if len(results) > 5:
|
|
88
|
+
print(f" ... and {len(results) - 5} more")
|
|
89
|
+
except pylo.PyloEx as e:
|
|
90
|
+
print(f"Error: {e}")
|
|
91
|
+
|
|
92
|
+
# Interactive query mode
|
|
93
|
+
print("\n" + "=" * 60)
|
|
94
|
+
print("Interactive Query Mode")
|
|
95
|
+
print("Enter a query to search workloads, or 'quit' to exit")
|
|
96
|
+
print("=" * 60)
|
|
97
|
+
|
|
98
|
+
while True:
|
|
99
|
+
try:
|
|
100
|
+
query = input("\nQuery> ").strip()
|
|
101
|
+
if query.lower() in ('quit', 'exit', 'q'):
|
|
102
|
+
break
|
|
103
|
+
if not query:
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
results = org.WorkloadStore.find_workloads_matching_query(query)
|
|
107
|
+
print(f"Found {len(results)} workload(s)")
|
|
108
|
+
for wkl in results[:10]:
|
|
109
|
+
labels = wkl.get_labels_str()
|
|
110
|
+
print(f" - {wkl.get_name()} | {labels} | online={wkl.online}")
|
|
111
|
+
if len(results) > 10:
|
|
112
|
+
print(f" ... and {len(results) - 10} more")
|
|
113
|
+
except pylo.PyloEx as e:
|
|
114
|
+
print(f"Query error: {e}")
|
|
115
|
+
except KeyboardInterrupt:
|
|
116
|
+
break
|
|
117
|
+
|
|
118
|
+
print("\nGoodbye!")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == '__main__':
|
|
122
|
+
main()
|
|
@@ -28,10 +28,6 @@ from typing import Union, Dict, Any, List, Optional, Literal
|
|
|
28
28
|
|
|
29
29
|
requests.packages.urllib3.disable_warnings()
|
|
30
30
|
|
|
31
|
-
objects_types_strings = Literal[
|
|
32
|
-
'workloads', 'virtual_services', 'labels', 'labelgroups', 'iplists', 'services',
|
|
33
|
-
'rulesets', 'security_principals', 'label_dimensions']
|
|
34
|
-
|
|
35
31
|
default_retry_count_if_api_call_limit_reached = 3
|
|
36
32
|
default_retry_wait_time_if_api_call_limit_reached = 10
|
|
37
33
|
default_max_objects_for_sync_calls = 200000
|
|
@@ -49,7 +45,7 @@ def get_field_or_die(field_name: str, data):
|
|
|
49
45
|
|
|
50
46
|
|
|
51
47
|
ObjectTypes = Literal['iplists', 'workloads', 'virtual_services', 'labels', 'labelgroups', 'services', 'rulesets',
|
|
52
|
-
|
|
48
|
+
'security_principals', 'label_dimensions']
|
|
53
49
|
|
|
54
50
|
all_object_types: Dict[ObjectTypes, ObjectTypes] = {
|
|
55
51
|
'iplists': 'iplists',
|
|
@@ -316,7 +312,7 @@ class APIConnector:
|
|
|
316
312
|
or\
|
|
317
313
|
method == 'DELETE' and req.status_code != 204 \
|
|
318
314
|
or \
|
|
319
|
-
method == 'PUT' and req.status_code != 204 and req.status_code != 200:
|
|
315
|
+
method == 'PUT' and req.status_code != 204 and req.status_code != 202 and req.status_code != 200:
|
|
320
316
|
|
|
321
317
|
if req.status_code == 429:
|
|
322
318
|
# too many requests sent in short amount of time? [{"token":"too_many_requests_error", ....}]
|
|
@@ -371,7 +367,7 @@ class APIConnector:
|
|
|
371
367
|
self.collect_pce_infos()
|
|
372
368
|
return self.version_string
|
|
373
369
|
|
|
374
|
-
def get_objects_count_by_type(self, object_type:
|
|
370
|
+
def get_objects_count_by_type(self, object_type: ObjectTypes) -> int:
|
|
375
371
|
|
|
376
372
|
def extract_count(headers):
|
|
377
373
|
count = headers.get('x-total-count')
|
|
@@ -1323,12 +1319,23 @@ class APIConnector:
|
|
|
1323
1319
|
|
|
1324
1320
|
raise pylo.PyloObjectNotFound("Request with ID {} not found".format(request_href))
|
|
1325
1321
|
|
|
1326
|
-
def explorer_search(self, filters: Union[Dict, 'pylo.ExplorerFilterSetV1'],
|
|
1327
|
-
max_running_time_seconds=1800,
|
|
1328
|
-
|
|
1322
|
+
def explorer_search(self, filters: Union[Dict, 'pylo.ExplorerFilterSetV1', 'pylo.ExplorerFilterSetV2'],
|
|
1323
|
+
max_running_time_seconds=1800,check_for_update_interval_seconds=10, draft_mode_enabled=False)\
|
|
1324
|
+
-> Union['pylo.ExplorerResultSetV1', 'pylo.ExplorerResultSetV2']:
|
|
1325
|
+
"""
|
|
1326
|
+
|
|
1327
|
+
:param filters:
|
|
1328
|
+
:param max_running_time_seconds:
|
|
1329
|
+
:param check_for_update_interval_seconds:
|
|
1330
|
+
:param draft_mode_enabled: only for V2 filters
|
|
1331
|
+
:return:
|
|
1332
|
+
"""
|
|
1333
|
+
|
|
1329
1334
|
path = "/traffic_flows/async_queries"
|
|
1330
1335
|
if isinstance(filters, pylo.ExplorerFilterSetV1):
|
|
1331
1336
|
data = filters.generate_json_query()
|
|
1337
|
+
elif isinstance(filters, pylo.ExplorerFilterSetV2):
|
|
1338
|
+
data = filters.generate_json_query()
|
|
1332
1339
|
else:
|
|
1333
1340
|
data = filters
|
|
1334
1341
|
|
|
@@ -1373,11 +1380,45 @@ class APIConnector:
|
|
|
1373
1380
|
if query_status is None:
|
|
1374
1381
|
raise pylo.PyloEx("Unexpected logic where query_status is None", query_queued_json_response)
|
|
1375
1382
|
|
|
1376
|
-
|
|
1383
|
+
# if draft mode is not enabled we can download the results and pass them over
|
|
1384
|
+
if not draft_mode_enabled or isinstance(filters, pylo.ExplorerFilterSetV1):
|
|
1385
|
+
query_json_response = self.do_get_call(query_href + "/download", json_output_expected=True, include_org_id=False)
|
|
1386
|
+
|
|
1387
|
+
if isinstance(filters, pylo.ExplorerFilterSetV1):
|
|
1388
|
+
result = pylo.ExplorerResultSetV1(query_json_response,
|
|
1389
|
+
owner=self,
|
|
1390
|
+
emulated_process_exclusion=filters.exclude_processes_emulate)
|
|
1391
|
+
else:
|
|
1392
|
+
result = pylo.ExplorerResultSetV2(query_json_response)
|
|
1393
|
+
|
|
1394
|
+
return result
|
|
1395
|
+
|
|
1396
|
+
# from here we are in draft mode with V2 filters so we must request API to calculate the draft results
|
|
1397
|
+
draft_mode_trigger_url = query_href + "/update_rules?label_based_rules=false&offset=0&limit=250000"
|
|
1398
|
+
draft_mode_trigger_response = self.do_put_call(draft_mode_trigger_url, json_output_expected=False, include_org_id=False)
|
|
1399
|
+
|
|
1400
|
+
time.sleep(5) # wait a bit before checking for results
|
|
1377
1401
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1402
|
+
while True:
|
|
1403
|
+
# check that we don't wait too long
|
|
1404
|
+
if time.time() - start_time > max_running_time_seconds:
|
|
1405
|
+
raise pylo.PyloApiEx("Timeout while waiting for draft mode results to be calculated", draft_mode_trigger_response)
|
|
1406
|
+
|
|
1407
|
+
draft_mode_status_response = self.explorer_async_query_get_specific_request_status(query_href)
|
|
1408
|
+
if draft_mode_status_response['rules'] == "completed":
|
|
1409
|
+
query_status = draft_mode_status_response
|
|
1410
|
+
break
|
|
1411
|
+
|
|
1412
|
+
if draft_mode_status_response['rules'] not in ["queued", "working"]:
|
|
1413
|
+
raise pylo.PyloApiEx("Draft mode results calculation failed with status {}".format(draft_mode_status_response['status']),
|
|
1414
|
+
draft_mode_status_response)
|
|
1415
|
+
|
|
1416
|
+
time.sleep(check_for_update_interval_seconds)
|
|
1417
|
+
|
|
1418
|
+
if query_status is None:
|
|
1419
|
+
raise pylo.PyloEx("Unexpected logic where query_status is None", query_queued_json_response)
|
|
1420
|
+
query_json_response = self.do_get_call(query_href + "/download", json_output_expected=True, include_org_id=False)
|
|
1421
|
+
result = pylo.ExplorerResultSetV2(query_json_response)
|
|
1381
1422
|
|
|
1382
1423
|
return result
|
|
1383
1424
|
|
|
@@ -1407,6 +1448,12 @@ class APIConnector:
|
|
|
1407
1448
|
check_for_update_interval_seconds: int = 10) -> 'pylo.ExplorerQuery':
|
|
1408
1449
|
return pylo.ExplorerQuery(self, max_results, max_running_time_seconds, check_for_update_interval_seconds)
|
|
1409
1450
|
|
|
1451
|
+
def new_explorer_query_v2(self, max_results: int = 2500, draft_mode_enabled=False, max_running_time_seconds: int = 1800,
|
|
1452
|
+
check_for_update_interval_seconds: int = 10) -> 'pylo.ExplorerQueryV2':
|
|
1453
|
+
return pylo.ExplorerQueryV2(self, max_results=max_results, draft_mode_enabled=draft_mode_enabled,
|
|
1454
|
+
max_running_time_seconds=max_running_time_seconds,
|
|
1455
|
+
check_for_update_interval_seconds=check_for_update_interval_seconds)
|
|
1456
|
+
|
|
1410
1457
|
def new_audit_log_query(self, max_results: int = 10000, max_running_time_seconds: int = 1800,
|
|
1411
1458
|
check_for_update_interval_seconds: int = 10) -> 'pylo.AuditLogQuery':
|
|
1412
1459
|
return pylo.AuditLogQuery(self, max_results, max_running_time_seconds)
|