blob: 05c0b9f1d49c2dee55f9c191aa5c2309f8fd239e [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 sys
import getopt
import shutil
import subprocess
from stat import *
from ducc_util import DuccUtil
from ducc_base import Properties
from ducc_base import Property
from ducc_base import find_ducc_home
from ducc_base import find_localhost
from ducc_base import which
from ducc import Ducc
class PostInstall():
def usage(self, msg):
if ( msg != None ):
print ' '.join(msg)
print "Usage:"
print " ducc_post_install [options]"
print " If no options prompts are given for expected parameters."
print ""
print "Options:"
print " [-n, --head-node] <ducc head node>"
print " This is the name of the host that will run the DUCC management processes."
print ""
print " [-k, --keystore] <webserver keystore password>"
print " This is the password to be used to establish the webserver's keystore."
print ""
print " [-j, --jvm] <path to java executable>"
print " This is the full path to java command to be used to start DUCC; e.g., /usr/bin/java"
print ""
print " [-h, -? --help]"
print " Prints this message."
print ""
sys.exit(1)
def fail(self, *msg):
print ' '.join(msg)
print "POST INSTALLATION FAILED"
sys.exit(1)
def warn(self, *msg):
print ''
print 'WARNING'
print 'WARNING', ' '.join(msg)
print 'WARNING'
print ''
def get_keystore_pw(self, prompt):
if ( self.keystore_pw == None ):
reply = ''
while ( reply == self.default_keystore_pw or reply == '' or reply == prompt):
reply = raw_input('Enter password for webserver keystore:')
if ( reply == self.default_keystore_pw):
print 'May not use the preconfigured password of', reply
if ( reply == prompt ):
print 'Please enter a different password', reply
else:
reply = self.keystore_pw
return reply
def create_keystore(self, keytool):
'''
CN - Common Name of the certificate owner
OU - Organizational Unit of the certificate owner
O - Organization to which the certificate owner belongs
L - Locality name of the certificate owner
S - State or province of the certificate owner
C - Country of the certificate owner
'''
keystore = self.DUCC_HOME + "/webserver/etc/keystore"
cmd = 'rm ' + keystore
os.system(cmd);
#/usr/bin/keytool
keystore_key = 'ducc.ws.port.ssl.pw'
self.default_keystore_prop = self.ducc_private_properties.get_property(keystore_key)
self.default_keystore_pw = self.default_keystore_prop.k
rc = 1
reply = ''
while ( rc != 0 ):
reply = self.get_keystore_pw(reply)
cmd = keytool
cmd += ' '
cmd += '-genkey'
cmd += ' '
cmd += '-noprompt'
cmd += ' '
cmd += '-alias jetty'
cmd += ' '
cmd += '-dname "CN=org.apache.uima.ducc, OU=uima.ducc, O=Apache, L=Wilmington, S=Delaware, C=USA"'
cmd += ' '
cmd += '-keyalg RSA'
cmd += ' '
cmd += '-validity 10000'
cmd += ' '
cmd += '-keystore ' + keystore
cmd += ' '
cmd += '-storepass '+ reply
cmd += ' '
cmd += '-keypass '+ reply
rc = os.system(cmd);
self.default_keystore_prop.v = reply
print 'keystore created as', reply
# Setup and verify amq
# make sure verify_ducc is sufficient - maybe move some checks to there?
def update_property(self, key, val, comment):
self.ducc_site_properties.put(key, val, [comment])
def get_java_bindir(self):
if ( self.path_to_java == None ):
self.path_to_java = which('java')
if ( self.path_to_java == None ):
reply = ''
while ( reply == '' ):
reply = raw_input('Enter full path to the Java executable: ')
else:
prompt = '[' + self.path_to_java + ']'
reply = raw_input('Enter full path to the Java executable: ' + prompt)
if ( reply != '' ):
self.path_to_java = reply
self.update_property('ducc.jvm', self.path_to_java, '# The full path to java')
print 'Initialized property "ducc.jvm" to', self.path_to_java
# We're going to do more checks here so we don't proceed with bogosities
proc = subprocess.Popen(self.path_to_java + ' -version', shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = []
for line in proc.stdout:
lines.append(line.strip())
proc.wait()
rc = proc.returncode
for line in lines:
print "JAVA: " + line
vertoks = lines[0].split()
self.java_version = vertoks[-1]
if ( rc != 0 ):
self.fail("Requested java at '" + self.path_to_java +"' cannot be run.")
return os.path.dirname(self.path_to_java)
def get_java_version(self):
return self.java_version
def get_java_keytool(self, bindir):
keytool = bindir + "/keytool"
if ( not os.path.exists(keytool) ):
self.fail("Cannot find keytool in ", bindir + '.', + "Is ducc.jvm configured correctly?")
return keytool
def check_nodes(self):
nodes = self.DUCC_HOME + "/resources/ducc.nodes"
self.mkbackup(nodes)
nf = open(nodes, 'w')
nf.write(self.localhost)
nf.close()
print "Initial", nodes, "created."
jdnodes = self.DUCC_HOME + "/resources/jobdriver.nodes"
self.mkbackup(jdnodes)
rn = open(jdnodes, "w")
rn.write(self.localhost)
rn.close()
print "Initial nodepool", jdnodes, "created.\n"
def merge_properties(self):
# first task, always, merge the properties so subsequent code can depend on their validity.
base_props = self.DUCC_HOME + '/resources/default.ducc.properties'
site_props = self.DUCC_HOME + '/resources/site.ducc.properties'
run_props = self.DUCC_HOME + '/resources/ducc.properties'
merger = self.DUCC_HOME + '/admin/ducc_props_manager'
CMD = [merger, '--merge', base_props, '--with', site_props, '--to', run_props]
CMD = ' '.join(CMD)
print 'Merging', base_props, 'with', site_props, 'into', run_props
os.system(CMD)
def setup_ducc_head(self):
if ( self.ducc_head == None ):
self.ducc_head = self.localhost
reply = raw_input('Enter hostname of DUCC head[' + self.ducc_head + ']')
if ( reply != '' ):
self.ducc_head = reply
self.update_property('ducc.head', self.ducc_head, "# ducc.head is the node where the main DUCC daemons run");
print "Ducc head is configured as", self.ducc_head, '\n'
def mkbackup(self, fn):
if ( os.path.exists(fn) ):
bak = fn + '.bak'
shutil.move(fn, bak)
print 'Existing', fn, 'moved to', bak
def verify_permissions(self):
# should have 755 permissions
spot_checked_directories = ['../bin', '../lib', '../resources' ]
# should have 755 permissions
spot_checked_execs = ['../bin/ducc_submit']
# should have 644 permissions
spot_checked_data = ['../lib/uima-ducc-cli.jar', '../resources/default.ducc.properties']
ret = True
for f in spot_checked_directories:
if ( not os.path.exists(f) ):
print 'ERROR: Directory', f, 'cannot be found.'
ret = false
continue
stat = os.stat(f)
mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
expected = oct(0755)
if ( mode != expected ):
print 'ERROR: Directory', f, 'has permissions', mode, 'expected', expected
ret = False
for f in spot_checked_execs:
if ( not os.path.exists(f) ):
print 'ERROR: File', f, 'cannot be found.'
ret = False
continue
stat = os.stat(f)
mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
expected = oct(0755)
if ( mode != expected ):
print 'ERROR: File', f, 'has permissions', mode, 'expected', expected
ret = False
for f in spot_checked_data:
if ( not os.path.exists(f) ):
print 'ERROR: File', f, 'cannot be found.'
ret = False
continue
stat = os.stat(f)
mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
expected = oct(0644)
if ( mode != expected ):
print 'ERROR: File', f, 'has permissions', mode, 'expected', expected
ret = False
return ret
def main(self, argv):
if ( not self.verify_permissions() ):
print '--------------------------------------------------------------------------------'
print 'Package verificaiton fails. Most likely cause is an unexpected UMASK unpacking the distribution.'
print 'To unpack the distribution your UMASK must be set to 022.'
print ''
print 'Example:'
print ''
print 'umask 022; tar -xf [distribution]'
print '--------------------------------------------------------------------------------'
sys.exit(1)
self.ducc_head = None
self.keystore_pw = None
self.path_to_java = None
try:
opts, args = getopt.getopt(argv, 'j:k:n:h?', ['jvm=', 'keystore=', 'head-node=', 'help'])
except:
self.usage("Invalid arguments " + ' '.join(argv))
for ( o, a ) in opts:
if o in ('-n', '--head-node'):
self.ducc_head = a
if o in ('-k', '--keystore'):
self.keystore_pw = a
if o in ('-j', '--jvm'):
self.path_to_java = a
elif o in ('-h', '-?', '--help'):
usage(None)
self.DUCC_HOME = find_ducc_home()
self.localhost = find_localhost()
print 'Using DUCC HOME:', self.DUCC_HOME, '\n'
resources = self.DUCC_HOME + '/resources'
self.site_properties_name = resources + '/site.ducc.properties'
self.ducc_properties = resources + '/ducc.properties'
self.mkbackup(self.site_properties_name)
self.mkbackup(self.ducc_properties)
print 'Python version:'
print sys.version
keystore_properties_name = self.DUCC_HOME + '/resources.private/ducc.private.properties'
self.ducc_private_properties = Properties()
self.ducc_private_properties.load(keystore_properties_name)
self.ducc_site_properties = Properties()
py_version = sys.version_info
if ( (py_version[0] != 2) or (py_version[1] < 4) ):
self.fail("\nPython must be installed at level 2.4 or higher.")
self.setup_ducc_head()
self.check_nodes()
# As of DUCC 2.0, always set here on installation
print 'ActiveMQ is automanaged on node ', self.localhost
statedir = self.DUCC_HOME + "/state"
logdir = self.DUCC_HOME + "/logs"
logwsdir = self.DUCC_HOME + "/logs/webserver"
historydir = self.DUCC_HOME + "/history"
if ( not os.path.exists(statedir) ):
os.mkdir(statedir)
if ( not os.path.exists(logdir) ):
os.mkdir(logdir)
if ( not os.path.exists(logwsdir) ):
os.mkdir(logwsdir)
if ( not os.path.exists(historydir) ):
os.mkdir(historydir)
# insure java is configured and installed
self.java_bindir = self.get_java_bindir()
self.keytool = self.get_java_keytool(self.java_bindir)
print 'Java version:', self.get_java_version()
print 'Java is verified.'
self.create_keystore(self.keytool)
print '\nWeb server keystore generated from ducc.properties'
ws_duccbook = self.DUCC_HOME + "/webserver/root/system.duccbook.html"
if ( not os.path.lexists(ws_duccbook) ):
duccbook = self.DUCC_HOME + "/docs/book.html"
os.symlink(duccbook, ws_duccbook)
print '\nDUCC book installed into webserver root\n'
# set up the local properties, required to build ducc_ling
self.ducc_private_properties.write(keystore_properties_name)
self.ducc_site_properties.write(self.site_properties_name)
self.merge_properties()
# Make duccling
rc = os.system(self.DUCC_HOME + '/admin/build_duccling')
if ( rc != 0 ):
print 'Could not build ducc_ling. Run the command'
print ' build_duccling'
print 'to complete the installation (it must run without error).'
sys.exit(1)
print 'Initial DUCC setup complete.'
if __name__ == "__main__":
os.environ['DUCC_POST_INSTALL'] = 'DUCC_POST_INSTALL'
postinstall = PostInstall()
postinstall.main(sys.argv[1:])