Commit 45b36504 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

NBI Component - IETF Network plugin:

- Added missing YANG data models
- Updated build-yang-bindings.sh script
- Corrected unitary test test_ietf_network.py
- Reorganized message composer scripts
- Removed unneeded code files
- Implemented composition of nodes, links, termination points, and networks
- Implemented manual fixes for what pyangbind does not support
- Added NameMapping class to cache entity names
parent e9169b92
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.

from common.proto.context_pb2 import Link
from .bindings.networks.network.link import link
from .NameMapping import NameMappings
from .NetworkTypeEnum import NetworkTypeEnum

def compose_link(
    ietf_link_obj : link, link_specs : Link, name_mappings : NameMappings, network_type : NetworkTypeEnum
) -> None:
    src_endpoint_id = link_specs.link_endpoint_ids[0]
    ietf_link_obj.source.source_node = name_mappings.get_device_name(src_endpoint_id.device_id)
    ietf_link_obj.source.source_tp   = name_mappings.get_endpoint_name(src_endpoint_id)

    dst_endpoint_id = link_specs.link_endpoint_ids[-1]
    ietf_link_obj.destination.dest_node = name_mappings.get_device_name(dst_endpoint_id.device_id)
    ietf_link_obj.destination.dest_tp   = name_mappings.get_endpoint_name(dst_endpoint_id)

    ietf_link_obj.te._set_oper_status('up')

    te_link_attrs = ietf_link_obj.te.te_link_attributes
    te_link_attrs.access_type = 'point-to-point'
    te_link_attrs.admin_status = 'up'
    te_link_attrs.name = link_specs.name

    if network_type == NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY:
        link_total_cap_kbps = link_specs.attributes.total_capacity_gbps * 1e6
        link_used_cap_kbps  = link_specs.attributes.used_capacity_gbps * 1e6
        link_avail_cap_kbps = link_total_cap_kbps - link_used_cap_kbps

        te_link_attrs.max_link_bandwidth.te_bandwidth.eth_bandwidth = link_total_cap_kbps
        unresv_bw = te_link_attrs.unreserved_bandwidth.add(7)
        unresv_bw.te_bandwidth.eth_bandwidth = link_avail_cap_kbps
    elif network_type == NetworkTypeEnum.TE_OTN_TOPOLOGY:
        te_link_attrs.te_delay_metric = 1
        oduitem = te_link_attrs.max_link_bandwidth.te_bandwidth.otn.odulist.add('ietf-layer1-types:ODU0')
        oduitem.ts_number = 80

        unresv_bw = te_link_attrs.unreserved_bandwidth.add(7)
        oduitem = unresv_bw.te_bandwidth.otn.odulist.add('ietf-layer1-types:ODU0')
        oduitem.ts_number = 80
+68 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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, re
from common.proto.context_pb2 import TopologyDetails
from nbi.service.rest_server.nbi_plugins.ietf_network.NetworkTypeEnum import NetworkTypeEnum, get_network_topology_type
from .bindings.networks.network import network
from .NameMapping import NameMappings
from .ComposeNode import compose_node
from .ComposeLink import compose_link

LOGGER = logging.getLogger(__name__)

def compose_network(ietf_network_obj : network, topology_details : TopologyDetails) -> None:
    ietf_network_obj.te.name = 'Huawei-Network'

    topology_name = topology_details.name
    match = re.match(r'providerId\-([^\-]*)-clientId-([^\-]*)-topologyId-([^\-]*)', topology_name)
    if match is not None:
        provider_id, client_id, topology_id = match.groups()
        ietf_network_obj.te_topology_identifier.provider_id = int(provider_id)
        ietf_network_obj.te_topology_identifier.client_id   = int(client_id)
        ietf_network_obj.te_topology_identifier.topology_id = str(topology_id)
    else:
        ietf_network_obj.te_topology_identifier.provider_id = 10
        ietf_network_obj.te_topology_identifier.client_id   = 0
        ietf_network_obj.te_topology_identifier.topology_id = '0'

    ietf_network_obj.network_types.te_topology._set_present()
    # TODO: resolve setting of otn_topology/eth_tran_topology network type; not working in bindings.
    # See "../ManualFixes.py".
    topology_id = ietf_network_obj.te_topology_identifier.topology_id
    topology_id = {
        '1': NetworkTypeEnum.TE_OTN_TOPOLOGY.value,
        '2': NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY.value,
    }.get(topology_id, topology_id)
    network_type = get_network_topology_type(topology_id)
    if network_type == NetworkTypeEnum.TE_OTN_TOPOLOGY:
        #ietf_network_obj.network_types.te_topology.otn_topology._set_present()
        pass
    elif network_type == NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY:
        #ietf_network_obj.network_types.te_topology.eth_tran_topology._set_present()
        pass
    else:
        raise Exception('Unsupported TopologyId({:s})'.format(str(topology_id)))

    name_mappings = NameMappings()

    for device in topology_details.devices:
        device_name = device.name
        ietf_node_obj = ietf_network_obj.node.add(device_name)
        compose_node(ietf_node_obj, device, name_mappings, network_type)

    for link in topology_details.links:
        link_name = link.name
        ietf_link_obj = ietf_network_obj.link.add(link_name)
        compose_link(ietf_link_obj, link, name_mappings, network_type)
+46 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.

from common.proto.context_pb2 import Device
from .bindings.networks.network.node import node
from .ComposeTermPoint import compose_term_point
from .NameMapping import NameMappings
from .NetworkTypeEnum import NetworkTypeEnum

NODE_NAME_MAPPINGS = {
    '10.0.10.1': 'OA',
    '10.0.20.1': 'P',
    '10.0.30.1': 'OE',
    '10.0.40.1': 'P',
}

def compose_node(
    ietf_node_obj : node, device : Device, name_mappings : NameMappings, network_type : NetworkTypeEnum
) -> None:
    name_mappings.store_device_name(device)

    device_name = device.name
    ietf_node_obj.te_node_id = device_name

    ietf_node_obj.te._set_oper_status('up')
    ietf_node_obj.te.te_node_attributes.admin_status = 'up'
    ietf_node_obj.te.te_node_attributes.name = NODE_NAME_MAPPINGS.get(device_name, device_name)

    for endpoint in device.device_endpoints:
        endpoint_name = endpoint.name
        if endpoint_name == 'mgmt': continue
        if network_type == NetworkTypeEnum.TE_OTN_TOPOLOGY and endpoint.endpoint_type != 'optical': continue

        ietf_tp_obj = ietf_node_obj.termination_point.add(endpoint_name)
        compose_term_point(ietf_tp_obj, device, endpoint, name_mappings, network_type)
+74 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.

from common.proto.context_pb2 import Device, EndPoint
from .bindings.networks.network.node.termination_point import termination_point
from .NameMapping import NameMappings
from .NetworkTypeEnum import NetworkTypeEnum

TP_NAME_MAPPINGS = {
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.10.1', '200'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.10.1', '500'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.10.1', '501'): '1-1-1-1-1',

    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.20.1', '500'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.20.1', '501'): '1-1-1-1-1',

    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.30.1', '200'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.30.1', '500'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.30.1', '501'): '1-1-1-1-1',

    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.40.1', '500'): '1-1-1-1-1',
    (NetworkTypeEnum.TE_OTN_TOPOLOGY, '10.0.40.1', '501'): '1-1-1-1-1',
}

def compose_term_point(
    ietf_tp_obj : termination_point, device : Device, endpoint : EndPoint, name_mappings : NameMappings,
    network_type : NetworkTypeEnum
) -> None:
    name_mappings.store_endpoint_name(device, endpoint)

    device_name = device.name
    endpoint_name = endpoint.name

    ietf_tp_obj.te_tp_id = endpoint_name

    ietf_tp_obj.te._set_oper_status('up')
    ietf_tp_obj.te.admin_status = 'up'
    ietf_tp_obj.te.name = TP_NAME_MAPPINGS.get((network_type, device_name, endpoint_name), endpoint_name)

    if network_type == NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY:
        ietf_if_sw_cap = ietf_tp_obj.te.interface_switching_capability.add(
            'ietf-te-types:switching-l2sc ietf-te-types:lsp-encoding-ethernet'
        )
        ietf_max_lsp_bw = ietf_if_sw_cap.max_lsp_bandwidth.add('7')
        ietf_max_lsp_bw.te_bandwidth.eth_bandwidth = 10_000_000 # Kbps

        #ietf_tp_obj.eth_svc.client_facing = True

        ietf_tp_obj.eth_svc.supported_classification.port_classification = True
        outer_tag = ietf_tp_obj.eth_svc.supported_classification.vlan_classification.outer_tag
        outer_tag.supported_tag_types.append('ietf-eth-tran-types:classify-c-vlan')
        outer_tag.supported_tag_types.append('ietf-eth-tran-types:classify-s-vlan')
        outer_tag.vlan_bundling = False
        outer_tag.vlan_range = '1-4094'

    elif network_type == NetworkTypeEnum.TE_OTN_TOPOLOGY:
        #ietf_tp_obj.te.client_svc.client_facing = False

        ietf_if_sw_cap = ietf_tp_obj.te.interface_switching_capability.add(
            'ietf-te-types:switching-otn ietf-te-types:lsp-encoding-oduk'
        )
        ietf_max_lsp_bw = ietf_if_sw_cap.max_lsp_bandwidth.add('7')
        ietf_max_lsp_bw.te_bandwidth.otn.odu_type = 'ietf-layer1-types:ODU4'
+57 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.

from typing import Dict

from .NetworkTypeEnum import NetworkTypeEnum

def manual_fixes(json_response : Dict) -> None:
    # TODO: workaround to set network types manually. Currently does not work; refine bindings.
    # Seems limitation of pyangbind using multiple augmentations and nested empty containers.
    for json_network in json_response['ietf-network:networks']['network']:
        net_te_topo_id = json_network.get('ietf-te-topology:te-topology-identifier', {}).get('topology-id')
        if net_te_topo_id is None: continue
        net_te_topo_type = json_network['network-types']['ietf-te-topology:te-topology']
        if net_te_topo_id == '1':
            network_type = NetworkTypeEnum.TE_OTN_TOPOLOGY
            net_te_topo_type['ietf-otn-topology:otn-topology'] = {}
        elif net_te_topo_id == '2':
            network_type = NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY
            net_te_topo_type['ietf-eth-te-topology:eth-tran-topology'] = {}
        else:
            network_type = None

        # Fix value type of 
        for json_node in json_network.get('node', []):
            for json_tp in json_node.get('ietf-network-topology:termination-point', []):

                if json_node['node-id'] in {'10.0.10.1', '10.0.30.1'}:
                    if network_type == NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY:
                        if 'ietf-eth-te-topology:eth-svc' in json_tp:
                            json_tp['ietf-eth-te-topology:eth-svc']['client-facing'] = True

                json_tp_te = json_tp.get('ietf-te-topology:te', {})

                if network_type == NetworkTypeEnum.TE_OTN_TOPOLOGY:
                    json_tp_te_cs = json_tp_te.setdefault('ietf-otn-topology:client-svc', {})
                    json_tp_te_cs['client-facing'] = False

                for json_if_sw_cap in json_tp_te.get('interface-switching-capability', []):
                    for json_max_lsp_bandwidth in json_if_sw_cap.get('max-lsp-bandwidth', []):
                        json_te_bw = json_max_lsp_bandwidth.get('te-bandwidth', {})
                        if network_type == NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY:
                            if 'ietf-eth-te-topology:eth-bandwidth' in json_te_bw:
                                eth_bw = json_te_bw['ietf-eth-te-topology:eth-bandwidth']
                                if isinstance(eth_bw, str):
                                    json_te_bw['ietf-eth-te-topology:eth-bandwidth'] = int(eth_bw)
Loading