graylog-api-client 0.1.0__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.
- graylog_api_client/__init__.py +0 -0
- graylog_api_client/data_structures.py +14 -0
- graylog_api_client/exceptions.py +2 -0
- graylog_api_client/graylog_api_client.py +119 -0
- graylog_api_client/rest_adapter.py +98 -0
- graylog_api_client-0.1.0.dist-info/METADATA +28 -0
- graylog_api_client-0.1.0.dist-info/RECORD +8 -0
- graylog_api_client-0.1.0.dist-info/WHEEL +4 -0
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import List, Dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GraylogApiResult:
|
|
5
|
+
def __init__(self, status_code: int, message: str = "", data: List[Dict] = None):
|
|
6
|
+
"""Result returned from low-level RestAdapter
|
|
7
|
+
|
|
8
|
+
:param status_code: Standard HTTP Status code
|
|
9
|
+
:param message: Human readable result
|
|
10
|
+
:param data: Python List of Dictionaries (or maybe just a single Dictionary on error)
|
|
11
|
+
"""
|
|
12
|
+
self.status_code = int(status_code)
|
|
13
|
+
self.message = str(message)
|
|
14
|
+
self.data = data if data else []
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Union, Dict
|
|
3
|
+
from src.graylog_api_client.rest_adapter import RestAdapter
|
|
4
|
+
from src.graylog_api_client.data_structures import GraylogApiResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GraylogAPI:
|
|
8
|
+
def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
|
|
9
|
+
self._rest_adapter = RestAdapter(host=host, api_key=api_key, ssl_verify=ssl_verify, logger=logger)
|
|
10
|
+
# The endpoints grouped like they are in the api-browser
|
|
11
|
+
# /authz Authorization
|
|
12
|
+
def get_auth_grants_overview(self) -> GraylogApiResult:
|
|
13
|
+
overview = self._rest_adapter.get("authz/grants-overview")
|
|
14
|
+
return overview
|
|
15
|
+
|
|
16
|
+
def get_auth_roles(self, parameters: Dict = None) -> GraylogApiResult:
|
|
17
|
+
roles = self._rest_adapter.get("authz/roles", parameters)
|
|
18
|
+
return roles
|
|
19
|
+
|
|
20
|
+
def get_auth_roles_of_user(self, username: str, parameters: Dict = None) -> GraylogApiResult:
|
|
21
|
+
roles = self._rest_adapter.get(f"authz/roles/user/{username}", parameters)
|
|
22
|
+
return roles
|
|
23
|
+
|
|
24
|
+
def get_auth_role_by_id(self, role_id: str) -> GraylogApiResult:
|
|
25
|
+
roles = self._rest_adapter.get(f"authz/roles/{role_id}")
|
|
26
|
+
return roles
|
|
27
|
+
|
|
28
|
+
def get_auth_assignees_of_role(self, role_id: str, parameters: Dict = None) -> GraylogApiResult:
|
|
29
|
+
assignees = self._rest_adapter.get(f"authz/roles/{role_id}/assignees", parameters)
|
|
30
|
+
return assignees
|
|
31
|
+
|
|
32
|
+
# /ca
|
|
33
|
+
def get_ca(self) -> GraylogApiResult:
|
|
34
|
+
result = self._rest_adapter.get("ca")
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
# /certificates
|
|
38
|
+
def get_certificates(self) -> GraylogApiResult:
|
|
39
|
+
result = self._rest_adapter.get("certificates")
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
# /cluster
|
|
43
|
+
def get_cluster(self) -> GraylogApiResult:
|
|
44
|
+
result = self._rest_adapter.get("cluster")
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
def get_processbufferdump(self, node_id: str = "") -> GraylogApiResult:
|
|
48
|
+
result = self._rest_adapter.get(f"cluster/{node_id}/processbufferdump".replace("//", "/"))
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def get_jvminfo(self, node_id: str) -> GraylogApiResult:
|
|
52
|
+
result = self._rest_adapter.get(f"cluster/{node_id}/jvm")
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
# /contentstream
|
|
56
|
+
# /dashboards
|
|
57
|
+
# /datanodes
|
|
58
|
+
# /datanode
|
|
59
|
+
# /api-docs API-Documentation
|
|
60
|
+
# /enterprise
|
|
61
|
+
# /entitylists
|
|
62
|
+
# /entity_scopes
|
|
63
|
+
# /entity_suggestions
|
|
64
|
+
# /events
|
|
65
|
+
# /system/inputs/{inputId}/extractors Extractors
|
|
66
|
+
# /favorites
|
|
67
|
+
# /views/fields FieldTypes
|
|
68
|
+
# /system/indexer Indexer
|
|
69
|
+
# /search/universal/absolute Legacy Message Search
|
|
70
|
+
# /messages
|
|
71
|
+
# /migration
|
|
72
|
+
# /system/pipelines Pipelines
|
|
73
|
+
# /plugins
|
|
74
|
+
# /remote-reindex-migration
|
|
75
|
+
# /roles
|
|
76
|
+
# /views/search Search
|
|
77
|
+
# /search Search Options (Decorators, Export, Functions etc.)
|
|
78
|
+
# /views/searchjobs Searchjobs
|
|
79
|
+
# /sidecars
|
|
80
|
+
# /sidecar
|
|
81
|
+
# /startpage
|
|
82
|
+
# /system/inputs/{inputId}/staticfields Staticfields
|
|
83
|
+
# /streams
|
|
84
|
+
def get_streams(self) -> GraylogApiResult:
|
|
85
|
+
result = self._rest_adapter.get("streams")
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
# /system Main System endpoints
|
|
89
|
+
# /telemetry
|
|
90
|
+
# /token_usage
|
|
91
|
+
# /users
|
|
92
|
+
def get_users(self, parameters: Dict = None) -> GraylogApiResult:
|
|
93
|
+
result = self._rest_adapter.get("users", parameters)
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
def get_users_paginated(self, parameters: Dict = None) -> GraylogApiResult:
|
|
97
|
+
result = self._rest_adapter.get("users/paginated", parameters)
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
def get_user_by_id(self, user_id: str) -> GraylogApiResult:
|
|
101
|
+
result = self._rest_adapter.get(f"users/id/{user_id}")
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
def get_user_by_username(self, username: str) -> GraylogApiResult:
|
|
105
|
+
result = self._rest_adapter.get(f"users/{username}")
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
def get_user_tokens_by_id(self, user_id: str) -> GraylogApiResult:
|
|
109
|
+
result = self._rest_adapter.get(f"users/{user_id}/tokens")
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
# /views Main Views Endpoints
|
|
113
|
+
def get_views(self, parameters: Dict = None) -> GraylogApiResult:
|
|
114
|
+
result = self._rest_adapter.get("views", parameters)
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
def get_view_by_id(self, view_id: str) -> GraylogApiResult:
|
|
118
|
+
result = self._rest_adapter.get(f"views/{view_id}")
|
|
119
|
+
return result
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Dict, Union
|
|
5
|
+
|
|
6
|
+
from src.graylog_api_client.exceptions import GraylogApiException
|
|
7
|
+
from src.graylog_api_client.data_structures import GraylogApiResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RestAdapter:
|
|
11
|
+
def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
|
|
12
|
+
"""Constructor for RestAdapter
|
|
13
|
+
|
|
14
|
+
:param host: The hostname of the Graylog API endpoint plus the protocol. Example: https://graylog.com/api
|
|
15
|
+
:param api_key: An API Key to authenticate with Graylog
|
|
16
|
+
:param ssl_verify: Enables or Disables TLS Certificate verification. For a custom certificate, this value must a path pointing to the certificate, defaults to True
|
|
17
|
+
:param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
|
|
18
|
+
"""
|
|
19
|
+
self._logger = logger or logging.getLogger(__name__)
|
|
20
|
+
self.host = host + "/"
|
|
21
|
+
self._api_key = api_key
|
|
22
|
+
self._ssl_verify = ssl_verify
|
|
23
|
+
self._session = requests.Session()
|
|
24
|
+
self._session.headers.update({"Accept": "application/json"})
|
|
25
|
+
self._session.auth = (self._api_key, "token") # Graylog uses the format Basic Auth: "<API-token>:token"
|
|
26
|
+
if not ssl_verify:
|
|
27
|
+
requests.packages.urllib3.disable_warnings()
|
|
28
|
+
|
|
29
|
+
def _do(self, method: str, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
|
|
30
|
+
"""Run the HTTP Requests.
|
|
31
|
+
|
|
32
|
+
:param method: The HTTP Method that this request will use.
|
|
33
|
+
:param endpoint: The API endpoint that this request goes to. Example: /streams.
|
|
34
|
+
:param parameters: Dictionary to be sent in the query string of the Request.
|
|
35
|
+
:param data: Dictionary to be sent in the body of the Request.
|
|
36
|
+
:raises GraylogApiException: Generic Exception for Errors caused within the Adapter.
|
|
37
|
+
:return: An object containing the Result of the API request.
|
|
38
|
+
"""
|
|
39
|
+
url = self.host + endpoint
|
|
40
|
+
log_line_pre = f"method={method}, url={url}, parameters={parameters}"
|
|
41
|
+
log_line_post = ', '.join((log_line_pre, "success={}, status_code={}, message={}"))
|
|
42
|
+
try:
|
|
43
|
+
self._logger.debug(msg=log_line_pre)
|
|
44
|
+
response = self._session.request(method=method, url=url, verify=self._ssl_verify, params=parameters, json=data)
|
|
45
|
+
except requests.exceptions.RequestException as e:
|
|
46
|
+
self._logger.error(msg=str(e))
|
|
47
|
+
raise GraylogApiException("Invalid API Response") from e
|
|
48
|
+
try:
|
|
49
|
+
data = response.json()
|
|
50
|
+
except (ValueError, requests.exceptions.JSONDecodeError) as e:
|
|
51
|
+
self._logger.error(msg=log_line_post.format(False, None, e))
|
|
52
|
+
raise GraylogApiException("Bad JSON in response") from e
|
|
53
|
+
is_success = 299 >= response.status_code >= 200 # OK
|
|
54
|
+
log_line = log_line_post.format(is_success, response.status_code, response.reason)
|
|
55
|
+
if is_success:
|
|
56
|
+
self._logger.debug(msg=log_line)
|
|
57
|
+
return GraylogApiResult(status_code=response.status_code, message=response.reason, data=data)
|
|
58
|
+
self._logger.error(msg=log_line)
|
|
59
|
+
raise GraylogApiException(f"{response.status_code}: {response.reason}")
|
|
60
|
+
|
|
61
|
+
def get(self, endpoint: str, parameters: Dict = None) -> GraylogApiResult:
|
|
62
|
+
"""Send a GET request to the API.
|
|
63
|
+
|
|
64
|
+
:param endpoint: The API endpoint that this request goes to. Example: /streams.
|
|
65
|
+
:param parameters: Dictionary to be sent in the query string of the Request.
|
|
66
|
+
:return: An object containing the Result of the API request.
|
|
67
|
+
"""
|
|
68
|
+
return self._do(method="GET", endpoint=endpoint, parameters=parameters)
|
|
69
|
+
|
|
70
|
+
def post(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
|
|
71
|
+
"""Send a POST request to the API.
|
|
72
|
+
|
|
73
|
+
:param endpoint: The API endpoint that this request goes to. Example: /streams.
|
|
74
|
+
:param parameters: Dictionary to be sent in the query string of the Request.
|
|
75
|
+
:param data: Dictionary to be sent in the body of the Request.
|
|
76
|
+
:return: An object containing the Result of the API request.
|
|
77
|
+
"""
|
|
78
|
+
return self._do(method="POST", endpoint=endpoint, parameters=parameters, data=data)
|
|
79
|
+
|
|
80
|
+
def delete(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
|
|
81
|
+
"""Send a DELETE request to the API.
|
|
82
|
+
|
|
83
|
+
:param endpoint: The API endpoint that this request goes to. Example: /streams.
|
|
84
|
+
:param parameters: Dictionary to be sent in the query string of the Request.
|
|
85
|
+
:param data: Dictionary to be sent in the body of the Request.
|
|
86
|
+
:return: An object containing the Result of the API request.
|
|
87
|
+
"""
|
|
88
|
+
return self._do(method="DELETE", endpoint=endpoint, parameters=parameters, data=data)
|
|
89
|
+
|
|
90
|
+
def put(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
|
|
91
|
+
"""Send a PUT request to the API.
|
|
92
|
+
|
|
93
|
+
:param endpoint: The API endpoint that this request goes to. Example: /streams.
|
|
94
|
+
:param parameters: Dictionary to be sent in the query string of the Request.
|
|
95
|
+
:param data: Dictionary to be sent in the body of the Request.
|
|
96
|
+
:return: An object containing the Result of the API request.
|
|
97
|
+
"""
|
|
98
|
+
return self._do(method="PUT", endpoint=endpoint, parameters=parameters, data=data)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: graylog-api-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Chrxxxxs
|
|
6
|
+
Author-email: 134285960+Chrxxxxs@users.noreply.github.com
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Requires-Dist: requests (>=2.32.5,<3.0.0)
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
### This is a low level Graylog API Wrapper heavily based on the guide by [pretzel](https://www.pretzellogix.net/2021/12/08/how-to-write-a-python3-sdk-library-module-for-a-json-rest-api/).
|
|
15
|
+
As there are a lot of endpoints not all of them are implemented by now. I started by implementing the GET requests first.
|
|
16
|
+
Also the API responses all follow very different schemas and an implementation of that into Python is probably not worth the effort which is why the responses from this API are so low-level.
|
|
17
|
+
Feel free to add endpoints or schemas in PRs if you need them :)
|
|
18
|
+
#### Last tested Graylog Version is: 6.3.4
|
|
19
|
+
|
|
20
|
+
Planned:
|
|
21
|
+
1. Tests that run against a Graylog instance to check if the Wrapper works after Graylog Updates
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Test Environment:
|
|
25
|
+
1. Install docker and docker-compose
|
|
26
|
+
2. Copy the sample docker-compose file by [Graylog](https://github.com/Graylog2/docker-compose) for the open-core and create a .env file with the needed values
|
|
27
|
+
3. docker-compose up
|
|
28
|
+
4. Log in and create an api-key
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
graylog_api_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
graylog_api_client/data_structures.py,sha256=rLJ0_0t4j3mhk5lRyNkw9cD7Z_hYLHKxPk91YjYreoo,538
|
|
3
|
+
graylog_api_client/exceptions.py,sha256=hUQSv71dOAsI8iohbPeisj7_hXcMQcnbpjQO7IesLIw,49
|
|
4
|
+
graylog_api_client/graylog_api_client.py,sha256=LU5XvVX3-IWP0YNTPViOkItyDk7u9AsbnxV_IqzqNew,4372
|
|
5
|
+
graylog_api_client/rest_adapter.py,sha256=tQJT5q4cfjVKrRcSzsda8D2xjgEnfX_4Kn1bPTZ5SoI,5476
|
|
6
|
+
graylog_api_client-0.1.0.dist-info/METADATA,sha256=X6CCQj1B52pSEnDclA1nPalD_Bj2x0FF05wWccy_dTk,1395
|
|
7
|
+
graylog_api_client-0.1.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
8
|
+
graylog_api_client-0.1.0.dist-info/RECORD,,
|