|
a |
|
b/scripts/qiita-test-install |
|
|
1 |
#!/usr/bin/env python |
|
|
2 |
from os import environ |
|
|
3 |
from os.path import join, dirname, abspath, splitext |
|
|
4 |
from sys import platform, version as python_version, exit, executable, stdout |
|
|
5 |
from unittest import TestLoader, TextTestRunner, TestCase |
|
|
6 |
from smtplib import SMTP, SMTP_SSL |
|
|
7 |
|
|
|
8 |
# ----------------------------------------------------------------------------- |
|
|
9 |
# Copyright (c) 2014--, The Qiita Development Team. |
|
|
10 |
# |
|
|
11 |
# Distributed under the terms of the BSD 3-clause License. |
|
|
12 |
# |
|
|
13 |
# The full license is in the file LICENSE, distributed with this software. |
|
|
14 |
# ----------------------------------------------------------------------------- |
|
|
15 |
|
|
|
16 |
core_dependency_missing_msg = ( |
|
|
17 |
'"%s" is missing and is a core requirement, for more information see the ' |
|
|
18 |
'Qiita Installation Guide: ' |
|
|
19 |
'https://github.com/biocore/qiita/blob/master/INSTALL.md') |
|
|
20 |
|
|
|
21 |
extra_info = (core_dependency_missing_msg + '. It is also possible that you ' |
|
|
22 |
'have an old version of this package, if so, please update to ' |
|
|
23 |
'the latest.') |
|
|
24 |
|
|
|
25 |
dependency_missing_msg = ( |
|
|
26 |
'"%s" is missing but this is _not_ a core requirement, for more ' |
|
|
27 |
'information see the Qiita Installation Guide: ' |
|
|
28 |
'https://github.com/biocore/qiita/blob/master/INSTALL.md') |
|
|
29 |
|
|
|
30 |
missing_deps_errors = [] |
|
|
31 |
missing_deps_warnings = [] |
|
|
32 |
|
|
|
33 |
try: |
|
|
34 |
from click import __version__ as click_lib_version |
|
|
35 |
except ImportError as e: |
|
|
36 |
missing_deps_errors.append((e, extra_info % 'click')) |
|
|
37 |
|
|
|
38 |
try: |
|
|
39 |
from numpy import __version__ as numpy_lib_version |
|
|
40 |
except ImportError as e: |
|
|
41 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'NumPy')) |
|
|
42 |
|
|
|
43 |
try: |
|
|
44 |
from pandas import __version__ as pandas_lib_version |
|
|
45 |
except ImportError as e: |
|
|
46 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'pandas')) |
|
|
47 |
|
|
|
48 |
try: |
|
|
49 |
from tornado import version as tornado_lib_version |
|
|
50 |
except ImportError as e: |
|
|
51 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'tornado')) |
|
|
52 |
|
|
|
53 |
try: |
|
|
54 |
from redis import __version__ as redis_lib_version |
|
|
55 |
except ImportError as e: |
|
|
56 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'redis')) |
|
|
57 |
|
|
|
58 |
try: |
|
|
59 |
from toredis import Client as toredis_client |
|
|
60 |
toredis_client() |
|
|
61 |
except ImportError as e: |
|
|
62 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'toredis')) |
|
|
63 |
|
|
|
64 |
try: |
|
|
65 |
from redbiom import __version__ as redbiom_lib_version |
|
|
66 |
except ImportError as e: |
|
|
67 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'redbiom')) |
|
|
68 |
|
|
|
69 |
try: |
|
|
70 |
from bcrypt import __version__ as bcrypt_lib_version |
|
|
71 |
except ImportError as e: |
|
|
72 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'bcrypt')) |
|
|
73 |
|
|
|
74 |
try: |
|
|
75 |
from pyparsing import __version__ as pyparsing_lib_version |
|
|
76 |
except ImportError as e: |
|
|
77 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'pyparsing')) |
|
|
78 |
|
|
|
79 |
try: |
|
|
80 |
from networkx import __version__ as networkx_lib_version |
|
|
81 |
except ImportError as e: |
|
|
82 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'networkx')) |
|
|
83 |
|
|
|
84 |
try: |
|
|
85 |
from wtforms import __version__ as wtforms_lib_version |
|
|
86 |
except ImportError as e: |
|
|
87 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'wtforms')) |
|
|
88 |
|
|
|
89 |
try: |
|
|
90 |
from mock import __version__ as mock_lib_version |
|
|
91 |
except ImportError as e: |
|
|
92 |
missing_deps_warnings.append((e, dependency_missing_msg % 'mock')) |
|
|
93 |
mock_lib_version = 'Not installed' |
|
|
94 |
|
|
|
95 |
try: |
|
|
96 |
from psycopg2 import __version__ as psycopg2_lib_version |
|
|
97 |
except ImportError as e: |
|
|
98 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'psycopg2')) |
|
|
99 |
|
|
|
100 |
try: |
|
|
101 |
from qiita_core import __version__ as qiita_core_lib_version |
|
|
102 |
except ImportError as e: |
|
|
103 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'qiita_core')) |
|
|
104 |
|
|
|
105 |
try: |
|
|
106 |
from qiita_db import __version__ as qiita_db_lib_version |
|
|
107 |
except ImportError as e: |
|
|
108 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'qiita_db')) |
|
|
109 |
|
|
|
110 |
try: |
|
|
111 |
from qiita_pet import __version__ as qiita_pet_lib_version |
|
|
112 |
except ImportError as e: |
|
|
113 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'qiita_pet')) |
|
|
114 |
|
|
|
115 |
try: |
|
|
116 |
from qiita_ware import __version__ as qiita_ware_lib_version |
|
|
117 |
except ImportError as e: |
|
|
118 |
missing_deps_errors.append((e, core_dependency_missing_msg % 'qiita_ware')) |
|
|
119 |
|
|
|
120 |
try: |
|
|
121 |
from qiita_core.configuration_manager import ConfigurationManager |
|
|
122 |
ConfigurationManager() |
|
|
123 |
except Exception as e: |
|
|
124 |
missing_deps_errors.append((e, 'You need to add to your enviroment ' |
|
|
125 |
'the Qiita configuration using ' |
|
|
126 |
'QIITA_CONFIG_FP')) |
|
|
127 |
|
|
|
128 |
if missing_deps_errors: |
|
|
129 |
for e, t in missing_deps_errors: |
|
|
130 |
print('%s\n=============' % (t)) |
|
|
131 |
exit('Missing core dependencies, can not continue.') |
|
|
132 |
|
|
|
133 |
if missing_deps_warnings: |
|
|
134 |
for e, t in missing_deps_errors: |
|
|
135 |
print('%s\n=============' % (t)) |
|
|
136 |
|
|
|
137 |
|
|
|
138 |
# trick flake8 to not complain about module-level imports not being at the top |
|
|
139 |
# of the file. These imports can only really happen if none of the core |
|
|
140 |
# dependencies are missing |
|
|
141 |
if True: |
|
|
142 |
from qiita_db.sql_connection import TRN |
|
|
143 |
from redis import StrictRedis |
|
|
144 |
|
|
|
145 |
|
|
|
146 |
class QiitaConfig(TestCase): |
|
|
147 |
|
|
|
148 |
def setUp(self): |
|
|
149 |
self.config = ConfigurationManager() |
|
|
150 |
try: |
|
|
151 |
with TRN: |
|
|
152 |
TRN.add("SELECT version()") |
|
|
153 |
self.psql_version = TRN.execute_fetchflatten()[0] |
|
|
154 |
except Exception: |
|
|
155 |
self.psql_version = None |
|
|
156 |
try: |
|
|
157 |
r = StrictRedis( |
|
|
158 |
host=self.config.redis_host, |
|
|
159 |
password=self.config.redis_password, |
|
|
160 |
port=self.config.redis_port, |
|
|
161 |
db=self.config.redis_db) |
|
|
162 |
self.redis_version = r.info()['redis_version'] |
|
|
163 |
except Exception: |
|
|
164 |
self.redis_version = None |
|
|
165 |
|
|
|
166 |
def test_pandas_library_version(self): |
|
|
167 |
acceptable_version = (0, 15) |
|
|
168 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
169 |
version = tuple(map(int, pandas_lib_version.split('.'))) |
|
|
170 |
|
|
|
171 |
self.assertTrue(acceptable_version <= version, |
|
|
172 |
'Unsupported pandas version. You have %s but the ' |
|
|
173 |
'minimum required version is %s' |
|
|
174 |
% (pandas_lib_version, string_acceptable_version)) |
|
|
175 |
|
|
|
176 |
def test_torando_library_version(self): |
|
|
177 |
acceptable_version = (3, 1, 1) |
|
|
178 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
179 |
version = tuple(map(int, tornado_lib_version.split('.'))) |
|
|
180 |
|
|
|
181 |
self.assertTrue(acceptable_version <= version, |
|
|
182 |
'Unsupported tornado version. You have %s but the ' |
|
|
183 |
'minimum required version is %s' |
|
|
184 |
% (tornado_lib_version, string_acceptable_version)) |
|
|
185 |
|
|
|
186 |
def test_pyparsing_library_version(self): |
|
|
187 |
acceptable_version = (2, 0, 2) |
|
|
188 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
189 |
version = tuple(map(int, pyparsing_lib_version.split('.'))) |
|
|
190 |
|
|
|
191 |
self.assertTrue(acceptable_version <= version, |
|
|
192 |
'Unsupported pyparsing version. You have %s but the ' |
|
|
193 |
'minimum required version is %s' |
|
|
194 |
% (pyparsing_lib_version, string_acceptable_version)) |
|
|
195 |
|
|
|
196 |
def test_wtforms_library_version(self): |
|
|
197 |
acceptable_version = (2, 0, 1) |
|
|
198 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
199 |
version = tuple(map(int, wtforms_lib_version.split('.'))) |
|
|
200 |
|
|
|
201 |
self.assertTrue(acceptable_version <= version, |
|
|
202 |
'Unsupported WTForms version. You have %s but the ' |
|
|
203 |
'minimum required version is %s' |
|
|
204 |
% (wtforms_lib_version, string_acceptable_version)) |
|
|
205 |
|
|
|
206 |
def test_postgresql_version(self): |
|
|
207 |
if not self.psql_version: |
|
|
208 |
self.assertTrue(False, 'PostgreSQL not running or configured') |
|
|
209 |
|
|
|
210 |
acceptable_version = (9, 3, 0) |
|
|
211 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
212 |
version = tuple(map(int, self.psql_version.split(' ')[1].split('.'))) |
|
|
213 |
|
|
|
214 |
self.assertTrue(acceptable_version <= version, |
|
|
215 |
'Unsupported PostgreSQL version. You have %s but the ' |
|
|
216 |
'minimum required version is %s' |
|
|
217 |
% ('.'.join(map(str, version)), |
|
|
218 |
string_acceptable_version)) |
|
|
219 |
|
|
|
220 |
def test_redis_version(self): |
|
|
221 |
if not self.redis_version: |
|
|
222 |
self.assertTrue(False, 'redis not running or configured') |
|
|
223 |
|
|
|
224 |
acceptable_version = (2, 8, 17) |
|
|
225 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
226 |
version = tuple(map(int, self.redis_version.split('.'))) |
|
|
227 |
|
|
|
228 |
self.assertTrue(acceptable_version <= version, |
|
|
229 |
'Unsupported redis version. You have %s but the ' |
|
|
230 |
'minimum required version is %s' |
|
|
231 |
% ('.'.join(map(str, version)), |
|
|
232 |
string_acceptable_version)) |
|
|
233 |
|
|
|
234 |
def test_redbiom_version(self): |
|
|
235 |
acceptable_version = (0, 3, 8) |
|
|
236 |
string_acceptable_version = '.'.join(map(str, acceptable_version)) |
|
|
237 |
version = tuple(map(int, redbiom_lib_version.split('.'))) |
|
|
238 |
|
|
|
239 |
self.assertTrue(acceptable_version <= version, |
|
|
240 |
'Unsupported redbiom version. You have %s but the ' |
|
|
241 |
'minimum required version is %s' |
|
|
242 |
% ('.'.join(map(str, version)), |
|
|
243 |
string_acceptable_version)) |
|
|
244 |
|
|
|
245 |
|
|
|
246 |
system_info_header = """ |
|
|
247 |
System information |
|
|
248 |
================== |
|
|
249 |
""" |
|
|
250 |
|
|
|
251 |
dependency_info_header = """ |
|
|
252 |
Dependency versions |
|
|
253 |
=================== |
|
|
254 |
""" |
|
|
255 |
|
|
|
256 |
qiita_config_header = """ |
|
|
257 |
Qiita config |
|
|
258 |
============ |
|
|
259 |
For definitions of these settings and to learn how to configure Qiita, visit: |
|
|
260 |
https://github.com/biocore/qiita/blob/master/INSTALL.md#install |
|
|
261 |
""" |
|
|
262 |
|
|
|
263 |
qiita_config_tests_header = """ |
|
|
264 |
Qiita version and configuration tests |
|
|
265 |
===================================== |
|
|
266 |
""" |
|
|
267 |
|
|
|
268 |
qiita_plugins_header = """ |
|
|
269 |
Qiita plugins |
|
|
270 |
============= |
|
|
271 |
""" |
|
|
272 |
|
|
|
273 |
|
|
|
274 |
def main(): |
|
|
275 |
system_info = [ |
|
|
276 |
("Platform", platform), |
|
|
277 |
("Python version", python_version.replace('\n', ' ')), |
|
|
278 |
("Python executable", executable)] |
|
|
279 |
max_len = max([len(e[0]) for e in system_info]) |
|
|
280 |
print(system_info_header) |
|
|
281 |
for v in system_info: |
|
|
282 |
print("%*s:\t%s" % (max_len, v[0], v[1])) |
|
|
283 |
|
|
|
284 |
with TRN: |
|
|
285 |
TRN.add("SELECT current_patch FROM settings") |
|
|
286 |
current_patch = TRN.execute_fetchflatten()[0] |
|
|
287 |
qiita_db_patch_number = splitext(current_patch)[0] |
|
|
288 |
|
|
|
289 |
# Getting required environment variables |
|
|
290 |
if 'REDBIOM_HOST' in environ: |
|
|
291 |
redbiom_host = environ['REDBIOM_HOST'] |
|
|
292 |
else: |
|
|
293 |
redbiom_host = None |
|
|
294 |
|
|
|
295 |
version_info = [ |
|
|
296 |
('click library version', click_lib_version), |
|
|
297 |
('numpy library version', numpy_lib_version), |
|
|
298 |
('pandas library version', pandas_lib_version), |
|
|
299 |
('tornado library version', tornado_lib_version), |
|
|
300 |
('redis library version', redis_lib_version), |
|
|
301 |
('redbiom library version', '%s - host: %s' % ( |
|
|
302 |
redbiom_lib_version, redbiom_host)), |
|
|
303 |
('bcrypt library version', bcrypt_lib_version), |
|
|
304 |
('pyparsing library version', pyparsing_lib_version), |
|
|
305 |
('networkX library version', networkx_lib_version), |
|
|
306 |
('WTForms library version', wtforms_lib_version), |
|
|
307 |
('mock library version', mock_lib_version), |
|
|
308 |
('psycopg2 library version', psycopg2_lib_version), |
|
|
309 |
('Qiita core library version', qiita_core_lib_version), |
|
|
310 |
('Qiita db library version', qiita_db_lib_version), |
|
|
311 |
('Qiita db patch number', qiita_db_patch_number), |
|
|
312 |
('Qiita pet library version', qiita_pet_lib_version), |
|
|
313 |
('Qiita ware library version', qiita_ware_lib_version) |
|
|
314 |
] |
|
|
315 |
max_len = max([len(e[0]) for e in version_info]) |
|
|
316 |
print(dependency_info_header) |
|
|
317 |
for v in version_info: |
|
|
318 |
print("%*s:\t%s" % (max_len, v[0], v[1])) |
|
|
319 |
|
|
|
320 |
extra_info = None |
|
|
321 |
qiita_config = ConfigurationManager() |
|
|
322 |
try: |
|
|
323 |
qiita_conf_fp = environ['QIITA_CONFIG_FP'] |
|
|
324 |
except KeyError: |
|
|
325 |
qiita_conf_fp = join(dirname(abspath(__file__)), |
|
|
326 |
'support_files/config_test.cfg') |
|
|
327 |
smtp = SMTP_SSL() if qiita_config.smtp_ssl else SMTP() |
|
|
328 |
smtp.set_debuglevel(False) |
|
|
329 |
try: |
|
|
330 |
smtp.connect(qiita_config.smtp_host, qiita_config.smtp_port) |
|
|
331 |
smtp.verify |
|
|
332 |
send_email = True |
|
|
333 |
except Exception: |
|
|
334 |
send_email = False |
|
|
335 |
ebi_credentials = (qiita_config.ebi_center_name != '' and |
|
|
336 |
qiita_config.ebi_dropbox_url != '' and |
|
|
337 |
qiita_config.ebi_organization_prefix != '' and |
|
|
338 |
qiita_config.ebi_seq_xfer_pass != '' and |
|
|
339 |
qiita_config.ebi_seq_xfer_url != '' and |
|
|
340 |
qiita_config.ebi_seq_xfer_user != '') |
|
|
341 |
vamps_credentials = (qiita_config.vamps_pass != '' and |
|
|
342 |
qiita_config.vamps_url != '' and |
|
|
343 |
qiita_config.vamps_user != '') |
|
|
344 |
try: |
|
|
345 |
with TRN: |
|
|
346 |
psql_running = True |
|
|
347 |
except Exception: |
|
|
348 |
psql_running = False |
|
|
349 |
try: |
|
|
350 |
StrictRedis( |
|
|
351 |
host=qiita_config.redis_host, |
|
|
352 |
password=qiita_config.redis_password, |
|
|
353 |
port=qiita_config.redis_port, |
|
|
354 |
db=qiita_config.redis_db) |
|
|
355 |
redis_running = True |
|
|
356 |
except Exception: |
|
|
357 |
redis_running = False |
|
|
358 |
|
|
|
359 |
try: |
|
|
360 |
StrictRedis( |
|
|
361 |
host=qiita_config.redbiom_redis_host, |
|
|
362 |
password=qiita_config.redbiom_redis_password, |
|
|
363 |
port=qiita_config.redbiom_redis_port, |
|
|
364 |
db=qiita_config.redbiom_redis_db) |
|
|
365 |
redbiom_redis_running = True |
|
|
366 |
except Exception: |
|
|
367 |
redbiom_redis_running = False |
|
|
368 |
|
|
|
369 |
print(qiita_config_header) |
|
|
370 |
qiita_config_info = [ |
|
|
371 |
('QIITA_CONFIG_FP filepath', qiita_conf_fp), |
|
|
372 |
('Test environment', str(qiita_config.test_environment)), |
|
|
373 |
('Base URL', qiita_config.base_url), |
|
|
374 |
('EBI credentials exist', ebi_credentials), |
|
|
375 |
('VAMPS credentials exist', vamps_credentials), |
|
|
376 |
('Can the system send emails?', str(send_email) + '. When true, ' |
|
|
377 |
'emails could still not be going out due to your network ' |
|
|
378 |
'configuration.'), |
|
|
379 |
('Valid file extensions for upload', ', '.join( |
|
|
380 |
qiita_config.valid_upload_extension)), |
|
|
381 |
('PostgreSQL is up and configuration can connect?', psql_running), |
|
|
382 |
('Redis is up and configuracion can connect?', |
|
|
383 |
redis_running if not redis_running else '%s --port %d' % ( |
|
|
384 |
redis_running, qiita_config.redis_port)), |
|
|
385 |
('Redbiom redis is up and configuracion can connect?', |
|
|
386 |
redbiom_redis_running if not redbiom_redis_running else |
|
|
387 |
'%s --port %d' % (redbiom_redis_running, |
|
|
388 |
qiita_config.redbiom_redis_port)), |
|
|
389 |
('Extra info', extra_info) |
|
|
390 |
] |
|
|
391 |
max_len = max([len(e[0]) for e in qiita_config_info]) |
|
|
392 |
for v in qiita_config_info: |
|
|
393 |
if v != ('Extra info', None): |
|
|
394 |
print("%*s:\t%s" % (max_len, v[0], v[1])) |
|
|
395 |
|
|
|
396 |
print(qiita_plugins_header) |
|
|
397 |
if not psql_running: |
|
|
398 |
print("PostgreSQL not running, can't retrieve plugin information") |
|
|
399 |
else: |
|
|
400 |
try: |
|
|
401 |
import qiita_db as qdb |
|
|
402 |
with qdb.sql_connection.TRN: |
|
|
403 |
sql = """SELECT name, version, client_id, client_secret |
|
|
404 |
FROM qiita.software |
|
|
405 |
JOIN qiita.oauth_software USING (software_id) |
|
|
406 |
JOIN qiita.oauth_identifiers USING (client_id)""" |
|
|
407 |
qdb.sql_connection.TRN.add(sql) |
|
|
408 |
res = qdb.sql_connection.TRN.execute_fetchindex() |
|
|
409 |
for name, version, client_id, client_secret in res: |
|
|
410 |
print("Plugin name: %s" % name) |
|
|
411 |
print("\tVersion: %s" % version) |
|
|
412 |
print("\tClient id: %s" % client_id) |
|
|
413 |
print("\tClient secret: %s" % client_secret) |
|
|
414 |
except Exception as e: |
|
|
415 |
print("An error occurred while retrieving plugin information: %s" |
|
|
416 |
% str(e)) |
|
|
417 |
|
|
|
418 |
print(qiita_config_tests_header) |
|
|
419 |
suite = TestLoader().loadTestsFromTestCase(QiitaConfig) |
|
|
420 |
TextTestRunner(stream=stdout, verbosity=1).run(suite) |
|
|
421 |
|
|
|
422 |
|
|
|
423 |
if __name__ == "__main__": |
|
|
424 |
main() |