LinuxMonitor 1.5.6__tar.gz → 1.5.7__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.
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7/LinuxMonitor.egg-info}/PKG-INFO +44 -2
- {linuxmonitor-1.5.6/LinuxMonitor.egg-info → linuxmonitor-1.5.7}/PKG-INFO +44 -2
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/README.md +29 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/linuxmonitor/__main__.py +2 -1
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/linuxmonitor/linuxmonitor.py +157 -30
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/setup.py +1 -1
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/LICENSE.md +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/LinuxMonitor.egg-info/SOURCES.txt +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/LinuxMonitor.egg-info/dependency_links.txt +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/LinuxMonitor.egg-info/requires.txt +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/LinuxMonitor.egg-info/top_level.txt +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/linuxmonitor/__init__.py +0 -0
- {linuxmonitor-1.5.6 → linuxmonitor-1.5.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: LinuxMonitor
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.7
|
|
4
4
|
Summary: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
|
|
5
5
|
Home-page: https://github.com/QuentinCG/Linux-Monitor-Python-Library
|
|
6
6
|
Author: Quentin Comte-Gaz
|
|
@@ -25,6 +25,19 @@ License-File: LICENSE.md
|
|
|
25
25
|
Requires-Dist: psutil
|
|
26
26
|
Requires-Dist: typing
|
|
27
27
|
Requires-Dist: asyncio
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: platform
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
28
41
|
|
|
29
42
|
# Linux Monitor (Python library)
|
|
30
43
|
[](https://pypi.org/project/LinuxMonitor/) [](https://github.com/QuentinCG/Linux-Monitor-Python-Library/blob/master/LICENSE.md) [](https://paypal.me/QuentinCG) [](https://pepy.tech/project/LinuxMonitor) [](https://pepy.tech/project/LinuxMonitor)
|
|
@@ -162,10 +175,39 @@ python3 -m linuxmonitor --list_commands --config_file config-example.json --node
|
|
|
162
175
|
# Execute a custom commands
|
|
163
176
|
python3 -m linuxmonitor --execute_command CUSTOM_COMMAND_HERE --config_file config-example.json --nodebug
|
|
164
177
|
|
|
178
|
+
# Execute a custom command with parameters (only for commands with "accept_parameters": true or a {arg1}/{args} placeholder)
|
|
179
|
+
python3 -m linuxmonitor --execute_command unban_ip --parameters "1.2.3.4" --config_file config-example.json --nodebug
|
|
180
|
+
|
|
165
181
|
# Execute all custom commands
|
|
166
182
|
python3 -m linuxmonitor --execute_all_commands --config_file config-example.json --nodebug
|
|
167
183
|
```
|
|
168
184
|
|
|
185
|
+
## Custom commands with parameters
|
|
186
|
+
|
|
187
|
+
A custom command (in the `commands` section of the config file) can receive parameters at execution time.
|
|
188
|
+
This is opt-in per command: set `"accept_parameters": true` and/or use a placeholder in the `command` string.
|
|
189
|
+
|
|
190
|
+
Placeholders inside the `command` string:
|
|
191
|
+
- `{arg1}`, `{arg2}`, ... : replaced by the Nth provided parameter
|
|
192
|
+
- `{args}` : replaced by all provided parameters
|
|
193
|
+
- no placeholder : all parameters are appended at the end of the command
|
|
194
|
+
|
|
195
|
+
Every parameter is shell-quoted before substitution, so it is always treated as a single literal
|
|
196
|
+
argument. This prevents command injection through the parameters.
|
|
197
|
+
|
|
198
|
+
Example config entry:
|
|
199
|
+
```json
|
|
200
|
+
"unban_ip": {
|
|
201
|
+
"display_name": "Unban an IP from Fail2Ban",
|
|
202
|
+
"command": "sudo fail2ban-client unban {arg1}",
|
|
203
|
+
"is_private": true,
|
|
204
|
+
"accept_parameters": true,
|
|
205
|
+
"show_content_if_success": true,
|
|
206
|
+
"show_content_if_issue": true,
|
|
207
|
+
"timeout_in_sec": 60
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
169
211
|
## How to use in python script
|
|
170
212
|
|
|
171
213
|
Example of python script using this library:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: LinuxMonitor
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.7
|
|
4
4
|
Summary: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
|
|
5
5
|
Home-page: https://github.com/QuentinCG/Linux-Monitor-Python-Library
|
|
6
6
|
Author: Quentin Comte-Gaz
|
|
@@ -25,6 +25,19 @@ License-File: LICENSE.md
|
|
|
25
25
|
Requires-Dist: psutil
|
|
26
26
|
Requires-Dist: typing
|
|
27
27
|
Requires-Dist: asyncio
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: platform
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
28
41
|
|
|
29
42
|
# Linux Monitor (Python library)
|
|
30
43
|
[](https://pypi.org/project/LinuxMonitor/) [](https://github.com/QuentinCG/Linux-Monitor-Python-Library/blob/master/LICENSE.md) [](https://paypal.me/QuentinCG) [](https://pepy.tech/project/LinuxMonitor) [](https://pepy.tech/project/LinuxMonitor)
|
|
@@ -162,10 +175,39 @@ python3 -m linuxmonitor --list_commands --config_file config-example.json --node
|
|
|
162
175
|
# Execute a custom commands
|
|
163
176
|
python3 -m linuxmonitor --execute_command CUSTOM_COMMAND_HERE --config_file config-example.json --nodebug
|
|
164
177
|
|
|
178
|
+
# Execute a custom command with parameters (only for commands with "accept_parameters": true or a {arg1}/{args} placeholder)
|
|
179
|
+
python3 -m linuxmonitor --execute_command unban_ip --parameters "1.2.3.4" --config_file config-example.json --nodebug
|
|
180
|
+
|
|
165
181
|
# Execute all custom commands
|
|
166
182
|
python3 -m linuxmonitor --execute_all_commands --config_file config-example.json --nodebug
|
|
167
183
|
```
|
|
168
184
|
|
|
185
|
+
## Custom commands with parameters
|
|
186
|
+
|
|
187
|
+
A custom command (in the `commands` section of the config file) can receive parameters at execution time.
|
|
188
|
+
This is opt-in per command: set `"accept_parameters": true` and/or use a placeholder in the `command` string.
|
|
189
|
+
|
|
190
|
+
Placeholders inside the `command` string:
|
|
191
|
+
- `{arg1}`, `{arg2}`, ... : replaced by the Nth provided parameter
|
|
192
|
+
- `{args}` : replaced by all provided parameters
|
|
193
|
+
- no placeholder : all parameters are appended at the end of the command
|
|
194
|
+
|
|
195
|
+
Every parameter is shell-quoted before substitution, so it is always treated as a single literal
|
|
196
|
+
argument. This prevents command injection through the parameters.
|
|
197
|
+
|
|
198
|
+
Example config entry:
|
|
199
|
+
```json
|
|
200
|
+
"unban_ip": {
|
|
201
|
+
"display_name": "Unban an IP from Fail2Ban",
|
|
202
|
+
"command": "sudo fail2ban-client unban {arg1}",
|
|
203
|
+
"is_private": true,
|
|
204
|
+
"accept_parameters": true,
|
|
205
|
+
"show_content_if_success": true,
|
|
206
|
+
"show_content_if_issue": true,
|
|
207
|
+
"timeout_in_sec": 60
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
169
211
|
## How to use in python script
|
|
170
212
|
|
|
171
213
|
Example of python script using this library:
|
|
@@ -134,10 +134,39 @@ python3 -m linuxmonitor --list_commands --config_file config-example.json --node
|
|
|
134
134
|
# Execute a custom commands
|
|
135
135
|
python3 -m linuxmonitor --execute_command CUSTOM_COMMAND_HERE --config_file config-example.json --nodebug
|
|
136
136
|
|
|
137
|
+
# Execute a custom command with parameters (only for commands with "accept_parameters": true or a {arg1}/{args} placeholder)
|
|
138
|
+
python3 -m linuxmonitor --execute_command unban_ip --parameters "1.2.3.4" --config_file config-example.json --nodebug
|
|
139
|
+
|
|
137
140
|
# Execute all custom commands
|
|
138
141
|
python3 -m linuxmonitor --execute_all_commands --config_file config-example.json --nodebug
|
|
139
142
|
```
|
|
140
143
|
|
|
144
|
+
## Custom commands with parameters
|
|
145
|
+
|
|
146
|
+
A custom command (in the `commands` section of the config file) can receive parameters at execution time.
|
|
147
|
+
This is opt-in per command: set `"accept_parameters": true` and/or use a placeholder in the `command` string.
|
|
148
|
+
|
|
149
|
+
Placeholders inside the `command` string:
|
|
150
|
+
- `{arg1}`, `{arg2}`, ... : replaced by the Nth provided parameter
|
|
151
|
+
- `{args}` : replaced by all provided parameters
|
|
152
|
+
- no placeholder : all parameters are appended at the end of the command
|
|
153
|
+
|
|
154
|
+
Every parameter is shell-quoted before substitution, so it is always treated as a single literal
|
|
155
|
+
argument. This prevents command injection through the parameters.
|
|
156
|
+
|
|
157
|
+
Example config entry:
|
|
158
|
+
```json
|
|
159
|
+
"unban_ip": {
|
|
160
|
+
"display_name": "Unban an IP from Fail2Ban",
|
|
161
|
+
"command": "sudo fail2ban-client unban {arg1}",
|
|
162
|
+
"is_private": true,
|
|
163
|
+
"accept_parameters": true,
|
|
164
|
+
"show_content_if_success": true,
|
|
165
|
+
"show_content_if_issue": true,
|
|
166
|
+
"timeout_in_sec": 60
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
141
170
|
## How to use in python script
|
|
142
171
|
|
|
143
172
|
Example of python script using this library:
|
|
@@ -31,6 +31,7 @@ def main() -> None:
|
|
|
31
31
|
parser.add_argument('--list_commands', action='store_true', help='📋 List all available commands 📋')
|
|
32
32
|
parser.add_argument('execute_command', type=str, help='📋 Execute a command 📋')
|
|
33
33
|
parser.add_argument('execute_all_commands', type=str, help='📋 Execute all commands 📋')
|
|
34
|
+
parser.add_argument('--parameters', type=str, default='', help='Optional parameters passed to --execute_command (space separated, quoted if needed)')
|
|
34
35
|
|
|
35
36
|
parser.add_argument('--debug', action='store_true', help='Enable debug mode')
|
|
36
37
|
parser.add_argument('--nodebug', action='store_true', help='Disable all logs')
|
|
@@ -163,7 +164,7 @@ def main() -> None:
|
|
|
163
164
|
if args.execute_command is not None:
|
|
164
165
|
handled = True
|
|
165
166
|
print(f"Executing command: {args.execute_command}...")
|
|
166
|
-
out_msg: str = asyncio.run(monitoring.execute_command(is_private=True, command_name=args.execute_command))
|
|
167
|
+
out_msg: str = asyncio.run(monitoring.execute_command(is_private=True, command_name=args.execute_command, parameters=args.parameters))
|
|
167
168
|
print(out_msg)
|
|
168
169
|
|
|
169
170
|
if args.execute_all_commands is not None:
|
|
@@ -33,7 +33,7 @@ __email__ = "quentin@comte-gaz.com"
|
|
|
33
33
|
__license__ = "MIT License"
|
|
34
34
|
__copyright__ = "Copyright Quentin Comte-Gaz (2024)"
|
|
35
35
|
__python_version__ = "3.+"
|
|
36
|
-
__version__ = "1.5.
|
|
36
|
+
__version__ = "1.5.7 (2026/08/19)"
|
|
37
37
|
__status__ = "Usable for any Linux project"
|
|
38
38
|
|
|
39
39
|
import json
|
|
@@ -48,6 +48,7 @@ import socket
|
|
|
48
48
|
from datetime import datetime, timedelta
|
|
49
49
|
import platform
|
|
50
50
|
import re
|
|
51
|
+
import shlex
|
|
51
52
|
import logging
|
|
52
53
|
import asyncio
|
|
53
54
|
from http.client import responses
|
|
@@ -2020,40 +2021,166 @@ class LinuxMonitor:
|
|
|
2020
2021
|
logging.exception(msg=out_msg)
|
|
2021
2022
|
return out_msg
|
|
2022
2023
|
|
|
2023
|
-
|
|
2024
|
+
def _apply_command_parameters(self, command_template: str, parameters: str) -> str:
|
|
2025
|
+
"""
|
|
2026
|
+
Substitute user-provided parameters into a command template in an injection-safe way.
|
|
2027
|
+
|
|
2028
|
+
Placeholders supported in the template:
|
|
2029
|
+
- {args} : replaced by all provided parameters (space separated)
|
|
2030
|
+
- {arg1}, {arg2} : replaced by the Nth provided parameter
|
|
2031
|
+
If the template contains no placeholder, all parameters are appended at the end.
|
|
2032
|
+
|
|
2033
|
+
Every parameter is shell-quoted (shlex.quote) so it is always treated as a single
|
|
2034
|
+
literal argument, which prevents command injection through the parameters.
|
|
2035
|
+
|
|
2036
|
+
:param command_template: The command as defined in the configuration file.
|
|
2037
|
+
:param parameters: The raw parameters string provided by the user.
|
|
2038
|
+
|
|
2039
|
+
:return: The final command ready to be executed.
|
|
2040
|
+
"""
|
|
2041
|
+
param_list: List[str] = shlex.split(parameters) if parameters else []
|
|
2042
|
+
quoted_params: List[str] = [shlex.quote(param) for param in param_list]
|
|
2043
|
+
|
|
2044
|
+
has_placeholder: bool = ("{args}" in command_template) or bool(re.search(pattern=r"\{arg\d+\}", string=command_template))
|
|
2045
|
+
|
|
2046
|
+
# No placeholder: append every quoted parameter at the end of the command
|
|
2047
|
+
if not has_placeholder:
|
|
2048
|
+
if quoted_params:
|
|
2049
|
+
return command_template + " " + " ".join(quoted_params)
|
|
2050
|
+
return command_template
|
|
2051
|
+
|
|
2052
|
+
final_command: str = command_template
|
|
2053
|
+
|
|
2054
|
+
# Replace positional placeholders {arg1}, {arg2}, ...
|
|
2055
|
+
for index, quoted_param in enumerate(iterable=quoted_params, start=1):
|
|
2056
|
+
final_command = final_command.replace(f"{{arg{index}}}", quoted_param)
|
|
2057
|
+
|
|
2058
|
+
# Replace the catch-all {args} placeholder with every quoted parameter
|
|
2059
|
+
final_command = final_command.replace("{args}", " ".join(quoted_params))
|
|
2060
|
+
|
|
2061
|
+
# Any positional placeholder left unprovided is replaced by an empty string
|
|
2062
|
+
final_command = re.sub(pattern=r"\{arg\d+\}", repl="", string=final_command)
|
|
2063
|
+
|
|
2064
|
+
return final_command
|
|
2065
|
+
|
|
2066
|
+
def _normalize_command_name(self, name: str) -> str:
|
|
2067
|
+
"""
|
|
2068
|
+
Normalize a command name for permissive matching: lowercase and ignore spaces, '-' and '_'.
|
|
2069
|
+
"""
|
|
2070
|
+
return re.sub(pattern=r"[\s\-_]+", repl="", string=name.strip().lower())
|
|
2071
|
+
|
|
2072
|
+
def _levenshtein_distance(self, a: str, b: str) -> int:
|
|
2073
|
+
"""
|
|
2074
|
+
Compute the Levenshtein (edit) distance between two strings (number of single-character
|
|
2075
|
+
insertions, deletions or substitutions needed to turn one into the other).
|
|
2076
|
+
"""
|
|
2077
|
+
if a == b:
|
|
2078
|
+
return 0
|
|
2079
|
+
if len(a) == 0:
|
|
2080
|
+
return len(b)
|
|
2081
|
+
if len(b) == 0:
|
|
2082
|
+
return len(a)
|
|
2083
|
+
|
|
2084
|
+
previous_row: List[int] = list(range(len(b) + 1))
|
|
2085
|
+
for i, char_a in enumerate(iterable=a, start=1):
|
|
2086
|
+
current_row: List[int] = [i]
|
|
2087
|
+
for j, char_b in enumerate(iterable=b, start=1):
|
|
2088
|
+
insert_cost: int = current_row[j - 1] + 1
|
|
2089
|
+
delete_cost: int = previous_row[j] + 1
|
|
2090
|
+
replace_cost: int = previous_row[j - 1] + (0 if char_a == char_b else 1)
|
|
2091
|
+
current_row.append(min(insert_cost, delete_cost, replace_cost))
|
|
2092
|
+
previous_row = current_row
|
|
2093
|
+
|
|
2094
|
+
return previous_row[-1]
|
|
2095
|
+
|
|
2096
|
+
def _find_matching_command_key(self, command_name: str) -> Optional[str]:
|
|
2097
|
+
"""
|
|
2098
|
+
Find the configured command key matching the requested name in a permissive way:
|
|
2099
|
+
exact match first, then case/'-'/'_'/space-insensitive match, then a small typo tolerance.
|
|
2100
|
+
Ambiguous matches (several equally good candidates) are rejected to avoid running the wrong command.
|
|
2101
|
+
|
|
2102
|
+
:param command_name: The command name requested by the user.
|
|
2103
|
+
|
|
2104
|
+
:return: The matching command key from the configuration, or None if no safe match is found.
|
|
2105
|
+
"""
|
|
2106
|
+
command_keys: List[str] = list(self.config['commands'].keys())
|
|
2107
|
+
|
|
2108
|
+
# 1. Exact match
|
|
2109
|
+
if command_name in command_keys:
|
|
2110
|
+
return command_name
|
|
2111
|
+
|
|
2112
|
+
# 2. Normalized match (case-insensitive, '-'/'_'/spaces ignored)
|
|
2113
|
+
target: str = self._normalize_command_name(name=command_name)
|
|
2114
|
+
normalized_matches: List[str] = [key for key in command_keys if self._normalize_command_name(name=key) == target]
|
|
2115
|
+
if len(normalized_matches) == 1:
|
|
2116
|
+
return normalized_matches[0]
|
|
2117
|
+
if len(normalized_matches) > 1:
|
|
2118
|
+
# Several commands normalize to the same name: too ambiguous to guess
|
|
2119
|
+
return None
|
|
2120
|
+
|
|
2121
|
+
# 3. Fuzzy match tolerating small typos (on the normalized names)
|
|
2122
|
+
threshold: int = 1 if len(target) <= 4 else 2
|
|
2123
|
+
best_distance: Optional[int] = None
|
|
2124
|
+
best_keys: List[str] = []
|
|
2125
|
+
for key in command_keys:
|
|
2126
|
+
distance: int = self._levenshtein_distance(a=self._normalize_command_name(name=key), b=target)
|
|
2127
|
+
if best_distance is None or distance < best_distance:
|
|
2128
|
+
best_distance = distance
|
|
2129
|
+
best_keys = [key]
|
|
2130
|
+
elif distance == best_distance:
|
|
2131
|
+
best_keys.append(key)
|
|
2132
|
+
|
|
2133
|
+
# Only accept a typo-based match if it is close enough AND unambiguous
|
|
2134
|
+
if best_distance is not None and best_distance <= threshold and len(best_keys) == 1:
|
|
2135
|
+
return best_keys[0]
|
|
2136
|
+
|
|
2137
|
+
return None
|
|
2138
|
+
|
|
2139
|
+
async def execute_command(self, is_private: bool, command_name: str, parameters: str = "") -> str:
|
|
2024
2140
|
try:
|
|
2025
2141
|
out_msg: str = ""
|
|
2026
|
-
display_name: str = command_name
|
|
2027
|
-
for command_config_key in self.config['commands'].keys():
|
|
2028
|
-
if command_config_key == command_name:
|
|
2029
|
-
display_name = self.config['commands'][command_config_key]['display_name']
|
|
2030
|
-
command: str = self.config['commands'][command_config_key]['command']
|
|
2031
|
-
is_private_cmd: bool = self.config['commands'][command_config_key]['is_private']
|
|
2032
|
-
|
|
2033
|
-
if is_private or is_private == is_private_cmd:
|
|
2034
|
-
show_content_if_success: bool = self.config['commands'][command_config_key].get('show_content_if_success', False)
|
|
2035
|
-
show_content_if_issue: bool = self.config['commands'][command_config_key].get('show_content_if_issue', False)
|
|
2036
|
-
is_content_json: bool = self.config['commands'][command_config_key].get('is_content_json', False)
|
|
2037
|
-
timeout_in_sec: int = self.config['commands'][command_config_key].get('timeout_in_sec', 10)
|
|
2038
|
-
|
|
2039
|
-
res: str = await self._execute_command(command=command, display_name=display_name, show_content_if_success=show_content_if_success,
|
|
2040
|
-
show_content_if_issue=show_content_if_issue, is_content_json=is_content_json,
|
|
2041
|
-
timeout_in_sec=timeout_in_sec, display_only_if_critical=False)
|
|
2042
|
-
if res != "":
|
|
2043
|
-
if out_msg != "":
|
|
2044
|
-
out_msg += "\n"
|
|
2045
|
-
out_msg += f"{res}"
|
|
2046
|
-
else:
|
|
2047
|
-
out_msg = f"⚠️ **Command {display_name} is not allowed**"
|
|
2048
|
-
logging.warning(msg=out_msg)
|
|
2049
2142
|
|
|
2050
|
-
|
|
2051
|
-
|
|
2143
|
+
command_config_key: Optional[str] = self._find_matching_command_key(command_name=command_name)
|
|
2144
|
+
if command_config_key is None:
|
|
2145
|
+
out_msg = f"⚠️ **Command {command_name} not found**"
|
|
2146
|
+
logging.warning(msg=out_msg)
|
|
2147
|
+
return out_msg
|
|
2148
|
+
|
|
2149
|
+
display_name: str = self.config['commands'][command_config_key]['display_name']
|
|
2150
|
+
command: str = self.config['commands'][command_config_key]['command']
|
|
2151
|
+
is_private_cmd: bool = self.config['commands'][command_config_key]['is_private']
|
|
2152
|
+
|
|
2153
|
+
if is_private or is_private == is_private_cmd:
|
|
2154
|
+
parameters = parameters.strip()
|
|
2155
|
+
# A command accepts parameters only if it exposes a placeholder or explicitly opts in
|
|
2156
|
+
accept_parameters: bool = self.config['commands'][command_config_key].get('accept_parameters', False) \
|
|
2157
|
+
or ("{args}" in command) or bool(re.search(pattern=r"\{arg\d+\}", string=command))
|
|
2158
|
+
|
|
2159
|
+
if parameters != "" and not accept_parameters:
|
|
2160
|
+
out_msg = f"⚠️ **Command {display_name} does not accept parameters**"
|
|
2161
|
+
logging.warning(msg=out_msg)
|
|
2162
|
+
else:
|
|
2163
|
+
final_command: str = self._apply_command_parameters(command_template=command, parameters=parameters)
|
|
2052
2164
|
|
|
2053
|
-
|
|
2165
|
+
show_content_if_success: bool = self.config['commands'][command_config_key].get('show_content_if_success', False)
|
|
2166
|
+
show_content_if_issue: bool = self.config['commands'][command_config_key].get('show_content_if_issue', False)
|
|
2167
|
+
is_content_json: bool = self.config['commands'][command_config_key].get('is_content_json', False)
|
|
2168
|
+
timeout_in_sec: int = self.config['commands'][command_config_key].get('timeout_in_sec', 10)
|
|
2169
|
+
|
|
2170
|
+
res: str = await self._execute_command(command=final_command, display_name=display_name, show_content_if_success=show_content_if_success,
|
|
2171
|
+
show_content_if_issue=show_content_if_issue, is_content_json=is_content_json,
|
|
2172
|
+
timeout_in_sec=timeout_in_sec, display_only_if_critical=False)
|
|
2173
|
+
if res != "":
|
|
2174
|
+
if out_msg != "":
|
|
2175
|
+
out_msg += "\n"
|
|
2176
|
+
out_msg += f"{res}"
|
|
2177
|
+
else:
|
|
2178
|
+
out_msg = f"⚠️ **Command {display_name} is not allowed**"
|
|
2179
|
+
logging.warning(msg=out_msg)
|
|
2180
|
+
|
|
2181
|
+
if out_msg != "":
|
|
2182
|
+
out_msg = f"# 📜 Command {display_name} 📜\n{out_msg}"
|
|
2054
2183
|
|
|
2055
|
-
out_msg = f"⚠️ **Command {display_name} not found**"
|
|
2056
|
-
logging.warning(msg=out_msg)
|
|
2057
2184
|
return out_msg
|
|
2058
2185
|
except Exception as e:
|
|
2059
2186
|
return f"⚠️ **Error executing command {command_name}**:\n```sh\n{e}\n```"
|
|
@@ -6,7 +6,7 @@ with io.open(file='README.md', mode='r', encoding='utf-8') as readme_file:
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="LinuxMonitor",
|
|
9
|
-
version="1.5.
|
|
9
|
+
version="1.5.7",
|
|
10
10
|
description="Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)",
|
|
11
11
|
long_description=readme,
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|