forked from frappe/erpnext
-
Notifications
You must be signed in to change notification settings - Fork 19
/
install_erpnext.py
311 lines (254 loc) · 9.6 KB
/
install_erpnext.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# 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
import subprocess
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 = "gcc 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)
try:
exec_in_shell("which mysql")
except subprocess.CalledProcessError:
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
update_config_for_redhat()
def update_config_for_redhat():
import re
# set to autostart on startup
for service in ("mysqld", "memcached"):
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-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)
try:
exec_in_shell("which mysql")
except subprocess.CalledProcessError:
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",):
exec_in_shell("service %s restart" % service)
def install_python_modules():
print "-"*80
print "Installing Python Modules: (This may take some time)"
print "-"*80
try:
exec_in_shell("which pip2.7")
except subprocess.CalledProcessError:
exec_in_shell("easy_install-2.7 pip")
exec_in_shell("pip2.7 install --upgrade setuptools --no-use-wheel")
exec_in_shell("pip2.7 install --upgrade setuptools")
exec_in_shell("pip2.7 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 --branch master 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 --branch master 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
import subprocess
out = subprocess.check_output(cmd, shell=True)
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 https://localhost:8000"
print "Login using username = Administrator and password = admin"