blob: fe17e2f5745ced5429451663e981f84e8ead99c9 [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
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +05308
Rushabh Mehta54c15452013-07-16 12:05:41 +05309is_redhat = is_debian = None
10root_password = None
11
Anand Doshi0ebe35b2013-11-18 12:06:33 +053012requirements = [
13 "chardet",
14 "cssmin",
15 "dropbox",
16 "google-api-python-client",
17 "gunicorn",
18 "httplib2",
19 "jinja2",
20 "markdown2",
21 "markupsafe",
22 "mysql-python",
23 "pygeoip",
24 "python-dateutil",
25 "python-memcached",
26 "pytz==2013d",
27 "requests",
28 "six",
29 "slugify",
30 "termcolor",
31 "werkzeug"
Pratik Vyasfe44d272013-10-24 23:59:53 +053032]
33
Pratik Vyas18515d32013-11-11 02:52:08 +053034def install(install_path):
Pratik Vyasfe44d272013-10-24 23:59:53 +053035 setup_folders(install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +053036 install_erpnext(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +053037
Rushabh Mehta54c15452013-07-16 12:05:41 +053038 post_install(install_path)
39
40def install_pre_requisites():
41 global is_redhat, is_debian
42 is_redhat, is_debian = validate_install()
43 if is_redhat:
44 install_using_yum()
45 elif is_debian:
46 install_using_apt()
47
48 install_python_modules()
49
50 print "-"*80
51 print "Pre-requisites Installed"
52 print "-"*80
53
54def validate_install():
55 import platform
56
57 # check os
58 operating_system = platform.system()
59 print "Operating System =", operating_system
60 if operating_system != "Linux":
61 raise Exception, "Sorry! This installer works only for Linux based Operating Systems"
62
63 # check python version
64 python_version = sys.version.split(" ")[0]
65 print "Python Version =", python_version
Pratik Vyasfe44d272013-10-24 23:59:53 +053066 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 +053067 raise Exception, "Hey! ERPNext needs Python version to be 2.7+"
Rushabh Mehta54c15452013-07-16 12:05:41 +053068
69 # check distribution
70 distribution = platform.linux_distribution()[0].lower().replace('"', '')
71 print "Distribution = ", distribution
Luis Perez5651d982013-11-07 14:53:49 -060072 is_redhat = distribution in ("redhat", "red hat enterprise linux server", "centos", "centos linux", "fedora")
Nabin Haitada70992013-08-20 10:58:07 +053073 is_debian = distribution in ("debian", "ubuntu", "elementary os", "linuxmint")
Rushabh Mehta54c15452013-07-16 12:05:41 +053074
75 if not (is_redhat or is_debian):
76 raise Exception, "Sorry! This installer works only with yum or apt-get package management"
77
78 return is_redhat, is_debian
79
80def install_using_yum():
Pratik Vyasfe44d272013-10-24 23:59:53 +053081 packages = "python python-setuptools gcc python-devel MySQL-python git memcached ntp vim-enhanced screen"
Rushabh Mehta54c15452013-07-16 12:05:41 +053082
83 print "-"*80
84 print "Installing Packages: (This may take some time)"
85 print packages
86 print "-"*80
87 exec_in_shell("yum install -y %s" % packages)
88
89 if not exec_in_shell("which mysql"):
90 packages = "mysql mysql-server mysql-devel"
91 print "Installing Packages:", packages
92 exec_in_shell("yum install -y %s" % packages)
93 exec_in_shell("service mysqld restart")
94
95 # set a root password post install
96 global root_password
97 print "Please create a password for root user of MySQL"
98 root_password = (get_root_password() or "erpnext").strip()
99 exec_in_shell('mysqladmin -u root password "%s"' % (root_password,))
100 print "Root password set as", root_password
101
102 # install htop
103 if not exec_in_shell("which htop"):
104 try:
105 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")
106 except:
107 pass
108
109 update_config_for_redhat()
110
111def update_config_for_redhat():
112 import re
113
Rushabh Mehta54c15452013-07-16 12:05:41 +0530114 # set to autostart on startup
Pratik Vyas18515d32013-11-11 02:52:08 +0530115 for service in ("mysqld", "memcached", "ntpd"):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530116 exec_in_shell("chkconfig --level 2345 %s on" % service)
117 exec_in_shell("service %s restart" % service)
118
119def install_using_apt():
Anand Doshib1b564c2013-07-29 11:44:26 +0530120 exec_in_shell("apt-get update")
Pratik Vyasfe44d272013-10-24 23:59:53 +0530121 packages = "python python-setuptools python-dev build-essential python-pip python-mysqldb git memcached ntp vim screen htop"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530122 print "-"*80
123 print "Installing Packages: (This may take some time)"
124 print packages
125 print "-"*80
126 exec_in_shell("apt-get install -y %s" % packages)
Pratik Vyas4ec768b2013-10-28 21:57:59 +0530127 global root_password
128 if not root_password:
129 root_password = get_root_password()
130 exec_in_shell("echo mysql-server mysql-server/root_password password %s | sudo debconf-set-selections" % root_password)
131 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 +0530132
133 if not exec_in_shell("which mysql"):
134 packages = "mysql-server libmysqlclient-dev"
135 print "Installing Packages:", packages
136 exec_in_shell("apt-get install -y %s" % packages)
137
138 update_config_for_debian()
139
140def update_config_for_debian():
Pratik Vyasf4081c82013-10-28 14:43:00 +0530141 for service in ("mysql", "ntpd"):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530142 exec_in_shell("service %s restart" % service)
143
144def install_python_modules():
Rushabh Mehta54c15452013-07-16 12:05:41 +0530145 print "-"*80
146 print "Installing Python Modules: (This may take some time)"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530147 print "-"*80
148
Anand Doshie7d77ca2013-08-14 15:12:46 +0530149 if not exec_in_shell("which pip"):
150 exec_in_shell("easy_install pip")
151
152 exec_in_shell("pip install --upgrade pip")
Anand Doshi3a6f4f82013-09-12 19:10:05 +0530153 exec_in_shell("pip install --upgrade setuptools")
Anand Doshie7d77ca2013-08-14 15:12:46 +0530154 exec_in_shell("pip install --upgrade virtualenv")
Pratik Vyas4ec768b2013-10-28 21:57:59 +0530155 exec_in_shell("pip install {}".format(' '.join(requirements)))
Anand Doshi08352ca2013-07-17 08:24:50 +0530156
Rushabh Mehta54c15452013-07-16 12:05:41 +0530157def install_erpnext(install_path):
158 print
159 print "-"*80
160 print "Installing ERPNext"
161 print "-"*80
162
163 # ask for details
164 global root_password
165 if not root_password:
166 root_password = get_root_password()
167 test_root_connection(root_password)
168
169 db_name = raw_input("ERPNext Database Name: ")
170 if not db_name:
171 raise Exception, "Sorry! You must specify ERPNext Database Name"
172
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530173 # setup paths
Pratik Vyasfe44d272013-10-24 23:59:53 +0530174 sys.path = [".", "lib", "app"] + sys.path
175 import wnf
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530176
Rushabh Mehta54c15452013-07-16 12:05:41 +0530177 # install database, run patches, update schema
Pratik Vyasfe44d272013-10-24 23:59:53 +0530178 # setup_db(install_path, root_password, db_name)
179 wnf.install(db_name, root_password=root_password)
180
Pratik Vyas18515d32013-11-11 02:52:08 +0530181 setup_cron(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530182
183def get_root_password():
184 # ask for root mysql password
185 import getpass
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530186 root_pwd = None
Rushabh Mehta54c15452013-07-16 12:05:41 +0530187 root_pwd = getpass.getpass("MySQL Root user's Password: ")
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530188 return root_pwd
189
190def test_root_connection(root_pwd):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530191 out = exec_in_shell("mysql -u root %s -e 'exit'" % \
192 (("-p"+root_pwd) if root_pwd else "").replace('$', '\$').replace(' ', '\ '))
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530193 if "access denied" in out.lower():
194 raise Exception("Incorrect MySQL Root user's password")
Rushabh Mehta54c15452013-07-16 12:05:41 +0530195
196def setup_folders(install_path):
Pratik Vyas18515d32013-11-11 02:52:08 +0530197 os.chdir(install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530198 app = os.path.join(install_path, "app")
199 if not os.path.exists(app):
200 print "Cloning erpnext"
Pratik Vyasf64dfa12013-10-31 11:43:51 +0530201 exec_in_shell("cd %s && git clone https://github.com/webnotes/erpnext.git app" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530202 exec_in_shell("cd app && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530203 if not os.path.exists(app):
204 raise Exception, "Couldn't clone erpnext repository"
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530205
Rushabh Mehta54c15452013-07-16 12:05:41 +0530206 lib = os.path.join(install_path, "lib")
207 if not os.path.exists(lib):
208 print "Cloning wnframework"
Pratik Vyasf64dfa12013-10-31 11:43:51 +0530209 exec_in_shell("cd %s && git clone https://github.com/webnotes/wnframework.git lib" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530210 exec_in_shell("cd lib && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530211 if not os.path.exists(lib):
212 raise Exception, "Couldn't clone wnframework repository"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530213
214 public = os.path.join(install_path, "public")
215 for p in [public, os.path.join(public, "files"), os.path.join(public, "backups"),
216 os.path.join(install_path, "logs")]:
217 if not os.path.exists(p):
218 os.mkdir(p)
219
220def setup_conf(install_path, db_name):
221 import os, string, random, re
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530222
Rushabh Mehta54c15452013-07-16 12:05:41 +0530223 # generate db password
224 char_range = string.ascii_letters + string.digits
225 db_password = "".join((random.choice(char_range) for n in xrange(16)))
226
227 # make conf file
228 with open(os.path.join(install_path, "lib", "conf", "conf.py"), "r") as template:
229 conf = template.read()
230
231 conf = re.sub("db_name.*", 'db_name = "%s"' % (db_name,), conf)
232 conf = re.sub("db_password.*", 'db_password = "%s"' % (db_password,), conf)
233
234 with open(os.path.join(install_path, "conf.py"), "w") as conf_file:
235 conf_file.write(conf)
236
237 return db_password
238
Rushabh Mehta54c15452013-07-16 12:05:41 +0530239def post_install(install_path):
Pratik Vyas18515d32013-11-11 02:52:08 +0530240 pass
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530241
Rushabh Mehta54c15452013-07-16 12:05:41 +0530242def exec_in_shell(cmd):
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530243 # using Popen instead of os.system - as recommended by python docs
Rushabh Mehta54c15452013-07-16 12:05:41 +0530244 from subprocess import Popen
245 import tempfile
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530246
Rushabh Mehta54c15452013-07-16 12:05:41 +0530247 with tempfile.TemporaryFile() as stdout:
248 with tempfile.TemporaryFile() as stderr:
249 p = Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
250 p.wait()
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530251
Rushabh Mehta54c15452013-07-16 12:05:41 +0530252 stdout.seek(0)
253 out = stdout.read()
Nabin Hait096d3632013-10-17 17:01:14 +0530254 if out: out = out.decode('utf-8')
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530255
Rushabh Mehta54c15452013-07-16 12:05:41 +0530256 stderr.seek(0)
257 err = stderr.read()
Nabin Hait096d3632013-10-17 17:01:14 +0530258 if err: err = err.decode('utf-8')
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530259
Rushabh Mehta54c15452013-07-16 12:05:41 +0530260 if err and any((kw in err.lower() for kw in ["traceback", "error", "exception"])):
261 print out
262 raise Exception, err
263 else:
264 print "."
265
266 return out
267
Pratik Vyas18515d32013-11-11 02:52:08 +0530268def parse_args():
269 parser = argparse.ArgumentParser()
270 parser.add_argument('--create_user', default=False, action='store_true')
271 parser.add_argument('--username', default='erpnext')
272 parser.add_argument('--password', default='erpnext')
273 parser.add_argument('--no_install_prerequisites', default=False, action='store_true')
274 return parser.parse_args()
275
276def create_user(username, password):
277 import subprocess, pwd
278 p = subprocess.Popen("useradd -m -d /home/{username} -s {shell} {username}".format(username=username, shell=os.environ.get('SHELL')).split())
279 p.wait()
280 p = subprocess.Popen("passwd {username}".format(username=username).split(), stdin=subprocess.PIPE)
281 p.communicate('{password}\n{password}\n'.format(password=password))
282 p.wait()
283 return pwd.getpwnam(username).pw_uid
284
285def setup_cron(install_path):
Jev Bjorsellffd851e2013-11-17 16:30:25 -0800286 erpnext_cron_entries = [
Anand Doshi0ebe35b2013-11-18 12:06:33 +0530287 "*/3 * * * * cd %s && python2.7 lib/wnf.py --run_scheduler >> erpnext-sch.log 2>&1" % install_path,
288 "0 */6 * * * cd %s && python2.7 lib/wnf.py --backup >> erpnext-backup.log 2>&1" % install_path
Jev Bjorsellffd851e2013-11-17 16:30:25 -0800289 ]
290 for row in erpnext_cron_entries:
291 try:
292 existing_cron = exec_in_shell("crontab -l")
293 if row not in existing_cron:
294 exec_in_shell('{ crontab -l; echo "%s"; } | crontab' % row)
295 except:
296 exec_in_shell('echo "%s" | crontab' % row)
Pratik Vyas18515d32013-11-11 02:52:08 +0530297
Rushabh Mehta54c15452013-07-16 12:05:41 +0530298if __name__ == "__main__":
Pratik Vyas18515d32013-11-11 02:52:08 +0530299 args = parse_args()
300 install_path = os.getcwd()
301 if os.getuid() != 0 and args.create_user and not args.no_install_prequisites:
302 raise Exception, "Please run this script as root"
303
304 if args.create_user:
305 uid = create_user(args.username, args.password)
306 install_path = '/home/{username}/erpnext'.format(username=args.username)
307
308 if not args.no_install_prerequisites:
309 install_pre_requisites()
310
311 if os.environ.get('SUDO_UID') and not args.create_user:
312 os.setuid(int(os.environ.get('SUDO_UID')))
313
314 if os.getuid() == 0 and args.create_user:
315 os.setuid(uid)
316 if install_path:
317 os.mkdir(install_path)
318
319 install(install_path=install_path)
320 print
321 print "-"*80
322 print "Installation complete"
323 print "To start the development server,"
324 print "Login as {username} with password {password}".format(username=args.username, password=args.password)
325 print "cd {}".format(install_path)
326 print "./lib/wnf.py --serve"
327 print "-"*80
328 print "Open your browser and go to http://localhost:8000"
329 print "Login using username = Administrator and password = admin"