2024-06-25 19:11:07 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
# MIT License
|
|
|
|
|
2024-06-27 17:44:12 +00:00
|
|
|
# Copyright (c) 2024 Thomas Williams - https://git.server.wales/thomas
|
2024-06-25 19:11:07 +00:00
|
|
|
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
|
|
# in the Software without restriction, including without limitation the rights
|
|
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
|
|
# furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
|
|
# copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
# SOFTWARE.
|
|
|
|
|
2024-07-07 17:32:18 +00:00
|
|
|
def get_pip_command():
|
|
|
|
|
|
|
|
if sys.version_info >= (3, 0):
|
|
|
|
|
|
|
|
return 'pip3'
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return 'pip'
|
|
|
|
|
|
|
|
def install_dependencies():
|
|
|
|
|
2024-07-08 13:59:35 +00:00
|
|
|
if not config.usePip:
|
|
|
|
return
|
|
|
|
|
2024-07-07 17:32:18 +00:00
|
|
|
pip_command = get_pip_command()
|
2024-07-08 13:34:13 +00:00
|
|
|
|
|
|
|
with open('requirements.txt', 'r') as req_file:
|
|
|
|
|
|
|
|
requirements = req_file.read().splitlines()
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
with open('installedrequirements.txt', 'r') as installed_file:
|
|
|
|
installed_requirements = installed_file.read().splitlines()
|
|
|
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
|
|
|
installed_requirements = []
|
|
|
|
|
|
|
|
dependencies_to_install = [req for req in requirements if req not in installed_requirements]
|
|
|
|
|
|
|
|
if dependencies_to_install:
|
|
|
|
|
|
|
|
subprocess.check_call([pip_command, 'install'] + dependencies_to_install + ['--user'])
|
|
|
|
|
|
|
|
with open('installedrequirements.txt', 'a') as installed_file:
|
|
|
|
for dependency in dependencies_to_install:
|
|
|
|
installed_file.write(dependency + '\n')
|
2024-06-29 11:55:24 +00:00
|
|
|
|
|
|
|
def signal_handler(sig, frame):
|
|
|
|
print('SIGINT/SIGTERM aknowledged. Stopping script gracefully, please wait...')
|
|
|
|
stop_event.set()
|
|
|
|
|
2024-06-26 18:40:26 +00:00
|
|
|
def loadUrl(url):
|
|
|
|
|
2024-06-27 17:25:59 +00:00
|
|
|
headers = { 'User-Agent': 'Monutil monitor' }
|
|
|
|
|
2024-08-16 12:21:42 +00:00
|
|
|
@retry(stop_max_attempt_number=120, wait_fixed=1000)
|
2024-07-08 13:23:16 +00:00
|
|
|
def submitRequest(url):
|
|
|
|
|
|
|
|
response = requests.get(url, timeout=config.urlTimeout, headers=headers)
|
|
|
|
return response
|
|
|
|
|
|
|
|
response = submitRequest(url)
|
|
|
|
|
2024-06-26 18:40:26 +00:00
|
|
|
return response
|
|
|
|
|
|
|
|
def prepareUrl(src, baseUrl):
|
|
|
|
|
|
|
|
if not src.startswith("http://") and not src.startswith("https://"):
|
|
|
|
return baseUrl.rstrip("/") + "/" + src.lstrip("/")
|
|
|
|
return src
|
|
|
|
|
2024-07-03 18:14:57 +00:00
|
|
|
def nonPOSIXCPULoad(stop_event):
|
|
|
|
|
|
|
|
global nonPOSIXCPULoads
|
|
|
|
nonPOSIXCPULoad = 0
|
|
|
|
|
|
|
|
while not stop_event.is_set():
|
|
|
|
|
|
|
|
nonPOSIXCPULoad = psutil.cpu_percent(interval=1)
|
|
|
|
|
|
|
|
with lock:
|
|
|
|
|
|
|
|
nonPOSIXCPULoads.append(nonPOSIXCPULoad)
|
|
|
|
|
|
|
|
if len(nonPOSIXCPULoads) > 60:
|
|
|
|
nonPOSIXCPULoads.pop(0)
|
|
|
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
def getNonPOSIXCPUAverage():
|
|
|
|
|
|
|
|
global nonPOSIXCPULoads
|
|
|
|
|
|
|
|
with lock:
|
|
|
|
if sum(nonPOSIXCPULoads) > 0:
|
|
|
|
avgLoad = sum(nonPOSIXCPULoads) / len(nonPOSIXCPULoads)
|
|
|
|
else:
|
|
|
|
avgLoad = 0
|
|
|
|
|
|
|
|
return avgLoad
|
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
def monitorHost(stop_event):
|
2024-07-04 17:25:08 +00:00
|
|
|
|
2024-07-03 18:14:57 +00:00
|
|
|
nonPOSIXCPUStarted = False
|
|
|
|
|
2024-07-06 13:51:35 +00:00
|
|
|
while not (stop_event.is_set()) and (config.hostMonitoringPeriod > 0):
|
2024-07-05 12:42:28 +00:00
|
|
|
|
|
|
|
while not (stop_event.is_set()) and (time.strftime("%H:%M:%S") >= config.hostMonitorStartTime and time.strftime("%H:%M:%S") <= config.hostMonitorEndTime):
|
2024-07-03 18:14:57 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
if os.name != 'posix' or config.forceNonPOSIXCPU:
|
2024-07-03 18:14:57 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
if not nonPOSIXCPUStarted:
|
2024-07-03 18:14:57 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
nonPOSIXCPUMonitor = threading.Thread(target=nonPOSIXCPULoad, args=(stop_event,))
|
|
|
|
nonPOSIXCPUMonitor.start()
|
|
|
|
nonPOSIXCPUStarted = True
|
2024-07-03 18:14:57 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
loadavg = round(getNonPOSIXCPUAverage(), 2)
|
2024-07-03 18:14:57 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
else:
|
2024-06-25 19:11:07 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
load1, load5, load15 = psutil.getloadavg() # this takes time to warm up if not running script on *nix
|
|
|
|
loadavg = round((load1/os.cpu_count()) * 100, 2)
|
2024-06-29 16:59:54 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
memory = psutil.virtual_memory().percent
|
|
|
|
|
|
|
|
logHostLog(socket.gethostname(), datetime.now(), loadavg, memory)
|
2024-06-25 19:11:07 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
print("CPU %: " + str(loadavg))
|
|
|
|
print("Memory %: " + str(memory))
|
|
|
|
print() # new line
|
2024-06-25 19:11:07 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
time.sleep(config.hostMonitoringPeriod)
|
|
|
|
|
|
|
|
time.sleep(1)
|
2024-06-25 19:11:07 +00:00
|
|
|
|
2024-06-29 11:55:24 +00:00
|
|
|
def monitorUrls(stop_event):
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-06 13:51:35 +00:00
|
|
|
while not (stop_event.is_set()) and (config.urlMonitoringPeriod > 0):
|
2024-07-04 17:25:08 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
while not (stop_event.is_set()) and (time.strftime("%H:%M:%S") >= config.urlMonitorStartTime and time.strftime("%H:%M:%S") <= config.urlMonitorEndTime):
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
for url in config.urls:
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
baseUrl = url
|
|
|
|
urlFail = False
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
startTime = time.time()
|
|
|
|
|
|
|
|
request = loadUrl(url)
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
if request.status_code == 200:
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
html = BeautifulSoup(request.content, 'html.parser')
|
|
|
|
imageUrls = [img['src'] for img in html.find_all('img')]
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
with ThreadPoolExecutor(max_workers=config.maxWorkers) as executor:
|
|
|
|
|
|
|
|
responses = [executor.submit(loadUrl, prepareUrl(url, baseUrl)) for url in imageUrls]
|
|
|
|
responses = [future.result() for future in as_completed(responses)]
|
|
|
|
|
|
|
|
for response in responses:
|
|
|
|
|
|
|
|
if not response.status_code == 200:
|
|
|
|
|
|
|
|
urlFail = True
|
|
|
|
|
|
|
|
endTime = time.time()
|
|
|
|
timeDiff = endTime - startTime
|
|
|
|
|
|
|
|
print(baseUrl + " response time: " + str(timeDiff))
|
|
|
|
print() # new line
|
|
|
|
|
|
|
|
logURLLog(socket.gethostname(), datetime.now(), baseUrl, timeDiff)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
urlFail = True
|
|
|
|
|
|
|
|
time.sleep(config.urlMonitoringPeriod)
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-05 12:42:28 +00:00
|
|
|
time.sleep(1)
|
2024-06-27 17:25:59 +00:00
|
|
|
|
2024-07-02 19:46:36 +00:00
|
|
|
def logHostLog(hostname, logTime, cpu, memory):
|
|
|
|
|
2024-07-06 12:31:26 +00:00
|
|
|
if not config.loggingMode == 'none' and not config.loggingMode == 'rabbitmq':
|
2024-07-04 15:47:37 +00:00
|
|
|
|
2024-07-04 13:07:33 +00:00
|
|
|
manager = logsManager(config.sqlServer, config.sqlDatabase, config.sqlUsername, config.sqlPassword)
|
|
|
|
manager.insertHostLog(hostname, socket.gethostbyname(socket.gethostname()), logTime, cpu, memory)
|
2024-07-03 20:23:36 +00:00
|
|
|
|
2024-07-06 12:31:26 +00:00
|
|
|
if config.loggingMode == 'rabbitmq':
|
|
|
|
|
2024-07-06 13:40:04 +00:00
|
|
|
rabbitmq.publish(hostname + '|' + socket.gethostbyname(socket.gethostname()) + '|' + str(logTime) + '|' + 'cpumem' + '|' + str(cpu) + '|' + str(memory))
|
2024-07-06 12:31:26 +00:00
|
|
|
|
2024-07-03 20:23:36 +00:00
|
|
|
def logURLLog(hostname, logTime, url, responseTime):
|
|
|
|
|
2024-07-06 12:31:26 +00:00
|
|
|
if not config.loggingMode == 'none' and not config.loggingMode == 'rabbitmq':
|
2024-07-04 17:25:08 +00:00
|
|
|
|
2024-07-04 13:07:33 +00:00
|
|
|
manager = logsManager(config.sqlServer, config.sqlDatabase, config.sqlUsername, config.sqlPassword)
|
|
|
|
manager.insertURLLog(hostname, socket.gethostbyname(socket.gethostname()), logTime, url, responseTime)
|
2024-07-02 19:46:36 +00:00
|
|
|
|
2024-07-06 12:51:00 +00:00
|
|
|
if config.loggingMode == 'rabbitmq':
|
|
|
|
|
2024-07-06 13:40:04 +00:00
|
|
|
rabbitmq.publish(hostname + '|' + socket.gethostbyname(socket.gethostname()) + '|' + str(logTime) + '|' + 'url' + '|' + url + '|' + str(responseTime))
|
2024-07-06 12:51:00 +00:00
|
|
|
|
2024-06-27 17:25:59 +00:00
|
|
|
def main():
|
2024-06-29 11:55:24 +00:00
|
|
|
|
2024-07-07 17:17:02 +00:00
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
|
|
|
|
import win32api
|
|
|
|
|
|
|
|
win32api.SetConsoleCtrlHandler(lambda sig, frame:
|
|
|
|
|
|
|
|
signal_handler(signal.CTRL_C_EVENT, frame), True)
|
2024-07-07 17:35:15 +00:00
|
|
|
|
2024-07-07 17:17:02 +00:00
|
|
|
else:
|
|
|
|
|
|
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
2024-07-04 15:47:37 +00:00
|
|
|
|
2024-07-07 17:32:18 +00:00
|
|
|
install_dependencies()
|
|
|
|
|
2024-06-29 11:55:24 +00:00
|
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
2024-07-04 15:47:37 +00:00
|
|
|
|
2024-06-29 11:55:24 +00:00
|
|
|
hostMonitorThread = threading.Thread(target=monitorHost, args=(stop_event,))
|
|
|
|
urlMonitorThread = threading.Thread(target=monitorUrls, args=(stop_event,))
|
2024-06-27 17:25:59 +00:00
|
|
|
|
|
|
|
hostMonitorThread.start()
|
|
|
|
urlMonitorThread.start()
|
|
|
|
|
|
|
|
hostMonitorThread.join()
|
|
|
|
urlMonitorThread.join()
|
2024-06-25 19:11:07 +00:00
|
|
|
|
2024-06-27 17:25:59 +00:00
|
|
|
if __name__ == "__main__":
|
2024-07-07 17:32:18 +00:00
|
|
|
|
|
|
|
import sys
|
|
|
|
import subprocess
|
2024-07-08 13:59:35 +00:00
|
|
|
import config
|
2024-07-07 17:32:18 +00:00
|
|
|
|
|
|
|
install_dependencies()
|
|
|
|
|
|
|
|
import os
|
2024-07-08 13:59:35 +00:00
|
|
|
import psutil
|
2024-07-07 17:32:18 +00:00
|
|
|
import time
|
|
|
|
import log
|
|
|
|
import requests
|
|
|
|
import threading
|
|
|
|
import signal
|
|
|
|
import socket
|
2024-07-08 13:23:16 +00:00
|
|
|
from retrying import retry
|
2024-07-07 17:32:18 +00:00
|
|
|
from functools import partial
|
|
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
from log import logsManager
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
if config.loggingMode == 'rabbitmq':
|
|
|
|
|
|
|
|
import rabbitmq
|
|
|
|
rabbitmq = rabbitmq.rabbitMQClient(config.rabbitmqca,config.rabbitmqcacert,config.rabbitmqcakey,config.rabbitmqHost,config.rabbitmqPort,config.rabbitmqRoutingKey)
|
|
|
|
|
|
|
|
stop_event = threading.Event()
|
|
|
|
nonPOSIXCPULoads = []
|
|
|
|
lock = threading.Lock()
|
|
|
|
|
2024-06-27 17:25:59 +00:00
|
|
|
main()
|