blob: 9bf527cabd2fa3da9981a5e96f249a7ded40373a [file] [log] [blame]
#!/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:])