Compare what is in the git tree with what is deployed on the instrument
This commit is contained in:
338
site_ansto/instrument/compareSICS.py
Executable file
338
site_ansto/instrument/compareSICS.py
Executable file
@@ -0,0 +1,338 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: ts=8 sts=4 sw=4 et ai si
|
||||||
|
# Author: Douglas Clowes 2013 (dcl@ansto.gov.au)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Program to compare what *is* deployed with *was* deployed.
|
||||||
|
# It uses the file FILEMAP.TXT which can be created after the deployment.
|
||||||
|
# The location of FILEMAP.TXT may be different to the deployment path.
|
||||||
|
#
|
||||||
|
# For example, to compare what is on echidna (without a FILEMAP.TXT file) with
|
||||||
|
# what existed at the time it was deployed:
|
||||||
|
#
|
||||||
|
# sshfs -o ro echidna@ics2-echidna.nbi.ansto.gov.au:/usr/local/sics mnt
|
||||||
|
# git checkout git checkout 5579f3e
|
||||||
|
# git checkout merge-replace -- deploySICS.sh
|
||||||
|
# ./deploySICS.sh -n echidna localhost
|
||||||
|
# python compare_deploy.py --manifest=FILEMAP.TXT mnt/newserver/
|
||||||
|
# ...
|
||||||
|
# fusermount -u mnt
|
||||||
|
#
|
||||||
|
#Notes:
|
||||||
|
# "sshfs -o ro" mount the echidna tree read only
|
||||||
|
# the 5579f3e is from the DEPLOYMENT.TXT file on echidna (mnt/server/DEPLOYMENT.TXT)
|
||||||
|
# The first checkout is to get what *was* deployed to echidna in the working directory
|
||||||
|
# The second checkout is just to get a deploySICS.sh that produces FILEMAP.TXT in staging
|
||||||
|
# The "./deploySICS.sh -n" does NOT deploy anywhere but creates FILEMAP.TXT in staging
|
||||||
|
# The compare_deploy.py uses FILEMAP.TXT from the staging tree produced by the "./deploySICS.sh -n"
|
||||||
|
# The compare_deploy.py path is to what *is* deployed on echidna
|
||||||
|
# The untracked files may be manually deployed and missing from MANIFEST.TXT
|
||||||
|
# The untracked files may be new, modified, test or junk
|
||||||
|
# You can then "gvim -d " then cut/paste lines emitted under "Changed Files:" (or diff)
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import difflib
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def load_manifest(theManifest):
|
||||||
|
'''
|
||||||
|
This function loads the manifest file ("FILEMAP.TXT") which consists of a
|
||||||
|
series of shell variables followed by the output of various 'cp -v'
|
||||||
|
commands and produces two dictionaries, one for the assignments (shell variables)
|
||||||
|
and one for the filemap (file associations). The shell variables give
|
||||||
|
context to the file mapping.
|
||||||
|
'''
|
||||||
|
# get the lines from the manifest file with the newline removed
|
||||||
|
contents = [j[:-1] for j in open(theManifest).readlines()]
|
||||||
|
|
||||||
|
# divide the lines into assignments and filemaps
|
||||||
|
assignments = []
|
||||||
|
filemaps = []
|
||||||
|
for line in contents:
|
||||||
|
if "->" in line:
|
||||||
|
filemaps.append(line)
|
||||||
|
else:
|
||||||
|
assignments.append(line)
|
||||||
|
|
||||||
|
# parse the assignments and create the assignment map which maps shell variables (left)
|
||||||
|
# from the deploy script to their value at the time of deployment
|
||||||
|
assignment_map = {}
|
||||||
|
for line in assignments:
|
||||||
|
left, right = line.split("=")
|
||||||
|
assignment_map[left] = right
|
||||||
|
|
||||||
|
# parse the file associations and create the file map which maps the files in the
|
||||||
|
# destination tree (right) to the file in the source tree from which they came (left)
|
||||||
|
filemap_map = {}
|
||||||
|
for line in filemaps:
|
||||||
|
if line[0] in "`'":
|
||||||
|
val = re.sub(r"^[`'](.*)[`'] -> [`'](.*)[`'].*", r"\1|\2", line)
|
||||||
|
else:
|
||||||
|
val = re.sub(r"^(.*) -> (.*)", r"\1|\2", line)
|
||||||
|
if val == line:
|
||||||
|
# unexpected, probably an error
|
||||||
|
print "Val:", val
|
||||||
|
else:
|
||||||
|
left, right = val.split("|")
|
||||||
|
if (left + "|" + right) != val:
|
||||||
|
# unexpected, probably an error
|
||||||
|
print "LR:", left, right
|
||||||
|
else:
|
||||||
|
# use the assignment map to remove the staging (temporary) directory
|
||||||
|
# from the front of the filename and make it independent of that
|
||||||
|
# and reduce leading slashes to one only
|
||||||
|
if right.startswith(assignment_map["TEMPDIR"]):
|
||||||
|
right = right[len(assignment_map["TEMPDIR"]):]
|
||||||
|
while right.startswith("//"):
|
||||||
|
right = right[1:]
|
||||||
|
# use the assignment map to remove the destination directory
|
||||||
|
# from the front of the filename and make it independent of that
|
||||||
|
# and remove leading slashes
|
||||||
|
if right.startswith(assignment_map["DESTDIR"]):
|
||||||
|
right = right[len(assignment_map["DESTDIR"]):]
|
||||||
|
while right.startswith("/"):
|
||||||
|
right = right[1:]
|
||||||
|
prefix = "newserver/"
|
||||||
|
if right.startswith(prefix):
|
||||||
|
right = right[len(prefix):]
|
||||||
|
else:
|
||||||
|
# unexpected, probably an error
|
||||||
|
print "LR+:", left, right
|
||||||
|
# Note: map[destination] = source
|
||||||
|
filemap_map[right] = left
|
||||||
|
|
||||||
|
return (assignment_map, filemap_map)
|
||||||
|
|
||||||
|
def load_dir(theDir):
|
||||||
|
'''
|
||||||
|
Walk the directory tree and populate a double map with the filename and directory in which it was found
|
||||||
|
'''
|
||||||
|
myDirs = {}
|
||||||
|
theLen = len(theDir) + 1
|
||||||
|
for root, dirs, files in os.walk(theDir):
|
||||||
|
for file in files:
|
||||||
|
# Don't even put these files into the map
|
||||||
|
if file.endswith((".swp", ".swo", ".hdf", "~", ".bck", ".pyc")):
|
||||||
|
continue
|
||||||
|
if file.startswith(("core.", "SICServer")):
|
||||||
|
continue
|
||||||
|
if not file in myDirs:
|
||||||
|
myDirs[file] = {}
|
||||||
|
myDirs[file][root[theLen:]] = {}
|
||||||
|
return myDirs
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
default_dir = "/usr/local/TEST_SICS/taipan/nbi/sics/taipan"
|
||||||
|
verbose = False
|
||||||
|
parser = argparse.ArgumentParser(description = "Compare a deployed instrument tree with a repository")
|
||||||
|
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="emit tables for debugging")
|
||||||
|
parser.add_argument("--link", dest="link", action="store_true", help="link deployed files to /tmp tree")
|
||||||
|
parser.add_argument("--copy", dest="copy", action="store_true", help="copy deployed files to /tmp tree")
|
||||||
|
parser.add_argument("-m", "--manifest", dest="manifest", help="specify another manifest directory")
|
||||||
|
parser.add_argument("path", nargs="?", default = default_dir, help="directory containing FILEMAP.TXT")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.verbose:
|
||||||
|
verbose = True
|
||||||
|
default_dir = os.path.abspath(args.path)
|
||||||
|
if default_dir.endswith("FILEMAP.TXT"):
|
||||||
|
default_dir = os.path.dirname(default_dir)
|
||||||
|
if args.manifest:
|
||||||
|
temp_dir = args.manifest
|
||||||
|
if temp_dir.endswith("FILEMAP.TXT"):
|
||||||
|
temp_dir = os.path.dirname(temp_dir)
|
||||||
|
default_manifest = os.path.join(temp_dir, "FILEMAP.TXT")
|
||||||
|
else:
|
||||||
|
default_manifest = os.path.join(default_dir, "FILEMAP.TXT")
|
||||||
|
root_dir = os.path.dirname(default_dir)
|
||||||
|
print "path:", args.path
|
||||||
|
print "dest:", default_dir
|
||||||
|
print "root:", root_dir
|
||||||
|
print "--manifest:", args.manifest
|
||||||
|
print "Manifest:", default_manifest
|
||||||
|
|
||||||
|
amap, fmap = load_manifest(default_manifest)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "Assignments:"
|
||||||
|
for key in sorted(amap):
|
||||||
|
print " ", key, "=", amap[key]
|
||||||
|
|
||||||
|
print "Contents:"
|
||||||
|
for key in sorted(fmap):
|
||||||
|
print " ", key, "=", fmap[key]
|
||||||
|
|
||||||
|
gmap = load_dir(default_dir)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "Target:"
|
||||||
|
for key in sorted(gmap):
|
||||||
|
print " ", key
|
||||||
|
for entry in sorted(gmap[key]):
|
||||||
|
print " ", entry
|
||||||
|
|
||||||
|
print "Untracked Files:"
|
||||||
|
for key in sorted(gmap):
|
||||||
|
if key.endswith((".swp", ".swo", ".hdf", "~", ".bck", ".pyc")):
|
||||||
|
continue
|
||||||
|
if key.startswith(("core.", "SICServer")):
|
||||||
|
continue
|
||||||
|
for entry in sorted(gmap[key]):
|
||||||
|
if entry.endswith(("/data", "/log")):
|
||||||
|
continue
|
||||||
|
if "/data/" in entry:
|
||||||
|
continue
|
||||||
|
if os.path.join(entry, key) not in fmap:
|
||||||
|
#print "Looking in fmap for", os.path.join(entry, key)
|
||||||
|
print os.path.join(default_dir, os.path.join(entry, key))
|
||||||
|
|
||||||
|
print "Changed Files:"
|
||||||
|
for key in sorted(gmap):
|
||||||
|
if key.endswith((".swp", ".swo", ".hdf", "~", ".bck", ".pyc")):
|
||||||
|
continue
|
||||||
|
if key.startswith(("core.", "SICServer")):
|
||||||
|
continue
|
||||||
|
for entry in sorted(gmap[key]):
|
||||||
|
target = os.path.join(entry, key)
|
||||||
|
#print "Looking in fmap for", os.path.join(entry, key)
|
||||||
|
if target in fmap:
|
||||||
|
source = os.path.join(amap["SRCDIR"], fmap[target])
|
||||||
|
source = os.path.join(os.getenv("PWD"), fmap[target])
|
||||||
|
destin = os.path.join(default_dir, target)
|
||||||
|
if not (os.path.exists(source) and os.path.exists(destin)):
|
||||||
|
print " Compare:", source, destin
|
||||||
|
if not os.path.exists(source):
|
||||||
|
print " ", source, "does not exist"
|
||||||
|
if not os.path.exists(destin):
|
||||||
|
print " ", destin, "does not exist"
|
||||||
|
continue
|
||||||
|
delta = list(difflib.unified_diff(open(source).readlines(), open(destin).readlines()))
|
||||||
|
delta_len = len(delta)
|
||||||
|
if delta_len > 0:
|
||||||
|
if verbose:
|
||||||
|
print delta
|
||||||
|
print source, destin, "#%d" % delta_len
|
||||||
|
|
||||||
|
if args.link:
|
||||||
|
print "Linking Files:"
|
||||||
|
count = 0
|
||||||
|
lines = []
|
||||||
|
for key in sorted(gmap):
|
||||||
|
if key.endswith((".swp", ".swo", ".hdf", "~", ".bck", ".pyc")):
|
||||||
|
continue
|
||||||
|
if key.startswith(("core.", "SICServer")):
|
||||||
|
continue
|
||||||
|
for entry in sorted(gmap[key]):
|
||||||
|
target = os.path.join(entry, key)
|
||||||
|
#print "Looking in fmap for", os.path.join(entry, key)
|
||||||
|
if target in fmap:
|
||||||
|
source = os.path.join(amap["SRCDIR"], fmap[target])
|
||||||
|
source = os.path.join(os.getenv("PWD"), fmap[target])
|
||||||
|
destin = os.path.join(default_dir, target)
|
||||||
|
if not (os.path.exists(source) and os.path.exists(destin)):
|
||||||
|
print " Linking:", source, destin
|
||||||
|
if not os.path.exists(source):
|
||||||
|
print " ", source, "does not exist"
|
||||||
|
if not os.path.exists(destin):
|
||||||
|
print " ", destin, "does not exist"
|
||||||
|
continue
|
||||||
|
lines.append((destin, fmap[target]))
|
||||||
|
if len(lines) > 0:
|
||||||
|
tgt = "/tmp/link_%s" % str(os.getpid())
|
||||||
|
if (os.path.exists(tgt)):
|
||||||
|
for root, disr, files in os.walk(tgt, topdown=False):
|
||||||
|
for name in files:
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
|
for name in dirs:
|
||||||
|
os.rmdir(os.path.join(root, name))
|
||||||
|
os.mkdir(tgt)
|
||||||
|
for line in lines:
|
||||||
|
source = line[0]
|
||||||
|
destin = os.path.abspath(os.path.join(tgt, line[1]))
|
||||||
|
if not destin.startswith(tgt):
|
||||||
|
print "Cannot link: %s to %s" % (source, destin)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(destin))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if verbose:
|
||||||
|
cmd = "ln -s %s %s" % (source, destin)
|
||||||
|
print cmd
|
||||||
|
if os.path.exists(destin):
|
||||||
|
delta = list(difflib.unified_diff(open(source).readlines(), open(destin).readlines()))
|
||||||
|
if len(delta) == 0:
|
||||||
|
continue
|
||||||
|
print "Duplicate Link:", destin
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.symlink(source, destin)
|
||||||
|
count += 1
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
print "Links (%d) in: %s" % (count, tgt)
|
||||||
|
|
||||||
|
|
||||||
|
if args.copy:
|
||||||
|
print "Copying Files:"
|
||||||
|
count = 0
|
||||||
|
lines = []
|
||||||
|
for key in sorted(gmap):
|
||||||
|
if key.endswith((".swp", ".swo", ".hdf", "~", ".bck", ".pyc")):
|
||||||
|
continue
|
||||||
|
if key.startswith(("core.", "SICServer")):
|
||||||
|
continue
|
||||||
|
for entry in sorted(gmap[key]):
|
||||||
|
target = os.path.join(entry, key)
|
||||||
|
#print "Looking in fmap for", os.path.join(entry, key)
|
||||||
|
if target in fmap:
|
||||||
|
source = os.path.join(amap["SRCDIR"], fmap[target])
|
||||||
|
source = os.path.join(os.getenv("PWD"), fmap[target])
|
||||||
|
destin = os.path.join(default_dir, target)
|
||||||
|
if not (os.path.exists(source) and os.path.exists(destin)):
|
||||||
|
print " Copying:", source, destin
|
||||||
|
if not os.path.exists(source):
|
||||||
|
print " ", source, "does not exist"
|
||||||
|
if not os.path.exists(destin):
|
||||||
|
print " ", destin, "does not exist"
|
||||||
|
continue
|
||||||
|
lines.append((destin, fmap[target]))
|
||||||
|
if len(lines) > 0:
|
||||||
|
tgt = "/tmp/copy_%s" % str(os.getpid())
|
||||||
|
if (os.path.exists(tgt)):
|
||||||
|
for root, disr, files in os.walk(tgt, topdown=False):
|
||||||
|
for name in files:
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
|
for name in dirs:
|
||||||
|
os.rmdir(os.path.join(root, name))
|
||||||
|
os.mkdir(tgt)
|
||||||
|
for line in lines:
|
||||||
|
source = os.path.abspath(line[0])
|
||||||
|
destin = os.path.abspath(os.path.join(tgt, line[1]))
|
||||||
|
if not destin.startswith(tgt):
|
||||||
|
print "Cannot copy: %s to %s" % (source, destin)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(destin))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if verbose:
|
||||||
|
cmd = "cp -p %s %s" % (source, destin)
|
||||||
|
print cmd
|
||||||
|
if os.path.exists(destin):
|
||||||
|
delta = list(difflib.unified_diff(open(source).readlines(), open(destin).readlines()))
|
||||||
|
if len(delta) == 0:
|
||||||
|
continue
|
||||||
|
print "Duplicate Copy:", destin
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
shutil.copyfile(source, destin)
|
||||||
|
shutil.copystat(source, destin)
|
||||||
|
count += 1
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
print "Copies (%d) in: %s" % (count, tgt)
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user