332 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
 | |
| # License: GNU General Public License v3. See license.txt
 | |
| 
 | |
| #!/usr/bin/env python
 | |
| from __future__ import unicode_literals
 | |
| import os, sys
 | |
| import argparse
 | |
| 
 | |
| is_redhat = is_debian = None
 | |
| root_password = None
 | |
| 
 | |
| requirements = [
 | |
| 	"chardet", 
 | |
| 	"cssmin", 
 | |
| 	"dropbox", 
 | |
| 	"google-api-python-client", 
 | |
| 	"gunicorn", 
 | |
| 	"httplib2", 
 | |
| 	"jinja2", 
 | |
| 	"markdown2", 
 | |
| 	"markupsafe", 
 | |
| 	"mysql-python", 
 | |
| 	"pygeoip", 
 | |
| 	"python-dateutil", 
 | |
| 	"python-memcached", 
 | |
| 	"pytz==2013d", 
 | |
| 	"requests", 
 | |
| 	"six", 
 | |
| 	"slugify", 
 | |
| 	"termcolor", 
 | |
| 	"werkzeug",
 | |
| 	"semantic_version",
 | |
| 	"gitpython==0.3.2.RC1"
 | |
| ]
 | |
| 
 | |
| def install(install_path):
 | |
| 	setup_folders(install_path)
 | |
| 	install_erpnext(install_path)
 | |
| 	
 | |
| 	post_install(install_path)
 | |
| 	
 | |
| def install_pre_requisites():
 | |
| 	global is_redhat, is_debian
 | |
| 	is_redhat, is_debian = validate_install()
 | |
| 	if is_redhat:
 | |
| 		install_using_yum()
 | |
| 	elif is_debian:
 | |
| 		install_using_apt()
 | |
| 		
 | |
| 	install_python_modules()
 | |
| 	
 | |
| 	print "-"*80
 | |
| 	print "Pre-requisites Installed"
 | |
| 	print "-"*80
 | |
| 
 | |
| def validate_install():
 | |
| 	import platform
 | |
| 
 | |
| 	# check os
 | |
| 	operating_system = platform.system()
 | |
| 	print "Operating System =", operating_system
 | |
| 	if operating_system != "Linux":
 | |
| 		raise Exception, "Sorry! This installer works only for Linux based Operating Systems"
 | |
| 	
 | |
| 	# check python version
 | |
| 	python_version = sys.version.split(" ")[0]
 | |
| 	print "Python Version =", python_version
 | |
| 	if not (python_version and int(python_version.split(".")[0])==2 and int(python_version.split(".")[1]) >= 7):
 | |
| 		raise Exception, "Hey! ERPNext needs Python version to be 2.7+"
 | |
| 	
 | |
| 	# check distribution
 | |
| 	distribution = platform.linux_distribution()[0].lower().replace('"', '')
 | |
| 	print "Distribution = ", distribution
 | |
| 	is_redhat = distribution in ("redhat", "red hat enterprise linux server", "centos", "centos linux", "fedora")
 | |
| 	is_debian = distribution in ("debian", "ubuntu", "elementary os", "linuxmint")
 | |
| 	
 | |
| 	if not (is_redhat or is_debian):
 | |
| 		raise Exception, "Sorry! This installer works only with yum or apt-get package management"
 | |
| 	
 | |
| 	return is_redhat, is_debian
 | |
| 		
 | |
| def install_using_yum():
 | |
| 	packages = "python python-setuptools gcc python-devel MySQL-python git memcached ntp vim-enhanced screen"
 | |
| 	
 | |
| 	print "-"*80
 | |
| 	print "Installing Packages: (This may take some time)"
 | |
| 	print packages
 | |
| 	print "-"*80
 | |
| 	exec_in_shell("yum install -y %s" % packages)
 | |
| 	
 | |
| 	if not exec_in_shell("which mysql"):
 | |
| 		packages = "mysql mysql-server mysql-devel"
 | |
| 		print "Installing Packages:", packages
 | |
| 		exec_in_shell("yum install -y %s" % packages)
 | |
| 		exec_in_shell("service mysqld restart")
 | |
| 		
 | |
| 		# set a root password post install
 | |
| 		global root_password
 | |
| 		print "Please create a password for root user of MySQL"
 | |
| 		root_password = (get_root_password() or "erpnext").strip()
 | |
| 		exec_in_shell('mysqladmin -u root password "%s"' % (root_password,))
 | |
| 		print "Root password set as", root_password
 | |
| 	
 | |
| 	# install htop
 | |
| 	if not exec_in_shell("which htop"):
 | |
| 		try:
 | |
| 			exec_in_shell("cd /tmp && rpm -i --force http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm && yum install -y htop")
 | |
| 		except:
 | |
| 			pass
 | |
| 	
 | |
| 	update_config_for_redhat()
 | |
| 	
 | |
| def update_config_for_redhat():
 | |
| 	import re
 | |
| 	
 | |
| 	# set to autostart on startup
 | |
| 	for service in ("mysqld", "memcached", "ntpd"):
 | |
| 		exec_in_shell("chkconfig --level 2345 %s on" % service)
 | |
| 		exec_in_shell("service %s restart" % service)
 | |
| 	
 | |
| def install_using_apt():
 | |
| 	exec_in_shell("apt-get update")
 | |
| 	packages = "python python-setuptools python-dev build-essential python-pip python-mysqldb git memcached ntp vim screen htop"
 | |
| 	print "-"*80
 | |
| 	print "Installing Packages: (This may take some time)"
 | |
| 	print packages
 | |
| 	print "-"*80
 | |
| 	exec_in_shell("apt-get install -y %s" % packages)
 | |
| 	global root_password
 | |
| 	if not root_password:
 | |
| 		root_password = get_root_password()
 | |
| 	exec_in_shell("echo mysql-server mysql-server/root_password password %s | sudo debconf-set-selections" % root_password)
 | |
| 	exec_in_shell("echo mysql-server mysql-server/root_password_again password %s | sudo debconf-set-selections" % root_password)
 | |
| 	
 | |
| 	if not exec_in_shell("which mysql"):
 | |
| 		packages = "mysql-server libmysqlclient-dev"
 | |
| 		print "Installing Packages:", packages
 | |
| 		exec_in_shell("apt-get install -y %s" % packages)
 | |
| 	
 | |
| 	update_config_for_debian()
 | |
| 	
 | |
| def update_config_for_debian():
 | |
| 	for service in ("mysql", "ntpd"):
 | |
| 		exec_in_shell("service %s restart" % service)
 | |
| 	
 | |
| def install_python_modules():
 | |
| 	print "-"*80
 | |
| 	print "Installing Python Modules: (This may take some time)"
 | |
| 	print "-"*80
 | |
| 	
 | |
| 	if not exec_in_shell("which pip"):
 | |
| 		exec_in_shell("easy_install pip")
 | |
| 	
 | |
| 	exec_in_shell("pip install --upgrade pip")
 | |
| 	exec_in_shell("pip install --upgrade setuptools")
 | |
| 	exec_in_shell("pip install --upgrade virtualenv")
 | |
| 	exec_in_shell("pip install {}".format(' '.join(requirements)))
 | |
| 	
 | |
| def install_erpnext(install_path):
 | |
| 	print
 | |
| 	print "-"*80
 | |
| 	print "Installing ERPNext"
 | |
| 	print "-"*80
 | |
| 	
 | |
| 	# ask for details
 | |
| 	global root_password
 | |
| 	if not root_password:
 | |
| 		root_password = get_root_password()
 | |
| 		test_root_connection(root_password)
 | |
| 	
 | |
| 	db_name = raw_input("ERPNext Database Name: ")
 | |
| 	if not db_name:
 | |
| 		raise Exception, "Sorry! You must specify ERPNext Database Name"
 | |
| 	
 | |
| 	# setup paths
 | |
| 	sys.path = [".", "lib", "app"] + sys.path
 | |
| 	import wnf
 | |
| 	
 | |
| 	# install database, run patches, update schema
 | |
| 	# setup_db(install_path, root_password, db_name)
 | |
| 	wnf.install(db_name, root_password=root_password)
 | |
| 
 | |
| 	setup_cron(install_path)
 | |
| 	
 | |
| def get_root_password():
 | |
| 	# ask for root mysql password
 | |
| 	import getpass
 | |
| 	root_pwd = None
 | |
| 	root_pwd = getpass.getpass("MySQL Root user's Password: ")
 | |
| 	return root_pwd
 | |
| 	
 | |
| def test_root_connection(root_pwd):
 | |
| 	out = exec_in_shell("mysql -u root %s -e 'exit'" % \
 | |
| 		(("-p"+root_pwd) if root_pwd else "").replace('$', '\$').replace(' ', '\ '))
 | |
| 	if "access denied" in out.lower():
 | |
| 		raise Exception("Incorrect MySQL Root user's password")
 | |
| 		
 | |
| def setup_folders(install_path):
 | |
| 	os.chdir(install_path)
 | |
| 	app = os.path.join(install_path, "app")
 | |
| 	if not os.path.exists(app):
 | |
| 		print "Cloning erpnext"
 | |
| 		exec_in_shell("cd %s && git clone https://github.com/webnotes/erpnext.git app" % install_path)
 | |
| 		exec_in_shell("cd app && git config core.filemode false")
 | |
| 		if not os.path.exists(app):
 | |
| 			raise Exception, "Couldn't clone erpnext repository"
 | |
| 	
 | |
| 	lib = os.path.join(install_path, "lib")
 | |
| 	if not os.path.exists(lib):
 | |
| 		print "Cloning wnframework"
 | |
| 		exec_in_shell("cd %s && git clone https://github.com/webnotes/wnframework.git lib" % install_path)
 | |
| 		exec_in_shell("cd lib && git config core.filemode false")
 | |
| 		if not os.path.exists(lib):
 | |
| 			raise Exception, "Couldn't clone wnframework repository"
 | |
| 	
 | |
| 	public = os.path.join(install_path, "public")
 | |
| 	for p in [public, os.path.join(public, "files"), os.path.join(public, "backups"),
 | |
| 		os.path.join(install_path, "logs")]:
 | |
| 			if not os.path.exists(p):
 | |
| 				os.mkdir(p)
 | |
| 				
 | |
| def setup_conf(install_path, db_name):
 | |
| 	import os, string, random, re
 | |
| 
 | |
| 	# generate db password
 | |
| 	char_range = string.ascii_letters + string.digits
 | |
| 	db_password = "".join((random.choice(char_range) for n in xrange(16)))
 | |
| 	
 | |
| 	# make conf file
 | |
| 	with open(os.path.join(install_path, "lib", "conf", "conf.py"), "r") as template:
 | |
| 		conf = template.read()
 | |
| 	
 | |
| 	conf = re.sub("db_name.*", 'db_name = "%s"' % (db_name,), conf)
 | |
| 	conf = re.sub("db_password.*", 'db_password = "%s"' % (db_password,), conf)
 | |
| 	
 | |
| 	with open(os.path.join(install_path, "conf.py"), "w") as conf_file:
 | |
| 		conf_file.write(conf)
 | |
| 	
 | |
| 	return db_password
 | |
| 	
 | |
| def post_install(install_path):
 | |
| 	pass
 | |
| 
 | |
| def exec_in_shell(cmd):
 | |
| 	# using Popen instead of os.system - as recommended by python docs
 | |
| 	from subprocess import Popen
 | |
| 	import tempfile
 | |
| 
 | |
| 	with tempfile.TemporaryFile() as stdout:
 | |
| 		with tempfile.TemporaryFile() as stderr:
 | |
| 			p = Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
 | |
| 			p.wait()
 | |
| 
 | |
| 			stdout.seek(0)
 | |
| 			out = stdout.read()
 | |
| 			if out: out = out.decode('utf-8')
 | |
| 
 | |
| 			stderr.seek(0)
 | |
| 			err = stderr.read()
 | |
| 			if err: err = err.decode('utf-8')
 | |
| 
 | |
| 	if err and any((kw in err.lower() for kw in ["traceback", "error", "exception"])):
 | |
| 		print out
 | |
| 		raise Exception, err
 | |
| 	else:
 | |
| 		print "."
 | |
| 
 | |
| 	return out
 | |
| 
 | |
| def parse_args():
 | |
| 	parser = argparse.ArgumentParser()
 | |
| 	parser.add_argument('--create_user', default=False, action='store_true')
 | |
| 	parser.add_argument('--username', default='erpnext')
 | |
| 	parser.add_argument('--password', default='erpnext')
 | |
| 	parser.add_argument('--no_install_prerequisites', default=False, action='store_true')
 | |
| 	return parser.parse_args()
 | |
| 
 | |
| def create_user(username, password):
 | |
| 	import subprocess, pwd
 | |
| 	p = subprocess.Popen("useradd -m -d /home/{username} -s {shell} {username}".format(username=username, shell=os.environ.get('SHELL')).split())
 | |
| 	p.wait()
 | |
| 	p = subprocess.Popen("passwd {username}".format(username=username).split(), stdin=subprocess.PIPE)
 | |
| 	p.communicate('{password}\n{password}\n'.format(password=password))
 | |
| 	p.wait()
 | |
| 	return pwd.getpwnam(username).pw_uid
 | |
| 
 | |
| def setup_cron(install_path):
 | |
| 	erpnext_cron_entries = [
 | |
| 		"*/3 * * * * cd %s && python2.7 lib/wnf.py --run_scheduler >> erpnext-sch.log 2>&1" % install_path,
 | |
| 		"0 */6 * * * cd %s && python2.7 lib/wnf.py --backup >> erpnext-backup.log 2>&1" % install_path
 | |
| 		]
 | |
| 	for row in erpnext_cron_entries:
 | |
| 		try:
 | |
| 			existing_cron = exec_in_shell("crontab -l")
 | |
| 			if row not in existing_cron:
 | |
| 				exec_in_shell('{ crontab -l; echo "%s"; } | crontab' % row)
 | |
| 		except:
 | |
| 			exec_in_shell('echo "%s" | crontab' % row)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
| 	args = parse_args()
 | |
| 	install_path = os.getcwd()
 | |
| 	if os.getuid() != 0 and args.create_user and not args.no_install_prequisites:
 | |
| 		raise Exception, "Please run this script as root"
 | |
| 
 | |
| 	if args.create_user:
 | |
| 		uid = create_user(args.username, args.password)
 | |
| 		install_path = '/home/{username}/erpnext'.format(username=args.username)
 | |
| 
 | |
| 	if not args.no_install_prerequisites:
 | |
| 		install_pre_requisites()
 | |
| 
 | |
| 	if os.environ.get('SUDO_UID') and not args.create_user:
 | |
| 		os.setuid(int(os.environ.get('SUDO_UID')))
 | |
| 	
 | |
| 	if os.getuid() == 0 and args.create_user:
 | |
| 		os.setuid(uid)
 | |
| 		if install_path:
 | |
| 			os.mkdir(install_path)
 | |
| 	
 | |
| 	install(install_path=install_path)
 | |
| 	print
 | |
| 	print "-"*80
 | |
| 	print "Installation complete"
 | |
| 	print "To start the development server,"
 | |
| 	print "Login as {username} with password {password}".format(username=args.username, password=args.password)
 | |
| 	print "cd {}".format(install_path)
 | |
| 	print "./lib/wnf.py --serve"
 | |
| 	print "-"*80
 | |
| 	print "Open your browser and go to http://localhost:8000"
 | |
| 	print "Login using username = Administrator and password = admin"
 |