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

Service component - L3NM gNMI OpenConfig Service Handler:

- ConfigRuleComposer: Added ignore of management interfaces
- ConfigRuleComposer: Corrected endpoint attribute names
- ConfigRuleComposer: Added alias mappings from device/endpoint name to their UUID to prevent split-brain conditions
- L3NMGnmiOpenConfigServiceHandler: implemented correct path data loading
parent d41a3bc7
No related branches found
No related tags found
2 merge requests!294Release TeraFlowSDN 4.0,!172Resolve "(CTTC) Extend gNMI-OpenConfig SBI driver"
......@@ -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]]:
......
......@@ -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)
......
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