summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Admin/Compare.py
diff options
context:
space:
mode:
authorSol Jerome <sol.jerome@gmail.com>2012-03-24 11:20:07 -0500
committerSol Jerome <sol.jerome@gmail.com>2012-03-24 11:20:07 -0500
commitdab1d03d81c538966d03fb9318a4588a9e803b44 (patch)
treef51e27fa55887e9fb961766805fe43f0da56c5b9 /src/lib/Bcfg2/Server/Admin/Compare.py
parent5cd6238df496a3cea178e4596ecd87967cce1ce6 (diff)
downloadbcfg2-dab1d03d81c538966d03fb9318a4588a9e803b44.tar.gz
bcfg2-dab1d03d81c538966d03fb9318a4588a9e803b44.tar.bz2
bcfg2-dab1d03d81c538966d03fb9318a4588a9e803b44.zip
Allow to run directly from a git checkout (#1037)
Signed-off-by: Sol Jerome <sol.jerome@gmail.com>
Diffstat (limited to 'src/lib/Bcfg2/Server/Admin/Compare.py')
-rw-r--r--src/lib/Bcfg2/Server/Admin/Compare.py151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/lib/Bcfg2/Server/Admin/Compare.py b/src/lib/Bcfg2/Server/Admin/Compare.py
new file mode 100644
index 000000000..050dd69f8
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Admin/Compare.py
@@ -0,0 +1,151 @@
+import lxml.etree
+import os
+
+import Bcfg2.Server.Admin
+
+
+class Compare(Bcfg2.Server.Admin.Mode):
+ __shorthelp__ = ("Determine differences between files or "
+ "directories of client specification instances")
+ __longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin compare <file1> <file2>"
+ "\nbcfg2-admin compare -r <dir1> <dir2>")
+ __usage__ = ("bcfg2-admin compare <old> <new>\n\n"
+ " -r\trecursive")
+
+ def __init__(self, setup):
+ Bcfg2.Server.Admin.Mode.__init__(self, setup)
+ self.important = {'Path': ['name', 'type', 'owner', 'group', 'perms',
+ 'important', 'paranoid', 'sensitive',
+ 'dev_type', 'major', 'minor', 'prune',
+ 'encoding', 'empty', 'to', 'recursive',
+ 'vcstype', 'sourceurl', 'revision'],
+ 'Package': ['name', 'type', 'version', 'simplefile',
+ 'verify'],
+ 'Service': ['name', 'type', 'status', 'mode',
+ 'target', 'sequence', 'parameters'],
+ 'Action': ['name', 'timing', 'when', 'status',
+ 'command'],
+ 'PostInstall': ['name']
+ }
+
+ def compareStructures(self, new, old):
+ if new.tag == 'Independent':
+ bundle = 'Base'
+ else:
+ bundle = new.get('name')
+
+ identical = True
+
+ for child in new.getchildren():
+ if child.tag not in self.important:
+ print(" %s in (new) bundle %s:\n tag type not handled!" %
+ (child.tag, bundle))
+ continue
+ equiv = old.xpath('%s[@name="%s"]' %
+ (child.tag, child.get('name')))
+ if len(equiv) == 0:
+ print(" %s %s in bundle %s:\n only in new configuration" %
+ (child.tag, child.get('name'), bundle))
+ identical = False
+ continue
+ diff = []
+ if child.tag == 'Path' and child.get('type') == 'file' and \
+ child.text != equiv[0].text:
+ diff.append('contents')
+ attrdiff = [field for field in self.important[child.tag] if \
+ child.get(field) != equiv[0].get(field)]
+ if attrdiff:
+ diff.append('attributes (%s)' % ', '.join(attrdiff))
+ if diff:
+ print(" %s %s in bundle %s:\n %s differ" % (child.tag, \
+ child.get('name'), bundle, ' and '.join(diff)))
+ identical = False
+
+ for child in old.getchildren():
+ if child.tag not in self.important:
+ print(" %s in (old) bundle %s:\n tag type not handled!" %
+ (child.tag, bundle))
+ elif len(new.xpath('%s[@name="%s"]' %
+ (child.tag, child.get('name')))) == 0:
+ print(" %s %s in bundle %s:\n only in old configuration" %
+ (child.tag, child.get('name'), bundle))
+ identical = False
+
+ return identical
+
+ def compareSpecifications(self, path1, path2):
+ try:
+ new = lxml.etree.parse(path1).getroot()
+ except IOError:
+ print("Failed to read %s" % (path1))
+ raise SystemExit(1)
+
+ try:
+ old = lxml.etree.parse(path2).getroot()
+ except IOError:
+ print("Failed to read %s" % (path2))
+ raise SystemExit(1)
+
+ for src in [new, old]:
+ for bundle in src.findall('./Bundle'):
+ if bundle.get('name')[-4:] == '.xml':
+ bundle.set('name', bundle.get('name')[:-4])
+
+ identical = True
+
+ for bundle in old.findall('./Bundle'):
+ if len(new.xpath('Bundle[@name="%s"]' % (bundle.get('name')))) == 0:
+ print(" Bundle %s only in old configuration" %
+ bundle.get('name'))
+ identical = False
+ for bundle in new.findall('./Bundle'):
+ equiv = old.xpath('Bundle[@name="%s"]' % (bundle.get('name')))
+ if len(equiv) == 0:
+ print(" Bundle %s only in new configuration" %
+ bundle.get('name'))
+ identical = False
+ elif not self.compareStructures(bundle, equiv[0]):
+ identical = False
+
+ i1 = lxml.etree.Element('Independent')
+ i2 = lxml.etree.Element('Independent')
+ i1.extend(new.findall('./Independent/*'))
+ i2.extend(old.findall('./Independent/*'))
+ if not self.compareStructures(i1, i2):
+ identical = False
+
+ return identical
+
+ def __call__(self, args):
+ Bcfg2.Server.Admin.Mode.__call__(self, args)
+ if len(args) == 0:
+ self.errExit("No argument specified.\n"
+ "Please see bcfg2-admin compare help for usage.")
+ if '-r' in args:
+ args = list(args)
+ args.remove('-r')
+ (oldd, newd) = args
+ (old, new) = [os.listdir(spot) for spot in args]
+ old_extra = []
+ for item in old:
+ if item not in new:
+ old_extra.append(item)
+ continue
+ print("File: %s" % item)
+ state = self.__call__([oldd + '/' + item, newd + '/' + item])
+ new.remove(item)
+ if state:
+ print("File %s is good" % item)
+ else:
+ print("File %s is bad" % item)
+ if new:
+ print("%s has extra files: %s" % (newd, ', '.join(new)))
+ if old_extra:
+ print("%s has extra files: %s" % (oldd, ', '.join(old_extra)))
+ return
+ try:
+ (old, new) = args
+ return self.compareSpecifications(new, old)
+ except IndexError:
+ print(self.__call__.__doc__)
+ raise SystemExit(1)