class SettingsController(UserController): """ Controller for TVB-Settings web page. Inherit from UserController, to have the same fill_default_attributes method (with versionInfo). """ def __init__(self): UserController.__init__(self) self.settingsservice = SettingsService() @cherrypy.expose @handle_error(redirect=True) @using_template('user/base_user') @check_admin def settings(self, save_settings=False, **data): """Main settings page submit and get""" template_specification = dict(mainContent="settings/system_settings", title="System Settings") if save_settings: try: form = SettingsForm() data = form.to_python(data) isrestart, isreset = self.settingsservice.save_settings(**data) if isrestart: thread = threading.Thread(target=self._restart_services, kwargs={'should_reset': isreset}) thread.start() common.add2session(common.KEY_IS_RESTART, True) common.set_important_message('Please wait until TVB is restarted properly!') raise cherrypy.HTTPRedirect('/tvb') # Here we will leave the same settings page to be displayed. # It will continue reloading when CherryPy restarts. except formencode.Invalid as excep: template_specification[common.KEY_ERRORS] = excep.unpack_errors() except InvalidSettingsException as excep: self.logger.error('Invalid settings! Exception %s was raised' % (str(excep))) common.set_error_message(excep.message) template_specification.update({'keys_order': self.settingsservice.KEYS_DISPLAY_ORDER, 'config_data': self.settingsservice.configurable_keys, common.KEY_FIRST_RUN: TvbProfile.is_first_run()}) return self.fill_default_attributes(template_specification) def _restart_services(self, should_reset): """ Restart CherryPy Backend. """ cherrypy.engine.exit() self.logger.info("Waiting for CherryPy to shut down ... ") sleep(5) python_path = TvbProfile.current.PYTHON_INTERPRETER_PATH try: import tvb_bin proc_params = [python_path, '-m', 'tvb_bin.app', 'start', TvbProfile.CURRENT_PROFILE_NAME] if should_reset: proc_params.append('-reset') subprocess.Popen(proc_params, shell=False) except ImportError: proc_params = [python_path, '-m', 'tvb.interfaces.web.run', TvbProfile.CURRENT_PROFILE_NAME, "tvb.config"] if should_reset: proc_params.append('reset') subprocess.Popen(proc_params, shell=False).communicate() self.logger.info("Starting CherryPy again ... ") @cherrypy.expose @handle_error(redirect=False) @jsonify def check_db_url(self, **data): """ Action on DB-URL validate button. """ try: storage_path = data[self.settingsservice.KEY_STORAGE] if os.path.isfile(storage_path): raise InvalidSettingsException('TVB Storage should be set to a folder and not a file.') if not os.path.isdir(storage_path): try: os.mkdir(storage_path) except OSError: return {'status': 'not ok', 'message': 'Could not create root storage for TVB. Please check write permissions!'} self.settingsservice.check_db_url(data[self.settingsservice.KEY_DB_URL]) return {'status': 'ok', 'message': 'The database URL is valid.'} except InvalidSettingsException as excep: self.logger.error(excep) return {'status': 'not ok', 'message': 'The database URL is not valid.'} @cherrypy.expose @handle_error(redirect=False) @jsonify def validate_matlab_path(self, **data): """ Check if the set path from the ui actually corresponds to a matlab executable. """ submitted_path = data[self.settingsservice.KEY_MATLAB_EXECUTABLE] if len(submitted_path) == 0: return {'status': 'ok', 'message': 'No Matlab/Ocatve path was given. Some analyzers will not be available.'} if os.path.isfile(submitted_path): version = check_matlab_version(submitted_path) if version: return {'status': 'ok', 'message': "Valid Matlab/Octave. Found version: '%s'." % (version,)} else: return {'status': 'not ok', 'message': "Invalid Matlab/Octave. Found version: '%s' ." % (version,)} else: return {'status': 'not ok', 'message': 'Invalid Matlab/Octave path.'}
class SettingsController(UserController): """ Controller for TVB-Settings web page. Inherit from UserController, to have the same fill_default_attributes method (with versionInfo). """ def __init__(self): UserController.__init__(self) self.settingsservice = SettingsService() @cherrypy.expose @handle_error(redirect=True) @using_template('user/base_user') @check_admin def settings(self, save_settings=False, **data): """Main settings page submit and get""" template_specification = dict(mainContent="../settings/system_settings", title="System Settings") if save_settings: try: form = SettingsForm() data = form.to_python(data) isrestart, isreset = self.settingsservice.save_settings(**data) if isrestart: thread = threading.Thread(target=self._restart_services, kwargs={'should_reset': isreset}) thread.start() common.add2session(common.KEY_IS_RESTART, True) common.set_important_message('Please wait until TVB is restarted properly!') raise cherrypy.HTTPRedirect('/tvb') # Here we will leave the same settings page to be displayed. # It will continue reloading when CherryPy restarts. except formencode.Invalid as excep: template_specification[common.KEY_ERRORS] = excep.unpack_errors() except InvalidSettingsException as excep: self.logger.error('Invalid settings! Exception %s was raised' % (str(excep))) common.set_error_message(excep.message) template_specification.update({'keys_order': self.settingsservice.KEYS_DISPLAY_ORDER, 'config_data': self.settingsservice.configurable_keys, common.KEY_FIRST_RUN: TvbProfile.is_first_run()}) return self.fill_default_attributes(template_specification) def _restart_services(self, should_reset): """ Restart CherryPy Backend. """ cherrypy.engine.exit() self.logger.info("Waiting for Cherrypy to shut down ... ") sleep(5) python_path = TvbProfile.current.PYTHON_INTERPRETER_PATH proc_params = [python_path, '-m', 'tvb_bin.app', 'start', TvbProfile.CURRENT_PROFILE_NAME] if should_reset: proc_params.append('-reset') self.logger.info("Starting CherryPy again ... ") subprocess.Popen(proc_params, shell=False) @cherrypy.expose @handle_error(redirect=False) @jsonify def check_db_url(self, **data): """ Action on DB-URL validate button. """ try: storage_path = data[self.settingsservice.KEY_STORAGE] if os.path.isfile(storage_path): raise InvalidSettingsException('TVB Storage should be set to a folder and not a file.') if not os.path.isdir(storage_path): try: os.mkdir(storage_path) except OSError: return {'status': 'not ok', 'message': 'Could not create root storage for TVB. Please check write permissions!'} self.settingsservice.check_db_url(data[self.settingsservice.KEY_DB_URL]) return {'status': 'ok', 'message': 'The database URL is valid.'} except InvalidSettingsException as excep: self.logger.error(excep) return {'status': 'not ok', 'message': 'The database URL is not valid.'} @cherrypy.expose @handle_error(redirect=False) @jsonify def validate_matlab_path(self, **data): """ Check if the set path from the ui actually corresponds to a matlab executable. """ submitted_path = data[self.settingsservice.KEY_MATLAB_EXECUTABLE] if len(submitted_path) == 0: return {'status': 'ok', 'message': 'No Matlab/Ocatve path was given. Some analyzers will not be available.'} if os.path.isfile(submitted_path): version = check_matlab_version(submitted_path) if version: return {'status': 'ok', 'message': "Valid Matlab/Octave. Found version: '%s'." % (version,)} else: return {'status': 'not ok', 'message': "Invalid Matlab/Octave. Found version: '%s' ." % (version,)} else: return {'status': 'not ok', 'message': 'Invalid Matlab/Octave path.'}
class TestSettingsService(BaseTestCase): """ This class contains tests for the tvb.core.services.settings_service module. """ TEST_SETTINGS = { SettingsService.KEY_ADMIN_NAME: 'test_name', SettingsService.KEY_ADMIN_EMAIL: '*****@*****.**', SettingsService.KEY_PORT: 8081, SettingsService.KEY_MAX_DISK_SPACE_USR: 2**8 } def setup_method(self): """ Prepare the usage of a different config file for this class only. """ if os.path.exists(TEST_CONFIG_FILE): os.remove(TEST_CONFIG_FILE) if os.path.exists(TestSQLiteProfile.DEFAULT_STORAGE): shutil.rmtree(TestSQLiteProfile.DEFAULT_STORAGE) self.old_config_file = TvbProfile.current.TVB_CONFIG_FILE TvbProfile.current.__class__.TVB_CONFIG_FILE = TEST_CONFIG_FILE TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() def teardown_method(self): """ Restore configuration file """ if os.path.exists(TEST_CONFIG_FILE): os.remove(TEST_CONFIG_FILE) TvbProfile.current.__class__.TVB_CONFIG_FILE = self.old_config_file TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) def test_check_db_url_invalid(self): """ Make sure a proper exception is raised in case an invalid database url is passed. """ with pytest.raises(InvalidSettingsException): self.settings_service.check_db_url("this-url-should-be-invalid") def test_get_free_disk_space(self): """ Check that no exception is raised during the query for free disk space. Also do a check that returned value is greater than 0. Not most precise check but other does not seem possible so far. """ disk_space = self.settings_service.get_disk_free_space( TvbProfile.current.TVB_STORAGE) assert disk_space > 0, "Disk space should never be negative." def test_first_run_save(self): """ Check that before setting something, all flags are pointing towards empty. After storing some configurations, check that flags are changed. """ initial_configurations = self.settings_service.configurable_keys first_run = TvbProfile.is_first_run() assert first_run, "Invalid First_Run flag!!" assert not os.path.exists(TEST_CONFIG_FILE) assert len(TvbProfile.current.manager.stored_settings) == 0 to_store_data = { key: value['value'] for key, value in initial_configurations.items() } for key, value in self.TEST_SETTINGS.items(): to_store_data[key] = value _, shoud_reset = self.settings_service.save_settings(**to_store_data) assert shoud_reset first_run = TvbProfile.is_first_run() assert not first_run, "Invalid First_Run flag!!" assert os.path.exists(TEST_CONFIG_FILE) assert not len(TvbProfile.current.manager.stored_settings) == 0 def test_read_stored_settings(self): """ Test to see that keys from the configuration dict is updated with the value from the configuration file after store. """ initial_configurations = self.settings_service.configurable_keys to_store_data = { key: value['value'] for key, value in initial_configurations.items() } for key, value in self.TEST_SETTINGS.items(): to_store_data[key] = value is_changed, shoud_reset = self.settings_service.save_settings( **to_store_data) assert shoud_reset and is_changed # enforce keys to get repopulated: TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() updated_configurations = self.settings_service.configurable_keys for key, value in updated_configurations.items(): if key in self.TEST_SETTINGS: assert self.TEST_SETTINGS[key] == value['value'] elif key == SettingsService.KEY_ADMIN_PWD: assert TvbProfile.current.web.admin.ADMINISTRATOR_PASSWORD == value[ 'value'] assert TvbProfile.current.web.admin.ADMINISTRATOR_BLANK_PWD == initial_configurations[ key]['value'] else: assert initial_configurations[key]['value'] == value['value'] def test_update_settings(self): """ Test update of settings: correct flags should be returned, and check storage folder renamed """ # 1. save on empty config-file: to_store_data = { key: value['value'] for key, value in self.settings_service.configurable_keys.items() } for key, value in self.TEST_SETTINGS.items(): to_store_data[key] = value is_changed, shoud_reset = self.settings_service.save_settings( **to_store_data) assert shoud_reset and is_changed # 2. Reload and save with the same values (is_changed expected to be False) TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() to_store_data = { key: value['value'] for key, value in self.settings_service.configurable_keys.items() } is_changed, shoud_reset = self.settings_service.save_settings( **to_store_data) assert not is_changed assert not shoud_reset # 3. Reload and check that changing TVB_STORAGE is done correctly TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() to_store_data = { key: value['value'] for key, value in self.settings_service.configurable_keys.items() } to_store_data[SettingsService.KEY_STORAGE] = os.path.join( TvbProfile.current.TVB_STORAGE, 'RENAMED') # Write a test-file and check that it is moved file_writer = open( os.path.join(TvbProfile.current.TVB_STORAGE, "test_rename-xxx43"), 'w') file_writer.write('test-content') file_writer.close() is_changed, shoud_reset = self.settings_service.save_settings( **to_store_data) assert is_changed assert not shoud_reset # Check that the file was correctly moved: data = open( os.path.join(TvbProfile.current.TVB_STORAGE, 'RENAMED', "test_rename-xxx43"), 'r').read() assert data == 'test-content' shutil.rmtree(os.path.join(TvbProfile.current.TVB_STORAGE, 'RENAMED')) os.remove( os.path.join(TvbProfile.current.TVB_STORAGE, "test_rename-xxx43"))
class TestSettingsService(BaseTestCase): """ This class contains tests for the tvb.core.services.settings_service module. """ TEST_SETTINGS = {SettingsService.KEY_ADMIN_NAME: 'test_name', SettingsService.KEY_ADMIN_EMAIL: '*****@*****.**', SettingsService.KEY_PORT: 8081, SettingsService.KEY_URL_WEB: "http://192.168.123.11:8081/", SettingsService.KEY_MAX_DISK_SPACE_USR: 2 ** 8} def setup_method(self): """ Prepare the usage of a different config file for this class only. """ if os.path.exists(TEST_CONFIG_FILE): os.remove(TEST_CONFIG_FILE) self.old_config_file = TvbProfile.current.TVB_CONFIG_FILE TvbProfile.current.__class__.TVB_CONFIG_FILE = TEST_CONFIG_FILE TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() def teardown_method(self): """ Restore configuration file """ if os.path.exists(TEST_CONFIG_FILE): os.remove(TEST_CONFIG_FILE) TvbProfile.current.__class__.TVB_CONFIG_FILE = self.old_config_file TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) def test_check_db_url_invalid(self): """ Make sure a proper exception is raised in case an invalid database url is passed. """ with pytest.raises(InvalidSettingsException): self.settings_service.check_db_url("this-url-should-be-invalid") def test_get_free_disk_space(self): """ Check that no exception is raised during the query for free disk space. Also do a check that returned value is greater than 0. Not most precise check but other does not seem possible so far. """ disk_space = self.settings_service.get_disk_free_space(TvbProfile.current.TVB_STORAGE) assert disk_space > 0, "Disk space should never be negative." def test_first_run_save(self): """ Check that before setting something, all flags are pointing towards empty. After storing some configurations, check that flags are changed. """ initial_configurations = self.settings_service.configurable_keys first_run = TvbProfile.is_first_run() assert first_run, "Invalid First_Run flag!!" assert not os.path.exists(TEST_CONFIG_FILE) assert len(TvbProfile.current.manager.stored_settings) == 0 to_store_data = {key: value['value'] for key, value in initial_configurations.iteritems()} for key, value in self.TEST_SETTINGS.iteritems(): to_store_data[key] = value _, shoud_reset = self.settings_service.save_settings(**to_store_data) assert shoud_reset first_run = TvbProfile.is_first_run() assert not first_run, "Invalid First_Run flag!!" assert os.path.exists(TEST_CONFIG_FILE) assert not len(TvbProfile.current.manager.stored_settings) == 0 def test_read_stored_settings(self): """ Test to see that keys from the configuration dict is updated with the value from the configuration file after store. """ initial_configurations = self.settings_service.configurable_keys to_store_data = {key: value['value'] for key, value in initial_configurations.iteritems()} for key, value in self.TEST_SETTINGS.iteritems(): to_store_data[key] = value is_changed, shoud_reset = self.settings_service.save_settings(**to_store_data) assert shoud_reset and is_changed # enforce keys to get repopulated: TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() updated_configurations = self.settings_service.configurable_keys for key, value in updated_configurations.iteritems(): if key in self.TEST_SETTINGS: assert self.TEST_SETTINGS[key] == value['value'] elif key == SettingsService.KEY_ADMIN_PWD: assert TvbProfile.current.web.admin.ADMINISTRATOR_PASSWORD == value['value'] assert TvbProfile.current.web.admin.ADMINISTRATOR_BLANK_PWD == initial_configurations[key]['value'] else: assert initial_configurations[key]['value'] == value['value'] def test_update_settings(self): """ Test update of settings: correct flags should be returned, and check storage folder renamed """ # 1. save on empty config-file: to_store_data = {key: value['value'] for key, value in self.settings_service.configurable_keys.iteritems()} for key, value in self.TEST_SETTINGS.iteritems(): to_store_data[key] = value is_changed, shoud_reset = self.settings_service.save_settings(**to_store_data) assert shoud_reset and is_changed # 2. Reload and save with the same values (is_changed expected to be False) TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() to_store_data = {key: value['value'] for key, value in self.settings_service.configurable_keys.iteritems()} is_changed, shoud_reset = self.settings_service.save_settings(**to_store_data) assert not is_changed assert not shoud_reset # 3. Reload and check that changing TVB_STORAGE is done correctly TvbProfile._build_profile_class(TvbProfile.CURRENT_PROFILE_NAME) self.settings_service = SettingsService() to_store_data = {key: value['value'] for key, value in self.settings_service.configurable_keys.iteritems()} to_store_data[SettingsService.KEY_STORAGE] = os.path.join(TvbProfile.current.TVB_STORAGE, 'RENAMED') # Write a test-file and check that it is moved file_writer = open(os.path.join(TvbProfile.current.TVB_STORAGE, "test_rename-xxx43"), 'w') file_writer.write('test-content') file_writer.close() is_changed, shoud_reset = self.settings_service.save_settings(**to_store_data) assert is_changed assert not shoud_reset # Check that the file was correctly moved: data = open(os.path.join(TvbProfile.current.TVB_STORAGE, 'RENAMED', "test_rename-xxx43"), 'r').read() assert data == 'test-content' shutil.rmtree(os.path.join(TvbProfile.current.TVB_STORAGE, 'RENAMED')) os.remove(os.path.join(TvbProfile.current.TVB_STORAGE, "test_rename-xxx43"))