Example #1
0
def connect_to_membership(params):
    control_console = helpers.get_control_console(params['console_sock'])
    module_hostvars = params['module_hostvars']
    play_hosts = params['play_hosts']

    changed = False

    for instance_name, instance_vars in module_hostvars.items():
        if helpers.is_expelled(instance_vars) or helpers.is_stateboard(
                instance_vars):
            continue

        if 'config' not in instance_vars or 'advertise_uri' not in instance_vars[
                'config']:
            continue

        connected, err = probe_server(control_console,
                                      instance_vars['config']['advertise_uri'])
        if err is not None and instance_name in play_hosts:
            return helpers.ModuleRes(failed=True, msg=err)

        if connected:
            changed = True

    return helpers.ModuleRes(changed=changed)
def get_control_instance(params):
    module_hostvars = params['module_hostvars']
    play_hosts = params['play_hosts']
    console_sock = params['console_sock']
    app_name = params['app_name']

    control_console = helpers.get_control_console(console_sock)

    control_instance_name, err = get_control_instance_name(
        module_hostvars, play_hosts, control_console)
    if err is not None:
        return helpers.ModuleRes(failed=True, msg=err)

    # in the ideal imagined world we could just use
    # instance_vars['instance_info'], but if control instance is not
    # in play_hosts, instance_info isn't computed for it
    instance_vars = module_hostvars[control_instance_name]

    run_dir = instance_vars.get('cartridge_run_dir')
    control_instance_console_sock = helpers.get_instance_console_sock(
        run_dir,
        app_name,
        control_instance_name,
    )

    http_port = instance_vars.get('config', {}).get('http_port')

    return helpers.ModuleRes(changed=False,
                             fact={
                                 'name': control_instance_name,
                                 'console_sock': control_instance_console_sock,
                                 'http_port': http_port,
                             })
def check_members_alive(params):
    console_sock = params['console_sock']
    allowed_states = params['allowed_states']

    control_console = helpers.get_control_console(console_sock)

    bad_members, err = control_console.eval_res_err(
        '''
        local fun = require('fun')
        local membership = require('membership')

        local cartridge_topology = require('cartridge.topology')
        local confapplier = require('cartridge.confapplier')

        local topology_cfg = confapplier.get_readonly('topology')

        if topology_cfg == nil then
            return nil, "Instances aren't joined to cluster yet"
        end

        local allowed_states = ...
        local bad_members = {}

        for _it, instance_uuid, server in fun.filter(cartridge_topology.not_disabled, topology_cfg.servers) do
            local member = membership.get_member(server.uri) or {}

            if (member.payload.uuid ~= instance_uuid) then
                table.insert(bad_members, string.format(
                    '%s uuid mismatch: expected %s, have %s',
                    server.uri, instance_uuid, member.payload.uuid
                ))
            elseif (member.status ~= 'alive') then
                table.insert(bad_members, string.format(
                    '%s status is %s',
                    server.uri, member.status
                ))
            elseif allowed_states ~= nil and next(allowed_states) ~= nil then
                local member_state = member.payload.state
                if fun.index(member_state, allowed_states) == nil then
                    table.insert(bad_members, string.format(
                    '%s state is %s',
                    server.uri, member_state
                ))
                end
            end
        end

        return bad_members
    ''', allowed_states)

    if err is not None:
        return helpers.ModuleRes(failed=True, msg=err)

    if bad_members:
        return helpers.ModuleRes(failed=True,
                                 msg="Some instances aren't alive: %s" %
                                 ', '.join(sorted(bad_members)))

    return helpers.ModuleRes(changed=False)
Example #4
0
def check_instance_state(control_console, expected_states,
                         check_buckets_are_discovered):
    instance_state, err = control_console.eval_res_err('''
        return require('cartridge.confapplier').get_state()
    ''')
    if not instance_state:
        return helpers.ModuleRes(failed=True,
                                 msg="Impossible to get state: %s" % err)
    if instance_state not in expected_states:
        return helpers.ModuleRes(
            failed=True,
            msg="Instance is not in one of states: %s, it's in '%s' state" % (
                expected_states,
                instance_state,
            ),
        )

    if check_buckets_are_discovered:
        buckets_ok, err = control_console.eval_res_err('''
            local vshard_utils = require('cartridge.vshard-utils')
            local vshard_router = require('cartridge.roles.vshard-router')

            local function check_group_buckets(group_name, group_opts)
                if not group_opts.bootstrapped then
                    return true
                end

                local router = vshard_router.get(group_name)
                if router == nil then
                    return true
                end

                local unknown_buckets = router:info().bucket.unknown
                if unknown_buckets == 0 then
                    return true
                end

                return nil, string.format(
                    "%s out of %s buckets are not discovered in group '%s'",
                    unknown_buckets,
                    group_opts.bucket_count,
                    group_name
                )
            end

            local groups = vshard_utils.get_known_groups()
            for group_name, group_opts in pairs(groups) do
                local _, err = check_group_buckets(group_name, group_opts)
                if err ~= nil then
                    return nil, err
                end
            end

            return true
        ''')
        if not buckets_ok:
            return helpers.ModuleRes(failed=True, msg=err)

    return helpers.ModuleRes(changed=False)
Example #5
0
def patch_instance_in_runtime(params):
    console_sock = params['console_sock']
    instance_config = params['instance_config']
    cartridge_defaults = params['cartridge_defaults']
    strict_mode = params['strict_mode']

    changed, error = configure_box_cfg_params(console_sock, instance_config,
                                              cartridge_defaults, strict_mode)
    if error is not None:
        return helpers.ModuleRes(failed=True, msg=error)
    return helpers.ModuleRes(changed=changed)
Example #6
0
def check_stateboard_state(control_console):
    box_status, err = control_console.eval_res_err('''
        if type(box.cfg) == 'function' or box.cfg.listen == nil then
            return nil, "box hasn't been configured"
        end
        return true
    ''')
    if not box_status:
        return helpers.ModuleRes(failed=True,
                                 msg="Stateboard is not running: %s" % err)

    return helpers.ModuleRes(changed=False)
def get_alive_not_expelled_instance(params):
    module_hostvars = params['module_hostvars']
    play_hosts = params['play_hosts']
    app_name = params['app_name']

    canditates_by_uris = {}

    for instance_name in play_hosts:
        instance_vars = module_hostvars[instance_name]
        if helpers.is_expelled(instance_vars) or helpers.is_stateboard(
                instance_vars):
            continue

        instance_config = instance_vars.get('config')
        if instance_config is None:
            continue

        advertise_uri = instance_config['advertise_uri']
        canditates_by_uris[advertise_uri] = instance_name

    alive_not_expelled_instance_name = None
    for uri, instance_name in sorted(canditates_by_uris.items()):
        host, port = uri.rsplit(':', 1)

        try:
            conn = socket.create_connection((host, port),
                                            timeout=CONNECTION_TIMEOUT)
            conn.settimeout(CONNECTION_TIMEOUT)
            conn.recv(1024)
        except socket.error:
            continue

        alive_not_expelled_instance_name = instance_name
        break

    if alive_not_expelled_instance_name is None:
        errmsg = "Not found any alive instance that is not expelled and is not a stateboard"
        return helpers.ModuleRes(failed=True, msg=errmsg)

    instance_vars = module_hostvars[alive_not_expelled_instance_name]
    run_dir = instance_vars.get('cartridge_run_dir')
    console_sock = helpers.get_instance_console_sock(
        run_dir,
        app_name,
        alive_not_expelled_instance_name,
    )

    return helpers.ModuleRes(changed=False,
                             fact={
                                 'name': alive_not_expelled_instance_name,
                                 'console_sock': console_sock,
                             })
def main():
    module = AnsibleModule(argument_spec=argument_spec)
    try:
        res = get_systemd_units_info(module.params)
    except Exception as e:
        res = helpers.ModuleRes(exception=e)
    res.exit(module)
Example #9
0
def manage_failover_new(control_console, failover_params):
    current_failover_params = get_failover_params(control_console)

    func_body = '''
        return require('cartridge').failover_set_params(...)
    '''
    res, err = control_console.eval_res_err(func_body, failover_params)

    if err is not None:
        errmsg = 'Failed to set failover params: {}'.format(err)
        return helpers.ModuleRes(failed=True, msg=errmsg)

    new_failover_params = get_failover_params(control_console)

    changed = new_failover_params != current_failover_params
    return helpers.ModuleRes(changed=changed)
Example #10
0
def manage_failover(params):
    failover_params = params.get('failover_params')

    if isinstance(failover_params, bool):
        failover_params = {
            'mode': 'eventual' if failover_params is True else 'disabled'
        }

    rename_dict_key_if_exists(failover_params, 'stateboard_params',
                              'tarantool_params')
    if failover_params.get('state_provider') == 'stateboard':
        failover_params['state_provider'] = 'tarantool'

    control_console = helpers.get_control_console(params['console_sock'])

    helpers.set_twophase_options_from_params(control_console, params)

    version = get_tarantool_version(control_console)
    if version is not None and version >= NEW_FAILOVER_API_CARTRIDGE_VERSION:
        return manage_failover_new(control_console, failover_params)
    else:
        if failover_params['mode'] == 'stateful':
            errmsg = 'Stateful failover is supported since cartridge {}'.format(
                NEW_FAILOVER_API_CARTRIDGE_VERSION)
            return helpers.ModuleRes(failed=True, msg=errmsg)
        return manage_failover_old(control_console, failover_params)
def eval_code(params):
    body = params['body']
    args = params['args']

    control_console = helpers.get_control_console(params['console_sock'])
    eval_res = control_console.eval(body, *args)

    return helpers.ModuleRes(changed=False, fact=eval_res)
def config_app(params):
    control_console = helpers.get_control_console(params['console_sock'])
    config = params['app_config']

    new_sections = {}
    for section_name, section in config.items():
        if section_is_deleted(section):
            new_sections[section_name] = None
        else:
            new_sections[section_name] = section.get('body')

    helpers.set_twophase_options_from_params(control_console, params)

    changed, err = helpers.patch_clusterwide_config(control_console,
                                                    new_sections)
    if err is not None:
        return helpers.ModuleRes(failed=True, msg=err)

    return helpers.ModuleRes(changed=changed)
Example #13
0
def get_scenario_steps(params):
    steps_paths = get_steps_paths(params['role_path'], params['custom_steps_dir'], params['custom_steps'])
    scenario, err = get_scenario(
        params['scenario'],
        params['role_scenarios'], params['custom_scenarios'], params['scenario_name'],
    )
    if err:
        return helpers.ModuleRes(failed=True, msg=err)

    scenario_steps = []
    for step_name in scenario:
        if step_name not in steps_paths:
            return helpers.ModuleRes(failed=True, msg="Unknown step '%s'" % step_name)

        scenario_steps.append({
            'name': step_name,
            'path': steps_paths[step_name],
        })

    return helpers.ModuleRes(changed=False, fact=scenario_steps)
Example #14
0
def bootstrap_vshard(params):
    control_console = helpers.get_control_console(params['console_sock'])

    helpers.set_twophase_options_from_params(control_console, params)

    can_bootstrap, _ = control_console.eval_res_err('''
        return require('cartridge.vshard-utils').can_bootstrap()
    ''')

    if not can_bootstrap:
        return helpers.ModuleRes(changed=False)

    ok, err = control_console.eval_res_err('''
        return require('cartridge.admin').bootstrap_vshard()
    ''')

    if not ok:
        errmsg = 'Vshard bootstrap failed: {}'.format(err)
        return helpers.ModuleRes(failed=True, msg=errmsg)

    return helpers.ModuleRes()
Example #15
0
def check_cluster_issues(params):
    allow_warnings = params['allow_warnings']
    show_issues = params['show_issues']
    console_sock = params['console_sock']

    control_console = helpers.get_control_console(console_sock)

    issues, err = control_console.eval_res_err('''
        return require('cartridge.issues').list_on_cluster()
    ''')

    if err is not None:
        msg = "Received error on getting list of cluster issues: %s" % err
        if issues is None:
            return helpers.ModuleRes(failed=True, msg=msg)

        helpers.warn(msg)

    issues_by_level = {}
    for issue in issues:
        level = issue['level']
        if level not in issues_by_level:
            issues_by_level[level] = []

        issues_by_level[level].append(issue)

    if show_issues:
        messages = get_messages(issues_by_level)
        helpers.warn(*messages)

    if issues:
        if allow_warnings:
            critical_issues_num = len(issues) - len(issues_by_level.get('warning', []))
            if critical_issues_num > 0:
                return helpers.ModuleRes(failed=True, msg="Cluster has %s critical issues" % critical_issues_num)
        else:
            return helpers.ModuleRes(failed=True, msg="Cluster has %s issues" % len(issues))

    return helpers.ModuleRes(changed=False)
Example #16
0
def manage_failover_old(control_console, failover_params):
    failover_enabled = failover_params['mode'] == 'eventual'

    current_failover, _ = control_console.eval_res_err('''
        return require('cartridge').admin_get_failover()
    ''')

    if current_failover == failover_enabled:
        return helpers.ModuleRes(changed=False)

    function_name = 'admin_enable_failover' if failover_enabled else 'admin_disable_failover'

    func_body = '''
        local function_name = ...
        return require('cartridge')[function_name]()
    '''
    _, err = control_console.eval_res_err(func_body, function_name)

    if err is not None:
        errmsg = 'Failed {}: {}'.format(function_name, err)
        return helpers.ModuleRes(failed=True, msg=errmsg)

    return helpers.ModuleRes()
def get_package_info(params):
    package_path = params['package_path']
    app_name = params['app_name']

    package_type = get_package_type(package_path)

    if package_type == 'rpm':
        package_info = get_rpm_info(package_path)
    elif package_type == 'deb':
        package_info = get_deb_info(package_path)
    elif package_type == 'tgz':
        package_info = get_tgz_info(package_path)
    else:
        return helpers.ModuleRes(failed=True,
                                 msg='Unknown package type: %s' % package_type)

    if package_type != 'tgz' and package_info['name'] != app_name:
        msg = "Value 'cartridge_app_name' should be equal to package name. " + \
              "Found 'cartridge_app_name': '%s', package name: '%s'" % (app_name, package_info['name'])
        return helpers.ModuleRes(failed=True, msg=msg)

    package_info['type'] = package_type

    return helpers.ModuleRes(changed=False, fact=package_info)
Example #18
0
def check_state(params):
    try:
        control_console = helpers.get_control_console(params['console_sock'])

        if params['stateboard']:
            return check_stateboard_state(control_console)
        else:
            return check_instance_state(
                control_console,
                params['expected_states'],
                params['check_buckets_are_discovered'],
            )

    except helpers.CartridgeException as e:
        return helpers.ModuleRes(exception=e)
def get_one_not_expelled_instance_for_machine(params):
    module_hostvars = params['module_hostvars']
    play_hosts = params['play_hosts']

    machine_hostnames = set()
    instance_names = []

    for instance_name in play_hosts:
        instance_vars = module_hostvars[instance_name]

        if helpers.is_expelled(instance_vars):
            continue

        machine_hostname = get_machine_hostname(instance_vars, instance_name)
        if machine_hostname not in machine_hostnames:
            machine_hostnames.add(machine_hostname)
            instance_names.append(instance_name)

    return helpers.ModuleRes(changed=False, fact=instance_names)
def get_cached_facts(params):
    hostvars = params['hostvars']

    facts = {}
    for instance_name, instance_vars in hostvars.items():
        role_vars = hostvars[instance_name].get('role_facts', {})

        for target, fact_names in FACTS_BY_TARGETS.items():
            facts[target] = facts.get(target, {})
            facts[target][instance_name] = facts[target].get(instance_name, {})

            for fact_name in fact_names:
                if fact_name in role_vars:
                    facts[target][instance_name][fact_name] = role_vars[
                        fact_name]
                elif fact_name in instance_vars:
                    facts[target][instance_name][fact_name] = instance_vars[
                        fact_name]

    return helpers.ModuleRes(changed=False, facts=facts)
Example #21
0
def apply_app_config(params):
    # We cannot use the fact 'dest' from the copy task,
    # because in the case of transferring a folder with one file,
    # 'dest' will contain the path to the file, not the path to the folder.
    # Therefore, we consider the path ourselves.
    remote_config_path = os.path.join(
        params['remote_dir'], os.path.basename(params['local_config_path']))

    upload_mode = prepare_upload_mode(
        upload_mode=params['upload_mode'],
        remote_config_path=remote_config_path,
    )

    output_format, apply_config_func = get_output_format_and_apply_config_func(
        upload_mode,
        console_sock=params['console_sock'],
        upload_url=params['upload_url'],
        cluster_cookie=params['cluster_cookie'],
        tdg_token=params['tdg_token'],
    )

    temp_paths = [remote_config_path]
    new_config_path = prepare_config(remote_config_path, output_format,
                                     upload_mode)
    if new_config_path != remote_config_path:
        temp_paths.append(new_config_path)

    changed = apply_config_func(new_config_path)

    return helpers.ModuleRes(changed=changed,
                             fact={
                                 'temp_paths': temp_paths,
                                 'upload_url': params['upload_url'],
                                 'upload_mode': params['upload_mode'],
                                 'expected_format': output_format,
                             })
Example #22
0
def set_needs_restart(params):
    instance_info = params['instance_info']
    console_sock = instance_info['console_sock']

    # check if instance was not started yet
    if not os.path.exists(console_sock):
        return helpers.ModuleRes(changed=True, fact=True)

    try:
        control_console = helpers.get_control_console(console_sock)
    except helpers.CartridgeException as e:
        allowed_errcodes = [
            helpers.CartridgeErrorCodes.SOCKET_NOT_FOUND,
            helpers.CartridgeErrorCodes.FAILED_TO_CONNECT_TO_SOCKET,
            helpers.CartridgeErrorCodes.INSTANCE_IS_NOT_STARTED_YET
        ]
        if e.code in allowed_errcodes:
            return helpers.ModuleRes(changed=True, fact=True)

        raise e

    if params['check_package_updated']:
        needs_restart, err = check_needs_restart_to_update_package(params)
        if err is not None:
            return helpers.ModuleRes(failed=True, msg=err)
        if needs_restart:
            return helpers.ModuleRes(changed=True, fact=True)

    if params['check_config_updated']:
        needs_restart, err = check_needs_restart_to_update_config(
            params, control_console)
        if err is not None:
            return helpers.ModuleRes(failed=True, msg=err)
        if needs_restart:
            return helpers.ModuleRes(changed=True, fact=True)

    return helpers.ModuleRes(changed=False, fact=False)
Example #23
0
def failover_promote(params):
    console_sock = params['console_sock']
    control_console = helpers.get_control_console(console_sock)

    err = check_leaders_promotion_is_possible(control_console)
    if err is not None:
        return helpers.ModuleRes(failed=True, msg=err)

    failover_promote_params = params.get('failover_promote_params')
    if failover_promote_params is None:
        failover_promote_params = {}

    critical_warnings = []

    # get replicaset leaders
    if params['promote_play_hosts']:
        module_hostvars = params['module_hostvars']
        play_hosts = params['play_hosts']

        replicaset_leaders, dead_replicasets = get_replicaset_leaders_by_play_hosts(
            play_hosts, module_hostvars, control_console)
        if dead_replicasets:
            critical_warnings.append(
                'These replicasets have no alive instances across specified play hosts: %s'
                % ', '.join(sorted(dead_replicasets)))
    else:
        specified_replicaset_leaders = failover_promote_params.get(
            'replicaset_leaders')

        replicaset_leaders, err = get_replicaset_leaders_by_aliases(
            specified_replicaset_leaders, control_console)

        if err is not None:
            return helpers.ModuleRes(failed=True, msg=err)

    if not replicaset_leaders:
        if critical_warnings:
            return helpers.ModuleRes(
                failed=True,
                msg="Failed to promote leaders: %s" % err,
                warnings=critical_warnings,
            )
        return helpers.ModuleRes(changed=False)

    force_inconsistency = failover_promote_params.get('force_inconsistency')

    # set two-phase commit opts
    helpers.set_twophase_options_from_params(control_console, params)

    active_leaders = get_active_leaders(control_console)

    _, err = call_failover_promote(control_console, replicaset_leaders,
                                   force_inconsistency)
    if err is not None:
        return helpers.ModuleRes(
            failed=True,
            msg="Failed to promote leaders: %s" % err,
            warnings=critical_warnings,
        )

    new_active_leaders = get_active_leaders(control_console)

    if critical_warnings:
        return helpers.ModuleRes(failed=True,
                                 msg="Promoted with critical warnings",
                                 warnings=critical_warnings)

    return helpers.ModuleRes(changed=active_leaders != new_active_leaders)
def validate_config(params):
    found_replicasets = {}
    found_common_params = {}

    warnings = []

    for host in params['play_hosts']:
        instance_vars = params['module_hostvars'][host]

        if instance_vars.get('edit_topology_timeout') is not None:
            warnings.append(
                "Variable 'edit_topology_timeout' is deprecated since 1.9.0 and will be removed in 2.0.0. "
                "Use 'edit_topology_healthy_timeout' instead."
            )
        if instance_vars.get('instance_start_timeout') is not None:
            warnings.append(
                "Variable 'instance_start_timeout' is deprecated since 1.10.0 and will be removed in 2.0.0. "
                "Use 'instance_start_retries' and 'instance_start_delay' instead."
            )
        if instance_vars.get('instance_discover_buckets_timeout') is not None:
            warnings.append(
                "Variable 'instance_discover_buckets_timeout' is deprecated since 1.10.0 and will be removed in 2.0.0. "
                "Use 'instance_discover_buckets_retries' and 'instance_discover_buckets_delay' instead."
            )

        # Validate types
        errmsg = validate_types(instance_vars)
        if errmsg is not None:
            return helpers.ModuleRes(failed=True, msg=errmsg)

        if instance_vars.get('stateboard') is True:
            errmsg = check_stateboard(instance_vars)
            if errmsg is not None:
                return helpers.ModuleRes(failed=True, msg=errmsg)
            continue

        # All required params should be specified
        errmsg = check_required_params(instance_vars, host)
        if errmsg is not None:
            return helpers.ModuleRes(failed=True, msg=errmsg)

        errmsg = check_cluster_cookie_symbols(instance_vars.get('cartridge_cluster_cookie'))
        if errmsg is not None:
            return errmsg

        # Instance config
        errmsg = check_instance_config(instance_vars['config'], host)
        if errmsg is not None:
            return helpers.ModuleRes(failed=True, msg=errmsg)

        # Params common for all instances
        errmsg = check_params_the_same_for_all_hosts(instance_vars, found_common_params)
        if errmsg is not None:
            return helpers.ModuleRes(failed=True, msg=errmsg)

        # Cartridge defaults
        if 'cartridge_defaults' in instance_vars:
            if 'cluster_cookie' in instance_vars['cartridge_defaults']:
                errmsg = 'Cluster cookie must be specified in "cartridge_cluster_cookie", not in "cartridge_defaults"'
                return helpers.ModuleRes(failed=True, msg=errmsg)

        # Instance state
        if instance_vars.get('expelled') is True and instance_vars.get('restarted') is True:
            errmsg = 'Flags "expelled" and "restarted" cannot be set at the same time'
            return helpers.ModuleRes(failed=True, msg=errmsg)

        # Replicasets
        errmsg = check_replicaset(instance_vars, found_replicasets)
        if errmsg is not None:
            return helpers.ModuleRes(failed=True, msg=errmsg)

        # Dist retention
        if 'cartridge_keep_num_latest_dists' in instance_vars:
            keep_num_latest_dists = instance_vars['cartridge_keep_num_latest_dists']
            if keep_num_latest_dists <= 0:
                errmsg = '"cartridge_keep_num_latest_dists" should be greater than 0'
                return helpers.ModuleRes(failed=True, msg=errmsg)
            if keep_num_latest_dists == 1:
                warnings.append(
                    "Using 'cartridge_keep_num_latest_dists' equals to 1 can be dangerous. "
                    "Make sure that there are no instances that use old versions"
                )

    # Authorization params
    errmsg = check_auth(found_common_params)
    if errmsg is not None:
        return helpers.ModuleRes(failed=True, msg=errmsg)

    # Clusterwide config
    errmsg = check_app_config(found_common_params)
    if errmsg is not None:
        return helpers.ModuleRes(failed=True, msg=errmsg)

    # Failover
    errmsg = check_failover(found_common_params)
    if errmsg is not None:
        return helpers.ModuleRes(failed=True, msg=errmsg)

    # Scenario
    errmsg = check_scenario(found_common_params)
    if errmsg is not None:
        return helpers.ModuleRes(failed=True, msg=errmsg)

    # Failover promote params
    errmsg = check_failover_promote_params(found_common_params)
    if errmsg is not None:
        return helpers.ModuleRes(failed=True, msg=errmsg)

    if found_common_params.get('cartridge_failover') is not None:
        warnings.append(
            "Variable 'cartridge_failover' is deprecated since 1.3.0 and will be removed in 2.0.0. "
            "Use 'cartridge_failover_params' instead."
        )

    return helpers.ModuleRes(changed=False, warnings=warnings)
def edit_topology(params):
    console_sock = params['console_sock']
    module_hostvars = params['module_hostvars']
    play_hosts = params['play_hosts']
    healthy_timeout = params['healthy_timeout']
    allow_missed_instances = params['allow_missed_instances']

    replicasets = get_configured_replicasets(module_hostvars, play_hosts)
    instances = get_instances_to_configure(module_hostvars, play_hosts)

    if not replicasets and not instances:
        return helpers.ModuleRes(changed=False)

    control_console = helpers.get_control_console(console_sock)

    helpers.set_twophase_options_from_params(control_console, params)
    set_enabled_roles(replicasets, control_console)

    cluster_instances = helpers.get_cluster_instances(control_console)
    cluster_replicasets = helpers.get_cluster_replicasets(control_console)

    # Configure replicasets and instances:
    # * Create new replicasets.
    # * Edit existent replicasets and join new instances to them.
    #   In this case failover_priority isn't changed since
    #   new instances hasn't UUIDs before join.
    # * Expel instances.
    # * Configure instances that are already joined.
    #   New instances aren't configured here since they don't have
    #   UUIDs before join.
    topology_params, err = get_topology_params(replicasets,
                                               cluster_replicasets, instances,
                                               cluster_instances,
                                               allow_missed_instances)
    if err is not None:
        return helpers.ModuleRes(
            failed=True,
            msg="Failed to collect edit topology params: %s" % err)

    topology_changed = False

    if topology_params:
        res, err = control_console.eval_res_err(edit_topology_func_body,
                                                topology_params)
        if err is not None:
            return helpers.ModuleRes(failed=True,
                                     msg="Failed to edit topology: %s" % err)

        topology_changed = True

        # Without this `Peer closed` error is returned on second `edit_topology`
        # call in some cases (e.g. when new instance is joined at first call
        # and then it's configured on second)
        # See https://github.com/tarantool/cartridge/issues/1320
        # The simplest w/a is to add a little delay between this calls,
        # and we just perform `is_healthy` call here.
        # If everything is Ok - this call doesn't take a long time, but
        # guarantees that next `edit_topology` call wouldn't fail.
        # If cluster isn't healthy then it's good to show error.
        if not wait_for_cluster_is_healthy(control_console, healthy_timeout):
            return helpers.ModuleRes(
                failed=True,
                msg="Cluster isn't healthy after editing topology")

        # Now we need to get updated instances and replicasets
        # configuration to check if we need one more call.
        # `edit_topology` returns summary of updated instances
        # so let's use it to update cluster_instances and cluster_replicasets.
        update_cluster_instances_and_replicasets(res, instances,
                                                 cluster_instances,
                                                 cluster_replicasets)

    # Configure failover_priority and instances that were joined on previous call:
    # * Edit failover_priority of replicasets if it's needed.
    # * Configure instances that weren't configured on first `edit_topology` call.
    topology_params, err = get_replicasets_failover_priority_and_instances_params(
        replicasets, cluster_replicasets, instances, cluster_instances,
        allow_missed_instances)
    if err is not None:
        return helpers.ModuleRes(
            failed=True,
            msg=
            "Failed to collect edit topology params for changing failover_priority "
            "and configuring new instances: %s" % err)

    if topology_params:
        res, err = control_console.eval_res_err(edit_topology_func_body,
                                                topology_params)
        if err is not None:
            return helpers.ModuleRes(
                failed=True,
                msg=
                "Failed to edit failover priority and configure instances: %s"
                % err)

        topology_changed = True

        if not wait_for_cluster_is_healthy(control_console, healthy_timeout):
            return helpers.ModuleRes(
                failed=True,
                msg=
                "Cluster isn't healthy after editing failover priority and configuring instances"
            )

    return helpers.ModuleRes(changed=topology_changed)
def get_instance_info(params):
    app_name = params['app_name']
    instance_name = params['instance_name']
    instance_vars = params['instance_vars']

    instance_info = {'paths_to_remove_on_expel': []}

    # app conf file
    instance_info['app_conf_file'] = get_app_conf_file(
        instance_vars['cartridge_conf_dir'], app_name)

    # instance conf file
    instance_info['conf_file'] = get_instance_conf_file(
        instance_vars['cartridge_conf_dir'],
        app_name,
        instance_name,
        instance_vars['stateboard'],
    )
    instance_info['paths_to_remove_on_expel'].append(
        instance_info['conf_file'])

    # instance id (e.g. used for conf section name)
    instance_info['instance_id'] = get_instance_conf_section(
        app_name, instance_name, instance_vars['stateboard'])

    # console socket, PID file paths
    instance_info['console_sock'] = helpers.get_instance_console_sock(
        instance_vars['cartridge_run_dir'], app_name, instance_name,
        instance_vars['stateboard'])
    instance_info['paths_to_remove_on_expel'].append(
        instance_info['console_sock'])

    instance_info['pid_file'] = helpers.get_instance_pid_file(
        instance_vars['cartridge_run_dir'], app_name, instance_name,
        instance_vars['stateboard'])

    # instance work dir
    instance_info['work_dir'] = helpers.get_instance_dir(
        instance_vars['cartridge_data_dir'], app_name, instance_name,
        instance_vars['stateboard'])
    instance_info['paths_to_remove_on_expel'].append(instance_info['work_dir'])

    # instance memtx dir
    instance_info['memtx_dir'] = None
    if instance_vars['cartridge_memtx_dir_parent']:
        instance_info['memtx_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_memtx_dir_parent'], app_name,
            instance_name, instance_vars['stateboard'])
        instance_info['paths_to_remove_on_expel'].append(
            instance_info['memtx_dir'])

    # instance vinyl dir
    instance_info['vinyl_dir'] = None
    if instance_vars['cartridge_vinyl_dir_parent']:
        instance_info['vinyl_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_vinyl_dir_parent'], app_name,
            instance_name, instance_vars['stateboard'])
        instance_info['paths_to_remove_on_expel'].append(
            instance_info['vinyl_dir'])

    # instance wal dir
    instance_info['wal_dir'] = None
    if instance_vars['cartridge_wal_dir_parent']:
        instance_info['wal_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_wal_dir_parent'], app_name, instance_name,
            instance_vars['stateboard'])
        instance_info['paths_to_remove_on_expel'].append(
            instance_info['wal_dir'])

    # systemd service name
    instance_info['systemd_service'] = get_instance_systemd_service(
        app_name, instance_name, instance_vars['stateboard'])

    # tmpfiles conf
    instance_info['tmpfiles_conf'] = os.path.join(
        instance_vars['cartridge_tmpfiles_dir'], '%s.conf' % app_name)

    # code dirs
    if not instance_vars['cartridge_multiversion']:
        dist_dir = os.path.join(instance_vars['cartridge_app_install_dir'],
                                app_name)

        instance_info['dist_dir'] = dist_dir
        instance_info['instance_dist_dir'] = dist_dir
    else:
        instance_info['dist_dir'] = get_multiversion_dist_dir(
            instance_vars['cartridge_app_install_dir'],
            instance_vars.get('cartridge_package_path'))

        instance_info[
            'instance_dist_dir'] = helpers.get_multiversion_instance_code_dir(
                instance_vars['cartridge_app_instances_dir'],
                app_name,
                instance_name,
                instance_vars['stateboard'],
            )

    return helpers.ModuleRes(changed=False, fact=instance_info)
def get_systemd_units_info(params):
    app_name = params['app_name']
    instance_vars = params['instance_vars']
    tnt_version = params['tnt_version']

    systemd_units_info = {}

    stateboard_name = helpers.get_instance_id(app_name, stateboard=True)
    systemd_units_info['stateboard_name'] = stateboard_name

    systemd_units_info['app_unit_file'] = '%[email protected]' % app_name
    systemd_units_info['stateboard_unit_file'] = '%s.service' % stateboard_name

    systemd_units_info['instance_work_dir'] = helpers.get_instance_dir(
        instance_vars['cartridge_data_dir'], app_name, instance_name="%i"
    )
    systemd_units_info['stateboard_work_dir'] = helpers.get_instance_dir(
        instance_vars['cartridge_data_dir'], app_name, stateboard=True
    )

    systemd_units_info['instance_memtx_dir'] = None
    systemd_units_info['stateboard_memtx_dir'] = None
    if instance_vars['cartridge_memtx_dir_parent']:
        systemd_units_info['instance_memtx_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_memtx_dir_parent'], app_name, instance_name="%i"
        )
        systemd_units_info['stateboard_memtx_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_memtx_dir_parent'], app_name, stateboard=True
        )

    systemd_units_info['instance_vinyl_dir'] = None
    systemd_units_info['stateboard_vinyl_dir'] = None
    if instance_vars['cartridge_vinyl_dir_parent']:
        systemd_units_info['instance_vinyl_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_vinyl_dir_parent'], app_name, instance_name="%i"
        )
        systemd_units_info['stateboard_vinyl_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_vinyl_dir_parent'], app_name, stateboard=True
        )

    systemd_units_info['instance_wal_dir'] = None
    systemd_units_info['stateboard_wal_dir'] = None
    if instance_vars['cartridge_wal_dir_parent']:
        systemd_units_info['instance_wal_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_wal_dir_parent'], app_name, instance_name="%i"
        )
        systemd_units_info['stateboard_wal_dir'] = helpers.get_instance_dir(
            instance_vars['cartridge_wal_dir_parent'], app_name, stateboard=True
        )

    systemd_units_info['instance_pid_file'] = helpers.get_instance_pid_file(
        instance_vars['cartridge_run_dir'], app_name, instance_name="%i"
    )
    systemd_units_info['stateboard_pid_file'] = helpers.get_instance_pid_file(
        instance_vars['cartridge_run_dir'], app_name, stateboard=True
    )

    systemd_units_info['instance_console_sock'] = helpers.get_instance_console_sock(
        instance_vars['cartridge_run_dir'], app_name, instance_name="%i"
    )
    systemd_units_info['stateboard_console_sock'] = helpers.get_instance_console_sock(
        instance_vars['cartridge_run_dir'], app_name, stateboard=True
    )

    if not instance_vars['cartridge_multiversion']:
        instance_dist_dir = instance_vars['dist_dir']
        stateboard_dir = instance_vars['dist_dir']
    else:
        instances_dir = instance_vars['cartridge_app_instances_dir']

        instance_dist_dir = helpers.get_multiversion_instance_code_dir(
            instances_dir, app_name, instance_name="%i",
        )
        stateboard_dir = helpers.get_multiversion_instance_code_dir(
            instances_dir, app_name, stateboard=True,
        )

    systemd_units_info['instance_entrypoint'] = os.path.join(instance_dist_dir, 'init.lua')
    systemd_units_info['stateboard_entrypoint'] = os.path.join(stateboard_dir, 'stateboard.init.lua')

    if tnt_version:
        opensource_tarantool_binary = '/usr/bin/tarantool'
        systemd_units_info['instance_tarantool_binary'] = opensource_tarantool_binary
        systemd_units_info['stateboard_tarantool_binary'] = opensource_tarantool_binary
    else:
        systemd_units_info['instance_tarantool_binary'] = os.path.join(instance_dist_dir, 'tarantool')
        systemd_units_info['stateboard_tarantool_binary'] = os.path.join(stateboard_dir, 'tarantool')

    return helpers.ModuleRes(changed=False, fact=systemd_units_info)