Skip to content
Snippets Groups Projects
Commit 022cd29d authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

PathComp component - Front-end:

- Added method generate_neighbor_endpoint_config_rules to compose config rules for neighbor endpoints
parent 538582a7
No related branches found
No related tags found
2 merge requests!235Release TeraFlowSDN 3.0,!199Resolve "(CTTC) Implement Service Handler for L3 services using ACTN SBI driver"
...@@ -15,12 +15,16 @@ ...@@ -15,12 +15,16 @@
import json, logging, requests, uuid import json, logging, requests, uuid
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from common.proto.context_pb2 import ( 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.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 pathcomp.frontend.Config import BACKEND_URL
from .tools.EroPathToHops import eropath_to_hops from .tools.EroPathToHops import eropath_to_hops
from .tools.ComposeConfigRules import ( 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.ComposeRequest import compose_device, compose_link, compose_service
from .tools.ComputeSubServices import ( from .tools.ComputeSubServices import (
convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection)
...@@ -227,12 +231,25 @@ class _Algorithm: ...@@ -227,12 +231,25 @@ class _Algorithm:
continue continue
orig_config_rules = grpc_orig_service.service_config.config_rules 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']: for service_path_ero in response['path']:
self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices']))) 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()} _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))) 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) 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))) self.logger.debug('path_hops = {:s}'.format(str(path_hops)))
try: try:
_device_dict = {k:v[0] for k,v in self.device_dict.items()} _device_dict = {k:v[0] for k,v in self.device_dict.items()}
...@@ -256,7 +273,7 @@ class _Algorithm: ...@@ -256,7 +273,7 @@ class _Algorithm:
if service_key in grpc_services: continue if service_key in grpc_services: continue
grpc_service = self.add_service_to_reply( grpc_service = self.add_service_to_reply(
reply, context_uuid, service_uuid, service_type, path_hops=path_hops, 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 grpc_services[service_key] = grpc_service
for connection in connections: for connection in connections:
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import itertools, json, logging, re import copy, itertools, json, logging, re
from typing import Dict, List, Optional, Tuple from typing import Dict, Iterable, List, Optional, Set, Tuple
from common.proto.context_pb2 import ConfigRule from common.proto.context_pb2 import ConfigRule
from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.grpc.Tools import grpc_message_to_json_string
from common.tools.object_factory.ConfigRule import json_config_rule_set from common.tools.object_factory.ConfigRule import json_config_rule_set
...@@ -23,10 +23,15 @@ LOGGER = logging.getLogger(__name__) ...@@ -23,10 +23,15 @@ LOGGER = logging.getLogger(__name__)
SETTINGS_RULE_NAME = '/settings' SETTINGS_RULE_NAME = '/settings'
STATIC_ROUTING_RULE_NAME = '/static_routing' 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_DEVICE_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/settings')
RE_ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings') RE_ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
RE_ENDPOINT_VLAN_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/vlan\[([^\]]+)\]\/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 = { L2NM_SETTINGS_FIELD_DEFAULTS = {
#'encapsulation_type': 'dot1q', #'encapsulation_type': 'dot1q',
#'vlan_id' : 100, #'vlan_id' : 100,
...@@ -183,4 +188,146 @@ def compose_device_config_rules( ...@@ -183,4 +188,146 @@ def compose_device_config_rules(
else: else:
continue 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') 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment