025382e78c78545dd33b6f877755b38ceb9f94d5
[mirror/dsa-puppet.git] / modules / mirror_health / files / mirror-health
1 #! /usr/bin/python3
2
3 import os
4 import requests
5 import time
6 import calendar
7 import logging
8 import subprocess
9 from email.utils import parsedate
10 logging.basicConfig(level=logging.INFO)
11
12 if 'MIRROR_CHECK_HOSTS' in os.environ:
13     HOSTS = os.environ['MIRROR_CHECK_HOSTS'].split()
14 else:
15     HOSTS = open(os.environ['MIRROR_CHECK_HOSTS_FILE']).read().split()
16
17 OUTPUT_DIR = "/run/dsa-mirror-health-{}".format(os.environ['MIRROR_CHECK_SERVICE'])
18 HEALTH_FILE = os.path.join(OUTPUT_DIR, "health")
19 URL = os.environ['MIRROR_CHECK_URL']
20 HEALTH_CHECK_URL = os.environ['MIRROR_CHECK_HEALTH_URL']
21 INTERVAL = int(os.environ.get('MIRROR_CHECK_INTERVAL', '60'))
22
23 def retrieve_from_host(host, url):
24     proxies = {
25         'http': 'http://{}:80'.format(host),
26         'https': 'http://{}:443'.format(host),
27     }
28     headers = {'User-Agent': 'mirror-health'}
29     return requests.get(url, headers=headers, timeout=5, proxies=proxies, allow_redirects=False)
30
31 def last_modified(response):
32     lm = 0
33     if response.status_code == 200 and response.headers.get('last-modified'):
34         lm = calendar.timegm(parsedate(response.headers['last-modified']))
35     return lm
36
37 def healthy(response):
38     if response.status_code == 200:
39         return True
40     return False
41
42 def check_shutdown():
43     if subprocess.call(['dsa-is-shutdown-scheduled']) == 0:
44         logging.info("considering myself unhealthy, shutdown scheduled")
45         return False
46     return True
47
48 def check_uptodate():
49     latest_ts = 0
50     for host in HOSTS:
51         try:
52             lm = last_modified(retrieve_from_host(host, URL))
53             logging.debug("lm for host %s: %s", host, lm)
54             if healthy(retrieve_from_host(host, HEALTH_CHECK_URL)):
55                 latest_ts = max(latest_ts, lm)
56         except (requests.exceptions.ProxyError, requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError):
57             pass
58     try:
59         local_lm = last_modified(retrieve_from_host('localhost', URL))
60     except (requests.exceptions.ProxyError, requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError):
61         return False
62     logging.debug("lm for localhost: %s", local_lm)
63     if local_lm < latest_ts:
64         logging.info("considering myself unhealthy my ts=%s latest_ts=%s", local_lm, latest_ts)
65         return False
66     return True
67
68 while True:
69     start = time.time()
70     if check_shutdown() and check_uptodate():
71         logging.info("considering myself healthy")
72         open(HEALTH_FILE, 'w').write("OK")
73     else:
74         try:
75             os.remove(HEALTH_FILE)
76         except OSError:
77             pass
78     sleep_time = start + INTERVAL - time.time()
79     logging.debug("sleeping for %d seconds", sleep_time)
80     time.sleep(sleep_time)