From 7db3ffe74069ddd7da8a908ec353a84b5645810c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 22 Dec 2023 12:47:49 +0000 Subject: [PATCH 1/3] NBI Component - IETF L3VPN: - Added initial implementation and unitary tests - Added scripts to test manually - Integrated with CI/CD pipeline - Added requirements - Added README file describing how to build and install libyang from source - Updated Dockerfile --- scripts/run_tests_locally-nbi-ietf-l3vpn.sh | 25 + src/nbi/.gitlab-ci.yml | 1 + src/nbi/Dockerfile | 15 + src/nbi/README.md | 32 + src/nbi/requirements.in | 2 + src/nbi/service/__main__.py | 2 + .../nbi_plugins/ietf_l3vpn/Handlers.py | 191 ++ .../nbi_plugins/ietf_l3vpn/L3VPN_Service.py | 78 + .../nbi_plugins/ietf_l3vpn/L3VPN_Services.py | 53 + .../ietf_l3vpn/L3VPN_SiteNetworkAccesses.py | 58 + .../nbi_plugins/ietf_l3vpn/YangValidator.py | 36 + .../nbi_plugins/ietf_l3vpn/__init__.py | 41 + .../yang/ietf-inet-types@2013-07-15.yang | 459 +++ .../yang/ietf-l3vpn-svc@2018-01-19.yang | 2856 +++++++++++++++++ .../yang/ietf-netconf-acm@2018-02-14.yang | 468 +++ .../yang/ietf-yang-types@2013-07-15.yang | 475 +++ src/nbi/tests/PrepareTestScenario.py | 2 + src/nbi/tests/data/ietf_l3vpn_req_svc1.json | 231 ++ src/nbi/tests/data/ietf_l3vpn_req_svc2.json | 231 ++ src/nbi/tests/test_ietf_l3vpn.py | 113 + 20 files changed, 5369 insertions(+) create mode 100755 scripts/run_tests_locally-nbi-ietf-l3vpn.sh create mode 100644 src/nbi/README.md create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_SiteNetworkAccesses.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/YangValidator.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/__init__.py create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-inet-types@2013-07-15.yang create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-l3vpn-svc@2018-01-19.yang create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-netconf-acm@2018-02-14.yang create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-yang-types@2013-07-15.yang create mode 100644 src/nbi/tests/data/ietf_l3vpn_req_svc1.json create mode 100644 src/nbi/tests/data/ietf_l3vpn_req_svc2.json create mode 100644 src/nbi/tests/test_ietf_l3vpn.py diff --git a/scripts/run_tests_locally-nbi-ietf-l3vpn.sh b/scripts/run_tests_locally-nbi-ietf-l3vpn.sh new file mode 100755 index 000000000..0bd133e16 --- /dev/null +++ b/scripts/run_tests_locally-nbi-ietf-l3vpn.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc + +# Run unitary tests and analyze coverage of code at same time +# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_ietf_l3vpn.py diff --git a/src/nbi/.gitlab-ci.yml b/src/nbi/.gitlab-ci.yml index 47bd5bcbc..8f7f4d6f4 100644 --- a/src/nbi/.gitlab-ci.yml +++ b/src/nbi/.gitlab-ci.yml @@ -68,6 +68,7 @@ unit_test nbi: - docker logs $IMAGE_NAME - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_l2vpn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_l2vpn.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_network.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_network.xml" + - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_l3vpn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_l3vpn.xml" - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: diff --git a/src/nbi/Dockerfile b/src/nbi/Dockerfile index 31b826e01..eda4d2956 100644 --- a/src/nbi/Dockerfile +++ b/src/nbi/Dockerfile @@ -53,6 +53,21 @@ RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto RUN rm *.proto RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \; +# Download, build and install libyang. Note that APT package is outdated +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install build-essential cmake libpcre2-dev python3-dev python3-cffi && \ + rm -rf /var/lib/apt/lists/* +RUN mkdir -p /var/libyang +RUN git clone https://github.com/CESNET/libyang.git /var/libyang +RUN mkdir -p /var/libyang/build +WORKDIR /var/libyang/build +RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. +RUN make +RUN make install +RUN ldconfig + # Create component sub-folders, get specific Python packages RUN mkdir -p /var/teraflow/nbi WORKDIR /var/teraflow/nbi diff --git a/src/nbi/README.md b/src/nbi/README.md new file mode 100644 index 000000000..c5ed72704 --- /dev/null +++ b/src/nbi/README.md @@ -0,0 +1,32 @@ +# NBI Component + +The NBI component uses libyang to validate and process messages. Follow instructions below to install it. + +## Install libyang +- Ref: https://github.com/CESNET/libyang +- Ref: https://github.com/CESNET/libyang-python/ + +__NOTE__: APT package is extremely outdated and does not work for our purposes. + +### Build Requisites +```bash +sudo apt-get install build-essential cmake libpcre2-dev +sudo apt-get install python3-dev gcc python3-cffi +``` + +### Build from source +```bash +mkdir ~/tfs-ctrl/libyang +git clone https://github.com/CESNET/libyang.git ~/tfs-ctrl/libyang +mkdir ~/tfs-ctrl/libyang/build +cd ~/tfs-ctrl/libyang/build +cmake -D CMAKE_BUILD_TYPE:String="Release" .. +make +sudo make install +sudo ldconfig +``` + +### Install Python bindings +```bash +pip install libyang==2.8.0 +``` diff --git a/src/nbi/requirements.in b/src/nbi/requirements.in index a1da58d01..52094c61f 100644 --- a/src/nbi/requirements.in +++ b/src/nbi/requirements.in @@ -17,6 +17,8 @@ Flask==2.1.3 Flask-HTTPAuth==4.5.0 Flask-RESTful==0.3.9 jsonschema==4.4.0 +libyang==2.8.0 +netaddr==0.9.0 pyang==2.6.0 git+https://github.com/robshakir/pyangbind.git requests==2.27.1 diff --git a/src/nbi/service/__main__.py b/src/nbi/service/__main__.py index 07cc9193d..8834e45a2 100644 --- a/src/nbi/service/__main__.py +++ b/src/nbi/service/__main__.py @@ -23,6 +23,7 @@ from .rest_server.RestServer import RestServer from .rest_server.nbi_plugins.debug_api import register_debug_api from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn +from .rest_server.nbi_plugins.ietf_l3vpn import register_ietf_l3vpn from .rest_server.nbi_plugins.ietf_network import register_ietf_network from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss @@ -64,6 +65,7 @@ def main(): register_debug_api(rest_server) register_etsi_bwm_api(rest_server) register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint + register_ietf_l3vpn(rest_server) # Registering L3VPN entrypoint register_ietf_network(rest_server) register_ietf_nss(rest_server) # Registering NSS entrypoint rest_server.start() diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py new file mode 100644 index 000000000..c2d5d17ce --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py @@ -0,0 +1,191 @@ +# 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, netaddr +from typing import Dict, List, Optional, Tuple +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import Service, ServiceStatusEnum +from common.tools.context_queries.Service import get_service_by_uuid +from common.tools.grpc.ConfigRules import update_config_rule_custom +from common.tools.grpc.Constraints import ( + update_constraint_endpoint_location, update_constraint_sla_availability, update_constraint_sla_capacity, + update_constraint_sla_latency +) +from common.tools.grpc.EndPointIds import update_endpoint_ids +from context.client.ContextClient import ContextClient +from service.client.ServiceClient import ServiceClient + +LOGGER = logging.getLogger(__name__) + +def create_service( + service_uuid : str, context_uuid : Optional[str] = DEFAULT_CONTEXT_NAME +) -> Optional[Exception]: + # pylint: disable=no-member + service_request = Service() + service_request.service_id.context_id.context_uuid.uuid = context_uuid + service_request.service_id.service_uuid.uuid = service_uuid + service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + + try: + service_client = ServiceClient() + service_client.CreateService(service_request) + return None + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unhandled exception creating Service') + return e + +def process_vpn_service( + vpn_service : Dict, errors : List[Dict] +) -> None: + vpn_id = vpn_service['vpn-id'] + exc = create_service(vpn_id) + if exc is not None: errors.append({'error': str(exc)}) + +def update_service_endpoint( + service_uuid : str, site_id : str, device_uuid : str, endpoint_uuid : str, + vlan_tag : int, ipv4_address : str, ipv4_prefix_length : int, + capacity_gbps : Optional[float] = None, e2e_latency_ms : Optional[float] = None, + availability : Optional[float] = None, mtu : Optional[int] = None, + context_uuid : Optional[str] = DEFAULT_CONTEXT_NAME, +) -> Optional[Exception]: + context_client = ContextClient() + service = get_service_by_uuid(context_client, service_uuid, context_uuid=context_uuid, rw_copy=True) + if service is None: raise Exception('VPN({:s}) not found in database'.format(str(service_uuid))) + + endpoint_ids = service.service_endpoint_ids + endpoint_id = update_endpoint_ids(endpoint_ids, device_uuid, endpoint_uuid) + + constraints = service.service_constraints + update_constraint_endpoint_location(constraints, endpoint_id, region=site_id) + if capacity_gbps is not None: update_constraint_sla_capacity (constraints, capacity_gbps) + if e2e_latency_ms is not None: update_constraint_sla_latency (constraints, e2e_latency_ms) + if availability is not None: update_constraint_sla_availability(constraints, 1, True, availability) + + config_rules = service.service_config.config_rules + + service_settings_key = '/settings' + service_settings = {'address_families': (['IPV4'], True)} + if mtu is not None: service_settings['mtu'] = (mtu, True) + update_config_rule_custom(config_rules, service_settings_key, service_settings) + + ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings' + endpoint_settings_key = ENDPOINT_SETTINGS_KEY.format(device_uuid, endpoint_uuid, vlan_tag) + field_updates = {} + if vlan_tag is not None: field_updates['vlan_tag' ] = (vlan_tag, True) + if ipv4_address is not None: field_updates['ip_address' ] = (ipv4_address, True) + if ipv4_prefix_length is not None: field_updates['prefix_length'] = (ipv4_prefix_length, True) + update_config_rule_custom(config_rules, endpoint_settings_key, field_updates) + + try: + service_client = ServiceClient() + service_client.UpdateService(service) + return None + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unhandled exception updating Service') + return e + +def process_site_network_access( + site_id : str, network_access : Dict, site_static_routing : Dict[Tuple[str, str], str], errors : List[Dict] +) -> None: + endpoint_uuid = network_access['site-network-access-id'] + + if network_access['type'] != 'ietf-l3vpn-svc:multipoint': + MSG = 'Site Network Access Type: {:s}' + raise NotImplementedError(MSG.format(str(network_access['type']))) + + device_uuid = network_access['device-reference'] + service_uuid = network_access['vpn-attachment']['vpn-id'] + + access_role : str = network_access['vpn-attachment']['site-role'] + access_role = access_role.replace('ietf-l3vpn-svc:', '').replace('-role', '') # hub/spoke + + ipv4_allocation = network_access['ip-connection']['ipv4'] + if ipv4_allocation['address-allocation-type'] != 'ietf-l3vpn-svc:static-address': + MSG = 'Site Network Access IPv4 Allocation Type: {:s}' + raise NotImplementedError(MSG.format(str(ipv4_allocation['address-allocation-type']))) + ipv4_provider_address = ipv4_allocation['provider-address'] + #ipv4_customer_address = ipv4_allocation['customer-address'] + ipv4_prefix_length = ipv4_allocation['prefix-length' ] + + vlan_tag = None + ipv4_provider_range = netaddr.IPNetwork('{:s}/{:d}'.format(ipv4_provider_address, ipv4_prefix_length)) + for (_, _, lan_tag), next_hop in site_static_routing.items(): + ip_next_hop = netaddr.IPAddress(next_hop) + if ip_next_hop not in ipv4_provider_range: continue + vlan_tag = lan_tag + break + + ## network_access_static_routing: (lan-range, lan-prefix-len, lan-tag) => next-hop + #network_access_static_routing : Dict[Tuple[str, str], str] = {} + #for rt_proto in network_access['routing-protocols']['routing-protocol']: + # if rt_proto['type'] != 'ietf-l3vpn-svc:static': + # MSG = 'Site Network Access Routing Protocol Type: {:s}' + # raise NotImplementedError(MSG.format(str(rt_proto['type']))) + # for ipv4_rt in rt_proto['static']['cascaded-lan-prefixes']['ipv4-lan-prefixes']: + # lan_range, lan_prefix = ipv4_rt['lan'].split('/') + # lan_prefix = int(lan_prefix) + # lan_tag = int(ipv4_rt['lan-tag'].replace('vlan', '')) + # next_hop = ipv4_rt['next-hop'] + # network_access_static_routing[(lan_range, lan_prefix, lan_tag)] = next_hop + + service_mtu = network_access['service']['svc-mtu'] + service_input_bandwidth = network_access['service']['svc-input-bandwidth'] + service_output_bandwidth = network_access['service']['svc-output-bandwidth'] + service_bandwidth_bps = max(service_input_bandwidth, service_output_bandwidth) + service_bandwidth_gbps = service_bandwidth_bps / 1.e9 + + max_e2e_latency_ms = None + availability = None + for qos_profile_class in network_access['service']['qos']['qos-profile']['classes']['class']: + if qos_profile_class['class-id'] != 'qos-realtime': + MSG = 'Site Network Access QoS Class Id: {:s}' + raise NotImplementedError(MSG.format(str(qos_profile_class['class-id']))) + + if qos_profile_class['direction'] != 'ietf-l3vpn-svc:both': + MSG = 'Site Network Access QoS Class Direction: {:s}' + raise NotImplementedError(MSG.format(str(qos_profile_class['direction']))) + + max_e2e_latency_ms = qos_profile_class['latency']['latency-boundary'] + availability = qos_profile_class['bandwidth']['guaranteed-bw-percent'] + + exc = update_service_endpoint( + service_uuid, site_id, device_uuid, endpoint_uuid, vlan_tag, ipv4_provider_address, ipv4_prefix_length, + capacity_gbps=service_bandwidth_gbps, e2e_latency_ms=max_e2e_latency_ms, availability=availability, + mtu=service_mtu + ) + if exc is not None: errors.append({'error': str(exc)}) + +def process_site(site : Dict, errors : List[Dict]) -> None: + site_id = site['site-id'] + + if site['management']['type'] != 'ietf-l3vpn-svc:provider-managed': + MSG = 'Site Management Type: {:s}' + raise NotImplementedError(MSG.format(str(site['management']['type']))) + + # site_static_routing: (lan-range, lan-prefix-len, lan-tag) => next-hop + site_static_routing : Dict[Tuple[str, str], str] = {} + for rt_proto in site['routing-protocols']['routing-protocol']: + if rt_proto['type'] != 'ietf-l3vpn-svc:static': + MSG = 'Site Routing Protocol Type: {:s}' + raise NotImplementedError(MSG.format(str(rt_proto['type']))) + for ipv4_rt in rt_proto['static']['cascaded-lan-prefixes']['ipv4-lan-prefixes']: + lan_range, lan_prefix = ipv4_rt['lan'].split('/') + lan_prefix = int(lan_prefix) + lan_tag = int(ipv4_rt['lan-tag'].replace('vlan', '')) + next_hop = ipv4_rt['next-hop'] + site_static_routing[(lan_range, lan_prefix, lan_tag)] = next_hop + + network_accesses : List[Dict] = site['site-network-accesses']['site-network-access'] + for network_access in network_accesses: + process_site_network_access(site_id, network_access, site_static_routing, errors) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py new file mode 100644 index 000000000..470ff0add --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py @@ -0,0 +1,78 @@ +# 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 +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from common.proto.context_pb2 import ServiceStatusEnum +from common.tools.context_queries.Service import get_service_by_uuid +from context.client.ContextClient import ContextClient +from service.client.ServiceClient import ServiceClient +from ..tools.Authentication import HTTP_AUTH +from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR + +LOGGER = logging.getLogger(__name__) + +class L3VPN_Service(Resource): + @HTTP_AUTH.login_required + def get(self, vpn_id : str): + LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id))) + LOGGER.debug('Request: {:s}'.format(str(request))) + + try: + context_client = ContextClient() + + target = get_service_by_uuid(context_client, vpn_id, rw_copy=True) + if target is None: + raise Exception('VPN({:s}) not found in database'.format(str(vpn_id))) + + service_ids = {target.service_id.service_uuid.uuid, target.name} # pylint: disable=no-member + if vpn_id not in service_ids: + raise Exception('Service retrieval failed. Wrong Service Id was returned') + + service_ready_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE + service_status = target.service_status.service_status # pylint: disable=no-member + response = jsonify({}) + response.status_code = HTTP_OK if service_status == service_ready_status else HTTP_GATEWAYTIMEOUT + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Something went wrong Retrieving VPN({:s})'.format(str(vpn_id))) + response = jsonify({'error': str(e)}) + response.status_code = HTTP_SERVERERROR + return response + + @HTTP_AUTH.login_required + def delete(self, vpn_id : str): + LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id))) + LOGGER.debug('Request: {:s}'.format(str(request))) + + try: + context_client = ContextClient() + + target = get_service_by_uuid(context_client, vpn_id) + if target is None: + LOGGER.warning('VPN({:s}) not found in database. Nothing done.'.format(str(vpn_id))) + else: + service_ids = {target.service_id.service_uuid.uuid, target.name} # pylint: disable=no-member + if vpn_id not in service_ids: + raise Exception('Service retrieval failed. Wrong Service Id was returned') + service_client = ServiceClient() + service_client.DeleteService(target.service_id) + response = jsonify({}) + response.status_code = HTTP_NOCONTENT + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Something went wrong Deleting VPN({:s})'.format(str(vpn_id))) + response = jsonify({'error': str(e)}) + response.status_code = HTTP_SERVERERROR + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py new file mode 100644 index 000000000..13d5c5324 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py @@ -0,0 +1,53 @@ +# 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 +from typing import Dict +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType +from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR +from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH +from .Handlers import process_site, process_vpn_service +from .YangValidator import YangValidator + +LOGGER = logging.getLogger(__name__) + +class L3VPN_Services(Resource): + @HTTP_AUTH.login_required + def get(self): + return {} + + @HTTP_AUTH.login_required + def post(self): + if not request.is_json: raise UnsupportedMediaType('JSON payload is required') + request_data : Dict = request.json + LOGGER.debug('Request: {:s}'.format(str(request_data))) + + yang_validator = YangValidator('ietf-l3vpn-svc') + request_data = yang_validator.parse_to_dict(request_data) + yang_validator.destroy() + + errors = [] + + for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']: + process_vpn_service(vpn_service, errors) + + for site in request_data['l3vpn-svc']['sites']['site']: + process_site(site, errors) + + response = jsonify(errors) + response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_SiteNetworkAccesses.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_SiteNetworkAccesses.py new file mode 100644 index 000000000..cd0334789 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_SiteNetworkAccesses.py @@ -0,0 +1,58 @@ +# 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 +from typing import Dict +from flask import request +from flask.json import jsonify +from flask.wrappers import Response +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType +from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH +from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR +from .Handlers import process_site_network_access +from .YangValidator import YangValidator + +LOGGER = logging.getLogger(__name__) + +def process_site_network_accesses(site_id : str) -> Response: + if not request.is_json: raise UnsupportedMediaType('JSON payload is required') + request_data : Dict = request.json + LOGGER.debug('Site_Id: {:s}'.format(str(site_id))) + LOGGER.debug('Request: {:s}'.format(str(request_data))) + + yang_validator = YangValidator('ietf-l3vpn-svc') + request_data = yang_validator.parse_to_dict(request_data) + yang_validator.destroy() + + # TODO: retrieve site static routing from service + site_static_routing = dict() + + errors = [] + for network_access in request_data['site-network-accesses']['site-network-access']: + exc = process_site_network_access(site_id, network_access, site_static_routing, errors) + if exc is not None: errors.append({'error': str(exc)}) + + response = jsonify(errors) + response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR + return response + +class L3VPN_SiteNetworkAccesses(Resource): + @HTTP_AUTH.login_required + def post(self, site_id : str): + return process_site_network_accesses(site_id) + + @HTTP_AUTH.login_required + def put(self, site_id : str): + return process_site_network_accesses(site_id) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/YangValidator.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/YangValidator.py new file mode 100644 index 000000000..d267640dd --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/YangValidator.py @@ -0,0 +1,36 @@ +# 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 libyang, os +from typing import Dict, Optional + +YANG_DIR = os.path.join(os.path.dirname(__file__), 'yang') + +class YangValidator: + def __init__(self, module_name : str) -> None: + self._yang_context = libyang.Context(YANG_DIR) + self._yang_module = self._yang_context.load_module(module_name) + self._yang_module.feature_enable_all() + + def parse_to_dict(self, message : Dict) -> Dict: + dnode : Optional[libyang.DNode] = self._yang_module.parse_data_dict( + message, validate_present=True, validate=True, strict=True + ) + if dnode is None: raise Exception('Unable to parse Message({:s})'.format(str(message))) + message = dnode.print_dict() + dnode.free() + return message + + def destroy(self) -> None: + self._yang_context.destroy() diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/__init__.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/__init__.py new file mode 100644 index 000000000..454e10b84 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/__init__.py @@ -0,0 +1,41 @@ +# 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. + +# RFC 8299 - YANG Data Model for L3VPN Service Delivery +# Ref: https://datatracker.ietf.org/doc/rfc8299 + +from flask_restful import Resource +from nbi.service.rest_server.RestServer import RestServer +from .L3VPN_Services import L3VPN_Services +from .L3VPN_Service import L3VPN_Service +from .L3VPN_SiteNetworkAccesses import L3VPN_SiteNetworkAccesses + +URL_PREFIX = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc' + +def _add_resource(rest_server : RestServer, resource : Resource, *urls, **kwargs): + urls = [(URL_PREFIX + url) for url in urls] + rest_server.add_resource(resource, *urls, **kwargs) + +def register_ietf_l3vpn(rest_server : RestServer): + _add_resource(rest_server, L3VPN_Services, + '/vpn-services', + ) + _add_resource(rest_server, L3VPN_Service, + '/vpn-services/vpn-service=', + '/vpn-services/vpn-service=/', + ) + _add_resource(rest_server, L3VPN_SiteNetworkAccesses, + '/sites/site=/site-network-accesses', + '/sites/site=/site-network-accesses/', + ) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-inet-types@2013-07-15.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-inet-types@2013-07-15.yang new file mode 100644 index 000000000..790bafc31 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-inet-types@2013-07-15.yang @@ -0,0 +1,459 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - ipv6-address-no-zone"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of types related to protocol fields ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet + protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The ipv6-flow-label type represents the flow identifier or Flow + Label in an IPv6 packet header that may be used to + discriminate traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport-layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from . + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of types related to autonomous systems ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASes. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4001: Textual Conventions for Internet Network Addresses + RFC 6793: BGP Support for Four-Octet Autonomous System (AS) + Number Space"; + } + + /*** collection of types related to IP addresses and hostnames ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representation + implies the IP version. This type supports scoped addresses + by allowing zone identifiers in the address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the textual + representation defined in Section 4 of RFC 5952. The + canonical format for the zone index is the numerical + format as described in Section 11.2 of RFC 4007."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-no-zone { + type union { + type inet:ipv4-address-no-zone; + type inet:ipv6-address-no-zone; + } + description + "The ip-address-no-zone type represents an IP address and is + IP version neutral. The format of the textual representation + implies the IP version. This type does not support scoped + addresses since it does not allow zone identifiers in the + address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address-no-zone { + type inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, the IPv6 address is represented + as defined in Section 4 of RFC 5952."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern + '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitly or may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be A-labels as per RFC 5890."; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-l3vpn-svc@2018-01-19.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-l3vpn-svc@2018-01-19.yang new file mode 100644 index 000000000..e05e33722 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-l3vpn-svc@2018-01-19.yang @@ -0,0 +1,2856 @@ + module ietf-l3vpn-svc { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-l3vpn-svc"; + prefix l3vpn-svc; + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + import ietf-netconf-acm { + prefix nacm; + } + organization + "IETF L3SM Working Group"; + contact + "WG List: + Editor: + L3SM WG + Chairs: + Adrian Farrel, Qin Wu + "; + description + "This YANG module defines a generic service configuration + model for Layer 3 VPNs. This model is common across all + vendor implementations. + + + + + Copyright (c) 2018 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8299; see + the RFC itself for full legal notices."; + + revision 2018-01-19 { + description + "Revision of RFC 8049 to fix implementation issues."; + reference + "RFC 8299"; + } + revision 2017-01-27 { + description + "Initial document."; + reference + "RFC 8049."; + } + /* Features */ + feature cloud-access { + description + "Allows the VPN to connect to a CSP."; + } + feature multicast { + description + "Enables multicast capabilities in a VPN."; + } + feature ipv4 { + description + "Enables IPv4 support in a VPN."; + } + feature ipv6 { + description + "Enables IPv6 support in a VPN."; + } + feature lan-tag { + description + "Enables LAN Tag support in a VPN Policy filter."; + } + feature carrierscarrier { + description + + "Enables support of CsC."; + } + feature extranet-vpn { + description + "Enables support of extranet VPNs."; + } + feature site-diversity { + description + "Enables support of site diversity constraints."; + } + feature encryption { + description + "Enables support of encryption."; + } + feature qos { + description + "Enables support of classes of services."; + } + feature qos-custom { + description + "Enables support of the custom QoS profile."; + } + feature rtg-bgp { + description + "Enables support of the BGP routing protocol."; + } + feature rtg-rip { + description + "Enables support of the RIP routing protocol."; + } + feature rtg-ospf { + description + "Enables support of the OSPF routing protocol."; + } + feature rtg-ospf-sham-link { + description + "Enables support of OSPF sham links."; + } + feature rtg-vrrp { + description + "Enables support of the VRRP routing protocol."; + } + feature fast-reroute { + description + "Enables support of Fast Reroute."; + } + feature bfd { + description + + "Enables support of BFD."; + } + feature always-on { + description + "Enables support of the 'always-on' access constraint."; + } + feature requested-type { + description + "Enables support of the 'requested-type' access constraint."; + } + feature bearer-reference { + description + "Enables support of the 'bearer-reference' access constraint."; + } + feature target-sites { + description + "Enables support of the 'target-sites' match flow parameter."; + } + /* Typedefs */ + typedef svc-id { + type string; + description + "Defines a type of service component identifier."; + } + typedef template-id { + type string; + description + "Defines a type of service template identifier."; + } + typedef address-family { + type enumeration { + enum ipv4 { + description + "IPv4 address family."; + } + enum ipv6 { + description + "IPv6 address family."; + } + } + description + "Defines a type for the address family."; + } + /* Identities */ + identity site-network-access-type { + description + "Base identity for site-network-access type."; + } + + identity point-to-point { + base site-network-access-type; + description + "Identity for point-to-point connection."; + } + identity multipoint { + base site-network-access-type; + description + "Identity for multipoint connection. + Example: Ethernet broadcast segment."; + } + identity placement-diversity { + description + "Base identity for site placement constraints."; + } + identity bearer-diverse { + base placement-diversity; + description + "Identity for bearer diversity. + The bearers should not use common elements."; + } + identity pe-diverse { + base placement-diversity; + description + "Identity for PE diversity."; + } + identity pop-diverse { + base placement-diversity; + description + "Identity for POP diversity."; + } + identity linecard-diverse { + base placement-diversity; + description + "Identity for linecard diversity."; + } + identity same-pe { + base placement-diversity; + description + "Identity for having sites connected on the same PE."; + } + identity same-bearer { + base placement-diversity; + description + "Identity for having sites connected using the same bearer."; + } + identity customer-application { + description + + "Base identity for customer application."; + } + identity web { + base customer-application; + description + "Identity for Web application (e.g., HTTP, HTTPS)."; + } + identity mail { + base customer-application; + description + "Identity for mail application."; + } + identity file-transfer { + base customer-application; + description + "Identity for file transfer application (e.g., FTP, SFTP)."; + } + identity database { + base customer-application; + description + "Identity for database application."; + } + identity social { + base customer-application; + description + "Identity for social-network application."; + } + identity games { + base customer-application; + description + "Identity for gaming application."; + } + identity p2p { + base customer-application; + description + "Identity for peer-to-peer application."; + } + identity network-management { + base customer-application; + description + "Identity for management application + (e.g., Telnet, syslog, SNMP)."; + } + identity voice { + base customer-application; + description + "Identity for voice application."; + } + + identity video { + base customer-application; + description + "Identity for video conference application."; + } + identity embb { + base customer-application; + description + "Identity for an enhanced Mobile Broadband (eMBB) + application. Note that an eMBB application demands + network performance with a wide variety of + characteristics, such as data rate, latency, + loss rate, reliability, and many other parameters."; + } + identity urllc { + base customer-application; + description + "Identity for an Ultra-Reliable and Low Latency + Communications (URLLC) application. Note that a + URLLC application demands network performance + with a wide variety of characteristics, such as latency, + reliability, and many other parameters."; + } + identity mmtc { + base customer-application; + description + "Identity for a massive Machine Type + Communications (mMTC) application. Note that an + mMTC application demands network performance + with a wide variety of characteristics, such as data + rate, latency, loss rate, reliability, and many + other parameters."; + } + identity site-vpn-flavor { + description + "Base identity for the site VPN service flavor."; + } + identity site-vpn-flavor-single { + base site-vpn-flavor; + description + "Base identity for the site VPN service flavor. + Used when the site belongs to only one VPN."; + } + identity site-vpn-flavor-multi { + base site-vpn-flavor; + description + "Base identity for the site VPN service flavor. + Used when a logical connection of a site + + belongs to multiple VPNs."; + } + identity site-vpn-flavor-sub { + base site-vpn-flavor; + description + "Base identity for the site VPN service flavor. + Used when a site has multiple logical connections. + Each connection may belong to different multiple VPNs."; + } + identity site-vpn-flavor-nni { + base site-vpn-flavor; + description + "Base identity for the site VPN service flavor. + Used to describe an NNI option A connection."; + } + identity management { + description + "Base identity for site management scheme."; + } + identity co-managed { + base management; + description + "Base identity for co-managed site."; + } + identity customer-managed { + base management; + description + "Base identity for customer-managed site."; + } + identity provider-managed { + base management; + description + "Base identity for provider-managed site."; + } + identity address-allocation-type { + description + "Base identity for address-allocation-type for PE-CE link."; + } + identity provider-dhcp { + base address-allocation-type; + description + "Provider network provides DHCP service to customer."; + } + identity provider-dhcp-relay { + base address-allocation-type; + description + "Provider network provides DHCP relay service to customer."; + } + + identity provider-dhcp-slaac { + base address-allocation-type; + description + "Provider network provides DHCP service to customer, + as well as SLAAC."; + } + identity static-address { + base address-allocation-type; + description + "Provider-to-customer addressing is static."; + } + identity slaac { + base address-allocation-type; + description + "Use IPv6 SLAAC."; + } + identity site-role { + description + "Base identity for site type."; + } + identity any-to-any-role { + base site-role; + description + "Site in an any-to-any IP VPN."; + } + identity spoke-role { + base site-role; + description + "Spoke site in a Hub-and-Spoke IP VPN."; + } + identity hub-role { + base site-role; + description + "Hub site in a Hub-and-Spoke IP VPN."; + } + identity vpn-topology { + description + "Base identity for VPN topology."; + } + identity any-to-any { + base vpn-topology; + description + "Identity for any-to-any VPN topology."; + } + identity hub-spoke { + base vpn-topology; + description + "Identity for Hub-and-Spoke VPN topology."; + + } + identity hub-spoke-disjoint { + base vpn-topology; + description + "Identity for Hub-and-Spoke VPN topology + where Hubs cannot communicate with each other."; + } + identity multicast-tree-type { + description + "Base identity for multicast tree type."; + } + identity ssm-tree-type { + base multicast-tree-type; + description + "Identity for SSM tree type."; + } + identity asm-tree-type { + base multicast-tree-type; + description + "Identity for ASM tree type."; + } + identity bidir-tree-type { + base multicast-tree-type; + description + "Identity for bidirectional tree type."; + } + identity multicast-rp-discovery-type { + description + "Base identity for RP discovery type."; + } + identity auto-rp { + base multicast-rp-discovery-type; + description + "Base identity for Auto-RP discovery type."; + } + identity static-rp { + base multicast-rp-discovery-type; + description + "Base identity for static type."; + } + identity bsr-rp { + base multicast-rp-discovery-type; + description + "Base identity for BSR discovery type."; + } + identity routing-protocol-type { + description + "Base identity for routing protocol type."; + + } + identity ospf { + base routing-protocol-type; + description + "Identity for OSPF protocol type."; + } + identity bgp { + base routing-protocol-type; + description + "Identity for BGP protocol type."; + } + identity static { + base routing-protocol-type; + description + "Identity for static routing protocol type."; + } + identity rip { + base routing-protocol-type; + description + "Identity for RIP protocol type."; + } + identity vrrp { + base routing-protocol-type; + description + "Identity for VRRP protocol type. + This is to be used when LANs are directly connected + to PE routers."; + } + identity direct { + base routing-protocol-type; + description + "Identity for direct protocol type."; + } + identity protocol-type { + description + "Base identity for protocol field type."; + } + identity tcp { + base protocol-type; + description + "TCP protocol type."; + } + identity udp { + base protocol-type; + description + "UDP protocol type."; + } + + + identity icmp { + base protocol-type; + description + "ICMP protocol type."; + } + identity icmp6 { + base protocol-type; + description + "ICMPv6 protocol type."; + } + identity gre { + base protocol-type; + description + "GRE protocol type."; + } + identity ipip { + base protocol-type; + description + "IP-in-IP protocol type."; + } + identity hop-by-hop { + base protocol-type; + description + "Hop-by-Hop IPv6 header type."; + } + identity routing { + base protocol-type; + description + "Routing IPv6 header type."; + } + identity esp { + base protocol-type; + description + "ESP header type."; + } + identity ah { + base protocol-type; + description + "AH header type."; + } + identity vpn-policy-filter-type { + description + "Base identity for VPN Policy filter type."; + } + identity ipv4 { + base vpn-policy-filter-type; + description + "Identity for IPv4 Prefix filter type."; + + } + identity ipv6 { + base vpn-policy-filter-type; + description + "Identity for IPv6 Prefix filter type."; + } + identity lan { + base vpn-policy-filter-type; + description + "Identity for LAN Tag filter type."; + } + + identity qos-profile-direction { + description + "Base identity for QoS profile direction."; + } + + identity site-to-wan { + base qos-profile-direction; + description + "Identity for Site-to-WAN direction."; + } + identity wan-to-site { + base qos-profile-direction; + description + "Identity for WAN-to-Site direction."; + } + identity both { + base qos-profile-direction; + description + "Identity for both WAN-to-Site direction + and Site-to-WAN direction."; + } + /* Groupings */ + grouping vpn-service-cloud-access { + container cloud-accesses { + if-feature cloud-access; + list cloud-access { + key cloud-identifier; + leaf cloud-identifier { + type leafref { + path "/l3vpn-svc/vpn-profiles/"+ + "valid-provider-identifiers/cloud-identifier/id"; + } + description + "Identification of cloud service. + Local administration meaning."; + } + + choice list-flavor { + case permit-any { + leaf permit-any { + type empty; + description + "Allows all sites."; + } + } + case deny-any-except { + leaf-list permit-site { + type leafref { + path "/l3vpn-svc/sites/site/site-id"; + } + description + "Site ID to be authorized."; + } + } + case permit-any-except { + leaf-list deny-site { + type leafref { + path "/l3vpn-svc/sites/site/site-id"; + } + description + "Site ID to be denied."; + } + } + description + "Choice for cloud access policy. By + default, all sites in the IP VPN MUST + be authorized to access the cloud."; + } + container address-translation { + container nat44 { + leaf enabled { + type boolean; + default false; + description + "Controls whether or not Network address + translation from IPv4 to IPv4 (NAT44) + [RFC3022] is required."; + } + leaf nat44-customer-address { + type inet:ipv4-address; + description + "Address to be used for network address + translation from IPv4 to IPv4. This is + to be used if the customer is providing + the IPv4 address. If the customer address + + is not set, the model assumes that the + provider will allocate the address."; + } + description + "IPv4-to-IPv4 translation."; + } + description + "Container for NAT."; + } + description + "Cloud access configuration."; + } + description + "Container for cloud access configurations."; + } + description + "Grouping for VPN cloud definition."; + } + grouping multicast-rp-group-cfg { + choice group-format { + mandatory true; + case singleaddress { + leaf group-address { + type inet:ip-address; + description + "A single multicast group address."; + } + } + case startend { + leaf group-start { + type inet:ip-address; + description + "The first multicast group address in + the multicast group address range."; + } + leaf group-end { + type inet:ip-address; + description + "The last multicast group address in + the multicast group address range."; + } + } + description + "Choice for multicast group format."; + } + description + "This grouping defines multicast group or + multicast groups for RP-to-group mapping."; + + } + grouping vpn-service-multicast { + container multicast { + if-feature multicast; + leaf enabled { + type boolean; + default false; + description + "Enables multicast."; + } + container customer-tree-flavors { + leaf-list tree-flavor { + type identityref { + base multicast-tree-type; + } + description + "Type of tree to be used."; + } + description + "Type of trees used by customer."; + } + container rp { + container rp-group-mappings { + list rp-group-mapping { + key id; + leaf id { + type uint16; + description + "Unique identifier for the mapping."; + } + container provider-managed { + leaf enabled { + type boolean; + default false; + description + "Set to true if the Rendezvous Point (RP) + must be a provider-managed node. Set to false + if it is a customer-managed node."; + } + leaf rp-redundancy { + type boolean; + default false; + description + "If true, a redundancy mechanism for the RP + is required."; + } + leaf optimal-traffic-delivery { + type boolean; + + default false; + description + "If true, the SP must ensure that + traffic uses an optimal path. An SP may use + Anycast RP or RP-tree-to-SPT switchover + architectures."; + } + description + "Parameters for a provider-managed RP."; + } + leaf rp-address { + when "../provider-managed/enabled = 'false'" { + description + "Relevant when the RP is not provider-managed."; + } + type inet:ip-address; + mandatory true; + description + "Defines the address of the RP. + Used if the RP is customer-managed."; + } + container groups { + list group { + key id; + leaf id { + type uint16; + description + "Identifier for the group."; + } + uses multicast-rp-group-cfg; + description + "List of multicast groups."; + } + description + "Multicast groups associated with the RP."; + } + description + "List of RP-to-group mappings."; + } + description + "RP-to-group mappings parameters."; + } + container rp-discovery { + leaf rp-discovery-type { + type identityref { + base multicast-rp-discovery-type; + } + default static-rp; + + description + "Type of RP discovery used."; + } + container bsr-candidates { + when "derived-from-or-self(../rp-discovery-type, "+ + "'l3vpn-svc:bsr-rp')" { + description + "Only applicable if discovery type + is BSR-RP."; + } + leaf-list bsr-candidate-address { + type inet:ip-address; + description + "Address of BSR candidate."; + } + description + "Container for List of Customer + BSR candidate's addresses."; + } + description + "RP discovery parameters."; + } + description + "RP parameters."; + } + description + "Multicast global parameters for the VPN service."; + } + description + "Grouping for multicast VPN definition."; + } + grouping vpn-service-mpls { + leaf carrierscarrier { + if-feature carrierscarrier; + type boolean; + default false; + description + "The VPN is using CsC, and so MPLS is required."; + } + description + "Grouping for MPLS CsC definition."; + } + grouping customer-location-info { + container locations { + list location { + key location-id; + leaf location-id { + type svc-id; + + description + "Identifier for a particular location."; + } + leaf address { + type string; + description + "Address (number and street) of the site."; + } + leaf postal-code { + type string; + description + "Postal code of the site."; + } + leaf state { + type string; + description + "State of the site. This leaf can also be + used to describe a region for a country that + does not have states."; + } + leaf city { + type string; + description + "City of the site."; + } + leaf country-code { + type string { + pattern '[A-Z]{2}'; + } + description + "Country of the site. + Expressed as ISO ALPHA-2 code."; + } + description + "Location of the site."; + } + description + "List of locations for the site."; + } + description + "This grouping defines customer location parameters."; + } + grouping site-group { + container groups { + list group { + key group-id; + leaf group-id { + type string; + + description + "Group-id the site belongs to."; + } + description + "List of group-ids."; + } + description + "Groups the site or site-network-access belongs to."; + } + description + "Grouping definition to assign + group-ids to site or site-network-access."; + } + grouping site-diversity { + container site-diversity { + if-feature site-diversity; + uses site-group; + description + "Diversity constraint type. All + site-network-accesses will inherit + the group values defined here."; + } + description + "This grouping defines site + diversity parameters."; + } + grouping access-diversity { + container access-diversity { + if-feature site-diversity; + uses site-group; + container constraints { + list constraint { + key constraint-type; + leaf constraint-type { + type identityref { + base placement-diversity; + } + description + "Diversity constraint type."; + } + container target { + choice target-flavor { + default id; + case id { + list group { + key group-id; + leaf group-id { + type string; + + description + "The constraint will be applied against + this particular group-id for this site + network access level."; + } + description + "List of group-ids associated with one specific + constraint for this site network access level."; + } + } + case all-accesses { + leaf all-other-accesses { + type empty; + description + "The constraint will be applied against + all other site network accesses of this site."; + } + } + case all-groups { + leaf all-other-groups { + type empty; + description + "The constraint will be applied against + all other groups managed by the customer."; + } + } + description + "Choice for the target flavor definition."; + } + description + "The constraint will be applied against a + Specific target, and the target can be a list + of group-ids,all other site network accesses of + this site, or all other groups managed by the + customer."; + } + description + "List of constraints."; + } + description + "Placement constraints for this site network access."; + } + description + "Diversity parameters."; + } + description + "This grouping defines access diversity parameters."; + } + + grouping operational-requirements { + leaf requested-site-start { + type yang:date-and-time; + description + "Optional leaf indicating requested date and + time when the service at a particular site is + expected to start."; + } + + leaf requested-site-stop { + type yang:date-and-time; + description + "Optional leaf indicating requested date and + time when the service at a particular site is + expected to stop."; + } + description + "This grouping defines some operational + parameters."; + } + grouping operational-requirements-ops { + leaf actual-site-start { + type yang:date-and-time; + config false; + description + "Optional leaf indicating actual date and + time when the service at a particular site + actually started."; + } + leaf actual-site-stop { + type yang:date-and-time; + config false; + description + "Optional leaf indicating actual date and + time when the service at a particular site + actually stopped."; + } + description + "This grouping defines some operational + parameters."; + } + grouping flow-definition { + container match-flow { + leaf dscp { + type inet:dscp; + description + "DSCP value."; + } + + leaf dot1p { + type uint8 { + range "0..7"; + } + description + "802.1p matching."; + } + leaf ipv4-src-prefix { + type inet:ipv4-prefix; + description + "Match on IPv4 src address."; + } + leaf ipv6-src-prefix { + type inet:ipv6-prefix; + description + "Match on IPv6 src address."; + } + leaf ipv4-dst-prefix { + type inet:ipv4-prefix; + description + "Match on IPv4 dst address."; + } + leaf ipv6-dst-prefix { + type inet:ipv6-prefix; + description + "Match on IPv6 dst address."; + } + leaf l4-src-port { + type inet:port-number; + must "current() < ../l4-src-port-range/lower-port or "+ + "current() > ../l4-src-port-range/upper-port" { + description + "If l4-src-port and l4-src-port-range/lower-port and + upper-port are set at the same time, l4-src-port + should not overlap with l4-src-port-range."; + } + description + "Match on Layer 4 src port."; + } + leaf-list target-sites { + if-feature target-sites; + type svc-id; + description + "Identify a site as traffic destination."; + } + container l4-src-port-range { + leaf lower-port { + type inet:port-number; + + description + "Lower boundary for port."; + } + leaf upper-port { + type inet:port-number; + must ". >= ../lower-port" { + description + "Upper boundary for port. If it + exists, the upper boundary must be + higher than the lower boundary."; + } + description + "Upper boundary for port."; + } + description + "Match on Layer 4 src port range. When + only the lower-port is present, it represents + a single port. When both the lower-port and + upper-port are specified, it implies + a range inclusive of both values."; + } + leaf l4-dst-port { + type inet:port-number; + must "current() < ../l4-dst-port-range/lower-port or "+ + "current() > ../l4-dst-port-range/upper-port" { + description + "If l4-dst-port and l4-dst-port-range/lower-port + and upper-port are set at the same time, + l4-dst-port should not overlap with + l4-src-port-range."; + } + description + "Match on Layer 4 dst port."; + } + container l4-dst-port-range { + leaf lower-port { + type inet:port-number; + description + "Lower boundary for port."; + } + leaf upper-port { + type inet:port-number; + must ". >= ../lower-port" { + description + "Upper boundary must be + higher than lower boundary."; + } + description + + "Upper boundary for port. If it exists, + upper boundary must be higher than lower + boundary."; + } + description + "Match on Layer 4 dst port range. When only + lower-port is present, it represents a single + port. When both lower-port and upper-port are + specified, it implies a range inclusive of both + values."; + } + leaf protocol-field { + type union { + type uint8; + type identityref { + base protocol-type; + } + } + description + "Match on IPv4 protocol or IPv6 Next Header field."; + } + description + "Describes flow-matching criteria."; + } + description + "Flow definition based on criteria."; + } + grouping site-service-basic { + leaf svc-input-bandwidth { + type uint64; + units bps; + mandatory true; + description + "From the customer site's perspective, the service + input bandwidth of the connection or download + bandwidth from the SP to the site."; + } + leaf svc-output-bandwidth { + type uint64; + units bps; + mandatory true; + description + "From the customer site's perspective, the service + output bandwidth of the connection or upload + bandwidth from the site to the SP."; + } + leaf svc-mtu { + type uint16; + + units bytes; + mandatory true; + description + "MTU at service level. If the service is IP, + it refers to the IP MTU. If CsC is enabled, + the requested 'svc-mtu' leaf will refer to the + MPLS MTU and not to the IP MTU."; + } + description + "Defines basic service parameters for a site."; + } + grouping site-protection { + container traffic-protection { + if-feature fast-reroute; + leaf enabled { + type boolean; + default false; + description + "Enables traffic protection of access link."; + } + description + "Fast Reroute service parameters for the site."; + } + description + "Defines protection service parameters for a site."; + } + grouping site-service-mpls { + container carrierscarrier { + if-feature carrierscarrier; + leaf signalling-type { + type enumeration { + enum ldp { + description + "Use LDP as the signalling protocol + between the PE and the CE. In this case, + an IGP routing protocol must also be activated."; + } + enum bgp { + description + "Use BGP (as per RFC 8277) as the signalling protocol + between the PE and the CE. + In this case, BGP must also be configured as + the routing protocol."; + } + } + default bgp; + description + "MPLS signalling type."; + + } + description + "This container is used when the customer provides + MPLS-based services. This is only used in the case + of CsC (i.e., a customer builds an MPLS service using + an IP VPN to carry its traffic)."; + } + description + "Defines MPLS service parameters for a site."; + } + grouping site-service-qos-profile { + container qos { + if-feature qos; + container qos-classification-policy { + list rule { + key id; + ordered-by user; + leaf id { + type string; + description + "A description identifying the + qos-classification-policy rule."; + } + choice match-type { + default match-flow; + case match-flow { + uses flow-definition; + } + case match-application { + leaf match-application { + type identityref { + base customer-application; + } + description + "Defines the application to match."; + } + } + description + "Choice for classification."; + } + leaf target-class-id { + type string; + description + "Identification of the class of service. + This identifier is internal to the administration."; + } + description + "List of marking rules."; + + } + description + "Configuration of the traffic classification policy."; + } + container qos-profile { + choice qos-profile { + description + "Choice for QoS profile. + Can be standard profile or customized profile."; + case standard { + description + "Standard QoS profile."; + leaf profile { + type leafref { + path "/l3vpn-svc/vpn-profiles/valid-provider-identifiers"+ + "/qos-profile-identifier/id"; + } + description + "QoS profile to be used."; + } + } + case custom { + description + "Customized QoS profile."; + container classes { + if-feature qos-custom; + list class { + key class-id; + leaf class-id { + type string; + description + "Identification of the class of service. + This identifier is internal to the + administration."; + } + leaf direction { + type identityref { + base qos-profile-direction; + } + default both; + description + "The direction to which the QoS profile + is applied."; + } + leaf rate-limit { + type decimal64 { + fraction-digits 5; + range "0..100"; + + } + units percent; + description + "To be used if the class must be rate-limited. + Expressed as percentage of the service + bandwidth."; + } + container latency { + choice flavor { + case lowest { + leaf use-lowest-latency { + type empty; + description + "The traffic class should use the path with the + lowest latency."; + } + } + case boundary { + leaf latency-boundary { + type uint16; + units msec; + default 400; + description + "The traffic class should use a path with a + defined maximum latency."; + } + } + description + "Latency constraint on the traffic class."; + } + description + "Latency constraint on the traffic class."; + } + container jitter { + choice flavor { + case lowest { + leaf use-lowest-jitter { + type empty; + description + "The traffic class should use the path with the + lowest jitter."; + } + } + case boundary { + leaf latency-boundary { + type uint32; + units usec; + default 40000; + + description + "The traffic class should use a path with a + defined maximum jitter."; + } + } + description + "Jitter constraint on the traffic class."; + } + description + "Jitter constraint on the traffic class."; + } + container bandwidth { + leaf guaranteed-bw-percent { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + units percent; + mandatory true; + description + "To be used to define the guaranteed bandwidth + as a percentage of the available service bandwidth."; + } + leaf end-to-end { + type empty; + description + "Used if the bandwidth reservation + must be done on the MPLS network too."; + } + description + "Bandwidth constraint on the traffic class."; + } + description + "List of classes of services."; + } + description + "Container for list of classes of services."; + } + } + } + description + "QoS profile configuration."; + } + description + "QoS configuration."; + } + description + "This grouping defines QoS parameters for a site."; + + } + grouping site-security-authentication { + container authentication { + description + "Authentication parameters."; + } + description + "This grouping defines authentication parameters for a site."; + } + grouping site-security-encryption { + container encryption { + if-feature encryption; + leaf enabled { + type boolean; + default false; + description + "If true, traffic encryption on the connection is required."; + } + leaf layer { + when "../enabled = 'true'" { + description + "Require a value for layer when enabled is true."; + } + type enumeration { + enum layer2 { + description + "Encryption will occur at Layer 2."; + } + enum layer3 { + description + "Encryption will occur at Layer 3. + For example, IPsec may be used when + a customer requests Layer 3 encryption."; + } + } + description + "Layer on which encryption is applied."; + } + container encryption-profile { + choice profile { + case provider-profile { + leaf profile-name { + type leafref { + path "/l3vpn-svc/vpn-profiles/valid-provider-identifiers"+ + "/encryption-profile-identifier/id"; + } + description + "Name of the SP profile to be applied."; + + } + } + case customer-profile { + leaf algorithm { + type string; + description + "Encryption algorithm to be used."; + } + choice key-type { + default psk; + case psk { + leaf preshared-key { + type string; + description + "Pre-Shared Key (PSK) coming from the customer."; + } + } + description + "Type of keys to be used."; + } + } + description + "Choice of encryption profile. The encryption + profile can be the provider profile or customer profile."; + } + description + "Profile of encryption to be applied."; + } + description + "Encryption parameters."; + } + description + "This grouping defines encryption parameters for a site."; + } + grouping site-attachment-bearer { + container bearer { + container requested-type { + if-feature requested-type; + leaf requested-type { + type string; + description + "Type of requested bearer: Ethernet, DSL, + Wireless, etc. Operator specific."; + } + leaf strict { + type boolean; + default false; + description + + "Defines whether requested-type is a preference + or a strict requirement."; + } + description + "Container for requested-type."; + } + leaf always-on { + if-feature always-on; + type boolean; + default true; + description + "Request for an always-on access type. + For example, this could mean no dial access type."; + } + leaf bearer-reference { + if-feature bearer-reference; + type string; + description + "This is an internal reference for the SP."; + } + description + "Bearer-specific parameters. + To be augmented."; + } + description + "Defines physical properties of a site attachment."; + } + grouping site-routing { + container routing-protocols { + list routing-protocol { + key type; + leaf type { + type identityref { + base routing-protocol-type; + } + description + "Type of routing protocol."; + } + container ospf { + when "derived-from-or-self(../type, 'l3vpn-svc:ospf')" { + description + "Only applies when protocol is OSPF."; + } + if-feature rtg-ospf; + leaf-list address-family { + type address-family; + min-elements "1"; + description + + "If OSPF is used on this site, this node + contains a configured value. This node + contains at least one address family + to be activated."; + } + leaf area-address { + type yang:dotted-quad; + mandatory true; + description + "Area address."; + } + leaf metric { + type uint16; + default 1; + description + "Metric of the PE-CE link. It is used + in the routing state calculation and + path selection."; + } + container sham-links { + if-feature rtg-ospf-sham-link; + list sham-link { + key target-site; + leaf target-site { + type svc-id; + description + "Target site for the sham link connection. + The site is referred to by its ID."; + } + leaf metric { + type uint16; + default 1; + description + "Metric of the sham link. It is used in + the routing state calculation and path + selection. The default value is set + to 1."; + } + description + "Creates a sham link with another site."; + } + description + "List of sham links."; + } + description + "OSPF-specific configuration."; + } + container bgp { + + when "derived-from-or-self(../type, 'l3vpn-svc:bgp')" { + description + "Only applies when protocol is BGP."; + } + if-feature rtg-bgp; + leaf autonomous-system { + type uint32; + mandatory true; + description + "Customer AS number in case the customer + requests BGP routing."; + } + leaf-list address-family { + type address-family; + min-elements "1"; + description + "If BGP is used on this site, this node + contains a configured value. This node + contains at least one address family + to be activated."; + } + description + "BGP-specific configuration."; + } + container static { + when "derived-from-or-self(../type, 'l3vpn-svc:static')" { + description + "Only applies when protocol is static. + BGP activation requires the SP to know + the address of the customer peer. When + BGP is enabled, the 'static-address' + allocation type for the IP connection + MUST be used."; + } + container cascaded-lan-prefixes { + list ipv4-lan-prefixes { + if-feature ipv4; + key "lan next-hop"; + leaf lan { + type inet:ipv4-prefix; + description + "LAN prefixes."; + } + leaf lan-tag { + type string; + description + "Internal tag to be used in VPN policies."; + } + + leaf next-hop { + type inet:ipv4-address; + description + "Next-hop address to use on the customer side."; + } + description + "List of LAN prefixes for the site."; + } + list ipv6-lan-prefixes { + if-feature ipv6; + key "lan next-hop"; + leaf lan { + type inet:ipv6-prefix; + description + "LAN prefixes."; + } + leaf lan-tag { + type string; + description + "Internal tag to be used in VPN policies."; + } + leaf next-hop { + type inet:ipv6-address; + description + "Next-hop address to use on the customer side."; + } + description + "List of LAN prefixes for the site."; + } + description + "LAN prefixes from the customer."; + } + description + "Configuration specific to static routing."; + } + container rip { + when "derived-from-or-self(../type, 'l3vpn-svc:rip')" { + description + "Only applies when the protocol is RIP. For IPv4, + the model assumes that RIP version 2 is used."; + } + if-feature rtg-rip; + leaf-list address-family { + type address-family; + min-elements "1"; + description + "If RIP is used on this site, this node + contains a configured value. This node + + contains at least one address family + to be activated."; + } + description + "Configuration specific to RIP routing."; + } + container vrrp { + when "derived-from-or-self(../type, 'l3vpn-svc:vrrp')" { + description + "Only applies when protocol is VRRP."; + } + if-feature rtg-vrrp; + leaf-list address-family { + type address-family; + min-elements "1"; + description + "If VRRP is used on this site, this node + contains a configured value. This node contains + at least one address family to be activated."; + } + description + "Configuration specific to VRRP routing."; + } + description + "List of routing protocols used on + the site. This list can be augmented."; + } + description + "Defines routing protocols."; + } + description + "Grouping for routing protocols."; + } + grouping site-attachment-ip-connection { + container ip-connection { + container ipv4 { + if-feature ipv4; + leaf address-allocation-type { + type identityref { + base address-allocation-type; + } + must "not(derived-from-or-self(current(), 'l3vpn-svc:slaac') or "+ + "derived-from-or-self(current(), "+ + "'l3vpn-svc:provider-dhcp-slaac'))" { + error-message "SLAAC is only applicable to IPv6"; + } + description + "Defines how addresses are allocated. + + If there is no value for the address + allocation type, then IPv4 is not enabled."; + } + container provider-dhcp { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:provider-dhcp')" { + description + "Only applies when addresses are allocated by DHCP."; + } + leaf provider-address { + type inet:ipv4-address; + description + "Address of provider side. If provider-address is not + specified, then prefix length should not be specified + either. It also implies provider-dhcp allocation is + not enabled. If provider-address is specified, then + the prefix length may or may not be specified."; + } + leaf prefix-length { + type uint8 { + range "0..32"; + } + must "(../provider-address)" { + error-message + "If the prefix length is specified, provider-address + must also be specified."; + description + "If the prefix length is specified, provider-address + must also be specified."; + } + description + "Subnet prefix length expressed in bits. + If not specified, or specified as zero, + this means the customer leaves the actual + prefix length value to the provider."; + } + choice address-assign { + default number; + case number { + leaf number-of-dynamic-address { + type uint16; + default 1; + description + "Describes the number of IP addresses + the customer requires."; + } + } + case explicit { + + container customer-addresses { + list address-group { + key "group-id"; + leaf group-id { + type string; + description + "Group-id for the address range from + start-address to end-address."; + } + leaf start-address { + type inet:ipv4-address; + description + "First address."; + } + leaf end-address { + type inet:ipv4-address; + description + "Last address."; + } + description + "Describes IP addresses allocated by DHCP. + When only start-address or only end-address + is present, it represents a single address. + When both start-address and end-address are + specified, it implies a range inclusive of both + addresses. If no address is specified, it implies + customer addresses group is not supported."; + } + description + "Container for customer addresses is allocated by DHCP."; + } + } + description + "Choice for the way to assign addresses."; + } + description + "DHCP allocated addresses related parameters."; + } + container dhcp-relay { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:provider-dhcp-relay')" { + description + "Only applies when provider is required to implement + DHCP relay function."; + } + leaf provider-address { + type inet:ipv4-address; + description + + "Address of provider side. If provider-address is not + specified, then prefix length should not be specified + either. It also implies provider-dhcp allocation is + not enabled. If provider-address is specified, then + prefix length may or may not be specified."; + } + leaf prefix-length { + type uint8 { + range "0..32"; + } + must "(../provider-address)" { + error-message + "If prefix length is specified, provider-address + must also be specified."; + description + "If prefix length is specified, provider-address + must also be specified."; + } + description + "Subnet prefix length expressed in bits. If not + specified, or specified as zero, this means the + customer leaves the actual prefix length value + to the provider."; + } + container customer-dhcp-servers { + leaf-list server-ip-address { + type inet:ipv4-address; + description + "IP address of customer DHCP server."; + } + description + "Container for list of customer DHCP servers."; + } + description + "DHCP relay provided by operator."; + } + container addresses { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:static-address')" { + description + "Only applies when protocol allocation type is static."; + } + leaf provider-address { + type inet:ipv4-address; + description + "IPv4 Address List of the provider side. + When the protocol allocation type is static, + the provider address must be configured."; + + } + leaf customer-address { + type inet:ipv4-address; + description + "IPv4 Address of customer side."; + } + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "Subnet prefix length expressed in bits. + It is applied to both provider-address + and customer-address."; + } + description + "Describes IPv4 addresses used."; + } + description + "IPv4-specific parameters."; + } + container ipv6 { + if-feature ipv6; + leaf address-allocation-type { + type identityref { + base address-allocation-type; + } + description + "Defines how addresses are allocated. + If there is no value for the address + allocation type, then IPv6 is + not enabled."; + } + + container provider-dhcp { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:provider-dhcp') "+ + "or derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:provider-dhcp-slaac')" { + description + "Only applies when addresses are allocated by DHCP."; + } + leaf provider-address { + type inet:ipv6-address; + description + "Address of the provider side. If provider-address + is not specified, then prefix length should not be + specified either. It also implies provider-dhcp + + allocation is not enabled. If provider-address is + specified, then prefix length may or may + not be specified."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + must "(../provider-address)" { + error-message + "If prefix length is specified, provider-address + must also be specified."; + description + "If prefix length is specified, provider-address + must also be specified."; + } + description + "Subnet prefix length expressed in bits. If not + specified, or specified as zero, this means the + customer leaves the actual prefix length value + to the provider."; + } + choice address-assign { + default number; + case number { + leaf number-of-dynamic-address { + type uint16; + default 1; + description + "Describes the number of IP addresses the customer + requires."; + } + } + case explicit { + container customer-addresses { + list address-group { + key "group-id"; + leaf group-id { + type string; + description + "Group-id for the address range from + start-address to end-address."; + } + leaf start-address { + type inet:ipv6-address; + description + "First address."; + } + + leaf end-address { + type inet:ipv6-address; + description + "Last address."; + } + description + "Describes IP addresses allocated by DHCP. When only + start-address or only end-address is present, it + represents a single address. When both start-address + and end-address are specified, it implies a range + inclusive of both addresses. If no address is + specified, it implies customer addresses group is + not supported."; + } + description + "Container for customer addresses allocated by DHCP."; + } + } + description + "Choice for the way to assign addresses."; + } + description + "DHCP allocated addresses related parameters."; + } + container dhcp-relay { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:provider-dhcp-relay')" { + description + "Only applies when the provider is required + to implement DHCP relay function."; + } + leaf provider-address { + type inet:ipv6-address; + description + "Address of the provider side. If provider-address is + not specified, then prefix length should not be + specified either. It also implies provider-dhcp + allocation is not enabled. If provider address + is specified, then prefix length may or may + not be specified."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + must "(../provider-address)" { + error-message + "If prefix length is specified, provider-address + + must also be specified."; + description + "If prefix length is specified, provider-address + must also be specified."; + } + description + "Subnet prefix length expressed in bits. If not + specified, or specified as zero, this means the + customer leaves the actual prefix length value + to the provider."; + } + container customer-dhcp-servers { + leaf-list server-ip-address { + type inet:ipv6-address; + description + "This node contains the IP address of + the customer DHCP server. If the DHCP relay + function is implemented by the + provider, this node contains the + configured value."; + } + description + "Container for list of customer DHCP servers."; + } + description + "DHCP relay provided by operator."; + } + container addresses { + when "derived-from-or-self(../address-allocation-type, "+ + "'l3vpn-svc:static-address')" { + description + "Only applies when protocol allocation type is static."; + } + leaf provider-address { + type inet:ipv6-address; + description + "IPv6 Address of the provider side. When the protocol + allocation type is static, the provider address + must be configured."; + } + leaf customer-address { + type inet:ipv6-address; + description + "The IPv6 Address of the customer side."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + + } + description + "Subnet prefix length expressed in bits. + It is applied to both provider-address and + customer-address."; + } + description + "Describes IPv6 addresses used."; + } + description + "IPv6-specific parameters."; + } + container oam { + container bfd { + if-feature bfd; + leaf enabled { + type boolean; + default false; + description + "If true, BFD activation is required."; + } + choice holdtime { + default fixed; + case fixed { + leaf fixed-value { + type uint32; + units msec; + description + "Expected BFD holdtime expressed in msec. The customer + may impose some fixed values for the holdtime period + if the provider allows the customer use this function. + If the provider doesn't allow the customer to use this + function, the fixed-value will not be set."; + } + } + case profile { + leaf profile-name { + type leafref { + path "/l3vpn-svc/vpn-profiles/valid-provider-identifiers/"+ + "bfd-profile-identifier/id"; + } + description + "Well-known SP profile name. The provider can propose + some profiles to the customer, depending on the service + level the customer wants to achieve. Profile names + must be communicated to the customer."; + } + description + + "Well-known SP profile."; + } + description + "Choice for holdtime flavor."; + } + description + "Container for BFD."; + } + description + "Defines the Operations, Administration, and Maintenance (OAM) + mechanisms used on the connection. BFD is set as a fault + detection mechanism, but the 'oam' container can easily + be augmented by other mechanisms"; + } + description + "Defines connection parameters."; + } + description + "This grouping defines IP connection parameters."; + } + grouping site-service-multicast { + container multicast { + if-feature multicast; + leaf multicast-site-type { + type enumeration { + enum receiver-only { + description + "The site only has receivers."; + } + enum source-only { + description + "The site only has sources."; + } + enum source-receiver { + description + "The site has both sources and receivers."; + } + } + default source-receiver; + description + "Type of multicast site."; + } + container multicast-address-family { + leaf ipv4 { + if-feature ipv4; + type boolean; + default false; + description + + "Enables IPv4 multicast."; + } + leaf ipv6 { + if-feature ipv6; + type boolean; + default false; + description + "Enables IPv6 multicast."; + } + description + "Defines protocol to carry multicast."; + } + leaf protocol-type { + type enumeration { + enum host { + description + "Hosts are directly connected to the provider network. + Host protocols such as IGMP or MLD are required."; + } + enum router { + description + "Hosts are behind a customer router. + PIM will be implemented."; + } + enum both { + description + "Some hosts are behind a customer router, and + some others are directly connected to the + provider network. Both host and routing protocols + must be used. Typically, IGMP and PIM will be + implemented."; + } + } + default "both"; + description + "Multicast protocol type to be used with the customer site."; + } + description + "Multicast parameters for the site."; + } + description + "Multicast parameters for the site."; + } + grouping site-management { + container management { + leaf type { + type identityref { + base management; + + } + mandatory true; + description + "Management type of the connection."; + } + description + "Management configuration."; + } + description + "Management parameters for the site."; + } + grouping site-devices { + container devices { + when "derived-from-or-self(../management/type, "+ + "'l3vpn-svc:provider-managed') or "+ + "derived-from-or-self(../management/type, 'l3vpn-svc:co-managed')" { + description + "Applicable only for provider-managed or + co-managed device."; + } + list device { + key device-id; + leaf device-id { + type svc-id; + description + "Identifier for the device."; + } + leaf location { + type leafref { + path "../../../locations/"+ + "location/location-id"; + } + mandatory true; + description + "Location of the device."; + } + container management { + when "derived-from-or-self(../../../management/type,"+ + "'l3vpn-svc:co-managed')" { + description + "Applicable only for co-managed device."; + } + leaf address-family { + type address-family; + description + "Address family used for management."; + } + leaf address { + + when "(../address-family)" { + description + "If address-family is specified, then address should + also be specified. If address-family is not specified, + then address should also not be specified."; + } + type inet:ip-address; + mandatory true; + description + "Management address."; + } + description + "Management configuration. Applicable only for + co-managed device."; + } + description + "List of devices requested by customer."; + } + description + "Device configuration."; + } + description + "Grouping for device allocation."; + } + grouping site-vpn-flavor { + leaf site-vpn-flavor { + type identityref { + base site-vpn-flavor; + } + default site-vpn-flavor-single; + description + "Defines the way the VPN multiplexing is done, e.g., whether + the site belongs to a single VPN site or a multiVPN; or, in the case + of a multiVPN, whether the logical accesses of the sites belong + to the same set of VPNs or each logical access maps to + different VPNs."; + } + description + "Grouping for site VPN flavor."; + } + grouping site-vpn-policy { + container vpn-policies { + list vpn-policy { + key vpn-policy-id; + leaf vpn-policy-id { + type svc-id; + description + "Unique identifier for the VPN policy."; + + } + list entries { + key id; + leaf id { + type svc-id; + description + "Unique identifier for the policy entry."; + } + container filters { + list filter { + key type; + ordered-by user; + leaf type { + type identityref { + base vpn-policy-filter-type; + } + description + "Type of VPN Policy filter."; + } + leaf-list lan-tag { + when "derived-from-or-self(../type, 'l3vpn-svc:lan')" { + description + "Only applies when the VPN Policy filter is a + LAN Tag filter."; + } + if-feature lan-tag; + type string; + description + "List of 'lan-tag' items to be matched. LAN Tag + is an Internal tag to be used in VPN policies "; + } + leaf-list ipv4-lan-prefix { + when "derived-from-or-self(../type, 'l3vpn-svc:ipv4')" { + description + "Only applies when VPN Policy filter is IPv4 Prefix filter."; + } + if-feature ipv4; + type inet:ipv4-prefix; + description + "List of IPv4 prefixes as LAN Prefixes to be matched."; + } + leaf-list ipv6-lan-prefix { + when "derived-from-or-self(../type, 'l3vpn-svc:ipv6')" { + description + "Only applies when VPN Policy filter is IPv6 Prefix filter."; + } + if-feature ipv6; + type inet:ipv6-prefix; + + description + "List of IPv6 prefixes as LAN prefixes to be matched."; + } + description + "List of filters used on the site. This list can + be augmented."; + } + description + "If a more-granular VPN attachment is necessary, filtering can + be used. If used, it permits the splitting of site LANs among + multiple VPNs. The Site LAN can be split based on either LAN + Tag or LAN prefix. If no filter is used, all the LANs will be + part of the same VPNs with the same role."; + } + list vpn { + key vpn-id; + leaf vpn-id { + type leafref { + path "/l3vpn-svc/vpn-services/"+ + "vpn-service/vpn-id"; + } + mandatory true; + description + "Reference to an IP VPN."; + } + leaf site-role { + type identityref { + base site-role; + } + default any-to-any-role; + description + "Role of the site in the IP VPN."; + } + description + "List of VPNs the LAN is associated with."; + } + description + "List of entries for export policy."; + } + description + "List of VPN policies."; + } + description + "VPN policy."; + } + description + "VPN policy parameters for the site."; + } + + grouping site-maximum-routes { + container maximum-routes { + list address-family { + key af; + leaf af { + type address-family; + description + "Address family."; + } + leaf maximum-routes { + type uint32; + description + "Maximum prefixes the VRF can accept + for this address family."; + } + description + "List of address families."; + } + description + "Defines 'maximum-routes' for the VRF."; + } + description + "Defines 'maximum-routes' for the site."; + } + grouping site-security { + container security { + uses site-security-authentication; + uses site-security-encryption; + description + "Site-specific security parameters."; + } + description + "Grouping for security parameters."; + } + grouping site-service { + container service { + uses site-service-qos-profile; + uses site-service-mpls; + uses site-service-multicast; + description + "Service parameters on the attachment."; + } + description + "Grouping for service parameters."; + } + grouping site-network-access-service { + container service { + uses site-service-basic; + + uses site-service-qos-profile; + uses site-service-mpls; + uses site-service-multicast; + description + "Service parameters on the attachment."; + } + description + "Grouping for service parameters."; + } + grouping vpn-extranet { + container extranet-vpns { + if-feature extranet-vpn; + list extranet-vpn { + key vpn-id; + leaf vpn-id { + type svc-id; + description + "Identifies the target VPN the local VPN want to access."; + } + leaf local-sites-role { + type identityref { + base site-role; + } + default any-to-any-role; + description + "This describes the role of the + local sites in the target VPN topology. In the any-to-any VPN + service topology, the local sites must have the same role, which + will be 'any-to-any-role'. In the Hub-and-Spoke VPN service + topology or the Hub-and-Spoke disjoint VPN service topology, + the local sites must have a Hub role or a Spoke role."; + } + description + "List of extranet VPNs or target VPNs the local VPN is + attached to."; + } + description + "Container for extranet VPN configuration."; + } + description + "Grouping for extranet VPN configuration. + This provides an easy way to interconnect + all sites from two VPNs."; + } + grouping site-attachment-availability { + container availability { + leaf access-priority { + type uint32; + + default 100; + description + "Defines the priority for the access. + The higher the access-priority value, + the higher the preference of the + access will be."; + } + description + "Availability parameters (used for multihoming)."; + } + description + "Defines availability parameters for a site."; + } + grouping access-vpn-policy { + container vpn-attachment { + choice attachment-flavor { + case vpn-policy-id { + leaf vpn-policy-id { + type leafref { + path "../../../../"+ + "vpn-policies/vpn-policy/"+ + "vpn-policy-id"; + } + description + "Reference to a VPN policy. When referencing VPN + policy for attachment, the vpn-policy-id must be + configured."; + } + } + case vpn-id { + leaf vpn-id { + type leafref { + path "/l3vpn-svc/vpn-services"+ + "/vpn-service/vpn-id"; + } + description + "Reference to an IP VPN. Referencing a vpn-id provides + an easy way to attach a particular logical access to + a VPN. In this case, vpn-id must be configured."; + } + leaf site-role { + type identityref { + base site-role; + } + default any-to-any-role; + description + "Role of the site in the IP VPN. When referencing a vpn-id, + the site-role setting must be added to express the role of + + the site in the target VPN service topology."; + } + } + mandatory true; + description + "Choice for VPN attachment flavor. A choice is implemented + to allow the user to choose the flavor that provides the + best fit."; + } + description + "Defines VPN attachment of a site."; + } + description + "Defines the VPN attachment rules for + a site's logical access."; + } + grouping vpn-profile-cfg { + container valid-provider-identifiers { + list cloud-identifier { + if-feature cloud-access; + key id; + leaf id { + type string; + description + "Identification of cloud service. + Local administration meaning."; + } + description + "List for Cloud Identifiers."; + } + list encryption-profile-identifier { + key id; + leaf id { + type string; + description + "Identification of the SP encryption profile + to be used. Local administration meaning."; + } + description + "List for encryption profile identifiers."; + } + list qos-profile-identifier { + key id; + leaf id { + type string; + description + "Identification of the QoS Profile to be used. + Local administration meaning."; + + } + description + "List for QoS Profile Identifiers."; + } + list bfd-profile-identifier { + key id; + leaf id { + type string; + description + "Identification of the SP BFD Profile to be used. + Local administration meaning."; + } + description + "List for BFD Profile identifiers."; + } + nacm:default-deny-write; + description + "Container for Valid Provider Identifies."; + } + description + "Grouping for VPN Profile configuration."; + } + grouping vpn-svc-cfg { + leaf vpn-id { + type svc-id; + description + "VPN identifier. Local administration meaning."; + } + leaf customer-name { + type string; + description + "Name of the customer that actually uses the VPN service. + In the case that any intermediary (e.g., Tier-2 provider + or partner) sells the VPN service to their end user + on behalf of the original service provider (e.g., Tier-1 + provider), the original service provider may require the + customer name to provide smooth activation/commissioning + and operation for the service."; + } + leaf vpn-service-topology { + type identityref { + base vpn-topology; + } + default any-to-any; + description + "VPN service topology."; + } + uses vpn-service-cloud-access; + + uses vpn-service-multicast; + uses vpn-service-mpls; + uses vpn-extranet; + description + "Grouping for VPN service configuration."; + } + grouping site-top-level-cfg { + uses operational-requirements; + uses customer-location-info; + uses site-devices; + uses site-diversity; + uses site-management; + uses site-vpn-policy; + uses site-vpn-flavor; + uses site-maximum-routes; + uses site-security; + uses site-service; + uses site-protection; + uses site-routing; + description + "Grouping for site top-level configuration."; + } + grouping site-network-access-top-level-cfg { + leaf site-network-access-type { + type identityref { + base site-network-access-type; + } + default point-to-point; + description + "Describes the type of connection, e.g., + point-to-point or multipoint."; + } + choice location-flavor { + case location { + when "derived-from-or-self(../../management/type, "+ + "'l3vpn-svc:customer-managed')" { + description + "Applicable only for customer-managed device."; + } + leaf location-reference { + type leafref { + path "../../../locations/location/location-id"; + } + description + "Location of the site-network-access."; + } + } + case device { + + when "derived-from-or-self(../../management/type, "+ + "'l3vpn-svc:provider-managed') or "+ + "derived-from-or-self(../../management/type, "+ + "'l3vpn-svc:co-managed')" { + description + "Applicable only for provider-managed or co-managed device."; + } + leaf device-reference { + type leafref { + path "../../../devices/device/device-id"; + } + description + "Identifier of CE to use."; + } + } + mandatory true; + description + "Choice of how to describe the site's location."; + } + uses access-diversity; + uses site-attachment-bearer; + uses site-attachment-ip-connection; + uses site-security; + uses site-network-access-service; + uses site-routing; + uses site-attachment-availability; + uses access-vpn-policy; + description + "Grouping for site network access top-level configuration."; + } + /* Main blocks */ + container l3vpn-svc { + container vpn-profiles { + uses vpn-profile-cfg; + description + "Container for VPN Profiles."; + } + container vpn-services { + list vpn-service { + key vpn-id; + uses vpn-svc-cfg; + description + "List of VPN services."; + } + description + "Top-level container for the VPN services."; + } + container sites { + list site { + key site-id; + leaf site-id { + type svc-id; + description + "Identifier of the site."; + } + uses site-top-level-cfg; + uses operational-requirements-ops; + container site-network-accesses { + list site-network-access { + key site-network-access-id; + leaf site-network-access-id { + type svc-id; + description + "Identifier for the access."; + } + uses site-network-access-top-level-cfg; + description + "List of accesses for a site."; + } + description + "List of accesses for a site."; + } + description + "List of sites."; + } + description + "Container for sites."; + } + description + "Main container for L3VPN service configuration."; + } + } diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-netconf-acm@2018-02-14.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-netconf-acm@2018-02-14.yang new file mode 100644 index 000000000..dc0deb8ff --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-netconf-acm@2018-02-14.yang @@ -0,0 +1,468 @@ +module ietf-netconf-acm { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; + + prefix nacm; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: + WG List: + + Author: Andy Bierman + + + Author: Martin Bjorklund + "; + + description + "Network Configuration Access Control Model. + + Copyright (c) 2012 - 2018 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8341; see + the RFC itself for full legal notices."; + + revision "2018-02-14" { + description + "Added support for YANG 1.1 actions and notifications tied to + data nodes. Clarified how NACM extensions can be used by + other data models."; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + revision "2012-02-22" { + description + "Initial version."; + reference + "RFC 6536: Network Configuration Protocol (NETCONF) + Access Control Model"; + } + + /* + * Extension statements + */ + + extension default-deny-write { + description + "Used to indicate that the data model node + represents a sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have write access to the node. An + explicit access control rule is required for all other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-write' extension MAY appear within a data + definition statement. It is ignored otherwise."; + } + + extension default-deny-all { + description + "Used to indicate that the data model node + controls a very sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have read, write, or execute access to + the node. An explicit access control rule is required for all + other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-all' extension MAY appear within a data + definition statement, 'rpc' statement, or 'notification' + statement. It is ignored otherwise."; + } + + /* + * Derived types + */ + + typedef user-name-type { + type string { + length "1..max"; + } + description + "General-purpose username string."; + } + + typedef matchall-string-type { + type string { + pattern '\*'; + } + description + "The string containing a single asterisk '*' is used + to conceptually represent all possible values + for the particular leaf using this data type."; + } + + typedef access-operations-type { + type bits { + bit create { + description + "Any protocol operation that creates a + new data node."; + position 0; + } + bit read { + description + "Any protocol operation or notification that + returns the value of a data node."; + position 1; + } + bit update { + description + "Any protocol operation that alters an existing + data node."; + position 2; + } + bit delete { + description + "Any protocol operation that removes a data node."; + position 3; + } + bit exec { + description + "Execution access to the specified protocol operation."; + position 4; + } + } + description + "Access operation."; + } + + typedef group-name-type { + type string { + length "1..max"; + pattern '[^\*].*'; + } + description + "Name of administrative group to which + users can be assigned."; + } + + typedef action-type { + type enumeration { + enum permit { + description + "Requested action is permitted."; + } + enum deny { + description + "Requested action is denied."; + } + } + description + "Action taken by the server when a particular + rule matches."; + } + + typedef node-instance-identifier { + type yang:xpath1.0; + description + "Path expression used to represent a special + data node, action, or notification instance-identifier + string. + + A node-instance-identifier value is an + unrestricted YANG instance-identifier expression. + All the same rules as an instance-identifier apply, + except that predicates for keys are optional. If a key + predicate is missing, then the node-instance-identifier + represents all possible server instances for that key. + + This XML Path Language (XPath) expression is evaluated in the + following context: + + o The set of namespace declarations are those in scope on + the leaf element where this type is used. + + o The set of variable bindings contains one variable, + 'USER', which contains the name of the user of the + current session. + + o The function library is the core function library, but + note that due to the syntax restrictions of an + instance-identifier, no functions are allowed. + + o The context node is the root node in the data tree. + + The accessible tree includes actions and notifications tied + to data nodes."; + } + + /* + * Data definition statements + */ + + container nacm { + nacm:default-deny-all; + + description + "Parameters for NETCONF access control model."; + + leaf enable-nacm { + type boolean; + default "true"; + description + "Enables or disables all NETCONF access control + enforcement. If 'true', then enforcement + is enabled. If 'false', then enforcement + is disabled."; + } + + leaf read-default { + type action-type; + default "permit"; + description + "Controls whether read access is granted if + no appropriate rule is found for a + particular read request."; + } + + leaf write-default { + type action-type; + default "deny"; + description + "Controls whether create, update, or delete access + is granted if no appropriate rule is found for a + particular write request."; + } + + leaf exec-default { + type action-type; + default "permit"; + description + "Controls whether exec access is granted if no appropriate + rule is found for a particular protocol operation request."; + } + + leaf enable-external-groups { + type boolean; + default "true"; + description + "Controls whether the server uses the groups reported by the + NETCONF transport layer when it assigns the user to a set of + NACM groups. If this leaf has the value 'false', any group + names reported by the transport layer are ignored by the + server."; + } + + leaf denied-operations { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request was denied."; + } + + leaf denied-data-writes { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request to alter + a configuration datastore was denied."; + } + + leaf denied-notifications { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that + a notification was dropped for a subscription because + access to the event type was denied."; + } + + container groups { + description + "NETCONF access control groups."; + + list group { + key name; + + description + "One NACM group entry. This list will only contain + configured entries, not any entries learned from + any transport protocols."; + + leaf name { + type group-name-type; + description + "Group name associated with this entry."; + } + + leaf-list user-name { + type user-name-type; + description + "Each entry identifies the username of + a member of the group associated with + this entry."; + } + } + } + + list rule-list { + key name; + ordered-by user; + description + "An ordered collection of access control rules."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule-list."; + } + leaf-list group { + type union { + type matchall-string-type; + type group-name-type; + } + description + "List of administrative groups that will be + assigned the associated access rights + defined by the 'rule' list. + + The string '*' indicates that all groups apply to the + entry."; + } + + list rule { + key name; + ordered-by user; + description + "One access control rule. + + Rules are processed in user-defined order until a match is + found. A rule matches if 'module-name', 'rule-type', and + 'access-operations' match the request. If a rule + matches, the 'action' leaf determines whether or not + access is granted."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule."; + } + + leaf module-name { + type union { + type matchall-string-type; + type string; + } + default "*"; + description + "Name of the module associated with this rule. + + This leaf matches if it has the value '*' or if the + object being accessed is defined in the module with the + specified module name."; + } + choice rule-type { + description + "This choice matches if all leafs present in the rule + match the request. If no leafs are present, the + choice matches all requests."; + case protocol-operation { + leaf rpc-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if + its value equals the requested protocol operation + name."; + } + } + case notification { + leaf notification-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if its + value equals the requested notification name."; + } + } + case data-node { + leaf path { + type node-instance-identifier; + mandatory true; + description + "Data node instance-identifier associated with the + data node, action, or notification controlled by + this rule. + + Configuration data or state data + instance-identifiers start with a top-level + data node. A complete instance-identifier is + required for this type of path value. + + The special value '/' refers to all possible + datastore contents."; + } + } + } + + leaf access-operations { + type union { + type matchall-string-type; + type access-operations-type; + } + default "*"; + description + "Access operations associated with this rule. + + This leaf matches if it has the value '*' or if the + bit corresponding to the requested operation is set."; + } + + leaf action { + type action-type; + mandatory true; + description + "The access control action associated with the + rule. If a rule has been determined to match a + particular request, then this object is used + to determine whether to permit or deny the + request."; + } + + leaf comment { + type string; + description + "A textual description of the access rule."; + } + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-yang-types@2013-07-15.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-yang-types@2013-07-15.yang new file mode 100644 index 000000000..956562a7b --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf-yang-types@2013-07-15.yang @@ -0,0 +1,475 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - yang-identifier + - hex-string + - uuid + - dotted-quad"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier-related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifiers. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type; the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of types related to date and time***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z + all represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using + the time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually + referring to the notion of local time) uses the time-offset + -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence + happened. The specific occurrence must be defined in the + description of any schema node defined using this type. When + the specific occurrence occurred prior to the last time the + associated timeticks attribute was zero, then the timestamp + value is zero. Note that this requires all timestamp values + to be reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML-specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + /*** collection of string types ***/ + + typedef hex-string { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + + description + "A hexadecimal string with octets represented as hex digits + separated by colons. The canonical representation uses + lowercase characters."; + } + + typedef uuid { + type string { + pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' + + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + } + description + "A Universally Unique IDentifier in the string representation + defined in RFC 4122. The canonical representation uses + lowercase characters. + + The following is an example of a UUID in string representation: + f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + "; + reference + "RFC 4122: A Universally Unique IDentifier (UUID) URN + Namespace"; + } + + typedef dotted-quad { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; + } + description + "An unsigned 32-bit number expressed in the dotted-quad + notation, i.e., four octets written as decimal numbers + and separated with the '.' (full stop) character."; + } + } diff --git a/src/nbi/tests/PrepareTestScenario.py b/src/nbi/tests/PrepareTestScenario.py index 88a77b6e0..3c1fb2739 100644 --- a/src/nbi/tests/PrepareTestScenario.py +++ b/src/nbi/tests/PrepareTestScenario.py @@ -23,6 +23,7 @@ from context.client.ContextClient import ContextClient from nbi.service.rest_server.RestServer import RestServer from nbi.service.rest_server.nbi_plugins.debug_api import register_debug_api from nbi.service.rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn +from nbi.service.rest_server.nbi_plugins.ietf_l3vpn import register_ietf_l3vpn from nbi.service.rest_server.nbi_plugins.ietf_network import register_ietf_network from nbi.tests.MockService_Dependencies import MockService_Dependencies from service.client.ServiceClient import ServiceClient @@ -49,6 +50,7 @@ def nbi_service_rest(mock_service : MockService_Dependencies): # pylint: disabl _rest_server = RestServer() register_debug_api(_rest_server) register_ietf_l2vpn(_rest_server) + register_ietf_l3vpn(_rest_server) register_ietf_network(_rest_server) _rest_server.start() time.sleep(1) # bring time for the server to start diff --git a/src/nbi/tests/data/ietf_l3vpn_req_svc1.json b/src/nbi/tests/data/ietf_l3vpn_req_svc1.json new file mode 100644 index 000000000..66e253cb5 --- /dev/null +++ b/src/nbi/tests/data/ietf_l3vpn_req_svc1.json @@ -0,0 +1,231 @@ +{ + "ietf-l3vpn-svc:l3vpn-svc": { + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "vpn1" + } + ] + }, + "sites": { + "site": [ + { + "site-id": "site_OLT", + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "locations": { + "location": [ + { + "location-id": "OLT" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "128.32.33.5", + "location": "OLT" + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.1/24", + "lan-tag": "vlan21", + "next-hop": "128.32.33.5" + }, + { + "lan": "128.32.20.1/24", + "lan-tag": "vlan21", + "next-hop": "128.32.33.5" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "500", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "128.32.33.5", + "vpn-attachment": { + "vpn-id": "vpn1", + "site-role": "ietf-l3vpn-svc:spoke-role" + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "128.32.33.254", + "customer-address": "128.32.33.2", + "prefix-length": 24 + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.1/24", + "lan-tag": "vlan21", + "next-hop": "10.0.10.1" + } + ] + } + } + } + ] + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + } + } + ] + } + } + } + } + } + ] + } + }, + { + "site-id": "site_POP", + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "locations": { + "location": [ + { + "location-id": "POP" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "172.10.33.5", + "location": "POP" + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.1/24", + "lan-tag": "vlan101", + "next-hop": "172.10.33.5" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "500", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "172.10.33.5", + "vpn-attachment": { + "vpn-id": "vpn1", + "site-role": "ietf-l3vpn-svc:hub-role" + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "172.10.33.254", + "customer-address": "172.10.33.2", + "prefix-length": 24 + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.1/24", + "lan-tag": "vlan101", + "next-hop": "10.0.30.1" + }, + { + "lan": "128.32.20.1/24", + "lan-tag": "vlan101", + "next-hop": "10.0.30.1" + } + ] + } + } + } + ] + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + } + } + ] + } + } + } + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/nbi/tests/data/ietf_l3vpn_req_svc2.json b/src/nbi/tests/data/ietf_l3vpn_req_svc2.json new file mode 100644 index 000000000..fa142f4fa --- /dev/null +++ b/src/nbi/tests/data/ietf_l3vpn_req_svc2.json @@ -0,0 +1,231 @@ +{ + "ietf-l3vpn-svc:l3vpn-svc": { + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "vpn2" + } + ] + }, + "sites": { + "site": [ + { + "site-id": "site_OLT", + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "locations": { + "location": [ + { + "location-id": "OLT" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "128.32.33.5", + "location": "OLT" + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.1/24", + "lan-tag": "vlan31", + "next-hop": "128.32.33.5" + }, + { + "lan": "128.32.20.1/24", + "lan-tag": "vlan31", + "next-hop": "128.32.33.5" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "service access 3", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "128.32.33.5", + "vpn-attachment": { + "vpn-id": "vpn2", + "site-role": "ietf-l3vpn-svc:spoke-role" + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "128.32.33.254", + "customer-address": "128.32.33.2", + "prefix-length": 24 + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.1/24", + "lan-tag": "vlan31", + "next-hop": "10.0.10.1" + } + ] + } + } + } + ] + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + } + } + ] + } + } + } + } + } + ] + } + }, + { + "site-id": "site_POP", + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "locations": { + "location": [ + { + "location-id": "POP" + } + ] + }, + "devices": { + "device": [ + { + "device-id": "172.10.33.5", + "location": "POP" + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.1/24", + "lan-tag": "vlan201", + "next-hop": "172.10.33.1" + } + ] + } + } + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "service access 4", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "172.10.33.5", + "vpn-attachment": { + "vpn-id": "vpn2", + "site-role": "ietf-l3vpn-svc:hub-role" + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "172.10.33.254", + "customer-address": "172.10.33.2", + "prefix-length": 24 + } + } + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.1/24", + "lan-tag": "vlan201", + "next-hop": "10.0.30.1" + }, + { + "lan": "128.32.20.1/24", + "lan-tag": "vlan201", + "next-hop": "10.0.30.1" + } + ] + } + } + } + ] + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + } + } + ] + } + } + } + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/nbi/tests/test_ietf_l3vpn.py b/src/nbi/tests/test_ietf_l3vpn.py new file mode 100644 index 000000000..17f3c3e93 --- /dev/null +++ b/src/nbi/tests/test_ietf_l3vpn.py @@ -0,0 +1,113 @@ +# 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 json, logging, pytest +from typing import Dict +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId +from common.tools.descriptor.Loader import ( + DescriptorLoader, check_descriptor_load_results, validate_empty_scenario +) +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from nbi.service.rest_server import RestServer +from .PrepareTestScenario import ( # pylint: disable=unused-import + # be careful, order of symbols is important here! + do_rest_delete_request, do_rest_get_request, do_rest_post_request, + mock_service, nbi_service_rest, osm_wim, context_client +) + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = 'nbi/tests/data/topology-dummy.json' +SVC1_DATA_FILE = 'nbi/tests/data/ietf_l3vpn_req_svc1.json' +SVC2_DATA_FILE = 'nbi/tests/data/ietf_l3vpn_req_svc2.json' + +JSON_ADMIN_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_NAME) +ADMIN_CONTEXT_ID = ContextId(**JSON_ADMIN_CONTEXT_ID) + +@pytest.fixture(scope='session') +def storage() -> Dict: + yield dict() + +def test_prepare_environment(context_client : ContextClient) -> None: # pylint: disable=redefined-outer-name + validate_empty_scenario(context_client) + descriptor_loader = DescriptorLoader(descriptors_file=DESCRIPTOR_FILE, context_client=context_client) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + descriptor_loader.validate() + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.topology_ids) == 1 + assert len(response.service_ids ) == 0 + assert len(response.slice_ids ) == 0 + +# pylint: disable=redefined-outer-name, unused-argument +def test_create_svc1(nbi_service_rest : RestServer, storage : Dict): + with open(SVC1_DATA_FILE, 'r', encoding='UTF-8') as f: + svc1_data = json.load(f) + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services' + do_rest_post_request(URL, body=svc1_data, logger=LOGGER, expected_status_codes={201}) + storage['svc1-uuid'] = svc1_data['ietf-l3vpn-svc:l3vpn-svc']['vpn-services']['vpn-service'][0]['vpn-id'] + +# pylint: disable=redefined-outer-name, unused-argument +def test_create_svc2(nbi_service_rest : RestServer, storage : Dict): + with open(SVC2_DATA_FILE, 'r', encoding='UTF-8') as f: + svc2_data = json.load(f) + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services' + do_rest_post_request(URL, body=svc2_data, logger=LOGGER, expected_status_codes={201}) + storage['svc2-uuid'] = svc2_data['ietf-l3vpn-svc:l3vpn-svc']['vpn-services']['vpn-service'][0]['vpn-id'] + +# pylint: disable=redefined-outer-name, unused-argument +def test_get_state_svc1(nbi_service_rest : RestServer, storage : Dict): + assert 'svc1-uuid' in storage + service_uuid = storage['svc1-uuid'] + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service={:s}/'.format(service_uuid) + do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + +# pylint: disable=redefined-outer-name, unused-argument +def test_get_state_svc2(nbi_service_rest : RestServer, storage : Dict): + assert 'svc2-uuid' in storage + service_uuid = storage['svc2-uuid'] + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service={:s}/'.format(service_uuid) + do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + +# pylint: disable=redefined-outer-name, unused-argument +def test_delete_svc1(nbi_service_rest : RestServer, storage : Dict): + assert 'svc1-uuid' in storage + service_uuid = storage['svc1-uuid'] + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service={:s}/'.format(service_uuid) + do_rest_delete_request(URL, logger=LOGGER, expected_status_codes={204}) + +# pylint: disable=redefined-outer-name, unused-argument +def test_delete_svc2(nbi_service_rest : RestServer, storage : Dict): + assert 'svc2-uuid' in storage + service_uuid = storage['svc2-uuid'] + URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service={:s}/'.format(service_uuid) + do_rest_delete_request(URL, logger=LOGGER, expected_status_codes={204}) + +def test_cleanup_environment(context_client : ContextClient) -> None: # pylint: disable=redefined-outer-name + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.topology_ids) == 1 + assert len(response.service_ids ) == 0 + assert len(response.slice_ids ) == 0 + + # Load descriptors and validate the base scenario + descriptor_loader = DescriptorLoader(descriptors_file=DESCRIPTOR_FILE, context_client=context_client) + descriptor_loader.validate() + descriptor_loader.unload() + validate_empty_scenario(context_client) -- GitLab From 505f372f921464f63d53172bf618bbdec578a9a6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 22 Dec 2023 13:04:30 +0000 Subject: [PATCH 2/3] NBI Component - IETF L3VPN: - Fixed parsing of requests --- .../rest_server/nbi_plugins/ietf_l3vpn/Handlers.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py index c2d5d17ce..8490eb915 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py @@ -100,23 +100,27 @@ def process_site_network_access( ) -> None: endpoint_uuid = network_access['site-network-access-id'] - if network_access['type'] != 'ietf-l3vpn-svc:multipoint': + if network_access['site-network-access-type'] != 'ietf-l3vpn-svc:multipoint': MSG = 'Site Network Access Type: {:s}' - raise NotImplementedError(MSG.format(str(network_access['type']))) + raise NotImplementedError(MSG.format(str(network_access['site-network-access-type']))) device_uuid = network_access['device-reference'] service_uuid = network_access['vpn-attachment']['vpn-id'] access_role : str = network_access['vpn-attachment']['site-role'] access_role = access_role.replace('ietf-l3vpn-svc:', '').replace('-role', '') # hub/spoke + if access_role not in {'hub', 'spoke'}: + MSG = 'Site VPN Attackment Role: {:s}' + raise NotImplementedError(MSG.format(str(network_access['site-network-access-type']))) ipv4_allocation = network_access['ip-connection']['ipv4'] if ipv4_allocation['address-allocation-type'] != 'ietf-l3vpn-svc:static-address': MSG = 'Site Network Access IPv4 Allocation Type: {:s}' raise NotImplementedError(MSG.format(str(ipv4_allocation['address-allocation-type']))) - ipv4_provider_address = ipv4_allocation['provider-address'] - #ipv4_customer_address = ipv4_allocation['customer-address'] - ipv4_prefix_length = ipv4_allocation['prefix-length' ] + ipv4_allocation_addresses = ipv4_allocation['addresses'] + ipv4_provider_address = ipv4_allocation_addresses['provider-address'] + #ipv4_customer_address = ipv4_allocation_addresses['customer-address'] + ipv4_prefix_length = ipv4_allocation_addresses['prefix-length' ] vlan_tag = None ipv4_provider_range = netaddr.IPNetwork('{:s}/{:d}'.format(ipv4_provider_address, ipv4_prefix_length)) -- GitLab From 76f78369d087156e4846876cd9f9948eda515659 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 22 Dec 2023 13:08:06 +0000 Subject: [PATCH 3/3] Common: - Added installation of libyang to install_requirements script --- install_requirements.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/install_requirements.sh b/install_requirements.sh index 74db1cb34..c59ea7f13 100755 --- a/install_requirements.sh +++ b/install_requirements.sh @@ -24,6 +24,22 @@ ALL_COMPONENTS="${ALL_COMPONENTS} dbscanserving opticalattackmitigator opticalat ALL_COMPONENTS="${ALL_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector" TFS_COMPONENTS=${TFS_COMPONENTS:-$ALL_COMPONENTS} +# Some components require libyang built from source code +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +echo "Installing libyang..." +sudo apt-get --yes --quiet --quiet update +sudo apt-get --yes --quiet --quiet install build-essential cmake libpcre2-dev python3-dev python3-cffi +mkdir libyang +git clone https://github.com/CESNET/libyang.git libyang +mkdir libyang/build +cd libyang/build +cmake -D CMAKE_BUILD_TYPE:String="Release" .. +make +sudo make install +sudo ldconfig +cd ../.. + echo "Updating PIP, SetupTools and Wheel..." pip install --upgrade pip # ensure next packages get the latest versions pip install --upgrade setuptools wheel # bring basic tooling for other requirements -- GitLab