Exemple #1
0
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"))