summaryrefslogtreecommitdiff
blob: bebe9e5f3250bd8bb19043249afd8a7c1d2f8d31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/python

import logging
import urllib2
import time
import rfc822
import re

class GardCheck:
	# Base class which provides some helper functions
	def __init__(self, url, log=True):
		self.url = url
		if log:
			self.logger = logging.getLogger('')
		else:
			self.logger = None

	def log_info(self, msg):
		if self.logger is not None:
			self.logger.info(msg)

	def log_error(self, msg):
		if self.logger is not None:
			self.logger.error(msg)

	def check_file_exists(self, url):
		ret = True
		try:
			f = urllib2.urlopen(url)
			if len(f.read()) == 0:
				raise IOError
		except:
			ret = False

		return ret

	# Takes the URL to a timestamp.{chk|x} file and returns the
	# corresponding time stamp in seconds
	def _get_timestamp_from_url(self, url):
		try:
			f = urllib2.urlopen(url)
			date = f.read()
			f.close()
			if date is None or len(date) == 0:
				raise ValueError
			try:
				# timestamp.chk format
				ts = self.timestamp_to_secs(date)
			except:
				# timestamp.x format?
				ts = float(date.split(' ')[0])
		except:
			return None

		return ts

	def get_lag(self, path):
		ts = self._get_timestamp_from_url(self.url + path)
		now = time.mktime(time.gmtime())
		if ts is None or now < ts:
			return None
		return now - ts

	def humanize_time(self, secs):
		mins, secs = divmod(secs, 60)
		hours, mins = divmod(mins, 60)
		days, hours = divmod(hours, 24)
    		return '%02dd %02dh %02dm %02ds' % (days, hours, mins, secs)

	def timestamp_to_secs(self, ts):
		return rfc822.mktime_tz(rfc822.parsedate_tz(ts))

	# OPTIONAL: Override or supplement these in child classes if desired.
	def check_name(self):
		return re.sub('.*\.|(Check$)', '', str(self.__class__))

	def check(self, maxlag):
		lag = self.lag()

		if lag is None:
			self.log_error('Could not get %s timestamp for %s' % (self.check_name(), self.url))
			ret = False
		elif lag > maxlag:
			self.log_error('%s at %s is lagging (delta is %s)' \
					% (self.check_name(), self.url, self.humanize_time(lag)))
			ret = False
		else:
			ret = True

		return ret

	# REQUIRED: You must override these in child classes
	def lag(self):
		return None

# Check distfiles mirrors
class DistfilesCheck(GardCheck):
	def lag(self):
		path = '/distfiles/timestamp.chk'
		return self.get_lag(path)

# Check releases mirrors
class ReleasesCheck(GardCheck):
	def lag(self):
		path = '/releases/.test/timestamp.x'
		return self.get_lag(path)

	def check(self, maxlag):
		# Call the superclass first if exists
		try: fun = getattr(GardCheck, 'check')
		except AttributeError: pass
		else: ret = fun(self, maxlag)

		# Verify that releases/.test/THIS-FILE-SHOULD-NOT-BE-PUBLIC.txt
		# is not world readable
		if self.check_file_exists(self.url+'releases/.test/THIS-FILE-SHOULD-NOT-BE-PUBLIC.txt'):
			self.log_error('ERROR: releases permission check failed on %s' % self.url)
			ret = False

		return ret