#!/usr/bin/ruby require 'date' require 'net/http' require 'optparse' require 'thread' require 'uri' def get_dates(base, archs_per_distro, optional=true) r = {} begin r['base'] = get_timestamp(base) rescue Net::OpenTimeout, Timeout::Error, ArgumentError, NoMethodError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET, IOError => e end archs_per_distro.each{|d, archs| r[d] = {} archs.each{|a| begin r[d][a] = get_date(base, d, a) rescue Net::OpenTimeout, Timeout::Error, ArgumentError, NoMethodError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET, IOError => e if !optional then STDERR.puts "Failed to fetch #{version_url(base, d, a)}" raise end end } } r end def get_mirrors # TODO Get it from the DB mirrors = [] url = nil tier1 = false fetch_url("http://mirrors.mageia.org/").each_line{|l| if l =~ /rsync.mageia.org/ then tier1 = true next end if l=~ /<\/tr>/ && !url.nil? then if tier1 then mirrors.prepend url tier1 = false else mirrors.append url end url = nil next end next unless l =~ /https?:.*>http/ # No need to check twice mirrors available in http + https if !url.nil? && url =~ /https:/ && l =~ /https:\/\// # Skip http:// if https:// already seen for current mirror # If the are in the other order http one will just be replaced next end url = l.sub(/ 'check_mirrors'}) case response when Net::HTTPSuccess then return response.body when Net::HTTPRedirection then location = response['location'] # Make location absolute if it was not if location =~ /:\/\// then fetch_url(location, redirect_limit - 1) else uri.path = location fetch_url(uri.to_s, redirect_limit - 1) end end end end def timestamp_url(url) "#{url}mageia_timestamp" end def get_timestamp(url) ti = fetch_url(timestamp_url(url)).to_i if ti == 0 then return nil end return DateTime.strptime(ti.to_s, '%s') end def parse_version(version) date = version.sub(/.* (........ ..:..)$/, '\1').rstrip DateTime.strptime(date, '%Y%m%d %H:%M') end def version_url(url, distrib, arch) "#{url}distrib/#{distrib}/#{arch}/VERSION" end def get_date(url, distrib, arch) return parse_version(fetch_url(version_url(url, distrib, arch))) end def format_age(ref_time, time) return " X" unless time diff = ref_time - time cls = 'broken' if diff == 0 then cls = 'ok' elsif diff < 0.5 then cls = 'almost' elsif diff < 2 then cls = 'bad' end if cls == 'ok' then return "  " else return " #{time.strftime("%F %R")}" end end def print_output(archs_per_distro, mirrors, ref_times, times) puts " " puts "Last checked on #{Time.now.strftime("%F %R")}
" puts "
Up to dateLess than 12h oldLess than 2 days oldOld or broken
" puts "" puts "" archs_per_distro.each{|d, archs| nb_arches = archs.size puts " " } puts "" puts "" } } puts "" puts "" puts "" puts " " archs_per_distro.each{|d, archs| puts " " } } puts "" mirrors.each{|u| puts "" puts " " } puts "
" puts "Base directory#{d}
" archs_per_distro.each{|d, archs| puts " " archs.each{|a| puts " #{a}
Reference" puts " #{ref_times['base'].strftime("%F %R")}" archs.each{|a| puts " #{ref_times[d][a].strftime("%F %R")}
#{u}" puts format_age(ref_times['base'], times[u]['base']) archs_per_distro.each{|d, archs| puts " " archs.each{|a| puts format_age(ref_times[d][a], times[u][d][a]) } } puts "
" puts "" end # Defaults ref = 'http://repository.mageia.org/' archs_per_distro = { '8' => ['i586', 'x86_64', 'armv7hl', 'aarch64'], '7' => ['i586', 'x86_64', 'armv7hl', 'aarch64'], 'cauldron' => ['i586', 'x86_64', 'armv7hl', 'aarch64'] } parallel = 8 OptionParser.new {|opts| opts.banner = "Usage: #{$0} [options]" opts.on("--repository URL", "Reference repository. Default: #{ref}") { |url| ref = url } opts.on("--parallel n", Integer, "Max number of parallel connections. Default: #{parallel}") { |n| $parallel = n } opts.on("--output file", "Write output into given file. Default to STDOUT") { |f| $stdout.reopen(f, "w") } }.parse! # Get dates from the reference repository, and fail if some requested distros # or archs are missing ref_times = get_dates(ref, archs_per_distro, false) # Get the list of mirror URLs to check mirrors = get_mirrors workqueue = Queue.new times = {} # Create all the thread and have them loop on the work queue threads = (1..parallel).map{|n| Thread.new { loop do u = workqueue.pop break if u == :exit times[u] = get_dates(u, archs_per_distro) end } } # Push all mirrors into the queue mirrors.each{|u| workqueue << u } # Get all the threads to exit after all the work is done parallel.times{|i| workqueue << :exit } # Wait for the threads to exit threads.each{|t| t.join } # Generate output print_output(archs_per_distro, mirrors, ref_times, times)