aboutsummaryrefslogtreecommitdiff
path: root/rcynic
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2012-10-24 21:36:25 +0000
committerRob Austein <sra@hactrn.net>2012-10-24 21:36:25 +0000
commitdf97c0bd7052a5679e3a86a5f66ad1b2b5e6c173 (patch)
tree2781e71732414c1c297776817f1548152c3a3613 /rcynic
parentc4be735c645bdbcb86b2448899a3aa664d5e97df (diff)
rcynic-svn.
svn path=/branches/tk274/; revision=4787
Diffstat (limited to 'rcynic')
-rw-r--r--rcynic/Makefile.in5
-rw-r--r--rcynic/rcynic-svn.py190
2 files changed, 194 insertions, 1 deletions
diff --git a/rcynic/Makefile.in b/rcynic/Makefile.in
index 0ec6e301..0370f33a 100644
--- a/rcynic/Makefile.in
+++ b/rcynic/Makefile.in
@@ -23,7 +23,7 @@ abs_top_builddir = @abs_top_builddir@
host_os = @host_os@
-SCRIPTS = rcynic-text rcynic-html validation_status
+SCRIPTS = rcynic-text rcynic-html rcynic-svn validation_status
all: ${BIN} ${SCRIPTS}
@@ -73,6 +73,9 @@ rcynic-text: rcynic-text.py
rcynic-html: rcynic-html.py
${COMPILE_PYTHON}
+rcynic-svn: rcynic-svn.py
+ ${COMPILE_PYTHON}
+
validation_status: validation_status.py
${COMPILE_PYTHON}
diff --git a/rcynic/rcynic-svn.py b/rcynic/rcynic-svn.py
new file mode 100644
index 00000000..d17e20e1
--- /dev/null
+++ b/rcynic/rcynic-svn.py
@@ -0,0 +1,190 @@
+"""
+Archive rcynic output in a Subversion repository.
+"""
+
+# $Id$
+#
+# Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+import subprocess
+import argparse
+import datetime
+import fcntl
+import glob
+import os
+
+try:
+ from lxml.etree import ElementTree
+except ImportError:
+ from xml.etree.ElementTree import ElementTree
+
+
+mime_types = (
+ ("html", "application/xhtml+xml"),
+ ("cer", "application/pkix-cert"),
+ ("crl", "application/pkix-crl"),
+ ("mft", "application/rpki-manifest"),
+ ("mnf", "application/rpki-manifest"),
+ ("roa", "application/rpki-roa"),
+ ("gbr", "application/rpki-ghostbusters"))
+
+
+def run(*cmd, **kwargs):
+ """
+ Run a program, displaying timing data when appropriate.
+ """
+
+ t = datetime.datetime.utcnow()
+ subprocess.check_call(cmd, **kwargs)
+ if args.show_timing:
+ now = datetime.datetime.utcnow()
+ print now, (now - t), " ".join(cmd)
+
+
+def runxml(*cmd):
+ """
+
+ Run a program which produces XML output, displaying timing data when
+ appropriate and returning an ElementTree constructed from the
+ program's output.
+ """
+ t = datetime.datetime.utcnow()
+ p = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+ x = ElementTree(file = p.stdout)
+ s = p.wait()
+ if s:
+ raise subprocess.CalledProcessError(s, cmd[0])
+ if args.show_timing:
+ now = datetime.datetime.utcnow()
+ print now, (now - t), " ".join(cmd)
+ return x
+
+
+# Main program.
+
+parser = argparse.ArgumentParser(description = __doc__)
+
+parser.add_argument("--show_timing", action = "store_true", help = \
+ """
+ Show timing data on programs we run.
+ """)
+
+parser.add_argument("--verbatim", action = "store_true", help = \
+ """
+ Whether to archive rcynic's data output exactly as
+ rcynic writes it or map it into a directory
+ structure which makes more sense when used with
+ Subversion. True means archive exactly as rcynic
+ writes it, interpreting file and directory names
+ as rsync would, transient directories and all.
+ False means map the current authenticated/ tree in
+ rcynic's output to a stable authenticated/ subtree
+ in the subversion repository, with file and
+ directory anmes from the command line shorted to
+ their last component.
+ """)
+
+parser.add_argument("--lockfile", default = "rcynic-svn.lock", help = \
+ """
+ Lock file to to prevent multiple copies of this
+ program (eg, running under cron) from stepping on
+ each other while modifying the working directory.
+ """)
+
+parser.add_argument("files_to_archive", nargs = "*", help = \
+ """
+ Files to archive using Subversion. If omitted, we
+ assume that some other process has already
+ modified the Subversion working directory.
+ """)
+
+parser.add_argument("working_directory", help = \
+ """
+ Subversion working directory to use (must already
+ exist).
+ """)
+
+args = parser.parse_args()
+
+if args.show_timing:
+ t0 = datetime.datetime.utcnow()
+ print t0, "Starting"
+
+# Lock out other instances of this program. We may want some more
+# sophsiticated approach when combining this with other programs, but
+# this should minimize the risk of multiple copies of this program
+# trying to modify the same subversion working directory at the same
+# time and messing each other up. We leave the lock file in place
+# because doing so removes a potential race condition.
+
+lock = os.open("cronjob.lock", os.O_RDONLY | os.O_CREAT | os.O_NONBLOCK, 0666)
+fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+# Make sure working tree is up to date.
+
+run("svn", "update", "--quiet", args.working_directory)
+
+# Copy rcynic's output as appropriate.
+
+if args.files_to_archive:
+
+ if args.verbatim:
+ cmd = ["rsync", "--archive", "--quiet", "--delete"]
+ cmd.extend(args.files_to_archive)
+ cmd.append(args.working_directory)
+ run(*cmd)
+
+ else:
+ for src in args.files_to_archive:
+ cmd = ["rsync", "--archive", "--quiet", "--delete", "--copy-links"]
+ cmd.append(src.rstrip("/"))
+ cmd.append(args.working_directory.rstrip("/") + "/")
+ run(*cmd)
+
+# Ask Subversion to add any new files, trying hard to get the MIME
+# types right.
+
+cmd = ["svn", "add", "--quiet", "--force", "--auto-props"]
+
+for fn2, mime_type in mime_types:
+ cmd.append("--config-option")
+ cmd.append("config:auto-props:*.%s=svn:mime-type=%s" % (fn2, mime_type))
+
+cmd.append(".")
+
+run(*cmd, cwd = args.working_directory)
+
+# Parse XML version of Subversion's status output to figure out what
+# files have been deleted, and tell Subversion that we deleted them
+# intentionally.
+
+missing = sorted(entry.get("path")
+ for entry in runxml("svn", "status", "--xml", args.working_directory).find("target").findall("entry")
+ if entry.find("wc-status").get("item") == "missing")
+deleted = []
+
+for path in missing:
+ if not any(path.startswith(r) for r in deleted):
+ run("svn", "delete", "--quiet", path)
+ deleted.append(path + "/")
+
+# Commit our changes and update the working tree.
+
+run("svn", "commit", "--quiet", "--message", "Auto update.", args.working_directory)
+run("svn", "update", "--quiet", args.working_directory)
+
+if args.show_timing:
+ now = datetime.datetime.utcnow()
+ print now, now - t0, "total runtime"