Skip to content
Snippets Groups Projects
Commit 5ece61dc authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch 'develop' of ssh://gifrerenom_labs.etsi.org/tfs/controller into...

Merge branch 'develop' of ssh://gifrerenom_labs.etsi.org/tfs/controller into feat/199-cttc-add-missing-ci-cd-descriptor-for-analytics-component
parents caea0622 b830b87a
No related branches found
No related tags found
2 merge requests!294Release TeraFlowSDN 4.0,!271Resolve "(CTTC) Add missing CI/CD descriptor for Analytics component"
...@@ -38,6 +38,8 @@ spec: ...@@ -38,6 +38,8 @@ spec:
env: env:
- name: LOG_LEVEL - name: LOG_LEVEL
value: "INFO" value: "INFO"
- name: IETF_NETWORK_RENDERER
value: "LIBYANG"
readinessProbe: readinessProbe:
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:9090"] command: ["/bin/grpc_health_probe", "-addr=:9090"]
......
...@@ -33,20 +33,25 @@ ...@@ -33,20 +33,25 @@
# # do test ... # # do test ...
# descriptor_loader.unload() # descriptor_loader.unload()
import concurrent.futures, json, logging, operator import concurrent.futures, copy, json, logging, operator
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union
from common.proto.context_pb2 import ( from common.proto.context_pb2 import (
Connection, Context, ContextId, Device, DeviceId, Empty, Link, LinkId, Service, ServiceId, Slice, SliceId, Connection, Context, ContextId, Device, DeviceId, Empty,
Topology, TopologyId) Link, LinkId, Service, ServiceId, Slice, SliceId,
Topology, TopologyId
)
from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Context import json_context_id
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient from device.client.DeviceClient import DeviceClient
from service.client.ServiceClient import ServiceClient from service.client.ServiceClient import ServiceClient
from slice.client.SliceClient import SliceClient from slice.client.SliceClient import SliceClient
from .Tools import ( from .Tools import (
format_device_custom_config_rules, format_service_custom_config_rules, format_slice_custom_config_rules, format_device_custom_config_rules, format_service_custom_config_rules,
get_descriptors_add_contexts, get_descriptors_add_services, get_descriptors_add_slices, format_slice_custom_config_rules, get_descriptors_add_contexts,
get_descriptors_add_topologies, split_controllers_and_network_devices, split_devices_by_rules) get_descriptors_add_services, get_descriptors_add_slices,
get_descriptors_add_topologies, split_controllers_and_network_devices,
split_devices_by_rules
)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
LOGGERS = { LOGGERS = {
...@@ -78,6 +83,30 @@ TypeResults = List[Tuple[str, str, int, List[str]]] # entity_name, action, num_o ...@@ -78,6 +83,30 @@ TypeResults = List[Tuple[str, str, int, List[str]]] # entity_name, action, num_o
TypeNotification = Tuple[str, str] # message, level TypeNotification = Tuple[str, str] # message, level
TypeNotificationList = List[TypeNotification] TypeNotificationList = List[TypeNotification]
SLICE_TEMPLATE = {
"slice_id": {
"context_id": {"context_uuid": {"uuid": "admin"}},
"slice_uuid": {"uuid": None}
},
"name": {},
"slice_config": {"config_rules": [
{"action": 1, "custom": {"resource_key": "/settings", "resource_value": {
"address_families": ["IPV4"], "bgp_as": 65000,
"bgp_route_target": "65000:333", "mtu": 1512
}}}
]},
"slice_constraints": [
{"sla_capacity": {"capacity_gbps": 20.0}},
{"sla_availability": {"availability": 20.0, "num_disjoint_paths": 1, "all_active": True}},
{"sla_isolation": {"isolation_level": [0]}}
],
"slice_endpoint_ids": [
],
"slice_status": {"slice_status": 1}
}
class DescriptorLoader: class DescriptorLoader:
def __init__( def __init__(
self, descriptors : Optional[Union[str, Dict]] = None, descriptors_file : Optional[str] = None, self, descriptors : Optional[Union[str, Dict]] = None, descriptors_file : Optional[str] = None,
...@@ -106,8 +135,53 @@ class DescriptorLoader: ...@@ -106,8 +135,53 @@ class DescriptorLoader:
self.__links = self.__descriptors.get('links' , []) self.__links = self.__descriptors.get('links' , [])
self.__services = self.__descriptors.get('services' , []) self.__services = self.__descriptors.get('services' , [])
self.__slices = self.__descriptors.get('slices' , []) self.__slices = self.__descriptors.get('slices' , [])
self.__ietf_slices = self.__descriptors.get('ietf-network-slice-service:network-slice-services', {})
self.__connections = self.__descriptors.get('connections', []) self.__connections = self.__descriptors.get('connections', [])
if len(self.__ietf_slices) > 0:
for slice_service in self.__ietf_slices["slice-service"]:
tfs_slice = copy.deepcopy(SLICE_TEMPLATE)
tfs_slice["slice_id"]["slice_uuid"]["uuid"] = slice_service["id"]
tfs_slice["name"] = slice_service["description"]
for sdp in slice_service["sdps"]["sdp"]:
sdp_id = sdp["id"]
for attcircuit in sdp["attachment-circuits"]["attachment-circuit"]:
att_cir_tp_id = attcircuit["ac-tp-id"]
RESOURCE_KEY = "/device[{:s}]/endpoint[{:s}]/settings"
resource_key = RESOURCE_KEY.format(str(sdp_id), str(att_cir_tp_id))
for tag in attcircuit['ac-tags']['ac-tag']:
if tag.get('tag-type') == 'ietf-nss:vlan-id':
vlan_id = tag.get('value')
else:
vlan_id = 0
tfs_slice["slice_config"]["config_rules"].append({
"action": 1, "custom": {
"resource_key": resource_key, "resource_value": {
"router_id": sdp.get("node-id",[]),
"sub_interface_index": 0,
"vlan_id": vlan_id
}
}
})
tfs_slice["slice_endpoint_ids"].append({
"device_id": {"device_uuid": {"uuid": sdp_id}},
"endpoint_uuid": {"uuid": att_cir_tp_id},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}},
"topology_uuid": {"uuid": "admin"}}
})
#tfs_slice["slice_constraints"].append({
# "endpoint_location": {
# "endpoint_id": {
# "device_id": {"device_uuid": {"uuid": sdp["id"]}},
# "endpoint_uuid": {"uuid": attcircuit["ac-tp-id"]}
# },
# "location": {"region": "4"}
# }
#})
self.__slices.append(tfs_slice)
self.__contexts_add = None self.__contexts_add = None
self.__topologies_add = None self.__topologies_add = None
self.__devices_add = None self.__devices_add = None
...@@ -232,7 +306,9 @@ class DescriptorLoader: ...@@ -232,7 +306,9 @@ class DescriptorLoader:
def _load_dummy_mode(self) -> None: def _load_dummy_mode(self) -> None:
# Dummy Mode: used to pre-load databases (WebUI debugging purposes) with no smart or automated tasks. # Dummy Mode: used to pre-load databases (WebUI debugging purposes) with no smart or automated tasks.
controllers, network_devices = split_controllers_and_network_devices(self.__devices) controllers, network_devices = split_controllers_and_network_devices(self.__devices)
self.__ctx_cli.connect() self.__ctx_cli.connect()
self._process_descr('context', 'add', self.__ctx_cli.SetContext, Context, self.__contexts_add ) self._process_descr('context', 'add', self.__ctx_cli.SetContext, Context, self.__contexts_add )
self._process_descr('topology', 'add', self.__ctx_cli.SetTopology, Topology, self.__topologies_add) self._process_descr('topology', 'add', self.__ctx_cli.SetTopology, Topology, self.__topologies_add)
......
...@@ -19,7 +19,7 @@ class NameMappings: ...@@ -19,7 +19,7 @@ class NameMappings:
def __init__(self) -> None: def __init__(self) -> None:
self._device_uuid_to_name : Dict[str, str] = dict() self._device_uuid_to_name : Dict[str, str] = dict()
self._endpoint_uuid_to_name : Dict[Tuple[str, str], str] = dict() self._endpoint_uuid_to_name : Dict[Tuple[str, str], str] = dict()
def store_device_name(self, device : Device) -> None: def store_device_name(self, device : Device) -> None:
device_uuid = device.device_id.device_uuid.uuid device_uuid = device.device_id.device_uuid.uuid
device_name = device.name device_name = device.name
......
...@@ -12,19 +12,23 @@ ...@@ -12,19 +12,23 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json, logging import enum, json, logging
import pyangbind.lib.pybindJSON as pybindJSON import pyangbind.lib.pybindJSON as pybindJSON
from flask import request from flask import request
from flask.json import jsonify from flask.json import jsonify
from flask_restful import Resource from flask_restful import Resource
from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME
from common.Settings import get_setting
from common.proto.context_pb2 import ContextId, Empty
from common.tools.context_queries.Topology import get_topology_details from common.tools.context_queries.Topology import get_topology_details
from common.tools.object_factory.Context import json_context_id
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH
from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_OK, HTTP_SERVERERROR from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_OK, HTTP_SERVERERROR
from .bindings import ietf_network from .bindings import ietf_network
from .ComposeNetwork import compose_network from .ComposeNetwork import compose_network
from .ManualFixes import manual_fixes from .ManualFixes import manual_fixes
from .YangHandler import YangHandler
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
...@@ -33,6 +37,14 @@ TE_TOPOLOGY_NAMES = [ ...@@ -33,6 +37,14 @@ TE_TOPOLOGY_NAMES = [
'providerId-10-clientId-0-topologyId-2' 'providerId-10-clientId-0-topologyId-2'
] ]
class Renderer(enum.Enum):
LIBYANG = 'LIBYANG'
PYANGBIND = 'PYANGBIND'
DEFAULT_RENDERER = Renderer.LIBYANG
USE_RENDERER = get_setting('IETF_NETWORK_RENDERER', DEFAULT_RENDERER.value)
class Networks(Resource): class Networks(Resource):
@HTTP_AUTH.login_required @HTTP_AUTH.login_required
def get(self): def get(self):
...@@ -40,31 +52,59 @@ class Networks(Resource): ...@@ -40,31 +52,59 @@ class Networks(Resource):
topology_id = '' topology_id = ''
try: try:
context_client = ContextClient() context_client = ContextClient()
#target = get_slice_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)))
ietf_nets = ietf_network() if USE_RENDERER == Renderer.PYANGBIND.value:
#target = get_slice_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)))
ietf_nets = ietf_network()
topology_details = get_topology_details(
context_client, DEFAULT_TOPOLOGY_NAME, context_uuid=DEFAULT_CONTEXT_NAME,
#rw_copy=True
)
if topology_details is None:
MSG = 'Topology({:s}/{:s}) not found'
raise Exception(MSG.format(DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME))
topology_details = get_topology_details( for te_topology_name in TE_TOPOLOGY_NAMES:
context_client, DEFAULT_TOPOLOGY_NAME, context_uuid=DEFAULT_CONTEXT_NAME, #rw_copy=True ietf_net = ietf_nets.networks.network.add(te_topology_name)
) compose_network(ietf_net, te_topology_name, topology_details)
if topology_details is None:
MSG = 'Topology({:s}/{:s}) not found'
raise Exception(MSG.format(DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME))
for te_topology_name in TE_TOPOLOGY_NAMES: # TODO: improve these workarounds to enhance performance
ietf_net = ietf_nets.networks.network.add(te_topology_name) json_response = json.loads(pybindJSON.dumps(ietf_nets, mode='ietf'))
compose_network(ietf_net, te_topology_name, topology_details)
# Workaround; pyangbind does not allow to set otn_topology / eth-tran-topology
manual_fixes(json_response)
elif USE_RENDERER == Renderer.LIBYANG.value:
yang_handler = YangHandler()
json_response = []
# TODO: improve these workarounds to enhance performance contexts = context_client.ListContexts(Empty()).contexts
json_response = json.loads(pybindJSON.dumps(ietf_nets, mode='ietf')) context_names = [context.name for context in contexts]
LOGGER.info(f'Contexts detected: {context_names}')
# Workaround; pyangbind does not allow to set otn_topology / eth-tran-topology
manual_fixes(json_response) for context_name in context_names:
topologies = context_client.ListTopologies(ContextId(**json_context_id(context_name))).topologies
topology_names = [topology.name for topology in topologies]
LOGGER.info(f'Topologies detected for context {context_name}: {topology_names}')
for topology_name in topology_names:
topology_details = get_topology_details(context_client, topology_name, context_name)
if topology_details is None:
raise Exception(f'Topology({context_name}/{topology_name}) not found')
network_reply = yang_handler.compose_network(topology_name, topology_details)
json_response.append(network_reply)
yang_handler.destroy()
else:
raise Exception('Unsupported Renderer: {:s}'.format(str(USE_RENDERER)))
response = jsonify(json_response) response = jsonify(json_response)
response.status_code = HTTP_OK response.status_code = HTTP_OK
except Exception as e: # pylint: disable=broad-except except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Retrieving Topology({:s})'.format(str(topology_id))) LOGGER.exception('Something went wrong Retrieving Topology({:s})'.format(str(topology_id)))
response = jsonify({'error': str(e)}) response = jsonify({'error': str(e)})
......
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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
import libyang, logging, os
from typing import Any
from common.proto.context_pb2 import TopologyDetails, Device, Link
from .NameMapping import NameMappings
from context.client.ContextClient import ContextClient
from common.tools.object_factory.Device import json_device_id
from common.proto.context_pb2 import DeviceId
LOGGER = logging.getLogger(__name__)
YANG_DIR = os.path.join(os.path.dirname(__file__), 'yang')
YANG_MODULES = ['ietf-network', 'ietf-network-topology', 'ietf-l3-unicast-topology']
class YangHandler:
def __init__(self) -> None:
self._yang_context = libyang.Context(YANG_DIR)
for yang_module_name in YANG_MODULES:
LOGGER.info('Loading module: {:s}'.format(str(yang_module_name)))
self._yang_context.load_module(yang_module_name).feature_enable_all()
def compose_network(self, te_topology_name: str, topology_details: TopologyDetails) -> dict:
networks = self._yang_context.create_data_path('/ietf-network:networks')
network = networks.create_path(f'network[network-id="{te_topology_name}"]')
network.create_path('network-id', te_topology_name)
network_types = network.create_path('network-types')
network_types.create_path('ietf-l3-unicast-topology:l3-unicast-topology')
name_mappings = NameMappings()
for device in topology_details.devices:
self.compose_node(device, name_mappings, network)
for link in topology_details.links:
self.compose_link(link, name_mappings, network)
return json.loads(networks.print_mem('json'))
def compose_node(self, dev: Device, name_mappings: NameMappings, network: Any) -> None:
device_name = dev.name
name_mappings.store_device_name(dev)
node = network.create_path(f'node[node-id="{device_name}"]')
node.create_path('node-id', device_name)
node_attributes = node.create_path('ietf-l3-unicast-topology:l3-node-attributes')
node_attributes.create_path('name', device_name)
context_client = ContextClient()
device = context_client.GetDevice(DeviceId(**json_device_id(device_name)))
for endpoint in device.device_endpoints:
name_mappings.store_endpoint_name(dev, endpoint)
self._process_device_config(device, node)
def _process_device_config(self, device: Device, node: Any) -> None:
for config in device.device_config.config_rules:
if config.WhichOneof('config_rule') != 'custom' or '/interface[' not in config.custom.resource_key:
continue
for endpoint in device.device_endpoints:
endpoint_name = endpoint.name
if f'/interface[{endpoint_name}]' in config.custom.resource_key or f'/interface[{endpoint_name}.' in config.custom.resource_key:
interface_name = config.custom.resource_key.split('interface[')[1].split(']')[0]
self._create_termination_point(node, interface_name, endpoint_name, config.custom.resource_value)
def _create_termination_point(self, node: Any, interface_name: str, endpoint_name: str, resource_value: str) -> None:
ip_addresses = self._extract_ip_addresses(json.loads(resource_value))
if ip_addresses:
tp = node.create_path(f'ietf-network-topology:termination-point[tp-id="{interface_name}"]')
tp.create_path('tp-id', interface_name)
tp_attributes = tp.create_path('ietf-l3-unicast-topology:l3-termination-point-attributes')
for ip in ip_addresses:
tp_attributes.create_path('ip-address', ip)
tp_attributes.create_path('interface-name', endpoint_name)
@staticmethod
def _extract_ip_addresses(resource_value: dict) -> list:
ip_addresses = []
if 'address_ip' in resource_value:
ip_addresses.append(resource_value['address_ip'])
if 'address_ipv6' in resource_value:
ip_addresses.append(resource_value['address_ipv6'])
return ip_addresses
def compose_link(self, link_specs: Link, name_mappings: NameMappings, network: Any) -> None:
link_name = link_specs.name
links = network.create_path(f'ietf-network-topology:link[link-id="{link_name}"]')
links.create_path('link-id', link_name)
self._create_link_endpoint(links, 'source', link_specs.link_endpoint_ids[0], name_mappings)
self._create_link_endpoint(links, 'destination', link_specs.link_endpoint_ids[-1], name_mappings)
def _create_link_endpoint(self, links: Any, endpoint_type: str, endpoint_id: Any, name_mappings: NameMappings) -> None:
endpoint = links.create_path(endpoint_type)
if endpoint_type == 'destination': endpoint_type = 'dest'
endpoint.create_path(f'{endpoint_type}-node', name_mappings.get_device_name(endpoint_id.device_id))
endpoint.create_path(f'{endpoint_type}-tp', name_mappings.get_endpoint_name(endpoint_id))
def destroy(self) -> None:
self._yang_context.destroy()
module ietf-l3-unicast-topology {
yang-version 1.1;
namespace
"urn:ietf:params:xml:ns:yang:ietf-l3-unicast-topology";
prefix "l3t";
import ietf-network {
prefix "nw";
}
import ietf-network-topology {
prefix "nt";
}
import ietf-inet-types {
prefix "inet";
}
import ietf-routing-types {
prefix "rt-types";
}
organization
"IETF I2RS (Interface to the Routing System) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/i2rs/>
WG List: <mailto:i2rs@ietf.org>
Editor: Alexander Clemm
<mailto:ludwig@clemm.org>
Editor: Jan Medved
<mailto:jmedved@cisco.com>
Editor: Robert Varga
<mailto:robert.varga@pantheon.tech>
Editor: Xufeng Liu
<mailto:xufeng.liu.ietf@gmail.com>
Editor: Nitin Bahadur
<mailto:nitin_bahadur@yahoo.com>
Editor: Hariharan Ananthakrishnan
<mailto:hari@packetdesign.com>";
description
"This module defines a model for Layer 3 Unicast
topologies.
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 8346; see the RFC itself for full legal notices.";
revision "2018-02-26" {
description
"Initial revision.";
reference
"RFC 8346: A YANG Data Model for Layer 3 Topologies";
}
identity flag-identity {
description "Base type for flags";
}
typedef l3-event-type {
type enumeration {
enum "add" {
description
"A Layer 3 node, link, prefix, or termination point has
been added";
}
enum "remove" {
description
"A Layer 3 node, link, prefix, or termination point has
been removed";
}
enum "update" {
description
"A Layer 3 node, link, prefix, or termination point has
been updated";
}
}
description "Layer 3 event type for notifications";
}
typedef prefix-flag-type {
type identityref {
base "flag-identity";
}
description "Prefix flag attributes";
}
typedef node-flag-type {
type identityref {
base "flag-identity";
}
description "Node flag attributes";
}
typedef link-flag-type {
type identityref {
base "flag-identity";
}
description "Link flag attributes";
}
typedef l3-flag-type {
type identityref {
base "flag-identity";
}
description "L3 flag attributes";
}
grouping l3-prefix-attributes {
description
"L3 prefix attributes";
leaf prefix {
type inet:ip-prefix;
description
"IP prefix value";
}
leaf metric {
type uint32;
description
"Prefix metric";
}
leaf-list flag {
type prefix-flag-type;
description
"Prefix flags";
}
}
grouping l3-unicast-topology-type {
description "Identifies the topology type to be L3 Unicast.";
container l3-unicast-topology {
presence "indicates L3 Unicast topology";
description
"The presence of the container node indicates L3 Unicast
topology";
}
}
grouping l3-topology-attributes {
description "Topology scope attributes";
container l3-topology-attributes {
description "Contains topology attributes";
leaf name {
type string;
description
"Name of the topology";
}
leaf-list flag {
type l3-flag-type;
description
"Topology flags";
}
}
}
grouping l3-node-attributes {
description "L3 node scope attributes";
container l3-node-attributes {
description
"Contains node attributes";
leaf name {
type inet:domain-name;
description
"Node name";
}
leaf-list flag {
type node-flag-type;
description
"Node flags";
}
leaf-list router-id {
type rt-types:router-id;
description
"Router-id for the node";
}
list prefix {
key "prefix";
description
"A list of prefixes along with their attributes";
uses l3-prefix-attributes;
}
}
}
grouping l3-link-attributes {
description
"L3 link scope attributes";
container l3-link-attributes {
description
"Contains link attributes";
leaf name {
type string;
description
"Link Name";
}
leaf-list flag {
type link-flag-type;
description
"Link flags";
}
leaf metric1 {
type uint64;
description
"Link Metric 1";
}
leaf metric2 {
type uint64;
description
"Link Metric 2";
}
}
}
grouping l3-termination-point-attributes {
description "L3 termination point scope attributes";
container l3-termination-point-attributes {
description
"Contains termination point attributes";
choice termination-point-type {
description
"Indicates the termination point type";
case ip {
leaf-list ip-address {
type inet:ip-address;
description
"IPv4 or IPv6 address.";
}
}
case unnumbered {
leaf unnumbered-id {
type uint32;
description
"Unnumbered interface identifier.
The identifier will correspond to the ifIndex value
of the interface, i.e., the ifIndex value of the
ifEntry that represents the interface in
implementations where the Interfaces Group MIB
(RFC 2863) is supported.";
reference
"RFC 2863: The Interfaces Group MIB";
}
}
case interface-name {
leaf interface-name {
type string;
description
"Name of the interface. The name can (but does not
have to) correspond to an interface reference of a
containing node's interface, i.e., the path name of a
corresponding interface data node on the containing
node reminiscent of data type interface-ref defined
in RFC 8343. It should be noted that data type
interface-ref of RFC 8343 cannot be used directly,
as this data type is used to reference an interface
in a datastore of a single node in the network, not
to uniquely reference interfaces across a network.";
reference
"RFC 8343: A YANG Data Model for Interface Management";
}
}
}
}
}
augment "/nw:networks/nw:network/nw:network-types" {
description
"Introduces new network type for L3 Unicast topology";
uses l3-unicast-topology-type;
}
augment "/nw:networks/nw:network" {
when "nw:network-types/l3t:l3-unicast-topology" {
description
"Augmentation parameters apply only for networks with
L3 Unicast topology";
}
description
"L3 Unicast for the network as a whole";
uses l3-topology-attributes;
}
augment "/nw:networks/nw:network/nw:node" {
when "../nw:network-types/l3t:l3-unicast-topology" {
description
"Augmentation parameters apply only for networks with
L3 Unicast topology";
}
description
"L3 Unicast node-level attributes ";
uses l3-node-attributes;
}
augment "/nw:networks/nw:network/nt:link" {
when "../nw:network-types/l3t:l3-unicast-topology" {
description
"Augmentation parameters apply only for networks with
L3 Unicast topology";
}
description
"Augments topology link attributes";
uses l3-link-attributes;
}
augment "/nw:networks/nw:network/nw:node/"
+"nt:termination-point" {
when "../../nw:network-types/l3t:l3-unicast-topology" {
description
"Augmentation parameters apply only for networks with
L3 Unicast topology";
}
description "Augments topology termination point configuration";
uses l3-termination-point-attributes;
}
notification l3-node-event {
description
"Notification event for L3 node";
leaf l3-event-type {
type l3-event-type;
description
"Event type";
}
uses nw:node-ref;
uses l3-unicast-topology-type;
uses l3-node-attributes;
}
notification l3-link-event {
description
"Notification event for L3 link";
leaf l3-event-type {
type l3-event-type;
description
"Event type";
}
uses nt:link-ref;
uses l3-unicast-topology-type;
uses l3-link-attributes;
}
notification l3-prefix-event {
description
"Notification event for L3 prefix";
leaf l3-event-type {
type l3-event-type;
description
"Event type";
}
uses nw:node-ref;
uses l3-unicast-topology-type;
container prefix {
description
"Contains L3 prefix attributes";
uses l3-prefix-attributes;
}
}
notification termination-point-event {
description
"Notification event for L3 termination point";
leaf l3-event-type {
type l3-event-type;
description
"Event type";
}
uses nt:tp-ref;
uses l3-unicast-topology-type;
uses l3-termination-point-attributes;
}
}
...@@ -12,14 +12,20 @@ ...@@ -12,14 +12,20 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import deepdiff, json, logging, operator import deepdiff, json, logging, operator, os
from typing import Dict from typing import Dict
from common.Constants import DEFAULT_CONTEXT_NAME from common.Constants import DEFAULT_CONTEXT_NAME
from common.proto.context_pb2 import ContextId from common.proto.context_pb2 import ContextId
from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario 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 common.tools.object_factory.Context import json_context_id
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from nbi.service.rest_server import RestServer from nbi.service.rest_server import RestServer
# Explicitly state NBI to use PyangBind Renderer for this test
os.environ['IETF_NETWORK_RENDERER'] = 'PYANGBIND'
from .PrepareTestScenario import ( # pylint: disable=unused-import from .PrepareTestScenario import ( # pylint: disable=unused-import
# be careful, order of symbols is important here! # be careful, order of symbols is important here!
do_rest_get_request, mock_service, nbi_service_rest, osm_wim, context_client do_rest_get_request, mock_service, nbi_service_rest, osm_wim, context_client
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment