zscaler-sdk-python 0.5.2__tar.gz → 0.6.1__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.
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/PKG-INFO +2 -2
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/pyproject.toml +2 -2
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/__init__.py +1 -1
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/ratelimiter/ratelimiter.py +23 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/utils.py +223 -21
- zscaler_sdk_python-0.6.1/zscaler/zdx/__init__.py +78 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/admin.py +58 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/alerts.py +121 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/apps.py +180 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/devices.py +448 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/filters.py +59 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/inventory.py +71 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/troubleshooting.py +124 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/users.py +60 -0
- zscaler_sdk_python-0.6.1/zscaler/zdx/zdx_client.py +374 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/isolation_profile.py +1 -2
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/locations.py +60 -8
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/client.py +1 -1
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/README.md +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/cache/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/cache/cache.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/cache/no_op_cache.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/cache/zscaler_cache.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/constants.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/errors/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/errors/error.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/errors/http_error.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/errors/zscaler_api_error.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/exceptions/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/exceptions/exceptions.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/logger.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/ratelimiter/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/user_agent.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/activate.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/apptotal.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/audit_logs.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/authentication_settings.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/client.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/cloud_apps.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/device_management.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/dlp.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/errors.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/firewall.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/forwarding_control.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/labels.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/sandbox.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/security.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/ssl_inspection.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/traffic.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/url_categories.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/url_filtering.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/users.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/web_dlp.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/workload_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zia/zpa_gateway.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/README.md +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/__init__.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/app_segments.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/app_segments_inspection.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/app_segments_pra.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/authdomains.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/certificates.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/cloud_connector_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/connectors.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/emergency_access.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/errors.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/idp.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/inspection.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/isolation.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/lss.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/machine_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/microtenants.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/policies.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/posture_profiles.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/privileged_remote_access.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/provisioning.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/saml_attributes.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/scim_attributes.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/scim_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/segment_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/server_groups.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/servers.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/service_edges.py +0 -0
- {zscaler_sdk_python-0.5.2 → zscaler_sdk_python-0.6.1}/zscaler/zpa/trusted_networks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zscaler-sdk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Official Python SDK for the Zscaler Products (Beta)
|
|
5
5
|
Home-page: https://github.com/zscaler/zscaler-sdk-python
|
|
6
6
|
License: MIT
|
|
@@ -37,7 +37,7 @@ Requires-Dist: python-box (>=7.2.0,<8.0.0)
|
|
|
37
37
|
Requires-Dist: python-dateutil
|
|
38
38
|
Requires-Dist: pytz
|
|
39
39
|
Requires-Dist: pyyaml
|
|
40
|
-
Requires-Dist: requests (>=2.32.
|
|
40
|
+
Requires-Dist: requests (>=2.32.3)
|
|
41
41
|
Requires-Dist: responses (>=0.25.3)
|
|
42
42
|
Requires-Dist: restfly
|
|
43
43
|
Requires-Dist: six
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "zscaler-sdk-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
4
4
|
description = "Official Python SDK for the Zscaler Products (Beta)"
|
|
5
5
|
authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -35,7 +35,7 @@ charset-normalizer = "*"
|
|
|
35
35
|
idna = "*"
|
|
36
36
|
python-box = "^7.2.0"
|
|
37
37
|
python-dateutil = "*"
|
|
38
|
-
requests = ">=2.32.
|
|
38
|
+
requests = ">=2.32.3"
|
|
39
39
|
responses = ">=0.25.3"
|
|
40
40
|
restfly = "*"
|
|
41
41
|
six = "*"
|
|
@@ -35,3 +35,26 @@ class RateLimiter:
|
|
|
35
35
|
self.post_put_delete_requests.append(now)
|
|
36
36
|
|
|
37
37
|
return False, 0
|
|
38
|
+
|
|
39
|
+
def update_limits(self, headers):
|
|
40
|
+
if "X-Ratelimit-Limit-Second" in headers:
|
|
41
|
+
self.get_limit = int(headers["X-Ratelimit-Limit-Second"])
|
|
42
|
+
self.post_put_delete_limit = int(headers["X-Ratelimit-Limit-Second"])
|
|
43
|
+
if "X-Ratelimit-Reset" in headers:
|
|
44
|
+
self.get_freq = int(headers["X-Ratelimit-Reset"])
|
|
45
|
+
self.post_put_delete_freq = int(headers["X-Ratelimit-Reset"])
|
|
46
|
+
|
|
47
|
+
# Handle minute, hour, and day limits
|
|
48
|
+
if "X-RateLimit-Limit-Minute" in headers:
|
|
49
|
+
self.minute_limit = int(headers["X-RateLimit-Limit-Minute"])
|
|
50
|
+
if "X-RateLimit-Limit-Hour" in headers:
|
|
51
|
+
self.hour_limit = int(headers["X-RateLimit-Limit-Hour"])
|
|
52
|
+
if "X-RateLimit-Limit-Day" in headers:
|
|
53
|
+
self.day_limit = int(headers["X-RateLimit-Limit-Day"])
|
|
54
|
+
|
|
55
|
+
if "X-RateLimit-Remaining-Minute" in headers:
|
|
56
|
+
self.remaining_minute = int(headers["X-RateLimit-Remaining-Minute"])
|
|
57
|
+
if "X-RateLimit-Remaining-Hour" in headers:
|
|
58
|
+
self.remaining_hour = int(headers["X-RateLimit-Remaining-Hour"])
|
|
59
|
+
if "X-RateLimit-Remaining-Day" in headers:
|
|
60
|
+
self.remaining_day = int(headers["X-RateLimit-Remaining-Day"])
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import argparse
|
|
18
18
|
import base64
|
|
19
19
|
import datetime
|
|
20
|
-
import
|
|
20
|
+
import functools
|
|
21
21
|
import json as jsonp
|
|
22
22
|
import logging
|
|
23
23
|
import random
|
|
@@ -147,26 +147,6 @@ def add_id_groups(id_groups: list, kwargs: dict, payload: dict):
|
|
|
147
147
|
return
|
|
148
148
|
|
|
149
149
|
|
|
150
|
-
# def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
151
|
-
# for entry in id_groups:
|
|
152
|
-
# if kwargs.get(entry[0]):
|
|
153
|
-
# # Ensure each ID is treated as an integer before adding it to the payload
|
|
154
|
-
# payload[entry[1]] = [{"id": int(param_id)} for param_id in kwargs.pop(entry[0])]
|
|
155
|
-
# return
|
|
156
|
-
|
|
157
|
-
# ####### Function used in the ZIA Forwarding Control Rule #######
|
|
158
|
-
# def transform_fwd_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
159
|
-
# for entry in id_groups:
|
|
160
|
-
# key, payload_key = entry
|
|
161
|
-
# if key in kwargs:
|
|
162
|
-
# value = kwargs.pop(key)
|
|
163
|
-
# if isinstance(value, dict):
|
|
164
|
-
# payload[payload_key] = {k: v for k, v in value.items() if k in ['id', 'name']}
|
|
165
|
-
# elif isinstance(value, list):
|
|
166
|
-
# payload[payload_key] = [{"id": int(item)} if isinstance(item, str) and item.isdigit() else item for item in value]
|
|
167
|
-
# return
|
|
168
|
-
|
|
169
|
-
|
|
170
150
|
def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
171
151
|
for entry in id_groups:
|
|
172
152
|
key, payload_key = entry
|
|
@@ -343,6 +323,208 @@ class Iterator(APIIterator):
|
|
|
343
323
|
time.sleep(1)
|
|
344
324
|
|
|
345
325
|
|
|
326
|
+
def calculate_epoch(hours: int):
|
|
327
|
+
current_time = int(time.time())
|
|
328
|
+
past_time = int(current_time - (hours * 3600))
|
|
329
|
+
return current_time, past_time
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def zdx_params(func):
|
|
333
|
+
"""
|
|
334
|
+
Decorator to add custom parameter functionality for ZDX API calls.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
func: The function to decorate.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
The decorated function.
|
|
341
|
+
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
@functools.wraps(func)
|
|
345
|
+
def wrapper(self, *args, **kwargs):
|
|
346
|
+
since = kwargs.pop("since", None)
|
|
347
|
+
search = kwargs.pop("search", None)
|
|
348
|
+
location_id = kwargs.pop("location_id", None)
|
|
349
|
+
department_id = kwargs.pop("department_id", None)
|
|
350
|
+
geo_id = kwargs.pop("geo_id", None)
|
|
351
|
+
|
|
352
|
+
if since:
|
|
353
|
+
current_time, past_time = calculate_epoch(since)
|
|
354
|
+
kwargs["to"] = current_time
|
|
355
|
+
kwargs["from"] = past_time
|
|
356
|
+
|
|
357
|
+
kwargs["q"] = search or kwargs.get("q")
|
|
358
|
+
kwargs["loc"] = location_id or kwargs.get("loc")
|
|
359
|
+
kwargs["dept"] = department_id or kwargs.get("dept")
|
|
360
|
+
kwargs["geo"] = geo_id or kwargs.get("geo")
|
|
361
|
+
|
|
362
|
+
return func(self, *args, **kwargs)
|
|
363
|
+
|
|
364
|
+
return wrapper
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class CommonFilters:
|
|
368
|
+
def __init__(self, **kwargs):
|
|
369
|
+
valid_params = {
|
|
370
|
+
"from_time": None,
|
|
371
|
+
"to": None,
|
|
372
|
+
"score_bucket": None,
|
|
373
|
+
"app_id": None,
|
|
374
|
+
"loc": None,
|
|
375
|
+
"exclude_loc": None,
|
|
376
|
+
"dept": None,
|
|
377
|
+
"exclude_dept": None,
|
|
378
|
+
"geo": None,
|
|
379
|
+
"exclude_geo": None,
|
|
380
|
+
"offset": None,
|
|
381
|
+
"limit": None,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
for key, value in kwargs.items():
|
|
385
|
+
if key in valid_params:
|
|
386
|
+
setattr(self, key, value)
|
|
387
|
+
|
|
388
|
+
def to_dict(self):
|
|
389
|
+
return {
|
|
390
|
+
k: v
|
|
391
|
+
for k, v in {
|
|
392
|
+
"from": getattr(self, "from_time", None),
|
|
393
|
+
"to": getattr(self, "to", None),
|
|
394
|
+
"score_bucket": getattr(self, "score_bucket", None),
|
|
395
|
+
"app_id": getattr(self, "app_id", None),
|
|
396
|
+
"loc": getattr(self, "loc", None),
|
|
397
|
+
"exclude_loc": getattr(self, "exclude_loc", None),
|
|
398
|
+
"dept": getattr(self, "dept", None),
|
|
399
|
+
"exclude_dept": getattr(self, "exclude_dept", None),
|
|
400
|
+
"geo": getattr(self, "geo", None),
|
|
401
|
+
"exclude_geo": getattr(self, "exclude_geo", None),
|
|
402
|
+
"offset": getattr(self, "offset", None),
|
|
403
|
+
"limit": getattr(self, "limit", None),
|
|
404
|
+
}.items()
|
|
405
|
+
if v is not None
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class ZDXIterator:
|
|
410
|
+
def __init__(self, client, endpoint, filters=None):
|
|
411
|
+
self.client = client
|
|
412
|
+
self.endpoint = endpoint
|
|
413
|
+
self.filters = {k: v for k, v in (filters or {}).items() if v is not None}
|
|
414
|
+
self.next_offset = None
|
|
415
|
+
self.previous_offset = None
|
|
416
|
+
self.page = []
|
|
417
|
+
self.page_count = 0
|
|
418
|
+
self.total = 0
|
|
419
|
+
self.logger = logging.getLogger("zscaler-sdk-python")
|
|
420
|
+
self._get_page()
|
|
421
|
+
|
|
422
|
+
def __iter__(self):
|
|
423
|
+
return self
|
|
424
|
+
|
|
425
|
+
def __next__(self):
|
|
426
|
+
if self.page_count >= len(self.page):
|
|
427
|
+
if self.next_offset is None:
|
|
428
|
+
raise StopIteration
|
|
429
|
+
if self.next_offset == self.previous_offset:
|
|
430
|
+
self.logger.warning(
|
|
431
|
+
f"Detected repeated next_offset: {self.next_offset}, stopping iteration to avoid infinite loop."
|
|
432
|
+
)
|
|
433
|
+
raise StopIteration
|
|
434
|
+
self.previous_offset = self.next_offset
|
|
435
|
+
self._get_page()
|
|
436
|
+
if not self.page:
|
|
437
|
+
raise StopIteration
|
|
438
|
+
item = self.page[self.page_count]
|
|
439
|
+
self.page_count += 1
|
|
440
|
+
return item
|
|
441
|
+
|
|
442
|
+
def _get_page(self):
|
|
443
|
+
params = {**self.filters, "offset": self.next_offset} if self.next_offset else self.filters
|
|
444
|
+
response = self.client.get(self.endpoint, params=params)
|
|
445
|
+
|
|
446
|
+
self.logger.debug(f"API response: {response}")
|
|
447
|
+
|
|
448
|
+
if response is None:
|
|
449
|
+
self.logger.error(f"Invalid response: {response}")
|
|
450
|
+
raise StopIteration
|
|
451
|
+
|
|
452
|
+
if isinstance(response, list):
|
|
453
|
+
self.page = response
|
|
454
|
+
self.next_offset = None
|
|
455
|
+
elif isinstance(response, dict):
|
|
456
|
+
self.next_offset = response.get("next_offset")
|
|
457
|
+
self.page = (
|
|
458
|
+
response.get("alerts", [])
|
|
459
|
+
or response.get("items", [])
|
|
460
|
+
or response.get("devices", [])
|
|
461
|
+
or response.get("probes", [])
|
|
462
|
+
or response.get("software", [])
|
|
463
|
+
)
|
|
464
|
+
else:
|
|
465
|
+
self.logger.error(f"Unexpected response type: {type(response)}")
|
|
466
|
+
raise StopIteration
|
|
467
|
+
|
|
468
|
+
if not self.page:
|
|
469
|
+
self.next_offset = None
|
|
470
|
+
|
|
471
|
+
self.total += len(self.page)
|
|
472
|
+
self.page_count = 0
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
# class ZDXIterator:
|
|
476
|
+
# def __init__(self, client, endpoint, filters=None):
|
|
477
|
+
# self.client = client
|
|
478
|
+
# self.endpoint = endpoint
|
|
479
|
+
# self.filters = {k: v for k, v in (filters or {}).items() if v is not None}
|
|
480
|
+
# self.next_offset = None
|
|
481
|
+
# self.page = []
|
|
482
|
+
# self.page_count = 0
|
|
483
|
+
# self.total = 0
|
|
484
|
+
# self.logger = logging.getLogger("zscaler-sdk-python")
|
|
485
|
+
# self._get_page()
|
|
486
|
+
|
|
487
|
+
# def __iter__(self):
|
|
488
|
+
# return self
|
|
489
|
+
|
|
490
|
+
# def __next__(self):
|
|
491
|
+
# if self.page_count >= len(self.page):
|
|
492
|
+
# if self.next_offset is None:
|
|
493
|
+
# raise StopIteration
|
|
494
|
+
# self._get_page()
|
|
495
|
+
# if not self.page:
|
|
496
|
+
# raise StopIteration
|
|
497
|
+
# item = self.page[self.page_count]
|
|
498
|
+
# self.page_count += 1
|
|
499
|
+
# return item
|
|
500
|
+
|
|
501
|
+
# def _get_page(self):
|
|
502
|
+
# params = {**self.filters, "offset": self.next_offset} if self.next_offset else self.filters
|
|
503
|
+
# response = self.client.get(self.endpoint, params=params)
|
|
504
|
+
|
|
505
|
+
# self.logger.debug(f"API response: {response}")
|
|
506
|
+
|
|
507
|
+
# if response is None:
|
|
508
|
+
# self.logger.error(f"Invalid response: {response}")
|
|
509
|
+
# raise StopIteration
|
|
510
|
+
|
|
511
|
+
# if isinstance(response, list):
|
|
512
|
+
# self.page = response
|
|
513
|
+
# self.next_offset = None
|
|
514
|
+
# elif isinstance(response, dict):
|
|
515
|
+
# self.next_offset = response.get("next_offset")
|
|
516
|
+
# self.page = response.get("devices", []) or response.get("items", []) or response.get("probes", [])
|
|
517
|
+
# else:
|
|
518
|
+
# self.logger.error(f"Unexpected response type: {type(response)}")
|
|
519
|
+
# raise StopIteration
|
|
520
|
+
|
|
521
|
+
# if not self.page:
|
|
522
|
+
# self.next_offset = None
|
|
523
|
+
|
|
524
|
+
# self.total += len(self.page)
|
|
525
|
+
# self.page_count = 0
|
|
526
|
+
|
|
527
|
+
|
|
346
528
|
def remove_cloud_suffix(str_name: str) -> str:
|
|
347
529
|
"""
|
|
348
530
|
Removes appended cloud name (e.g. "(zscalerthree.net)") from the string.
|
|
@@ -516,6 +698,26 @@ def validate_and_convert_times(start_time_str, end_time_str, time_zone_str):
|
|
|
516
698
|
return start_epoch, end_epoch
|
|
517
699
|
|
|
518
700
|
|
|
701
|
+
# Maps ZCC numeric os_type and registration_type arguments to a human-readable string
|
|
702
|
+
zcc_param_map = {
|
|
703
|
+
"os": {
|
|
704
|
+
"ios": 1,
|
|
705
|
+
"android": 2,
|
|
706
|
+
"windows": 3,
|
|
707
|
+
"macos": 4,
|
|
708
|
+
"linux": 5,
|
|
709
|
+
},
|
|
710
|
+
"reg_type": {
|
|
711
|
+
"all": 0,
|
|
712
|
+
"registered": 1,
|
|
713
|
+
"removal_pending": 3,
|
|
714
|
+
"unregistered": 4,
|
|
715
|
+
"removed": 5,
|
|
716
|
+
"quarantined": 6,
|
|
717
|
+
},
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
|
|
519
721
|
def dump_request(logger, url: str, method: str, json, params, headers, request_uuid: str, body=True):
|
|
520
722
|
request_headers_filtered = {key: value for key, value in headers.items() if key != "Authorization"}
|
|
521
723
|
# Log the request details before sending the request
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from zscaler import __version__
|
|
4
|
+
from zscaler.zdx.admin import AdminAPI
|
|
5
|
+
from zscaler.zdx.alerts import AlertsAPI
|
|
6
|
+
from zscaler.zdx.apps import AppsAPI
|
|
7
|
+
from zscaler.zdx.devices import DevicesAPI
|
|
8
|
+
from zscaler.zdx.inventory import InventoryAPI
|
|
9
|
+
from zscaler.zdx.troubleshooting import TroubleshootingAPI
|
|
10
|
+
from zscaler.zdx.users import UsersAPI
|
|
11
|
+
|
|
12
|
+
from zscaler.logger import setup_logging
|
|
13
|
+
from .zdx_client import ZDXClientHelper # Import ZDXClientHelper from zdx_client.py
|
|
14
|
+
|
|
15
|
+
# Setup the logger
|
|
16
|
+
setup_logging(logger_name="zscaler-sdk-python")
|
|
17
|
+
logger = logging.getLogger("zscaler-sdk-python")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ZDX:
|
|
21
|
+
"""
|
|
22
|
+
A Controller to access Endpoints in the Zscaler Digital Experience (ZDX) API.
|
|
23
|
+
|
|
24
|
+
The ZDX object stores the session token and simplifies access to CRUD options within the ZDX Portal.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
client_id (str): The ZDX Client ID generated from the ZDX Portal.
|
|
28
|
+
client_secret (str): The ZDX Client Secret generated from the ZDX Portal.
|
|
29
|
+
cloud (str): The Zscaler cloud for your tenancy, accepted values are below. Defaults to ``zdxcloud``.
|
|
30
|
+
|
|
31
|
+
* ``zdxcloud``
|
|
32
|
+
* ``zdxbeta``
|
|
33
|
+
|
|
34
|
+
override_url (str):
|
|
35
|
+
If supplied, this attribute can be used to override the production URL that is derived
|
|
36
|
+
from supplying the `cloud` attribute. Use this attribute if you have a non-standard tenant URL
|
|
37
|
+
(e.g. internal test instance etc). When using this attribute, there is no need to supply the `cloud`
|
|
38
|
+
attribute. The override URL will be prepended to the API endpoint suffixes. The protocol must be included
|
|
39
|
+
i.e. http:// or https://.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, **kw):
|
|
43
|
+
self.client = ZDXClientHelper(**kw)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def admin(self):
|
|
47
|
+
"""The interface object for the :ref:`ZDX Admin interface <zdx-admin>`."""
|
|
48
|
+
return AdminAPI(self.client)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def alerts(self):
|
|
52
|
+
"""The interface object for the :ref:`ZDX Alerts interface <zdx-alerts>`."""
|
|
53
|
+
return AlertsAPI(self.client)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def apps(self):
|
|
57
|
+
"""The interface object for the :ref:`ZDX Apps interface <zdx-apps>`."""
|
|
58
|
+
return AppsAPI(self.client)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def devices(self):
|
|
62
|
+
"""The interface object for the :ref:`ZDX Devices interface <zdx-devices>`."""
|
|
63
|
+
return DevicesAPI(self.client)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def inventory(self):
|
|
67
|
+
"""The interface object for the :ref:`ZDX Inventory interface <zdx-inventory>`."""
|
|
68
|
+
return InventoryAPI(self.client)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def troubleshooting(self):
|
|
72
|
+
"""The interface object for the :ref:`ZDX Troubleshooting interface <zdx-troubleshooting>`."""
|
|
73
|
+
return TroubleshootingAPI(self.client)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def users(self):
|
|
77
|
+
"""The interface object for the :ref:`ZDX Users interface <zdx-users>`."""
|
|
78
|
+
return UsersAPI(self.client)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from box import BoxList
|
|
2
|
+
from zscaler.utils import zdx_params
|
|
3
|
+
from zscaler.utils import ZDXIterator, CommonFilters
|
|
4
|
+
from zscaler.zdx.zdx_client import ZDXClientHelper
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AdminAPI:
|
|
8
|
+
def __init__(self, client: ZDXClientHelper):
|
|
9
|
+
self.rest = client
|
|
10
|
+
|
|
11
|
+
@zdx_params
|
|
12
|
+
def list_departments(self, filters=None, **kwargs) -> BoxList:
|
|
13
|
+
"""
|
|
14
|
+
Returns a list of departments that are configured within ZDX.
|
|
15
|
+
|
|
16
|
+
Keyword Args:
|
|
17
|
+
since (int): The number of hours to look back for devices.
|
|
18
|
+
search (str): The search string to filter by name or department ID.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
:obj:`BoxList`: The list of departments in ZDX.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
List all departments in ZDX for the past 2 hours:
|
|
25
|
+
|
|
26
|
+
>>> for department in zdx.admin.list_departments():
|
|
27
|
+
... print(department)
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
filters = CommonFilters(**kwargs).to_dict()
|
|
31
|
+
return ZDXIterator(self.rest, "administration/departments", filters)
|
|
32
|
+
|
|
33
|
+
@zdx_params
|
|
34
|
+
def list_locations(self, filters=None, **kwargs) -> BoxList:
|
|
35
|
+
"""
|
|
36
|
+
Returns a list of locations that are configured within ZDX.
|
|
37
|
+
|
|
38
|
+
Keyword Args:
|
|
39
|
+
since (int): The number of hours to look back for devices.
|
|
40
|
+
search (str): The search string to filter by name or location ID.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
:obj:`BoxList`: The list of locations in ZDX.
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
List all locations in ZDX for the past 2 hours:
|
|
47
|
+
|
|
48
|
+
>>> for location in zdx.admin.list_locations():
|
|
49
|
+
... print(location)
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
if filters is None:
|
|
53
|
+
filters = CommonFilters(**kwargs).to_dict()
|
|
54
|
+
else:
|
|
55
|
+
filters.update(kwargs)
|
|
56
|
+
|
|
57
|
+
iterator = ZDXIterator(self.rest, "administration/locations", filters)
|
|
58
|
+
return BoxList(iterator)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from box import BoxList
|
|
2
|
+
|
|
3
|
+
from zscaler.utils import ZDXIterator, CommonFilters
|
|
4
|
+
from zscaler.zdx.zdx_client import ZDXClientHelper
|
|
5
|
+
from zscaler.utils import zdx_params
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AlertsAPI:
|
|
9
|
+
def __init__(self, client: ZDXClientHelper):
|
|
10
|
+
self.rest = client
|
|
11
|
+
|
|
12
|
+
@zdx_params
|
|
13
|
+
def list_ongoing(self, **kwargs) -> BoxList:
|
|
14
|
+
"""
|
|
15
|
+
Returns a list of all ongoing alert rules across an organization in ZDX.
|
|
16
|
+
|
|
17
|
+
Keyword Args:
|
|
18
|
+
since (int): The number of hours to look back for devices.
|
|
19
|
+
department_id (str): The unique ID for the department.
|
|
20
|
+
geo_id (str): The unique ID for the geolocation.
|
|
21
|
+
user_ids (list): List of user IDs.
|
|
22
|
+
device_ids (list): List of device IDs.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
:obj:`BoxList`: The list of software in ZDX.
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
List all ongoing alerts in ZDX for the past 2 hours:
|
|
29
|
+
|
|
30
|
+
>>> for alert in zdx.alerts.list_ongoing():
|
|
31
|
+
... print(alert)
|
|
32
|
+
|
|
33
|
+
List all ongoing alerts in ZDX for the past 24 hours:
|
|
34
|
+
|
|
35
|
+
>>> for alert in zdx.alerts.list_ongoing(since=24):
|
|
36
|
+
... print(alert)
|
|
37
|
+
"""
|
|
38
|
+
filters = CommonFilters(**kwargs).to_dict()
|
|
39
|
+
return ZDXIterator(self.rest, "alerts/ongoing", filters)
|
|
40
|
+
|
|
41
|
+
@zdx_params
|
|
42
|
+
def list_historical(self, **kwargs) -> BoxList:
|
|
43
|
+
"""
|
|
44
|
+
Returns a list of all ongoing alert rules across an organization in ZDX.
|
|
45
|
+
|
|
46
|
+
Keyword Args:
|
|
47
|
+
since (int): The number of hours to look back for devices.
|
|
48
|
+
department_id (str): The unique ID for the department.
|
|
49
|
+
geo_id (str): The unique ID for the geolocation.
|
|
50
|
+
user_ids (list): List of user IDs.
|
|
51
|
+
device_ids (list): List of device IDs.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
:obj:`BoxList`: The list of software in ZDX.
|
|
55
|
+
|
|
56
|
+
Examples:
|
|
57
|
+
List all ongoing alerts in ZDX for the past 2 hours:
|
|
58
|
+
|
|
59
|
+
>>> for alert in zdx.alerts.list_historical():
|
|
60
|
+
... print(alert)
|
|
61
|
+
|
|
62
|
+
List all ongoing alerts in ZDX for the past 24 hours:
|
|
63
|
+
|
|
64
|
+
>>> for alert in zdx.alerts.list_historical(since=24):
|
|
65
|
+
... print(alert)
|
|
66
|
+
"""
|
|
67
|
+
filters = CommonFilters(**kwargs).to_dict()
|
|
68
|
+
return ZDXIterator(self.rest, "alerts/historical", filters)
|
|
69
|
+
|
|
70
|
+
@zdx_params
|
|
71
|
+
def get_alert(self, alert_id: str, **kwargs):
|
|
72
|
+
"""
|
|
73
|
+
Returns a single alert in ZDX.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
alert_id (str): The unique ID for the alert.
|
|
77
|
+
|
|
78
|
+
Keyword Args:
|
|
79
|
+
since (int): The number of hours to look back for devices.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
:obj:`Box`: The ZDX device resource record.
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
Get information for the device with an ID of 123456789.
|
|
86
|
+
>>> device = zdx.alerts.get_alert('123456789')
|
|
87
|
+
|
|
88
|
+
Get information for the device with an ID of 123456789 for the last 24 hours.
|
|
89
|
+
>>> device = zdx.alerts.get_alert('123456789', since=24)
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
return self.rest.get(f"alerts/{alert_id}", params=kwargs)
|
|
93
|
+
|
|
94
|
+
@zdx_params
|
|
95
|
+
def list_affected_devices(self, alert_id, **kwargs) -> BoxList:
|
|
96
|
+
"""
|
|
97
|
+
Returns a list of all all affected devices associated with
|
|
98
|
+
an alert rule in conjunction with provided filters.
|
|
99
|
+
|
|
100
|
+
Keyword Args:
|
|
101
|
+
since (int): The number of hours to look back for devices.
|
|
102
|
+
department_id (str): The unique ID for the department.
|
|
103
|
+
geo_id (str): The unique ID for the geolocation.
|
|
104
|
+
location_groups (list): List of location group IDs.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
:obj:`BoxList`: The list of software in ZDX.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
List all ongoing alerts in ZDX for the past 2 hours:
|
|
111
|
+
|
|
112
|
+
>>> for alert in zdx.alerts.list_affected_devices():
|
|
113
|
+
... print(alert)
|
|
114
|
+
|
|
115
|
+
List all ongoing alerts in ZDX for the past 24 hours:
|
|
116
|
+
|
|
117
|
+
>>> for alert in zdx.alerts.list_affected_devices(since=24):
|
|
118
|
+
... print(alert)
|
|
119
|
+
"""
|
|
120
|
+
filters = CommonFilters(**kwargs).to_dict()
|
|
121
|
+
return ZDXIterator(self.rest, f"alerts/{alert_id}/affected_devices", filters)
|