Commit dcb44d80 authored by PabloArmingolRobles's avatar PabloArmingolRobles
Browse files

Device component:

- Implemented support for ACLs
- Added suport for Candidate datastore
parent f83b23a5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ RESOURCE_ENDPOINTS = '__endpoints__'
RESOURCE_INTERFACES        = '__interfaces__'
RESOURCE_NETWORK_INSTANCES = '__network_instances__'
RESOURCE_ROUTING_POLICIES  = '__routing_policies__'
RESOURCE_ACL               = '__acl__'

class _Driver:
    def __init__(self, address : str, port : int, **settings) -> None:
+32 −4
Original line number Diff line number Diff line
@@ -31,7 +31,11 @@ from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_su
from .templates import ALL_RESOURCE_KEYS, EMPTY_CONFIG, compose_config, get_filter, parse
from .RetryDecorator import retry

DEBUG_MODE = False

from ncclient import manager


DEBUG_MODE = True
#logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING)
logging.getLogger('apscheduler.executors.default').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
logging.getLogger('apscheduler.scheduler').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR)
@@ -239,11 +243,22 @@ class OpenConfigDriver(_Driver):
                    resource_key,resource_value = resource
                    chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
                    str_config_message = compose_config(resource_key, resource_value)
                    
                    with manager.connect(host=address, port=830, username='admin', password='admin',
                                    hostkey_verify=False, device_params={'name':'huaweiyang'},
                                    look_for_keys=False, allow_agent=False) as m:
                        assert(":candidate" in m.server_capabilities)
                        with m.locked(target='candidate'):
                            m.edit_config(config=str_config_message, default_operation="merge", target="candidate")
                            m.commit()
                            results.append(True)
                    """
                    if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
                    LOGGER.info('[SetConfig] str_config_message[{:d}] = {:s}'.format(
                        len(str_config_message), str(str_config_message)))
                    self.__netconf_handler.edit_config(str_config_message, target='running')
                    results.append(True)
                    """
                except Exception as e: # pylint: disable=broad-except
                    LOGGER.exception('Exception setting {:s}: {:s}'.format(str_resource_name, str(resource)))
                    results.append(e) # if validation fails, store the exception         
@@ -264,11 +279,23 @@ class OpenConfigDriver(_Driver):
                    resource_key,resource_value = resource
                    chk_string(str_resource_name + '.key', resource_key, allow_empty=False)
                    str_config_message = compose_config(resource_key, resource_value, delete=True)
                    
                    with manager.connect(host=address, port=830, username='admin', password='admin',
                                    hostkey_verify=False, device_params={'name':'huaweiyang'},
                                    look_for_keys=False, allow_agent=False) as m:
                        assert(":candidate" in m.server_capabilities)
                        with m.locked(target='candidate'):
                            m.edit_config(config=str_config_message, default_operation="merge", target="candidate")
                            m.commit()
                            results.append(True)
                    
                    """
                    if str_config_message is None: raise UnsupportedResourceKeyException(resource_key)
                    LOGGER.info('[DeleteConfig] str_config_message[{:d}] = {:s}'.format(
                        len(str_config_message), str(str_config_message)))
                    self.__netconf_handler.edit_config(str_config_message, target='running')
                    results.append(True)
                    """
                except Exception as e: # pylint: disable=broad-except
                    LOGGER.exception('Exception deleting {:s}: {:s}'.format(str_resource_name, str(resource_key)))
                    results.append(e) # if validation fails, store the exception
@@ -364,3 +391,4 @@ class OpenConfigDriver(_Driver):
                return
            if sample is None: continue
            yield sample
            
 No newline at end of file
+123 −0
Original line number Diff line number Diff line
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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, lxml.etree as ET
from typing import Any, Dict, List, Tuple
from .Namespace import NAMESPACES
from .Tools import add_value_from_collection, add_value_from_tag

LOGGER = logging.getLogger(__name__)

XPATH_ACL_SET     = "//ocacl:acl/ocacl:acl-sets/ocacl:acl-set"
XPATH_A_ACL_ENTRY = ".//ocacl:acl-entries/ocacl:ecl-entry"
XPATH_A_IPv4      = ".//ocacl:ipv4/ocacl:config"
XPATH_A_TRANSPORT = ".//ocacl:transport/ocacl:config"
XPATH_A_ACTIONS   = ".//ocacl:actions/ocacl:config"

XPATH_INTERFACE         = "//ocacl:acl/ocacl:interfaces/ocacl:interface"
XPATH_I_INGRESS         = ".//ocacl:ingress-acl-sets/ocacl:ingress-acl-set"
XPATH_I_EGRESS          = ".//ocacl:egress-acl-sets/ocacl:egress-acl-set"

def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
    #LOGGER.info('[ACL] xml_data = {:s}'.format(str(ET.tostring(xml_data))))

    response = []
    acl = {}
    
    for xml_acl in xml_data.xpath(XPATH_ACL_SET, namespaces=NAMESPACES):
        #LOGGER.info('xml_acl = {:s}'.format(str(ET.tostring(xml_acl))))

        acl_name = xml_acl.find('ocacl:name', namespaces=NAMESPACES)
        if acl_name is None or acl_name.text is None: continue
        add_value_from_tag(acl, 'name', acl_name)

        acl_type = xml_acl.find('ocacl:type', namespaces=NAMESPACES)
        add_value_from_tag(acl, 'type', acl_type)

        for xml_acl_entries in xml_acl.xpath(XPATH_A_ACL_ENTRY, namespaces=NAMESPACES):

            acl_id = xml_acl_entries.find('ocacl:sequence_id', namespaces=NAMESPACES)
            add_value_from_tag(acl, 'sequence_id', acl_id)
            
            for xml_ipv4 in xml_acl_entries.xpath(XPATH_A_IPv4, namespaces=NAMESPACES):

                ipv4_source = xml_ipv4.find('ocacl:source_address', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'source_address' , ipv4_source)

                ipv4_destination = xml_ipv4.find('ocacl:destination_address', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'destination_address' , ipv4_destination)

                ipv4_protocol = xml_ipv4.find('ocacl:protocol', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'protocol' , ipv4_protocol)
                
                ipv4_dscp = xml_ipv4.find('ocacl:dscp', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'dscp' , ipv4_dscp)
                
                ipv4_hop_limit = xml_ipv4.find('ocacl:hop_limit', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'hop_limit' , ipv4_hop_limit)
                
            for xml_transport in xml_acl_entries.xpath(XPATH_A_TRANSPORT, namespaces=NAMESPACES):

                transport_source = xml_transport.find('ocacl:source_port', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'source_port' ,transport_source)

                transport_destination = xml_transport.find('ocacl:destination_port', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'destination_port' ,transport_destination)
                
                transport_tcp_flags = xml_transport.find('ocacl:tcp_flags', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'tcp_flags' ,transport_tcp_flags)
            
            for xml_action in xml_acl_entries.xpath(XPATH_A_ACTIONS, namespaces=NAMESPACES):

                action = xml_action.find('ocacl:forwarding_action', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'forwarding_action' ,action)
                
                log_action = xml_action.find('ocacl:log_action', namespaces=NAMESPACES)
                add_value_from_tag(acl, 'log_action' ,log_action)

            resource_key =  '/acl/acl-set[{:s}][{:s}]/acl-entry[{:s}]'.format(
                acl['name'], acl['type'], acl['sequence-id'])
            response.append((resource_key,acl))
    
    for xml_interface in xml_data.xpath(XPATH_INTERFACE, namespaces=NAMESPACES):

        interface = {}

        interface_id = xml_interface.find('ocacl:id', namespaces=NAMESPACES)
        add_value_from_tag(interface, 'id' , interface_id)

        for xml_ingress in xml_interface.xpath(XPATH_I_INGRESS, namespaces=NAMESPACES):

            i_name = xml_ingress.find('ocacl:set_name_ingress', namespaces=NAMESPACES)
            add_value_from_tag(interface, 'ingress_set_name' , i_name)

            i_type = xml_ingress.find('ocacl:type_ingress', namespaces=NAMESPACES)
            add_value_from_tag(interface, 'ingress_type' , i_type)
            
            resource_key =  '/acl/interfaces/ingress[{:s}][{:s}]'.format(
                acl['name'], acl['type'])
            response.append((resource_key,interface))

        for xml_egress in xml_interface.xpath(XPATH_I_EGRESS, namespaces=NAMESPACES):

            e_name = xml_egress.find('ocacl:set_name_egress', namespaces=NAMESPACES)
            add_value_from_tag(interface, 'egress_set_name' , e_name)

            e_type = xml_egress.find('ocacl:type_egress', namespaces=NAMESPACES)
            add_value_from_tag(interface, 'egress_type' , e_type)
    
            resource_key =  '/acl/interfaces/egress[{:s}][{:s}]'.format(
                acl['name'], acl['type'])
            response.append((resource_key,interface))
    return response
+3 −0
Original line number Diff line number Diff line
@@ -28,8 +28,11 @@ NAMESPACE_POLICY_TYPES_2 = 'http://openconfig.net/yang/policy_types'
NAMESPACE_ROUTING_POLICY         = 'http://openconfig.net/yang/routing-policy'
NAMESPACE_VLAN                   = 'http://openconfig.net/yang/vlan'

NAMESPACE_ACL                    = 'http://openconfig.net/yang/acl'

NAMESPACES = {
    'nc'   : NAMESPACE_NETCONF,
    'ocacl': NAMESPACE_ACL,
    'ocbp' : NAMESPACE_BGP_POLICY,
    'oci'  : NAMESPACE_INTERFACES,
    'ociip': NAMESPACE_INTERFACES_IP,
+5 −1
Original line number Diff line number Diff line
@@ -16,17 +16,19 @@ import json, logging, lxml.etree as ET, re
from typing import Any, Dict
from jinja2 import Environment, PackageLoader, select_autoescape
from device.service.driver_api._Driver import (
    RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES)
    RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES,RESOURCE_ACL)
from .EndPoints import parse as parse_endpoints
from .Interfaces import parse as parse_interfaces, parse_counters
from .NetworkInstances import parse as parse_network_instances
from .RoutingPolicy import parse as parse_routing_policy
from .Acl import parse as parse_acl

ALL_RESOURCE_KEYS = [
    RESOURCE_ENDPOINTS,
    RESOURCE_INTERFACES,
    RESOURCE_ROUTING_POLICIES,      # routing policies should come before network instances
    RESOURCE_NETWORK_INSTANCES,
    RESOURCE_ACL,
]

RESOURCE_KEY_MAPPINGS = {
@@ -34,6 +36,7 @@ RESOURCE_KEY_MAPPINGS = {
    RESOURCE_INTERFACES       : 'interface',
    RESOURCE_NETWORK_INSTANCES: 'network_instance',
    RESOURCE_ROUTING_POLICIES : 'routing_policy',
    RESOURCE_ACL              : 'acl',
}

RESOURCE_PARSERS = {
@@ -42,6 +45,7 @@ RESOURCE_PARSERS = {
    'network_instance': parse_network_instances,
    'routing_policy'  : parse_routing_policy,
    'interfaces/interface/state/counters': parse_counters,
    'acl'             : parse_acl,
}

LOGGER = logging.getLogger(__name__)
Loading