LinuxMonitor 1.2.7__tar.gz → 1.3.0__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.2.7 → linuxmonitor-1.3.0/LinuxMonitor.egg-info}/PKG-INFO +6 -2
- {linuxmonitor-1.2.7/LinuxMonitor.egg-info → linuxmonitor-1.3.0}/PKG-INFO +6 -2
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/README.md +5 -1
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/linuxmonitor/__main__.py +11 -4
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/linuxmonitor/linuxmonitor.py +124 -66
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/setup.py +1 -1
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/LICENSE.md +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/SOURCES.txt +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/dependency_links.txt +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/requires.txt +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/top_level.txt +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/linuxmonitor/__init__.py +0 -0
- {linuxmonitor-1.2.7 → linuxmonitor-1.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: LinuxMonitor
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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
|
|
@@ -61,6 +61,7 @@ Additionnal functionalities:
|
|
|
61
61
|
- Get processes list (PID and name)
|
|
62
62
|
- Kill a process by PID
|
|
63
63
|
- Reboot server
|
|
64
|
+
- Restart/stop a service
|
|
64
65
|
|
|
65
66
|
## How to install (python script and shell)
|
|
66
67
|
|
|
@@ -84,7 +85,7 @@ echo "USERNAME_HERE ALL=(ALL) NOPASSWD: /etc/init.d/mariadb" >> /etc/sudoers.d/U
|
|
|
84
85
|
|
|
85
86
|
## How to use in shell
|
|
86
87
|
|
|
87
|
-
```
|
|
88
|
+
```sh
|
|
88
89
|
# Get help
|
|
89
90
|
python3 -m linuxmonitor --help
|
|
90
91
|
# Use "--debug" to show more information during command
|
|
@@ -135,6 +136,9 @@ python3 -m linuxmonitor --restart_all --config_file config-example.json --nodebu
|
|
|
135
136
|
# Restart a service
|
|
136
137
|
python3 -m linuxmonitor --restart_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
137
138
|
|
|
139
|
+
# Stop a service
|
|
140
|
+
python3 -m linuxmonitor --stop_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
141
|
+
|
|
138
142
|
# Check ports
|
|
139
143
|
python3 -m linuxmonitor --ports --config_file config-example.json --nodebug
|
|
140
144
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: LinuxMonitor
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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
|
|
@@ -61,6 +61,7 @@ Additionnal functionalities:
|
|
|
61
61
|
- Get processes list (PID and name)
|
|
62
62
|
- Kill a process by PID
|
|
63
63
|
- Reboot server
|
|
64
|
+
- Restart/stop a service
|
|
64
65
|
|
|
65
66
|
## How to install (python script and shell)
|
|
66
67
|
|
|
@@ -84,7 +85,7 @@ echo "USERNAME_HERE ALL=(ALL) NOPASSWD: /etc/init.d/mariadb" >> /etc/sudoers.d/U
|
|
|
84
85
|
|
|
85
86
|
## How to use in shell
|
|
86
87
|
|
|
87
|
-
```
|
|
88
|
+
```sh
|
|
88
89
|
# Get help
|
|
89
90
|
python3 -m linuxmonitor --help
|
|
90
91
|
# Use "--debug" to show more information during command
|
|
@@ -135,6 +136,9 @@ python3 -m linuxmonitor --restart_all --config_file config-example.json --nodebu
|
|
|
135
136
|
# Restart a service
|
|
136
137
|
python3 -m linuxmonitor --restart_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
137
138
|
|
|
139
|
+
# Stop a service
|
|
140
|
+
python3 -m linuxmonitor --stop_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
141
|
+
|
|
138
142
|
# Check ports
|
|
139
143
|
python3 -m linuxmonitor --ports --config_file config-example.json --nodebug
|
|
140
144
|
|
|
@@ -33,6 +33,7 @@ Additionnal functionalities:
|
|
|
33
33
|
- Get processes list (PID and name)
|
|
34
34
|
- Kill a process by PID
|
|
35
35
|
- Reboot server
|
|
36
|
+
- Restart/stop a service
|
|
36
37
|
|
|
37
38
|
## How to install (python script and shell)
|
|
38
39
|
|
|
@@ -56,7 +57,7 @@ echo "USERNAME_HERE ALL=(ALL) NOPASSWD: /etc/init.d/mariadb" >> /etc/sudoers.d/U
|
|
|
56
57
|
|
|
57
58
|
## How to use in shell
|
|
58
59
|
|
|
59
|
-
```
|
|
60
|
+
```sh
|
|
60
61
|
# Get help
|
|
61
62
|
python3 -m linuxmonitor --help
|
|
62
63
|
# Use "--debug" to show more information during command
|
|
@@ -107,6 +108,9 @@ python3 -m linuxmonitor --restart_all --config_file config-example.json --nodebu
|
|
|
107
108
|
# Restart a service
|
|
108
109
|
python3 -m linuxmonitor --restart_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
109
110
|
|
|
111
|
+
# Stop a service
|
|
112
|
+
python3 -m linuxmonitor --stop_service SERVICE_NAME_HERE --config_file config-example.json --nodebug
|
|
113
|
+
|
|
110
114
|
# Check ports
|
|
111
115
|
python3 -m linuxmonitor --ports --config_file config-example.json --nodebug
|
|
112
116
|
|
|
@@ -23,6 +23,7 @@ def main() -> None:
|
|
|
23
23
|
parser.add_argument('--services_status', action='store_true', help='🩺 Check services are running 🩺')
|
|
24
24
|
parser.add_argument('--restart_all', action='store_true', help='🚀 Restart all services 🚀')
|
|
25
25
|
parser.add_argument('--restart_service', type=str, help='🚀 Restart a service 🚀')
|
|
26
|
+
parser.add_argument('--stop_service', type=str, help='🚫 Stop a service 🚫')
|
|
26
27
|
parser.add_argument('--list_services', action='store_true', help='📋 List all available services 📋')
|
|
27
28
|
parser.add_argument('--ports', action='store_true', help='🔒 Check ports 🔒')
|
|
28
29
|
parser.add_argument('--list_processes', action='store_true', help='📋 List active processes 📋')
|
|
@@ -123,7 +124,7 @@ def main() -> None:
|
|
|
123
124
|
if args.services_status:
|
|
124
125
|
handled = True
|
|
125
126
|
print("Checking if services are running and restart if down...")
|
|
126
|
-
out_msg: str = asyncio.run(monitoring.
|
|
127
|
+
out_msg: str = asyncio.run(monitoring.check_all_services_status(is_private=True, display_only_if_critical=False))
|
|
127
128
|
print(out_msg)
|
|
128
129
|
|
|
129
130
|
if args.restart_all:
|
|
@@ -134,19 +135,25 @@ def main() -> None:
|
|
|
134
135
|
if args.restart_service is not None:
|
|
135
136
|
handled = True
|
|
136
137
|
print(f"Restarting service: {args.restart_service}...")
|
|
137
|
-
out_msg: str = asyncio.run(monitoring.restart_service(is_private=True, service_name=args.restart_service))
|
|
138
|
+
out_msg: str = asyncio.run(monitoring.restart_service(is_private=True, service_name=args.restart_service, force_restart=True))
|
|
139
|
+
print(out_msg)
|
|
140
|
+
|
|
141
|
+
if args.stop_service is not None:
|
|
142
|
+
handled = True
|
|
143
|
+
print(f"Stopping service: {args.stop_service}...")
|
|
144
|
+
out_msg: str = asyncio.run(monitoring.stop_service(is_private=True, service_name=args.stop_service))
|
|
138
145
|
print(out_msg)
|
|
139
146
|
|
|
140
147
|
if args.list_services:
|
|
141
148
|
handled = True
|
|
142
149
|
print("Listing all available services...")
|
|
143
|
-
out_msg: str = monitoring.
|
|
150
|
+
out_msg: str = monitoring.get_all_services(is_private=True)
|
|
144
151
|
print(out_msg)
|
|
145
152
|
|
|
146
153
|
if args.ports:
|
|
147
154
|
handled = True
|
|
148
155
|
print("Checking ports...")
|
|
149
|
-
out_msg: str = asyncio.run(monitoring.check_all_ports(is_private=True, display_only_if_critical=False
|
|
156
|
+
out_msg: str = asyncio.run(monitoring.check_all_ports(is_private=True, display_only_if_critical=False))
|
|
150
157
|
print(out_msg)
|
|
151
158
|
|
|
152
159
|
if args.list_processes:
|
|
@@ -16,6 +16,9 @@ Non exhaustive list of features (available by using it in shell or in python scr
|
|
|
16
16
|
- Check last user connections IPs
|
|
17
17
|
- Check uptime (to inform if the server has been rebooted)
|
|
18
18
|
|
|
19
|
+
- Restart/stop a service
|
|
20
|
+
- List all services
|
|
21
|
+
|
|
19
22
|
- Get hostname, OS details, kernel version, server datetime, uptime
|
|
20
23
|
- Get connected users
|
|
21
24
|
|
|
@@ -30,7 +33,7 @@ __email__ = "quentin@comte-gaz.com"
|
|
|
30
33
|
__license__ = "MIT License"
|
|
31
34
|
__copyright__ = "Copyright Quentin Comte-Gaz (2024)"
|
|
32
35
|
__python_version__ = "3.+"
|
|
33
|
-
__version__ = "1.
|
|
36
|
+
__version__ = "1.3.0 (2024/09/19)"
|
|
34
37
|
__status__ = "Usable for any Linux project"
|
|
35
38
|
|
|
36
39
|
import json
|
|
@@ -179,7 +182,7 @@ class LinuxMonitor:
|
|
|
179
182
|
|
|
180
183
|
#region Execute Command
|
|
181
184
|
|
|
182
|
-
async def execute_and_verify(self, command:
|
|
185
|
+
async def execute_and_verify(self, command: str, display_name: str, show_only_std_and_exception: bool, timeout_in_sec: Optional[int] = None, display_only_if_critical: bool = False, check_also_stdout_not_containing: Optional[str] = None) -> Tuple[Optional[bool], str]:
|
|
183
186
|
"""
|
|
184
187
|
Execute a shell command asynchronously and verify its correct execution.
|
|
185
188
|
|
|
@@ -195,11 +198,11 @@ class LinuxMonitor:
|
|
|
195
198
|
|
|
196
199
|
try:
|
|
197
200
|
logging.debug(msg=f"Executing command {display_name} (command: {command})...")
|
|
198
|
-
process = await asyncio.
|
|
199
|
-
|
|
201
|
+
process = await asyncio.create_subprocess_shell(
|
|
202
|
+
command,
|
|
200
203
|
stdout=asyncio.subprocess.PIPE,
|
|
201
204
|
stderr=asyncio.subprocess.PIPE,
|
|
202
|
-
shell=
|
|
205
|
+
shell=True
|
|
203
206
|
)
|
|
204
207
|
|
|
205
208
|
try:
|
|
@@ -270,7 +273,7 @@ class LinuxMonitor:
|
|
|
270
273
|
IMPORTANT: To be usable, the user must have the right to execute `sudo /sbin/reboot` command without password.
|
|
271
274
|
"""
|
|
272
275
|
logging.info(msg="Rebooting the server...")
|
|
273
|
-
_, out_msg = await self.execute_and_verify(command=
|
|
276
|
+
_, out_msg = await self.execute_and_verify(command="sudo /sbin/reboot", display_name="Server reboot", show_only_std_and_exception=False, timeout_in_sec=None, display_only_if_critical=False)
|
|
274
277
|
return out_msg
|
|
275
278
|
|
|
276
279
|
#endregion
|
|
@@ -749,7 +752,7 @@ class LinuxMonitor:
|
|
|
749
752
|
:return: A string containing the result message.
|
|
750
753
|
"""
|
|
751
754
|
try:
|
|
752
|
-
ping_command:
|
|
755
|
+
ping_command: str = f"ping -c 1 {website}"
|
|
753
756
|
display_name = f"[{display_name}](https://{website})"
|
|
754
757
|
|
|
755
758
|
start_time: float = time.time()
|
|
@@ -795,7 +798,10 @@ class LinuxMonitor:
|
|
|
795
798
|
|
|
796
799
|
#region Check website response
|
|
797
800
|
|
|
798
|
-
async def _check_website(self, url: str, display_name: str,
|
|
801
|
+
async def _check_website(self, url: str, display_name: str, show_content_if_issue: bool, service_name: str = "",
|
|
802
|
+
timeout_in_sec: int = 5, auth_type: Optional[str] = None, username: Optional[str] = None,
|
|
803
|
+
password: Optional[str] = None, token: Optional[str] = None, additional_allowed_statuses: List[int] = [],
|
|
804
|
+
display_only_if_critical: bool=False) -> Tuple[bool, str]:
|
|
799
805
|
try:
|
|
800
806
|
display_name = f"[{display_name}]({url})"
|
|
801
807
|
|
|
@@ -834,9 +840,11 @@ class LinuxMonitor:
|
|
|
834
840
|
# Get the reason phrase (e.g., "Unauthorized" for 401)
|
|
835
841
|
status_reason: str = responses.get(response.status, "Unknown status")
|
|
836
842
|
out_msg = f"❌ **{display_name} answered with invalid status code {response.status} - {status_reason}**."
|
|
843
|
+
if show_content_if_issue:
|
|
844
|
+
content: str = (await response.text()).replace('`', '"').strip()
|
|
845
|
+
out_msg += f"\n```sh\n{content}\n```"
|
|
837
846
|
logging.warning(msg=out_msg)
|
|
838
847
|
return False, out_msg
|
|
839
|
-
|
|
840
848
|
except asyncio.TimeoutError:
|
|
841
849
|
out_msg = f"⚠️ **Error checking {display_name}: Timeout**"
|
|
842
850
|
return False, out_msg
|
|
@@ -852,22 +860,28 @@ class LinuxMonitor:
|
|
|
852
860
|
timeout_in_sec: int = website_config.get('timeout_in_sec', 5)
|
|
853
861
|
url: str = website_config['url']
|
|
854
862
|
display_name: str = website_config.get('display_name', url)
|
|
863
|
+
show_content_if_issue: bool = website_config.get('show_content_if_issue', False)
|
|
864
|
+
service_name_to_restart: str = website_config.get('service_name_to_restart', "")
|
|
855
865
|
auth_type: Optional[str] = website_config.get('auth_type', None)
|
|
856
866
|
username: Optional[str] = website_config.get('username', None)
|
|
857
867
|
password: Optional[str] = website_config.get('password', None)
|
|
858
868
|
token: Optional[str] = website_config.get('token', None)
|
|
859
869
|
additional_allowed_statuses: List[int] = website_config.get('additional_allowed_statuses', [])
|
|
860
870
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
871
|
+
res, msg = await self._check_website(url=url, display_name=display_name, timeout_in_sec=timeout_in_sec,
|
|
872
|
+
show_content_if_issue=show_content_if_issue,
|
|
873
|
+
auth_type=auth_type, username=username, password=password, token=token,
|
|
874
|
+
additional_allowed_statuses=additional_allowed_statuses,
|
|
875
|
+
display_only_if_critical=display_only_if_critical)
|
|
866
876
|
if msg != "":
|
|
867
877
|
if out_msg:
|
|
868
878
|
out_msg += "\n"
|
|
869
879
|
out_msg += f"- {msg}"
|
|
870
880
|
|
|
881
|
+
if not res and service_name_to_restart != "":
|
|
882
|
+
restart_msg = await self.restart_service(is_private=is_private, service_name=service_name_to_restart, force_restart=False)
|
|
883
|
+
out_msg += f"\n - {restart_msg}"
|
|
884
|
+
|
|
871
885
|
if out_msg != "":
|
|
872
886
|
out_msg = f"# 🌐 Website access state 🌐\n{out_msg}"
|
|
873
887
|
|
|
@@ -881,7 +895,7 @@ class LinuxMonitor:
|
|
|
881
895
|
|
|
882
896
|
#region Services
|
|
883
897
|
|
|
884
|
-
def
|
|
898
|
+
def get_all_services(self, is_private: bool) -> str:
|
|
885
899
|
"""
|
|
886
900
|
Get all services allowed to restart which are configured in the JSON configuration file.
|
|
887
901
|
|
|
@@ -894,12 +908,24 @@ class LinuxMonitor:
|
|
|
894
908
|
service = self.config['services'][service_name]
|
|
895
909
|
if is_private or service['is_private'] == is_private:
|
|
896
910
|
is_allowed_to_restart: bool = 'restart_command' in service
|
|
911
|
+
is_allowed_to_stop: bool = 'stop_command' in service
|
|
912
|
+
auto_restart: bool = service.get('force_restart', False)
|
|
897
913
|
|
|
898
914
|
if out_msg != "":
|
|
899
915
|
out_msg += "\n"
|
|
900
|
-
out_msg += f"- `{service_name}`: {service['display_name']}"
|
|
916
|
+
out_msg += f"- `{service_name}`: {service['display_name']}:\n"
|
|
901
917
|
if not is_allowed_to_restart:
|
|
902
|
-
out_msg += "
|
|
918
|
+
out_msg += " - ❌ Not authorized to restart (no 'restart_command')\n"
|
|
919
|
+
else:
|
|
920
|
+
if auto_restart:
|
|
921
|
+
out_msg += " - ✅ Can be restarted (automatically & manually)\n"
|
|
922
|
+
else:
|
|
923
|
+
out_msg += " - ✅ Can be restarted (manually only because 'force_restart' = false)\n"
|
|
924
|
+
|
|
925
|
+
if is_allowed_to_stop:
|
|
926
|
+
out_msg += " - ✅ Can be stopped manually"
|
|
927
|
+
else:
|
|
928
|
+
out_msg += " - ❌ Not authorized to stop manually (no 'stop_command')"
|
|
903
929
|
|
|
904
930
|
if out_msg != "":
|
|
905
931
|
out_msg = f"# 🔄 Services list 🔄\n{out_msg}"
|
|
@@ -909,7 +935,7 @@ class LinuxMonitor:
|
|
|
909
935
|
logging.info(msg=out_msg)
|
|
910
936
|
return out_msg
|
|
911
937
|
|
|
912
|
-
async def restart_service(self, is_private: bool, service_name: str) -> str:
|
|
938
|
+
async def restart_service(self, is_private: bool, service_name: str, force_restart: bool) -> str:
|
|
913
939
|
"""
|
|
914
940
|
Restart a specific service.
|
|
915
941
|
|
|
@@ -936,9 +962,14 @@ class LinuxMonitor:
|
|
|
936
962
|
logging.error(msg=out_msg)
|
|
937
963
|
return out_msg
|
|
938
964
|
|
|
965
|
+
if not force_restart and not service.get('force_restart', False):
|
|
966
|
+
out_msg = f"⚠️ **Service {service_name} can be restarted only manually** (change configuration if needed)."
|
|
967
|
+
logging.error(msg=out_msg)
|
|
968
|
+
return out_msg
|
|
969
|
+
|
|
939
970
|
timeout_in_sec: int = service.get('timeout_in_sec', 90)
|
|
940
971
|
|
|
941
|
-
service_call:
|
|
972
|
+
service_call: str = service['restart_command']
|
|
942
973
|
out_msg: str = ""
|
|
943
974
|
|
|
944
975
|
logging.info(f"Trying to restart {display_name} (command: {service_call}) in less than {timeout_in_sec}sec...")
|
|
@@ -975,7 +1006,7 @@ class LinuxMonitor:
|
|
|
975
1006
|
out_msg: str = ""
|
|
976
1007
|
for service_name in self.config["services"].keys():
|
|
977
1008
|
if is_private or self.config["services"][service_name]['is_private'] == is_private:
|
|
978
|
-
res: str = await self.restart_service(is_private=is_private, service_name=service_name)
|
|
1009
|
+
res: str = await self.restart_service(is_private=is_private, service_name=service_name, force_restart=True)
|
|
979
1010
|
if res != "":
|
|
980
1011
|
if out_msg:
|
|
981
1012
|
out_msg += "\n"
|
|
@@ -1012,7 +1043,7 @@ class LinuxMonitor:
|
|
|
1012
1043
|
logging.error(msg=f"Status command (status_command) not found for service {service_name}")
|
|
1013
1044
|
return None, ""
|
|
1014
1045
|
|
|
1015
|
-
status_command = service['status_command']
|
|
1046
|
+
status_command: str = service['status_command']
|
|
1016
1047
|
check_also_stdout_not_containing = service.get('check_also_stdout_not_containing', None)
|
|
1017
1048
|
|
|
1018
1049
|
res, out_msg = await self.execute_and_verify(command=status_command, display_name=f"état de {display_name}", show_only_std_and_exception=True, timeout_in_sec=timeout_in_sec, display_only_if_critical=False, check_also_stdout_not_containing=check_also_stdout_not_containing)
|
|
@@ -1022,7 +1053,7 @@ class LinuxMonitor:
|
|
|
1022
1053
|
logging.exception(msg=out_msg)
|
|
1023
1054
|
return None, out_msg
|
|
1024
1055
|
|
|
1025
|
-
async def check_all_services_status(self, is_private: bool) -> str:
|
|
1056
|
+
async def check_all_services_status(self, is_private: bool, display_only_if_critical: bool=False) -> str:
|
|
1026
1057
|
"""
|
|
1027
1058
|
Check the status of all services configured in the JSON configuration file.
|
|
1028
1059
|
|
|
@@ -1047,58 +1078,85 @@ class LinuxMonitor:
|
|
|
1047
1078
|
"❌"
|
|
1048
1079
|
)
|
|
1049
1080
|
|
|
1050
|
-
out_msg = "# 📱 Services status 📱\n"
|
|
1051
|
-
|
|
1052
1081
|
for service_name in self.config["services"].keys():
|
|
1053
1082
|
if is_private or self.config["services"][service_name]['is_private'] == is_private:
|
|
1054
1083
|
status, status_msg = await self._get_service_status(is_private=is_private, service_name=service_name)
|
|
1055
1084
|
service_status: str = status_to_string(res=status)
|
|
1056
1085
|
service_icon: str = status_to_icon(res=status)
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
out_msg += f"
|
|
1086
|
+
|
|
1087
|
+
if status is not True or not display_only_if_critical:
|
|
1088
|
+
if out_msg != "":
|
|
1089
|
+
out_msg += "\n"
|
|
1090
|
+
out_msg += f"- {service_icon} {self.config['services'][service_name]['display_name']}: **{service_status}**"
|
|
1091
|
+
if status != True and status_msg != "":
|
|
1092
|
+
out_msg += f"\n{status_msg}"
|
|
1093
|
+
|
|
1094
|
+
if status is False:
|
|
1095
|
+
out_msg += f"\n - " + await self.restart_service(is_private=is_private, service_name=service_name, force_restart=False)
|
|
1096
|
+
|
|
1062
1097
|
except Exception as e:
|
|
1063
1098
|
out_msg = f"**Internal error checking services status**:\n```sh\n{e}\n```"
|
|
1064
1099
|
logging.exception(msg=out_msg)
|
|
1065
1100
|
|
|
1101
|
+
|
|
1102
|
+
if out_msg != "":
|
|
1103
|
+
out_msg = f"# 📱 Services status 📱\n{out_msg}"
|
|
1104
|
+
|
|
1066
1105
|
return out_msg
|
|
1067
1106
|
|
|
1068
|
-
async def
|
|
1107
|
+
async def stop_service(self, is_private: bool, service_name: str) -> str:
|
|
1069
1108
|
"""
|
|
1070
|
-
|
|
1109
|
+
Stop a specific service.
|
|
1071
1110
|
|
|
1072
|
-
:param is_private: User permission to
|
|
1111
|
+
:param is_private: User permission to stop private or public services (True for private, False for public).
|
|
1112
|
+
:param service_name: The name of the service name to stop (as configured in the JSON configuration file).
|
|
1073
1113
|
|
|
1074
1114
|
:return: A string containing the result message.
|
|
1075
1115
|
"""
|
|
1076
|
-
|
|
1116
|
+
out_msg: str = ""
|
|
1077
1117
|
try:
|
|
1078
|
-
|
|
1079
|
-
out_msg
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
if status is False:
|
|
1083
|
-
out_msg = f"- ❌ **{self.config['services'][service_name]['display_name']} inactive**. Restarting the service is necessary."
|
|
1084
|
-
logging.warning(msg=out_msg)
|
|
1085
|
-
if status_msg != "":
|
|
1086
|
-
out_msg += f"\n{status_msg}"
|
|
1118
|
+
if service_name not in self.config['services'].keys():
|
|
1119
|
+
out_msg = f"❌ **Service {service_name} not found**."
|
|
1120
|
+
logging.error(msg=out_msg)
|
|
1121
|
+
return out_msg
|
|
1087
1122
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1123
|
+
service = self.config['services'][service_name]
|
|
1124
|
+
if not is_private and is_private != service['is_private']:
|
|
1125
|
+
out_msg = f"❌ **Service {service_name} not authorized to stop in public**."
|
|
1126
|
+
logging.error(msg=out_msg)
|
|
1127
|
+
return out_msg
|
|
1128
|
+
|
|
1129
|
+
display_name: str = service.get('display_name', service_name)
|
|
1130
|
+
if 'stop_command' not in service:
|
|
1131
|
+
out_msg = f"❌ **Stop command not found for {display_name}**."
|
|
1132
|
+
logging.error(msg=out_msg)
|
|
1133
|
+
return out_msg
|
|
1134
|
+
|
|
1135
|
+
timeout_in_sec: int = service.get('timeout_in_sec', 90)
|
|
1097
1136
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1137
|
+
service_call: str = service['stop_command']
|
|
1138
|
+
out_msg: str = ""
|
|
1100
1139
|
|
|
1101
|
-
|
|
1140
|
+
logging.info(f"Trying to stop {display_name} (command: {service_call}) in less than {timeout_in_sec}sec...")
|
|
1141
|
+
|
|
1142
|
+
start_time: float = time.time()
|
|
1143
|
+
res, res_msg = await self.execute_and_verify(command=service_call, display_name=f"stop {display_name}", show_only_std_and_exception=True, timeout_in_sec=timeout_in_sec, display_only_if_critical=False)
|
|
1144
|
+
end_time: float = time.time()
|
|
1145
|
+
|
|
1146
|
+
if res == True:
|
|
1147
|
+
readable_duration: str = "{:.2f}".format(end_time - start_time)
|
|
1148
|
+
out_msg = f"✅ **{display_name} stopped with success** in {readable_duration}sec."
|
|
1149
|
+
logging.info(msg=out_msg)
|
|
1150
|
+
else:
|
|
1151
|
+
out_msg = f"❌ **Error stopping service {display_name}**"
|
|
1152
|
+
if res_msg and res_msg != "":
|
|
1153
|
+
out_msg += f"\n{res_msg}"
|
|
1154
|
+
logging.warning(msg=out_msg)
|
|
1155
|
+
except Exception as e:
|
|
1156
|
+
out_msg = f"⚠️ **Error stopping service {service_name}**:\n```sh\n{e}\n```"
|
|
1157
|
+
logging.exception(msg=out_msg)
|
|
1158
|
+
|
|
1159
|
+
return out_msg
|
|
1102
1160
|
|
|
1103
1161
|
#endregion
|
|
1104
1162
|
|
|
@@ -1372,7 +1430,8 @@ class LinuxMonitor:
|
|
|
1372
1430
|
command: str = f"last --ip --hostlast --time-format iso --since '{past_date}' | grep 'pts/'"
|
|
1373
1431
|
last_output: str = subprocess.check_output(
|
|
1374
1432
|
args=command,
|
|
1375
|
-
shell=True,
|
|
1433
|
+
shell=True,
|
|
1434
|
+
text=True
|
|
1376
1435
|
).strip()
|
|
1377
1436
|
except subprocess.CalledProcessError as e:
|
|
1378
1437
|
logging.error(msg=f"Error getting recent user logins:\n{e}")
|
|
@@ -1503,13 +1562,12 @@ class LinuxMonitor:
|
|
|
1503
1562
|
|
|
1504
1563
|
return res, out_msg
|
|
1505
1564
|
|
|
1506
|
-
async def check_all_ports(self, is_private: bool, display_only_if_critical: bool=False
|
|
1565
|
+
async def check_all_ports(self, is_private: bool, display_only_if_critical: bool=False) -> str:
|
|
1507
1566
|
"""
|
|
1508
1567
|
Check all ports configured in the JSON configuration file (and restart the service if the port is down and service_name_to_restart added in config file).
|
|
1509
1568
|
|
|
1510
1569
|
:param is_private: User permission to check private or public ports (True for private, False for public).
|
|
1511
1570
|
:param display_only_if_critical: If True, the string result will only be returned if there is an error during execution.
|
|
1512
|
-
:param restart_if_down: If True, restart the service if the port is down.
|
|
1513
1571
|
|
|
1514
1572
|
:return: A string containing the result message.
|
|
1515
1573
|
"""
|
|
@@ -1524,7 +1582,7 @@ class LinuxMonitor:
|
|
|
1524
1582
|
want_port_to_be_open: bool = port_config.get('want_port_to_be_open', True)
|
|
1525
1583
|
|
|
1526
1584
|
service_name_to_restart: str = ""
|
|
1527
|
-
if want_port_to_be_open
|
|
1585
|
+
if want_port_to_be_open:
|
|
1528
1586
|
service_name_to_restart = port_config.get('service_name_to_restart', "")
|
|
1529
1587
|
|
|
1530
1588
|
result, result_msg = self._is_port_in_required_state(port=port, host=host, timeout_in_sec=timeout_in_sec, want_port_to_be_open=want_port_to_be_open, display_name=display_name)
|
|
@@ -1533,11 +1591,11 @@ class LinuxMonitor:
|
|
|
1533
1591
|
out_msg += "\n"
|
|
1534
1592
|
out_msg += f"- {result_msg}"
|
|
1535
1593
|
|
|
1536
|
-
if (not result) and
|
|
1594
|
+
if (not result) and service_name_to_restart != "":
|
|
1537
1595
|
if out_msg != "":
|
|
1538
1596
|
out_msg += "\n"
|
|
1539
1597
|
|
|
1540
|
-
restart_res: str = await self.restart_service(is_private=is_private, service_name=service_name_to_restart)
|
|
1598
|
+
restart_res: str = await self.restart_service(is_private=is_private, service_name=service_name_to_restart, force_restart=False)
|
|
1541
1599
|
out_msg += f" - {restart_res}"
|
|
1542
1600
|
|
|
1543
1601
|
if out_msg != "":
|
|
@@ -1658,7 +1716,7 @@ class LinuxMonitor:
|
|
|
1658
1716
|
process_cmdline = process_cmdline[:60] + '...'
|
|
1659
1717
|
|
|
1660
1718
|
# Attempt to terminate the process
|
|
1661
|
-
terminate_command:
|
|
1719
|
+
terminate_command: str = f"sudo /bin/kill -TERM {pid}"
|
|
1662
1720
|
result_terminate, _ = await self.execute_and_verify(command=terminate_command, display_name=f"stop process {pid} ({process_name})", show_only_std_and_exception=False, timeout_in_sec=timeout_in_sec, display_only_if_critical=False)
|
|
1663
1721
|
|
|
1664
1722
|
if result_terminate == True:
|
|
@@ -1670,7 +1728,7 @@ class LinuxMonitor:
|
|
|
1670
1728
|
out_msg = f"⚠️ **Stopping nicely {pid} ({process_name}) expired**.\n"
|
|
1671
1729
|
|
|
1672
1730
|
# Attempt to kill the process
|
|
1673
|
-
kill_command =
|
|
1731
|
+
kill_command: str = f"sudo /bin/kill -KILL {pid}"
|
|
1674
1732
|
_, strerror_kill = await self.execute_and_verify(command=kill_command, display_name=f"kill process {pid} ({process_name})", show_only_std_and_exception=True, timeout_in_sec=timeout_in_sec, display_only_if_critical=False)
|
|
1675
1733
|
out_msg += strerror_kill
|
|
1676
1734
|
|
|
@@ -1793,7 +1851,7 @@ class LinuxMonitor:
|
|
|
1793
1851
|
logging.info(msg="Checking services status and all disk usage, CPU, RAM, Swap, CPU temperature and ping of websites periodically...")
|
|
1794
1852
|
try:
|
|
1795
1853
|
# Services status
|
|
1796
|
-
msg: str = await self.
|
|
1854
|
+
msg: str = await self.check_all_services_status(is_private=is_private, display_only_if_critical=True)
|
|
1797
1855
|
if msg != "":
|
|
1798
1856
|
logging.warning(msg=msg)
|
|
1799
1857
|
if datetime_last_services_error_displayed is None or ((datetime.now() - datetime_last_services_error_displayed).total_seconds() > self.max_duration_seconds_showing_same_error_again_in_scheduled_tasks):
|
|
@@ -1919,7 +1977,7 @@ class LinuxMonitor:
|
|
|
1919
1977
|
logging.info(msg="- ✅ All websites availability (GET requests) are OK.")
|
|
1920
1978
|
|
|
1921
1979
|
# Ports
|
|
1922
|
-
msg = await self.check_all_ports(is_private=is_private, display_only_if_critical=True
|
|
1980
|
+
msg = await self.check_all_ports(is_private=is_private, display_only_if_critical=True)
|
|
1923
1981
|
if msg != "":
|
|
1924
1982
|
logging.warning(msg=msg)
|
|
1925
1983
|
if datetime_last_port_error_displayed is None or ((datetime.now() - datetime_last_port_error_displayed).total_seconds() > self.max_duration_seconds_showing_same_error_again_in_scheduled_tasks):
|
|
@@ -2100,7 +2158,7 @@ class LinuxMonitor:
|
|
|
2100
2158
|
logging.info(msg="-----------------------------------------------")
|
|
2101
2159
|
|
|
2102
2160
|
# Services status
|
|
2103
|
-
msg: str = await self.
|
|
2161
|
+
msg: str = await self.check_all_services_status(is_private=is_private, display_only_if_critical=False)
|
|
2104
2162
|
if msg != "":
|
|
2105
2163
|
if out_msg != "":
|
|
2106
2164
|
out_msg += "\n"
|
|
@@ -2142,7 +2200,7 @@ class LinuxMonitor:
|
|
|
2142
2200
|
out_msg += msg
|
|
2143
2201
|
|
|
2144
2202
|
# Ports
|
|
2145
|
-
msg = await self.check_all_ports(is_private=is_private, display_only_if_critical=False
|
|
2203
|
+
msg = await self.check_all_ports(is_private=is_private, display_only_if_critical=False)
|
|
2146
2204
|
if msg != "":
|
|
2147
2205
|
if out_msg != "":
|
|
2148
2206
|
out_msg += "\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.
|
|
9
|
+
version="1.3.0",
|
|
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
|