def restructure_mapping_facts(elements_map,
                              extra_elements_map=None,
                              target_parameter=None,
                              source_parameter=None,
                              source_value=None):
    """
    Function is used to restructure mapping values with the case of `facts`, `condition`, `arguments`, `value` keys
    :param elements_map:
    :param extra_elements_map:
    :param target_parameter:
    :param source_parameter:
    :param source_value:
    :return:
    """
    conditions = []
    elements_map = copy.deepcopy(elements_map)
    if not extra_elements_map:
        extra_elements_map = []

    if isinstance(elements_map, dict):
        cur_parameter = elements_map.get(PARAMETER)
        if cur_parameter and isinstance(cur_parameter, str):
            if elements_map.get(MAP_KEY):
                source_parameter = cur_parameter
                source_value = elements_map.get(VALUE)
            elif target_parameter:
                target_parameter += SEPARATOR + cur_parameter
            else:
                target_parameter = cur_parameter
        new_elements_map = dict()
        for k, v in elements_map.items():
            cur_elements, extra_elements_map, new_conditions = restructure_mapping_facts(
                v, extra_elements_map, target_parameter, source_parameter,
                source_value)
            new_elements_map.update({k: cur_elements})
            conditions.extend(new_conditions)

        if isinstance(new_elements_map.get(PARAMETER, ''), dict):
            separated_target_parameter = target_parameter.split(SEPARATOR)
            target_type = None
            target_short_parameter = None
            for i in range(len(separated_target_parameter)):
                if separated_target_parameter[i] in NODE_TEMPLATE_KEYS:
                    target_type = SEPARATOR.join(
                        separated_target_parameter[:i])
                    target_short_parameter = '_'.join(
                        separated_target_parameter[i:])
                    break
            if not target_short_parameter or not target_type:
                ExceptionCollector.appendException(
                    ToscaParametersMappingFailed(what=target_parameter))

            input_parameter = new_elements_map[PARAMETER]
            input_value = new_elements_map[VALUE]
            input_keyname = new_elements_map.get(KEYNAME)

            provider = separated_target_parameter[0]
            target_relationship_type = SEPARATOR.join(
                [provider, RELATIONSHIPS, "DependsOn"])
            relationship_name = "{self[name]}_server_" + snake_case.convert(
                separated_target_parameter[-1])

            operation_name = 'modify_' + target_short_parameter
            value_name = 'modified_' + target_short_parameter
            interface_name = 'Extra'
            new_elements_map = {
                GET_OPERATION_OUTPUT: [
                    relationship_name, interface_name, operation_name,
                    value_name
                ]
            }

            cur_target_parameter = SEPARATOR.join([
                target_relationship_type, INTERFACES, interface_name,
                operation_name
            ])
            cur_extra_element = {
                PARAMETER: source_parameter,
                MAP_KEY: {
                    PARAMETER: cur_target_parameter,
                    KEYNAME: relationship_name,
                    VALUE: {
                        IMPLEMENTATION: {
                            SOURCE: SET_FACT_SOURCE,
                            VALUE: "default_value",
                            EXECUTOR: ANSIBLE,
                            PARAMETERS: {
                                value_name:
                                "{{{{ {{ input_parameter: input_value }} }}}}"
                                # so many braces because format
                                # uses braces and replace '{{' with '{'
                            }
                        },
                        INPUTS: {
                            "input_parameter": input_parameter,
                            "input_value": input_value
                        }
                    }
                },
                VALUE: source_value
            }
            if input_keyname:
                # TODO add keyname to the parameter outside the new_elements_map
                cur_extra_element[map][KEYNAME] = input_keyname
            extra_elements_map.append(cur_extra_element)

        if_facts_structure = False
        keys = new_elements_map.keys()
        if len(keys) > 0:
            if_facts_structure = True
            for k in FACTS_MAPPING_VALUE_STRUCTURE:
                if k not in keys:
                    if_facts_structure = False
        if if_facts_structure:
            # NOTE: end of recursion
            assert target_parameter

            condition = new_elements_map[CONDITION]
            fact_name = new_elements_map[FACTS]
            value = new_elements_map[VALUE]
            arguments = new_elements_map[ARGUMENTS]
            executor = new_elements_map[EXECUTOR]
            if executor not in CONFIGURATION_TOOLS.keys():
                ExceptionCollector.appendException(
                    UnsupportedExecutorType(what=executor))
            new_elements_map, cur_extra_elements = get_source_structure_from_facts(
                condition, fact_name, value, arguments, executor,
                target_parameter, source_parameter, source_value)
            conditions.append(condition)
            extra_elements_map.extend(cur_extra_elements)

        return new_elements_map, extra_elements_map, conditions

    if isinstance(elements_map, list):
        new_elements_map = []
        for k in elements_map:
            cur_elements, extra_elements_map, new_conditions = restructure_mapping_facts(
                k, extra_elements_map, target_parameter, source_parameter,
                source_value)
            new_elements_map.append(cur_elements)
            conditions.extend(new_conditions)
        return new_elements_map, extra_elements_map, conditions

    return elements_map, extra_elements_map, conditions
def get_source_structure_from_facts(condition, fact_name, value, arguments,
                                    executor, target_parameter,
                                    source_parameter, source_value):
    """

    :param condition:
    :param fact_name:
    :param value:
    :param arguments:
    :param executor
    :param target_parameter:
    :param source_parameter:
    :param source_value:
    :return:
    """

    if isinstance(fact_name, six.string_types):
        fact_name_splitted = fact_name.split(SEPARATOR)
        source_name = fact_name_splitted[0]
        facts_result = "facts_result"
        if len(fact_name_splitted) > 1:
            facts_result += "[\"" + "\"][\"".join(
                fact_name_splitted[1:]) + "\"]"
        facts_result = "{{{{ " + facts_result + " }}}}"
        new_global_elements_map_total_implementation = [{
            SOURCE: source_name,
            VALUE: "facts_result",
            EXECUTOR: executor,
            PARAMETERS: {}
        }, {
            SOURCE: SET_FACT_SOURCE,
            PARAMETERS: {
                "target_objects": facts_result
            },
            VALUE: "tmp_value",
            EXECUTOR: executor
        }]
    else:
        new_global_elements_map_total_implementation = fact_name

    target_parameter_splitted = target_parameter.split(SEPARATOR)
    relationship_name = "{self[name]}_server_" + snake_case.convert(
        target_parameter_splitted[-1])

    provider = target_parameter_splitted[0]
    target_interface_name = "Target"
    target_relationship_type = SEPARATOR.join(
        [provider, RELATIONSHIPS, "DependsOn"])

    target_type = None
    target_short_parameter = None
    for i in range(len(target_parameter_splitted)):
        if target_parameter_splitted[i] in NODE_TEMPLATE_KEYS:
            target_type = SEPARATOR.join(target_parameter_splitted[:i])
            target_short_parameter = '_'.join(target_parameter_splitted[i:])
            break
    if not target_type or not target_short_parameter:
        ExceptionCollector.appendException(
            ToscaParametersMappingFailed(what=target_parameter))

    tag_operation_name = None
    if isinstance(fact_name, six.string_types):
        tag_operation_name = fact_name.replace(SEPARATOR, '_')
    elif isinstance(fact_name, dict):
        for k, v in fact_name:
            tag_operation_name = k.replace(SEPARATOR, '_')
            break
    elif isinstance(fact_name, list):
        if isinstance(fact_name[0], dict):
            for k, v in fact_name[0].items():
                tag_operation_name = k.replace(SEPARATOR, '_')
                break
        else:
            tag_operation_name = str(fact_name[0]).replace(SEPARATOR, '_')
    else:
        tag_operation_name = str(fact_name).replace(SEPARATOR, '_')

    choose_operation_name = "choose_" + tag_operation_name
    total_operation_name = "total_" + tag_operation_name

    target_total_parameter_new = SEPARATOR.join([
        target_relationship_type, INTERFACES, target_interface_name,
        total_operation_name
    ])
    target_choose_parameter_new = SEPARATOR.join([
        target_relationship_type, INTERFACES, target_interface_name,
        choose_operation_name
    ])

    new_elements_map = {
        GET_OPERATION_OUTPUT: [
            relationship_name, target_interface_name, choose_operation_name,
            value
        ]
    }
    new_global_elements_map_total = {
        PARAMETER: target_total_parameter_new,
        KEYNAME: relationship_name,
        VALUE: {
            IMPLEMENTATION: new_global_elements_map_total_implementation
        }
    }
    new_global_elements_map_choose = {
        PARAMETER: target_choose_parameter_new,
        KEYNAME: relationship_name,
        VALUE: {
            IMPLEMENTATION: [
                condition + ".yaml", {
                    SOURCE: SET_FACT_SOURCE,
                    PARAMETERS: {
                        value: "{{{{ matched_object[\"" + value + "\"] }}}}"
                    },
                    VALUE: "tmp_value",
                    EXECUTOR: executor
                }
            ],
            INPUTS: {
                "input_facts": {
                    GET_OPERATION_OUTPUT: [
                        SELF, target_interface_name, total_operation_name,
                        "target_objects"
                    ]
                },
                "input_args": arguments
            }
        }
    }
    new_global_elements_map = [{
        PARAMETER: source_parameter,
        MAP_KEY: new_global_elements_map_choose,
        VALUE: source_value
    }, {
        PARAMETER: source_parameter,
        MAP_KEY: new_global_elements_map_total,
        VALUE: source_value
    }]

    return new_elements_map, new_global_elements_map
def get_resulted_mapping_values(parameter, mapping_value, value, self):
    """
    Manage the case when mapping value has multiple structures in mapping_value
    :param parameter:
    :param mapping_value:
    :param value:
    :return:
    """
    mapping_value = copy.deepcopy(mapping_value)
    if isinstance(mapping_value, six.string_types):
        mapping_value = {PARAMETER: mapping_value, VALUE: "{self[value]}"}
    mapping_value_parameter = mapping_value.get(PARAMETER)
    mapping_value_value = mapping_value.get(VALUE)
    # NOTE at first check if parameter self[buffer] parameter
    if mapping_value_parameter and mapping_value_parameter[:6] == '{self[' and \
            mapping_value_parameter[-1] == '}':
        self[VALUE] = value
        self[PARAMETER] = parameter
        # The case when variable is written to the parameter self!
        # Inside string can be more self parameters
        format_parameter = mapping_value_parameter[6:-2].format(self=self)
        params_parameter = format_parameter.split('][')
        if isinstance(mapping_value_value, dict):
            if mapping_value_value.get(VALUE) and mapping_value_value.get(EXECUTOR) == PYTHON_EXECUTOR and \
                    mapping_value_value.get(SOURCE):
                mapping_value_value = execute_function(
                    PYTHON_SOURCE_DIRECTORY, mapping_value_value[SOURCE],
                    {'self': self})
        iter_value = format_value(mapping_value_value, self)
        iter_num = len(params_parameter)
        for i in range(iter_num - 1, 0, -1):
            temp_param = dict()
            temp_param[params_parameter[i]] = iter_value
            iter_value = temp_param
        self = deep_update_dict(self, {params_parameter[0]: iter_value})
        return []
    elif mapping_value_parameter:
        splitted_mapping_value_parameter = mapping_value_parameter.split(
            SEPARATOR)
        has_section = False
        for v in splitted_mapping_value_parameter:
            if v in NODE_TEMPLATE_KEYS:
                has_section = True
                break
        if not has_section:
            mapping_value_value = mapping_value.get(VALUE)
            if isinstance(mapping_value_value,
                          list) and len(mapping_value_value) > 1:
                r = []
                for v in mapping_value_value:
                    mapping_value[VALUE] = v
                    item = get_resulted_mapping_values(parameter,
                                                       mapping_value, value,
                                                       self)
                    if isinstance(item, list):
                        if len(item) == 1:
                            item = [item]
                    else:
                        item = [item]
                    r.extend(item)
                return r
            if isinstance(mapping_value_value, six.string_types):
                splitted_mapping_value_value = mapping_value_value.split(
                    SEPARATOR)
                for i in range(len(splitted_mapping_value_value)):
                    if splitted_mapping_value_value[i] in NODE_TEMPLATE_KEYS:
                        # parameter_tag = SEPARATOR.join(splitted_mapping_value_value[:i+1])
                        # value_new = SEPARATOR.join(splitted_mapping_value_value[i-1:])
                        # mapping_value[PARAMETER] = mapping_value_parameter + SEPARATOR + parameter_tag
                        # mapping_value[VALUE] = value_new
                        mapping_value[
                            PARAMETER] = mapping_value_parameter + SEPARATOR + mapping_value_value
                        mapping_value[VALUE] = "{self[value]}"
                        return dict(parameter=parameter,
                                    map=mapping_value,
                                    value=value)
            if isinstance(mapping_value_value, dict):
                # NOTE the only valid case when the value is parameter-value structure
                mapping_value_value_parameter = mapping_value_value.get(
                    PARAMETER)
                mapping_value_value_value = mapping_value_value.get(VALUE)
                if mapping_value_value_parameter and mapping_value_value_value:
                    mapping_value_value_keyname = mapping_value_value.get(
                        KEYNAME)
                    if mapping_value_value_keyname:
                        mapping_value[KEYNAME] = mapping_value_value_keyname
                    mapping_value[
                        PARAMETER] = mapping_value_parameter + SEPARATOR + mapping_value_value_parameter
                    mapping_value[VALUE] = mapping_value_value_value
                    r = get_resulted_mapping_values(parameter, mapping_value,
                                                    value, self)
                    return r

            ExceptionCollector.appendException(
                ToscaParametersMappingFailed(what=mapping_value))

    return dict(parameter=parameter, map=mapping_value, value=value)
def get_structure_of_mapped_param(mapped_param,
                                  value,
                                  input_value=None,
                                  indivisible=False,
                                  if_list_type=False):
    if mapped_param is None:
        # NOTE The case when parameter was 'input_value'
        return [], None
    if input_value is None:
        input_value = value

    if isinstance(mapped_param, str):
        splitted_mapped_param = mapped_param.split(SEPARATOR)
        if splitted_mapped_param[-1] in INDIVISIBLE_KEYS:
            indivisible = True
        if not indivisible:
            if isinstance(value, list):
                r = []
                len_v = len(value)
                if len_v == 1:
                    param, _ = get_structure_of_mapped_param(mapped_param,
                                                             value[0],
                                                             input_value,
                                                             if_list_type=True)
                    return param, None

                for v in value:
                    if isinstance(v, str):
                        param, _ = get_structure_of_mapped_param(
                            SEPARATOR.join([mapped_param, v]), input_value,
                            input_value)
                    else:
                        param, _ = get_structure_of_mapped_param(
                            mapped_param, v, input_value)
                    r += param
                return r, None

            if isinstance(value, dict):
                r = dict()
                for k, v in value.items():
                    param, _ = get_structure_of_mapped_param(k, v, input_value)
                    for p in param:
                        r = deep_update_dict(r, p)
                r, _ = get_structure_of_mapped_param(mapped_param,
                                                     r,
                                                     input_value,
                                                     indivisible=True,
                                                     if_list_type=if_list_type)
                return r, None

        # NOTE: end of recursion
        structure = value
        if if_list_type:
            structure = [value]
        for i in range(len(splitted_mapped_param), 0, -1):
            structure = {splitted_mapped_param[i - 1]: structure}
        return [structure], None

    if isinstance(mapped_param, list):
        r = []
        for p in mapped_param:
            param, _ = get_structure_of_mapped_param(p, value, input_value)
            r += param
        return r, None

    if isinstance(mapped_param, dict):
        # NOTE: Assert number of keys! Always start of recursion?
        num_of_keys = len(mapped_param.keys())
        # if mapped_param.get(PARAMETER) and (num_of_keys == 2 or num_of_keys == 3 and KEYNAME in mapped_param.keys()):
        if num_of_keys == 1 or num_of_keys == 2 and KEYNAME in mapped_param.keys(
        ):
            for k, v in mapped_param.items():
                if k != PARAMETER and k != KEYNAME:
                    param, _ = get_structure_of_mapped_param(k, v, input_value)
                    if isinstance(param, tuple):
                        (param, keyname) = param
                        if mapped_param.get(KEYNAME):
                            mapped_param[KEYNAME] = keyname
                    return param, mapped_param.get(KEYNAME)
        else:
            # TODO find the cases
            assert False

    ExceptionCollector.appendException(
        ToscaParametersMappingFailed(what=mapped_param))
def restructure_value(mapping_value, self, if_format_str=True, if_upper=True):
    """
    Recursive function which processes the mapping_value to become parameter:value format
    :param mapping_value: the map of non normative parameter:value
    :param self: the function used to store normative parameter, value and other values
    :param if_format_str:
    :param if_upper: detects if the parameter value must be saved
    :return: dict in the end of recursion, because the first is always (parameter, value) keys
    """
    if isinstance(mapping_value, dict):
        flat_mapping_value = dict()
        for key in MAPPING_VALUE_KEYS:
            mapping_sub_value = mapping_value.get(key)
            if mapping_sub_value is not None:
                restructured_value = restructure_value(mapping_sub_value, self,
                                                       key != PARAMETER, False)
                if restructured_value is None:
                    ExceptionCollector.appendException(
                        ToscaParametersMappingFailed(what=mapping_value))
                    return
                flat_mapping_value[key] = restructured_value

        # NOTE: the case when value has keys ERROR and REASON
        if flat_mapping_value.get(ERROR, False):
            ExceptionCollector.appendException(
                UnsupportedToscaParameterUsage(
                    what=flat_mapping_value.get(REASON).format(self=self)))

        # NOTE: the case when value has keys PARAMETER, VALUE, KEYNAME
        parameter = flat_mapping_value.get(PARAMETER)
        value = flat_mapping_value.get(VALUE)
        keyname = flat_mapping_value.get(KEYNAME)
        if value is None:
            # The case when variable is indivisible
            # This case parameter and keyname are None too or they doesn't have sense
            filled_value = dict()
            for k, v in mapping_value.items():
                filled_k = restructure_value(k, self, if_upper=False)
                filled_v = restructure_value(v, self, if_upper=False)
                filled_value[filled_k] = filled_v
            return filled_value
        if parameter is not None:
            if not isinstance(parameter, six.string_types):
                ExceptionCollector.appendException(
                    ToscaParametersMappingFailed(what=parameter))
            if parameter[:6] == '{self[' and parameter[-1] == '}':
                ExceptionCollector.appendException(
                    ToscaParametersMappingFailed(what=parameter))
            r = dict()
            r[parameter] = value

            if if_upper:
                # r[PARAMETER] = parameter
                if keyname:
                    r[KEYNAME] = keyname
            return r

        # NOTE: the case when value has keys SOURCE, PARAMETERS, EXTRA, VALUE, EXECUTOR
        source_name = flat_mapping_value.get(SOURCE)
        parameters_dict = flat_mapping_value.get(PARAMETERS)
        extra_parameters = flat_mapping_value.get(EXTRA)
        executor_name = flat_mapping_value.get(EXECUTOR)
        if source_name is not None and executor_name is not None:
            if executor_name == PYTHON_EXECUTOR:
                return execute_function(PYTHON_SOURCE_DIRECTORY, source_name,
                                        parameters_dict)
            if not CONFIGURATION_TOOLS.get(executor_name):
                ExceptionCollector.appendException(
                    UnsupportedExecutorType(what=executor_name))
            if self.get(ARTIFACTS) is None:
                self[ARTIFACTS] = []

            tool = CONFIGURATION_TOOLS[executor_name]()
            extension = tool.get_artifact_extension()

            seed(time())
            artifact_name = '_'.join([
                self[NAME], executor_name, source_name,
                str(randint(ARTIFACT_RANGE_START, ARTIFACT_RANGE_END))
            ]) + extension
            flat_mapping_value.update(name=artifact_name,
                                      configuration_tool=executor_name)
            self[ARTIFACTS].append(flat_mapping_value)
            # return the name of artifact
            return artifact_name

        ExceptionCollector.appendException(
            ToscaParametersMappingFailed(what=mapping_value))
        return

    elif isinstance(mapping_value, list):
        return [
            restructure_value(v, self, if_upper=False) for v in mapping_value
        ]

    if isinstance(mapping_value, str) and if_format_str:
        # NOTE: the process is needed because using only format function makes string from json
        mapping_value = format_value(mapping_value, self)
    return mapping_value