Commit 0ee4d415 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch...

Merge branch 'feat/114-cttc-integrate-management-of-network-access-control-lists-acls-created-through-the-nbi-and' into 'develop'

Resolve "(CTTC) Integrate management of Network Access Control Lists (ACLs) created through the NBI and forwarded to SBI"

See merge request !400
parents ce78997b ab2376f0
Loading
Loading
Loading
Loading
+9 −2
Original line number Original line Diff line number Diff line
@@ -566,9 +566,16 @@ message ConfigRule_Custom {
  string resource_value = 2;
  string resource_value = 2;
}
}


enum AclDirectionEnum {
  ACLDIRECTION_BOTH    = 0;
  ACLDIRECTION_INGRESS = 1;
  ACLDIRECTION_EGRESS  = 2;
}

message ConfigRule_ACL {
message ConfigRule_ACL {
  EndPointId       endpoint_id = 1;
  EndPointId       endpoint_id = 1;
  acl.AclRuleSet rule_set = 2;
  AclDirectionEnum direction   = 2;
  acl.AclRuleSet   rule_set    = 3;
}
}


message ConfigRule_IPOWDM {
message ConfigRule_IPOWDM {
+57 −15
Original line number Original line Diff line number Diff line
@@ -12,12 +12,9 @@
# See the License for the specific language governing permissions and
# See the License for the specific language governing permissions and
# limitations under the License.
# limitations under the License.


import json
import json, libyang, logging
import logging
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, List, Tuple

from common.proto.context_pb2 import AclDirectionEnum
import libyang

from ._Handler import _Handler
from ._Handler import _Handler
from .YangHandler import YangHandler
from .YangHandler import YangHandler


@@ -41,6 +38,20 @@ _OC_TFS_FWD_ACTION = {v: k for k, v in _TFS_OC_FWD_ACTION.items()}


# ─────────────────────────────────────────────────────────────────────────
# ─────────────────────────────────────────────────────────────────────────


DIRECTION_INGRESS = {
    AclDirectionEnum.ACLDIRECTION_BOTH,
    AclDirectionEnum.Name(AclDirectionEnum.ACLDIRECTION_BOTH),
    AclDirectionEnum.ACLDIRECTION_INGRESS,
    AclDirectionEnum.Name(AclDirectionEnum.ACLDIRECTION_INGRESS),
}

DIRECTION_EGRESS = {
    AclDirectionEnum.ACLDIRECTION_BOTH,
    AclDirectionEnum.Name(AclDirectionEnum.ACLDIRECTION_BOTH),
    AclDirectionEnum.ACLDIRECTION_EGRESS,
    AclDirectionEnum.Name(AclDirectionEnum.ACLDIRECTION_EGRESS),
}



class AclHandler(_Handler):
class AclHandler(_Handler):
    def get_resource_key(self) -> str:
    def get_resource_key(self) -> str:
@@ -59,8 +70,9 @@ class AclHandler(_Handler):
        rs = resource_value['rule_set']
        rs = resource_value['rule_set']
        rs_name = rs['name']
        rs_name = rs['name']
        oc_type = _TFS_OC_RULE_TYPE[rs['type']]
        oc_type = _TFS_OC_RULE_TYPE[rs['type']]
        device = resource_value['endpoint_id']['device_id']['device_uuid']['uuid']
        #device = resource_value['endpoint_id']['device_id']['device_uuid']['uuid']
        iface = resource_value['endpoint_id']['endpoint_uuid']['uuid']
        iface = resource_value['endpoint_id']['endpoint_uuid']['uuid']
        direction = resource_value['direction']


        if delete:
        if delete:
            path = f'/acl/acl-sets/acl-set[name={rs_name}][type={oc_type}]'
            path = f'/acl/acl-sets/acl-set[name={rs_name}][type={oc_type}]'
@@ -77,11 +89,11 @@ class AclHandler(_Handler):
        y_entries = y_set.create_path('acl-entries')
        y_entries = y_set.create_path('acl-entries')
        for entry in rs.get('entries', []):
        for entry in rs.get('entries', []):
            seq = int(entry['sequence_id'])
            seq = int(entry['sequence_id'])
            m_ = entry["match"]
            m_ = entry['match']
            src_address = m_.get('src_address', '0.0.0.0/0')
            src_address = m_.get('src_address', '0.0.0.0/0')
            dst_address = m_.get('dst_address', '0.0.0.0/0')
            dst_address = m_.get('dst_address', '0.0.0.0/0')
            src_port = m_.get("src_port")
            src_port = m_.get('src_port')
            dst_port = m_.get("dst_port")
            dst_port = m_.get('dst_port')
            act = _TFS_OC_FWD_ACTION[entry['action']['forward_action']]
            act = _TFS_OC_FWD_ACTION[entry['action']['forward_action']]


            y_e = y_entries.create_path(f'acl-entry[sequence-id="{seq}"]')
            y_e = y_entries.create_path(f'acl-entry[sequence-id="{seq}"]')
@@ -91,9 +103,12 @@ class AclHandler(_Handler):
            y_ipv4.create_path('config/source-address', src_address)
            y_ipv4.create_path('config/source-address', src_address)
            y_ipv4.create_path('config/destination-address', dst_address)
            y_ipv4.create_path('config/destination-address', dst_address)


            proto = m_.get('protocol')
            if proto is not None:
                y_ipv4.create_path('config/protocol', int(proto))

            if src_port or dst_port:
            if src_port or dst_port:
                proto = m_.get("protocol")
                y_trans = y_e.create_path('transport')
                y_trans = y_e.create_path("transport")
                if src_port:
                if src_port:
                    y_trans.create_path("config/source-port", int(src_port))
                    y_trans.create_path("config/source-port", int(src_port))
                if dst_port:
                if dst_port:
@@ -106,14 +121,41 @@ class AclHandler(_Handler):
        # Interface binding
        # Interface binding
        y_intfs = yang_acl.create_path('interfaces')
        y_intfs = yang_acl.create_path('interfaces')
        y_intf = y_intfs.create_path(f'interface[id="{iface}"]')
        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}"]')
        if direction in DIRECTION_INGRESS:
        y_ing_set.create_path('config/set-name', rs_name)
            y_ingress = y_intf.create_path('ingress-acl-sets')
        y_ing_set.create_path('config/type', oc_type)
            y_ingress_set = y_ingress.create_path(f'ingress-acl-set[set-name="{rs_name}"][type="{oc_type}"]')
            y_ingress_set.create_path('config/set-name', rs_name)
            y_ingress_set.create_path('config/type', oc_type)

        if direction in DIRECTION_EGRESS:
            y_egress = y_intf.create_path('egress-acl-sets')
            y_egress_set = y_egress.create_path(f'egress-acl-set[set-name="{rs_name}"][type="{oc_type}"]')
            y_egress_set.create_path('config/set-name', rs_name)
            y_egress_set.create_path('config/type', oc_type)


        json_data = yang_acl.print_mem('json')
        json_data = yang_acl.print_mem('json')
        #json_data = str(node.print_mem(
        #    fmt='json', with_siblings=True, pretty=True,
        #    keep_empty_containers=True, include_implicit_defaults=True
        #))
        LOGGER.debug('JSON data: %s', json_data)
        LOGGER.debug('JSON data: %s', json_data)
        json_obj = json.loads(json_data)['openconfig-acl:acl']
        json_obj = json.loads(json_data)['openconfig-acl:acl']

        # release generated nodes to prevent side effects
        node_ifs : libyang.DNode = yang_acl.find_path('/openconfig-acl:acl/interfaces')
        #if node is None: return None
        LOGGER.info('node_ifs = {:s}'.format(str(node_ifs)))
        node_ifs.unlink()
        node_ifs.free()

        # release generated nodes to prevent side effects
        node_acls : libyang.DNode = yang_acl.find_path('/openconfig-acl:acl/acl-sets')
        #if node is None: return None
        LOGGER.info('node_acls = {:s}'.format(str(node_acls)))
        node_acls.unlink()
        node_acls.free()

        return '/acl', json.dumps(json_obj)
        return '/acl', json.dumps(json_obj)


    def parse(  # pylint: disable=too-many-locals
    def parse(  # pylint: disable=too-many-locals
+15 −14
Original line number Original line Diff line number Diff line
@@ -46,12 +46,11 @@ def compose_interface_direction_acl_rules(
        if acl_set is None:
        if acl_set is None:
            MSG = 'Interface({:s})/{:s}/AclSet({:s}) not found'
            MSG = 'Interface({:s})/{:s}/AclSet({:s}) not found'
            raise NotFound(MSG.format(
            raise NotFound(MSG.format(
                str(interface_name), acl_direction_title,
                str(interface_name), acl_direction_title, str(acl_set_name)
                str(acl_set_name)
            ))
            ))


        acl_config_rule = config_rule_from_ietf_acl(
        acl_config_rule = config_rule_from_ietf_acl(
            device_name, interface_name, acl_set
            device_name, interface_name, acl_direction, acl_set
        )
        )
        MSG = 'Adding {:s} ACL Config Rule: {:s}'
        MSG = 'Adding {:s} ACL Config Rule: {:s}'
        LOGGER.info(MSG.format(
        LOGGER.info(MSG.format(
@@ -114,12 +113,14 @@ class Acls(Resource):
            interface_data = interface_name__to__interface_data.get(interface_name)
            interface_data = interface_name__to__interface_data.get(interface_name)
            if interface_data is None: continue
            if interface_data is None: continue


            if 'ingress' in interface_data:
                ingress_acl_config_rules = compose_interface_direction_acl_rules(
                ingress_acl_config_rules = compose_interface_direction_acl_rules(
                    device_name, interface_name, interface_data, AclDirectionEnum.INGRESS,
                    device_name, interface_name, interface_data, AclDirectionEnum.INGRESS,
                    acl_name__to__acl_data
                    acl_name__to__acl_data
                )
                )
                device.device_config.config_rules.extend(ingress_acl_config_rules)
                device.device_config.config_rules.extend(ingress_acl_config_rules)


            if 'egress' in interface_data:
                egress_acl_config_rules = compose_interface_direction_acl_rules(
                egress_acl_config_rules = compose_interface_direction_acl_rules(
                    device_name, interface_name, interface_data, AclDirectionEnum.EGRESS,
                    device_name, interface_name, interface_data, AclDirectionEnum.EGRESS,
                    acl_name__to__acl_data
                    acl_name__to__acl_data
+13 −3
Original line number Original line Diff line number Diff line
@@ -17,10 +17,11 @@ 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, AclDirectionEnum as Proto_AclDirectionEnum




class AclDirectionEnum(Enum):
class AclDirectionEnum(Enum):
    BOTH    = 'both'
    INGRESS = 'ingress'
    INGRESS = 'ingress'
    EGRESS  = 'egress'
    EGRESS  = 'egress'


@@ -132,14 +133,23 @@ TFS_IETF_FORWARDING_ACTION_MAPPING = {




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_direction : AclDirectionEnum,
    acl_set_data : Dict
) -> ConfigRule:
) -> ConfigRule:
    acl_config_rule = ConfigRule()
    acl_config_rule = ConfigRule()
    acl_config_rule.action = ConfigActionEnum.CONFIGACTION_SET
    acl_config_rule.action = ConfigActionEnum.CONFIGACTION_SET

    acl_endpoint_id = acl_config_rule.acl.endpoint_id
    acl_endpoint_id = acl_config_rule.acl.endpoint_id
    acl_endpoint_id.device_id.device_uuid.uuid = device_name
    acl_endpoint_id.device_id.device_uuid.uuid = device_name
    acl_endpoint_id.endpoint_uuid.uuid = endpoint_name
    acl_endpoint_id.endpoint_uuid.uuid = endpoint_name


    if acl_direction == AclDirectionEnum.INGRESS:
        acl_config_rule.acl.direction = Proto_AclDirectionEnum.ACLDIRECTION_INGRESS
    elif acl_direction == AclDirectionEnum.EGRESS:
        acl_config_rule.acl.direction = Proto_AclDirectionEnum.ACLDIRECTION_EGRESS
    else:
        acl_config_rule.acl.direction = Proto_AclDirectionEnum.ACLDIRECTION_BOTH

    acl_name = acl_set_data['name']
    acl_name = acl_set_data['name']
    acl_type = acl_set_data['type']
    acl_type = acl_set_data['type']
    if acl_type.startswith('ietf-access-control-list:'):
    if acl_type.startswith('ietf-access-control-list:'):
+3 −3
Original line number Original line Diff line number Diff line
@@ -31,9 +31,9 @@ RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
    chmod +x /bin/grpc_health_probe
    chmod +x /bin/grpc_health_probe


# Get generic Python packages
# Get generic Python packages
RUN python3 -m pip install --upgrade pip
RUN python3 -m pip install --upgrade 'pip==25.2'
RUN python3 -m pip install --upgrade setuptools wheel
RUN python3 -m pip install --upgrade 'setuptools==79.0.0' 'wheel==0.45.1'
RUN python3 -m pip install --upgrade pip-tools
RUN python3 -m pip install --upgrade 'pip-tools==7.3.0'


# Get common Python packages
# Get common Python packages
# Note: this step enables sharing the previous Docker build steps among all the Python components
# Note: this step enables sharing the previous Docker build steps among all the Python components
Loading