LinuxMonitor 1.2.8__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.8 → linuxmonitor-1.3.0/LinuxMonitor.egg-info}/PKG-INFO +5 -1
- {linuxmonitor-1.2.8/LinuxMonitor.egg-info → linuxmonitor-1.3.0}/PKG-INFO +5 -1
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/README.md +4 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/linuxmonitor/__main__.py +12 -5
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/linuxmonitor/linuxmonitor.py +111 -64
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/setup.py +1 -1
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/LICENSE.md +0 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/SOURCES.txt +0 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/dependency_links.txt +0 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/requires.txt +0 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/LinuxMonitor.egg-info/top_level.txt +0 -0
- {linuxmonitor-1.2.8 → linuxmonitor-1.3.0}/linuxmonitor/__init__.py +0 -0
- {linuxmonitor-1.2.8 → 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
|
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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 📋')
|
|
@@ -105,7 +106,7 @@ def main() -> None:
|
|
|
105
106
|
if args.websites:
|
|
106
107
|
handled = True
|
|
107
108
|
print("Checking websites availability...")
|
|
108
|
-
out_msg: str = asyncio.run(monitoring.check_all_websites(is_private=True, display_only_if_critical=False
|
|
109
|
+
out_msg: str = asyncio.run(monitoring.check_all_websites(is_private=True, display_only_if_critical=False))
|
|
109
110
|
print(out_msg)
|
|
110
111
|
|
|
111
112
|
if args.certificates:
|
|
@@ -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()
|
|
@@ -849,7 +852,7 @@ class LinuxMonitor:
|
|
|
849
852
|
out_msg = f"⚠️ **Error checking {display_name}**:\n```sh\n{e}\n```"
|
|
850
853
|
return False, out_msg
|
|
851
854
|
|
|
852
|
-
async def check_all_websites(self, is_private: bool, display_only_if_critical: bool=False
|
|
855
|
+
async def check_all_websites(self, is_private: bool, display_only_if_critical: bool=False) -> str:
|
|
853
856
|
try:
|
|
854
857
|
out_msg: str = ""
|
|
855
858
|
for website_config in self.config['websites']:
|
|
@@ -875,8 +878,8 @@ class LinuxMonitor:
|
|
|
875
878
|
out_msg += "\n"
|
|
876
879
|
out_msg += f"- {msg}"
|
|
877
880
|
|
|
878
|
-
if not res and
|
|
879
|
-
restart_msg = await self.restart_service(is_private=is_private, service_name=service_name_to_restart)
|
|
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)
|
|
880
883
|
out_msg += f"\n - {restart_msg}"
|
|
881
884
|
|
|
882
885
|
if out_msg != "":
|
|
@@ -892,7 +895,7 @@ class LinuxMonitor:
|
|
|
892
895
|
|
|
893
896
|
#region Services
|
|
894
897
|
|
|
895
|
-
def
|
|
898
|
+
def get_all_services(self, is_private: bool) -> str:
|
|
896
899
|
"""
|
|
897
900
|
Get all services allowed to restart which are configured in the JSON configuration file.
|
|
898
901
|
|
|
@@ -905,12 +908,24 @@ class LinuxMonitor:
|
|
|
905
908
|
service = self.config['services'][service_name]
|
|
906
909
|
if is_private or service['is_private'] == is_private:
|
|
907
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)
|
|
908
913
|
|
|
909
914
|
if out_msg != "":
|
|
910
915
|
out_msg += "\n"
|
|
911
|
-
out_msg += f"- `{service_name}`: {service['display_name']}"
|
|
916
|
+
out_msg += f"- `{service_name}`: {service['display_name']}:\n"
|
|
912
917
|
if not is_allowed_to_restart:
|
|
913
|
-
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')"
|
|
914
929
|
|
|
915
930
|
if out_msg != "":
|
|
916
931
|
out_msg = f"# 🔄 Services list 🔄\n{out_msg}"
|
|
@@ -920,7 +935,7 @@ class LinuxMonitor:
|
|
|
920
935
|
logging.info(msg=out_msg)
|
|
921
936
|
return out_msg
|
|
922
937
|
|
|
923
|
-
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:
|
|
924
939
|
"""
|
|
925
940
|
Restart a specific service.
|
|
926
941
|
|
|
@@ -947,9 +962,14 @@ class LinuxMonitor:
|
|
|
947
962
|
logging.error(msg=out_msg)
|
|
948
963
|
return out_msg
|
|
949
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
|
+
|
|
950
970
|
timeout_in_sec: int = service.get('timeout_in_sec', 90)
|
|
951
971
|
|
|
952
|
-
service_call:
|
|
972
|
+
service_call: str = service['restart_command']
|
|
953
973
|
out_msg: str = ""
|
|
954
974
|
|
|
955
975
|
logging.info(f"Trying to restart {display_name} (command: {service_call}) in less than {timeout_in_sec}sec...")
|
|
@@ -986,7 +1006,7 @@ class LinuxMonitor:
|
|
|
986
1006
|
out_msg: str = ""
|
|
987
1007
|
for service_name in self.config["services"].keys():
|
|
988
1008
|
if is_private or self.config["services"][service_name]['is_private'] == is_private:
|
|
989
|
-
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)
|
|
990
1010
|
if res != "":
|
|
991
1011
|
if out_msg:
|
|
992
1012
|
out_msg += "\n"
|
|
@@ -1023,7 +1043,7 @@ class LinuxMonitor:
|
|
|
1023
1043
|
logging.error(msg=f"Status command (status_command) not found for service {service_name}")
|
|
1024
1044
|
return None, ""
|
|
1025
1045
|
|
|
1026
|
-
status_command = service['status_command']
|
|
1046
|
+
status_command: str = service['status_command']
|
|
1027
1047
|
check_also_stdout_not_containing = service.get('check_also_stdout_not_containing', None)
|
|
1028
1048
|
|
|
1029
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)
|
|
@@ -1033,7 +1053,7 @@ class LinuxMonitor:
|
|
|
1033
1053
|
logging.exception(msg=out_msg)
|
|
1034
1054
|
return None, out_msg
|
|
1035
1055
|
|
|
1036
|
-
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:
|
|
1037
1057
|
"""
|
|
1038
1058
|
Check the status of all services configured in the JSON configuration file.
|
|
1039
1059
|
|
|
@@ -1058,58 +1078,85 @@ class LinuxMonitor:
|
|
|
1058
1078
|
"❌"
|
|
1059
1079
|
)
|
|
1060
1080
|
|
|
1061
|
-
out_msg = "# 📱 Services status 📱\n"
|
|
1062
|
-
|
|
1063
1081
|
for service_name in self.config["services"].keys():
|
|
1064
1082
|
if is_private or self.config["services"][service_name]['is_private'] == is_private:
|
|
1065
1083
|
status, status_msg = await self._get_service_status(is_private=is_private, service_name=service_name)
|
|
1066
1084
|
service_status: str = status_to_string(res=status)
|
|
1067
1085
|
service_icon: str = status_to_icon(res=status)
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
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
|
+
|
|
1073
1097
|
except Exception as e:
|
|
1074
1098
|
out_msg = f"**Internal error checking services status**:\n```sh\n{e}\n```"
|
|
1075
1099
|
logging.exception(msg=out_msg)
|
|
1076
1100
|
|
|
1101
|
+
|
|
1102
|
+
if out_msg != "":
|
|
1103
|
+
out_msg = f"# 📱 Services status 📱\n{out_msg}"
|
|
1104
|
+
|
|
1077
1105
|
return out_msg
|
|
1078
1106
|
|
|
1079
|
-
async def
|
|
1107
|
+
async def stop_service(self, is_private: bool, service_name: str) -> str:
|
|
1080
1108
|
"""
|
|
1081
|
-
|
|
1109
|
+
Stop a specific service.
|
|
1082
1110
|
|
|
1083
|
-
: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).
|
|
1084
1113
|
|
|
1085
1114
|
:return: A string containing the result message.
|
|
1086
1115
|
"""
|
|
1087
|
-
|
|
1116
|
+
out_msg: str = ""
|
|
1088
1117
|
try:
|
|
1089
|
-
|
|
1090
|
-
out_msg
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if status is False:
|
|
1094
|
-
out_msg = f"- ❌ **{self.config['services'][service_name]['display_name']} inactive**. Restarting the service is necessary."
|
|
1095
|
-
logging.warning(msg=out_msg)
|
|
1096
|
-
if status_msg != "":
|
|
1097
|
-
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
|
|
1098
1122
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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)
|
|
1136
|
+
|
|
1137
|
+
service_call: str = service['stop_command']
|
|
1138
|
+
out_msg: str = ""
|
|
1108
1139
|
|
|
1109
|
-
|
|
1110
|
-
out_msg_full = f"# 📱 Services status 📱\n{out_msg_full}"
|
|
1140
|
+
logging.info(f"Trying to stop {display_name} (command: {service_call}) in less than {timeout_in_sec}sec...")
|
|
1111
1141
|
|
|
1112
|
-
|
|
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
|
|
1113
1160
|
|
|
1114
1161
|
#endregion
|
|
1115
1162
|
|
|
@@ -1383,7 +1430,8 @@ class LinuxMonitor:
|
|
|
1383
1430
|
command: str = f"last --ip --hostlast --time-format iso --since '{past_date}' | grep 'pts/'"
|
|
1384
1431
|
last_output: str = subprocess.check_output(
|
|
1385
1432
|
args=command,
|
|
1386
|
-
shell=True,
|
|
1433
|
+
shell=True,
|
|
1434
|
+
text=True
|
|
1387
1435
|
).strip()
|
|
1388
1436
|
except subprocess.CalledProcessError as e:
|
|
1389
1437
|
logging.error(msg=f"Error getting recent user logins:\n{e}")
|
|
@@ -1514,13 +1562,12 @@ class LinuxMonitor:
|
|
|
1514
1562
|
|
|
1515
1563
|
return res, out_msg
|
|
1516
1564
|
|
|
1517
|
-
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:
|
|
1518
1566
|
"""
|
|
1519
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).
|
|
1520
1568
|
|
|
1521
1569
|
:param is_private: User permission to check private or public ports (True for private, False for public).
|
|
1522
1570
|
:param display_only_if_critical: If True, the string result will only be returned if there is an error during execution.
|
|
1523
|
-
:param restart_if_down: If True, restart the service if the port is down.
|
|
1524
1571
|
|
|
1525
1572
|
:return: A string containing the result message.
|
|
1526
1573
|
"""
|
|
@@ -1535,7 +1582,7 @@ class LinuxMonitor:
|
|
|
1535
1582
|
want_port_to_be_open: bool = port_config.get('want_port_to_be_open', True)
|
|
1536
1583
|
|
|
1537
1584
|
service_name_to_restart: str = ""
|
|
1538
|
-
if want_port_to_be_open
|
|
1585
|
+
if want_port_to_be_open:
|
|
1539
1586
|
service_name_to_restart = port_config.get('service_name_to_restart', "")
|
|
1540
1587
|
|
|
1541
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)
|
|
@@ -1544,11 +1591,11 @@ class LinuxMonitor:
|
|
|
1544
1591
|
out_msg += "\n"
|
|
1545
1592
|
out_msg += f"- {result_msg}"
|
|
1546
1593
|
|
|
1547
|
-
if (not result) and
|
|
1594
|
+
if (not result) and service_name_to_restart != "":
|
|
1548
1595
|
if out_msg != "":
|
|
1549
1596
|
out_msg += "\n"
|
|
1550
1597
|
|
|
1551
|
-
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)
|
|
1552
1599
|
out_msg += f" - {restart_res}"
|
|
1553
1600
|
|
|
1554
1601
|
if out_msg != "":
|
|
@@ -1669,7 +1716,7 @@ class LinuxMonitor:
|
|
|
1669
1716
|
process_cmdline = process_cmdline[:60] + '...'
|
|
1670
1717
|
|
|
1671
1718
|
# Attempt to terminate the process
|
|
1672
|
-
terminate_command:
|
|
1719
|
+
terminate_command: str = f"sudo /bin/kill -TERM {pid}"
|
|
1673
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)
|
|
1674
1721
|
|
|
1675
1722
|
if result_terminate == True:
|
|
@@ -1681,7 +1728,7 @@ class LinuxMonitor:
|
|
|
1681
1728
|
out_msg = f"⚠️ **Stopping nicely {pid} ({process_name}) expired**.\n"
|
|
1682
1729
|
|
|
1683
1730
|
# Attempt to kill the process
|
|
1684
|
-
kill_command =
|
|
1731
|
+
kill_command: str = f"sudo /bin/kill -KILL {pid}"
|
|
1685
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)
|
|
1686
1733
|
out_msg += strerror_kill
|
|
1687
1734
|
|
|
@@ -1804,7 +1851,7 @@ class LinuxMonitor:
|
|
|
1804
1851
|
logging.info(msg="Checking services status and all disk usage, CPU, RAM, Swap, CPU temperature and ping of websites periodically...")
|
|
1805
1852
|
try:
|
|
1806
1853
|
# Services status
|
|
1807
|
-
msg: str = await self.
|
|
1854
|
+
msg: str = await self.check_all_services_status(is_private=is_private, display_only_if_critical=True)
|
|
1808
1855
|
if msg != "":
|
|
1809
1856
|
logging.warning(msg=msg)
|
|
1810
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):
|
|
@@ -1909,7 +1956,7 @@ class LinuxMonitor:
|
|
|
1909
1956
|
logging.info(msg="- ✅ Ping of all websites are OK.")
|
|
1910
1957
|
|
|
1911
1958
|
# Websites availability (GET request)
|
|
1912
|
-
msg = await self.check_all_websites(is_private=is_private, display_only_if_critical=True
|
|
1959
|
+
msg = await self.check_all_websites(is_private=is_private, display_only_if_critical=True)
|
|
1913
1960
|
if msg != "":
|
|
1914
1961
|
logging.warning(msg=msg)
|
|
1915
1962
|
if datetime_last_websites_error_displayed is None or ((datetime.now() - datetime_last_websites_error_displayed).total_seconds() > self.max_duration_seconds_showing_same_error_again_in_scheduled_tasks):
|
|
@@ -1930,7 +1977,7 @@ class LinuxMonitor:
|
|
|
1930
1977
|
logging.info(msg="- ✅ All websites availability (GET requests) are OK.")
|
|
1931
1978
|
|
|
1932
1979
|
# Ports
|
|
1933
|
-
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)
|
|
1934
1981
|
if msg != "":
|
|
1935
1982
|
logging.warning(msg=msg)
|
|
1936
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):
|
|
@@ -2111,7 +2158,7 @@ class LinuxMonitor:
|
|
|
2111
2158
|
logging.info(msg="-----------------------------------------------")
|
|
2112
2159
|
|
|
2113
2160
|
# Services status
|
|
2114
|
-
msg: str = await self.
|
|
2161
|
+
msg: str = await self.check_all_services_status(is_private=is_private, display_only_if_critical=False)
|
|
2115
2162
|
if msg != "":
|
|
2116
2163
|
if out_msg != "":
|
|
2117
2164
|
out_msg += "\n"
|
|
@@ -2146,14 +2193,14 @@ class LinuxMonitor:
|
|
|
2146
2193
|
out_msg += msg
|
|
2147
2194
|
|
|
2148
2195
|
# Websites availability (GET request)
|
|
2149
|
-
msg = await self.check_all_websites(is_private=is_private, display_only_if_critical=False
|
|
2196
|
+
msg = await self.check_all_websites(is_private=is_private, display_only_if_critical=False)
|
|
2150
2197
|
if msg != "":
|
|
2151
2198
|
if out_msg != "":
|
|
2152
2199
|
out_msg += "\n"
|
|
2153
2200
|
out_msg += msg
|
|
2154
2201
|
|
|
2155
2202
|
# Ports
|
|
2156
|
-
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)
|
|
2157
2204
|
if msg != "":
|
|
2158
2205
|
if out_msg != "":
|
|
2159
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
|