Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tfs/controller
1 result
Show changes
Showing
with 513 additions and 21 deletions
...@@ -22,11 +22,12 @@ from common.proto.context_pb2 import Empty, EventTypeEnum, Link, LinkId, LinkIdL ...@@ -22,11 +22,12 @@ from common.proto.context_pb2 import Empty, EventTypeEnum, Link, LinkId, LinkIdL
from common.message_broker.MessageBroker import MessageBroker from common.message_broker.MessageBroker import MessageBroker
from common.method_wrappers.ServiceExceptions import NotFoundException from common.method_wrappers.ServiceExceptions import NotFoundException
from common.tools.object_factory.Link import json_link_id from common.tools.object_factory.Link import json_link_id
from context.service.database.uuids.Topology import topology_get_uuid from .models.enums.LinkType import grpc_to_enum__link_type_enum
from .models.LinkModel import LinkModel, LinkEndPointModel from .models.LinkModel import LinkModel, LinkEndPointModel
from .models.TopologyModel import TopologyLinkModel, TopologyModel from .models.TopologyModel import TopologyLinkModel, TopologyModel
from .uuids.EndPoint import endpoint_get_uuid from .uuids.EndPoint import endpoint_get_uuid
from .uuids.Link import link_get_uuid from .uuids.Link import link_get_uuid
from .uuids.Topology import topology_get_uuid
from .Events import notify_event_context, notify_event_link, notify_event_topology from .Events import notify_event_context, notify_event_link, notify_event_topology
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
...@@ -68,7 +69,9 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) ...@@ -68,7 +69,9 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link)
link_name = raw_link_uuid if len(raw_link_name) == 0 else raw_link_name link_name = raw_link_uuid if len(raw_link_name) == 0 else raw_link_name
link_uuid = link_get_uuid(request.link_id, link_name=link_name, allow_random=True) link_uuid = link_get_uuid(request.link_id, link_name=link_name, allow_random=True)
now = datetime.datetime.utcnow() link_type = grpc_to_enum__link_type_enum(request.link_type)
now = datetime.datetime.now(datetime.timezone.utc)
topology_uuids : Set[str] = set() topology_uuids : Set[str] = set()
related_topologies : List[Dict] = list() related_topologies : List[Dict] = list()
...@@ -117,6 +120,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) ...@@ -117,6 +120,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link)
link_data = [{ link_data = [{
'link_uuid' : link_uuid, 'link_uuid' : link_uuid,
'link_name' : link_name, 'link_name' : link_name,
'link_type' : link_type,
'total_capacity_gbps' : total_capacity_gbps, 'total_capacity_gbps' : total_capacity_gbps,
'used_capacity_gbps' : used_capacity_gbps, 'used_capacity_gbps' : used_capacity_gbps,
'created_at' : now, 'created_at' : now,
...@@ -129,6 +133,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) ...@@ -129,6 +133,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link)
index_elements=[LinkModel.link_uuid], index_elements=[LinkModel.link_uuid],
set_=dict( set_=dict(
link_name = stmt.excluded.link_name, link_name = stmt.excluded.link_name,
link_type = stmt.excluded.link_type,
total_capacity_gbps = stmt.excluded.total_capacity_gbps, total_capacity_gbps = stmt.excluded.total_capacity_gbps,
used_capacity_gbps = stmt.excluded.used_capacity_gbps, used_capacity_gbps = stmt.excluded.used_capacity_gbps,
updated_at = stmt.excluded.updated_at, updated_at = stmt.excluded.updated_at,
......
...@@ -64,7 +64,7 @@ def optical_link_set(db_engine : Engine, messagebroker : MessageBroker, request ...@@ -64,7 +64,7 @@ def optical_link_set(db_engine : Engine, messagebroker : MessageBroker, request
link_name = raw_link_uuid if len(raw_link_name) == 0 else raw_link_name link_name = raw_link_uuid if len(raw_link_name) == 0 else raw_link_name
link_uuid = link_get_uuid(request.link_id, link_name=link_name, allow_random=True) link_uuid = link_get_uuid(request.link_id, link_name=link_name, allow_random=True)
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
# By default, always add link to default Context/Topology # By default, always add link to default Context/Topology
topology_uuids : Set[str] = set() topology_uuids : Set[str] = set()
......
...@@ -84,7 +84,7 @@ def policyrule_set(db_engine : Engine, messagebroker : MessageBroker, request : ...@@ -84,7 +84,7 @@ def policyrule_set(db_engine : Engine, messagebroker : MessageBroker, request :
'actionList': json_policyrule_basic.get('actionList', []), 'actionList': json_policyrule_basic.get('actionList', []),
}, sort_keys=True) }, sort_keys=True)
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
policyrule_data = [{ policyrule_data = [{
'policyrule_uuid' : policyrule_uuid, 'policyrule_uuid' : policyrule_uuid,
......
...@@ -91,7 +91,7 @@ def service_set(db_engine : Engine, messagebroker : MessageBroker, request : Ser ...@@ -91,7 +91,7 @@ def service_set(db_engine : Engine, messagebroker : MessageBroker, request : Ser
service_status = grpc_to_enum__service_status(request.service_status.service_status) service_status = grpc_to_enum__service_status(request.service_status.service_status)
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
service_endpoints_data : List[Dict] = list() service_endpoints_data : List[Dict] = list()
for i,endpoint_id in enumerate(request.service_endpoint_ids): for i,endpoint_id in enumerate(request.service_endpoint_ids):
...@@ -180,7 +180,8 @@ def service_unset(db_engine : Engine, messagebroker : MessageBroker, request : S ...@@ -180,7 +180,8 @@ def service_unset(db_engine : Engine, messagebroker : MessageBroker, request : S
['should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(raw_context_uuid)]) ['should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(raw_context_uuid)])
service_endpoint_uuids.add(endpoint_get_uuid(endpoint_id, allow_random=False)[2]) service_endpoint_uuids.add(endpoint_get_uuid(endpoint_id, allow_random=False)[2])
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
constraints = compose_constraints_data(request.service_constraints, now, service_uuid=service_uuid) constraints = compose_constraints_data(request.service_constraints, now, service_uuid=service_uuid)
config_rules = compose_config_rules_data(request.service_config.config_rules, now, service_uuid=service_uuid) config_rules = compose_config_rules_data(request.service_config.config_rules, now, service_uuid=service_uuid)
......
...@@ -89,7 +89,7 @@ def slice_set(db_engine : Engine, messagebroker : MessageBroker, request : Slice ...@@ -89,7 +89,7 @@ def slice_set(db_engine : Engine, messagebroker : MessageBroker, request : Slice
slice_status = grpc_to_enum__slice_status(request.slice_status.slice_status) slice_status = grpc_to_enum__slice_status(request.slice_status.slice_status)
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
slice_endpoints_data : List[Dict] = list() slice_endpoints_data : List[Dict] = list()
for i,endpoint_id in enumerate(request.slice_endpoint_ids): for i,endpoint_id in enumerate(request.slice_endpoint_ids):
...@@ -222,7 +222,8 @@ def slice_unset(db_engine : Engine, messagebroker : MessageBroker, request : Sli ...@@ -222,7 +222,8 @@ def slice_unset(db_engine : Engine, messagebroker : MessageBroker, request : Sli
for subslice_id in request.slice_subslice_ids for subslice_id in request.slice_subslice_ids
} }
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
constraints = compose_constraints_data(request.slice_constraints, now, slice_uuid=slice_uuid) constraints = compose_constraints_data(request.slice_constraints, now, slice_uuid=slice_uuid)
config_rules = compose_config_rules_data(request.slice_config.config_rules, now, slice_uuid=slice_uuid) config_rules = compose_config_rules_data(request.slice_config.config_rules, now, slice_uuid=slice_uuid)
......
...@@ -134,7 +134,8 @@ def topology_set(db_engine : Engine, messagebroker : MessageBroker, request : To ...@@ -134,7 +134,8 @@ def topology_set(db_engine : Engine, messagebroker : MessageBroker, request : To
MSG += 'Items in field "link_ids" ignored. This field is used for retrieval purposes only.' MSG += 'Items in field "link_ids" ignored. This field is used for retrieval purposes only.'
LOGGER.warning(MSG) LOGGER.warning(MSG)
now = datetime.datetime.utcnow() now = datetime.datetime.now(datetime.timezone.utc)
topology_data = [{ topology_data = [{
'context_uuid' : context_uuid, 'context_uuid' : context_uuid,
'topology_uuid': topology_uuid, 'topology_uuid': topology_uuid,
......
...@@ -13,17 +13,22 @@ ...@@ -13,17 +13,22 @@
# limitations under the License. # limitations under the License.
import operator import operator
from sqlalchemy import CheckConstraint, Column, DateTime, Float, ForeignKey, Integer, String from sqlalchemy import (
CheckConstraint, Column, DateTime, Enum, Float, ForeignKey, Integer, String
)
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from typing import Dict from typing import Dict
from common.proto.context_pb2 import LinkTypeEnum
from ._Base import _Base from ._Base import _Base
from .enums.LinkType import ORM_LinkTypeEnum
class LinkModel(_Base): class LinkModel(_Base):
__tablename__ = 'link' __tablename__ = 'link'
link_uuid = Column(UUID(as_uuid=False), primary_key=True) link_uuid = Column(UUID(as_uuid=False), primary_key=True)
link_name = Column(String, nullable=False) link_name = Column(String, nullable=False)
link_type = Column(Enum(ORM_LinkTypeEnum), nullable=False)
total_capacity_gbps = Column(Float, nullable=True) total_capacity_gbps = Column(Float, nullable=True)
used_capacity_gbps = Column(Float, nullable=True) used_capacity_gbps = Column(Float, nullable=True)
created_at = Column(DateTime, nullable=False) created_at = Column(DateTime, nullable=False)
...@@ -44,11 +49,14 @@ class LinkModel(_Base): ...@@ -44,11 +49,14 @@ class LinkModel(_Base):
result = { result = {
'link_id' : self.dump_id(), 'link_id' : self.dump_id(),
'name' : self.link_name, 'name' : self.link_name,
'link_type' : self.link_type.value,
'link_endpoint_ids': [ 'link_endpoint_ids': [
link_endpoint.endpoint.dump_id() link_endpoint.endpoint.dump_id()
for link_endpoint in sorted(self.link_endpoints, key=operator.attrgetter('position')) for link_endpoint in sorted(self.link_endpoints, key=operator.attrgetter('position'))
], ],
} }
if self.link_type is None:
self.link_type = LinkTypeEnum.LINKTYPE_UNKNOWN
if self.total_capacity_gbps is not None: if self.total_capacity_gbps is not None:
attributes : Dict = result.setdefault('attributes', dict()) attributes : Dict = result.setdefault('attributes', dict())
attributes.setdefault('total_capacity_gbps', self.total_capacity_gbps) attributes.setdefault('total_capacity_gbps', self.total_capacity_gbps)
......
...@@ -33,6 +33,9 @@ class ORM_DeviceDriverEnum(enum.Enum): ...@@ -33,6 +33,9 @@ class ORM_DeviceDriverEnum(enum.Enum):
GNMI_OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG GNMI_OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG
OPTICAL_TFS = DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS OPTICAL_TFS = DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS
IETF_ACTN = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN IETF_ACTN = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN
IETF_L3VPN = DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN
NCE = DeviceDriverEnum.DEVICEDRIVER_NCE
IETF_SLICE = DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE
OC = DeviceDriverEnum.DEVICEDRIVER_OC OC = DeviceDriverEnum.DEVICEDRIVER_OC
QKD = DeviceDriverEnum.DEVICEDRIVER_QKD QKD = DeviceDriverEnum.DEVICEDRIVER_QKD
......
# Copyright 2022-2024 ETSI 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 enum, functools
from common.proto.context_pb2 import LinkTypeEnum
from ._GrpcToEnum import grpc_to_enum
# IMPORTANT: Entries of enum class ORM_LinkTypeEnum should be named as in
# the proto files removing the prefixes. For example, proto item
# LinkTypeEnum.DEVICEDRIVER_COPPER should be included as COPPER.
# If item name does not match, automatic mapping of proto enums
# to database enums will fail.
class ORM_LinkTypeEnum(enum.Enum):
UNKNOWN = LinkTypeEnum.LINKTYPE_UNKNOWN
COPPER = LinkTypeEnum.LINKTYPE_COPPER
FIBER = LinkTypeEnum.LINKTYPE_FIBER
RADIO = LinkTypeEnum.LINKTYPE_RADIO
VIRTUAL = LinkTypeEnum.LINKTYPE_VIRTUAL
grpc_to_enum__link_type_enum = functools.partial(
grpc_to_enum, LinkTypeEnum, ORM_LinkTypeEnum
)
...@@ -24,13 +24,13 @@ Flask-HTTPAuth==4.5.0 ...@@ -24,13 +24,13 @@ Flask-HTTPAuth==4.5.0
Flask-RESTful==0.3.9 Flask-RESTful==0.3.9
ipaddress ipaddress
Jinja2==3.0.3 Jinja2==3.0.3
libyang==2.8.0 libyang==2.8.4
macaddress macaddress
ncclient==0.6.15 ncclient==0.6.15
numpy<2.0.0 numpy<2.0.0
p4runtime==1.3.0 p4runtime==1.3.0
pandas==1.5.* pandas==1.5.*
paramiko==2.9.2 paramiko==2.11.*
pyang==2.6.* pyang==2.6.*
git+https://github.com/robshakir/pyangbind.git git+https://github.com/robshakir/pyangbind.git
python-json-logger==2.0.2 python-json-logger==2.0.2
...@@ -39,7 +39,7 @@ python-json-logger==2.0.2 ...@@ -39,7 +39,7 @@ python-json-logger==2.0.2
requests==2.27.1 requests==2.27.1
requests-mock==1.9.3 requests-mock==1.9.3
tabulate tabulate
websockets==10.4 websockets==12.0
werkzeug==2.3.7 werkzeug==2.3.7
xmltodict==0.12.0 xmltodict==0.12.0
yattag yattag
......
...@@ -12,10 +12,11 @@ ...@@ -12,10 +12,11 @@
# 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 os
from common.Constants import ServiceNameEnum from common.Constants import ServiceNameEnum
from common.Settings import get_service_port_grpc from common.Settings import get_service_port_grpc
from common.proto.device_pb2 import DESCRIPTOR as DEVICE_DESCRIPTOR
from common.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server from common.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
from common.proto.optical_device_pb2 import DESCRIPTOR as OPTICAL_DEVICE_DESCRIPTOR
from common.proto.optical_device_pb2_grpc import add_OpenConfigServiceServicer_to_server from common.proto.optical_device_pb2_grpc import add_OpenConfigServiceServicer_to_server
from common.tools.service.GenericGrpcService import GenericGrpcService from common.tools.service.GenericGrpcService import GenericGrpcService
from device.Config import LOAD_ALL_DEVICE_DRIVERS from device.Config import LOAD_ALL_DEVICE_DRIVERS
...@@ -41,8 +42,11 @@ class DeviceService(GenericGrpcService): ...@@ -41,8 +42,11 @@ class DeviceService(GenericGrpcService):
def install_servicers(self): def install_servicers(self):
self.monitoring_loops.start() self.monitoring_loops.start()
add_DeviceServiceServicer_to_server(self.device_servicer, self.server) add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
self.add_reflection_service_name(DEVICE_DESCRIPTOR, 'DeviceService')
if LOAD_ALL_DEVICE_DRIVERS: if LOAD_ALL_DEVICE_DRIVERS:
add_OpenConfigServiceServicer_to_server(self.openconfig_device_servicer,self.server) add_OpenConfigServiceServicer_to_server(self.openconfig_device_servicer,self.server)
self.add_reflection_service_name(OPTICAL_DEVICE_DESCRIPTOR, 'OpenConfigService')
def stop(self): def stop(self):
super().stop() super().stop()
......
...@@ -25,12 +25,15 @@ RESOURCE_ROUTING_POLICIES = '__routing_policies__' ...@@ -25,12 +25,15 @@ RESOURCE_ROUTING_POLICIES = '__routing_policies__'
RESOURCE_SERVICES = '__services__' RESOURCE_SERVICES = '__services__'
RESOURCE_ACL = '__acl__' RESOURCE_ACL = '__acl__'
RESOURCE_INVENTORY = '__inventory__' RESOURCE_INVENTORY = '__inventory__'
RESOURCE_RULES = "__rules__"
class _Driver: class _Driver:
def __init__(self, name : str, address: str, port: int, **settings) -> None: def __init__(self, name : str, address: str, port: int, **settings) -> None:
""" Initialize Driver. """ Initialize Driver.
Parameters: Parameters:
name : str
Device driver name
address : str address : str
The address of the device The address of the device
port : int port : int
......
...@@ -81,6 +81,16 @@ DRIVERS.append( ...@@ -81,6 +81,16 @@ DRIVERS.append(
} }
])) ]))
from .ietf_l3vpn.driver import IetfL3VpnDriver # pylint: disable=wrong-import-position
DRIVERS.append(
(IetfL3VpnDriver, [
{
FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER,
FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN,
}
]))
from .ietf_actn.IetfActnDriver import IetfActnDriver # pylint: disable=wrong-import-position from .ietf_actn.IetfActnDriver import IetfActnDriver # pylint: disable=wrong-import-position
DRIVERS.append( DRIVERS.append(
(IetfActnDriver, [ (IetfActnDriver, [
...@@ -90,6 +100,24 @@ DRIVERS.append( ...@@ -90,6 +100,24 @@ DRIVERS.append(
} }
])) ]))
from .ietf_slice.driver import IetfSliceDriver # pylint: disable=wrong-import-position
DRIVERS.append(
(IetfSliceDriver, [
{
FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.IETF_SLICE,
FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE,
}
]))
from .nce.driver import NCEDriver # pylint: disable=wrong-import-position
DRIVERS.append(
(NCEDriver, [
{
FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.NCE,
FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_NCE,
}
]))
if LOAD_ALL_DEVICE_DRIVERS: if LOAD_ALL_DEVICE_DRIVERS:
from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position
DRIVERS.append( DRIVERS.append(
......
...@@ -82,6 +82,26 @@ def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Optional[Tuple[ ...@@ -82,6 +82,26 @@ def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Optional[Tuple[
if 'location' in endpoint_data: if 'location' in endpoint_data:
endpoint_resource_value['location'] = endpoint_data['location'] endpoint_resource_value['location'] = endpoint_data['location']
if "site_location" in endpoint_data:
endpoint_resource_value["site_location"] = endpoint_data["site_location"]
if "ce-ip" in endpoint_data:
endpoint_resource_value["ce-ip"] = endpoint_data["ce-ip"]
if "address_ip" in endpoint_data:
endpoint_resource_value["address_ip"] = endpoint_data["address_ip"]
if "address_prefix" in endpoint_data:
endpoint_resource_value["address_prefix"] = endpoint_data["address_prefix"]
if "mtu" in endpoint_data:
endpoint_resource_value["mtu"] = endpoint_data["mtu"]
if "ipv4_lan_prefixes" in endpoint_data:
endpoint_resource_value["ipv4_lan_prefixes"] = endpoint_data[
"ipv4_lan_prefixes"
]
return endpoint_resource_key, endpoint_resource_value return endpoint_resource_key, endpoint_resource_value
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
......
...@@ -40,7 +40,6 @@ class GnmiSessionHandler: ...@@ -40,7 +40,6 @@ class GnmiSessionHandler:
self._use_tls = settings.get('use_tls', False) self._use_tls = settings.get('use_tls', False)
self._channel : Optional[grpc.Channel] = None self._channel : Optional[grpc.Channel] = None
self._stub : Optional[gNMIStub] = None self._stub : Optional[gNMIStub] = None
self._yang_handler = None
self._monit_thread = None self._monit_thread = None
self._yang_handler = YangHandler() self._yang_handler = YangHandler()
self._subscriptions = Subscriptions() self._subscriptions = Subscriptions()
......
# Copyright 2022-2024 ETSI 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, libyang, logging, os
from typing import Dict
logging.basicConfig(level=logging.DEBUG)
LOGGER = logging.getLogger(__name__)
YANG_BASE_PATH = '/home/tfs/tfs-ctrl/src/device/service/drivers/gnmi_openconfig/git/openconfig/public'
YANG_SEARCH_PATHS = ':'.join([
os.path.join(YANG_BASE_PATH, 'release'),
os.path.join(YANG_BASE_PATH, 'third_party'),
])
YANG_MODULES = [
'iana-if-type',
'openconfig-bgp-types',
'openconfig-vlan-types',
'openconfig-interfaces',
'openconfig-if-8021x',
'openconfig-if-aggregate',
'openconfig-if-ethernet-ext',
'openconfig-if-ethernet',
'openconfig-if-ip-ext',
'openconfig-if-ip',
'openconfig-if-poe',
'openconfig-if-sdn-ext',
'openconfig-if-tunnel',
'openconfig-vlan',
'openconfig-types',
'openconfig-policy-types',
'openconfig-mpls-types',
'openconfig-network-instance-types',
'openconfig-network-instance',
'openconfig-platform',
'openconfig-platform-controller-card',
'openconfig-platform-cpu',
'openconfig-platform-ext',
'openconfig-platform-fabric',
'openconfig-platform-fan',
'openconfig-platform-integrated-circuit',
'openconfig-platform-linecard',
'openconfig-platform-pipeline-counters',
'openconfig-platform-port',
'openconfig-platform-psu',
'openconfig-platform-software',
'openconfig-platform-transceiver',
'openconfig-platform-types',
]
class YangHandler:
def __init__(self) -> None:
self._yang_context = libyang.Context(YANG_SEARCH_PATHS)
self._loaded_modules = set()
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()
self._loaded_modules.add(yang_module_name)
self._data_path_instances = dict()
def get_data_paths(self) -> Dict[str, libyang.DNode]:
return self._data_path_instances
def get_data_path(self, path : str) -> libyang.DNode:
data_path_instance = self._data_path_instances.get(path)
if data_path_instance is None:
data_path_instance = self._yang_context.create_data_path(path)
self._data_path_instances[path] = data_path_instance
return data_path_instance
def destroy(self) -> None:
self._yang_context.destroy()
def main():
yang_handler = YangHandler()
LOGGER.info('YangHandler Data (before):')
for path, dnode in yang_handler.get_data_paths().items():
LOGGER.info('|-> {:s}: {:s}'.format(str(path), json.dumps(dnode.print_dict())))
if_name = 'eth1'
sif_index = 0
enabled = True
address_ip = '172.16.0.1'
address_ip2 = '192.168.0.1'
address_prefix = 24
mtu = 1500
yang_ifs : libyang.DContainer = yang_handler.get_data_path('/openconfig-interfaces:interfaces')
yang_if_path = 'interface[name="{:s}"]'.format(if_name)
yang_if : libyang.DContainer = yang_ifs.create_path(yang_if_path)
yang_if.create_path('config/name', if_name)
yang_if.create_path('config/enabled', enabled)
yang_if.create_path('config/mtu', mtu )
yang_sifs : libyang.DContainer = yang_if.create_path('subinterfaces')
yang_sif_path = 'subinterface[index="{:d}"]'.format(sif_index)
yang_sif : libyang.DContainer = yang_sifs.create_path(yang_sif_path)
yang_sif.create_path('config/index', sif_index)
yang_sif.create_path('config/enabled', enabled )
yang_ipv4 : libyang.DContainer = yang_sif.create_path('openconfig-if-ip:ipv4')
yang_ipv4.create_path('config/enabled', enabled)
yang_ipv4_addrs : libyang.DContainer = yang_ipv4.create_path('addresses')
yang_ipv4_addr_path = 'address[ip="{:s}"]'.format(address_ip)
yang_ipv4_addr : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path)
yang_ipv4_addr.create_path('config/ip', address_ip )
yang_ipv4_addr.create_path('config/prefix-length', address_prefix)
yang_ipv4_addr_path2 = 'address[ip="{:s}"]'.format(address_ip2)
yang_ipv4_addr2 : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path2)
yang_ipv4_addr2.create_path('config/ip', address_ip2 )
yang_ipv4_addr2.create_path('config/prefix-length', address_prefix)
str_data = yang_if.print_mem('json')
json_data = json.loads(str_data)
json_data = json_data['openconfig-interfaces:interface'][0]
str_data = json.dumps(json_data, indent=4)
LOGGER.info('Resulting Request (before unlink): {:s}'.format(str_data))
yang_ipv4_addr2.unlink()
root_node : libyang.DContainer = yang_handler.get_data_path('/openconfig-interfaces:interfaces')
LOGGER.info('root_node={:s}'.format(str(root_node.print_mem('json'))))
for s in root_node.siblings():
LOGGER.info('sibling: {:s}'.format(str(s)))
PATH_TMPL = '/openconfig-interfaces:interfaces/interface[name="{:s}"]/subinterfaces/subinterface[index="{:d}"]'
yang_sif = root_node.find_path(PATH_TMPL.format(if_name, sif_index))
if yang_sif is not None:
LOGGER.info('yang_sif={:s}'.format(str(yang_sif.print_mem('json'))))
yang_sif.unlink()
yang_sif.free()
str_data = yang_if.print_mem('json')
json_data = json.loads(str_data)
json_data = json_data['openconfig-interfaces:interface'][0]
str_data = json.dumps(json_data, indent=4)
LOGGER.info('Resulting Request (after unlink): {:s}'.format(str_data))
yang_handler.destroy()
if __name__ == '__main__':
main()
...@@ -34,6 +34,21 @@ class InterfaceHandler(_Handler): ...@@ -34,6 +34,21 @@ class InterfaceHandler(_Handler):
PATH_TMPL = '/interfaces/interface[name={:s}]/subinterfaces/subinterface[index={:d}]' PATH_TMPL = '/interfaces/interface[name={:s}]/subinterfaces/subinterface[index={:d}]'
str_path = PATH_TMPL.format(if_name, sif_index) str_path = PATH_TMPL.format(if_name, sif_index)
str_data = json.dumps({}) str_data = json.dumps({})
root_node : libyang.DContainer = yang_handler.get_data_path(
'/openconfig-interfaces:interfaces'
)
yang_sif = root_node.find_path('/'.join([
'', # add slash at the beginning
'openconfig-interfaces:interfaces',
'interface[name="{:s}"]'.format(if_name),
'subinterfaces',
'subinterface[index="{:d}"]'.format(sif_index),
]))
if yang_sif is not None:
yang_sif.unlink()
yang_sif.free()
return str_path, str_data return str_path, str_data
enabled = get_bool(resource_value, 'enabled', True) # True/False enabled = get_bool(resource_value, 'enabled', True) # True/False
...@@ -69,7 +84,6 @@ class InterfaceHandler(_Handler): ...@@ -69,7 +84,6 @@ class InterfaceHandler(_Handler):
yang_ipv4_addr : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path) yang_ipv4_addr : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path)
yang_ipv4_addr.create_path('config/ip', address_ip) yang_ipv4_addr.create_path('config/ip', address_ip)
yang_ipv4_addr.create_path('config/prefix-length', address_prefix) yang_ipv4_addr.create_path('config/prefix-length', address_prefix)
if mtu is not None: yang_ipv4_addr.create_path('config/mtu', mtu)
str_path = '/interfaces/interface[name={:s}]'.format(if_name) str_path = '/interfaces/interface[name={:s}]'.format(if_name)
str_data = yang_if.print_mem('json') str_data = yang_if.print_mem('json')
......
...@@ -36,15 +36,13 @@ class NetworkInstanceInterfaceHandler(_Handler): ...@@ -36,15 +36,13 @@ class NetworkInstanceInterfaceHandler(_Handler):
if IS_CEOS: ni_if_id = if_name if IS_CEOS: ni_if_id = if_name
PATH_TMPL = '/network-instances/network-instance[name={:s}]/interfaces/interface[id={:s}]'
str_path = PATH_TMPL.format(ni_name, ni_if_id)
if delete: if delete:
PATH_TMPL = '/network-instances/network-instance[name={:s}]/interfaces/interface[id={:s}]'
str_path = PATH_TMPL.format(ni_name, ni_if_id)
str_data = json.dumps({}) str_data = json.dumps({})
return str_path, str_data return str_path, str_data
str_path = '/network-instances/network-instance[name={:s}]/interfaces/interface[id={:s}]'.format(
ni_name, ni_if_id
)
#str_data = json.dumps({ #str_data = json.dumps({
# 'id': if_id, # 'id': if_id,
# 'config': {'id': if_id, 'interface': if_name, 'subinterface': sif_index}, # 'config': {'id': if_id, 'interface': if_name, 'subinterface': sif_index},
......
# 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.
from device.service.driver_api._Driver import (
RESOURCE_ENDPOINTS,
RESOURCE_INTERFACES,
RESOURCE_NETWORK_INSTANCES,
)
SPECIAL_RESOURCE_MAPPINGS = {
RESOURCE_ENDPOINTS: "/endpoints",
RESOURCE_INTERFACES: "/interfaces",
RESOURCE_NETWORK_INSTANCES: "/net-instances",
}
# 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 logging
from typing import Dict, List, Optional
import requests
from requests.auth import HTTPBasicAuth
from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum
GET_DEVICES_URL = "{:s}://{:s}:{:d}/tfs-api/devices"
GET_LINKS_URL = "{:s}://{:s}:{:d}/tfs-api/links"
L3VPN_URL = "{:s}://{:s}:{:d}/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services"
TIMEOUT = 30
HTTP_OK_CODES = {
200, # OK
201, # Created
202, # Accepted
204, # No Content
}
MAPPING_STATUS = {
"DEVICEOPERATIONALSTATUS_UNDEFINED": 0,
"DEVICEOPERATIONALSTATUS_DISABLED": 1,
"DEVICEOPERATIONALSTATUS_ENABLED": 2,
}
MAPPING_DRIVER = {
"DEVICEDRIVER_UNDEFINED": 0,
"DEVICEDRIVER_OPENCONFIG": 1,
"DEVICEDRIVER_TRANSPORT_API": 2,
"DEVICEDRIVER_P4": 3,
"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY": 4,
"DEVICEDRIVER_ONF_TR_532": 5,
"DEVICEDRIVER_XR": 6,
"DEVICEDRIVER_IETF_L2VPN": 7,
"DEVICEDRIVER_GNMI_OPENCONFIG": 8,
"DEVICEDRIVER_OPTICAL_TFS": 9,
"DEVICEDRIVER_IETF_ACTN": 10,
"DEVICEDRIVER_OC": 11,
}
MSG_ERROR = "Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}"
LOGGER = logging.getLogger(__name__)
class TfsApiClient:
def __init__(
self,
address: str,
port: int,
scheme: str = "http",
username: Optional[str] = None,
password: Optional[str] = None,
) -> None:
self._devices_url = GET_DEVICES_URL.format(scheme, address, port)
self._links_url = GET_LINKS_URL.format(scheme, address, port)
self._l3vpn_url = L3VPN_URL.format(scheme, address, port)
self._auth = None
# (
# HTTPBasicAuth(username, password)
# if username is not None and password is not None
# else None
# )
def get_devices_endpoints(
self, import_topology: ImportTopologyEnum = ImportTopologyEnum.DEVICES
) -> List[Dict]:
LOGGER.debug("[get_devices_endpoints] begin")
LOGGER.debug(
"[get_devices_endpoints] import_topology={:s}".format(str(import_topology))
)
reply = requests.get(self._devices_url, timeout=TIMEOUT, auth=self._auth)
if reply.status_code not in HTTP_OK_CODES:
msg = MSG_ERROR.format(
str(self._devices_url), str(reply.status_code), str(reply)
)
LOGGER.error(msg)
raise Exception(msg)
if import_topology == ImportTopologyEnum.DISABLED:
raise Exception(
"Unsupported import_topology mode: {:s}".format(str(import_topology))
)
result = list()
for json_device in reply.json()["devices"]:
device_uuid: str = json_device["device_id"]["device_uuid"]["uuid"]
device_type: str = json_device["device_type"]
device_status = json_device["device_operational_status"]
device_url = "/devices/device[{:s}]".format(device_uuid)
device_data = {
"uuid": json_device["device_id"]["device_uuid"]["uuid"],
"name": json_device["name"],
"type": device_type,
"status": MAPPING_STATUS[device_status],
"drivers": [
MAPPING_DRIVER[driver] for driver in json_device["device_drivers"]
],
}
result.append((device_url, device_data))
for json_endpoint in json_device["device_endpoints"]:
endpoint_uuid = json_endpoint["endpoint_id"]["endpoint_uuid"]["uuid"]
endpoint_url = "/endpoints/endpoint[{:s}]".format(endpoint_uuid)
endpoint_data = {
"device_uuid": device_uuid,
"uuid": endpoint_uuid,
"name": json_endpoint["name"],
"type": json_endpoint["endpoint_type"],
}
result.append((endpoint_url, endpoint_data))
if import_topology == ImportTopologyEnum.DEVICES:
LOGGER.debug("[get_devices_endpoints] devices only; returning")
return result
reply = requests.get(self._links_url, timeout=TIMEOUT, auth=self._auth)
if reply.status_code not in HTTP_OK_CODES:
msg = MSG_ERROR.format(
str(self._links_url), str(reply.status_code), str(reply)
)
LOGGER.error(msg)
raise Exception(msg)
for json_link in reply.json()["links"]:
link_uuid: str = json_link["link_id"]["link_uuid"]["uuid"]
link_url = "/links/link[{:s}]".format(link_uuid)
link_endpoint_ids = [
(
json_endpoint_id["device_id"]["device_uuid"]["uuid"],
json_endpoint_id["endpoint_uuid"]["uuid"],
)
for json_endpoint_id in json_link["link_endpoint_ids"]
]
link_data = {
"uuid": json_link["link_id"]["link_uuid"]["uuid"],
"name": json_link["name"],
"endpoints": link_endpoint_ids,
}
result.append((link_url, link_data))
LOGGER.debug("[get_devices_endpoints] topology; returning")
return result
def create_connectivity_service(self, l3vpn_data: dict) -> None:
try:
requests.post(self._l3vpn_url, json=l3vpn_data)
LOGGER.debug(
"[create_connectivity_service] l3vpn_data={:s}".format(str(l3vpn_data))
)
except requests.exceptions.ConnectionError:
raise Exception("faild to send post request to TFS L3VPN NBI")
def update_connectivity_service(self, l3vpn_data: dict) -> None:
vpn_id = l3vpn_data['ietf-l3vpn-svc:l3vpn-svc']["vpn-services"]["vpn-service"][0]["vpn-id"]
url = self._l3vpn_url + f"/vpn-service={vpn_id}"
try:
requests.put(url, json=l3vpn_data)
LOGGER.debug(
"[update_connectivity_service] l3vpn_data={:s}".format(str(l3vpn_data))
)
except requests.exceptions.ConnectionError:
raise Exception("faild to send post request to TFS L3VPN NBI")
def delete_connectivity_service(self, service_uuid: str) -> None:
url = self._l3vpn_url + f"/vpn-service={service_uuid}"
try:
requests.delete(url, auth=self._auth)
LOGGER.debug("[delete_connectivity_service] url={:s}".format(str(url)))
except requests.exceptions.ConnectionError:
raise Exception("faild to send delete request to TFS L3VPN NBI")