#!/usr/bin/env python # ----------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ----------------------------------------------------------------------- import os import re import sys import time import getopt from ducc_util import DuccUtil # sets the sys.path so we can find stuff from properties import Properties from properties import Property class DuccPropManager: def __init__(self): # simple bootstrap to establish DUCC_HOME and to set the python path so it can # find the common code in DUCC_HOME/admin # Infer DUCC_HOME from our location - no longer use a (possibly inaccurate) environment variable me = os.path.abspath(__file__) ndx = me.rindex('/') ndx = me.rindex('/', 0, ndx) self.DUCC_HOME = me[:ndx] # split from 0 to ndx def merge(self, file1, file2, file3): ''' Merge "file1" and "file2" to produce a merged "file3". ''' file1_props = Properties() file1_props.load(file1) file2_props = Properties() file2_props.load(file2) file3_props = Properties() # Check for a site specification override of the property renamed in 2.1.0 oname = 'ducc.threads.limit' nname = 'ducc.job.max.pipelines.count' prop = file2_props.get_property(oname) if prop != None: prop.c.append('# RENAMED by merger ' + time.strftime('%c') + ' old name: ' + prop.k) prop.k = nname file2_props.put_property(prop) file2_props.delete(oname) # first pass, create merged props with stuff in base props file updated with delta for k in file1_props.get_keys(): base = file1_props.get_property(k) upd = file2_props.get_property(k) if ( upd == None ): file3_props.put_property(base) else: upd.c.append('# OVERRIDE by merger ' + time.strftime('%c') + ' old value: ' + base.v) file3_props.put_property(upd) file2_props.delete(k) # everything left in delta is new stuff for k in file2_props.get_keys(): upd = file2_props.get_property(k) upd.c.append('# INSERT by merger ' + time.strftime('%c')) file3_props.put_property(upd) # automagically fix // in path to java prop = file3_props.get_property('ducc.jvm') prop.v = prop.v.replace('//', '/') file3_props.put_property(prop) file3_props.write(file3) def delta(self, left, right, delta): ''' Compare "left" to "right" to produce a "delta". ''' left_props = Properties() left_props.load(left) right_props = Properties() right_props.load(right) delta_props = Properties() for k in left_props.get_keys(): r = left_props.get_property(k) if ( r == None ): continue # left only, 'master' no need to delta it l = left_props.get_property(k) r = right_props.get_property(k) if ( r != None and l.v != r.v ): delta_props.put_property(r) # no match, save difference (right) in delta right_props.delete(k) # shrink left to see what's left over for k in right_props.get_keys(): delta_props.put_property(right_props.get_property(k)) delta_props.write(delta) def usage(self, *msg): if ( msg[0] != None ): print '' print ' '.join(msg) print '' print 'ducc_prop_manager has two functions:' print ' 1. Merge a local properties file with a default file to produce a merged ducc.properties.' print ' 2. Compare two properties files to create a local delta.' print '' print ' The merge and delta operations are inverse operations.' print '' print ' Comments and the structure of the default file are preserved whenever possible.' print '' print ' If the full path name to a file is not given, it is must reside within' print' $DUCC_HOME/resources' print '' print "Usage:" print ' ducc_prop_manager --merge file1 --with file2 --to file3' print ' ducc_prop_manager --delta file1 --with file2 --to file3' print '' print 'Options' print ' -m, --merge file1' print ' -d, --delta file1' print ' This is the base properties file, usually the unmodified file provided with the' print ' DUCC distribution.' print '' print ' If --merge is specified, the output file (file3) is the merger of file1 and file2' print '' print ' If --delta is specified, the output file (file3) is the delta of file1 and file2' print '' print ' -w, --with file2' print ' This file is either merged or difference with file1 to produce the result in file3' print '' print ' -t, --to file3' print ' This is the result of the merge or delta operation' print '' print 'Examples:' print ' Update your ducc.properties from the default properties and your site.ducc.properties:' print ' ducc_props_manager --merge default.ducc.properties --with site.ducc.properties --to ducc.properties' print '' print ' Create a new site.ducc.properties by differencing the default properties and ducc.properties' print ' ducc_props_manager --delta default.ducc.properties --with ducc.properties --to site.ducc.properties' sys.exit(1); def main(self, argv): action = None file1 = None file2 = None file3 = None try: opts, args = getopt.getopt(argv, 'm:w:t:h?', ['delta=', 'merge=', 'with=', 'to=', 'help']) except: self.usage('Invalid arguments', ' '.join(argv)) for ( o, a ) in opts: if o in ( '-m', '--merge' ): action = 'merge' file1 = a elif o in ( '-d', '--delta' ): action = 'delta' file1 = a elif o in ( '-w', '--with' ): file2 = a elif o in ( '-t', '--to' ): file3 = a elif o in ('-h', '-?', '--help'): self.usage(None); if ( action == None or file1 == None or file2 == None or file3 == None ): self.usage("Insufficient arguemnts. All arguments are required.") if ( not ( file1.startswith('/') or file1.startswith('.') ) ): file1 = '/'.join([self.DUCC_HOME, 'resources', file1]) if ( not ( file2.startswith('/') or file2.startswith('.') ) ): file2 = '/'.join([self.DUCC_HOME, 'resources', file2]) if ( not ( file3.startswith('/') or file3.startswith('.') ) ): file3 = '/'.join([self.DUCC_HOME, 'resources', file3]) if ( action == 'merge' ): self.merge(file1, file2, file3) elif ( action == 'delta'): self.delta(file1, file2, file3) else: self.usage('Invalid action:', action, 'must be --delta or --merge') if __name__ == "__main__": mgr = DuccPropManager() mgr.main(sys.argv[1:])