示例#1
0
def computer_setup(ctx, non_interactive, **kwargs):
    """Create a new computer."""
    from aiida.orm.utils.builders.computer import ComputerBuilder

    if kwargs['label'] in get_computer_names():
        echo.echo_critical(
            'A computer called {c} already exists. '
            'Use "verdi computer duplicate {c}" to set up a new '
            'computer starting from the settings of {c}.'.format(
                c=kwargs['label']))

    kwargs['transport'] = kwargs['transport'].name
    kwargs['scheduler'] = kwargs['scheduler'].name

    computer_builder = ComputerBuilder(**kwargs)
    try:
        computer = computer_builder.new()
    except (ComputerBuilder.ComputerValidationError, ValidationError) as e:
        echo.echo_critical(f'{type(e).__name__}: {e}')

    try:
        computer.store()
    except ValidationError as err:
        echo.echo_critical(f'unable to store the computer: {err}. Exiting...')
    else:
        echo.echo_success(f'Computer<{computer.pk}> {computer.label} created')

    echo.echo_info(
        'Note: before the computer can be used, it has to be configured with the command:'
    )
    echo.echo_info(
        f'  verdi computer configure {computer.transport_type} {computer.label}'
    )
示例#2
0
    def on_setup_computer(self, _=None, on_success=None):
        """Create a new computer."""
        with self.setup_compupter_out:
            clear_output()

            if self.label.value == "":  # check hostname
                print("Please specify the computer name (for AiiDA)")
                return
            try:
                computer = orm.Computer.objects.get(label=self.label.value)
                print(f"A computer called {self.label.value} already exists.")
                if on_success:
                    on_success()
                return
            except common.NotExistent:
                pass

            items_to_configure = [
                "label",
                "hostname",
                "description",
                "work_dir",
                "mpirun_command",
                "mpiprocs_per_machine",
                "transport",
                "scheduler",
                "prepend_text",
                "append_text",
                "shebang",
            ]
            kwargs = {
                key: getattr(self, key).value
                for key in items_to_configure
            }

            computer_builder = ComputerBuilder(**kwargs)
            try:
                computer = computer_builder.new()
            except (
                    ComputerBuilder.ComputerValidationError,
                    common.exceptions.ValidationError,
            ) as err:
                print(f"{type(err).__name__}: {err}")
                return

            try:
                computer.store()
            except common.exceptions.ValidationError as err:
                print(f"Unable to store the computer: {err}.")
                return

            if self._configure_computer():
                if on_success:
                    on_success()
                print(f"Computer<{computer.pk}> {computer.label} created")
示例#3
0
    def setUp(self):
        """Prepare current user and computer builder with common properties."""
        from aiida.orm.utils.builders.computer import ComputerBuilder

        self.comp_builder = ComputerBuilder(label='test', description='computer', hostname='localhost')
        self.comp_builder.scheduler = 'direct'
        self.comp_builder.work_dir = '/tmp/aiida'
        self.comp_builder.prepend_text = ''
        self.comp_builder.append_text = ''
        self.comp_builder.mpiprocs_per_machine = 8
        self.comp_builder.mpirun_command = 'mpirun'
        self.comp_builder.shebang = '#!xonsh'
        self.user = orm.User.objects.get_default()
示例#4
0
class TestComputerConfigure(AiidaTestCase):
    """Tests for the configuring of instance of the `Computer` ORM class."""
    def setUp(self):
        """Prepare current user and computer builder with common properties."""
        from aiida.orm.utils.builders.computer import ComputerBuilder

        self.comp_builder = ComputerBuilder(label='test',
                                            description='computer',
                                            hostname='localhost')
        self.comp_builder.scheduler = 'direct'
        self.comp_builder.work_dir = '/tmp/aiida'
        self.comp_builder.prepend_text = ''
        self.comp_builder.append_text = ''
        self.comp_builder.mpiprocs_per_machine = 8
        self.comp_builder.mpirun_command = 'mpirun'
        self.comp_builder.shebang = '#!xonsh'
        self.user = orm.User.objects.get_default()

    def test_configure_local(self):
        """Configure a computer for local transport and check it is configured."""
        self.comp_builder.label = 'test_configure_local'
        self.comp_builder.transport = 'local'
        comp = self.comp_builder.new()
        comp.store()

        comp.configure()
        self.assertTrue(comp.is_user_configured(self.user))

    def test_configure_ssh(self):
        """Configure a computer for ssh transport and check it is configured."""
        self.comp_builder.label = 'test_configure_ssh'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        comp.configure(username='******', port='22')
        self.assertTrue(comp.is_user_configured(self.user))

    def test_configure_ssh_invalid(self):
        """Try to configure computer with invalid auth params and check it fails."""
        self.comp_builder.label = 'test_configure_ssh_invalid'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        with self.assertRaises(ValueError):
            comp.configure(username='******', invalid_auth_param='TEST')
示例#5
0
def computer_setup(ctx, non_interactive, **kwargs):
    """Create a new computer."""
    from aiida.orm.utils.builders.computer import ComputerBuilder

    if kwargs['label'] in get_computer_names():
        echo.echo_critical(
            'A computer called {c} already exists. '
            'Use "verdi computer duplicate {c}" to set up a new '
            'computer starting from the settings of {c}.'.format(
                c=kwargs['label']))

    if not non_interactive:
        try:
            pre, post = ensure_scripts(kwargs.pop('prepend_text', ''),
                                       kwargs.pop('append_text', ''), kwargs)
        except InputValidationError as exception:
            raise click.BadParameter(
                'invalid prepend and or append text: {}'.format(exception))

        kwargs['prepend_text'] = pre
        kwargs['append_text'] = post

    kwargs['transport'] = kwargs['transport'].name
    kwargs['scheduler'] = kwargs['scheduler'].name

    computer_builder = ComputerBuilder(**kwargs)
    try:
        computer = computer_builder.new()
    except (ComputerBuilder.ComputerValidationError, ValidationError) as e:
        echo.echo_critical('{}: {}'.format(type(e).__name__, e))

    try:
        computer.store()
    except ValidationError as err:
        echo.echo_critical(
            'unable to store the computer: {}. Exiting...'.format(err))
    else:
        echo.echo_success('Computer<{}> {} created'.format(
            computer.pk, computer.name))

    echo.echo_info(
        'Note: before the computer can be used, it has to be configured with the command:'
    )
    echo.echo_info('  verdi computer configure {} {}'.format(
        computer.get_transport_type(), computer.name))
def get_cx1_computer(work_dir=None, key_filename=None):
    """get or create a Computer node, to interface with the ICL Cx1 HPC

    Parameters
    ----------
    work_dir :str
        absolute path to the work directory
    key_filename : str
        ssh key filename, e.g. /Users/user_name/.ssh/id_rsa

    Returns
    -------
    aiida.orm.Computer

    """
    try:
        computer_cx1 = Computer.objects.get(name=_COMPUTER_NAME)
    except NotExistent:
        if work_dir is None:
            raise ValueError('work_dir not specified')
        computer_builder = ComputerBuilder(
            label=_COMPUTER_NAME,
            description='Imperial HPC cx1 computer',
            transport='ssh',
            scheduler='pbspro_cx1',
            hostname='login.cx1.hpc.ic.ac.uk',
            prepend_text='',
            append_text='',
            work_dir=work_dir,
            shebang='#!/bin/bash',
            mpiprocs_per_machine=16,
            mpirun_command='mpiexec')
        computer_cx1 = computer_builder.new()
        computer_cx1.store()
        computer_cx1.configure(look_for_keys=True,
                               key_filename=key_filename,
                               timeout=60,
                               allow_agent=True,
                               compress=True,
                               load_system_host_keys=True,
                               safe_interval=5.0)
    return computer_cx1
示例#7
0
class TestVerdiComputerConfigure(AiidaTestCase):
    """Test the ``verdi computer configure`` command."""

    def setUp(self):
        """Prepare computer builder with common properties."""
        from aiida.orm.utils.builders.computer import ComputerBuilder
        self.cli_runner = CliRunner()
        self.user = orm.User.objects.get_default()
        self.comp_builder = ComputerBuilder(label='test_comp_setup')
        self.comp_builder.hostname = 'localhost'
        self.comp_builder.description = 'Test Computer'
        self.comp_builder.scheduler = 'direct'
        self.comp_builder.work_dir = '/tmp/aiida'
        self.comp_builder.prepend_text = ''
        self.comp_builder.append_text = ''
        self.comp_builder.mpiprocs_per_machine = 8
        self.comp_builder.mpirun_command = 'mpirun'
        self.comp_builder.shebang = '#!xonsh'

    def test_top_help(self):
        """Test help option of verdi computer configure."""
        result = self.cli_runner.invoke(computer_configure, ['--help'], catch_exceptions=False)
        self.assertIn('ssh', result.output)
        self.assertIn('local', result.output)

    def test_reachable(self):  # pylint: disable=no-self-use
        """Test reachability of top level and sub commands."""
        import subprocess as sp
        sp.check_output(['verdi', 'computer', 'configure', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'local', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'ssh', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'show', '--help'])

    def test_local_ni_empty(self):
        """
        Test verdi computer configure local <comp>

        Test twice, with comp setup for local or ssh.

         * with computer setup for local: should succeed
         * with computer setup for ssh: should fail
        """
        self.comp_builder.label = 'test_local_ni_empty'
        self.comp_builder.transport = 'local'
        comp = self.comp_builder.new()
        comp.store()

        options = ['local', comp.label, '--non-interactive', '--safe-interval', '0']
        result = self.cli_runner.invoke(computer_configure, options, catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

        self.comp_builder.label = 'test_local_ni_empty_mismatch'
        self.comp_builder.transport = 'ssh'
        comp_mismatch = self.comp_builder.new()
        comp_mismatch.store()

        options = ['local', comp_mismatch.label, '--non-interactive']
        result = self.cli_runner.invoke(computer_configure, options, catch_exceptions=False)
        self.assertIsNotNone(result.exception)
        self.assertIn('ssh', result.output)
        self.assertIn('local', result.output)

    def test_local_interactive(self):
        """Test computer configuration for local transports."""
        self.comp_builder.label = 'test_local_interactive'
        self.comp_builder.transport = 'local'
        comp = self.comp_builder.new()
        comp.store()

        result = self.cli_runner.invoke(computer_configure, ['local', comp.label], input='\n', catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

    def test_ssh_interactive(self):
        """
        Check that the interactive prompt is accepting the correct values.

        Actually, even passing a shorter set of options should work:
        ``verdi computer configure ssh`` is able to provide sensible default
        parameters reading from the ssh config file.
        We are here therefore only checking some of them.
        """
        self.comp_builder.label = 'test_ssh_interactive'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        remote_username = '******'
        port = 345
        look_for_keys = False
        key_filename = ''

        # I just pass the first four arguments:
        # the username, the port, look_for_keys, and the key_filename
        # This testing also checks that an empty key_filename is ok
        command_input = ('{remote_username}\n{port}\n{look_for_keys}\n{key_filename}\n').format(
            remote_username=remote_username,
            port=port,
            look_for_keys='yes' if look_for_keys else 'no',
            key_filename=key_filename
        )

        result = self.cli_runner.invoke(
            computer_configure, ['ssh', comp.label], input=command_input, catch_exceptions=False
        )
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)
        new_auth_params = comp.get_authinfo(self.user).get_auth_params()
        self.assertEqual(new_auth_params['username'], remote_username)
        self.assertEqual(new_auth_params['port'], port)
        self.assertEqual(new_auth_params['look_for_keys'], look_for_keys)
        self.assertEqual(new_auth_params['key_filename'], key_filename)

    def test_local_from_config(self):
        """Test configuring a computer from a config file"""
        label = 'test_local_from_config'
        self.comp_builder.label = label
        self.comp_builder.transport = 'local'
        computer = self.comp_builder.new()
        computer.store()

        interval = 20

        with tempfile.NamedTemporaryFile('w') as handle:
            handle.write("""---
safe_interval: {interval}
""".format(interval=interval))
            handle.flush()

            options = ['local', computer.label, '--config', os.path.realpath(handle.name)]
            result = self.cli_runner.invoke(computer_configure, options)

        self.assertClickResultNoException(result)
        self.assertEqual(computer.get_configuration()['safe_interval'], interval)

    def test_ssh_ni_empty(self):
        """
        Test verdi computer configure ssh <comp>

        Test twice, with comp setup for ssh or local.

         * with computer setup for ssh: should succeed
         * with computer setup for local: should fail
        """
        self.comp_builder.label = 'test_ssh_ni_empty'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        options = ['ssh', comp.label, '--non-interactive', '--safe-interval', '1']
        result = self.cli_runner.invoke(computer_configure, options, catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

        self.comp_builder.label = 'test_ssh_ni_empty_mismatch'
        self.comp_builder.transport = 'local'
        comp_mismatch = self.comp_builder.new()
        comp_mismatch.store()

        options = ['ssh', comp_mismatch.label, '--non-interactive']
        result = self.cli_runner.invoke(computer_configure, options, catch_exceptions=False)
        self.assertIsNotNone(result.exception)
        self.assertIn('local', result.output)
        self.assertIn('ssh', result.output)

    def test_ssh_ni_username(self):
        """Test verdi computer configure ssh <comp> --username=<username>."""
        self.comp_builder.label = 'test_ssh_ni_username'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        username = '******'
        options = ['ssh', comp.label, '--non-interactive', '--username={}'.format(username), '--safe-interval', '1']
        result = self.cli_runner.invoke(computer_configure, options, catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)
        self.assertEqual(
            orm.AuthInfo.objects.get(dbcomputer_id=comp.id, aiidauser_id=self.user.id).get_auth_params()['username'],
            username
        )

    def test_show(self):
        """Test verdi computer configure show <comp>."""
        self.comp_builder.label = 'test_show'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        result = self.cli_runner.invoke(computer_configure, ['show', comp.label], catch_exceptions=False)

        result = self.cli_runner.invoke(computer_configure, ['show', comp.label, '--defaults'], catch_exceptions=False)
        self.assertIn('* username', result.output)

        result = self.cli_runner.invoke(
            computer_configure, ['show', comp.label, '--defaults', '--as-option-string'], catch_exceptions=False
        )
        self.assertIn('--username='******'ssh', comp.label, '--non-interactive']
        config_cmd.extend(result.output.replace("'", '').split(' '))
        result_config = self.cli_runner.invoke(computer_configure, config_cmd, catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result_config.output)

        result_cur = self.cli_runner.invoke(
            computer_configure, ['show', comp.label, '--as-option-string'], catch_exceptions=False
        )
        self.assertIn('--username=', result.output)
        self.assertEqual(result_cur.output, result.output)
示例#8
0
class TestVerdiComputerConfigure(AiidaTestCase):
    def setUp(self):
        """Prepare computer builder with common properties."""
        from aiida.orm.utils.builders.computer import ComputerBuilder
        self.cli_runner = CliRunner()
        self.user = orm.User.objects.get_default()
        self.comp_builder = ComputerBuilder(label='test_comp_setup')
        self.comp_builder.hostname = 'localhost'
        self.comp_builder.description = 'Test Computer'
        self.comp_builder.scheduler = 'direct'
        self.comp_builder.work_dir = '/tmp/aiida'
        self.comp_builder.prepend_text = ''
        self.comp_builder.append_text = ''
        self.comp_builder.mpiprocs_per_machine = 8
        self.comp_builder.mpirun_command = 'mpirun'
        self.comp_builder.shebang = '#!xonsh'

    def test_top_help(self):
        """Test help option of verdi computer configure."""
        result = self.cli_runner.invoke(computer_configure, ['--help'],
                                        catch_exceptions=False)
        self.assertIn('ssh', result.output)
        self.assertIn('local', result.output)

    def test_reachable(self):
        """Test reachability of top level and sub commands."""
        import subprocess as sp
        output = sp.check_output(['verdi', 'computer', 'configure', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'local', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'ssh', '--help'])
        sp.check_output(['verdi', 'computer', 'configure', 'show', '--help'])

    def test_local_ni_empty(self):
        """
        Test verdi computer configure local <comp>

        Test twice, with comp setup for local or ssh.

         * with computer setup for local: should succeed
         * with computer setup for ssh: should fail
        """
        self.comp_builder.label = 'test_local_ni_empty'
        self.comp_builder.transport = 'local'
        comp = self.comp_builder.new()
        comp.store()

        options = [
            'local', comp.label, '--non-interactive', '--safe-interval', '0'
        ]
        result = self.cli_runner.invoke(computer_configure,
                                        options,
                                        catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

        self.comp_builder.label = 'test_local_ni_empty_mismatch'
        self.comp_builder.transport = 'ssh'
        comp_mismatch = self.comp_builder.new()
        comp_mismatch.store()

        options = ['local', comp_mismatch.label, '--non-interactive']
        result = self.cli_runner.invoke(computer_configure,
                                        options,
                                        catch_exceptions=False)
        self.assertIsNotNone(result.exception)
        self.assertIn('ssh', result.output)
        self.assertIn('local', result.output)

    def test_local_interactive(self):
        self.comp_builder.label = 'test_local_interactive'
        self.comp_builder.transport = 'local'
        comp = self.comp_builder.new()
        comp.store()

        result = self.cli_runner.invoke(computer_configure,
                                        ['local', comp.label],
                                        input='\n',
                                        catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

    def test_local_from_config(self):
        """Test configuring a computer from a config file"""
        label = 'test_local_from_config'
        self.comp_builder.label = label
        self.comp_builder.transport = 'local'
        computer = self.comp_builder.new()
        computer.store()

        interval = 20

        with tempfile.NamedTemporaryFile('w') as handle:
            handle.write("""---
safe_interval: {interval}
""".format(interval=interval))
            handle.flush()

            options = [
                'local', computer.label, '--config',
                os.path.realpath(handle.name)
            ]
            result = self.cli_runner.invoke(computer_configure, options)

        self.assertClickResultNoException(result)
        self.assertEqual(computer.get_configuration()['safe_interval'],
                         interval)

    def test_ssh_ni_empty(self):
        """
        Test verdi computer configure ssh <comp>

        Test twice, with comp setup for ssh or local.

         * with computer setup for ssh: should succeed
         * with computer setup for local: should fail
        """
        self.comp_builder.label = 'test_ssh_ni_empty'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        options = [
            'ssh', comp.label, '--non-interactive', '--safe-interval', '1'
        ]
        result = self.cli_runner.invoke(computer_configure,
                                        options,
                                        catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)

        self.comp_builder.label = 'test_ssh_ni_empty_mismatch'
        self.comp_builder.transport = 'local'
        comp_mismatch = self.comp_builder.new()
        comp_mismatch.store()

        options = ['ssh', comp_mismatch.label, '--non-interactive']
        result = self.cli_runner.invoke(computer_configure,
                                        options,
                                        catch_exceptions=False)
        self.assertIsNotNone(result.exception)
        self.assertIn('local', result.output)
        self.assertIn('ssh', result.output)

    def test_ssh_ni_username(self):
        """Test verdi computer configure ssh <comp> --username=<username>."""
        self.comp_builder.label = 'test_ssh_ni_username'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        username = '******'
        options = [
            'ssh', comp.label, '--non-interactive',
            '--username={}'.format(username), '--safe-interval', '1'
        ]
        result = self.cli_runner.invoke(computer_configure,
                                        options,
                                        catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user), msg=result.output)
        self.assertEqual(
            orm.AuthInfo.objects.get(
                dbcomputer_id=comp.id,
                aiidauser_id=self.user.id).get_auth_params()['username'],
            username)

    def test_show(self):
        """Test verdi computer configure show <comp>."""
        self.comp_builder.label = 'test_show'
        self.comp_builder.transport = 'ssh'
        comp = self.comp_builder.new()
        comp.store()

        result = self.cli_runner.invoke(computer_configure,
                                        ['show', comp.label],
                                        catch_exceptions=False)

        result = self.cli_runner.invoke(computer_configure,
                                        ['show', comp.label, '--defaults'],
                                        catch_exceptions=False)
        self.assertIn('* username', result.output)

        result = self.cli_runner.invoke(
            computer_configure,
            ['show', comp.label, '--defaults', '--as-option-string'],
            catch_exceptions=False)
        self.assertIn('--username='******'ssh', comp.label, '--non-interactive']
        config_cmd.extend(result.output.replace("'", '').split(' '))
        result_config = self.cli_runner.invoke(computer_configure,
                                               config_cmd,
                                               catch_exceptions=False)
        self.assertTrue(comp.is_user_configured(self.user),
                        msg=result_config.output)

        result_cur = self.cli_runner.invoke(
            computer_configure, ['show', comp.label, '--as-option-string'],
            catch_exceptions=False)
        self.assertIn('--username=', result.output)
        self.assertEqual(result_cur.output, result.output)
示例#9
0
def set_computer_builder(ctx, param, value):
    """Set the computer spec for defaults of following options."""
    from aiida.orm.utils.builders.computer import ComputerBuilder
    ctx.computer_builder = ComputerBuilder.from_computer(value)
    return value
示例#10
0
def duplicate_fe(computer: Computer, include_codes, input_plugin, suffix,
                 dry_run, job_should_keep_env):
    """
    Create copies of the existing computer using FwScheduler, add existing
    Code if requested.
    """
    from aiida import orm
    from aiida.orm.utils.builders.computer import ComputerBuilder

    from fireworks.fw_config import LAUNCHPAD_LOC

    if LAUNCHPAD_LOC is None:
        echo.echo_critical(
            'Cannot find the default Fireworks launchpad. '
            'Please make sure you have configured Fireworks correctly, see '
            'https://materialsproject.github.io/fireworks/config_tutorial.html'
        )

    builder = ComputerBuilder.from_computer(computer)
    if 'slurm' in computer.scheduler_type or job_should_keep_env:
        builder.scheduler = "fireworks_scheduler.keepenv"
    else:
        builder.scheduler = "fireworks_scheduler.default"

    echo.echo_info(f"Scheduler for the new computer: {builder.scheduler}")

    builder.label += "-" + suffix
    builder.description += "(Using Fireworks as the scheduler.)"
    comp = builder.new()
    echo.echo_info(f"Adding new computer {comp}")
    if not dry_run:
        comp.store()
        echo.echo_info(f"Computer {comp} has been saved to the database")

    if include_codes:
        qb_code_filters = dict()
        if input_plugin:
            qb_code_filters['attributes.input_plugin'] = input_plugin.name

        user = orm.User.objects.get_default()
        qbd = orm.QueryBuilder()
        qbd.append(orm.Computer, tag='computer', filters={'id': computer.pk})
        qbd.append(orm.Code,
                   with_computer='computer',
                   tag='code',
                   filters=qb_code_filters,
                   project=['*'])
        qbd.append(orm.User,
                   tag='user',
                   with_node='code',
                   filters={'email': user.email})
        new_codes = []
        for (code, ) in qbd.iterall():
            new_code = deepcopy(code)
            new_code.set_remote_computer_exec(
                (comp, code.get_remote_exec_path()))
            new_codes.append(new_code)
            echo.echo_info(f"Adding new code {new_code}")

        if not dry_run:
            for code in new_codes:
                code.store()
                echo.echo_info(f"Code {code} has been saved to the database")

    if dry_run:
        echo.echo_info("This is a dry-run nothing has been saved.")