Source code for aiida.manage.configuration.setup

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""Module that defines methods required to setup a new AiiDA instance."""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

import os

import click

from aiida.cmdline.utils import echo


[docs]def create_instance_directories(): """Create the base directories required for a new AiiDA instance. This will create the base AiiDA directory defined by the AIIDA_CONFIG_FOLDER variable, unless it already exists. Subsequently, it will create the daemon directory within it and the daemon log directory. """ from .settings import AIIDA_CONFIG_FOLDER, DAEMON_DIR, DAEMON_LOG_DIR, DEFAULT_UMASK directory_base = os.path.expanduser(AIIDA_CONFIG_FOLDER) directory_daemon = os.path.join(directory_base, DAEMON_DIR) directory_daemon_log = os.path.join(directory_base, DAEMON_LOG_DIR) umask = os.umask(DEFAULT_UMASK) try: if not os.path.isdir(directory_base): os.makedirs(directory_base) if not os.path.isdir(directory_daemon): os.makedirs(directory_daemon) if not os.path.isdir(directory_daemon_log): os.makedirs(directory_daemon_log) finally: os.umask(umask)
[docs]def setup_profile(profile_name, only_config, set_default=False, non_interactive=False, **kwargs): """ Setup an AiiDA profile and AiiDA user (and the AiiDA default user). :param profile_name: Profile name :param only_config: do not create a new user :param set_default: set the new profile as the default :param non_interactive: do not prompt for configuration values, fail if not all values are given as kwargs. :param backend: one of 'django', 'sqlalchemy' :param email: valid email address for the user :param db_host: hostname for the database :param db_port: port to connect to the database :param db_user: name of the db user :param db_pass: password of the db user """ # pylint: disable=superfluous-parens,too-many-locals,too-many-statements,too-many-branches from aiida.backends import settings from aiida.backends.profile import BACKEND_SQLA, BACKEND_DJANGO from aiida.backends.utils import set_backend_type from aiida.cmdline.commands import cmd_user from aiida.common.exceptions import InvalidOperation from aiida.common.setup import create_profile, create_profile_noninteractive from aiida.manage.configuration import get_config from aiida.manage.manager import get_manager from .settings import DEFAULT_AIIDA_USER config = get_config() manager = get_manager() only_user_config = only_config # Create the directories to store the configuration files create_instance_directories() # we need to overwrite this variable for the following to work settings.AIIDADB_PROFILE = profile_name profile = None # ask and store the configuration of the DB if non_interactive: try: profile = create_profile_noninteractive( config=config, profile_name=profile_name, backend=kwargs['backend'], email=kwargs['email'], db_host=kwargs['db_host'], db_port=kwargs['db_port'], db_name=kwargs['db_name'], db_user=kwargs['db_user'], db_pass=kwargs.get('db_pass', ''), repo=kwargs['repo'], force_overwrite=kwargs.get('force_overwrite', False)) except ValueError as exception: echo.echo_critical("Error during configuation: {}".format(exception)) except KeyError as exception: import traceback echo.echo(traceback.format_exc()) echo.echo_critical( "--non-interactive requires all values to be given on the commandline! Missing argument: {}".format( exception.args[0])) else: try: profile = create_profile(config=config, profile_name=profile_name) except ValueError as exception: echo.echo_critical("Error during configuration: {}".format(exception)) # Add the created profile and set it as the new default profile config.add_profile(profile_name, profile) config.set_default_profile(profile_name, overwrite=set_default) config.store() if only_user_config: echo.echo("Only user configuration requested, skipping the migrate command") else: echo.echo("Executing now a migrate command...") backend_choice = profile['AIIDADB_BACKEND'] if backend_choice == BACKEND_DJANGO: echo.echo("...for Django backend") backend = manager._load_backend(schema_check=False) # pylint: disable=protected-access backend.migrate() set_backend_type(BACKEND_DJANGO) elif backend_choice == BACKEND_SQLA: echo.echo("...for SQLAlchemy backend") backend = manager._load_backend(schema_check=False) # pylint: disable=protected-access backend.migrate() set_backend_type(BACKEND_SQLA) else: raise InvalidOperation("Not supported backend selected.") echo.echo("Database was created successfully") from aiida import orm if not orm.User.objects.find({'email': DEFAULT_AIIDA_USER}): echo.echo("Installing default AiiDA user...") nuser = orm.User(email=DEFAULT_AIIDA_USER, first_name="AiiDA", last_name="Daemon") nuser.is_active = True nuser.store() email = manager.get_profile().default_user_email echo.echo("Starting user configuration for {}...".format(email)) if email == DEFAULT_AIIDA_USER: echo.echo("You set up AiiDA using the default Daemon email ({}),".format(email)) echo.echo("therefore no further user configuration will be asked.") else: if non_interactive: # Here we map the keyword arguments onto the command line arguments # for verdi user configure. We have to be careful that there the # argument names are the same as those int he kwargs dict commands = [kwargs['email'], '--non-interactive'] for arg in ('first_name', 'last_name', 'institution'): value = kwargs.get(arg, None) if value is not None: commands.extend(('--{}'.format(arg.replace('_', '-')), str(value))) else: commands = [email] # Ask to configure the user try: # pylint: disable=no-value-for-parameter cmd_user.configure(commands) except SystemExit: # Have to catch this as the configure command will do a sys.exit() pass echo.echo("Setup finished.")
[docs]def delete_repository(profile, non_interactive=True): """ Delete an AiiDA file repository associated with an AiiDA profile. :param profile: AiiDA Profile :type profile: :class:`aiida.manage.configuration.profile.Profile` :param non_interactive: do not prompt for configuration values, fail if not all values are given as kwargs. :type non_interactive: bool """ from six.moves.urllib.parse import urlparse # pylint: disable=import-error pconfig = profile.dictionary repo_uri = pconfig.get('AIIDADB_REPOSITORY_URI', '') repo_path = urlparse(repo_uri).path repo_path = os.path.expanduser(repo_path) if not os.path.isabs(repo_path): echo.echo_info("Associated file repository '{}' does not exist.".format(repo_path)) return if not os.path.isdir(repo_path): echo.echo_info("Associated file repository '{}' is not a directory.".format(repo_path)) return if non_interactive or click.confirm("Delete associated file repository '{}'?\n" "WARNING: All data will be lost.".format(repo_path)): echo.echo_info("Deleting directory '{}'.".format(repo_path)) import shutil shutil.rmtree(repo_path)
[docs]def delete_db(profile, non_interactive=True, verbose=False): """ Delete an AiiDA database associated with an AiiDA profile. :param profile: AiiDA Profile :type profile: :class:`aiida.manage.configuration.profile.Profile` :param non_interactive: do not prompt for configuration values, fail if not all values are given as kwargs. :type non_interactive: bool :param verbose: if True, print parameters of DB connection :type verbose: bool """ from aiida.manage.configuration import get_config from aiida.manage.external.postgres import Postgres from aiida.common import json pdict = profile.dictionary postgres = Postgres.from_profile(profile, interactive=not non_interactive, quiet=False) postgres.determine_setup() if verbose: echo.echo_info("Parameters used to connect to postgres:") echo.echo(json.dumps(postgres.get_dbinfo(), indent=4)) db_name = pdict.get('AIIDADB_NAME', '') if not postgres.db_exists(db_name): echo.echo_info("Associated database '{}' does not exist.".format(db_name)) elif non_interactive or click.confirm("Delete associated database '{}'?\n" "WARNING: All data will be lost.".format(db_name)): echo.echo_info("Deleting database '{}'.".format(db_name)) postgres.drop_db(db_name) user = pdict.get('AIIDADB_USER', '') config = get_config() users = [profile.dictionary.get('AIIDADB_USER', '') for profile in config.profiles] if not postgres.dbuser_exists(user): echo.echo_info("Associated database user '{}' does not exist.".format(user)) elif users.count(user) > 1: echo.echo_info("Associated database user '{}' is used by other profiles " "and will not be deleted.".format(user)) elif non_interactive or click.confirm("Delete database user '{}'?".format(user)): echo.echo_info("Deleting user '{}'.".format(user)) postgres.drop_dbuser(user)
[docs]def delete_from_config(profile, non_interactive=True): """ Delete an AiiDA profile from the config file. :param profile: AiiDA Profile :type profile: :class:`aiida.manage.configuration.profile.Profile` :param non_interactive: do not prompt for configuration values, fail if not all values are given as kwargs. :type non_interactive: bool """ from aiida.manage.configuration import get_config if non_interactive or click.confirm("Delete configuration for profile '{}'?\n" "WARNING: Permanently removes profile from the list of AiiDA profiles.".format( profile.name)): echo.echo_info("Deleting configuration for profile '{}'.".format(profile.name)) config = get_config() config.remove_profile(profile.name) config.store()
[docs]def delete_profile(profile, non_interactive=True, include_db=True, include_repository=True, include_config=True): """ Delete an AiiDA profile and AiiDA user. :param profile: AiiDA profile :type profile: :class:`aiida.manage.configuration.profile.Profile` :param non_interactive: do not prompt for configuration values, fail if not all values are given as kwargs. :param include_db: Include deletion of associated database :type include_db: bool :param include_repository: Include deletion of associated file repository :type include_repository: bool :param include_config: Include deletion of entry from AiiDA configuration file :type include_config: bool """ if include_db: delete_db(profile, non_interactive) if include_repository: delete_repository(profile, non_interactive) if include_config: delete_from_config(profile, non_interactive)