diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py index 42747a1ae7e51eb9f365040d39728651d414c019..343c37f5d75684394410f907c4ff57446cf9782c 100644 --- a/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py +++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py @@ -12,17 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, netaddr, re +import json, logging, netaddr, re from typing import Dict, List, Optional, Set, Tuple from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ConfigActionEnum, Device, EndPoint, Service from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set from service.service.service_handler_api.AnyTreeTools import TreeNode +LOGGER = logging.getLogger(__name__) + NETWORK_INSTANCE = 'teraflowsdn' -RE_IF = re.compile(r'^\/interface\[([^\]]+)\]\/subinterface\[([^\]]+)\]$') -RE_SR = re.compile(r'^\/network_instance\[([^\]]+)\]\/protocols\[STATIC\]/route\[([^\:]+)\:([^\]]+)\]$') +RE_IF = re.compile(r'^\/interface\[([^\]]+)\]$') +RE_SUBIF = re.compile(r'^\/interface\[([^\]]+)\]\/subinterface\[([^\]]+)\]$') +RE_SR = re.compile(r'^\/network_instance\[([^\]]+)\]\/protocols\[STATIC\]/route\[([^\:]+)\:([^\]]+)\]$') def _interface( interface : str, if_type : Optional[str] = 'l3ipvlan', index : int = 0, vlan_id : Optional[int] = None, @@ -82,9 +85,9 @@ class EndpointComposer: self.objekt = endpoint_obj if settings is None: return json_settings : Dict = settings.value - self.ipv4_address = json_settings['ipv4_address'] - self.ipv4_prefix_len = json_settings['ipv4_prefix_len'] - self.sub_interface_index = json_settings['sub_interface_index'] + self.ipv4_address = json_settings['address_ip'] + self.ipv4_prefix_len = json_settings['address_prefix'] + self.sub_interface_index = json_settings.get('index', 0) def get_config_rules(self, network_instance_name : str, delete : bool = False) -> List[Dict]: if self.ipv4_address is None: return [] @@ -102,20 +105,25 @@ class EndpointComposer: def dump(self) -> Dict: return { - 'sub_interface_index' : self.sub_interface_index, - 'ipv4_address' : self.ipv4_address, - 'ipv4_prefix_len' : self.ipv4_prefix_len, + 'index' : self.sub_interface_index, + 'address_ip' : self.ipv4_address, + 'address_prefix': self.ipv4_prefix_len, } class DeviceComposer: def __init__(self, device_uuid : str) -> None: self.uuid = device_uuid self.objekt : Optional[Device] = None - self.endpoints : Dict[str, EndpointComposer] = dict() + self.aliases : Dict[str, str] = dict() # endpoint_name => endpoint_uuid + self.endpoints : Dict[str, EndpointComposer] = dict() # endpoint_uuid => EndpointComposer self.connected : Set[str] = set() self.static_routes : Dict[str, Dict[int, str]] = dict() # {prefix => {metric => next_hop}} - + + def set_endpoint_alias(self, endpoint_name : str, endpoint_uuid : str) -> None: + self.aliases[endpoint_name] = endpoint_uuid + def get_endpoint(self, endpoint_uuid : str) -> EndpointComposer: + endpoint_uuid = self.aliases.get(endpoint_uuid, endpoint_uuid) if endpoint_uuid not in self.endpoints: self.endpoints[endpoint_uuid] = EndpointComposer(endpoint_uuid) return self.endpoints[endpoint_uuid] @@ -123,17 +131,36 @@ class DeviceComposer: def configure(self, device_obj : Device, settings : Optional[TreeNode]) -> None: self.objekt = device_obj for endpoint_obj in device_obj.device_endpoints: + endpoint_uuid = endpoint_obj.endpoint_id.endpoint_uuid.uuid + self.set_endpoint_alias(endpoint_obj.name, endpoint_uuid) self.get_endpoint(endpoint_obj.name).configure(endpoint_obj, None) + # Find management interfaces + mgmt_ifaces = set() for config_rule in device_obj.device_config.config_rules: if config_rule.action != ConfigActionEnum.CONFIGACTION_SET: continue if config_rule.WhichOneof('config_rule') != 'custom': continue config_rule_custom = config_rule.custom - match = RE_IF.match(config_rule_custom.resource_key) + if match is None: continue + if_name = match.groups()[0] + resource_value = json.loads(config_rule_custom.resource_value) + management = resource_value.get('management', False) + if management: mgmt_ifaces.add(if_name) + + # Find data plane interfaces + for config_rule in device_obj.device_config.config_rules: + if config_rule.action != ConfigActionEnum.CONFIGACTION_SET: continue + if config_rule.WhichOneof('config_rule') != 'custom': continue + config_rule_custom = config_rule.custom + + match = RE_SUBIF.match(config_rule_custom.resource_key) if match is not None: if_name, subif_index = match.groups() + if if_name in mgmt_ifaces: continue resource_value = json.loads(config_rule_custom.resource_value) + if 'address_ip' not in resource_value: continue + if 'address_prefix' not in resource_value: continue ipv4_network = str(resource_value['address_ip']) ipv4_prefix_len = int(resource_value['address_prefix']) endpoint = self.get_endpoint(if_name) @@ -197,19 +224,24 @@ class DeviceComposer: class ConfigRuleComposer: def __init__(self) -> None: self.objekt : Optional[Service] = None - self.devices : Dict[str, DeviceComposer] = dict() + self.aliases : Dict[str, str] = dict() # device_name => device_uuid + self.devices : Dict[str, DeviceComposer] = dict() # device_uuid => DeviceComposer - def configure(self, service_obj : Service, settings : Optional[TreeNode]) -> None: - self.objekt = service_obj - if settings is None: return - #json_settings : Dict = settings.value - # For future use + def set_device_alias(self, device_name : str, device_uuid : str) -> None: + self.aliases[device_name] = device_uuid def get_device(self, device_uuid : str) -> DeviceComposer: + device_uuid = self.aliases.get(device_uuid, device_uuid) if device_uuid not in self.devices: self.devices[device_uuid] = DeviceComposer(device_uuid) return self.devices[device_uuid] + def configure(self, service_obj : Service, settings : Optional[TreeNode]) -> None: + self.objekt = service_obj + if settings is None: return + #json_settings : Dict = settings.value + # For future use + def get_config_rules( self, network_instance_name : str = NETWORK_INSTANCE, delete : bool = False ) -> Dict[str, List[Dict]]: diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py index 9142c9d1e32c698aef8cc1c461d6212a64b303dc..88bb5655b872dfce90988686e7d8bc242d866bf0 100644 --- a/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/L3NMGnmiOpenConfigServiceHandler.py @@ -15,13 +15,15 @@ import json, logging from typing import Any, Dict, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method -from common.proto.context_pb2 import ConfigRule, DeviceId, Service +from common.proto.context_pb2 import ConfigRule, ConnectionId, DeviceId, Service +from common.tools.object_factory.Connection import json_connection_id from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching from service.service.task_scheduler.TaskExecutor import TaskExecutor +from service.service.tools.EndpointIdFormatters import endpointids_to_raw from .ConfigRuleComposer import ConfigRuleComposer from .StaticRouteGenerator import StaticRouteGenerator @@ -51,17 +53,20 @@ class L3NMGnmiOpenConfigServiceHandler(_ServiceHandler): device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) device_settings = self.__settings_handler.get_device_settings(device_obj) + self.__config_rule_composer.set_device_alias(device_obj.name, device_uuid) _device = self.__config_rule_composer.get_device(device_obj.name) _device.configure(device_obj, device_settings) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) + _device.set_endpoint_alias(endpoint_obj.name, endpoint_uuid) _endpoint = _device.get_endpoint(endpoint_obj.name) _endpoint.configure(endpoint_obj, endpoint_settings) self.__endpoint_map[(device_uuid, endpoint_uuid)] = (device_obj.name, endpoint_obj.name) self.__static_route_generator.compose(endpoints) + LOGGER.debug('config_rule_composer = {:s}'.format(json.dumps(self.__config_rule_composer.dump()))) def _do_configurations( self, config_rules_per_device : Dict[str, List[Dict]], endpoints : List[Tuple[str, str, Optional[str]]], @@ -99,7 +104,9 @@ class L3NMGnmiOpenConfigServiceHandler(_ServiceHandler): chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] #service_uuid = self.__service.service_id.service_uuid.uuid - self._compose_config_rules(endpoints) + connection = self.__task_executor.get_connection(ConnectionId(**json_connection_id(connection_uuid))) + connection_endpoint_ids = endpointids_to_raw(connection.path_hops_endpoint_ids) + self._compose_config_rules(connection_endpoint_ids) #network_instance_name = service_uuid.split('-')[0] #config_rules_per_device = self.__config_rule_composer.get_config_rules(network_instance_name, delete=False) config_rules_per_device = self.__config_rule_composer.get_config_rules(delete=False) @@ -115,7 +122,9 @@ class L3NMGnmiOpenConfigServiceHandler(_ServiceHandler): chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] #service_uuid = self.__service.service_id.service_uuid.uuid - self._compose_config_rules(endpoints) + connection = self.__task_executor.get_connection(ConnectionId(**json_connection_id(connection_uuid))) + connection_endpoint_ids = endpointids_to_raw(connection.path_hops_endpoint_ids) + self._compose_config_rules(connection_endpoint_ids) #network_instance_name = service_uuid.split('-')[0] #config_rules_per_device = self.__config_rule_composer.get_config_rules(network_instance_name, delete=True) config_rules_per_device = self.__config_rule_composer.get_config_rules(delete=True)