Loading src/device/service/drivers/gnmi_openconfig/handlers/Acl.py 0 → 100644 +166 −0 Original line number Original line 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 import logging from typing import Any, Dict, List, Tuple import libyang from ._Handler import _Handler from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) # ────────────────────────── enum translations ────────────────────────── _TFS_OC_RULE_TYPE = { 'ACLRULETYPE_IPV4': 'ACL_IPV4', 'ACLRULETYPE_IPV6': 'ACL_IPV6', } _TFS_OC_FWD_ACTION = { 'ACLFORWARDINGACTION_DROP': 'DROP', 'ACLFORWARDINGACTION_ACCEPT': 'ACCEPT', 'ACLFORWARDINGACTION_REJECT': 'REJECT', } _OC_TFS_RULE_TYPE = {v: k for k, v in _TFS_OC_RULE_TYPE.items()} _OC_TFS_FWD_ACTION = {v: k for k, v in _TFS_OC_FWD_ACTION.items()} # ───────────────────────────────────────────────────────────────────────── class AclHandler(_Handler): def get_resource_key(self) -> str: return '/device/endpoint/acl_ruleset' def get_path(self) -> str: return '/openconfig-acl:acl' def compose( # pylint: disable=too-many-locals self, resource_key: str, resource_value: Dict[str, Any], yang: YangHandler, delete: bool = False, ) -> Tuple[str, str]: rs = resource_value['rule_set'] rs_name = rs['name'] oc_type = _TFS_OC_RULE_TYPE[rs['type']] device = resource_value['endpoint_id']['device_id']['device_uuid']['uuid'] iface = resource_value['endpoint_id']['endpoint_uuid']['uuid'] if delete: path = f'/acl/acl-sets/acl-set[name={rs_name}][type={oc_type}]' return path, '' yang_acl: libyang.DContainer = yang.get_data_path('/openconfig-acl:acl') y_sets = yang_acl.create_path('acl-sets') y_set = y_sets.create_path(f'acl-set[name="{rs_name}"][type="{oc_type}"]') y_set.create_path('config/name', rs_name) y_set.create_path('config/type', oc_type) # Entries (ACEs) y_entries = y_set.create_path('acl-entries') for entry in rs.get('entries', []): seq = int(entry['sequence_id']) m_ = entry["match"] src_address = m_.get('src_address', '0.0.0.0/0') dst_address = m_.get('dst_address', '0.0.0.0/0') src_port = m_.get("src_port") dst_port = m_.get("dst_port") act = _TFS_OC_FWD_ACTION[entry['action']['forward_action']] y_e = y_entries.create_path(f'acl-entry[sequence-id="{seq}"]') y_e.create_path('config/sequence-id', seq) y_ipv4 = y_e.create_path('ipv4') y_ipv4.create_path('config/source-address', src_address) y_ipv4.create_path('config/destination-address', dst_address) if src_port or dst_port: proto = m_.get("protocol") y_trans = y_e.create_path("transport") if src_port: y_trans.create_path("config/source-port", int(src_port)) if dst_port: y_trans.create_path("config/destination-port", int(dst_port)) y_ipv4.create_path('config/protocol', int(proto)) y_act = y_e.create_path('actions') y_act.create_path('config/forwarding-action', act) # Interface binding y_intfs = yang_acl.create_path('interfaces') y_intf = y_intfs.create_path(f'interface[id="{iface}"]') y_ing = y_intf.create_path('ingress-acl-sets') y_ing_set = y_ing.create_path(f'ingress-acl-set[set-name="{rs_name}"][type="{oc_type}"]') y_ing_set.create_path('config/set-name', rs_name) y_ing_set.create_path('config/type', oc_type) json_data = yang_acl.print_mem('json') LOGGER.debug('JSON data: %s', json_data) json_obj = json.loads(json_data)['openconfig-acl:acl'] return '/acl', json.dumps(json_obj) def parse( # pylint: disable=too-many-locals self, json_data: Dict[str, Any], yang: YangHandler, ) -> List[Tuple[str, Dict[str, Any]]]: acl_tree = json_data.get('openconfig-acl:acl') or json_data results: List[Tuple[str, Dict[str, Any]]] = [] for acl_set in acl_tree.get('acl-sets', {}).get('acl-set', []): rs_name = acl_set['name'] oc_type = acl_set['config']['type'] rs_type = _OC_TFS_RULE_TYPE[oc_type] rule_set: Dict[str, Any] = { 'name': rs_name, 'type': rs_type, 'description': acl_set.get('config', {}).get('description', ''), 'entries': [], } for ace in acl_set.get('acl-entries', {}).get('acl-entry', []): seq = ace['sequence-id'] act = ace.get('actions', {}).get('config', {}).get('forwarding-action', 'DROP') fwd_tfs = _OC_TFS_FWD_ACTION[act] ipv4_cfg = ace.get('ipv4', {}).get('config', {}) rule_set['entries'].append( { 'sequence_id': seq, 'match': { 'src_address': ipv4_cfg.get('source-address', ''), 'dst_address': ipv4_cfg.get('destination-address', ''), }, 'action': {'forward_action': fwd_tfs}, } ) # find where that ACL is bound (first matching interface) iface = '' for intf in acl_tree.get('interfaces', {}).get('interface', []): for ing in intf.get('ingress-acl-sets', {}).get('ingress-acl-set', []): if ing['set-name'] == rs_name: iface = intf['id'] break results.append(('/acl', {'interface': iface, 'rule_set': rule_set})) return results src/device/service/drivers/gnmi_openconfig/handlers/YangHandler.py +2 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,7 @@ YANG_MODULES = [ 'openconfig-mpls-types', 'openconfig-mpls-types', 'openconfig-network-instance-types', 'openconfig-network-instance-types', 'openconfig-network-instance', 'openconfig-network-instance', 'openconfig-acl', 'openconfig-platform', 'openconfig-platform', 'openconfig-platform-controller-card', 'openconfig-platform-controller-card', Loading @@ -59,6 +60,7 @@ YANG_MODULES = [ 'openconfig-platform-software', 'openconfig-platform-software', 'openconfig-platform-transceiver', 'openconfig-platform-transceiver', 'openconfig-platform-types', 'openconfig-platform-types', 'openconfig-platform-healthz', ] ] LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__) Loading src/device/service/drivers/gnmi_openconfig/handlers/__init__.py +8 −1 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ import logging import logging from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ACL from ._Handler import _Handler from ._Handler import _Handler from .Component import ComponentHandler from .Component import ComponentHandler from .Interface import InterfaceHandler from .Interface import InterfaceHandler Loading @@ -23,6 +23,7 @@ from .NetworkInstance import NetworkInstanceHandler from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler from .NetworkInstanceProtocol import NetworkInstanceProtocolHandler from .NetworkInstanceProtocol import NetworkInstanceProtocolHandler from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler from .Acl import AclHandler from .Tools import get_schema from .Tools import get_schema from .YangHandler import YangHandler from .YangHandler import YangHandler Loading @@ -35,17 +36,20 @@ nih = NetworkInstanceHandler() niifh = NetworkInstanceInterfaceHandler() niifh = NetworkInstanceInterfaceHandler() niph = NetworkInstanceProtocolHandler() niph = NetworkInstanceProtocolHandler() nisrh = NetworkInstanceStaticRouteHandler() nisrh = NetworkInstanceStaticRouteHandler() aclh = AclHandler() ALL_RESOURCE_KEYS = [ ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ACL, ] ] RESOURCE_KEY_MAPPER = { RESOURCE_KEY_MAPPER = { RESOURCE_ENDPOINTS : comph.get_resource_key(), RESOURCE_ENDPOINTS : comph.get_resource_key(), RESOURCE_INTERFACES : ifaceh.get_resource_key(), RESOURCE_INTERFACES : ifaceh.get_resource_key(), RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(), RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(), RESOURCE_ACL : aclh.get_resource_key(), } } PATH_MAPPER = { PATH_MAPPER = { Loading @@ -53,6 +57,7 @@ PATH_MAPPER = { '/components/component' : comph.get_path(), '/components/component' : comph.get_path(), '/interfaces' : ifaceh.get_path(), '/interfaces' : ifaceh.get_path(), '/network-instances' : nih.get_path(), '/network-instances' : nih.get_path(), '/acl' : aclh.get_path(), } } RESOURCE_KEY_TO_HANDLER = { RESOURCE_KEY_TO_HANDLER = { Loading @@ -63,6 +68,7 @@ RESOURCE_KEY_TO_HANDLER = { niifh.get_resource_key() : niifh, niifh.get_resource_key() : niifh, niph.get_resource_key() : niph, niph.get_resource_key() : niph, nisrh.get_resource_key() : nisrh, nisrh.get_resource_key() : nisrh, aclh.get_resource_key() : aclh, } } PATH_TO_HANDLER = { PATH_TO_HANDLER = { Loading @@ -73,6 +79,7 @@ PATH_TO_HANDLER = { niifh.get_path() : niifh, niifh.get_path() : niifh, niph.get_path() : niph, niph.get_path() : niph, nisrh.get_path() : nisrh, nisrh.get_path() : nisrh, aclh.get_path() : aclh, } } def get_handler( def get_handler( Loading src/nbi/service/ietf_acl/Acl.py +2 −2 Original line number Original line Diff line number Diff line Loading @@ -64,8 +64,8 @@ class Acl(Resource): _config_rule.CopyFrom(config_rule) _config_rule.CopyFrom(config_rule) _config_rule.action = ConfigActionEnum.CONFIGACTION_DELETE _config_rule.action = ConfigActionEnum.CONFIGACTION_DELETE delete_config_rules.append(_config_rule) delete_config_rules.append(_config_rule) break if len(delete_config_rules) == 0: else: raise NotFound('Acl({:s}) not found in Device({:s})'.format(str(acl_name), str(device_uuid))) raise NotFound('Acl({:s}) not found in Device({:s})'.format(str(acl_name), str(device_uuid))) device_client = DeviceClient() device_client = DeviceClient() Loading src/nbi/service/ietf_acl/ietf_acl_parser.py +103 −89 Original line number Original line Diff line number Diff line Loading @@ -13,104 +13,124 @@ # limitations under the License. # limitations under the License. from enum import Enum from enum import Enum from typing import List, Dict from typing import List, Dict, Optional from pydantic import BaseModel, Field from pydantic import BaseModel, Field from werkzeug.exceptions import NotImplemented from werkzeug.exceptions import NotImplemented from common.proto.acl_pb2 import AclForwardActionEnum, AclRuleTypeEnum, AclEntry from common.proto.acl_pb2 import AclForwardActionEnum, AclRuleTypeEnum, AclEntry from common.proto.context_pb2 import ConfigActionEnum, ConfigRule from common.proto.context_pb2 import ConfigActionEnum, ConfigRule class AclDirectionEnum(Enum): class AclDirectionEnum(Enum): INGRESS = 'ingress' INGRESS = 'ingress' EGRESS = 'egress' EGRESS = 'egress' class Ipv4(BaseModel): class Ipv4(BaseModel): dscp: int = 0 dscp: int = 0 source_ipv4_network: str = Field(serialization_alias="source-ipv4-network", default="") source_ipv4_network: str = Field(serialization_alias='source-ipv4-network', default='') destination_ipv4_network: str = Field(serialization_alias="destination-ipv4-network", default="") destination_ipv4_network: str = Field( serialization_alias='destination-ipv4-network', default='' ) class Port(BaseModel): class Port(BaseModel): port: int = 0 port: int = 0 operator: str = "eq" operator: str = 'eq' class Tcp(BaseModel): class Tcp(BaseModel): flags: str = "" flags: Optional[str] = None source_port: Port = Field(serialization_alias="source-port", default_factory=lambda: Port()) source_port: Optional[Port] = Field(serialization_alias='source-port', default=None) destination_port: Port = Field(serialization_alias="destination-port", default_factory=lambda: Port()) destination_port: Optional[Port] = Field(serialization_alias='destination-port', default=None) class Matches(BaseModel): class Matches(BaseModel): ipv4: Ipv4 = Ipv4() ipv4: Ipv4 = Ipv4() tcp: Tcp = Tcp() tcp: Optional[Tcp] = None class Action(BaseModel): class Action(BaseModel): forwarding: str = "" forwarding: str = '' class Ace(BaseModel): class Ace(BaseModel): name: str = "custom_rule" name: str = '' matches: Matches = Matches() matches: Matches = Matches() actions: Action = Action() actions: Action = Action() class Aces(BaseModel): class Aces(BaseModel): ace: List[Ace] = [Ace()] ace: List[Ace] = [Ace()] class Acl(BaseModel): class Acl(BaseModel): name: str = "" name: str = '' type: str = "" type: str = '' aces: Aces = Aces() aces: Aces = Aces() class Name(BaseModel): class Name(BaseModel): name: str = "" name: str = '' class AclSet(BaseModel): class AclSet(BaseModel): acl_set: List[Name] = Field(serialization_alias="acl-set", default=[Name()]) acl_set: List[Name] = Field(serialization_alias='acl-set', default=[Name()]) class AclSets(BaseModel): class AclSets(BaseModel): acl_sets: AclSet = Field(serialization_alias="acl-sets", default=AclSet()) acl_sets: AclSet = Field(serialization_alias='acl-sets', default=AclSet()) class Ingress(BaseModel): class Ingress(BaseModel): ingress: AclSets = AclSets() ingress: AclSets = AclSets() class Egress(BaseModel): class Egress(BaseModel): egress: AclSets = AclSets() egress: AclSets = AclSets() class Interface(BaseModel): class Interface(BaseModel): interface_id: str = Field(serialization_alias="interface-id", default="") interface_id: str = Field(serialization_alias='interface-id', default='') ingress : Ingress = Ingress() ingress: Optional[AclSets] = None egress : Egress = Egress() egress: Optional[AclSets] = None class Interfaces(BaseModel): class Interfaces(BaseModel): interface: List[Interface] = [Interface()] interface: List[Interface] = [Interface()] class AttachmentPoints(BaseModel): attachment_points: Interfaces = Field(serialization_alias="attachment-points", default=Interfaces()) class Acls(BaseModel): class Acls(BaseModel): acl: List[Acl] = [Acl()] acl: List[Acl] = [Acl()] attachment_points: AttachmentPoints = Field(serialization_alias="attachment-points", default=AttachmentPoints()) attachment_points: Optional[Interfaces] = Field( serialization_alias='attachment-points', default=None ) class IETF_ACL(BaseModel): class IETF_ACL(BaseModel): acls: Acls = Acls() acls: Optional[Acls] = Field(serialization_alias='ietf-access-control-list:acls', default=None) IETF_TFS_RULE_TYPE_MAPPING = { IETF_TFS_RULE_TYPE_MAPPING = { "ipv4-acl-type": "ACLRULETYPE_IPV4", 'ipv4-acl-type': 'ACLRULETYPE_IPV4', "ipv6-acl-type": "ACLRULETYPE_IPV6", 'ipv6-acl-type': 'ACLRULETYPE_IPV6', } } IETF_TFS_FORWARDING_ACTION_MAPPING = { IETF_TFS_FORWARDING_ACTION_MAPPING = { "accept": "ACLFORWARDINGACTION_ACCEPT", 'accept': 'ACLFORWARDINGACTION_ACCEPT', "drop" : "ACLFORWARDINGACTION_DROP", 'drop': 'ACLFORWARDINGACTION_DROP', } } TFS_IETF_RULE_TYPE_MAPPING = { TFS_IETF_RULE_TYPE_MAPPING = { "ACLRULETYPE_IPV4": "ipv4-acl-type", 'ACLRULETYPE_IPV4': 'ipv4-acl-type', "ACLRULETYPE_IPV6": "ipv6-acl-type", 'ACLRULETYPE_IPV6': 'ipv6-acl-type', } } TFS_IETF_FORWARDING_ACTION_MAPPING = { TFS_IETF_FORWARDING_ACTION_MAPPING = { "ACLFORWARDINGACTION_ACCEPT": "accept", 'ACLFORWARDINGACTION_ACCEPT': 'accept', "ACLFORWARDINGACTION_DROP" : "drop", 'ACLFORWARDINGACTION_DROP': 'drop', } } def config_rule_from_ietf_acl( def config_rule_from_ietf_acl( device_name: str, endpoint_name: str, acl_set_data: Dict device_name: str, endpoint_name: str, acl_set_data: Dict ) -> ConfigRule: ) -> ConfigRule: Loading Loading @@ -139,7 +159,7 @@ def config_rule_from_ietf_acl( acl_entry = AclEntry() acl_entry = AclEntry() acl_entry.sequence_id = sequence_id + 1 acl_entry.sequence_id = sequence_id + 1 #acl_entry.description = ... acl_entry.description = ace_name if 'ipv4' in ace_matches: if 'ipv4' in ace_matches: ipv4_data = ace_matches['ipv4'] ipv4_data = ace_matches['ipv4'] Loading Loading @@ -203,55 +223,49 @@ def config_rule_from_ietf_acl( return acl_config_rule return acl_config_rule def ietf_acl_from_config_rule_resource_value(config_rule_rv: Dict) -> Dict: def ietf_acl_from_config_rule_resource_value(config_rule_rv: Dict) -> Dict: rule_set = config_rule_rv['rule_set'] rule_set = config_rule_rv['rule_set'] acl_entry = rule_set['entries'][0] ace = [] match_ = acl_entry['match'] for acl_entry in rule_set['entries']: match_ = acl_entry['match'] ipv4 = Ipv4( ipv4 = Ipv4( dscp=match_["dscp"], dscp=match_['dscp'], source_ipv4_network=match_["src_address"], source_ipv4_network=match_['src_address'], destination_ipv4_network=match_["dst_address"] destination_ipv4_network=match_['dst_address'], ) ) tcp = None if match_['tcp_flags']: tcp = Tcp( tcp = Tcp( flags=match_["tcp_flags"], flags=match_['tcp_flags'], source_port=Port(port=match_["src_port"]), source_port=Port(port=match_['src_port']), destination_port=Port(port=match_["dst_port"]) destination_port=Port(port=match_['dst_port']), ) ) matches = Matches(ipvr=ipv4, tcp=tcp) matches = Matches(ipv4=ipv4, tcp=tcp) aces = Aces(ace=[ ace.append( Ace( Ace( name=acl_entry['description'], matches=matches, matches=matches, actions=Action( actions=Action( forwarding=TFS_IETF_FORWARDING_ACTION_MAPPING[acl_entry["action"]["forward_action"]] forwarding=TFS_IETF_FORWARDING_ACTION_MAPPING[ ) acl_entry['action']['forward_action'] ) ]) acl = Acl( name=rule_set["name"], type=TFS_IETF_RULE_TYPE_MAPPING[rule_set["type"]], aces=aces ) acl_sets = AclSets( acl_sets=AclSet( acl_set=[ Name(name=rule_set["name"]) ] ] ), ) ) ) ) ingress = Ingress(ingress=acl_sets) aces = Aces(ace=ace) interfaces = Interfaces(interface=[ acl = Acl(name=rule_set['name'], type=TFS_IETF_RULE_TYPE_MAPPING[rule_set['type']], aces=aces) acl_sets = AclSets(acl_sets=AclSet(acl_set=[Name(name=rule_set['name'])])) interfaces = Interfaces( interface=[ Interface( Interface( interface_id=config_rule_rv["interface"], interface_id=config_rule_rv['endpoint_id']['endpoint_uuid']['uuid'], ingress=ingress ingress=acl_sets, ) ]) acls = Acls( acl=[acl], attachment_points=AttachmentPoints( attachment_points=interfaces ) ) ] ) ) acls = Acls(acl=[acl], attachment_points=interfaces) ietf_acl = IETF_ACL(acls=acls) ietf_acl = IETF_ACL(acls=acls) return ietf_acl.model_dump(by_alias=True) return ietf_acl.model_dump(by_alias=True, exclude_none=True) Loading
src/device/service/drivers/gnmi_openconfig/handlers/Acl.py 0 → 100644 +166 −0 Original line number Original line 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 import logging from typing import Any, Dict, List, Tuple import libyang from ._Handler import _Handler from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) # ────────────────────────── enum translations ────────────────────────── _TFS_OC_RULE_TYPE = { 'ACLRULETYPE_IPV4': 'ACL_IPV4', 'ACLRULETYPE_IPV6': 'ACL_IPV6', } _TFS_OC_FWD_ACTION = { 'ACLFORWARDINGACTION_DROP': 'DROP', 'ACLFORWARDINGACTION_ACCEPT': 'ACCEPT', 'ACLFORWARDINGACTION_REJECT': 'REJECT', } _OC_TFS_RULE_TYPE = {v: k for k, v in _TFS_OC_RULE_TYPE.items()} _OC_TFS_FWD_ACTION = {v: k for k, v in _TFS_OC_FWD_ACTION.items()} # ───────────────────────────────────────────────────────────────────────── class AclHandler(_Handler): def get_resource_key(self) -> str: return '/device/endpoint/acl_ruleset' def get_path(self) -> str: return '/openconfig-acl:acl' def compose( # pylint: disable=too-many-locals self, resource_key: str, resource_value: Dict[str, Any], yang: YangHandler, delete: bool = False, ) -> Tuple[str, str]: rs = resource_value['rule_set'] rs_name = rs['name'] oc_type = _TFS_OC_RULE_TYPE[rs['type']] device = resource_value['endpoint_id']['device_id']['device_uuid']['uuid'] iface = resource_value['endpoint_id']['endpoint_uuid']['uuid'] if delete: path = f'/acl/acl-sets/acl-set[name={rs_name}][type={oc_type}]' return path, '' yang_acl: libyang.DContainer = yang.get_data_path('/openconfig-acl:acl') y_sets = yang_acl.create_path('acl-sets') y_set = y_sets.create_path(f'acl-set[name="{rs_name}"][type="{oc_type}"]') y_set.create_path('config/name', rs_name) y_set.create_path('config/type', oc_type) # Entries (ACEs) y_entries = y_set.create_path('acl-entries') for entry in rs.get('entries', []): seq = int(entry['sequence_id']) m_ = entry["match"] src_address = m_.get('src_address', '0.0.0.0/0') dst_address = m_.get('dst_address', '0.0.0.0/0') src_port = m_.get("src_port") dst_port = m_.get("dst_port") act = _TFS_OC_FWD_ACTION[entry['action']['forward_action']] y_e = y_entries.create_path(f'acl-entry[sequence-id="{seq}"]') y_e.create_path('config/sequence-id', seq) y_ipv4 = y_e.create_path('ipv4') y_ipv4.create_path('config/source-address', src_address) y_ipv4.create_path('config/destination-address', dst_address) if src_port or dst_port: proto = m_.get("protocol") y_trans = y_e.create_path("transport") if src_port: y_trans.create_path("config/source-port", int(src_port)) if dst_port: y_trans.create_path("config/destination-port", int(dst_port)) y_ipv4.create_path('config/protocol', int(proto)) y_act = y_e.create_path('actions') y_act.create_path('config/forwarding-action', act) # Interface binding y_intfs = yang_acl.create_path('interfaces') y_intf = y_intfs.create_path(f'interface[id="{iface}"]') y_ing = y_intf.create_path('ingress-acl-sets') y_ing_set = y_ing.create_path(f'ingress-acl-set[set-name="{rs_name}"][type="{oc_type}"]') y_ing_set.create_path('config/set-name', rs_name) y_ing_set.create_path('config/type', oc_type) json_data = yang_acl.print_mem('json') LOGGER.debug('JSON data: %s', json_data) json_obj = json.loads(json_data)['openconfig-acl:acl'] return '/acl', json.dumps(json_obj) def parse( # pylint: disable=too-many-locals self, json_data: Dict[str, Any], yang: YangHandler, ) -> List[Tuple[str, Dict[str, Any]]]: acl_tree = json_data.get('openconfig-acl:acl') or json_data results: List[Tuple[str, Dict[str, Any]]] = [] for acl_set in acl_tree.get('acl-sets', {}).get('acl-set', []): rs_name = acl_set['name'] oc_type = acl_set['config']['type'] rs_type = _OC_TFS_RULE_TYPE[oc_type] rule_set: Dict[str, Any] = { 'name': rs_name, 'type': rs_type, 'description': acl_set.get('config', {}).get('description', ''), 'entries': [], } for ace in acl_set.get('acl-entries', {}).get('acl-entry', []): seq = ace['sequence-id'] act = ace.get('actions', {}).get('config', {}).get('forwarding-action', 'DROP') fwd_tfs = _OC_TFS_FWD_ACTION[act] ipv4_cfg = ace.get('ipv4', {}).get('config', {}) rule_set['entries'].append( { 'sequence_id': seq, 'match': { 'src_address': ipv4_cfg.get('source-address', ''), 'dst_address': ipv4_cfg.get('destination-address', ''), }, 'action': {'forward_action': fwd_tfs}, } ) # find where that ACL is bound (first matching interface) iface = '' for intf in acl_tree.get('interfaces', {}).get('interface', []): for ing in intf.get('ingress-acl-sets', {}).get('ingress-acl-set', []): if ing['set-name'] == rs_name: iface = intf['id'] break results.append(('/acl', {'interface': iface, 'rule_set': rule_set})) return results
src/device/service/drivers/gnmi_openconfig/handlers/YangHandler.py +2 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,7 @@ YANG_MODULES = [ 'openconfig-mpls-types', 'openconfig-mpls-types', 'openconfig-network-instance-types', 'openconfig-network-instance-types', 'openconfig-network-instance', 'openconfig-network-instance', 'openconfig-acl', 'openconfig-platform', 'openconfig-platform', 'openconfig-platform-controller-card', 'openconfig-platform-controller-card', Loading @@ -59,6 +60,7 @@ YANG_MODULES = [ 'openconfig-platform-software', 'openconfig-platform-software', 'openconfig-platform-transceiver', 'openconfig-platform-transceiver', 'openconfig-platform-types', 'openconfig-platform-types', 'openconfig-platform-healthz', ] ] LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__) Loading
src/device/service/drivers/gnmi_openconfig/handlers/__init__.py +8 −1 Original line number Original line Diff line number Diff line Loading @@ -14,7 +14,7 @@ import logging import logging from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ACL from ._Handler import _Handler from ._Handler import _Handler from .Component import ComponentHandler from .Component import ComponentHandler from .Interface import InterfaceHandler from .Interface import InterfaceHandler Loading @@ -23,6 +23,7 @@ from .NetworkInstance import NetworkInstanceHandler from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler from .NetworkInstanceProtocol import NetworkInstanceProtocolHandler from .NetworkInstanceProtocol import NetworkInstanceProtocolHandler from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler from .Acl import AclHandler from .Tools import get_schema from .Tools import get_schema from .YangHandler import YangHandler from .YangHandler import YangHandler Loading @@ -35,17 +36,20 @@ nih = NetworkInstanceHandler() niifh = NetworkInstanceInterfaceHandler() niifh = NetworkInstanceInterfaceHandler() niph = NetworkInstanceProtocolHandler() niph = NetworkInstanceProtocolHandler() nisrh = NetworkInstanceStaticRouteHandler() nisrh = NetworkInstanceStaticRouteHandler() aclh = AclHandler() ALL_RESOURCE_KEYS = [ ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ACL, ] ] RESOURCE_KEY_MAPPER = { RESOURCE_KEY_MAPPER = { RESOURCE_ENDPOINTS : comph.get_resource_key(), RESOURCE_ENDPOINTS : comph.get_resource_key(), RESOURCE_INTERFACES : ifaceh.get_resource_key(), RESOURCE_INTERFACES : ifaceh.get_resource_key(), RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(), RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(), RESOURCE_ACL : aclh.get_resource_key(), } } PATH_MAPPER = { PATH_MAPPER = { Loading @@ -53,6 +57,7 @@ PATH_MAPPER = { '/components/component' : comph.get_path(), '/components/component' : comph.get_path(), '/interfaces' : ifaceh.get_path(), '/interfaces' : ifaceh.get_path(), '/network-instances' : nih.get_path(), '/network-instances' : nih.get_path(), '/acl' : aclh.get_path(), } } RESOURCE_KEY_TO_HANDLER = { RESOURCE_KEY_TO_HANDLER = { Loading @@ -63,6 +68,7 @@ RESOURCE_KEY_TO_HANDLER = { niifh.get_resource_key() : niifh, niifh.get_resource_key() : niifh, niph.get_resource_key() : niph, niph.get_resource_key() : niph, nisrh.get_resource_key() : nisrh, nisrh.get_resource_key() : nisrh, aclh.get_resource_key() : aclh, } } PATH_TO_HANDLER = { PATH_TO_HANDLER = { Loading @@ -73,6 +79,7 @@ PATH_TO_HANDLER = { niifh.get_path() : niifh, niifh.get_path() : niifh, niph.get_path() : niph, niph.get_path() : niph, nisrh.get_path() : nisrh, nisrh.get_path() : nisrh, aclh.get_path() : aclh, } } def get_handler( def get_handler( Loading
src/nbi/service/ietf_acl/Acl.py +2 −2 Original line number Original line Diff line number Diff line Loading @@ -64,8 +64,8 @@ class Acl(Resource): _config_rule.CopyFrom(config_rule) _config_rule.CopyFrom(config_rule) _config_rule.action = ConfigActionEnum.CONFIGACTION_DELETE _config_rule.action = ConfigActionEnum.CONFIGACTION_DELETE delete_config_rules.append(_config_rule) delete_config_rules.append(_config_rule) break if len(delete_config_rules) == 0: else: raise NotFound('Acl({:s}) not found in Device({:s})'.format(str(acl_name), str(device_uuid))) raise NotFound('Acl({:s}) not found in Device({:s})'.format(str(acl_name), str(device_uuid))) device_client = DeviceClient() device_client = DeviceClient() Loading
src/nbi/service/ietf_acl/ietf_acl_parser.py +103 −89 Original line number Original line Diff line number Diff line Loading @@ -13,104 +13,124 @@ # limitations under the License. # limitations under the License. from enum import Enum from enum import Enum from typing import List, Dict from typing import List, Dict, Optional from pydantic import BaseModel, Field from pydantic import BaseModel, Field from werkzeug.exceptions import NotImplemented from werkzeug.exceptions import NotImplemented from common.proto.acl_pb2 import AclForwardActionEnum, AclRuleTypeEnum, AclEntry from common.proto.acl_pb2 import AclForwardActionEnum, AclRuleTypeEnum, AclEntry from common.proto.context_pb2 import ConfigActionEnum, ConfigRule from common.proto.context_pb2 import ConfigActionEnum, ConfigRule class AclDirectionEnum(Enum): class AclDirectionEnum(Enum): INGRESS = 'ingress' INGRESS = 'ingress' EGRESS = 'egress' EGRESS = 'egress' class Ipv4(BaseModel): class Ipv4(BaseModel): dscp: int = 0 dscp: int = 0 source_ipv4_network: str = Field(serialization_alias="source-ipv4-network", default="") source_ipv4_network: str = Field(serialization_alias='source-ipv4-network', default='') destination_ipv4_network: str = Field(serialization_alias="destination-ipv4-network", default="") destination_ipv4_network: str = Field( serialization_alias='destination-ipv4-network', default='' ) class Port(BaseModel): class Port(BaseModel): port: int = 0 port: int = 0 operator: str = "eq" operator: str = 'eq' class Tcp(BaseModel): class Tcp(BaseModel): flags: str = "" flags: Optional[str] = None source_port: Port = Field(serialization_alias="source-port", default_factory=lambda: Port()) source_port: Optional[Port] = Field(serialization_alias='source-port', default=None) destination_port: Port = Field(serialization_alias="destination-port", default_factory=lambda: Port()) destination_port: Optional[Port] = Field(serialization_alias='destination-port', default=None) class Matches(BaseModel): class Matches(BaseModel): ipv4: Ipv4 = Ipv4() ipv4: Ipv4 = Ipv4() tcp: Tcp = Tcp() tcp: Optional[Tcp] = None class Action(BaseModel): class Action(BaseModel): forwarding: str = "" forwarding: str = '' class Ace(BaseModel): class Ace(BaseModel): name: str = "custom_rule" name: str = '' matches: Matches = Matches() matches: Matches = Matches() actions: Action = Action() actions: Action = Action() class Aces(BaseModel): class Aces(BaseModel): ace: List[Ace] = [Ace()] ace: List[Ace] = [Ace()] class Acl(BaseModel): class Acl(BaseModel): name: str = "" name: str = '' type: str = "" type: str = '' aces: Aces = Aces() aces: Aces = Aces() class Name(BaseModel): class Name(BaseModel): name: str = "" name: str = '' class AclSet(BaseModel): class AclSet(BaseModel): acl_set: List[Name] = Field(serialization_alias="acl-set", default=[Name()]) acl_set: List[Name] = Field(serialization_alias='acl-set', default=[Name()]) class AclSets(BaseModel): class AclSets(BaseModel): acl_sets: AclSet = Field(serialization_alias="acl-sets", default=AclSet()) acl_sets: AclSet = Field(serialization_alias='acl-sets', default=AclSet()) class Ingress(BaseModel): class Ingress(BaseModel): ingress: AclSets = AclSets() ingress: AclSets = AclSets() class Egress(BaseModel): class Egress(BaseModel): egress: AclSets = AclSets() egress: AclSets = AclSets() class Interface(BaseModel): class Interface(BaseModel): interface_id: str = Field(serialization_alias="interface-id", default="") interface_id: str = Field(serialization_alias='interface-id', default='') ingress : Ingress = Ingress() ingress: Optional[AclSets] = None egress : Egress = Egress() egress: Optional[AclSets] = None class Interfaces(BaseModel): class Interfaces(BaseModel): interface: List[Interface] = [Interface()] interface: List[Interface] = [Interface()] class AttachmentPoints(BaseModel): attachment_points: Interfaces = Field(serialization_alias="attachment-points", default=Interfaces()) class Acls(BaseModel): class Acls(BaseModel): acl: List[Acl] = [Acl()] acl: List[Acl] = [Acl()] attachment_points: AttachmentPoints = Field(serialization_alias="attachment-points", default=AttachmentPoints()) attachment_points: Optional[Interfaces] = Field( serialization_alias='attachment-points', default=None ) class IETF_ACL(BaseModel): class IETF_ACL(BaseModel): acls: Acls = Acls() acls: Optional[Acls] = Field(serialization_alias='ietf-access-control-list:acls', default=None) IETF_TFS_RULE_TYPE_MAPPING = { IETF_TFS_RULE_TYPE_MAPPING = { "ipv4-acl-type": "ACLRULETYPE_IPV4", 'ipv4-acl-type': 'ACLRULETYPE_IPV4', "ipv6-acl-type": "ACLRULETYPE_IPV6", 'ipv6-acl-type': 'ACLRULETYPE_IPV6', } } IETF_TFS_FORWARDING_ACTION_MAPPING = { IETF_TFS_FORWARDING_ACTION_MAPPING = { "accept": "ACLFORWARDINGACTION_ACCEPT", 'accept': 'ACLFORWARDINGACTION_ACCEPT', "drop" : "ACLFORWARDINGACTION_DROP", 'drop': 'ACLFORWARDINGACTION_DROP', } } TFS_IETF_RULE_TYPE_MAPPING = { TFS_IETF_RULE_TYPE_MAPPING = { "ACLRULETYPE_IPV4": "ipv4-acl-type", 'ACLRULETYPE_IPV4': 'ipv4-acl-type', "ACLRULETYPE_IPV6": "ipv6-acl-type", 'ACLRULETYPE_IPV6': 'ipv6-acl-type', } } TFS_IETF_FORWARDING_ACTION_MAPPING = { TFS_IETF_FORWARDING_ACTION_MAPPING = { "ACLFORWARDINGACTION_ACCEPT": "accept", 'ACLFORWARDINGACTION_ACCEPT': 'accept', "ACLFORWARDINGACTION_DROP" : "drop", 'ACLFORWARDINGACTION_DROP': 'drop', } } def config_rule_from_ietf_acl( def config_rule_from_ietf_acl( device_name: str, endpoint_name: str, acl_set_data: Dict device_name: str, endpoint_name: str, acl_set_data: Dict ) -> ConfigRule: ) -> ConfigRule: Loading Loading @@ -139,7 +159,7 @@ def config_rule_from_ietf_acl( acl_entry = AclEntry() acl_entry = AclEntry() acl_entry.sequence_id = sequence_id + 1 acl_entry.sequence_id = sequence_id + 1 #acl_entry.description = ... acl_entry.description = ace_name if 'ipv4' in ace_matches: if 'ipv4' in ace_matches: ipv4_data = ace_matches['ipv4'] ipv4_data = ace_matches['ipv4'] Loading Loading @@ -203,55 +223,49 @@ def config_rule_from_ietf_acl( return acl_config_rule return acl_config_rule def ietf_acl_from_config_rule_resource_value(config_rule_rv: Dict) -> Dict: def ietf_acl_from_config_rule_resource_value(config_rule_rv: Dict) -> Dict: rule_set = config_rule_rv['rule_set'] rule_set = config_rule_rv['rule_set'] acl_entry = rule_set['entries'][0] ace = [] match_ = acl_entry['match'] for acl_entry in rule_set['entries']: match_ = acl_entry['match'] ipv4 = Ipv4( ipv4 = Ipv4( dscp=match_["dscp"], dscp=match_['dscp'], source_ipv4_network=match_["src_address"], source_ipv4_network=match_['src_address'], destination_ipv4_network=match_["dst_address"] destination_ipv4_network=match_['dst_address'], ) ) tcp = None if match_['tcp_flags']: tcp = Tcp( tcp = Tcp( flags=match_["tcp_flags"], flags=match_['tcp_flags'], source_port=Port(port=match_["src_port"]), source_port=Port(port=match_['src_port']), destination_port=Port(port=match_["dst_port"]) destination_port=Port(port=match_['dst_port']), ) ) matches = Matches(ipvr=ipv4, tcp=tcp) matches = Matches(ipv4=ipv4, tcp=tcp) aces = Aces(ace=[ ace.append( Ace( Ace( name=acl_entry['description'], matches=matches, matches=matches, actions=Action( actions=Action( forwarding=TFS_IETF_FORWARDING_ACTION_MAPPING[acl_entry["action"]["forward_action"]] forwarding=TFS_IETF_FORWARDING_ACTION_MAPPING[ ) acl_entry['action']['forward_action'] ) ]) acl = Acl( name=rule_set["name"], type=TFS_IETF_RULE_TYPE_MAPPING[rule_set["type"]], aces=aces ) acl_sets = AclSets( acl_sets=AclSet( acl_set=[ Name(name=rule_set["name"]) ] ] ), ) ) ) ) ingress = Ingress(ingress=acl_sets) aces = Aces(ace=ace) interfaces = Interfaces(interface=[ acl = Acl(name=rule_set['name'], type=TFS_IETF_RULE_TYPE_MAPPING[rule_set['type']], aces=aces) acl_sets = AclSets(acl_sets=AclSet(acl_set=[Name(name=rule_set['name'])])) interfaces = Interfaces( interface=[ Interface( Interface( interface_id=config_rule_rv["interface"], interface_id=config_rule_rv['endpoint_id']['endpoint_uuid']['uuid'], ingress=ingress ingress=acl_sets, ) ]) acls = Acls( acl=[acl], attachment_points=AttachmentPoints( attachment_points=interfaces ) ) ] ) ) acls = Acls(acl=[acl], attachment_points=interfaces) ietf_acl = IETF_ACL(acls=acls) ietf_acl = IETF_ACL(acls=acls) return ietf_acl.model_dump(by_alias=True) return ietf_acl.model_dump(by_alias=True, exclude_none=True)