blob: 66138496bb6098930d695481f0b8bff5b83b8b6a [file] [log] [blame]
Rushabh Mehta54c15452013-07-16 12:05:41 +05301#!/usr/bin/env python
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +05302from __future__ import unicode_literals
Rushabh Mehta54c15452013-07-16 12:05:41 +05303import os, sys
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +05304
Rushabh Mehta54c15452013-07-16 12:05:41 +05305apache_user = None
6is_redhat = is_debian = None
7root_password = None
8
9def install(install_path=None):
10 install_pre_requisites()
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +053011
Rushabh Mehta54c15452013-07-16 12:05:41 +053012 if not install_path:
13 install_path = os.getcwd()
14 install_erpnext(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +053015
Rushabh Mehta54c15452013-07-16 12:05:41 +053016 post_install(install_path)
17
18def install_pre_requisites():
19 global is_redhat, is_debian
20 is_redhat, is_debian = validate_install()
21 if is_redhat:
22 install_using_yum()
23 elif is_debian:
24 install_using_apt()
25
26 install_python_modules()
27
28 print "-"*80
29 print "Pre-requisites Installed"
30 print "-"*80
31
32def validate_install():
33 import platform
34
35 # check os
36 operating_system = platform.system()
37 print "Operating System =", operating_system
38 if operating_system != "Linux":
39 raise Exception, "Sorry! This installer works only for Linux based Operating Systems"
40
41 # check python version
42 python_version = sys.version.split(" ")[0]
43 print "Python Version =", python_version
44 if not (python_version and int(python_version.split(".")[0])==2 and int(python_version.split(".")[1]) >= 6):
45 raise Exception, "Hey! ERPNext needs Python version to be 2.6+"
46
47 # check distribution
48 distribution = platform.linux_distribution()[0].lower().replace('"', '')
49 print "Distribution = ", distribution
Anand Doshib1b564c2013-07-29 11:44:26 +053050 is_redhat = distribution in ("redhat", "centos", "centos linux", "fedora")
Rushabh Mehta54c15452013-07-16 12:05:41 +053051 is_debian = distribution in ("debian", "ubuntu", "elementary os")
52
53 if not (is_redhat or is_debian):
54 raise Exception, "Sorry! This installer works only with yum or apt-get package management"
55
56 return is_redhat, is_debian
57
58def install_using_yum():
59 packages = "python python-setuptools MySQL-python httpd git memcached ntp vim-enhanced screen"
60
61 print "-"*80
62 print "Installing Packages: (This may take some time)"
63 print packages
64 print "-"*80
65 exec_in_shell("yum install -y %s" % packages)
66
67 if not exec_in_shell("which mysql"):
68 packages = "mysql mysql-server mysql-devel"
69 print "Installing Packages:", packages
70 exec_in_shell("yum install -y %s" % packages)
71 exec_in_shell("service mysqld restart")
72
73 # set a root password post install
74 global root_password
75 print "Please create a password for root user of MySQL"
76 root_password = (get_root_password() or "erpnext").strip()
77 exec_in_shell('mysqladmin -u root password "%s"' % (root_password,))
78 print "Root password set as", root_password
79
80 # install htop
81 if not exec_in_shell("which htop"):
82 try:
83 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")
84 except:
85 pass
86
87 update_config_for_redhat()
88
89def update_config_for_redhat():
90 import re
91
92 global apache_user
93 apache_user = "apache"
94
95 # update memcache user
96 with open("/etc/sysconfig/memcached", "r") as original:
97 memcached_conf = original.read()
98 with open("/etc/sysconfig/memcached", "w") as modified:
99 modified.write(re.sub('USER.*', 'USER="%s"' % apache_user, memcached_conf))
100
101 # set to autostart on startup
102 for service in ("mysqld", "httpd", "memcached", "ntpd"):
103 exec_in_shell("chkconfig --level 2345 %s on" % service)
104 exec_in_shell("service %s restart" % service)
105
106def install_using_apt():
Anand Doshib1b564c2013-07-29 11:44:26 +0530107 exec_in_shell("apt-get update")
Rushabh Mehta54c15452013-07-16 12:05:41 +0530108 packages = "python python-setuptools python-mysqldb apache2 git memcached ntp vim screen htop"
109 print "-"*80
110 print "Installing Packages: (This may take some time)"
111 print packages
112 print "-"*80
113 exec_in_shell("apt-get install -y %s" % packages)
114
115 if not exec_in_shell("which mysql"):
116 packages = "mysql-server libmysqlclient-dev"
117 print "Installing Packages:", packages
118 exec_in_shell("apt-get install -y %s" % packages)
119
120 update_config_for_debian()
121
122def update_config_for_debian():
123 global apache_user
124 apache_user = "www-data"
125
126 # update memcache user
127 with open("/etc/memcached.conf", "r") as original:
128 memcached_conf = original.read()
129 with open("/etc/memcached.conf", "w") as modified:
130 modified.write(memcached_conf.replace("-u memcache", "-u %s" % apache_user))
131
132 exec_in_shell("a2enmod rewrite")
133
134 for service in ("mysql", "apache2", "memcached", "ntpd"):
135 exec_in_shell("service %s restart" % service)
136
137def install_python_modules():
Anand Doshi08352ca2013-07-17 08:24:50 +0530138 python_modules = "pytz python-dateutil jinja2 markdown2 termcolor python-memcached requests chardet dropbox google-api-python-client pygeoip"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530139
140 print "-"*80
141 print "Installing Python Modules: (This may take some time)"
142 print python_modules
143 print "-"*80
144
145 exec_in_shell("easy_install pip")
146 exec_in_shell("pip install -q %s" % python_modules)
Anand Doshi08352ca2013-07-17 08:24:50 +0530147
Rushabh Mehta54c15452013-07-16 12:05:41 +0530148def install_erpnext(install_path):
149 print
150 print "-"*80
151 print "Installing ERPNext"
152 print "-"*80
153
154 # ask for details
155 global root_password
156 if not root_password:
157 root_password = get_root_password()
158 test_root_connection(root_password)
159
160 db_name = raw_input("ERPNext Database Name: ")
161 if not db_name:
162 raise Exception, "Sorry! You must specify ERPNext Database Name"
163
164 # install folders and conf
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530165 setup_folders(install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530166 setup_conf(install_path, db_name)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530167
168 # setup paths
Rushabh Mehta54c15452013-07-16 12:05:41 +0530169 sys.path.extend([".", "lib", "app"])
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530170
Rushabh Mehta54c15452013-07-16 12:05:41 +0530171 # install database, run patches, update schema
172 setup_db(install_path, root_password, db_name)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530173
Rushabh Mehta54c15452013-07-16 12:05:41 +0530174 setup_cron(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530175
Rushabh Mehta54c15452013-07-16 12:05:41 +0530176 setup_apache_conf(install_path)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530177
178def get_root_password():
179 # ask for root mysql password
180 import getpass
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530181 root_pwd = None
Rushabh Mehta54c15452013-07-16 12:05:41 +0530182 root_pwd = getpass.getpass("MySQL Root user's Password: ")
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530183 return root_pwd
184
185def test_root_connection(root_pwd):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530186 out = exec_in_shell("mysql -u root %s -e 'exit'" % \
187 (("-p"+root_pwd) if root_pwd else "").replace('$', '\$').replace(' ', '\ '))
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530188 if "access denied" in out.lower():
189 raise Exception("Incorrect MySQL Root user's password")
Rushabh Mehta54c15452013-07-16 12:05:41 +0530190
191def setup_folders(install_path):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530192 app = os.path.join(install_path, "app")
193 if not os.path.exists(app):
194 print "Cloning erpnext"
Anand Doshi08352ca2013-07-17 08:24:50 +0530195 exec_in_shell("cd %s && git clone https://github.com/webnotes/erpnext.git app" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530196 exec_in_shell("cd app && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530197 if not os.path.exists(app):
198 raise Exception, "Couldn't clone erpnext repository"
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530199
Rushabh Mehta54c15452013-07-16 12:05:41 +0530200 lib = os.path.join(install_path, "lib")
201 if not os.path.exists(lib):
202 print "Cloning wnframework"
Anand Doshi08352ca2013-07-17 08:24:50 +0530203 exec_in_shell("cd %s && git clone https://github.com/webnotes/wnframework.git lib" % install_path)
Rushabh Mehta54c15452013-07-16 12:05:41 +0530204 exec_in_shell("cd lib && git config core.filemode false")
Anand Doshi7d3e75d2013-07-24 19:01:02 +0530205 if not os.path.exists(lib):
206 raise Exception, "Couldn't clone wnframework repository"
Rushabh Mehta54c15452013-07-16 12:05:41 +0530207
208 public = os.path.join(install_path, "public")
209 for p in [public, os.path.join(public, "files"), os.path.join(public, "backups"),
210 os.path.join(install_path, "logs")]:
211 if not os.path.exists(p):
212 os.mkdir(p)
213
214def setup_conf(install_path, db_name):
215 import os, string, random, re
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530216
Rushabh Mehta54c15452013-07-16 12:05:41 +0530217 # generate db password
218 char_range = string.ascii_letters + string.digits
219 db_password = "".join((random.choice(char_range) for n in xrange(16)))
220
221 # make conf file
222 with open(os.path.join(install_path, "lib", "conf", "conf.py"), "r") as template:
223 conf = template.read()
224
225 conf = re.sub("db_name.*", 'db_name = "%s"' % (db_name,), conf)
226 conf = re.sub("db_password.*", 'db_password = "%s"' % (db_password,), conf)
227
228 with open(os.path.join(install_path, "conf.py"), "w") as conf_file:
229 conf_file.write(conf)
230
231 return db_password
232
233def setup_db(install_path, root_password, db_name):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530234 from webnotes.install_lib.install import Installer
235 inst = Installer("root", root_password)
Anand Doshi08352ca2013-07-17 08:24:50 +0530236 inst.import_from_db(db_name, verbose=1)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530237
Rushabh Mehta54c15452013-07-16 12:05:41 +0530238 # run patches and sync
Rushabh Mehta54c15452013-07-16 12:05:41 +0530239 exec_in_shell("./lib/wnf.py --patch_sync_build")
240
241def setup_cron(install_path):
Rushabh Mehta54c15452013-07-16 12:05:41 +0530242 erpnext_cron_entries = [
243 "*/3 * * * * cd %s && python lib/wnf.py --run_scheduler >> /var/log/erpnext-sch.log 2>&1" % install_path,
244 "0 */6 * * * cd %s && python lib/wnf.py --backup >> /var/log/erpnext-backup.log 2>&1" % install_path
245 ]
246
247 for row in erpnext_cron_entries:
248 try:
249 existing_cron = exec_in_shell("crontab -l")
250 if row not in existing_cron:
251 exec_in_shell('{ crontab -l; echo "%s"; } | crontab' % row)
252 except:
253 exec_in_shell('echo "%s" | crontab' % row)
254
255def setup_apache_conf(install_path):
256 apache_conf_content = """Listen 8080
257NameVirtualHost *:8080
258<VirtualHost *:8080>
259 ServerName localhost
260 DocumentRoot %s/public/
261
262 AddHandler cgi-script .cgi .xml .py
263 AddType application/vnd.ms-fontobject .eot
264 AddType font/ttf .ttf
265 AddType font/otf .otf
266 AddType application/x-font-woff .woff
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530267
Rushabh Mehta54c15452013-07-16 12:05:41 +0530268 <Directory %s/public/>
269 # directory specific options
270 Options -Indexes +FollowSymLinks +ExecCGI
271
272 # directory's index file
273 DirectoryIndex web.py
274
275 AllowOverride all
276 Order Allow,Deny
277 Allow from all
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530278
Rushabh Mehta54c15452013-07-16 12:05:41 +0530279 # rewrite rule
280 RewriteEngine on
281 RewriteCond %%{REQUEST_FILENAME} !-f
282 RewriteCond %%{REQUEST_FILENAME} !-d
283 RewriteCond %%{REQUEST_FILENAME} !-l
284 RewriteRule ^([^/]+)$ /web.py?page=$1 [QSA,L]
285 </Directory>
286</VirtualHost>""" % (install_path, install_path)
287
288 new_apache_conf_path = os.path.join(install_path, os.path.basename(install_path)+".conf")
289 with open(new_apache_conf_path, "w") as apache_conf_file:
290 apache_conf_file.write(apache_conf_content)
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530291
Rushabh Mehta54c15452013-07-16 12:05:41 +0530292def post_install(install_path):
293 global apache_user
294 exec_in_shell("chown -R %s %s" % (apache_user, install_path))
295
296 apache_conf_filename = os.path.basename(install_path)+".conf"
297 if is_redhat:
298 os.symlink(os.path.join(install_path, apache_conf_filename),
299 os.path.join("/etc/httpd/conf.d", apache_conf_filename))
300 exec_in_shell("service httpd restart")
301
302 elif is_debian:
303 os.symlink(os.path.join(install_path, apache_conf_filename),
304 os.path.join("/etc/apache2/sites-enabled", apache_conf_filename))
305 exec_in_shell("service apache2 restart")
306
307 print
308 print "-"*80
309 print "Installation complete"
310 print "Open your browser and go to http://localhost:8080"
311 print "Login using username = Administrator and password = admin"
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530312
Rushabh Mehta54c15452013-07-16 12:05:41 +0530313def exec_in_shell(cmd):
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530314 # using Popen instead of os.system - as recommended by python docs
Rushabh Mehta54c15452013-07-16 12:05:41 +0530315 from subprocess import Popen
316 import tempfile
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530317
Rushabh Mehta54c15452013-07-16 12:05:41 +0530318 with tempfile.TemporaryFile() as stdout:
319 with tempfile.TemporaryFile() as stderr:
320 p = Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
321 p.wait()
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530322
Rushabh Mehta54c15452013-07-16 12:05:41 +0530323 stdout.seek(0)
324 out = stdout.read()
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530325
Rushabh Mehta54c15452013-07-16 12:05:41 +0530326 stderr.seek(0)
327 err = stderr.read()
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530328
Rushabh Mehta54c15452013-07-16 12:05:41 +0530329 if err and any((kw in err.lower() for kw in ["traceback", "error", "exception"])):
330 print out
331 raise Exception, err
332 else:
333 print "."
334
335 return out
336
337if __name__ == "__main__":
Rushabh Mehtaa8f9aa02013-06-21 17:55:43 +0530338 install()