The Virtual Brain Project

Source code for tvb.core.services.settings_service

# -*- coding: utf-8 -*-
#
#
# TheVirtualBrain-Framework Package. This package holds all Data Management, and 
# Web-UI helpful to run brain-simulations. To use it, you also need do download
# TheVirtualBrain-Scientific Package (for simulators). See content of the
# documentation-folder for more details. See also http://www.thevirtualbrain.org
#
# (c) 2012-2017, Baycrest Centre for Geriatric Care ("Baycrest") and others
#
# This program is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.  See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this
# program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#   CITATION:
# When using The Virtual Brain for scientific publications, please cite it as follows:
#
#   Paula Sanz Leon, Stuart A. Knock, M. Marmaduke Woodman, Lia Domide,
#   Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013)
#       The Virtual Brain: a simulator of primate brain network dynamics.
#   Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010)
#
#
"""
Service layer for saving/editing TVB settings.

.. moduleauthor:: Bogdan Neacsa <bogdan.neacsa@codemart.ro>
.. moduleauthor:: Lia Domide <lia.domide@codemart.ro>
"""
import os
import sys
import shutil
import hashlib
from sqlalchemy import create_engine
from tvb.basic.profile import TvbProfile
from tvb.basic.config import stored
from tvb.basic.logger.builder import get_logger
from tvb.core.utils import get_matlab_executable
from tvb.core.services.exceptions import InvalidSettingsException


[docs]class SettingsService(object): """ Handle all TVB Setting related problems, at the service level. """ KEY_ADMIN_NAME = stored.KEY_ADMIN_NAME KEY_ADMIN_PWD = stored.KEY_ADMIN_PWD KEY_ADMIN_EMAIL = stored.KEY_ADMIN_EMAIL KEY_STORAGE = stored.KEY_STORAGE KEY_MAX_DISK_SPACE_USR = stored.KEY_MAX_DISK_SPACE_USR KEY_MATLAB_EXECUTABLE = stored.KEY_MATLAB_EXECUTABLE KEY_PORT = stored.KEY_PORT KEY_URL_WEB = stored.KEY_URL_WEB KEY_SELECTED_DB = stored.KEY_SELECTED_DB KEY_DB_URL = stored.KEY_DB_URL KEY_CLUSTER = stored.KEY_CLUSTER KEY_CLUSTER_SCHEDULER = stored.KEY_CLUSTER_SCHEDULER KEY_MAX_NR_THREADS = stored.KEY_MAX_THREAD_NR KEY_MAX_RANGE = stored.KEY_MAX_RANGE_NR KEY_MAX_NR_SURFACE_VERTEX = stored.KEY_MAX_NR_SURFACE_VERTEX # Display order for the keys. None means a separator/new line will be added KEYS_DISPLAY_ORDER = [KEY_ADMIN_NAME, KEY_ADMIN_PWD, KEY_ADMIN_EMAIL, None, KEY_STORAGE, KEY_MAX_DISK_SPACE_USR, KEY_MATLAB_EXECUTABLE, KEY_SELECTED_DB, KEY_DB_URL, None, KEY_PORT, KEY_URL_WEB, None, KEY_CLUSTER, KEY_CLUSTER_SCHEDULER, KEY_MAX_NR_THREADS, KEY_MAX_RANGE, KEY_MAX_NR_SURFACE_VERTEX] def __init__(self): self.logger = get_logger(__name__) first_run = TvbProfile.is_first_run() storage = TvbProfile.current.TVB_STORAGE if not first_run else TvbProfile.current.DEFAULT_STORAGE self.configurable_keys = { self.KEY_STORAGE: {'label': 'Root folder for all projects', 'value': storage, 'readonly': not first_run, 'type': 'text'}, self.KEY_MAX_DISK_SPACE_USR: {'label': 'Max hard disk space per user (MBytes)', 'value': TvbProfile.current.MAX_DISK_SPACE / 2 ** 10, 'type': 'text'}, self.KEY_MATLAB_EXECUTABLE: {'label': 'Optional Matlab or Octave path', 'type': 'text', 'value': TvbProfile.current.MATLAB_EXECUTABLE or get_matlab_executable() or '', 'description': 'Some analyzers will not be available when ' 'matlab/octave are not found'}, self.KEY_SELECTED_DB: {'label': 'Select one DB engine', 'value': TvbProfile.current.db.SELECTED_DB, 'type': 'select', 'readonly': not first_run, 'options': TvbProfile.current.db.ACEEPTED_DBS}, self.KEY_DB_URL: {'label': "DB connection URL", 'value': TvbProfile.current.db.ACEEPTED_DBS[TvbProfile.current.db.SELECTED_DB], 'type': 'text', 'readonly': TvbProfile.current.db.SELECTED_DB == 'sqlite'}, self.KEY_PORT: {'label': 'Port to run Cherrypy on', 'value': TvbProfile.current.web.SERVER_PORT, 'dtype': 'primitive', 'type': 'text'}, self.KEY_URL_WEB: {'label': 'URL for accessing web', 'value': TvbProfile.current.web.BASE_URL, 'type': 'text', 'dtype': 'primitive'}, self.KEY_MAX_NR_THREADS: {'label': 'Maximum no. of threads for local installations', 'type': 'text', 'value': TvbProfile.current.MAX_THREADS_NUMBER, 'dtype': 'primitive'}, self.KEY_MAX_RANGE: {'label': 'Maximum no. of operations in one PSE', 'description': "Parameters Space Exploration (PSE) maximum number of operations", 'value': TvbProfile.current.MAX_RANGE_NUMBER, 'type': 'text', 'dtype': 'primitive'}, self.KEY_MAX_NR_SURFACE_VERTEX: {'label': 'Maximum no. of vertices in a surface', 'type': 'text', 'dtype': 'primitive', 'value': TvbProfile.current.MAX_SURFACE_VERTICES_NUMBER}, self.KEY_CLUSTER: {'label': 'Deploy on cluster', 'value': TvbProfile.current.cluster.IS_DEPLOY, 'description': 'Check this only if on the web-server machine OARSUB command is enabled.', 'dtype': 'primitive', 'type': 'boolean'}, self.KEY_CLUSTER_SCHEDULER: {'label': 'Cluster Scheduler', 'readonly': False, 'value': TvbProfile.current.cluster.CLUSTER_SCHEDULER, 'type': 'select', 'options': TvbProfile.current.cluster.ACCEPTED_SCHEDULERS}, self.KEY_ADMIN_NAME: {'label': 'Administrator User Name', 'value': TvbProfile.current.web.admin.ADMINISTRATOR_NAME, 'type': 'text', 'readonly': not first_run, 'description': ('Password and Email can be edited after first run, ' 'from the profile page directly.')}, self.KEY_ADMIN_PWD: {'label': 'Password', 'value': TvbProfile.current.web.admin.ADMINISTRATOR_BLANK_PWD if first_run else TvbProfile.current.web.admin.ADMINISTRATOR_PASSWORD, 'type': 'password', 'readonly': not first_run}, self.KEY_ADMIN_EMAIL: {'label': 'Administrator Email', 'value': TvbProfile.current.web.admin.ADMINISTRATOR_EMAIL, 'readonly': not first_run, 'type': 'text'}}
[docs] def check_db_url(self, url): """Validate DB URL, that a connection can be done.""" try: engine = create_engine(url) connection = engine.connect() connection.close() except Exception as excep: self.logger.exception(excep) raise InvalidSettingsException('Could not connect to DB! ' 'Invalid URL:' + str(url))
@staticmethod
[docs] def get_disk_free_space(storage_path): """ :returns: the available HDD space in KB in TVB_STORAGE folder. """ if sys.platform.startswith('win'): import ctypes storage_path = os.path.abspath(storage_path) drive = unicode(storage_path.split(':')[0] + ':') freeuser = ctypes.c_int64() total = ctypes.c_int64() free = ctypes.c_int64() ctypes.windll.kernel32.GetDiskFreeSpaceExW(drive, ctypes.byref(freeuser), ctypes.byref(total), ctypes.byref(free)) bytes_value = freeuser.value else: mem_stat = os.statvfs(storage_path) bytes_value = mem_stat.f_frsize * mem_stat.f_bavail ## Occupied memory would be: # bytes_value = mem_stat.f_bsize * mem_stat.f_bavail return bytes_value / 2 ** 10
[docs] def save_settings(self, **data): """ Check if new settings are correct. Make necessary changes, then save new data in configuration file. :returns: two boolean values -there were any changes to the configuration; -a reset should be performed on the TVB relaunch. """ new_storage = data[self.KEY_STORAGE] previous_storage = TvbProfile.current.TVB_STORAGE new_db = data[self.KEY_SELECTED_DB] previous_db = TvbProfile.current.db.SELECTED_DB db_changed = new_db != previous_db storage_changed = new_storage != previous_storage matlab_exec = data[self.KEY_MATLAB_EXECUTABLE] if matlab_exec == 'None': data[self.KEY_MATLAB_EXECUTABLE] = '' # Storage changed but DB didn't, just copy TVB storage to new one. if storage_changed and not db_changed: if os.path.exists(new_storage): if os.access(new_storage, os.W_OK): shutil.rmtree(new_storage) else: raise InvalidSettingsException("No Write access on storage folder!!") shutil.copytree(previous_storage, new_storage) if not os.path.isdir(new_storage): os.makedirs(new_storage) max_space = data[self.KEY_MAX_DISK_SPACE_USR] available_mem_kb = SettingsService.get_disk_free_space(new_storage) kb_value = int(max_space) * 2 ** 10 if not (0 < kb_value < available_mem_kb): raise InvalidSettingsException("Not enough disk space. There is a maximum of %d MB available on this disk " "or partition. Wanted %d" % (available_mem_kb / (2 ** 10), max_space)) data[self.KEY_MAX_DISK_SPACE_USR] = kb_value # Save data to file, all while checking if any data has changed first_run = TvbProfile.is_first_run() if first_run: data[stored.KEY_LAST_CHECKED_FILE_VERSION] = TvbProfile.current.version.DATA_VERSION data[stored.KEY_LAST_CHECKED_CODE_VERSION] = TvbProfile.current.version.SVN_VERSION file_data = data if self.KEY_ADMIN_PWD in data: data[self.KEY_ADMIN_PWD] = hashlib.md5(data[self.KEY_ADMIN_PWD]).hexdigest() anything_changed = True else: file_data = TvbProfile.current.manager.stored_settings anything_changed = False for key in file_data: if key in data and str(data[key]) != str(file_data[key]): anything_changed = True file_data[key] = data[key] if db_changed: file_data[self.KEY_DB_URL] = TvbProfile.current.db.DB_URL for key in data: if key not in file_data: anything_changed = True file_data[key] = data[key] # Write in file new data if anything_changed: TvbProfile.current.manager.write_config_data(file_data) os.chmod(TvbProfile.current.TVB_CONFIG_FILE, 0o644) return anything_changed, first_run or db_changed