| #!/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.2.3 |
| oname = 'ducc.database.host' |
| nname = 'ducc.database.host.list' |
| 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) |
| |
| # 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:]) |