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
|
require 'open3'
require 'time'
require 'net/http'
# Mirror toolkit functionality and other shared stuff
module MirrorToolkit
RSYNC_TIMESTAMP_FILE = '/gentoo-portage/metadata/timestamp'
DISTFILES_TIMESTAMP_FILE = '/distfiles/timestamp.mirmon'
TYPE_RSYNC = 1
TYPE_DISTFILES = 2
module_function
# Calls `rsync_cat` to fetch a file from rsync
def rsync_cat(uri)
rsync_cat = File.join(File.dirname(__FILE__), '..', 'bin', 'rsync-cat')
stdin, stdout, stderr, wait_thr = Open3.popen3(rsync_cat, uri)
stdin.close
if wait_thr.value == 0
return stdout.gets
else
fail "Rsync call unsuccessful: '#{stderr.gets.chomp}'"
end
end
def curl(uri)
stdin, stdout, stderr, wait_thr = Open3.popen3('curl', '-m', '20', uri)
stdin.close
if wait_thr.value == 0
return stdout.gets
else
fail "curl call unsuccessful: '#{stderr.gets.chomp}'"
end
end
# Fetches a URI from any of the Gentoo mirror types
def remote_fetch(url)
case url.scheme
when 'rsync'
rsync_cat(url.to_s)
when 'http', 'https'
Net::HTTP.get(url)
when 'ftp'
curl(url.to_s)
else
fail 'Unknown URI scheme.'
end
end
# Tries to return a Time object for every kind of timestamp used on Gentoo mirrors
def parse_timestamp(ts)
if ts.numeric?
Time.at(ts.to_i).utc
else
Time.parse(ts).utc
end
end
# Returns a URI object where to find the timestamp for the given url and mirror type.
def get_timestamp_url(url, type)
mirror = URI(url)
case mirror.scheme
when 'rsync'
if type == TYPE_RSYNC
mirror.path = MirrorToolkit::RSYNC_TIMESTAMP_FILE
elsif type == TYPE_DISTFILES
mirror.path += MirrorToolkit::DISTFILES_TIMESTAMP_FILE
end
when 'http', 'https', 'ftp'
mirror.path += MirrorToolkit::DISTFILES_TIMESTAMP_FILE
end
mirror
end
# Returns the number of seconds a mirror is lagging behind, or nil if it cannot be determined.
def get_lag(url, type)
Time.now.utc - parse_timestamp(remote_fetch(get_timestamp_url(url, type)))
rescue
nil
end
# Renders seconds as a nice human-printable string
def humanize_seconds(secs)
[[60, :seconds], [60, :minutes], [24, :hours], [1000, :days]].map do |count, name|
if secs > 0
secs, n = secs.divmod(count)
"#{n.to_i} #{name}"
end
end.compact.reverse.join(' ')
end
end
class String
def numeric?
true if Float(self) rescue false
end
end
|