summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Legler <alex@a3li.li>2016-01-04 02:18:48 +0100
committerAlex Legler <alex@a3li.li>2016-01-04 02:18:48 +0100
commitfd7fa9102a50dedb2f1cbad4993fa2c7427407aa (patch)
tree7ca0f15a86f6a84599f076e01e8a27a8e22ec8d2
downloadmirror-toolkit-fd7fa9102a50dedb2f1cbad4993fa2c7427407aa.tar.gz
mirror-toolkit-fd7fa9102a50dedb2f1cbad4993fa2c7427407aa.tar.bz2
mirror-toolkit-fd7fa9102a50dedb2f1cbad4993fa2c7427407aa.zip
Add initial set of tools
- rsync-cat: rsync-to-stdout utility - gm-lag: displays the current lag of a mirror
-rw-r--r--.rubocop.yml23
-rwxr-xr-xbin/gm-lag27
-rwxr-xr-xbin/rsync-cat14
-rw-r--r--lib/mirror_toolkit.rb89
4 files changed, 153 insertions, 0 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..bc15b92
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,23 @@
+Style/AsciiComments:
+ Enabled: false
+
+Style/FormatString:
+ Enabled: false
+
+Style/FileName:
+ Enabled: false
+
+Style/Documentation:
+ Enabled: false
+
+Style/PerlBackrefs:
+ Enabled: false
+
+Metrics/LineLength:
+ Max: 120
+
+Metrics/MethodLength:
+ Max: 20
+
+Metrics/ModuleLength:
+ Max: 200
diff --git a/bin/gm-lag b/bin/gm-lag
new file mode 100755
index 0000000..ae81875
--- /dev/null
+++ b/bin/gm-lag
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+# Displays the number of seconds a mirror is lagging behind
+
+require 'optparse'
+require 'time'
+require 'uri'
+require 'net/http'
+require_relative '../lib/mirror_toolkit'
+
+options = {}
+OptionParser.new do |opts|
+ opts.on('-h', '--human', 'Display human times') { |v| options[:human] = v }
+ opts.on('-d', '--distfiles', 'Treat as distfiles mirror') { |v| options[:distfiles] = v }
+ opts.on('-r', '--rsync', 'Treat as rsync mirror') { |v| options[:rsync] = v }
+end.parse!
+
+abort '-d and -r are exclusive.' if options[:distfiles] && options[:rsync]
+
+lag = MirrorToolkit.get_lag(ARGV[0], options[:rsync] ? MirrorToolkit::TYPE_RSYNC : MirrorToolkit::TYPE_DISTFILES)
+
+if lag.nil?
+ puts 'unknown'
+elsif options[:human]
+ puts MirrorToolkit.humanize_seconds(lag)
+else
+ puts lag.to_i
+end
diff --git a/bin/rsync-cat b/bin/rsync-cat
new file mode 100755
index 0000000..e2b7c3c
--- /dev/null
+++ b/bin/rsync-cat
@@ -0,0 +1,14 @@
+#!/bin/bash
+# Prints an rsync file to stdout
+
+me=$(basename $0)
+TMPFILE=$(mktemp /tmp/${me}.XXXXXX) || exit 1
+trap "rm ${TMPFILE}" EXIT
+
+if [ -z "${1}" ]; then
+ echo "Usage: ${0} <rsync url>"
+ exit 1
+fi
+
+rsync -q "${1}" "${TMPFILE}" || exit 1
+cat "${TMPFILE}"
diff --git a/lib/mirror_toolkit.rb b/lib/mirror_toolkit.rb
new file mode 100644
index 0000000..cc53090
--- /dev/null
+++ b/lib/mirror_toolkit.rb
@@ -0,0 +1,89 @@
+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
+
+ # 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', 'ftp'
+ Net::HTTP.get(url)
+ 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