blob: e285d4bbfea396000485eb4223f9d0637865bb16 [file] [log] [blame]
Rushabh Mehtaad45e312013-11-20 12:59:58 +05301# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
Rushabh Mehtae67d1fb2013-08-05 14:59:54 +05302# License: GNU General Public License v3. See license.txt
3
Rushabh Mehta54c15452013-07-16 12:05:41 +05304#!/usr/bin/env python
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +05305from __future__ import unicode_literals
Rushabh Mehta54c15452013-07-16 12:05:41 +05306import os, sys
Pratik Vyas18515d32013-11-11 02:52:08 +05307import argparse
Pratik Vyas69951e52014-01-07 18:24:40 +05308import subprocess
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +05309
Rushabh Mehta54c15452013-07-16 12:05:41 +053010is_redhat = is_debian = None
11root_password = None
12
Anand Doshi0ebe35b2013-11-18 12:06:33 +053013requirements = [
14 "chardet",
15 "cssmin",
16 "dropbox",
17 "google-api-python-client",
18 "gunicorn",
19 "httplib2",
20 "jinja2",
21 "markdown2",
22 "markupsafe",
Pratik Vyasa11e1442014-01-07 12:02:45 +053023 "mysql-python==1.2.4",
Anand Doshi0ebe35b2013-11-18 12:06:33 +053024 "pygeoip",
25 "python-dateutil",
26 "python-memcached",
27 "pytz==2013d",
28 "requests",
29 "six",
30 "slugify",
31 "termcolor",
Pratik Vyas374461b2013-11-28 16:57:33 +053032 "werkzeug",
33 "semantic_version",
34 "gitpython==0.3.2.RC1"
Pratik Vyasfe44d272013-10-24 23:59:53 +053035]
36
Pratik Vyas18515d32013-11-11 02:52:08 +053037def install(install_path):
Pratik Vyasfe44d272013-10-24 23:59:53 +053038 setup_folders(install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +053039 install_erpnext(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +053040
Rushabh Mehta54c15452013-07-16 12:05:41 +053041 post_install(install_path)
42
43def install_pre_requisites():
44 global is_redhat, is_debian
45 is_redhat, is_debian = validate_install()
46 if is_redhat:
47 install_using_yum()
48 elif is_debian:
49 install_using_apt()
50
51 install_python_modules()
52
53 print "-"*80
54 print "Pre-requisites Installed"
55 print "-"*80
56
57def validate_install():
58 import platform
59
60 # check os
61 operating_system = platform.system()
62 print "Operating System =", operating_system
63 if operating_system != "Linux":
64 raise Exception, "Sorry! This installer works only for Linux based Operating Systems"
65
66 # check python version
67 python_version = sys.version.split(" ")[0]
68 print "Python Version =", python_version
Pratik Vyasfe44d272013-10-24 23:59:53 +053069 if not (python_version and int(python_version.split(".")[0])==2 and int(python_version.split(".")[1]) >= 7):
Pratik Vyas18515d32013-11-11 02:52:08 +053070 raise Exception, "Hey! ERPNext needs Python version to be 2.7+"
Rushabh Mehta54c15452013-07-16 12:05:41 +053071
72 # check distribution
73 distribution = platform.linux_distribution()[0].lower().replace('"', '')
74 print "Distribution = ", distribution
Luis Perez5651d982013-11-07 14:53:49 -060075 is_redhat = distribution in ("redhat", "red hat enterprise linux server", "centos", "centos linux", "fedora")
Nabin Haitada70992013-08-20 10:58:07 +053076 is_debian = distribution in ("debian", "ubuntu", "elementary os", "linuxmint")
Rushabh Mehta54c15452013-07-16 12:05:41 +053077
78 if not (is_redhat or is_debian):
79 raise Exception, "Sorry! This installer works only with yum or apt-get package management"
80
81 return is_redhat, is_debian
82
83def install_using_yum():
Pratik Vyas69951e52014-01-07 18:24:40 +053084 packages = "gcc MySQL-python git memcached ntp vim-enhanced screen"
Rushabh Mehta54c15452013-07-16 12:05:41 +053085
86 print "-"*80
87 print "Installing Packages: (This may take some time)"
88 print packages
89 print "-"*80
90 exec_in_shell("yum install -y %s" % packages)
91
Pratik Vyas69951e52014-01-07 18:24:40 +053092
93 try:
94 exec_in_shell("which mysql")
95 except subprocess.CalledProcessError:
Rushabh Mehta54c15452013-07-16 12:05:41 +053096 packages = "mysql mysql-server mysql-devel"
97 print "Installing Packages:", packages
98 exec_in_shell("yum install -y %s" % packages)
99 exec_in_shell("service mysqld restart")
100
101 # set a root password post install
102 global root_password
103 print "Please create a password for root user of MySQL"
104 root_password = (get_root_password() or "erpnext").strip()
105 exec_in_shell('mysqladmin -u root password "%s"' % (root_password,))
106 print "Root password set as", root_password
107
Rushabh Mehta54c15452013-07-16 12:05:41 +0530108 update_config_for_redhat()
109
110def update_config_for_redhat():
111 import re
112
Rushabh Mehta54c15452013-07-16 12:05:41 +0530113 # set to autostart on startup
Pratik Vyas69951e52014-01-07 18:24:40 +0530114 for service in ("mysqld", "memcached"):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530115 exec_in_shell("chkconfig --level 2345 %s on" % service)
116 exec_in_shell("service %s restart" % service)
117
118def install_using_apt():
Anand Doshib1b564c2013-07-29 11:44:26 +0530119 exec_in_shell("apt-get update")
Pratik Vyas69951e52014-01-07 18:24:40 +0530120 packages = "python python-setuptools python-dev build-essential python-mysqldb git memcached ntp vim screen htop"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530121 print "-"*80
122 print "Installing Packages: (This may take some time)"
123 print packages
124 print "-"*80
125 exec_in_shell("apt-get install -y %s" % packages)
Pratik Vyas4ec768b2013-10-28 21:57:59 +0530126 global root_password
127 if not root_password:
128 root_password = get_root_password()
129 exec_in_shell("echo mysql-server mysql-server/root_password password %s | sudo debconf-set-selections" % root_password)
130 exec_in_shell("echo mysql-server mysql-server/root_password_again password %s | sudo debconf-set-selections" % root_password)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530131
Pratik Vyas69951e52014-01-07 18:24:40 +0530132 try:
133 exec_in_shell("which mysql")
134 except subprocess.CalledProcessError:
Rushabh Mehta54c15452013-07-16 12:05:41 +0530135 packages = "mysql-server libmysqlclient-dev"
136 print "Installing Packages:", packages
137 exec_in_shell("apt-get install -y %s" % packages)
138
139 update_config_for_debian()
140
141def update_config_for_debian():
Pratik Vyas69951e52014-01-07 18:24:40 +0530142 for service in ("mysql",):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530143 exec_in_shell("service %s restart" % service)
144
145def install_python_modules():
Rushabh Mehta54c15452013-07-16 12:05:41 +0530146 print "-"*80
147 print "Installing Python Modules: (This may take some time)"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530148 print "-"*80
149
Pratik Vyas69951e52014-01-07 18:24:40 +0530150 try:
151 exec_in_shell("which pip2.7")
152 except subprocess.CalledProcessError:
Pratik Vyasa11e1442014-01-07 12:02:45 +0530153 exec_in_shell("easy_install-2.7 pip")
Anand Doshie7d77ca2013-08-14 15:12:46 +0530154
Pratik Vyas69951e52014-01-07 18:24:40 +0530155 exec_in_shell("pip2.7 install --upgrade setuptools --no-use-wheel")
156 exec_in_shell("pip2.7 install --upgrade setuptools")
157 exec_in_shell("pip2.7 install {}".format(' '.join(requirements)))
Anand Doshi08352ca2013-07-17 08:24:50 +0530158
Rushabh Mehta54c15452013-07-16 12:05:41 +0530159def install_erpnext(install_path):
160 print
161 print "-"*80
162 print "Installing ERPNext"
163 print "-"*80
164
165 # ask for details
166 global root_password
167 if not root_password:
168 root_password = get_root_password()
169 test_root_connection(root_password)
170
171 db_name = raw_input("ERPNext Database Name: ")
172 if not db_name:
173 raise Exception, "Sorry! You must specify ERPNext Database Name"
174
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530175 # setup paths
Pratik Vyasfe44d272013-10-24 23:59:53 +0530176 sys.path = [".", "lib", "app"] + sys.path
177 import wnf
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530178
Rushabh Mehta54c15452013-07-16 12:05:41 +0530179 # install database, run patches, update schema
Pratik Vyasfe44d272013-10-24 23:59:53 +0530180 # setup_db(install_path, root_password, db_name)
181 wnf.install(db_name, root_password=root_password)
182
Pratik Vyas18515d32013-11-11 02:52:08 +0530183 setup_cron(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530184
185def get_root_password():
186 # ask for root mysql password
187 import getpass
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530188 root_pwd = None
Rushabh Mehta54c15452013-07-16 12:05:41 +0530189 root_pwd = getpass.getpass("MySQL Root user's Password: ")
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530190 return root_pwd
191
192def test_root_connection(root_pwd):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530193 out = exec_in_shell("mysql -u root %s -e 'exit'" % \
194 (("-p"+root_pwd) if root_pwd else "").replace('$', '\$').replace(' ', '\ '))
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530195 if "access denied" in out.lower():
196 raise Exception("Incorrect MySQL Root user's password")
Rushabh Mehta54c15452013-07-16 12:05:41 +0530197
198def setup_folders(install_path):
Pratik Vyas18515d32013-11-11 02:52:08 +0530199 os.chdir(install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530200 app = os.path.join(install_path, "app")
201 if not os.path.exists(app):
202 print "Cloning erpnext"
Pratik Vyasa11e1442014-01-07 12:02:45 +0530203 exec_in_shell("cd %s && git clone --branch master https://github.com/webnotes/erpnext.git app" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530204 exec_in_shell("cd app && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530205 if not os.path.exists(app):
206 raise Exception, "Couldn't clone erpnext repository"
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530207
Rushabh Mehta54c15452013-07-16 12:05:41 +0530208 lib = os.path.join(install_path, "lib")
209 if not os.path.exists(lib):
210 print "Cloning wnframework"
Pratik Vyasa11e1442014-01-07 12:02:45 +0530211 exec_in_shell("cd %s && git clone --branch master https://github.com/webnotes/wnframework.git lib" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530212 exec_in_shell("cd lib && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530213 if not os.path.exists(lib):
214 raise Exception, "Couldn't clone wnframework repository"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530215
216 public = os.path.join(install_path, "public")
217 for p in [public, os.path.join(public, "files"), os.path.join(public, "backups"),
218 os.path.join(install_path, "logs")]:
219 if not os.path.exists(p):
220 os.mkdir(p)
221
222def setup_conf(install_path, db_name):
223 import os, string, random, re
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530224
Rushabh Mehta54c15452013-07-16 12:05:41 +0530225 # generate db password
226 char_range = string.ascii_letters + string.digits
227 db_password = "".join((random.choice(char_range) for n in xrange(16)))
228
229 # make conf file
230 with open(os.path.join(install_path, "lib", "conf", "conf.py"), "r") as template:
231 conf = template.read()
232
233 conf = re.sub("db_name.*", 'db_name = "%s"' % (db_name,), conf)
234 conf = re.sub("db_password.*", 'db_password = "%s"' % (db_password,), conf)
235
236 with open(os.path.join(install_path, "conf.py"), "w") as conf_file:
237 conf_file.write(conf)
238
239 return db_password
240
Rushabh Mehta54c15452013-07-16 12:05:41 +0530241def post_install(install_path):
Pratik Vyas18515d32013-11-11 02:52:08 +0530242 pass
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530243
Rushabh Mehta54c15452013-07-16 12:05:41 +0530244def exec_in_shell(cmd):
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530245 # using Popen instead of os.system - as recommended by python docs
Pratik Vyas69951e52014-01-07 18:24:40 +0530246 import subprocess
247 out = subprocess.check_output(cmd, shell=True)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530248 return out
249
Pratik Vyas18515d32013-11-11 02:52:08 +0530250def parse_args():
251 parser = argparse.ArgumentParser()
252 parser.add_argument('--create_user', default=False, action='store_true')
253 parser.add_argument('--username', default='erpnext')
254 parser.add_argument('--password', default='erpnext')
255 parser.add_argument('--no_install_prerequisites', default=False, action='store_true')
256 return parser.parse_args()
257
258def create_user(username, password):
259 import subprocess, pwd
260 p = subprocess.Popen("useradd -m -d /home/{username} -s {shell} {username}".format(username=username, shell=os.environ.get('SHELL')).split())
261 p.wait()
262 p = subprocess.Popen("passwd {username}".format(username=username).split(), stdin=subprocess.PIPE)
263 p.communicate('{password}\n{password}\n'.format(password=password))
264 p.wait()
265 return pwd.getpwnam(username).pw_uid
266
267def setup_cron(install_path):
Jev Bjorsellffd851e2013-11-17 16:30:25 -0800268 erpnext_cron_entries = [
Anand Doshi0ebe35b2013-11-18 12:06:33 +0530269 "*/3 * * * * cd %s && python2.7 lib/wnf.py --run_scheduler >> erpnext-sch.log 2>&1" % install_path,
270 "0 */6 * * * cd %s && python2.7 lib/wnf.py --backup >> erpnext-backup.log 2>&1" % install_path
Jev Bjorsellffd851e2013-11-17 16:30:25 -0800271 ]
272 for row in erpnext_cron_entries:
273 try:
274 existing_cron = exec_in_shell("crontab -l")
275 if row not in existing_cron:
276 exec_in_shell('{ crontab -l; echo "%s"; } | crontab' % row)
277 except:
278 exec_in_shell('echo "%s" | crontab' % row)
Pratik Vyas18515d32013-11-11 02:52:08 +0530279
Rushabh Mehta54c15452013-07-16 12:05:41 +0530280if __name__ == "__main__":
Pratik Vyas18515d32013-11-11 02:52:08 +0530281 args = parse_args()
282 install_path = os.getcwd()
283 if os.getuid() != 0 and args.create_user and not args.no_install_prequisites:
284 raise Exception, "Please run this script as root"
285
286 if args.create_user:
287 uid = create_user(args.username, args.password)
288 install_path = '/home/{username}/erpnext'.format(username=args.username)
289
290 if not args.no_install_prerequisites:
291 install_pre_requisites()
292
293 if os.environ.get('SUDO_UID') and not args.create_user:
294 os.setuid(int(os.environ.get('SUDO_UID')))
295
296 if os.getuid() == 0 and args.create_user:
297 os.setuid(uid)
298 if install_path:
299 os.mkdir(install_path)
300
301 install(install_path=install_path)
302 print
303 print "-"*80
304 print "Installation complete"
305 print "To start the development server,"
306 print "Login as {username} with password {password}".format(username=args.username, password=args.password)
307 print "cd {}".format(install_path)
308 print "./lib/wnf.py --serve"
309 print "-"*80
310 print "Open your browser and go to http://localhost:8000"
311 print "Login using username = Administrator and password = admin"