def quicksetup( ctx, non_interactive, profile, email, first_name, last_name, institution, db_engine, db_backend, db_host, db_port, db_name, db_username, db_password, su_db_name, su_db_username, su_db_password, repository ): """Setup a new profile in a fully automated fashion.""" # pylint: disable=too-many-arguments,too-many-locals from aiida.manage.external.postgres import Postgres, manual_setup_instructions dbinfo_su = { 'host': db_host, 'port': db_port, 'user': su_db_username, 'password': su_db_password, } postgres = Postgres(interactive=not non_interactive, quiet=False, dbinfo=dbinfo_su) if not postgres.is_connected: echo.echo_critical('failed to determine the PostgreSQL setup') try: create = True if not postgres.dbuser_exists(db_username): postgres.create_dbuser(db_username, db_password) else: db_name, create = postgres.check_db_name(db_name) if create: postgres.create_db(db_username, db_name) except Exception as exception: echo.echo_error( '\n'.join([ 'Oops! quicksetup was unable to create the AiiDA database for you.', 'For AiiDA to work, please either create the database yourself as follows:', manual_setup_instructions(dbuser=su_db_username, dbname=su_db_name), '', 'Alternatively, give your (operating system) user permission to create postgresql databases' + 'and run quicksetup again.', '' ]) ) raise exception # The contextual defaults or `verdi setup` are not being called when `invoking`, so we have to explicitly define # them here, even though the `verdi setup` command would populate those when called from the command line. setup_parameters = { 'non_interactive': non_interactive, 'profile': profile, 'email': email, 'first_name': first_name, 'last_name': last_name, 'institution': institution, 'db_engine': db_engine, 'db_backend': db_backend, 'db_name': db_name, # from now on we connect as the AiiDA DB user, which may be forbidden when going via sockets 'db_host': db_host or 'localhost', 'db_port': db_port, 'db_username': db_username, 'db_password': db_password, 'repository': repository, } ctx.invoke(setup, **setup_parameters)
def test_setup(self): """Test `verdi setup`.""" postgres = Postgres(interactive=False, quiet=True, dbinfo=self.pg_test.dsn) postgres.determine_setup() db_name = 'aiida_test_setup' db_user = '******' db_pass = '******' postgres.create_dbuser(db_user, db_pass) postgres.create_db(db_user, db_name) configuration.reset_profile() profile_name = 'testing' user_email = '*****@*****.**' user_first_name = 'John' user_last_name = 'Smith' user_institution = 'ECMA' # Keep the `--profile` option last as a regression test for #2897 and #2907. Some of the other options have # defaults, callbacks and or contextual defaults that might depend on it, but should not fail if they are parsed # before the profile option is parsed. options = [ '--non-interactive', '--email', user_email, '--first-name', user_first_name, '--last-name', user_last_name, '--institution', user_institution, '--db-name', db_name, '--db-username', db_user, '--db-password', db_pass, '--db-port', self.pg_test.dsn['port'], '--db-backend', self.backend, '--profile', profile_name ] result = self.cli_runner.invoke(cmd_setup.setup, options) self.assertClickResultNoException(result) self.assertClickSuccess(result) config = configuration.get_config() self.assertIn(profile_name, config.profile_names) profile = config.get_profile(profile_name) profile.default_user = user_email # Verify that the backend type of the created profile matches that of the profile for the current test session self.assertEqual(self.backend, profile.database_backend) user = orm.User.objects.get(email=user_email) self.assertEqual(user.first_name, user_first_name) self.assertEqual(user.last_name, user_last_name) self.assertEqual(user.institution, user_institution)
def create_db(profile): """Create PostgreSQL database, if missing.""" dbinfo_su = { 'host': profile.database_hostname, 'port': profile.database_port, 'user': os.getenv("AIIDADB_SUPER_USER"), 'password': os.getenv("AIIDADB_SUPER_PASS"), 'database': 'template1', } postgres = Postgres(interactive=False, quiet=False, dbinfo=dbinfo_su) #, determine_setup=False) if not postgres.is_connected: raise ConnectionError("Unable to connect as super user") try: if not postgres.dbuser_exists(dbuser=profile.database_username): postgres.create_dbuser(dbuser=profile.database_username, dbpass=profile.database_password) if not postgres.db_exists(dbname=profile.database_name): postgres.create_db(profile.database_username, profile.database_name) # Fill DB with vanilla content load_profile(profile.name) backend = get_manager()._load_backend(schema_check=False) # pylint: disable=protected-access try: backend.migrate() except Exception as exception: # pylint: disable=broad-except print( 'database migration failed, probably because connection details are incorrect:\n{}' .format(exception)) # Create the user if it does not yet exist created, user = orm.User.objects.get_or_create( email=profile.default_user, first_name='AiiDA', last_name='EXPLORER', institution='WEBSERVICE') if created: user.store() profile.default_user = user.email except: print(traceback.format_exc())
class TemporaryProfileManager(ProfileManager): """ Manage the life cycle of a completely separated and temporary AiiDA environment. * No profile / database setup required * Tests run via the TemporaryProfileManager never pollute the user's working environment Filesystem: * temporary ``.aiida`` configuration folder * temporary repository folder Database: * temporary database cluster (via the ``pgtest`` package) * with ``aiida`` database user * with ``aiida_db`` database AiiDA: * configured to use the temporary configuration * sets up a temporary profile for tests All of this happens automatically when using the corresponding tests classes & tests runners (unittest) or fixtures (pytest). Example:: tests = TemporaryProfileManager(backend=backend) tests.create_aiida_db() # set up only the database tests.create_profile() # set up a profile (creates the db too if necessary) # ready for tests # run tests 1 tests.reset_db() # database ready for independent tests 2 # run tests 2 tests.destroy_all() # everything cleaned up """ _test_case = None def __init__(self, backend=BACKEND_DJANGO, pgtest=None): # pylint: disable=super-init-not-called """Construct a TemporaryProfileManager :param backend: a database backend :param pgtest: a dictionary of arguments to be passed to PGTest() for starting the postgresql cluster, e.g. {'pg_ctl': '/somepath/pg_ctl'}. Should usually not be necessary. """ from aiida.manage.configuration import settings self.dbinfo = {} self.profile_info = _DEFAULT_PROFILE_INFO self.profile_info['database_backend'] = backend self._pgtest = pgtest or {} self.pg_cluster = None self.postgres = None self._profile = None self._has_test_db = False self._backup = {} self._backup['config'] = configuration.CONFIG self._backup['config_dir'] = settings.AIIDA_CONFIG_FOLDER self._backup['profile'] = configuration.PROFILE @property def profile_dictionary(self): """Profile parameters. Used to set up AiiDA profile from self.profile_info dictionary. """ dictionary = { 'database_engine': self.profile_info['database_engine'], 'database_backend': self.profile_info['database_backend'], 'database_port': self.dbinfo.get('port'), 'database_hostname': self.dbinfo.get('host'), 'database_name': self.profile_info.get('database_name'), 'database_username': self.profile_info.get('database_username'), 'database_password': self.profile_info.get('database_password'), 'repository_uri': 'file://' + self.repo, } return dictionary def create_db_cluster(self): """ Create the database cluster using PGTest. """ from pgtest.pgtest import PGTest if self.pg_cluster is not None: raise TestManagerError( 'Running temporary postgresql cluster detected.' + 'Use destroy_all() before creating a new cluster.') self.pg_cluster = PGTest(**self._pgtest) self.dbinfo.update(self.pg_cluster.dsn) def create_aiida_db(self): """ Create the necessary database on the temporary postgres instance. """ if configuration.PROFILE is not None: raise TestManagerError( 'AiiDA dbenv can not be loaded while creating a tests db environment' ) if self.pg_cluster is None: self.create_db_cluster() self.postgres = Postgres(interactive=False, quiet=True, dbinfo=self.dbinfo) self.dbinfo = self.postgres.dbinfo.copy() self.postgres.create_dbuser(self.profile_info['database_username'], self.profile_info['database_password']) self.postgres.create_db(self.profile_info['database_username'], self.profile_info['database_name']) self._has_test_db = True def create_profile(self): """ Set AiiDA to use the tests config dir and create a default profile there Warning: the AiiDA dbenv must not be loaded when this is called! """ from aiida.manage.configuration import settings, load_profile, Profile if not self._has_test_db: self.create_aiida_db() if not self.root_dir: self.root_dir = tempfile.mkdtemp() configuration.CONFIG = None settings.AIIDA_CONFIG_FOLDER = self.config_dir configuration.PROFILE = None create_instance_directories() profile_name = self.profile_info['name'] config = configuration.get_config(create=True) profile = Profile(profile_name, self.profile_dictionary) config.add_profile(profile) config.set_default_profile(profile_name).store() self._profile = profile load_profile(profile_name) backend = manager.get_manager()._load_backend(schema_check=False) backend.migrate() self._select_db_test_case(backend=self._profile.database_backend) self.init_db() def repo_ok(self): return bool(self.repo and os.path.isdir(os.path.dirname(self.repo))) @property def repo(self): return self._return_dir(self.profile_info['repo_dir']) def _return_dir(self, dir_path): """Return a path to a directory from the fs environment""" if os.path.isabs(dir_path): return dir_path return os.path.join(self.root_dir, dir_path) @property def backend(self): return self.profile_info['backend'] @backend.setter def backend(self, backend): if self.has_profile_open(): raise TestManagerError( 'backend cannot be changed after setting up the environment') valid_backends = [BACKEND_DJANGO, BACKEND_SQLA] if backend not in valid_backends: raise ValueError('invalid backend {}, must be one of {}'.format( backend, valid_backends)) self.profile_info['backend'] = backend @property def config_dir_ok(self): return bool(self.config_dir and os.path.isdir(self.config_dir)) @property def config_dir(self): return self._return_dir(self.profile_info['config_dir']) @property def root_dir(self): return self.profile_info['root_path'] @root_dir.setter def root_dir(self, root_dir): self.profile_info['root_path'] = root_dir @property def root_dir_ok(self): return bool(self.root_dir and os.path.isdir(self.root_dir)) def destroy_all(self): """Remove all traces of the tests run""" from aiida.manage.configuration import settings if self.root_dir: shutil.rmtree(self.root_dir) self.root_dir = None if self.pg_cluster: self.pg_cluster.close() self.pg_cluster = None self._has_test_db = False self._profile = None self._user = None if 'config' in self._backup: configuration.CONFIG = self._backup['config'] if 'config_dir' in self._backup: settings.AIIDA_CONFIG_FOLDER = self._backup['config_dir'] if 'profile' in self._backup: configuration.PROFILE = self._backup['profile'] def has_profile_open(self): return self._profile is not None