Loading src/service/service/service_handlers/l3nm_restconf/ConfigRules.py 0 → 100644 +316 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from typing import Any, Dict, List, Optional, Tuple 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__) def get_value(field_name : str, *containers, default=None) -> Optional[Any]: if len(containers) == 0: raise Exception('No containers specified') for container in containers: if field_name not in container: continue return container[field_name] return default def setup_config_rules( service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple] ) -> List[Dict]: LOGGER.info(f"service_settings : {service_settings}") LOGGER.info(f"endpoint_settings : {endpoint_settings}") if service_settings is None: return [] if device_settings is None: device_settings = TreeNode('settings') device_settings.value = {} if endpoint_settings is None: return [] json_settings : Dict = service_settings.value json_device_settings : Dict = device_settings.value json_endpoint_settings : Dict = endpoint_settings.value settings = (json_settings, json_endpoint_settings, json_device_settings) mtu = get_value('mtu', *settings, default=1450) # 1512 #address_families = json_settings.get('address_families', [] ) # ['IPV4'] bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' route_distinguisher = json_settings.get('route_distinguisher', '65000:101' ) # '60001:801' sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 #network_interface_desc = '{:s}-NetIf'.format(service_uuid) network_interface_desc = json_endpoint_settings.get('ni_description','') #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) network_subinterface_desc = json_endpoint_settings.get('subif_description','') #service_short_uuid = service_uuid.split('-')[-1] #network_instance_name = '{:s}-NetInst'.format(service_short_uuid) network_instance_name = json_endpoint_settings.get('ni_name', service_uuid.split('-')[-1]) #ELAN-AC:1 mtu = int(mtu) bgp_as = int(bgp_as) sub_interface_index = int(sub_interface_index) vlan_id = int(vlan_id) address_prefix = int(address_prefix) if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) json_config_rules = [ # Configure Interface (not used) #json_config_rule_set( # '/interface[{:s}]'.format(endpoint_name), { # 'name': endpoint_name, # 'description': network_interface_desc, # 'mtu': mtu, #}), #Create network instance json_config_rule_set( '/network_instance[{:s}]'.format(network_instance_name), { 'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF', 'route_distinguisher': route_distinguisher, #'router_id': router_id, #'address_families': address_families, }), #Add BGP protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { 'name': network_instance_name, 'protocol_name': 'BGP', 'identifier': 'BGP', 'type': 'L3VRF', 'as': bgp_as, 'router_id': router_id, }), #Add DIRECTLY CONNECTED protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), { 'name': network_instance_name, 'identifier': 'DIRECTLY_CONNECTED', 'protocol_name': 'DIRECTLY_CONNECTED', }), #Add STATIC protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), { 'name': network_instance_name, 'identifier': 'STATIC', 'protocol_name': 'STATIC', }), #Create interface with subinterface json_config_rule_set( '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), { 'name' : if_subif_name, 'type' :'l3ipvlan', 'mtu' : mtu, 'index' : sub_interface_index, 'description' : network_subinterface_desc, 'vlan_id' : vlan_id, 'address_ip' : address_ip, 'address_prefix': address_prefix, }), #Associate interface to network instance json_config_rule_set( '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { 'name' : network_instance_name, 'type' : 'L3VRF', 'id' : if_subif_name, 'interface' : if_subif_name, 'subinterface': sub_interface_index, }), # TODO Validate need #Create routing policy # json_config_rule_set( # '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # 'ext_community_member' : route_distinguisher, # }), # json_config_rule_set( # # pylint: disable=duplicate-string-formatting-argument # '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), { # 'policy_name' : policy_import, # 'statement_name' : 'stm_{:s}'.format(policy_import), # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # 'policy_result' : 'ACCEPT_ROUTE', # }), #Associate routing policy to network instance # json_config_rule_set( # '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), { # 'name' : network_instance_name, # 'import_policy': policy_import, # }), # TODO Validate need #Create routing policy # json_config_rule_set( # '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # 'ext_community_member' : route_distinguisher, # }), # json_config_rule_set( # # pylint: disable=duplicate-string-formatting-argument # '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), { # 'policy_name' : policy_export, # 'statement_name' : 'stm_{:s}'.format(policy_export), # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # 'policy_result' : 'ACCEPT_ROUTE', # }), # #Associate routing policy to network instance # json_config_rule_set( # '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{ # 'name' : network_instance_name, # 'export_policy': policy_export, # }), #Create table connections # json_config_rule_set( # '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'DIRECTLY_CONNECTED', # 'dst_protocol' : 'BGP', # 'address_family' : 'IPV4', # 'default_import_policy': 'ACCEPT_ROUTE', # }), # json_config_rule_set( # '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'STATIC', # 'dst_protocol' : 'BGP', # 'address_family' : 'IPV4', # 'default_import_policy': 'ACCEPT_ROUTE', # }), ] for res_key, res_value in endpoint_acls: json_config_rules.append( {'action': 1, 'acl': res_value} ) return json_config_rules def teardown_config_rules( service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode ) -> List[Dict]: if service_settings is None: return [] if device_settings is None: return [] if endpoint_settings is None: return [] json_settings : Dict = service_settings.value json_device_settings : Dict = device_settings.value json_endpoint_settings : Dict = endpoint_settings.value settings = (json_settings, json_endpoint_settings, json_device_settings) service_short_uuid = service_uuid.split('-')[-1] network_instance_name = '{:s}-NetInst'.format(service_short_uuid) #network_interface_desc = '{:s}-NetIf'.format(service_uuid) #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) #mtu = get_value('mtu', *settings, default=1450) # 1512 #address_families = json_settings.get('address_families', [] ) # ['IPV4'] #bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 route_distinguisher = json_settings.get('route_distinguisher', '0:0' ) # '60001:801' #sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 #address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' #address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) json_config_rules = [ #Delete table connections # json_config_rule_delete( # '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name),{ # 'name' : network_instance_name, # 'src_protocol' : 'DIRECTLY_CONNECTED', # 'dst_protocol' : 'BGP', # 'address_family': 'IPV4', # }), # json_config_rule_delete( # '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'STATIC', # 'dst_protocol' : 'BGP', # 'address_family': 'IPV4', # }), # TODO Validate need #Delete export routing policy # json_config_rule_delete( # '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { # 'policy_name': '{:s}_export'.format(network_instance_name), # }), # json_config_rule_delete( # '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # }), # #Delete import routing policy # json_config_rule_delete( # '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { # 'policy_name': '{:s}_import'.format(network_instance_name), # }), # json_config_rule_delete( # '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # }), #Delete interface; automatically deletes: # - /interface[]/subinterface[] json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name), { 'name': if_subif_name, }), #Delete network instance; automatically deletes: # - /network_instance[]/interface[] # - /network_instance[]/protocols[] # - /network_instance[]/inter_instance_policies[] json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name), { 'name': network_instance_name }), ] return json_config_rules src/service/service/service_handlers/l3nm_restconf/L3NMRestconfServiceHandler.py 0 → 100644 +166 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import json, logging from typing import Any, 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.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_restconf'}) class L3NMRestconfServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings ) -> None: self.__service = service self.__task_executor = task_executor self.__settings_handler = SettingsHandler(service.service_config, **settings) @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') results = [] for endpoint in endpoints: try: device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) device_settings = self.__settings_handler.get_device_settings(device_obj) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) endpoint_acls = self.__settings_handler.get_endpoint_acls(device_obj, endpoint_obj) endpoint_name = endpoint_obj.name json_config_rules = setup_config_rules( service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, settings, device_settings, endpoint_settings, endpoint_acls) LOGGER.info(f"json_config_rules : {json_config_rules}") if len(json_config_rules) > 0: del device_obj.device_config.config_rules[:] for json_config_rule in json_config_rules: device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device_obj) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') results = [] for endpoint in endpoints: try: device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) device_settings = self.__settings_handler.get_device_settings(device_obj) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) endpoint_name = endpoint_obj.name json_config_rules = teardown_config_rules( service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, settings, device_settings, endpoint_settings) if len(json_config_rules) > 0: del device_obj.device_config.config_rules[:] for json_config_rule in json_config_rules: device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device_obj) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] results = [] for resource in resources: try: resource_value = json.loads(resource[1]) self.__settings_handler.set(resource[0], resource_value) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] results = [] for resource in resources: try: self.__settings_handler.delete(resource[0]) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) results.append(e) return results src/service/service/service_handlers/l3nm_restconf/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Loading
src/service/service/service_handlers/l3nm_restconf/ConfigRules.py 0 → 100644 +316 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from typing import Any, Dict, List, Optional, Tuple 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__) def get_value(field_name : str, *containers, default=None) -> Optional[Any]: if len(containers) == 0: raise Exception('No containers specified') for container in containers: if field_name not in container: continue return container[field_name] return default def setup_config_rules( service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple] ) -> List[Dict]: LOGGER.info(f"service_settings : {service_settings}") LOGGER.info(f"endpoint_settings : {endpoint_settings}") if service_settings is None: return [] if device_settings is None: device_settings = TreeNode('settings') device_settings.value = {} if endpoint_settings is None: return [] json_settings : Dict = service_settings.value json_device_settings : Dict = device_settings.value json_endpoint_settings : Dict = endpoint_settings.value settings = (json_settings, json_endpoint_settings, json_device_settings) mtu = get_value('mtu', *settings, default=1450) # 1512 #address_families = json_settings.get('address_families', [] ) # ['IPV4'] bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' route_distinguisher = json_settings.get('route_distinguisher', '65000:101' ) # '60001:801' sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 #network_interface_desc = '{:s}-NetIf'.format(service_uuid) network_interface_desc = json_endpoint_settings.get('ni_description','') #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) network_subinterface_desc = json_endpoint_settings.get('subif_description','') #service_short_uuid = service_uuid.split('-')[-1] #network_instance_name = '{:s}-NetInst'.format(service_short_uuid) network_instance_name = json_endpoint_settings.get('ni_name', service_uuid.split('-')[-1]) #ELAN-AC:1 mtu = int(mtu) bgp_as = int(bgp_as) sub_interface_index = int(sub_interface_index) vlan_id = int(vlan_id) address_prefix = int(address_prefix) if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) json_config_rules = [ # Configure Interface (not used) #json_config_rule_set( # '/interface[{:s}]'.format(endpoint_name), { # 'name': endpoint_name, # 'description': network_interface_desc, # 'mtu': mtu, #}), #Create network instance json_config_rule_set( '/network_instance[{:s}]'.format(network_instance_name), { 'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF', 'route_distinguisher': route_distinguisher, #'router_id': router_id, #'address_families': address_families, }), #Add BGP protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { 'name': network_instance_name, 'protocol_name': 'BGP', 'identifier': 'BGP', 'type': 'L3VRF', 'as': bgp_as, 'router_id': router_id, }), #Add DIRECTLY CONNECTED protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), { 'name': network_instance_name, 'identifier': 'DIRECTLY_CONNECTED', 'protocol_name': 'DIRECTLY_CONNECTED', }), #Add STATIC protocol to network instance json_config_rule_set( '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), { 'name': network_instance_name, 'identifier': 'STATIC', 'protocol_name': 'STATIC', }), #Create interface with subinterface json_config_rule_set( '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), { 'name' : if_subif_name, 'type' :'l3ipvlan', 'mtu' : mtu, 'index' : sub_interface_index, 'description' : network_subinterface_desc, 'vlan_id' : vlan_id, 'address_ip' : address_ip, 'address_prefix': address_prefix, }), #Associate interface to network instance json_config_rule_set( '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { 'name' : network_instance_name, 'type' : 'L3VRF', 'id' : if_subif_name, 'interface' : if_subif_name, 'subinterface': sub_interface_index, }), # TODO Validate need #Create routing policy # json_config_rule_set( # '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # 'ext_community_member' : route_distinguisher, # }), # json_config_rule_set( # # pylint: disable=duplicate-string-formatting-argument # '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), { # 'policy_name' : policy_import, # 'statement_name' : 'stm_{:s}'.format(policy_import), # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # 'policy_result' : 'ACCEPT_ROUTE', # }), #Associate routing policy to network instance # json_config_rule_set( # '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), { # 'name' : network_instance_name, # 'import_policy': policy_import, # }), # TODO Validate need #Create routing policy # json_config_rule_set( # '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # 'ext_community_member' : route_distinguisher, # }), # json_config_rule_set( # # pylint: disable=duplicate-string-formatting-argument # '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), { # 'policy_name' : policy_export, # 'statement_name' : 'stm_{:s}'.format(policy_export), # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # 'policy_result' : 'ACCEPT_ROUTE', # }), # #Associate routing policy to network instance # json_config_rule_set( # '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{ # 'name' : network_instance_name, # 'export_policy': policy_export, # }), #Create table connections # json_config_rule_set( # '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'DIRECTLY_CONNECTED', # 'dst_protocol' : 'BGP', # 'address_family' : 'IPV4', # 'default_import_policy': 'ACCEPT_ROUTE', # }), # json_config_rule_set( # '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'STATIC', # 'dst_protocol' : 'BGP', # 'address_family' : 'IPV4', # 'default_import_policy': 'ACCEPT_ROUTE', # }), ] for res_key, res_value in endpoint_acls: json_config_rules.append( {'action': 1, 'acl': res_value} ) return json_config_rules def teardown_config_rules( service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str, service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode ) -> List[Dict]: if service_settings is None: return [] if device_settings is None: return [] if endpoint_settings is None: return [] json_settings : Dict = service_settings.value json_device_settings : Dict = device_settings.value json_endpoint_settings : Dict = endpoint_settings.value settings = (json_settings, json_endpoint_settings, json_device_settings) service_short_uuid = service_uuid.split('-')[-1] network_instance_name = '{:s}-NetInst'.format(service_short_uuid) #network_interface_desc = '{:s}-NetIf'.format(service_uuid) #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) #mtu = get_value('mtu', *settings, default=1450) # 1512 #address_families = json_settings.get('address_families', [] ) # ['IPV4'] #bgp_as = get_value('bgp_as', *settings, default=65000) # 65000 route_distinguisher = json_settings.get('route_distinguisher', '0:0' ) # '60001:801' #sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 #address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' #address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 policy_import = json_endpoint_settings.get('policy_AZ', '2' ) # 2 policy_export = json_endpoint_settings.get('policy_ZA', '7' ) # 30 if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) json_config_rules = [ #Delete table connections # json_config_rule_delete( # '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name),{ # 'name' : network_instance_name, # 'src_protocol' : 'DIRECTLY_CONNECTED', # 'dst_protocol' : 'BGP', # 'address_family': 'IPV4', # }), # json_config_rule_delete( # '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { # 'name' : network_instance_name, # 'src_protocol' : 'STATIC', # 'dst_protocol' : 'BGP', # 'address_family': 'IPV4', # }), # TODO Validate need #Delete export routing policy # json_config_rule_delete( # '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { # 'policy_name': '{:s}_export'.format(network_instance_name), # }), # json_config_rule_delete( # '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_export), # }), # #Delete import routing policy # json_config_rule_delete( # '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { # 'policy_name': '{:s}_import'.format(network_instance_name), # }), # json_config_rule_delete( # '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), { # 'ext_community_set_name': 'set_{:s}'.format(policy_import), # }), #Delete interface; automatically deletes: # - /interface[]/subinterface[] json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name), { 'name': if_subif_name, }), #Delete network instance; automatically deletes: # - /network_instance[]/interface[] # - /network_instance[]/protocols[] # - /network_instance[]/inter_instance_policies[] json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name), { 'name': network_instance_name }), ] return json_config_rules
src/service/service/service_handlers/l3nm_restconf/L3NMRestconfServiceHandler.py 0 → 100644 +166 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import json, logging from typing import Any, 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.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_restconf'}) class L3NMRestconfServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings ) -> None: self.__service = service self.__task_executor = task_executor self.__settings_handler = SettingsHandler(service.service_config, **settings) @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') results = [] for endpoint in endpoints: try: device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) device_settings = self.__settings_handler.get_device_settings(device_obj) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) endpoint_acls = self.__settings_handler.get_endpoint_acls(device_obj, endpoint_obj) endpoint_name = endpoint_obj.name json_config_rules = setup_config_rules( service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, settings, device_settings, endpoint_settings, endpoint_acls) LOGGER.info(f"json_config_rules : {json_config_rules}") if len(json_config_rules) > 0: del device_obj.device_config.config_rules[:] for json_config_rule in json_config_rules: device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device_obj) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') results = [] for endpoint in endpoints: try: device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) device_settings = self.__settings_handler.get_device_settings(device_obj) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) endpoint_name = endpoint_obj.name json_config_rules = teardown_config_rules( service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, settings, device_settings, endpoint_settings) if len(json_config_rules) > 0: del device_obj.device_config.config_rules[:] for json_config_rule in json_config_rules: device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device_obj) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] results = [] for resource in resources: try: resource_value = json.loads(resource[1]) self.__settings_handler.set(resource[0], resource_value) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) results.append(e) return results @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] results = [] for resource in resources: try: self.__settings_handler.delete(resource[0]) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) results.append(e) return results
src/service/service/service_handlers/l3nm_restconf/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.