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 Diff line number Diff line
@@ -566,9 +566,16 @@ message ConfigRule_Custom {
  string resource_value = 2;
}

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

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

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

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

import libyang

from common.proto.context_pb2 import AclDirectionEnum
from ._Handler import _Handler
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):
    def get_resource_key(self) -> str:
@@ -59,8 +70,9 @@ class AclHandler(_Handler):
        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']
        #device = resource_value['endpoint_id']['device_id']['device_uuid']['uuid']
        iface = resource_value['endpoint_id']['endpoint_uuid']['uuid']
        direction = resource_value['direction']

        if delete:
            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')
        for entry in rs.get('entries', []):
            seq = int(entry['sequence_id'])
            m_ = entry["match"]
            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")
            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}"]')
@@ -91,9 +103,12 @@ class AclHandler(_Handler):
            y_ipv4.create_path('config/source-address', src_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:
                proto = m_.get("protocol")
                y_trans = y_e.create_path("transport")
                y_trans = y_e.create_path('transport')
                if src_port:
                    y_trans.create_path("config/source-port", int(src_port))
                if dst_port:
@@ -106,14 +121,41 @@ class AclHandler(_Handler):
        # 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)

        if direction in DIRECTION_INGRESS:
            y_ingress = y_intf.create_path('ingress-acl-sets')
            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 = 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)
        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)

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

        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}'
        LOGGER.info(MSG.format(
@@ -114,12 +113,14 @@ class Acls(Resource):
            interface_data = interface_name__to__interface_data.get(interface_name)
            if interface_data is None: continue

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

            if 'egress' in interface_data:
                egress_acl_config_rules = compose_interface_direction_acl_rules(
                    device_name, interface_name, interface_data, AclDirectionEnum.EGRESS,
                    acl_name__to__acl_data
+13 −3
Original line number Diff line number Diff line
@@ -17,10 +17,11 @@ from typing import List, Dict, Optional
from pydantic import BaseModel, Field
from werkzeug.exceptions import NotImplemented
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):
    BOTH    = 'both'
    INGRESS = 'ingress'
    EGRESS  = 'egress'

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


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:
    acl_config_rule = ConfigRule()
    acl_config_rule.action = ConfigActionEnum.CONFIGACTION_SET

    acl_endpoint_id = acl_config_rule.acl.endpoint_id
    acl_endpoint_id.device_id.device_uuid.uuid = device_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_type = acl_set_data['type']
    if acl_type.startswith('ietf-access-control-list:'):
+3 −3
Original line number Diff line number Diff line
@@ -31,9 +31,9 @@ RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
    chmod +x /bin/grpc_health_probe

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

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