Loading src/pathcomp/frontend/service/algorithms/_Algorithm.py +20 −3 Original line number Diff line number Diff line Loading @@ -15,12 +15,16 @@ import json, logging, requests, uuid from typing import Dict, List, Optional, Tuple, Union from common.proto.context_pb2 import ( Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum) ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum ) from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.tools.grpc.Tools import grpc_message_list_to_json from pathcomp.frontend.Config import BACKEND_URL from .tools.EroPathToHops import eropath_to_hops from .tools.ComposeConfigRules import ( compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules) compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules, generate_neighbor_endpoint_config_rules ) from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) Loading Loading @@ -227,12 +231,25 @@ class _Algorithm: continue orig_config_rules = grpc_orig_service.service_config.config_rules json_orig_config_rules = grpc_message_list_to_json(orig_config_rules) for service_path_ero in response['path']: self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices']))) _endpoint_to_link_dict = {k:v[0] for k,v in self.endpoint_to_link_dict.items()} self.logger.debug('self.endpoint_to_link_dict = {:s}'.format(str(_endpoint_to_link_dict))) path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) json_generated_config_rules = generate_neighbor_endpoint_config_rules( json_orig_config_rules, path_hops, self.device_name_mapping, self.endpoint_name_mapping ) json_extended_config_rules = list() json_extended_config_rules.extend(json_orig_config_rules) json_extended_config_rules.extend(json_generated_config_rules) extended_config_rules = [ ConfigRule(**json_extended_config_rule) for json_extended_config_rule in json_extended_config_rules ] self.logger.debug('path_hops = {:s}'.format(str(path_hops))) try: _device_dict = {k:v[0] for k,v in self.device_dict.items()} Loading @@ -256,7 +273,7 @@ class _Algorithm: if service_key in grpc_services: continue grpc_service = self.add_service_to_reply( reply, context_uuid, service_uuid, service_type, path_hops=path_hops, config_rules=orig_config_rules) config_rules=extended_config_rules) grpc_services[service_key] = grpc_service for connection in connections: Loading src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +149 −2 Original line number Diff line number Diff line Loading @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. import itertools, json, logging, re from typing import Dict, List, Optional, Tuple import copy, itertools, json, logging, re from typing import Dict, Iterable, List, Optional, Set, Tuple from common.proto.context_pb2 import ConfigRule from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.ConfigRule import json_config_rule_set Loading @@ -23,10 +23,15 @@ LOGGER = logging.getLogger(__name__) SETTINGS_RULE_NAME = '/settings' STATIC_ROUTING_RULE_NAME = '/static_routing' RE_UUID = re.compile(r'([0-9a-fA-F]{8})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{12})') RE_DEVICE_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/settings') RE_ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings') RE_ENDPOINT_VLAN_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/vlan\[([^\]]+)\]\/settings') TMPL_ENDPOINT_SETTINGS = '/device[{:s}]/endpoint[{:s}]/settings' TMPL_ENDPOINT_VLAN_SETTINGS = '/device[{:s}]/endpoint[{:s}]/vlan[{:s}]/settings' L2NM_SETTINGS_FIELD_DEFAULTS = { #'encapsulation_type': 'dot1q', #'vlan_id' : 100, Loading Loading @@ -183,4 +188,146 @@ def compose_device_config_rules( else: continue for config_rule in subservice_config_rules: LOGGER.debug('[compose_device_config_rules] result config_rule: {:s}'.format( grpc_message_to_json_string(config_rule))) LOGGER.debug('[compose_device_config_rules] end') def pairwise(iterable : Iterable) -> Tuple[Iterable, Iterable]: # TODO: To be replaced by itertools.pairwise() when we move to Python 3.10 # Python 3.10 introduced method itertools.pairwise() # Standalone method extracted from: # - https://docs.python.org/3/library/itertools.html#itertools.pairwise a, b = itertools.tee(iterable, 2) next(b, None) return zip(a, b) def compute_device_keys( device_uuid_or_name : str, device_name_mapping : Dict[str, str] ) -> Set[str]: LOGGER.debug('[compute_device_keys] begin') LOGGER.debug('[compute_device_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name))) #LOGGER.debug('[compute_device_keys] device_name_mapping={:s}'.format(str(device_name_mapping))) device_keys = {device_uuid_or_name} for k,v in device_name_mapping.items(): if device_uuid_or_name not in {k, v}: continue device_keys.add(k) device_keys.add(v) LOGGER.debug('[compute_device_keys] device_keys={:s}'.format(str(device_keys))) LOGGER.debug('[compute_device_keys] end') return device_keys def compute_endpoint_keys( device_keys : Set[str], endpoint_uuid_or_name : str, endpoint_name_mapping : Dict[str, str] ) -> Set[str]: LOGGER.debug('[compute_endpoint_keys] begin') LOGGER.debug('[compute_endpoint_keys] device_keys={:s}'.format(str(device_keys))) LOGGER.debug('[compute_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name))) #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) endpoint_keys = {endpoint_uuid_or_name} for k,v in endpoint_name_mapping.items(): if (k[0] in device_keys or v in device_keys) and (endpoint_uuid_or_name in {k[1], v}): endpoint_keys.add(k[1]) endpoint_keys.add(v) LOGGER.debug('[compute_endpoint_keys] endpoint_keys={:s}'.format(str(endpoint_keys))) LOGGER.debug('[compute_endpoint_keys] end') return endpoint_keys def compute_device_endpoint_keys( device_uuid_or_name : str, endpoint_uuid_or_name : str, device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] ) -> Set[Tuple[str, str]]: LOGGER.debug('[compute_device_endpoint_keys] begin') LOGGER.debug('[compute_device_endpoint_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name))) LOGGER.debug('[compute_device_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name))) #LOGGER.debug('[compute_device_endpoint_keys] device_name_mapping={:s}'.format(str(device_name_mapping))) #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) device_keys = compute_device_keys(device_uuid_or_name, device_name_mapping) endpoint_keys = compute_endpoint_keys(device_keys, endpoint_uuid_or_name, endpoint_name_mapping) device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys)) LOGGER.debug('[compute_device_endpoint_keys] device_endpoint_keys={:s}'.format(str(device_endpoint_keys))) LOGGER.debug('[compute_device_endpoint_keys] end') return device_endpoint_keys def generate_neighbor_endpoint_config_rules( config_rules : List[Dict], path_hops : List[Dict], device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] ) -> List[Dict]: LOGGER.debug('[generate_neighbor_endpoint_config_rules] begin') LOGGER.debug('[generate_neighbor_endpoint_config_rules] config_rules={:s}'.format(str(config_rules))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] path_hops={:s}'.format(str(path_hops))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] device_name_mapping={:s}'.format(str(device_name_mapping))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) generated_config_rules = list() for link_endpoint_a, link_endpoint_b in pairwise(path_hops): LOGGER.debug('[generate_neighbor_endpoint_config_rules] loop begin') LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_a={:s}'.format(str(link_endpoint_a))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_b={:s}'.format(str(link_endpoint_b))) device_endpoint_keys_a = compute_device_endpoint_keys( link_endpoint_a['device'], link_endpoint_a['egress_ep'], device_name_mapping, endpoint_name_mapping ) device_endpoint_keys_b = compute_device_endpoint_keys( link_endpoint_b['device'], link_endpoint_b['ingress_ep'], device_name_mapping, endpoint_name_mapping ) for config_rule in config_rules: # Only applicable, by now, to Custom Config Rules for endpoint settings if 'custom' not in config_rule: continue match = RE_ENDPOINT_SETTINGS.match(config_rule['custom']['resource_key']) if match is None: match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule['custom']['resource_key']) if match is None: continue resource_key_values = match.groups() if resource_key_values[0:2] in device_endpoint_keys_a: resource_key_values = list(resource_key_values) resource_key_values[0] = link_endpoint_b['device'] resource_key_values[1] = link_endpoint_b['ingress_ep'] elif resource_key_values[0:2] in device_endpoint_keys_b: resource_key_values = list(resource_key_values) resource_key_values[0] = link_endpoint_a['device'] resource_key_values[1] = link_endpoint_a['egress_ep'] else: continue device_keys = compute_device_keys(resource_key_values[0], device_name_mapping) device_names = {device_key for device_key in device_keys if RE_UUID.match(device_key) is None} if len(device_names) != 1: MSG = 'Unable to identify name for Device({:s}): device_keys({:s})' raise Exception(MSG.format(str(resource_key_values[0]), str(device_keys))) resource_key_values[0] = device_names.pop() endpoint_keys = compute_endpoint_keys(device_keys, resource_key_values[1], endpoint_name_mapping) endpoint_names = {endpoint_key for endpoint_key in endpoint_keys if RE_UUID.match(endpoint_key) is None} if len(endpoint_names) != 1: MSG = 'Unable to identify name for Endpoint({:s}): endpoint_keys({:s})' raise Exception(MSG.format(str(resource_key_values[1]), str(endpoint_keys))) resource_key_values[1] = endpoint_names.pop() resource_value : Dict = json.loads(config_rule['custom']['resource_value']) if 'neighbor_address' not in resource_value: continue resource_value['ip_address'] = resource_value.pop('neighbor_address') # remove neighbor_address also from original rule as it is already consumed resource_key_template = TMPL_ENDPOINT_VLAN_SETTINGS if len(match.groups()) == 3 else TMPL_ENDPOINT_SETTINGS generated_config_rule = copy.deepcopy(config_rule) generated_config_rule['custom']['resource_key'] = resource_key_template.format(*resource_key_values) generated_config_rule['custom']['resource_value'] = json.dumps(resource_value) generated_config_rules.append(generated_config_rule) LOGGER.debug('[generate_neighbor_endpoint_config_rules] generated_config_rules={:s}'.format(str(generated_config_rules))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] end') return generated_config_rules Loading
src/pathcomp/frontend/service/algorithms/_Algorithm.py +20 −3 Original line number Diff line number Diff line Loading @@ -15,12 +15,16 @@ import json, logging, requests, uuid from typing import Dict, List, Optional, Tuple, Union from common.proto.context_pb2 import ( Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum) ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum ) from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.tools.grpc.Tools import grpc_message_list_to_json from pathcomp.frontend.Config import BACKEND_URL from .tools.EroPathToHops import eropath_to_hops from .tools.ComposeConfigRules import ( compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules) compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules, generate_neighbor_endpoint_config_rules ) from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) Loading Loading @@ -227,12 +231,25 @@ class _Algorithm: continue orig_config_rules = grpc_orig_service.service_config.config_rules json_orig_config_rules = grpc_message_list_to_json(orig_config_rules) for service_path_ero in response['path']: self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices']))) _endpoint_to_link_dict = {k:v[0] for k,v in self.endpoint_to_link_dict.items()} self.logger.debug('self.endpoint_to_link_dict = {:s}'.format(str(_endpoint_to_link_dict))) path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) json_generated_config_rules = generate_neighbor_endpoint_config_rules( json_orig_config_rules, path_hops, self.device_name_mapping, self.endpoint_name_mapping ) json_extended_config_rules = list() json_extended_config_rules.extend(json_orig_config_rules) json_extended_config_rules.extend(json_generated_config_rules) extended_config_rules = [ ConfigRule(**json_extended_config_rule) for json_extended_config_rule in json_extended_config_rules ] self.logger.debug('path_hops = {:s}'.format(str(path_hops))) try: _device_dict = {k:v[0] for k,v in self.device_dict.items()} Loading @@ -256,7 +273,7 @@ class _Algorithm: if service_key in grpc_services: continue grpc_service = self.add_service_to_reply( reply, context_uuid, service_uuid, service_type, path_hops=path_hops, config_rules=orig_config_rules) config_rules=extended_config_rules) grpc_services[service_key] = grpc_service for connection in connections: Loading
src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +149 −2 Original line number Diff line number Diff line Loading @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. import itertools, json, logging, re from typing import Dict, List, Optional, Tuple import copy, itertools, json, logging, re from typing import Dict, Iterable, List, Optional, Set, Tuple from common.proto.context_pb2 import ConfigRule from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.ConfigRule import json_config_rule_set Loading @@ -23,10 +23,15 @@ LOGGER = logging.getLogger(__name__) SETTINGS_RULE_NAME = '/settings' STATIC_ROUTING_RULE_NAME = '/static_routing' RE_UUID = re.compile(r'([0-9a-fA-F]{8})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{12})') RE_DEVICE_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/settings') RE_ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings') RE_ENDPOINT_VLAN_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/vlan\[([^\]]+)\]\/settings') TMPL_ENDPOINT_SETTINGS = '/device[{:s}]/endpoint[{:s}]/settings' TMPL_ENDPOINT_VLAN_SETTINGS = '/device[{:s}]/endpoint[{:s}]/vlan[{:s}]/settings' L2NM_SETTINGS_FIELD_DEFAULTS = { #'encapsulation_type': 'dot1q', #'vlan_id' : 100, Loading Loading @@ -183,4 +188,146 @@ def compose_device_config_rules( else: continue for config_rule in subservice_config_rules: LOGGER.debug('[compose_device_config_rules] result config_rule: {:s}'.format( grpc_message_to_json_string(config_rule))) LOGGER.debug('[compose_device_config_rules] end') def pairwise(iterable : Iterable) -> Tuple[Iterable, Iterable]: # TODO: To be replaced by itertools.pairwise() when we move to Python 3.10 # Python 3.10 introduced method itertools.pairwise() # Standalone method extracted from: # - https://docs.python.org/3/library/itertools.html#itertools.pairwise a, b = itertools.tee(iterable, 2) next(b, None) return zip(a, b) def compute_device_keys( device_uuid_or_name : str, device_name_mapping : Dict[str, str] ) -> Set[str]: LOGGER.debug('[compute_device_keys] begin') LOGGER.debug('[compute_device_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name))) #LOGGER.debug('[compute_device_keys] device_name_mapping={:s}'.format(str(device_name_mapping))) device_keys = {device_uuid_or_name} for k,v in device_name_mapping.items(): if device_uuid_or_name not in {k, v}: continue device_keys.add(k) device_keys.add(v) LOGGER.debug('[compute_device_keys] device_keys={:s}'.format(str(device_keys))) LOGGER.debug('[compute_device_keys] end') return device_keys def compute_endpoint_keys( device_keys : Set[str], endpoint_uuid_or_name : str, endpoint_name_mapping : Dict[str, str] ) -> Set[str]: LOGGER.debug('[compute_endpoint_keys] begin') LOGGER.debug('[compute_endpoint_keys] device_keys={:s}'.format(str(device_keys))) LOGGER.debug('[compute_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name))) #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) endpoint_keys = {endpoint_uuid_or_name} for k,v in endpoint_name_mapping.items(): if (k[0] in device_keys or v in device_keys) and (endpoint_uuid_or_name in {k[1], v}): endpoint_keys.add(k[1]) endpoint_keys.add(v) LOGGER.debug('[compute_endpoint_keys] endpoint_keys={:s}'.format(str(endpoint_keys))) LOGGER.debug('[compute_endpoint_keys] end') return endpoint_keys def compute_device_endpoint_keys( device_uuid_or_name : str, endpoint_uuid_or_name : str, device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] ) -> Set[Tuple[str, str]]: LOGGER.debug('[compute_device_endpoint_keys] begin') LOGGER.debug('[compute_device_endpoint_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name))) LOGGER.debug('[compute_device_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name))) #LOGGER.debug('[compute_device_endpoint_keys] device_name_mapping={:s}'.format(str(device_name_mapping))) #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) device_keys = compute_device_keys(device_uuid_or_name, device_name_mapping) endpoint_keys = compute_endpoint_keys(device_keys, endpoint_uuid_or_name, endpoint_name_mapping) device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys)) LOGGER.debug('[compute_device_endpoint_keys] device_endpoint_keys={:s}'.format(str(device_endpoint_keys))) LOGGER.debug('[compute_device_endpoint_keys] end') return device_endpoint_keys def generate_neighbor_endpoint_config_rules( config_rules : List[Dict], path_hops : List[Dict], device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] ) -> List[Dict]: LOGGER.debug('[generate_neighbor_endpoint_config_rules] begin') LOGGER.debug('[generate_neighbor_endpoint_config_rules] config_rules={:s}'.format(str(config_rules))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] path_hops={:s}'.format(str(path_hops))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] device_name_mapping={:s}'.format(str(device_name_mapping))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping))) generated_config_rules = list() for link_endpoint_a, link_endpoint_b in pairwise(path_hops): LOGGER.debug('[generate_neighbor_endpoint_config_rules] loop begin') LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_a={:s}'.format(str(link_endpoint_a))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_b={:s}'.format(str(link_endpoint_b))) device_endpoint_keys_a = compute_device_endpoint_keys( link_endpoint_a['device'], link_endpoint_a['egress_ep'], device_name_mapping, endpoint_name_mapping ) device_endpoint_keys_b = compute_device_endpoint_keys( link_endpoint_b['device'], link_endpoint_b['ingress_ep'], device_name_mapping, endpoint_name_mapping ) for config_rule in config_rules: # Only applicable, by now, to Custom Config Rules for endpoint settings if 'custom' not in config_rule: continue match = RE_ENDPOINT_SETTINGS.match(config_rule['custom']['resource_key']) if match is None: match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule['custom']['resource_key']) if match is None: continue resource_key_values = match.groups() if resource_key_values[0:2] in device_endpoint_keys_a: resource_key_values = list(resource_key_values) resource_key_values[0] = link_endpoint_b['device'] resource_key_values[1] = link_endpoint_b['ingress_ep'] elif resource_key_values[0:2] in device_endpoint_keys_b: resource_key_values = list(resource_key_values) resource_key_values[0] = link_endpoint_a['device'] resource_key_values[1] = link_endpoint_a['egress_ep'] else: continue device_keys = compute_device_keys(resource_key_values[0], device_name_mapping) device_names = {device_key for device_key in device_keys if RE_UUID.match(device_key) is None} if len(device_names) != 1: MSG = 'Unable to identify name for Device({:s}): device_keys({:s})' raise Exception(MSG.format(str(resource_key_values[0]), str(device_keys))) resource_key_values[0] = device_names.pop() endpoint_keys = compute_endpoint_keys(device_keys, resource_key_values[1], endpoint_name_mapping) endpoint_names = {endpoint_key for endpoint_key in endpoint_keys if RE_UUID.match(endpoint_key) is None} if len(endpoint_names) != 1: MSG = 'Unable to identify name for Endpoint({:s}): endpoint_keys({:s})' raise Exception(MSG.format(str(resource_key_values[1]), str(endpoint_keys))) resource_key_values[1] = endpoint_names.pop() resource_value : Dict = json.loads(config_rule['custom']['resource_value']) if 'neighbor_address' not in resource_value: continue resource_value['ip_address'] = resource_value.pop('neighbor_address') # remove neighbor_address also from original rule as it is already consumed resource_key_template = TMPL_ENDPOINT_VLAN_SETTINGS if len(match.groups()) == 3 else TMPL_ENDPOINT_SETTINGS generated_config_rule = copy.deepcopy(config_rule) generated_config_rule['custom']['resource_key'] = resource_key_template.format(*resource_key_values) generated_config_rule['custom']['resource_value'] = json.dumps(resource_value) generated_config_rules.append(generated_config_rule) LOGGER.debug('[generate_neighbor_endpoint_config_rules] generated_config_rules={:s}'.format(str(generated_config_rules))) LOGGER.debug('[generate_neighbor_endpoint_config_rules] end') return generated_config_rules