-
Notifications
You must be signed in to change notification settings - Fork 20
/
generate_env.py
executable file
·303 lines (246 loc) · 7.24 KB
/
generate_env.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
#!/usr/bin/python3
import secrets
import os
import sys
from shutil import which
ENV_TEMPLATE = """
# Django cryptographic salt
CALCUS_SECRET_KEY='{}'
# Object hash ids cryptographic salt
CALCUS_HASH_ID_SALT='{}'
{}
# Number of OpenMPI threads to use (xTB)
OMP_NUM_THREADS={},1
# Number of cores to use for local calculations
NUM_CPU={}
# Amount of memory allocated per core/thread in MB (must be whole number)
OMP_STACKSIZE={}M
# Use cached calculation logs during testing (for developers)
USE_CACHED_LOGS=true
# Password of the PostgreSQL database (SENSITIVE)
POSTGRES_PASSWORD={}
# User ID to use (Linux/Mac only)
UID={}
# Group ID to use (Linux/Mac only)
GID={}
# Username of the superuser account (automatically created on startup if does not already exist)
CALCUS_SU_EMAIL={}
# Ping server to participate in user statistics (True/False)
CALCUS_PING_SATELLITE={}
# Randomly generated user code to identify this instance of CalcUS
CALCUS_PING_CODE={}
"""
OVERRIDE_TEMPLATE = """
version: "3.4"
services:
celery_comp:
volumes:
- .:/calcus
{}"""
TEST_OVERRIDE_TEMPLATE = """
version: "3.4"
services:
web:
volumes:
- .:/calcus
{}
"""
cwd = os.getcwd()
if not os.path.isdir("data"):
os.mkdir("data")
def gen_key():
length = 50
chars = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)"
return "".join(secrets.choice(chars) for i in range(length))
def gen_chars():
length = 50
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
return "".join(secrets.choice(chars) for i in range(length))
def valid_dir(path, name):
if path == "":
return False
if not os.path.isdir(path):
return False
if not os.path.isfile(os.path.join(path, name)):
return False
return True
def find_software(name):
ret = which(name)
if ret is not None:
print(f"{name} found in $PATH ({ret})")
return os.path.dirname(ret)
ans = ""
while True:
ans = input(
f"{name} has not been found in $PATH. Will you use this software locally? (Y/N)\n"
)
if ans.lower() in ["y", "n"]:
break
print("Invalid option")
if ans.lower() == "n":
print(f"{name} skipped")
return ""
path = ""
while True:
path = input(
"What is the full path to the directory containing this software?\n"
)
if not valid_dir(path, name):
print(
f"The specified path is invalid or does not contain the software {name}"
)
else:
break
return path
def get_env_backup_path():
for i in range(1, 51):
path = os.path.join("backups", f"env.backup.{i}")
if not os.path.isfile(path):
return path
else:
path = "env.backup.latest"
print(f"Too many backup environment files! Writing to {path}")
return path
HEADER = """
******************************
CalcUS parameters file creator
******************************
"""
print(HEADER)
if os.path.isfile(".env"):
print(
"The local directory already contains a .env file. Delete it if you want to create a new one."
)
exit(0)
secret_key = gen_key()
hash_salt = gen_key()
print("Secret key and hash salt generated\n")
postgres = gen_chars()
print("PostgreSQL password generated\n")
while True:
su_email = input(
"Enter an email for the superuser account. This account will have the password 'default'. Make sure to change this password in the profile.\n"
)
if su_email.strip() == "":
print("Invalid username\n")
else:
break
print("\n")
software_list = {
"g16": "# Path to the directory directly containing g16 (e.g. /home/user/software/gaussian)\nCALCUS_GAUSSIAN={}\n",
"orca": "# Path to the directory directly containing orca\nCALCUS_ORCA={}\n",
}
override_list = {
"g16": [" - ${CALCUS_GAUSSIAN}:/binaries/g16"],
"orca": [" - ${CALCUS_ORCA}:/binaries/orca"],
}
software_paths = ""
override_content = ""
local_calc = False
for s, path_template in software_list.items():
path = find_software(s)
if path != "":
software_paths += path_template.format(path)
for p in override_list[s]:
override_content += p + "\n"
local_calc = True
if local_calc:
while True:
n = input(
"How many CPU cores do you want CalcUS to use for local calculations? (e.g. 8)\n"
)
try:
num_cpu = int(n)
except ValueError:
print("Invalid number of cores\n")
else:
if num_cpu < 1:
print("Invalid number of cores\n")
else:
break
print("\n")
while True:
n = input(
"How many MB of RAM per core do you want CalcUS to use for local calculations? (e.g. 1024)\n"
)
try:
mem = int(n)
except ValueError:
print("Invalid number of MB\n")
else:
if mem < 1:
print("Invalid number of MB\n")
else:
break
print("\n")
else:
num_cpu = 1
mem = 1024
while True:
ans = input(
"Do you accept to send anonymous signals to indicate that you are using CalcUS? (Y/N)\n\nThis will allow us to estimate how many users are benefiting from this project and report this number to funding agencies. If enabled, CalcUS will send a signal every hour to our server. You will only be identified by a randomly generated code in order to differentiate unique instances. This is optional, but helps the project.\n"
)
if ans.lower() in ["y", "n"]:
break
print("Invalid option\n")
if ans.lower() == "y":
print("Anonymous signals enabled\n")
ping = "True"
ping_code = gen_chars()
print("Code generated")
else:
print("Anonymous signals disabled\n")
ping = "False"
ping_code = "empty"
if sys.platform == "win32":
uid = ""
gid = ""
else:
uid = os.getuid()
gid = os.getgid()
print("Writing .env and docker override files...\n")
with open(".env", "w") as out:
out.write(
ENV_TEMPLATE.format(
secret_key,
hash_salt,
software_paths,
num_cpu,
num_cpu,
mem,
postgres,
uid,
gid,
su_email,
ping,
ping_code,
)
)
with open("docker-compose.override.yml", "w") as out:
out.write(OVERRIDE_TEMPLATE.format(override_content))
with open("test-compose.override.yml", "w") as out:
out.write(TEST_OVERRIDE_TEMPLATE.format(override_content))
print("Creating necessary folders")
for d in ["scr", "keys", "logs", "backups"]:
if not os.path.isdir(d):
os.mkdir(d)
env_backup_path = get_env_backup_path()
print(f"Creating .env backup ({env_backup_path})")
with open(env_backup_path, "w") as out:
out.write(
ENV_TEMPLATE.format(
secret_key,
hash_salt,
software_paths,
num_cpu,
num_cpu,
mem,
postgres,
uid,
gid,
su_email,
ping,
ping_code,
)
)
print("Done! You can now start CalcUS.")