Source code for almir.scripts.configure_deploy
"""Dirty script to output buildout.cfg, but it does the job.
"""
import os
import getpass
import socket
import subprocess
import readline
import time
readline # pyflakes: we import readline and raw_input has history!
import pytz
import sqlalchemy
from almir.lib.bconsole import BConsole
ROOT = os.path.join(os.path.dirname(__file__), '..', '..')
OUTPUT = os.path.join(ROOT, 'buildout.cfg')
TEMPLATE = """
[buildout]
extends = buildout.d/production.cfg
[almir]
# define sql database connection as specified in
# http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
#sqla_engine_url = sqlite:///%%(here)s/bacula.db
#sqla_engine_url = postgresql+pg8000://bacula:<password>@hostname/<database>
#sqla_engine_url = mysql+mysqlconnector://bacula:<password>@hostname/<database>
sqla_engine_url = %(engine)s
# your timezone from TZ* column in table https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# if not set, defaults to system timezone
#timezone = Europe/Ljubljana
timezone = %(timezone)s
# configure http daemon
host = %(host)s
port = %(port)s
# bconsole director parameters
director_name = %(director_name)s
director_port = %(director_port)s
director_address = %(director_address)s
director_password = %(director_password)s
"""
[docs]def validate_open_port(v):
validate_int(v)
# python2.6 will overflow port integer if it is too big
if int(v) not in xrange(0, 65536):
raise ValueError('Cannot listen on port %s: port must be 0-65535.' % v)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind(('', int(v)))
# port is open
except Exception, e:
raise ValueError('Cannot listen on port %s: %s' % (v, e))
finally:
s.close()
[docs]def validate_timezone(v):
try:
pytz.timezone(v)
except:
raise ValueError('Invalid timezone: %s' % v)
[docs]def validate_int(v):
try:
int(v)
except ValueError:
raise ValueError('Must be an integer!')
[docs]def validate_engine(v):
print 'Connecting to catalog database to verify configuration ...'
try:
engine = None
engine = sqlalchemy.create_engine(v)
if 'client' not in map(lambda e: e.lower(), engine.table_names()):
raise ValueError('Connection string has wrong parameters (could not connect to catalog database)') # PRAGMA: no cover
except:
if engine and engine.dialect.name == 'sqlite': # PRAGMA: no cover
print
print 'WARNING: Using sqlite, database needs to be readable by %s. Fix is usually:' % getpass.getuser()
print '$ sudo gpassword -a %s bacula' % getpass.getuser()
print 'You will need to relogin and restart install procedure, since permission are not updated on the fly'
print
# TODO: display file permissions
raise
print 'OK!'
[docs]def ask_question(question, default=None, validator=None, func=raw_input):
good_answer = None
while good_answer is None:
answer = func("--> " + question)
if validator and answer:
try:
validator(answer)
except Exception, e:
print ' Try again: ' + str(e)
continue
if answer:
good_answer = answer
elif not answer and default is not None:
good_answer = default
elif not answer and default is None:
continue
# add newline
print
return good_answer
[docs]def main():
"""Entry point of this script"""
print '\n\nConfiguring almir ... If you want to use the default value (where possible), press enter.\n'
options = {}
options['host'] = ask_question('Host to listen on (default: 127.0.0.1): ', default='127.0.0.1')
options['port'] = ask_question('Port to listen on (default: 2500): ', default='2500', validator=validate_open_port)
print 'Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
print 'For example:'
print ' postgresql+pg8000://<user>:<password>@<hostname>/<database>'
print ' mysql+mysqlconnector://<user>:<password>@<hostname>/<database>'
print ' sqlite:////var/lib/bacula/bacula.db'
print ' sqlite:///bacula.db'
print
options['engine'] = ask_question('SQL connection string: ', validator=validate_engine)
print 'Timezone of director in format of TZ* column in table https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'
print 'For example:'
print ' Europe/Ljubljana'
print ' CET'
print
try:
default_timezone = " (default: %s)" % pytz.timezone(time.tzname[0])
except:
default_timezone = ''
options['timezone'] = ask_question('Timezone%s' % default_timezone, default='', validator=validate_timezone)
# TODO: in future we may extract this from bconsole config file?
print 'Almost finished, we just need bconsole parameters to connect to director!'
print 'Normally you will find needed information in /etc/bacula/bconsole.conf'
print
try:
output = subprocess.Popen(['which', 'bconsole'], stdout=subprocess.PIPE).communicate()[0].strip()
except subprocess.CalledProcessError:
print 'WARNING: bconsole command is not executable from current user!'
else:
if not os.access(output, os.X_OK):
print 'WARNING: bconsole command is not executable from current user!'
bconsole_running = False
while not bconsole_running:
options['director_name'] = ask_question('Name of director to connect to (default: localhost-dir): ', default='localhost-dir')
options['director_address'] = ask_question('Address of director to connect to (default: localhost): ', default='localhost')
options['director_port'] = ask_question('Port of director to connect to (default: 9101): ', default='9101', validator=validate_open_port)
options['director_password'] = ask_question('Password of director to connect to: ', func=getpass.getpass)
print 'Connecting director with bconsole to verify configuration ...'
# need a way to generate bconsole config before running this
with BConsole.from_temp_config(name=options['director_name'],
address=options['director_address'],
port=options['director_port'],
password=options['director_password']) as bconsole:
if bconsole.is_running():
print 'OK!'
bconsole_running = True
else:
print 'ERROR: Could not connect to director %s, verify configuration and try again!' % options['director_name'] # PRAGMA: no cover
with open(OUTPUT, 'w') as f:
f.write(TEMPLATE % options)
print
print 'Written to %s.' % os.path.realpath(OUTPUT)
if __name__ == '__main__':
main() # PRAGMA: no cover
# TODO: add notes how to proceed if we get an exception