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
Commits on Source (198)
Showing
with 1125 additions and 123 deletions
......@@ -32,6 +32,7 @@ MANIFEST
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
.manifest/
*.spec
# Installer logs
......
......@@ -77,13 +77,19 @@ service ContextService {
// ------------------------------ Experimental -----------------------------
rpc GetOpticalConfig (Empty ) returns (OpticalConfigList ) {}
rpc SetOpticalConfig (OpticalConfig ) returns (OpticalConfigId ) {}
rpc SelectOpticalConfig(OpticalConfigId) returns (OpticalConfig ) {}
rpc GetOpticalConfig (Empty ) returns (OpticalConfigList) {}
rpc SetOpticalConfig (OpticalConfig ) returns (OpticalConfigId ) {}
rpc UpdateOpticalConfig (OpticalConfig ) returns (OpticalConfigId ) {}
rpc SelectOpticalConfig (OpticalConfigId ) returns (OpticalConfig ) {}
rpc DeleteOpticalConfig (OpticalConfigId ) returns (Empty ) {}
rpc DeleteOpticalChannel (OpticalConfig ) returns (Empty ) {}
rpc SetOpticalLink (OpticalLink ) returns (Empty ) {}
rpc GetOpticalLink (OpticalLinkId ) returns (OpticalLink ) {}
rpc GetFiber (FiberId ) returns (Fiber ) {}
rpc SetOpticalLink (OpticalLink ) returns (Empty ) {}
rpc GetOpticalLink (LinkId ) returns (OpticalLink ) {}
rpc DeleteOpticalLink (LinkId ) returns (Empty ) {}
rpc GetOpticalLinkList (Empty ) returns (OpticalLinkList ) {}
rpc DeleteServiceConfigRule(ServiceConfigRule) returns (Empty ) {}
}
// ----- Generic -------------------------------------------------------------------------------------------------------
......@@ -148,6 +154,7 @@ message Topology {
string name = 2;
repeated DeviceId device_ids = 3;
repeated LinkId link_ids = 4;
repeated LinkId optical_link_ids = 5;
}
message TopologyDetails {
......@@ -155,6 +162,7 @@ message TopologyDetails {
string name = 2;
repeated Device devices = 3;
repeated Link links = 4;
repeated OpticalLink optical_links = 5;
}
message TopologyIdList {
......@@ -653,45 +661,57 @@ message OpticalConfigId {
message OpticalConfig {
OpticalConfigId opticalconfig_id = 1;
string config = 2;
DeviceId device_id = 3;
}
message OpticalConfigList {
repeated OpticalConfig opticalconfigs = 1;
}
message OpticalConfigEvent {
Event event = 1;
OpticalConfigId opticalconfig_id = 2;
}
// ---- Optical Link ----
message OpticalLinkId {
Uuid optical_link_uuid = 1;
message OpticalEndPointId {
DeviceId device_id = 2;
Uuid endpoint_uuid = 3;
}
message FiberId {
Uuid fiber_uuid = 1;
}
message Fiber {
string ID = 10;
string src_port = 1;
string dst_port = 2;
string local_peer_port = 3;
string remote_peer_port = 4;
repeated int32 c_slots = 5;
repeated int32 l_slots = 6;
repeated int32 s_slots = 7;
float length = 8;
bool used = 9;
FiberId fiber_uuid = 11;
message OpticalLinkList {
repeated OpticalLink optical_links = 1;
}
message OpticalLinkDetails {
float length = 1;
string source = 2;
string target = 3;
repeated Fiber fibers = 4;
string src_port = 2;
string dst_port = 3;
string local_peer_port = 4;
string remote_peer_port = 5 ;
bool used = 6 ;
map<string, int32> c_slots = 7;
map<string, int32> l_slots = 8;
map<string, int32> s_slots = 9;
}
message OpticalLink {
string name = 1;
OpticalLinkDetails details = 2;
OpticalLinkId optical_link_uuid = 3;
OpticalLinkDetails optical_details = 2;
LinkId link_id = 3;
repeated EndPointId link_endpoint_ids=4;
}
////////////////// Config Rule Delete ////////////
message ServiceConfigRule {
ServiceId service_id =1;
ConfigRule_Custom configrule_custom =2;
}
......@@ -13,11 +13,13 @@
// limitations under the License.
syntax = "proto3";
package openconfig_device;
package optical_device;
import "context.proto";
service OpenConfigService {
rpc AddOpenConfigDevice (context.OpticalConfig) returns (context.OpticalConfigId) {}
rpc ConfigureOpticalDevice(context.OpticalConfig) returns (context.Empty ) {}
rpc AddOpenConfigDevice (context.OpticalConfig ) returns (context.OpticalConfigId) {}
rpc ConfigureOpticalDevice(context.OpticalConfig ) returns (context.Empty ) {}
rpc DisableOpticalDevice (context.OpticalConfig ) returns (context.Empty ) {}
rpc GetDeviceConfiguration(context.OpticalConfigList) returns (context.Empty ) {}
}
#!/bin/bash
# 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.
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 \
device/tests/gnmi_openconfig/test_unitary_gnmi_openconfig.py
#!/bin/bash
# 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.
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 -o log_cli=true --verbose \
device/tests/test_unitary_openconfig_arista_l2vpn.py
......@@ -19,7 +19,7 @@ from common.proto.policy_pb2 import PolicyRuleService, PolicyRuleState
from common.proto.policy_pb2_grpc import PolicyServiceStub
from common.tools.client.RetryDecorator import retry, delay_exponential
from common.tools.grpc.Tools import grpc_message_to_json_string
from common.proto.openconfig_device_pb2_grpc import OpenConfigServiceStub
LOGGER = logging.getLogger(__name__)
MAX_RETRIES = 15
DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
......@@ -40,7 +40,6 @@ class PolicyClient:
def connect(self):
self.channel = grpc.insecure_channel(self.endpoint)
self.stub = PolicyServiceStub(self.channel)
self.openconfig_stub=OpenConfigServiceStub(self.channel)
def close(self):
if self.channel is not None: self.channel.close()
......
# 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 common.method_wrappers.ServiceExceptions import InvalidArgumentsException
from typing import Optional, Union
from uuid import UUID, uuid4, uuid5
# Generate a UUIDv5-like from the SHA-1 of "TFS" and no namespace to be used as the NAMESPACE for all
# the context UUIDs generated. For efficiency purposes, the UUID is hardcoded; however, it is produced
# using the following code:
# from hashlib import sha1
# from uuid import UUID
# hash = sha1(bytes('TFS', 'utf-8')).digest()
# NAMESPACE_TFS = UUID(bytes=hash[:16], version=5)
NAMESPACE_TFS = UUID('200e3a1f-2223-534f-a100-758e29c37f40')
def get_uuid_from_string(str_uuid_or_name : Union[str, UUID], prefix_for_name : Optional[str] = None) -> str:
# if UUID given, assume it is already a valid UUID
if isinstance(str_uuid_or_name, UUID): return str_uuid_or_name
if not isinstance(str_uuid_or_name, str):
MSG = 'Parameter({:s}) cannot be used to produce a UUID'
raise Exception(MSG.format(str(repr(str_uuid_or_name))))
try:
# try to parse as UUID
return str(UUID(str_uuid_or_name))
except: # pylint: disable=bare-except
# produce a UUID within TFS namespace from parameter
if prefix_for_name is not None:
str_uuid_or_name = '{:s}/{:s}'.format(prefix_for_name, str_uuid_or_name)
return str(uuid5(NAMESPACE_TFS, str_uuid_or_name))
def get_uuid_random() -> str:
# Generate random UUID. No need to use namespace since "namespace + random = random".
return str(uuid4())
def device_get_uuid (device_name) :
if (len(device_name)> 0):
return get_uuid_from_string(device_name)
raise InvalidArgumentsException([
('name', device_name),
], extra_details=['Device Name is required to produce Device UUID'])
def opticalconfig_get_uuid(
device_name : str = '', allow_random : bool = False
) -> str:
if len(device_name) > 0:
device_uuid= device_get_uuid(device_name=device_name)
return get_uuid_from_string(f"{device_uuid}_opticalconfig")
if allow_random: return get_uuid_random()
raise InvalidArgumentsException([
('name', device_name),
], extra_details=['At least one is required to produce a OpticalConfig UUID'])
......@@ -38,7 +38,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from common.proto.context_pb2 import (
Connection, Context, ContextId, Device, DeviceId, Empty,
Link, LinkId, Service, ServiceId, Slice, SliceId,
Topology, TopologyId
Topology, TopologyId , OpticalLink
)
from common.tools.object_factory.Context import json_context_id
from context.client.ContextClient import ContextClient
......@@ -128,15 +128,16 @@ class DescriptorLoader:
self.__num_workers = num_workers
self.__dummy_mode = self.__descriptors.get('dummy_mode' , False)
self.__contexts = self.__descriptors.get('contexts' , [])
self.__topologies = self.__descriptors.get('topologies' , [])
self.__devices = self.__descriptors.get('devices' , [])
self.__links = self.__descriptors.get('links' , [])
self.__services = self.__descriptors.get('services' , [])
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.__dummy_mode = self.__descriptors.get('dummy_mode' , False)
self.__contexts = self.__descriptors.get('contexts' , [])
self.__topologies = self.__descriptors.get('topologies' , [])
self.__devices = self.__descriptors.get('devices' , [])
self.__links = self.__descriptors.get('links' , [])
self.__services = self.__descriptors.get('services' , [])
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.__optical_links = self.__descriptors.get('optical_links',[])
if len(self.__ietf_slices) > 0:
for slice_service in self.__ietf_slices["slice-service"]:
......@@ -285,6 +286,9 @@ class DescriptorLoader:
@property
def num_connections(self) -> int: return len(self.__connections)
@property
def optical_links(self) -> List[Dict]: return self.__optical_links
def process(self) -> TypeResults:
# Format CustomConfigRules in Devices, Services and Slices provided in JSON format
......@@ -348,16 +352,17 @@ class DescriptorLoader:
self.__svc_cli.connect()
self.__slc_cli.connect()
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('controller', 'add', self.__dev_cli.AddDevice, Device, controllers_add )
self._process_descr('device', 'add', self.__dev_cli.AddDevice, Device, network_devices_add )
self._process_descr('device', 'config', self.__dev_cli.ConfigureDevice, Device, self.__devices_config)
self._process_descr('link', 'add', self.__ctx_cli.SetLink, Link, self.__links )
self._process_descr('service', 'add', self.__svc_cli.CreateService, Service, self.__services_add )
self._process_descr('service', 'update', self.__svc_cli.UpdateService, Service, self.__services )
self._process_descr('slice', 'add', self.__slc_cli.CreateSlice, Slice, self.__slices_add )
self._process_descr('slice', 'update', self.__slc_cli.UpdateSlice, Slice, self.__slices )
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('controller', 'add', self.__dev_cli.AddDevice, Device, controllers_add )
self._process_descr('device', 'add', self.__dev_cli.AddDevice, Device, network_devices_add )
self._process_descr('device', 'config', self.__dev_cli.ConfigureDevice, Device, self.__devices_config)
self._process_descr('link', 'add', self.__ctx_cli.SetLink, Link, self.__links )
self._process_descr('service', 'add', self.__svc_cli.CreateService, Service, self.__services_add )
self._process_descr('service', 'update', self.__svc_cli.UpdateService, Service, self.__services )
self._process_descr('slice', 'add', self.__slc_cli.CreateSlice, Slice, self.__slices_add )
self._process_descr('slice', 'update', self.__slc_cli.UpdateSlice, Slice, self.__slices )
self._process_descr('link', 'add', self.__ctx_cli.SetOpticalLink, OpticalLink, self.__optical_links )
# By default the Context component automatically assigns devices and links to topologies based on their
# endpoints, and assigns topologies, services, and slices to contexts based on their identifiers.
......
......@@ -43,13 +43,14 @@ def json_endpoint_ids(
def json_endpoint(
device_id : Dict, endpoint_uuid : str, endpoint_type : str, topology_id : Optional[Dict] = None,
kpi_sample_types : List[int] = [], location : Optional[Dict] = None
name : Optional[str] = None, kpi_sample_types : List[int] = [], location : Optional[Dict] = None
):
result = {
'endpoint_id': json_endpoint_id(device_id, endpoint_uuid, topology_id=topology_id),
'endpoint_type': endpoint_type,
}
if name is not None: result['name'] = name
if kpi_sample_types is not None and len(kpi_sample_types) > 0:
result['kpi_sample_types'] = copy.deepcopy(kpi_sample_types)
if location is not None:
......
# 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 copy
def convert_to_dict(single_val:int)->dict:
slot= dict()
bin_num = bin(single_val)
sliced_num=bin_num[2:]
for i in range(len(sliced_num)):
slot[str(i+1)]=int(sliced_num[i])
return slot
def correct_slot(dic: dict) -> dict:
_dict = copy.deepcopy(dic)
keys_list = list(_dict.keys())
if len(keys_list) < 20:
num_keys = [int(i) for i in keys_list]
if num_keys[-1] != 20:
missed_keys = []
diff = 20 - len(num_keys)
#print(f"diff {diff}")
for i in range(diff+1):
missed_keys.append(num_keys[-1]+i)
#print(f"missed_keys {missed_keys}")
for key in missed_keys :
_dict[key]=1
#print(f"result {_dict}")
return _dict
......@@ -451,7 +451,7 @@ def validate_slice(message):
def validate_topology(message, num_devices=None, num_links=None):
assert isinstance(message, dict)
assert len(message.keys()) == 4
assert len(message.keys()) == 5
assert 'topology_id' in message
validate_topology_id(message['topology_id'])
assert 'name' in message
......@@ -464,6 +464,10 @@ def validate_topology(message, num_devices=None, num_links=None):
assert isinstance(message['link_ids'], list)
if num_links is not None: assert len(message['link_ids']) == num_links
for link_id in message['link_ids']: validate_link_id(link_id)
assert 'optical_link_ids' in message
assert isinstance(message['optical_link_ids'], list)
#if num_links is not None: assert len(message['optical_link_ids']) == num_links
for link_id in message['optical_link_ids']: validate_link_id(link_id)
def validate_endpoint(message):
assert isinstance(message, dict)
......
......@@ -24,10 +24,10 @@ from common.proto.context_pb2 import (
Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList,
Empty, EndPointIdList, EndPointNameList,
Link, LinkEvent, LinkId, LinkIdList, LinkList,
Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList,
OpticalConfig, OpticalConfigId, OpticalConfigList , OpticalLink, OpticalLinkList,
Service, ServiceConfigRule, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList,
Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList,
Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList,
OpticalConfig, OpticalConfigId, OpticalConfigList
)
from common.proto.context_pb2_grpc import ContextServiceStub
from common.proto.context_policy_pb2_grpc import ContextPolicyServiceStub
......@@ -447,6 +447,14 @@ class ContextClient:
response = self.stub.SetOpticalConfig(request)
LOGGER.debug('SetOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def UpdateOpticalConfig(self, request : OpticalConfig) -> OpticalConfigId:
LOGGER.debug('SetOpticalConfig request: {:s}'.format(grpc_message_to_json_string(request)))
response_future = self.stub.UpdateOpticalConfig.future(request)
response = response_future.result()
LOGGER.debug('SetOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def GetOpticalConfig(self, request : Empty) -> OpticalConfigList:
......@@ -461,3 +469,55 @@ class ContextClient:
response = self.stub.SelectOpticalConfig(request)
LOGGER.debug('SelectOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def DeleteOpticalConfig(self,request : OpticalConfigId) -> Empty:
LOGGER.debug('DeleteOpticalConfig request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.DeleteOpticalConfig(request)
LOGGER.debug('DeleteOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def DeleteOpticalChannel(self,request : OpticalConfig) -> Empty:
LOGGER.debug('DeleteOpticalChannel request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.DeleteOpticalChannel(request)
LOGGER.debug('DeleteOpticalChannel result: {:s}'.format(grpc_message_to_json_string(response)))
return response
#--------------------------- Optical Link ------------------------
def GetOpticalLinkList(self, request: Empty) -> OpticalLinkList:
LOGGER.debug('ListOpticalLinks request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.GetOpticalLinkList(request)
LOGGER.debug('ListOpticalLinks result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def GetOpticalLink(self, request: LinkId) -> OpticalLink:
LOGGER.debug('GetOpticalLink request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.GetOpticalLink(request)
LOGGER.debug('GetOpticalLink result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def SetOpticalLink(self, request: OpticalLink) -> LinkId:
LOGGER.debug('SetOpticalLink request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.SetOpticalLink(request)
LOGGER.debug('SetOpticalLink result: {:s}'.format(grpc_message_to_json_string(response)))
return response
@RETRY_DECORATOR
def DeleteOpticalLink(self, request: LinkId) -> Empty:
LOGGER.debug('RemoveOpticalLink request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.DeleteOpticalLink(request)
LOGGER.debug('RemoveOpticalLink result: {:s}'.format(grpc_message_to_json_string(response)))
return response
# --------------------------------- Service ConfigRule Deletion ------------------
@RETRY_DECORATOR
def DeleteServiceConfigRule(self, request: ServiceConfigRule) -> Empty:
LOGGER.debug('ServiceConfigRule Delete request: {:s}'.format(grpc_message_to_json_string(request)))
response = self.stub.DeleteServiceConfigRule(request)
LOGGER.debug('ServiceConfigRule Delete result: {:s}'.format(grpc_message_to_json_string(response)))
return response
......@@ -24,29 +24,51 @@ from common.proto.context_pb2 import (
Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList,
Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList,
Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList,
OpticalConfigList, OpticalConfigId, OpticalConfig
OpticalConfigList, OpticalConfigId, OpticalConfig, OpticalLink, OpticalLinkList,
ServiceConfigRule
)
from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule
from common.proto.context_pb2_grpc import ContextServiceServicer
from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer
from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method
from .database.Connection import (
connection_delete, connection_get, connection_list_ids, connection_list_objs, connection_set)
from .database.Context import context_delete, context_get, context_list_ids, context_list_objs, context_set
from .database.Device import device_delete, device_get, device_list_ids, device_list_objs, device_select, device_set
connection_delete, connection_get, connection_list_ids, connection_list_objs, connection_set
)
from .database.Context import (
context_delete, context_get, context_list_ids, context_list_objs, context_set
)
from .database.Device import (
device_delete, device_get, device_list_ids, device_list_objs, device_select, device_set
)
from .database.EndPoint import endpoint_list_names
from .database.Events import EventTopicEnum, consume_events
from .database.Link import link_delete, link_get, link_list_ids, link_list_objs, link_set
from .database.Link import (
link_delete, link_get, link_list_ids, link_list_objs, link_set
)
from .database.PolicyRule import (
policyrule_delete, policyrule_get, policyrule_list_ids, policyrule_list_objs, policyrule_set)
policyrule_delete, policyrule_get, policyrule_list_ids, policyrule_list_objs,
policyrule_set
)
from .database.Service import (
service_delete, service_get, service_list_ids, service_list_objs, service_select, service_set, service_unset)
service_delete, service_get, service_list_ids, service_list_objs, service_select,
service_set, service_unset
)
from .database.Slice import (
slice_delete, slice_get, slice_list_ids, slice_list_objs, slice_select, slice_set, slice_unset)
slice_delete, slice_get, slice_list_ids, slice_list_objs, slice_select,
slice_set, slice_unset
)
from .database.Topology import (
topology_delete, topology_get, topology_get_details, topology_list_ids, topology_list_objs, topology_set)
from .database.OpticalConfig import set_opticalconfig, select_opticalconfig, get_opticalconfig
topology_delete, topology_get, topology_get_details, topology_list_ids,
topology_list_objs, topology_set
)
from .database.OpticalConfig import (
set_opticalconfig, select_opticalconfig, get_opticalconfig, delete_opticalconfig,
update_opticalconfig, delete_opticalchannel
)
from .database.OpticalLink import (
optical_link_delete, optical_link_get, optical_link_list_objs, optical_link_set
)
from .database.ConfigRule import delete_config_rule
LOGGER = logging.getLogger(__name__)
METRICS_POOL = MetricsPool('Context', 'RPC')
......@@ -312,7 +334,48 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer
result = set_opticalconfig(self.db_engine, request)
return OpticalConfigId(**result)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def UpdateOpticalConfig(self, request : OpticalConfig, context : grpc.ServicerContext) -> OpticalConfigId:
result = update_opticalconfig(self.db_engine, request)
return OpticalConfigId(**result)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def SelectOpticalConfig(self, request : OpticalConfigId, context : grpc.ServicerContext) -> OpticalConfig:
result = select_opticalconfig(self.db_engine, request)
return OpticalConfig(config=result.config, opticalconfig_id=result.opticalconfig_id)
optical_config_id = OpticalConfigId()
device_id = DeviceId()
optical_config_id.CopyFrom(result.opticalconfig_id)
device_id.CopyFrom(result.device_id)
return OpticalConfig(config=result.config, opticalconfig_id=optical_config_id , device_id=device_id)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def DeleteOpticalConfig(self, request : OpticalConfigId, context : grpc.ServicerContext) -> Empty:
delete_opticalconfig(self.db_engine, self.messagebroker, request)
return Empty()
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def DeleteOpticalChannel(self, request : OpticalConfig, context : grpc.ServicerContext) -> Empty:
delete_opticalchannel(self.db_engine, self.messagebroker, request)
return Empty()
#--------------------- Experimental Optical Link -------------------
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def GetOpticalLinkList(self, request : Empty, context : grpc.ServicerContext) -> OpticalLinkList:
return optical_link_list_objs(self.db_engine)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def GetOpticalLink(self, request : LinkId, context : grpc.ServicerContext) -> OpticalLink:
return optical_link_get(self.db_engine, request)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def SetOpticalLink(self, request : Link, context : grpc.ServicerContext) -> LinkId:
return optical_link_set(self.db_engine, self.messagebroker, request)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def DeleteOpticalLink(self, request : LinkId, context : grpc.ServicerContext) -> Empty:
return optical_link_delete(self.db_engine, self.messagebroker, request)
@safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
def DeleteServiceConfigRule(self, request : ServiceConfigRule, context : grpc.ServicerContext) -> Empty:
return delete_config_rule(self.db_engine, request)
......@@ -16,15 +16,18 @@ import datetime, json, logging
from sqlalchemy import delete
#from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session
from typing import Dict, List, Optional, Set
from common.proto.context_pb2 import ConfigRule
from common.proto.context_pb2 import ConfigRule, ServiceConfigRule, Empty
from common.tools.grpc.Tools import grpc_message_to_json_string
from .models.enums.ConfigAction import ORM_ConfigActionEnum, grpc_to_enum__config_action
from .models.ConfigRuleModel import (
ConfigRuleKindEnum, DeviceConfigRuleModel, ServiceConfigRuleModel, SliceConfigRuleModel)
from .uuids._Builder import get_uuid_from_string
from .uuids.EndPoint import endpoint_get_uuid
from sqlalchemy_cockroachdb import run_transaction
from sqlalchemy.orm import Session, sessionmaker
LOGGER = logging.getLogger(__name__)
......@@ -149,3 +152,16 @@ def upsert_config_rules(
upsert_affected = any([(updated_at > created_at) for created_at,updated_at in configrule_updates])
return delete_affected or upsert_affected
def delete_config_rule(db_engine : Engine, request : ServiceConfigRule):
config_rule = request.configrule_custom
service_id = request.service_id
parent_uuid = service_id.service_uuid.uuid
configrule_name = 'service:custom:{:s}'.format( config_rule.resource_key)
configrule_uuid = get_uuid_from_string(configrule_name, prefix_for_name=parent_uuid)
def callback(session : Session) -> bool:
num_deleted = session.query(ServiceConfigRuleModel).filter_by(configrule_uuid=configrule_uuid).delete()
return num_deleted > 0
deleted = run_transaction(sessionmaker(bind=db_engine), callback)
return Empty()
......@@ -106,6 +106,7 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi
topology_uuids.add(topology_uuid)
is_oc_driver = DeviceDriverEnum.DEVICEDRIVER_OC in set(request.device_drivers)
#optical_endpoints_data : List[Dict] = list()
endpoints_data : List[Dict] = list()
for i, endpoint in enumerate(request.device_endpoints):
......@@ -136,6 +137,18 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi
'created_at' : now,
'updated_at' : now,
})
# # ------------------- Experimental -----------------------
# if is_oc_driver:
# optical_endpoints_data.append({
# 'endpoint_uuid' : endpoint_uuid,
# 'device_uuid' : endpoint_device_uuid,
# 'name' : endpoint_name,
# 'endpoint_type' : endpoint.endpoint_type,
# 'created_at' : now,
# 'updated_at' : now,
# })
if endpoint_topology_uuid not in topology_uuids:
related_topologies.append({
......@@ -191,6 +204,23 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi
stmt = stmt.returning(EndPointModel.created_at, EndPointModel.updated_at)
endpoint_updates = session.execute(stmt).fetchall()
updated_endpoints = any([(updated_at > created_at) for created_at,updated_at in endpoint_updates])
#---------------------- Experimental ---------------------------------
# if len(optical_endpoints_data) > 0:
# LOGGER.info(f"Optical endpoint data_ device_model {optical_endpoints_data}")
# stmt = insert(OpticalEndPointModel).values(optical_endpoints_data)
# stmt = stmt.on_conflict_do_update(
# index_elements=[OpticalEndPointModel.endpoint_uuid],
# set_=dict(
# name = stmt.excluded.name,
# endpoint_type = stmt.excluded.endpoint_type,
# updated_at = stmt.excluded.updated_at,
# )
# )
# stmt = stmt.returning(OpticalEndPointModel.created_at, OpticalEndPointModel.updated_at)
# optical_endpoint_updates = session.execute(stmt).fetchall()
# updated_optical_endpoints = any([(updated_at > created_at) for created_at,updated_at in endpoint_updates])
device_topology_ids = []
if not updated or len(related_topologies) > 1:
......
......@@ -17,27 +17,32 @@ from typing import Dict, Iterator, Set
from common.message_broker.Message import Message
from common.message_broker.MessageBroker import MessageBroker
from common.proto.context_pb2 import (
ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, LinkEvent, ServiceEvent, SliceEvent, TopologyEvent)
ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, LinkEvent,
ServiceEvent, SliceEvent, TopologyEvent, OpticalConfigEvent
)
class EventTopicEnum(enum.Enum):
CONNECTION = 'connection'
CONTEXT = 'context'
DEVICE = 'device'
LINK = 'link'
POLICY_RULE = 'policy-rule'
SERVICE = 'service'
SLICE = 'slice'
TOPOLOGY = 'topology'
CONNECTION = 'connection'
CONTEXT = 'context'
DEVICE = 'device'
LINK = 'link'
POLICY_RULE = 'policy-rule'
SERVICE = 'service'
SLICE = 'slice'
TOPOLOGY = 'topology'
OPTICALCONFIG = 'optical-config'
TOPIC_TO_EVENTCLASS = {
EventTopicEnum.CONNECTION.value : ConnectionEvent,
EventTopicEnum.CONTEXT.value : ContextEvent,
EventTopicEnum.DEVICE.value : DeviceEvent,
EventTopicEnum.LINK.value : LinkEvent,
#EventTopicEnum.POLICY_RULE.value : PolicyRuleEvent, # Not defined in proto files
EventTopicEnum.SERVICE.value : ServiceEvent,
EventTopicEnum.SLICE.value : SliceEvent,
EventTopicEnum.TOPOLOGY.value : TopologyEvent,
EventTopicEnum.CONNECTION.value : ConnectionEvent,
EventTopicEnum.CONTEXT.value : ContextEvent,
EventTopicEnum.DEVICE.value : DeviceEvent,
EventTopicEnum.LINK.value : LinkEvent,
#EventTopicEnum.POLICY_RULE.value : PolicyRuleEvent, # Not defined in proto files
EventTopicEnum.SERVICE.value : ServiceEvent,
EventTopicEnum.SLICE.value : SliceEvent,
EventTopicEnum.TOPOLOGY.value : TopologyEvent,
EventTopicEnum.OPTICALCONFIG.value : OpticalConfigEvent,
}
CONSUME_TIMEOUT = 0.5 # seconds
......@@ -61,6 +66,9 @@ def notify_event_topology(messagebroker : MessageBroker, event_type : EventTypeE
def notify_event_device(messagebroker : MessageBroker, event_type : EventTypeEnum, device_id : Dict) -> None:
notify_event(messagebroker, EventTopicEnum.DEVICE, event_type, {'device_id': device_id})
def notify_event_opticalconfig(messagebroker : MessageBroker, event_type : EventTypeEnum, opticalconfig_id : Dict) -> None:
notify_event(messagebroker, EventTopicEnum.DEVICE, event_type, {'opticalconfig_id': opticalconfig_id})
def notify_event_link(messagebroker : MessageBroker, event_type : EventTypeEnum, link_id : Dict) -> None:
notify_event(messagebroker, EventTopicEnum.LINK, event_type, {'link_id': link_id})
......
# 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 datetime, logging
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session, selectinload, sessionmaker
from sqlalchemy_cockroachdb import run_transaction
from typing import Dict, List, Optional, Set, Tuple
from common.proto.context_pb2 import (
Empty, EventTypeEnum, OpticalLink, LinkId, OpticalLinkList, TopologyId
)
from common.message_broker.MessageBroker import MessageBroker
from common.method_wrappers.ServiceExceptions import NotFoundException
from common.tools.object_factory.Link import json_link_id
from .models.OpticalLinkModel import OpticalLinkModel,OpticalLinkEndPointModel
from .models.TopologyModel import TopologyOpticalLinkModel, TopologyModel
from .uuids.EndPoint import endpoint_get_uuid
from .uuids.Link import link_get_uuid
from .uuids.Topology import topology_get_uuid
from .Events import notify_event_link
LOGGER = logging.getLogger(__name__)
def optical_link_list_objs(db_engine : Engine) -> OpticalLinkList:
def callback(session : Session) -> List[Dict]:
obj_list : List[OpticalLinkModel] = session.query(OpticalLinkModel)\
.options(selectinload(OpticalLinkModel.opticallink_endpoints))\
.all()
return [obj.dump() for obj in obj_list]
links = run_transaction(sessionmaker(bind=db_engine), callback)
return OpticalLinkList(optical_links=links)
def optical_link_get(db_engine : Engine, request : LinkId) -> OpticalLink:
link_uuid = link_get_uuid(request, allow_random=False)
def callback(session : Session) -> Optional[Dict]:
obj : Optional[OpticalLinkModel] = session.query(OpticalLinkModel)\
.options(selectinload(OpticalLinkModel.opticallink_endpoints))\
.filter_by(opticallink_uuid=link_uuid).one_or_none()
return None if obj is None else obj.dump()
obj = run_transaction(sessionmaker(bind=db_engine), callback)
if obj is None:
raw_link_uuid = request.link_uuid.uuid
raise NotFoundException('Optical Link', raw_link_uuid, extra_details=[
'link_uuid generated was: {:s}'.format(link_uuid)
])
return OpticalLink(**obj)
def optical_link_set(db_engine : Engine, messagebroker : MessageBroker, request : OpticalLink) -> LinkId:
raw_link_uuid = request.link_id.link_uuid.uuid
raw_link_name = request.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)
now = datetime.datetime.utcnow()
# By default, always add link to default Context/Topology
topology_uuids : Set[str] = set()
related_topologies : List[Dict] = list()
_,topology_uuid = topology_get_uuid(TopologyId(), allow_random=False, allow_default=True)
related_topologies.append({
'topology_uuid': topology_uuid,
'optical_link_uuid' : link_uuid,
})
topology_uuids.add(topology_uuid)
link_endpoints_data : List[Dict] = list()
for i,endpoint_id in enumerate(request.link_endpoint_ids):
endpoint_topology_uuid, endpoint_device_uuid, endpoint_uuid = endpoint_get_uuid(
endpoint_id, endpoint_name="", allow_random=True)
link_endpoints_data.append({
'link_uuid' : link_uuid,
'endpoint_uuid': endpoint_uuid,
})
if endpoint_topology_uuid not in topology_uuids:
related_topologies.append({
'topology_uuid': endpoint_topology_uuid,
'optical_link_uuid' : link_uuid,
})
topology_uuids.add(endpoint_topology_uuid)
optical_link_data = [{
'opticallink_uuid' : link_uuid,
'name' : link_name,
'created_at' : now,
'updated_at' : now,
'length' : request.optical_details.length,
"src_port" : request.optical_details.src_port,
"dst_port" : request.optical_details.dst_port,
"local_peer_port" : request.optical_details.local_peer_port,
"remote_peer_port" : request.optical_details.remote_peer_port,
"used" : request.optical_details.used,
"c_slots" : request.optical_details.c_slots ,
"l_slots" : request.optical_details.l_slots,
"s_slots" : request.optical_details.s_slots,
}]
def callback(session : Session) -> Tuple[bool, List[Dict]]:
stmt = insert(OpticalLinkModel).values(optical_link_data)
stmt = stmt.on_conflict_do_update(
index_elements=[OpticalLinkModel.opticallink_uuid],
set_=dict(
updated_at = stmt.excluded.updated_at,
src_port = stmt.excluded.src_port,
dst_port = stmt.excluded.dst_port,
local_peer_port = stmt.excluded.local_peer_port,
remote_peer_port = stmt.excluded.remote_peer_port,
used = stmt.excluded.used ,
c_slots = stmt.excluded.c_slots,
l_slots = stmt.excluded.l_slots,
s_slots = stmt.excluded.s_slots
)
)
stmt = stmt.returning(OpticalLinkModel.created_at, OpticalLinkModel.updated_at)
created_at,updated_at = session.execute(stmt).fetchone()
updated = updated_at > created_at
updated_endpoints = False
if len(link_endpoints_data) > 0:
stmt = insert(OpticalLinkEndPointModel).values(link_endpoints_data)
stmt = stmt.on_conflict_do_nothing(
index_elements=[OpticalLinkEndPointModel.link_uuid, OpticalLinkEndPointModel.endpoint_uuid]
)
link_endpoint_inserts = session.execute(stmt)
updated_endpoints = int(link_endpoint_inserts.rowcount) > 0
if not updated or len(related_topologies) > 1:
# Only update topology-link relations when link is created (not updated) or when endpoint_ids are
# modified (len(related_topologies) > 1).
stmt = insert(TopologyOpticalLinkModel).values(related_topologies)
stmt = stmt.on_conflict_do_nothing(
index_elements=[TopologyOpticalLinkModel.topology_uuid, TopologyOpticalLinkModel.optical_link_uuid]
)
stmt = stmt.returning(TopologyOpticalLinkModel.topology_uuid)
topology_uuids = session.execute(stmt).fetchall()
#LOGGER.warning('RAW topology_uuids={:s}'.format(str(topology_uuids)))
if len(topology_uuids) > 0:
topology_uuids = [topology_uuid[0] for topology_uuid in topology_uuids]
#LOGGER.warning('NEW topology_uuids={:s}'.format(str(topology_uuids)))
query = session.query(TopologyModel)
query = query.filter(TopologyModel.topology_uuid.in_(topology_uuids))
link_topologies : List[TopologyModel] = query.all()
link_topology_ids = [obj.dump_id() for obj in link_topologies]
#LOGGER.warning('link_topology_ids={:s}'.format(str(link_topology_ids)))
return updated or updated_endpoints
updated = run_transaction(sessionmaker(bind=db_engine), callback )
link_id = json_link_id(link_uuid)
event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
notify_event_link(messagebroker, event_type, link_id)
return LinkId(**link_id)
def optical_link_delete(db_engine : Engine, messagebroker : MessageBroker, request : LinkId) -> Empty:
link_uuid = link_get_uuid(request, allow_random=False)
def callback(session : Session) -> bool:
num_deleted = session.query(OpticalLinkModel).filter_by(opticallink_uuid=link_uuid).delete()
return num_deleted > 0
deleted = run_transaction(sessionmaker(bind=db_engine), callback)
link_id = json_link_id(link_uuid)
if deleted:
notify_event_link(messagebroker, EventTypeEnum.EVENTTYPE_REMOVE, link_id)
return Empty()
......@@ -27,7 +27,8 @@ from common.tools.object_factory.Topology import json_topology_id
from context.Config import ALLOW_EXPLICIT_ADD_DEVICE_TO_TOPOLOGY, ALLOW_EXPLICIT_ADD_LINK_TO_TOPOLOGY
from .models.DeviceModel import DeviceModel
from .models.LinkModel import LinkModel
from .models.TopologyModel import TopologyDeviceModel, TopologyLinkModel, TopologyModel
from .models.OpticalLinkModel import OpticalLinkModel
from .models.TopologyModel import TopologyDeviceModel, TopologyLinkModel, TopologyModel, TopologyOpticalLinkModel
from .uuids.Context import context_get_uuid
from .uuids.Device import device_get_uuid
from .uuids.Link import link_get_uuid
......@@ -79,6 +80,7 @@ def topology_get_details(db_engine : Engine, request : TopologyId) -> TopologyDe
obj : Optional[TopologyModel] = session.query(TopologyModel)\
.options(selectinload(TopologyModel.topology_devices, TopologyDeviceModel.device, DeviceModel.endpoints))\
.options(selectinload(TopologyModel.topology_links, TopologyLinkModel.link, LinkModel.link_endpoints))\
.options(selectinload(TopologyModel.topology_optical_links, TopologyOpticalLinkModel.optical_link, OpticalLinkModel.opticallink_endpoints))\
.filter_by(topology_uuid=topology_uuid).one_or_none()
#.options(selectinload(DeviceModel.components))\
return None if obj is None else obj.dump_details()
......
......@@ -34,10 +34,11 @@ class DeviceModel(_Base):
updated_at = Column(DateTime, nullable=False)
#topology_devices = relationship('TopologyDeviceModel', back_populates='device')
config_rules = relationship('DeviceConfigRuleModel', passive_deletes=True) # lazy='joined', back_populates='device'
endpoints = relationship('EndPointModel', passive_deletes=True) # lazy='joined', back_populates='device'
components = relationship('ComponentModel', passive_deletes=True) # lazy='joined', back_populates='device'
controller = relationship('DeviceModel', remote_side=[device_uuid], passive_deletes=True) # lazy='joined', back_populates='device'
config_rules = relationship('DeviceConfigRuleModel', passive_deletes=True) # lazy='joined', back_populates='device'
endpoints = relationship('EndPointModel', passive_deletes=True) # lazy='joined', back_populates='device'
components = relationship('ComponentModel', passive_deletes=True) # lazy='joined', back_populates='device'
controller = relationship('DeviceModel', remote_side=[device_uuid], passive_deletes=True) # lazy='joined', back_populates='device'
optical_config = relationship('OpticalConfigModel', passive_deletes=True)
def dump_id(self) -> Dict:
return {'device_uuid': {'uuid': self.device_uuid}}
......