seafile_ynh/sources/seafile-server-3.1.1/setup-seafile-mysql.py
2014-08-06 22:42:37 -04:00

1258 lines
42 KiB
Python

#coding: UTF-8
'''This script would guide the seafile admin to setup seafile with MySQL'''
import sys
import os
import time
import re
import shutil
import glob
import subprocess
import hashlib
import getpass
import uuid
import warnings
import MySQLdb
from ConfigParser import ConfigParser
try:
import readline # pylint: disable=W0611
except ImportError:
pass
SERVER_MANUAL_HTTP = 'https://github.com/haiwen/seafile/wiki'
class Utils(object):
'''Groups all helper functions here'''
@staticmethod
def welcome():
'''Show welcome message'''
welcome_msg = '''\
-----------------------------------------------------------------
This script will guide you to setup your seafile server using MySQL.
Make sure you have read seafile server manual at
%s
Press ENTER to continue
-----------------------------------------------------------------''' % SERVER_MANUAL_HTTP
print welcome_msg
raw_input()
@staticmethod
def highlight(content):
'''Add ANSI color to content to get it highlighted on terminal'''
return '\x1b[33m%s\x1b[m' % content
@staticmethod
def info(msg):
print msg
@staticmethod
def error(msg):
'''Print error and exit'''
print
print 'Error: ' + msg
sys.exit(1)
@staticmethod
def run_argv(argv, cwd=None, env=None, suppress_stdout=False, suppress_stderr=False):
'''Run a program and wait it to finish, and return its exit code. The
standard output of this program is supressed.
'''
with open(os.devnull, 'w') as devnull:
if suppress_stdout:
stdout = devnull
else:
stdout = sys.stdout
if suppress_stderr:
stderr = devnull
else:
stderr = sys.stderr
proc = subprocess.Popen(argv,
cwd=cwd,
stdout=stdout,
stderr=stderr,
env=env)
return proc.wait()
@staticmethod
def run(cmdline, cwd=None, env=None, suppress_stdout=False, suppress_stderr=False):
'''Like run_argv but specify a command line string instead of argv'''
with open(os.devnull, 'w') as devnull:
if suppress_stdout:
stdout = devnull
else:
stdout = sys.stdout
if suppress_stderr:
stderr = devnull
else:
stderr = sys.stderr
proc = subprocess.Popen(cmdline,
cwd=cwd,
stdout=stdout,
stderr=stderr,
env=env,
shell=True)
return proc.wait()
@staticmethod
def prepend_env_value(name, value, env=None, seperator=':'):
'''prepend a new value to a list'''
if env is None:
env = os.environ
try:
current_value = env[name]
except KeyError:
current_value = ''
new_value = value
if current_value:
new_value += seperator + current_value
env[name] = new_value
@staticmethod
def must_mkdir(path):
'''Create a directory, exit on failure'''
try:
os.mkdir(path)
except OSError, e:
Utils.error('failed to create directory %s:%s' % (path, e))
@staticmethod
def must_copy(src, dst):
'''Copy src to dst, exit on failure'''
try:
shutil.copy(src, dst)
except Exception, e:
Utils.error('failed to copy %s to %s: %s' % (src, dst, e))
@staticmethod
def find_in_path(prog):
if 'win32' in sys.platform:
sep = ';'
else:
sep = ':'
dirs = os.environ['PATH'].split(sep)
for d in dirs:
d = d.strip()
if d == '':
continue
path = os.path.join(d, prog)
if os.path.exists(path):
return path
return None
@staticmethod
def get_python_executable():
'''Return the python executable. This should be the PYTHON environment
variable which is set in setup-seafile-mysql.sh
'''
return os.environ['PYTHON']
@staticmethod
def read_config(fn):
'''Return a case sensitive ConfigParser by reading the file "fn"'''
cp = ConfigParser()
cp.optionxform = str
cp.read(fn)
return cp
@staticmethod
def write_config(cp, fn):
'''Return a case sensitive ConfigParser by reading the file "fn"'''
with open(fn, 'w') as fp:
cp.write(fp)
@staticmethod
def ask_question(desc,
key=None,
note=None,
default=None,
validate=None,
yes_or_no=False,
password=False):
'''Ask a question, return the answer.
@desc description, e.g. "What is the port of ccnet?"
@key a name to represent the target of the question, e.g. "port for
ccnet server"
@note additional information for the question, e.g. "Must be a valid
port number"
@default the default value of the question. If the default value is
not None, when the user enter nothing and press [ENTER], the default
value would be returned
@validate a function that takes the user input as the only parameter
and validate it. It should return a validated value, or throws an
"InvalidAnswer" exception if the input is not valid.
@yes_or_no If true, the user must answer "yes" or "no", and a boolean
value would be returned
@password If true, the user input would not be echoed to the
console
'''
assert key or yes_or_no
# Format description
print
if note:
desc += '\n' + note
desc += '\n'
if yes_or_no:
desc += '[ yes or no ]'
else:
if default:
desc += '[ default "%s" ]' % default
else:
desc += '[ %s ]' % key
desc += ' '
while True:
# prompt for user input
if password:
answer = getpass.getpass(desc).strip()
else:
answer = raw_input(desc).strip()
# No user input: use default
if not answer:
if default:
answer = default
else:
continue
# Have user input: validate answer
if yes_or_no:
if answer not in ['yes', 'no']:
print Utils.highlight('\nPlease answer yes or no\n')
continue
else:
return answer == 'yes'
else:
if validate:
try:
return validate(answer)
except InvalidAnswer, e:
print Utils.highlight('\n%s\n' % e)
continue
else:
return answer
@staticmethod
def validate_port(port):
try:
port = int(port)
except ValueError:
raise InvalidAnswer('%s is not a valid port' % Utils.highlight(port))
if port <= 0 or port > 65535:
raise InvalidAnswer('%s is not a valid port' % Utils.highlight(port))
return port
class InvalidAnswer(Exception):
def __init__(self, msg):
Exception.__init__(self)
self.msg = msg
def __str__(self):
return self.msg
### END of Utils
####################
class EnvManager(object):
'''System environment and directory layout'''
def __init__(self):
self.install_path = os.path.dirname(os.path.abspath(__file__))
self.top_dir = os.path.dirname(self.install_path)
self.bin_dir = os.path.join(self.install_path, 'seafile', 'bin')
def check_pre_condiction(self):
def error_if_not_exists(path):
if not os.path.exists(path):
Utils.error('"%s" not found' % path)
paths = [
os.path.join(self.install_path, 'seafile'),
os.path.join(self.install_path, 'seahub'),
os.path.join(self.install_path, 'runtime'),
]
for path in paths:
error_if_not_exists(path)
if os.path.exists(ccnet_config.ccnet_dir):
Utils.error('Ccnet config dir \"%s\" already exists.' % ccnet_config.ccnet_dir)
def get_seahub_env(self):
'''Prepare for seahub syncdb'''
env = dict(os.environ)
env['CCNET_CONF_DIR'] = ccnet_config.ccnet_dir
env['SEAFILE_CONF_DIR'] = seafile_config.seafile_dir
self.setup_python_path(env)
return env
def setup_python_path(self, env):
'''And PYTHONPATH and CCNET_CONF_DIR/SEAFILE_CONF_DIR to env, which is
needed by seahub
'''
install_path = self.install_path
pro_pylibs_dir = os.path.join(install_path, 'pro', 'python')
extra_python_path = [
pro_pylibs_dir,
os.path.join(install_path, 'seahub', 'thirdpart'),
os.path.join(install_path, 'seafile/lib/python2.6/site-packages'),
os.path.join(install_path, 'seafile/lib64/python2.6/site-packages'),
os.path.join(install_path, 'seafile/lib/python2.7/site-packages'),
os.path.join(install_path, 'seafile/lib64/python2.7/site-packages'),
]
for path in extra_python_path:
Utils.prepend_env_value('PYTHONPATH', path, env=env)
def get_binary_env(self):
'''Set LD_LIBRARY_PATH for seafile server executables'''
env = dict(os.environ)
lib_dir = os.path.join(self.install_path, 'seafile', 'lib')
lib64_dir = os.path.join(self.install_path, 'seafile', 'lib64')
Utils.prepend_env_value('LD_LIBRARY_PATH', lib_dir, env=env)
Utils.prepend_env_value('LD_LIBRARY_PATH', lib64_dir, env=env)
return env
class AbstractConfigurator(object):
'''Abstract Base class for ccnet/seafile/seahub/db configurator'''
def __init__(self):
pass
def ask_questions(self):
raise NotImplementedError
def generate(self):
raise NotImplementedError
class AbstractDBConfigurator(AbstractConfigurator):
'''Abstract class for database related configuration'''
def __init__(self):
AbstractConfigurator.__init__(self)
self.mysql_host = 'localhost'
self.mysql_port = 3306
self.use_existing_db = False
self.seafile_mysql_user = ''
self.seafile_mysql_password = ''
self.ccnet_db_name = ''
self.seafile_db_name = ''
self.seahub_db_name = ''
self.seahub_admin_email = ''
self.seahub_admin_password = ''
@staticmethod
def ask_use_existing_db():
def validate(choice):
if choice not in ['1', '2']:
raise InvalidAnswer('Please choose 1 or 2')
return choice == '2'
question = '''\
-------------------------------------------------------
Please choose a way to initialize seafile databases:
-------------------------------------------------------
'''
note = '''\
[1] Create new ccnet/seafile/seahub databases
[2] Use existing ccnet/seafile/seahub databases
'''
return Utils.ask_question(question,
key='1 or 2',
note=note,
validate=validate)
def ask_mysql_host_port(self):
def validate(host):
if not re.match(r'^[a-zA-Z0-9_\-\.]+$', host):
raise InvalidAnswer('%s is not a valid host' % Utils.highlight(host))
if host == 'localhost':
host = '127.0.0.1'
question = 'What is the port of mysql server?'
key = 'mysql server port'
default = '3306'
port = Utils.ask_question(question,
key=key,
default=default,
validate=Utils.validate_port)
# self.check_mysql_server(host, port)
self.mysql_port = port
return host
question = 'What is the host of mysql server?'
key = 'mysql server host'
default = 'localhost'
self.mysql_host = Utils.ask_question(question,
key=key,
default=default,
validate=validate)
def check_mysql_server(self, host, port):
print '\nverifying mysql server running ... ',
try:
dummy = MySQLdb.connect(host=host, port=port)
except Exception:
print
raise InvalidAnswer('Failed to connect to mysql server at "%s:%s"' \
% (host, port))
print 'done'
def check_mysql_user(self, user, password):
print '\nverifying password of user %s ... ' % user,
kwargs = dict(host=self.mysql_host,
port=self.mysql_port,
user=user,
passwd=password)
try:
conn = MySQLdb.connect(**kwargs)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
raise InvalidAnswer('Failed to connect to mysql server using user "%s" and password "***": %s' \
% (user, e.args[1]))
else:
raise InvalidAnswer('Failed to connect to mysql server using user "%s" and password "***": %s' \
% (user, e))
print 'done'
return conn
def create_seahub_admin(self):
try:
conn = MySQLdb.connect(host=self.mysql_host,
port=self.mysql_port,
user=self.seafile_mysql_user,
passwd=self.seafile_mysql_password,
db=self.ccnet_db_name)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to connect to mysql database %s: %s' % (self.ccnet_db_name, e.args[1]))
else:
Utils.error('Failed to connect to mysql database %s: %s' % (self.ccnet_db_name, e))
cursor = conn.cursor()
sql = '''\
CREATE TABLE IF NOT EXISTS EmailUser (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, email VARCHAR(255), passwd CHAR(64), is_staff BOOL NOT NULL, is_active BOOL NOT NULL, ctime BIGINT, UNIQUE INDEX (email)) ENGINE=INNODB'''
try:
cursor.execute(sql)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to create ccnet user table: %s' % e.args[1])
else:
Utils.error('Failed to create ccnet user table: %s' % e)
sql = '''REPLACE INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES ('%s', '%s', 1, 1, 0)''' \
% (seahub_config.admin_email, seahub_config.hashed_admin_password())
try:
cursor.execute(sql)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to create admin user: %s' % e.args[1])
else:
Utils.error('Failed to create admin user: %s' % e)
conn.commit()
def ask_questions(self):
'''Ask questions and do database operations'''
raise NotImplementedError
class NewDBConfigurator(AbstractDBConfigurator):
'''Handles the case of creating new mysql databases for ccnet/seafile/seahub'''
def __init__(self):
AbstractDBConfigurator.__init__(self)
self.root_password = ''
self.root_conn = ''
def ask_questions(self):
self.ask_mysql_host_port()
self.ask_root_password()
self.ask_seafile_mysql_user_password()
self.ask_db_names()
def generate(self):
if not self.mysql_user_exists(self.seafile_mysql_user):
self.create_user()
self.create_databases()
def ask_root_password(self):
def validate(password):
self.root_conn = self.check_mysql_user('root', password)
return password
question = 'What is the password of the mysql root user?'
key = 'root password'
self.root_password = Utils.ask_question(question,
key=key,
validate=validate,
password=True)
def mysql_user_exists(self, user):
cursor = self.root_conn.cursor()
sql = '''SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '%s')''' % user
try:
cursor.execute(sql)
return cursor.fetchall()[0][0]
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to check mysql user %s: %s' % (user, e.args[1]))
else:
Utils.error('Failed to check mysql user %s: %s' % (user, e))
finally:
cursor.close()
def ask_seafile_mysql_user_password(self):
def validate(user):
if user == 'root':
self.seafile_mysql_password = self.root_password
else:
question = 'Enter the password for mysql user "%s":' % Utils.highlight(user)
key = 'password for %s' % user
password = Utils.ask_question(question, key=key, password=True)
# If the user already exists, check the password here
if self.mysql_user_exists(user):
self.check_mysql_user(user, password)
self.seafile_mysql_password = password
return user
question = 'Enter the name for mysql user of seafile. It would be created if not exists.'
key = 'mysql user for seafile'
default = 'root'
self.seafile_mysql_user = Utils.ask_question(question,
key=key,
default=default,
validate=validate)
def ask_db_name(self, program, default):
question = 'Enter the database name for %s:' % program
key = '%s database' % program
return Utils.ask_question(question,
key=key,
default=default,
validate=self.validate_db_name)
def ask_db_names(self):
self.ccnet_db_name = self.ask_db_name('ccnet-server', 'ccnet-db')
self.seafile_db_name = self.ask_db_name('seafile-server', 'seafile-db')
self.seahub_db_name = self.ask_db_name('seahub', 'seahub-db')
def validate_db_name(self, db_name):
return db_name
def create_user(self):
cursor = self.root_conn.cursor()
sql = '''CREATE USER '%s'@'localhost' IDENTIFIED BY '%s' ''' \
% (self.seafile_mysql_user, self.seafile_mysql_password)
try:
cursor.execute(sql)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to create mysql user %s: %s' % (self.seafile_mysql_user, e.args[1]))
else:
Utils.error('Failed to create mysql user %s: %s' % (self.seafile_mysql_user, e))
finally:
cursor.close()
def create_db(self, db_name):
cursor = self.root_conn.cursor()
sql = '''CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8''' \
% db_name
try:
cursor.execute(sql)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to create database %s: %s' % (db_name, e.args[1]))
else:
Utils.error('Failed to create database %s: %s' % (db_name, e))
finally:
cursor.close()
def grant_db_permission(self, db_name):
cursor = self.root_conn.cursor()
sql = '''GRANT ALL PRIVILEGES ON `%s`.* to `%s` ''' \
% (db_name, self.seafile_mysql_user)
try:
cursor.execute(sql)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
Utils.error('Failed to grant permission of database %s: %s' % (db_name, e.args[1]))
else:
Utils.error('Failed to grant permission of database %s: %s' % (db_name, e))
finally:
cursor.close()
def create_databases(self):
self.create_db(self.ccnet_db_name)
self.create_db(self.seafile_db_name)
self.create_db(self.seahub_db_name)
if self.seafile_mysql_user != 'root':
self.grant_db_permission(self.ccnet_db_name)
self.grant_db_permission(self.seafile_db_name)
self.grant_db_permission(self.seahub_db_name)
class ExistingDBConfigurator(AbstractDBConfigurator):
'''Handles the case of use existing mysql databases for ccnet/seafile/seahub'''
def __init__(self):
AbstractDBConfigurator.__init__(self)
self.use_existing_db = True
def ask_questions(self):
self.ask_mysql_host_port()
self.ask_existing_mysql_user_password()
self.ccnet_db_name = self.ask_db_name('ccnet')
self.seafile_db_name = self.ask_db_name('seafile')
self.seahub_db_name = self.ask_db_name('seahub')
def generate(self):
pass
def ask_existing_mysql_user_password(self):
def validate(user):
question = 'What is the password for mysql user "%s"?' % Utils.highlight(user)
key = 'password for %s' % user
password = Utils.ask_question(question, key=key, password=True)
self.check_mysql_user(user, password)
self.seafile_mysql_password = password
return user
question = 'Which mysql user to use for seafile?'
key = 'mysql user for seafile'
self.seafile_mysql_user = Utils.ask_question(question,
key=key,
validate=validate)
def ask_db_name(self, program):
def validate(db_name):
if self.seafile_mysql_user != 'root':
self.check_user_db_access(db_name)
return db_name
question = 'Enter the existing database name for %s:' % program
key = '%s database' % program
return Utils.ask_question(question,
key=key,
validate=validate)
def check_user_db_access(self, db_name):
user = self.seafile_mysql_user
password = self.seafile_mysql_password
print '\nverifying user "%s" access to database %s ... ' % (user, db_name),
try:
conn = MySQLdb.connect(host=self.mysql_host,
port=self.mysql_port,
user=user,
passwd=password,
db=db_name)
except Exception, e:
if isinstance(e, MySQLdb.OperationalError):
raise InvalidAnswer('Failed to access database %s using user "%s" and password "***": %s' \
% (db_name, user, e.args[1]))
else:
raise InvalidAnswer('Failed to access database %s using user "%s" and password "***": %s' \
% (db_name, user, e))
print 'done'
return conn
class CcnetConfigurator(AbstractConfigurator):
SERVER_NAME_REGEX = r'^[a-zA-Z0-9_\-]{3,14}$'
SERVER_IP_OR_DOMAIN_REGEX = '^[^.].+\..+[^.]$'
def __init__(self):
'''Initialize default values of ccnet configuration'''
AbstractConfigurator.__init__(self)
self.ccnet_dir = os.path.join(env_mgr.top_dir, 'ccnet')
self.port = 10001
self.server_name = 'my-seafile'
self.ip_or_domain = None
def ask_questions(self):
self.ask_server_name()
self.ask_server_ip_or_domain()
self.ask_port()
def generate(self):
print 'Generating ccnet configuration ...\n'
ccnet_init = os.path.join(env_mgr.bin_dir, 'ccnet-init')
argv = [
ccnet_init,
'--config-dir', self.ccnet_dir,
'--name', self.server_name,
'--host', self.ip_or_domain,
'--port', str(self.port),
]
if Utils.run_argv(argv, env=env_mgr.get_binary_env()) != 0:
Utils.error('Failed to generate ccnet configuration')
time.sleep(1)
self.generate_db_conf()
def generate_db_conf(self):
ccnet_conf = os.path.join(self.ccnet_dir, 'ccnet.conf')
config = Utils.read_config(ccnet_conf)
# [Database]
# ENGINE=
# HOST=
# USER=
# PASSWD=
# DB=
db_section = 'Database'
if not config.has_section(db_section):
config.add_section(db_section)
config.set(db_section, 'ENGINE', 'mysql')
config.set(db_section, 'HOST', db_config.mysql_host)
config.set(db_section, 'PORT', db_config.mysql_port)
config.set(db_section, 'USER', db_config.seafile_mysql_user)
config.set(db_section, 'PASSWD', db_config.seafile_mysql_password)
config.set(db_section, 'DB', db_config.ccnet_db_name)
config.set(db_section, 'CONNECTION_CHARSET', 'utf8')
Utils.write_config(config, ccnet_conf)
def ask_server_name(self):
def validate(name):
if not re.match(self.SERVER_NAME_REGEX, name):
raise InvalidAnswer('%s is not a valid name' % Utils.highlight(name))
return name
question = 'What is the name of the server? It will be displayed on the client.'
key = 'server name'
note = '3 - 15 letters or digits'
self.server_name = Utils.ask_question(question,
key=key,
note=note,
validate=validate)
def ask_server_ip_or_domain(self):
def validate(ip_or_domain):
if not re.match(self.SERVER_IP_OR_DOMAIN_REGEX, ip_or_domain):
raise InvalidAnswer('%s is not a valid ip or domain' % ip_or_domain)
return ip_or_domain
question = 'What is the ip or domain of the server?'
key = 'This server\'s ip or domain'
note = 'For example: www.mycompany.com, 192.168.1.101'
self.ip_or_domain = Utils.ask_question(question,
key=key,
note=note,
validate=validate)
def ask_port(self):
def validate(port):
return Utils.validate_port(port)
question = 'Which port do you want to use for the ccnet server?'
key = 'ccnet server port'
default = 10001
self.port = Utils.ask_question(question,
key=key,
default=default,
validate=validate)
class SeafileConfigurator(AbstractConfigurator):
def __init__(self):
AbstractConfigurator.__init__(self)
self.seafile_dir = os.path.join(env_mgr.top_dir, 'seafile-data')
self.port = 12001
self.fileserver_port = 8082
def ask_questions(self):
self.ask_seafile_dir()
self.ask_port()
self.ask_fileserver_port()
def generate(self):
print 'Generating seafile configuration ...\n'
seafserv_init = os.path.join(env_mgr.bin_dir, 'seaf-server-init')
argv = [
seafserv_init,
'--seafile-dir', self.seafile_dir,
'--port', str(self.port),
'--fileserver-port', str(self.fileserver_port),
]
if Utils.run_argv(argv, env=env_mgr.get_binary_env()) != 0:
Utils.error('Failed to generate ccnet configuration')
time.sleep(1)
self.generate_db_conf()
self.write_seafile_ini()
print 'done'
def generate_db_conf(self):
seafile_conf = os.path.join(self.seafile_dir, 'seafile.conf')
config = Utils.read_config(seafile_conf)
# [database]
# type=
# host=
# user=
# password=
# db_name=
# unix_socket=
db_section = 'database'
if not config.has_section(db_section):
config.add_section(db_section)
config.set(db_section, 'type', 'mysql')
config.set(db_section, 'host', db_config.mysql_host)
config.set(db_section, 'port', db_config.mysql_port)
config.set(db_section, 'user', db_config.seafile_mysql_user)
config.set(db_section, 'password', db_config.seafile_mysql_password)
config.set(db_section, 'db_name', db_config.seafile_db_name)
config.set(db_section, 'connection_charset', 'utf8')
Utils.write_config(config, seafile_conf)
def ask_seafile_dir(self):
def validate(path):
if os.path.exists(path):
raise InvalidAnswer('%s already exists' % Utils.highlight(path))
return path
question = 'Where do you want to put your seafile data?'
key = 'seafile-data'
note = 'Please use a volume with enough free space'
default = os.path.join(env_mgr.top_dir, 'seafile-data')
self.seafile_dir = Utils.ask_question(question,
key=key,
note=note,
default=default,
validate=validate)
def ask_port(self):
def validate(port):
port = Utils.validate_port(port)
if port == ccnet_config.port:
raise InvalidAnswer('%s is used by ccnet server, choose another one' \
% Utils.highlight(port))
return port
question = 'Which port do you want to use for the seafile server?'
key = 'seafile server port'
default = 12001
self.port = Utils.ask_question(question,
key=key,
default=default,
validate=validate)
def ask_fileserver_port(self):
def validate(port):
port = Utils.validate_port(port)
if port == ccnet_config.port:
raise InvalidAnswer('%s is used by ccnet server, choose another one' \
% Utils.highlight(port))
if port == seafile_config.port:
raise InvalidAnswer('%s is used by seafile server, choose another one' \
% Utils.highlight(port))
return port
question = 'Which port do you want to use for the seafile fileserver?'
key = 'seafile fileserver port'
default = 8082
self.fileserver_port = Utils.ask_question(question,
key=key,
default=default,
validate=validate)
def write_seafile_ini(self):
seafile_ini = os.path.join(ccnet_config.ccnet_dir, 'seafile.ini')
with open(seafile_ini, 'w') as fp:
fp.write(self.seafile_dir)
class SeahubConfigurator(AbstractConfigurator):
def __init__(self):
AbstractConfigurator.__init__(self)
self.admin_email = ''
self.admin_password = ''
def hashed_admin_password(self):
return hashlib.sha1(self.admin_password).hexdigest() # pylint: disable=E1101
def ask_questions(self):
pass
# self.ask_admin_email()
# self.ask_admin_password()
def generate(self):
'''Generating seahub_settings.py'''
print 'Generating seahub configuration ...\n'
time.sleep(1)
seahub_settings_py = os.path.join(env_mgr.top_dir, 'seahub_settings.py')
with open(seahub_settings_py, 'w') as fp:
self.write_secret_key(fp)
self.write_database_config(fp)
def write_secret_key(self, fp):
text = 'SECRET_KEY = "%s"\n\n' % self.gen_secret_key()
fp.write(text)
def write_database_config(self, fp):
template = '''\
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '%(name)s',
'USER': '%(username)s',
'PASSWORD': '%(password)s',
'HOST': '%(host)s',
'PORT': '%(port)s',
'OPTIONS': {
'init_command': 'SET storage_engine=INNODB',
}
}
}
'''
text = template % dict(name=db_config.seahub_db_name,
username=db_config.seafile_mysql_user,
password=db_config.seafile_mysql_password,
host=db_config.mysql_host,
port=db_config.mysql_port)
fp.write(text)
def gen_secret_key(self):
data = str(uuid.uuid4()) + str(uuid.uuid4())
return data[:40]
def ask_admin_email(self):
print
print '----------------------------------------'
print 'Now let\'s create the admin account'
print '----------------------------------------'
def validate(email):
# whitespace is not allowed
if re.match(r'[\s]', email):
raise InvalidAnswer('%s is not a valid email address' % Utils.highlight(email))
# must be a valid email address
if not re.match(r'^.+@.*\..+$', email):
raise InvalidAnswer('%s is not a valid email address' % Utils.highlight(email))
return email
key = 'admin email'
question = 'What is the ' + Utils.highlight('email') + ' for the admin account?'
self.admin_email = Utils.ask_question(question,
key=key,
validate=validate)
def ask_admin_password(self):
def validate(password):
key = 'admin password again'
question = 'Enter the ' + Utils.highlight('password again:')
password_again = Utils.ask_question(question,
key=key,
password=True)
if password_again != password:
raise InvalidAnswer('password mismatch')
return password
key = 'admin password'
question = 'What is the ' + Utils.highlight('password') + ' for the admin account?'
self.admin_password = Utils.ask_question(question,
key=key,
password=True,
validate=validate)
def do_syncdb(self):
print '----------------------------------------'
print 'Now creating seahub database tables ...\n'
print '----------------------------------------'
env = env_mgr.get_seahub_env()
cwd = os.path.join(env_mgr.install_path, 'seahub')
argv = [
Utils.get_python_executable(),
'manage.py',
'syncdb',
]
if Utils.run_argv(argv, cwd=cwd, env=env) != 0:
Utils.error("Failed to create seahub databases")
def prepare_avatar_dir(self):
# media_dir=${INSTALLPATH}/seahub/media
# orig_avatar_dir=${INSTALLPATH}/seahub/media/avatars
# dest_avatar_dir=${TOPDIR}/seahub-data/avatars
# if [[ ! -d ${dest_avatar_dir} ]]; then
# mkdir -p "${TOPDIR}/seahub-data"
# mv "${orig_avatar_dir}" "${dest_avatar_dir}"
# ln -s ../../../seahub-data/avatars ${media_dir}
# fi
try:
media_dir = os.path.join(env_mgr.install_path, 'seahub', 'media')
orig_avatar_dir = os.path.join(media_dir, 'avatars')
seahub_data_dir = os.path.join(env_mgr.top_dir, 'seahub-data')
dest_avatar_dir = os.path.join(seahub_data_dir, 'avatars')
if os.path.exists(dest_avatar_dir):
return
if not os.path.exists(seahub_data_dir):
os.mkdir(seahub_data_dir)
shutil.move(orig_avatar_dir, dest_avatar_dir)
os.symlink('../../../seahub-data/avatars', orig_avatar_dir)
except Exception, e:
Utils.error('Failed to prepare seahub avatars dir: %s' % e)
class SeafDavConfigurator(AbstractConfigurator):
def __init__(self):
AbstractConfigurator.__init__(self)
self.conf_dir = None
self.seafdav_conf = None
def ask_questions(self):
pass
def generate(self):
self.conf_dir = os.path.join(env_mgr.top_dir, 'conf')
if not os.path.exists('conf'):
Utils.must_mkdir(self.conf_dir)
self.seafdav_conf = os.path.join(self.conf_dir, 'seafdav.conf')
text = '''
[WEBDAV]
enabled = false
port = 8080
fastcgi = false
share_name = /
'''
with open(self.seafdav_conf, 'w') as fp:
fp.write(text)
class UserManualHandler(object):
def __init__(self):
self.src_docs_dir = os.path.join(env_mgr.install_path, 'seafile', 'docs')
self.library_template_dir = None
def copy_user_manuals(self):
self.library_template_dir = os.path.join(seafile_config.seafile_dir, 'library-template')
Utils.must_mkdir(self.library_template_dir)
pattern = os.path.join(self.src_docs_dir, '*.doc')
for doc in glob.glob(pattern):
Utils.must_copy(doc, self.library_template_dir)
def report_config():
print
print '---------------------------------'
print 'This is your configuration'
print '---------------------------------'
print
template = '''\
server name: %(server_name)s
server ip/domain: %(ip_or_domain)s
ccnet port: %(ccnet_port)s
seafile data dir: %(seafile_dir)s
seafile port: %(seafile_port)s
fileserver port: %(fileserver_port)s
database: %(use_existing_db)s
ccnet database: %(ccnet_db_name)s
seafile database: %(seafile_db_name)s
seahub database: %(seahub_db_name)s
database user: %(db_user)s
'''
config = {
'server_name' : ccnet_config.server_name,
'ip_or_domain' : ccnet_config.ip_or_domain,
'ccnet_port' : ccnet_config.port,
'seafile_dir' : seafile_config.seafile_dir,
'seafile_port' : seafile_config.port,
'fileserver_port' : seafile_config.fileserver_port,
'admin_email' : seahub_config.admin_email,
'use_existing_db': 'use exising' if db_config.use_existing_db else 'create new',
'ccnet_db_name': db_config.ccnet_db_name,
'seafile_db_name': db_config.seafile_db_name,
'seahub_db_name': db_config.seahub_db_name,
'db_user': db_config.seafile_mysql_user
}
print template % config
print
print '---------------------------------'
print 'Press ENTER to continue, or Ctrl-C to abort'
print '---------------------------------'
raw_input()
def create_seafile_server_symlink():
print '\ncreating seafile-server-latest symbolic link ... ',
seafile_server_symlink = os.path.join(env_mgr.top_dir, 'seafile-server-latest')
try:
os.symlink(os.path.basename(env_mgr.install_path), seafile_server_symlink)
except Exception, e:
print '\n'
Utils.error('Failed to create symbolic link %s: %s' % (seafile_server_symlink, e))
else:
print 'done\n\n'
env_mgr = EnvManager()
ccnet_config = CcnetConfigurator()
seafile_config = SeafileConfigurator()
seafdav_config = SeafDavConfigurator()
seahub_config = SeahubConfigurator()
user_manuals_handler = UserManualHandler()
# Would be created after AbstractDBConfigurator.ask_use_existing_db()
db_config = None
def main():
global db_config
Utils.welcome()
warnings.filterwarnings('ignore', category=MySQLdb.Warning)
env_mgr.check_pre_condiction()
# Part 1: collect configuration
ccnet_config.ask_questions()
seafile_config.ask_questions()
seahub_config.ask_questions()
if AbstractDBConfigurator.ask_use_existing_db():
db_config = ExistingDBConfigurator()
else:
db_config = NewDBConfigurator()
db_config.ask_questions()
report_config()
# Part 2: generate configuration
db_config.generate()
ccnet_config.generate()
seafile_config.generate()
seafdav_config.generate()
seahub_config.generate()
seahub_config.do_syncdb()
seahub_config.prepare_avatar_dir()
# db_config.create_seahub_admin()
user_manuals_handler.copy_user_manuals()
create_seafile_server_symlink()
report_success()
def report_success():
message = '''\
-----------------------------------------------------------------
Your seafile server configuration has been finished successfully.
-----------------------------------------------------------------
run seafile server: ./seafile.sh { start | stop | restart }
run seahub server: ./seahub.sh { start <port> | stop | restart <port> }
-----------------------------------------------------------------
If you are behind a firewall, remember to allow input/output of these tcp ports:
-----------------------------------------------------------------
port of ccnet server: %(ccnet_port)s
port of seafile server: %(seafile_port)s
port of seafile fileserver: %(fileserver_port)s
port of seahub: 8000
When problems occur, Refer to
%(server_manual_http)s
for information.
'''
print message % dict(ccnet_port=ccnet_config.port,
seafile_port=seafile_config.port,
fileserver_port=seafile_config.fileserver_port,
server_manual_http=SERVER_MANUAL_HTTP)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print
print Utils.highlight('The setup process is aborted')
print