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

Context component:

- cleaned up script run tests locally
- temporarily added script to automate test & coverage reporting
- reorganized unitary tests
- migration in progress to use single-column primary-key for main entities
- intermediate backup ; work in progress
parent e719962b
No related branches found
No related tags found
2 merge requests!54Release 2.0.0,!34Context Scalability extensions using CockroachDB + Removal of Stateful database inside Device + other
Showing
with 805 additions and 527 deletions
......@@ -20,8 +20,6 @@
# If not already set, set the name of the Kubernetes namespace to deploy to.
export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"}
#export TFS_K8S_HOSTNAME="tfs-vm"
########################################################################################################################
# Automated steps start here
########################################################################################################################
......@@ -29,24 +27,14 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"}
PROJECTDIR=`pwd`
cd $PROJECTDIR/src
#RCFILE=$PROJECTDIR/coverage/.coveragerc
#kubectl --namespace $TFS_K8S_NAMESPACE expose deployment contextservice --name=redis-tests --port=6379 --type=NodePort
#export REDIS_SERVICE_HOST=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.clusterIP}')
#export REDIS_SERVICE_HOST=$(kubectl get node $TFS_K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}')
#export REDIS_SERVICE_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}')
RCFILE=$PROJECTDIR/coverage/.coveragerc
#export CRDB_URI="cockroachdb://tfs:tfs123@127.0.0.1:26257/tfs_test?sslmode=require"
export CRDB_URI="cockroachdb://tfs:tfs123@10.1.7.195:26257/tfs_test?sslmode=require"
export PYTHONPATH=/home/tfs/tfs-ctrl/src
# Run unitary tests and analyze coverage of code at same time
#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1 \
# context/tests/test_unitary.py
# --log-level=INFO -o log_cli=true --durations=0
pytest --verbose --maxfail=1 \
# 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 --maxfail=1 \
context/tests/test_unitary.py \
context/tests/test_hasher.py
#kubectl --namespace $TFS_K8S_NAMESPACE delete service redis-tests
......@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
@safe_and_metered_rpc_method(METRICS, LOGGER)
def GetContextEvents(self, request : Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]:
pass
......
......@@ -16,14 +16,15 @@ TOPIC_CONNECTION = 'connection'
TOPIC_CONTEXT = 'context'
TOPIC_DEVICE = 'device'
TOPIC_LINK = 'link'
TOPIC_POLICY = 'policy'
#TOPIC_POLICY = 'policy'
TOPIC_SERVICE = 'service'
TOPIC_SLICE = 'slice'
TOPIC_TOPOLOGY = 'topology'
TOPICS = {
TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK,
TOPIC_POLICY, TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY
#TOPIC_POLICY,
TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY
}
CONSUME_TIMEOUT = 0.5 # seconds
This diff is collapsed.
......@@ -28,13 +28,13 @@ class Engine:
try:
engine = sqlalchemy.create_engine(
crdb_uri, connect_args={'application_name': APP_NAME}, echo=ECHO, future=True)
except: # pylint: disable=bare-except
except: # pylint: disable=bare-except # pragma: no cover
LOGGER.exception('Failed to connect to database: {:s}'.format(crdb_uri))
return None
try:
Engine.create_database(engine)
except: # pylint: disable=bare-except
except: # pylint: disable=bare-except # pragma: no cover
LOGGER.exception('Failed to check/create to database: {:s}'.format(engine.url))
return None
......
......@@ -12,15 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import time
import logging
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy_cockroachdb import run_transaction
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Tuple
from common.proto.context_pb2 import Context, ContextId, ContextIdList, ContextList
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException
from common.rpc_method_wrapper.ServiceExceptions import NotFoundException
from common.tools.object_factory.Context import json_context_id
from context.service.database.models.ContextModel import ContextModel
from .uuids.Context import context_get_uuid
LOGGER = logging.getLogger(__name__)
def context_list_ids(db_engine : Engine) -> ContextIdList:
def callback(session : Session) -> List[Dict]:
......@@ -37,46 +41,44 @@ def context_list_objs(db_engine : Engine) -> ContextList:
return ContextList(contexts=run_transaction(sessionmaker(bind=db_engine), callback))
def context_get(db_engine : Engine, request : ContextId) -> Context:
context_uuid = request.context_uuid.uuid
context_uuid = context_get_uuid(request, allow_random=False)
def callback(session : Session) -> Optional[Dict]:
obj : Optional[ContextModel] = session.query(ContextModel)\
.filter_by(context_uuid=context_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: raise NotFoundException('Context', context_uuid)
if obj is None:
raw_context_uuid = request.context_uuid.uuid
raise NotFoundException('Context', raw_context_uuid, extra_details=[
'context_uuid generated was: {:s}'.format(context_uuid)
])
return Context(**obj)
def context_set(db_engine : Engine, request : Context) -> bool:
context_uuid = request.context_id.context_uuid.uuid
def context_set(db_engine : Engine, request : Context) -> Tuple[ContextId, bool]:
context_name = request.name
if len(context_name) == 0: context_name = request.context_id.context_uuid.uuid
context_uuid = context_get_uuid(request.context_id, context_name=context_name, allow_random=True)
# Ignore request.topology_ids, request.service_ids, and request.slice_ids. They are used
# for retrieving topologies, services and slices added into the context. Explicit addition
# into the context is done automatically qhen creating the topology, service or slice
# specifying the associated context.
if len(request.topology_ids) > 0: # pragma: no cover
LOGGER.warning('Items in field "topology_ids" ignored. This field is used for retrieval purposes only.')
for i, topology_id in enumerate(request.topology_ids):
topology_context_uuid = topology_id.context_id.context_uuid.uuid
if topology_context_uuid != context_uuid:
raise InvalidArgumentException(
'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid,
['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
if len(request.service_ids) > 0: # pragma: no cover
LOGGER.warning('Items in field "service_ids" ignored. This field is used for retrieval purposes only.')
for i, service_id in enumerate(request.service_ids):
service_context_uuid = service_id.context_id.context_uuid.uuid
if service_context_uuid != context_uuid:
raise InvalidArgumentException(
'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid,
['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
if len(request.slice_ids) > 0: # pragma: no cover
LOGGER.warning('Items in field "slice_ids" ignored. This field is used for retrieval purposes only.')
for i, slice_id in enumerate(request.slice_ids):
slice_context_uuid = slice_id.context_id.context_uuid.uuid
if slice_context_uuid != context_uuid:
raise InvalidArgumentException(
'request.slice_ids[{:d}].context_id.context_uuid.uuid'.format(i), slice_context_uuid,
['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)])
context_data = [{
'context_uuid': context_uuid,
'context_name': context_name,
}]
def callback(session : Session) -> None:
context_data = [{
'context_uuid': context_uuid,
'context_name': context_name,
'created_at' : time.time(),
}]
stmt = insert(ContextModel).values(context_data)
stmt = stmt.on_conflict_do_update(
index_elements=[ContextModel.context_uuid],
......@@ -85,10 +87,11 @@ def context_set(db_engine : Engine, request : Context) -> bool:
session.execute(stmt)
run_transaction(sessionmaker(bind=db_engine), callback)
return False # TODO: improve and check if created/updated
updated = False # TODO: improve and check if created/updated
return ContextId(**json_context_id(context_uuid)),updated
def context_delete(db_engine : Engine, request : ContextId) -> bool:
context_uuid = request.context_uuid.uuid
context_uuid = context_get_uuid(request, allow_random=False)
def callback(session : Session) -> bool:
num_deleted = session.query(ContextModel).filter_by(context_uuid=context_uuid).delete()
return num_deleted > 0
......
......@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from sqlalchemy import delete
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
......@@ -21,15 +20,18 @@ from sqlalchemy_cockroachdb import run_transaction
from typing import Dict, List, Optional, Set, Tuple
from common.proto.context_pb2 import Device, DeviceId, DeviceIdList, DeviceList
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException
from common.tools.grpc.Tools import grpc_message_to_json_string
from context.service.database.models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel
from common.tools.object_factory.Device import json_device_id
#from common.tools.grpc.Tools import grpc_message_to_json_string
#from context.service.database.models.ConfigRuleModel import ConfigRuleKindEnum, ConfigRuleModel
from context.service.database.models.DeviceModel import DeviceModel
from context.service.database.models.EndPointModel import EndPointModel
from context.service.database.models.RelationModels import TopologyDeviceModel
from context.service.database.models.enums.ConfigAction import grpc_to_enum__config_action
#from context.service.database.models.enums.ConfigAction import grpc_to_enum__config_action
from context.service.database.models.enums.DeviceDriver import grpc_to_enum__device_driver
from context.service.database.models.enums.DeviceOperationalStatus import grpc_to_enum__device_operational_status
from context.service.database.models.enums.KpiSampleType import grpc_to_enum__kpi_sample_type
from .uuids.Device import device_get_uuid
from .uuids.EndPoint import endpoint_get_uuid
def device_list_ids(db_engine : Engine) -> DeviceIdList:
def callback(session : Session) -> List[Dict]:
......@@ -46,115 +48,121 @@ def device_list_objs(db_engine : Engine) -> DeviceList:
return DeviceList(devices=run_transaction(sessionmaker(bind=db_engine), callback))
def device_get(db_engine : Engine, request : DeviceId) -> Device:
device_uuid = request.device_uuid.uuid
device_uuid = device_get_uuid(request, allow_random=False)
def callback(session : Session) -> Optional[Dict]:
obj : Optional[DeviceModel] = session.query(DeviceModel)\
.filter_by(device_uuid=device_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: raise NotFoundException('Device', device_uuid)
if obj is None:
raw_device_uuid = request.device_uuid.uuid
raise NotFoundException('Device', raw_device_uuid, extra_details=[
'device_uuid generated was: {:s}'.format(device_uuid)
])
return Device(**obj)
def device_set(db_engine : Engine, request : Device) -> bool:
device_uuid = request.device_id.device_uuid.uuid
device_name = request.name
raw_device_uuid = request.device_id.device_uuid.uuid
raw_device_name = request.name
device_name = request.device_id.device_uuid.uuid if len(raw_device_name) == 0 else raw_device_name
device_uuid = device_get_uuid(request.device_id, device_name=device_name, allow_random=True)
device_type = request.device_type
oper_status = grpc_to_enum__device_operational_status(request.device_operational_status)
device_drivers = [grpc_to_enum__device_driver(d) for d in request.device_drivers]
topology_keys : Set[Tuple[str, str]] = set()
topology_uuids : Set[str] = set()
related_topologies : List[Dict] = list()
endpoints_data : List[Dict] = list()
for i, endpoint in enumerate(request.device_endpoints):
endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid
if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid
if device_uuid != endpoint_device_uuid:
if endpoint_device_uuid not in {raw_device_uuid, device_uuid}:
raise InvalidArgumentException(
'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid,
['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)])
['should be == request.device_id.device_uuid.uuid({:s})'.format(raw_device_uuid)]
)
endpoint_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid
endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid
raw_endpoint_name = endpoint.name
endpoint_topology_uuid, endpoint_device_uuid, endpoint_uuid = endpoint_get_uuid(
endpoint.endpoint_id, endpoint_name=raw_endpoint_name, allow_random=True)
kpi_sample_types = [grpc_to_enum__kpi_sample_type(kst) for kst in endpoint.kpi_sample_types]
endpoints_data.append({
'context_uuid' : endpoint_context_uuid,
'topology_uuid' : endpoint_topology_uuid,
'endpoint_uuid' : endpoint_uuid,
'device_uuid' : endpoint_device_uuid,
'endpoint_uuid' : endpoint.endpoint_id.endpoint_uuid.uuid,
'topology_uuid' : endpoint_topology_uuid,
'name' : raw_endpoint_name,
'endpoint_type' : endpoint.endpoint_type,
'kpi_sample_types': kpi_sample_types,
})
if len(endpoint_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
topology_key = (endpoint_context_uuid, endpoint_topology_uuid)
if topology_key not in topology_keys:
related_topologies.append({
'context_uuid': endpoint_context_uuid,
'topology_uuid': endpoint_topology_uuid,
'device_uuid': endpoint_device_uuid,
})
topology_keys.add(topology_key)
if endpoint_topology_uuid not in topology_uuids:
related_topologies.append({
'topology_uuid': endpoint_topology_uuid,
'device_uuid' : endpoint_device_uuid,
})
topology_uuids.add(endpoint_topology_uuid)
config_rules : List[Dict] = list()
for position,config_rule in enumerate(request.device_config.config_rules):
str_kind = config_rule.WhichOneof('config_rule')
config_rules.append({
'device_uuid': device_uuid,
'kind' : ConfigRuleKindEnum._member_map_.get(str_kind.upper()), # pylint: disable=no-member
'action' : grpc_to_enum__config_action(config_rule.action),
'position' : position,
'data' : grpc_message_to_json_string(getattr(config_rule, str_kind, {})),
})
#config_rules : List[Dict] = list()
#for position,config_rule in enumerate(request.device_config.config_rules):
# str_kind = config_rule.WhichOneof('config_rule')
# config_rules.append({
# 'device_uuid': device_uuid,
# 'kind' : ConfigRuleKindEnum._member_map_.get(str_kind.upper()), # pylint: disable=no-member
# 'action' : grpc_to_enum__config_action(config_rule.action),
# 'position' : position,
# 'data' : grpc_message_to_json_string(getattr(config_rule, str_kind, {})),
# })
device_data = [{
'device_uuid' : device_uuid,
'device_name' : device_name,
'device_type' : device_type,
'device_operational_status': oper_status,
'device_drivers' : device_drivers,
}]
def callback(session : Session) -> None:
obj : Optional[DeviceModel] = session.query(DeviceModel).with_for_update()\
.filter_by(device_uuid=device_uuid).one_or_none()
is_update = obj is not None
if is_update:
obj.device_name = device_name
obj.device_type = device_type
obj.device_operational_status = oper_status
obj.device_drivers = device_drivers
session.merge(obj)
else:
session.add(DeviceModel(
device_uuid=device_uuid, device_name=device_name, device_type=device_type,
device_operational_status=oper_status, device_drivers=device_drivers, created_at=time.time()))
obj : Optional[DeviceModel] = session.query(DeviceModel)\
.filter_by(device_uuid=device_uuid).one_or_none()
stmt = insert(DeviceModel).values(device_data)
stmt = stmt.on_conflict_do_update(
index_elements=[DeviceModel.device_uuid],
set_=dict(
device_name = stmt.excluded.device_name,
device_type = stmt.excluded.device_type,
device_operational_status = stmt.excluded.device_operational_status,
device_drivers = stmt.excluded.device_drivers,
)
)
session.execute(stmt)
stmt = insert(EndPointModel).values(endpoints_data)
stmt = stmt.on_conflict_do_update(
index_elements=[
EndPointModel.context_uuid, EndPointModel.topology_uuid, EndPointModel.device_uuid,
EndPointModel.endpoint_uuid
],
index_elements=[EndPointModel.endpoint_uuid],
set_=dict(
endpoint_type = stmt.excluded.endpoint_type,
name = stmt.excluded.name,
endpoint_type = stmt.excluded.endpoint_type,
kpi_sample_types = stmt.excluded.kpi_sample_types,
)
)
session.execute(stmt)
session.execute(insert(TopologyDeviceModel).values(related_topologies).on_conflict_do_nothing(
index_elements=[
TopologyDeviceModel.context_uuid, TopologyDeviceModel.topology_uuid,
TopologyDeviceModel.device_uuid
]
index_elements=[TopologyDeviceModel.topology_uuid, TopologyDeviceModel.device_uuid]
))
session.execute(delete(ConfigRuleModel).where(ConfigRuleModel.device_uuid == device_uuid))
session.execute(insert(ConfigRuleModel).values(config_rules))
#session.execute(delete(ConfigRuleModel).where(ConfigRuleModel.device_uuid == device_uuid))
#session.execute(insert(ConfigRuleModel).values(config_rules))
run_transaction(sessionmaker(bind=db_engine), callback)
return False # TODO: improve and check if created/updated
updated = False # TODO: improve and check if created/updated
return DeviceId(**json_device_id(device_uuid)),updated
def device_delete(db_engine : Engine, request : DeviceId) -> bool:
device_uuid = request.device_uuid.uuid
device_uuid = device_get_uuid(request, allow_random=False)
def callback(session : Session) -> bool:
session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete()
#session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete()
num_deleted = session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete()
#db_device = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none()
#session.query(ConfigRuleModel).filter_by(config_uuid=db_device.device_config_uuid).delete()
......
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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 time
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy_cockroachdb import run_transaction
from typing import Dict, List, Optional
from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceIdList, ServiceList
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException
from context.service.database.models.ServiceModel import ServiceModel
def service_list_ids(db_engine : Engine, request : ContextId) -> ServiceIdList:
context_uuid = request.context_uuid.uuid
def callback(session : Session) -> List[Dict]:
obj_list : List[ServiceModel] = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all()
#.options(selectinload(ContextModel.service)).filter_by(context_uuid=context_uuid).one_or_none()
return [obj.dump_id() for obj in obj_list]
return ServiceIdList(service_ids=run_transaction(sessionmaker(bind=db_engine), callback))
def service_list_objs(db_engine : Engine, request : ContextId) -> ServiceList:
context_uuid = request.context_uuid.uuid
def callback(session : Session) -> List[Dict]:
obj_list : List[ServiceModel] = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all()
#.options(selectinload(ContextModel.service)).filter_by(context_uuid=context_uuid).one_or_none()
return [obj.dump() for obj in obj_list]
return ServiceList(services=run_transaction(sessionmaker(bind=db_engine), callback))
def service_get(db_engine : Engine, request : ServiceId) -> Service:
context_uuid = request.context_id.context_uuid.uuid
service_uuid = request.service_uuid.uuid
def callback(session : Session) -> Optional[Dict]:
obj : Optional[ServiceModel] = session.query(ServiceModel)\
.filter_by(context_uuid=context_uuid, service_uuid=service_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:
obj_uuid = '{:s}/{:s}'.format(context_uuid, service_uuid)
raise NotFoundException('Service', obj_uuid)
return Service(**obj)
def service_set(db_engine : Engine, request : Service) -> bool:
context_uuid = request.service_id.context_id.context_uuid.uuid
service_uuid = request.service_id.service_uuid.uuid
service_name = request.name
for i,endpoint_id in enumerate(request.service_endpoint_ids):
endpoint_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
if len(endpoint_context_uuid) > 0 and context_uuid != endpoint_context_uuid:
raise InvalidArgumentException(
'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i),
endpoint_context_uuid,
['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)])
def callback(session : Session) -> None:
service_data = [{
'context_uuid' : context_uuid,
'service_uuid': service_uuid,
'service_name': service_name,
'created_at' : time.time(),
}]
stmt = insert(ServiceModel).values(service_data)
stmt = stmt.on_conflict_do_update(
index_elements=[ServiceModel.context_uuid, ServiceModel.service_uuid],
set_=dict(service_name = stmt.excluded.service_name)
)
session.execute(stmt)
run_transaction(sessionmaker(bind=db_engine), callback)
return False # TODO: improve and check if created/updated
# # db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
# db_context = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none()
# # str_service_key = key_to_str([context_uuid, service_uuid])
# constraints_result = self.set_constraints(service_uuid, 'constraints', request.service_constraints)
# db_constraints = constraints_result[0][0]
#
# config_rules = grpc_config_rules_to_raw(request.service_config.config_rules)
# running_config_result = update_config(self.database, str_service_key, 'running', config_rules)
# db_running_config = running_config_result[0][0]
#
# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, {
# 'context_fk' : db_context,
# 'service_uuid' : service_uuid,
# 'service_type' : grpc_to_enum__service_type(request.service_type),
# 'service_constraints_fk': db_constraints,
# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status),
# 'service_config_fk' : db_running_config,
# })
# db_service, updated = result
#
# for i,endpoint_id in enumerate(request.service_endpoint_ids):
# endpoint_uuid = endpoint_id.endpoint_uuid.uuid
# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid
# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid
# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
#
# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
#
# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
#
# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--')
# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object(
# self.database, ServiceEndPointModel, str_service_endpoint_key, {
# 'service_fk': db_service, 'endpoint_fk': db_endpoint})
# #db_service_endpoint, service_endpoint_created = result
#
# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
# dict_service_id = db_service.dump_id()
# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
# return ServiceId(**dict_service_id)
# context_uuid = request.service_id.context_id.context_uuid.uuid
# db_context : ContextModel = get_object(self.database, ContextModel, context_uuid)
#
# for i,endpoint_id in enumerate(request.service_endpoint_ids):
# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid:
# raise InvalidArgumentException(
# 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i),
# endpoint_topology_context_uuid,
# ['should be == {:s}({:s})'.format(
# 'request.service_id.context_id.context_uuid.uuid', context_uuid)])
#
# service_uuid = request.service_id.service_uuid.uuid
# str_service_key = key_to_str([context_uuid, service_uuid])
#
# constraints_result = set_constraints(
# self.database, str_service_key, 'service', request.service_constraints)
# db_constraints = constraints_result[0][0]
#
# running_config_rules = update_config(
# self.database, str_service_key, 'service', request.service_config.config_rules)
# db_running_config = running_config_rules[0][0]
#
# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, {
# 'context_fk' : db_context,
# 'service_uuid' : service_uuid,
# 'service_type' : grpc_to_enum__service_type(request.service_type),
# 'service_constraints_fk': db_constraints,
# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status),
# 'service_config_fk' : db_running_config,
# })
# db_service, updated = result
#
# for i,endpoint_id in enumerate(request.service_endpoint_ids):
# endpoint_uuid = endpoint_id.endpoint_uuid.uuid
# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid
# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid
# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid
#
# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
#
# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key)
#
# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--')
# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object(
# self.database, ServiceEndPointModel, str_service_endpoint_key, {
# 'service_fk': db_service, 'endpoint_fk': db_endpoint})
# #db_service_endpoint, service_endpoint_created = result
#
# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
# dict_service_id = db_service.dump_id()
# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id})
# return ServiceId(**dict_service_id)
# def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int
# ) -> Tuple[Union_ConstraintModel, bool]:
# with self.session() as session:
#
# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint'))
#
# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind)
# if parser is None:
# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format(
# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint)))
#
# # create specific constraint
# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint)
# str_constraint_id = str(uuid.uuid4())
# LOGGER.info('str_constraint_id: {}'.format(str_constraint_id))
# # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id]))
# # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':')
#
# # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object(
# # database, constraint_class, str_constraint_key, constraint_data)
# constraint_data[constraint_class.main_pk_name()] = str_constraint_id
# db_new_constraint = constraint_class(**constraint_data)
# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint)
# db_specific_constraint, updated = result
#
# # create generic constraint
# # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value)
# constraint_data = {
# 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind
# }
#
# db_new_constraint = ConstraintModel(**constraint_data)
# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint)
# db_constraint, updated = result
#
# return db_constraint, updated
#
# def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints
# ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]:
# with self.session() as session:
# # str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':')
# # result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key)
# result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none()
# created = None
# if result:
# created = True
# session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none()
# db_constraints = ConstraintsModel(constraints_uuid=service_uuid)
# session.add(db_constraints)
#
# db_objects = [(db_constraints, created)]
#
# for position,grpc_constraint in enumerate(grpc_constraints):
# result : Tuple[ConstraintModel, bool] = self.set_constraint(
# db_constraints, grpc_constraint, position)
# db_constraint, updated = result
# db_objects.append((db_constraint, updated))
#
# return db_objects
def service_delete(db_engine : Engine, request : ServiceId) -> bool:
context_uuid = request.context_id.context_uuid.uuid
service_uuid = request.service_uuid.uuid
def callback(session : Session) -> bool:
num_deleted = session.query(ServiceModel)\
.filter_by(context_uuid=context_uuid, service_uuid=service_uuid).delete()
return num_deleted > 0
return run_transaction(sessionmaker(bind=db_engine), callback)
# def delete(self) -> None:
# from .RelationModels import ServiceEndPointModel
# for db_service_endpoint_pk,_ in self.references(ServiceEndPointModel):
# ServiceEndPointModel(self.database, db_service_endpoint_pk).delete()
# super().delete()
# ConfigModel(self.database, self.service_config_fk).delete()
# ConstraintsModel(self.database, self.service_constraints_fk).delete()
......@@ -12,19 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy_cockroachdb import run_transaction
from typing import Dict, List, Optional, Set
from common.proto.context_pb2 import ContextId, Topology, TopologyId, TopologyIdList, TopologyList
from common.rpc_method_wrapper.ServiceExceptions import NotFoundException
from context.service.database.models.RelationModels import TopologyDeviceModel
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException, NotFoundException
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Topology import json_topology_id
#from context.service.database.models.RelationModels import TopologyDeviceModel, TopologyLinkModel
from context.service.database.models.TopologyModel import TopologyModel
from .uuids.Context import context_get_uuid
from .uuids.Topology import topology_get_uuid
def topology_list_ids(db_engine : Engine, request : ContextId) -> TopologyIdList:
context_uuid = request.context_uuid.uuid
context_uuid = context_get_uuid(request, allow_random=False)
def callback(session : Session) -> List[Dict]:
obj_list : List[TopologyModel] = session.query(TopologyModel).filter_by(context_uuid=context_uuid).all()
#.options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none()
......@@ -32,7 +35,7 @@ def topology_list_ids(db_engine : Engine, request : ContextId) -> TopologyIdList
return TopologyIdList(topology_ids=run_transaction(sessionmaker(bind=db_engine), callback))
def topology_list_objs(db_engine : Engine, request : ContextId) -> TopologyList:
context_uuid = request.context_uuid.uuid
context_uuid = context_get_uuid(request, allow_random=False)
def callback(session : Session) -> List[Dict]:
obj_list : List[TopologyModel] = session.query(TopologyModel).filter_by(context_uuid=context_uuid).all()
#.options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none()
......@@ -40,84 +43,74 @@ def topology_list_objs(db_engine : Engine, request : ContextId) -> TopologyList:
return TopologyList(topologies=run_transaction(sessionmaker(bind=db_engine), callback))
def topology_get(db_engine : Engine, request : TopologyId) -> Topology:
context_uuid = request.context_id.context_uuid.uuid
topology_uuid = request.topology_uuid.uuid
_,topology_uuid = topology_get_uuid(request, allow_random=False)
def callback(session : Session) -> Optional[Dict]:
obj : Optional[TopologyModel] = session.query(TopologyModel)\
.filter_by(context_uuid=context_uuid, topology_uuid=topology_uuid).one_or_none()
.filter_by(topology_uuid=topology_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:
obj_uuid = '{:s}/{:s}'.format(context_uuid, topology_uuid)
raise NotFoundException('Topology', obj_uuid)
context_uuid = context_get_uuid(request.context_id, allow_random=False)
raw_topology_uuid = '{:s}/{:s}'.format(request.context_id.context_uuid.uuid, request.topology_uuid.uuid)
raise NotFoundException('Topology', raw_topology_uuid, extra_details=[
'context_uuid generated was: {:s}'.format(context_uuid),
'topology_uuid generated was: {:s}'.format(topology_uuid),
])
return Topology(**obj)
def topology_set(db_engine : Engine, request : Topology) -> bool:
context_uuid = request.topology_id.context_id.context_uuid.uuid
topology_uuid = request.topology_id.topology_uuid.uuid
topology_name = request.name
if len(topology_name) == 0: topology_name = request.topology_id.topology_uuid.uuid
context_uuid,topology_uuid = topology_get_uuid(request.topology_id, topology_name=topology_name, allow_random=True)
#device_uuids : Set[str] = set()
#devices_to_add : List[Dict] = list()
#for device_id in request.device_ids:
# device_uuid = device_id.device_uuid.uuid
# if device_uuid in device_uuids: continue
# devices_to_add.append({'topology_uuid': topology_uuid, 'device_uuid': device_uuid})
# device_uuids.add(device_uuid)
device_uuids : Set[str] = set()
devices_to_add : List[Dict] = list()
for device_id in request.device_ids:
device_uuid = device_id.device_uuid.uuid
if device_uuid in device_uuids: continue
devices_to_add.append({
'context_uuid': context_uuid, 'topology_uuid': topology_uuid, 'device_uuid': device_uuid
})
device_uuids.add(device_uuid)
#link_uuids : Set[str] = set()
#links_to_add : List[Dict] = list()
#for link_id in request.link_ids:
# link_uuid = link_id.link_uuid.uuid
# if link_uuid in link_uuids: continue
# links_to_add.append({'topology_uuid': topology_uuid, 'link_uuid': link_uuid})
# link_uuids.add(link_uuid)
link_uuids : Set[str] = set()
links_to_add : List[Dict] = list()
for link_id in request.link_ids:
link_uuid = link_id.link_uuid.uuid
if link_uuid in link_uuids: continue
links_to_add.append({
'context_uuid': context_uuid, 'topology_uuid': topology_uuid, 'link_uuid': link_uuid
})
link_uuids.add(link_uuid)
topology_data = [{
'context_uuid' : context_uuid,
'topology_uuid': topology_uuid,
'topology_name': topology_name,
}]
def callback(session : Session) -> None:
topology_data = [{
'context_uuid' : context_uuid,
'topology_uuid': topology_uuid,
'topology_name': topology_name,
'created_at' : time.time(),
}]
stmt = insert(TopologyModel).values(topology_data)
stmt = stmt.on_conflict_do_update(
index_elements=[TopologyModel.context_uuid, TopologyModel.topology_uuid],
index_elements=[TopologyModel.topology_uuid],
set_=dict(topology_name = stmt.excluded.topology_name)
)
session.execute(stmt)
if len(devices_to_add) > 0:
session.execute(insert(TopologyDeviceModel).values(devices_to_add).on_conflict_do_nothing(
index_elements=[
TopologyDeviceModel.context_uuid, TopologyDeviceModel.topology_uuid,
TopologyDeviceModel.device_uuid
]
))
#if len(devices_to_add) > 0:
# session.execute(insert(TopologyDeviceModel).values(devices_to_add).on_conflict_do_nothing(
# index_elements=[TopologyDeviceModel.topology_uuid, TopologyDeviceModel.device_uuid]
# ))
#if len(link_to_add) > 0:
#if len(links_to_add) > 0:
# session.execute(insert(TopologyLinkModel).values(links_to_add).on_conflict_do_nothing(
# index_elements=[
# TopologyLinkModel.context_uuid, TopologyLinkModel.topology_uuid,
# TopologyLinkModel.link_uuid
# ]
# index_elements=[TopologyLinkModel.topology_uuid, TopologyLinkModel.link_uuid]
# ))
run_transaction(sessionmaker(bind=db_engine), callback)
return False # TODO: improve and check if created/updated
updated = False # TODO: improve and check if created/updated
return TopologyId(**json_topology_id(topology_uuid, json_context_id(context_uuid))),updated
def topology_delete(db_engine : Engine, request : TopologyId) -> bool:
context_uuid = request.context_id.context_uuid.uuid
topology_uuid = request.topology_uuid.uuid
_,topology_uuid = topology_get_uuid(request, allow_random=False)
def callback(session : Session) -> bool:
num_deleted = session.query(TopologyModel)\
.filter_by(context_uuid=context_uuid, topology_uuid=topology_uuid).delete()
.filter_by(topology_uuid=topology_uuid).delete()
return num_deleted > 0
return run_transaction(sessionmaker(bind=db_engine), callback)
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from common.proto.context_pb2 import ContextId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
def context_get_uuid(
context_id : ContextId, context_name : str = '', allow_random : bool = False
) -> str:
context_uuid = context_id.context_uuid.uuid
if len(context_uuid) > 0:
return get_uuid_from_string(context_uuid)
if len(context_name) > 0:
return get_uuid_from_string(context_name)
if allow_random: return get_uuid_random()
raise InvalidArgumentsException([
('context_id.context_uuid.uuid', context_uuid),
('name', context_name),
], extra_details=['At least one is required to produce a Context UUID'])
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from common.proto.context_pb2 import DeviceId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
def device_get_uuid(
device_id : DeviceId, device_name : str = '', allow_random : bool = False
) -> str:
device_uuid = device_id.device_uuid.uuid
if len(device_uuid) > 0:
return get_uuid_from_string(device_uuid)
if len(device_name) > 0:
return get_uuid_from_string(device_name)
if allow_random: return get_uuid_random()
raise InvalidArgumentsException([
('device_id.device_uuid.uuid', device_uuid),
('name', device_name),
], extra_details=['At least one is required to produce a Device UUID'])
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Tuple
from common.proto.context_pb2 import EndPointId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
from .Device import device_get_uuid
from .Topology import topology_get_uuid
def endpoint_get_uuid(
endpoint_id : EndPointId, endpoint_name : str = '', allow_random : bool = False
) -> Tuple[str, str, str]:
device_uuid = device_get_uuid(endpoint_id.device_id, allow_random=False)
_,topology_uuid = topology_get_uuid(endpoint_id.topology_id, allow_random=False)
raw_endpoint_uuid = endpoint_id.endpoint_uuid.uuid
if len(raw_endpoint_uuid) > 0:
prefix_for_name = '{:s}/{:s}'.format(topology_uuid, device_uuid)
return topology_uuid, device_uuid, get_uuid_from_string(raw_endpoint_uuid, prefix_for_name=prefix_for_name)
if len(endpoint_name) > 0:
prefix_for_name = '{:s}/{:s}'.format(topology_uuid, device_uuid)
return topology_uuid, device_uuid, get_uuid_from_string(endpoint_name, prefix_for_name=prefix_for_name)
if allow_random:
return topology_uuid, device_uuid, get_uuid_random()
raise InvalidArgumentsException([
('endpoint_id.endpoint_uuid.uuid', raw_endpoint_uuid),
('name', endpoint_name),
], extra_details=['At least one is required to produce a EndPoint UUID'])
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from common.proto.context_pb2 import LinkId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
def link_get_uuid(
link_id : LinkId, link_name : str = '', allow_random : bool = False
) -> str:
link_uuid = link_id.link_uuid.uuid
if len(link_uuid) > 0:
return get_uuid_from_string(link_uuid)
if len(link_name) > 0:
return get_uuid_from_string(link_name)
if allow_random: return get_uuid_random()
raise InvalidArgumentsException([
('link_id.link_uuid.uuid', link_uuid),
('name', link_name),
], extra_details=['At least one is required to produce a Link UUID'])
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Tuple
from common.proto.context_pb2 import TopologyId
from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
from .Context import context_get_uuid
def topology_get_uuid(
topology_id : TopologyId, topology_name : str = '', allow_random : bool = False
) -> Tuple[str, str]:
context_uuid = context_get_uuid(topology_id.context_id, allow_random=False)
raw_topology_uuid = topology_id.topology_uuid.uuid
if len(raw_topology_uuid) > 0:
return context_uuid, get_uuid_from_string(raw_topology_uuid, prefix_for_name=context_uuid)
if len(topology_name) > 0:
return context_uuid, get_uuid_from_string(topology_name, prefix_for_name=context_uuid)
if allow_random:
return context_uuid, get_uuid_random()
raise InvalidArgumentsException([
('topology_id.topology_uuid.uuid', raw_topology_uuid),
('name', topology_name),
], extra_details=['At least one is required to produce a Topology UUID'])
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import 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())
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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.
......@@ -13,32 +13,53 @@
# limitations under the License.
import enum, json
from sqlalchemy import Column, ForeignKey, INTEGER, CheckConstraint, Enum, String, text
from sqlalchemy import Column, INTEGER, CheckConstraint, Enum, ForeignKeyConstraint, String, UniqueConstraint, text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from typing import Dict
from .enums.ConfigAction import ORM_ConfigActionEnum
from ._Base import _Base
# enum values should match name of field in ConfigRuleModel
# Enum values should match name of field in ConfigRuleModel
class ConfigRuleKindEnum(enum.Enum):
CUSTOM = 'custom'
ACL = 'acl'
class ConfigRuleModel(_Base):
__tablename__ = 'config_rule'
device_uuid = Column(UUID(as_uuid=False), ForeignKey('device.device_uuid', ondelete='CASCADE'), primary_key=True)
rule_uuid = Column(UUID(as_uuid=False), primary_key=True, server_default=text('uuid_generate_v4()'))
kind = Column(Enum(ConfigRuleKindEnum))
action = Column(Enum(ORM_ConfigActionEnum))
position = Column(INTEGER, nullable=False)
data = Column(String, nullable=False)
config_rule_uuid = Column(UUID(as_uuid=False), primary_key=True, server_default=text('uuid_generate_v4()'))
device_uuid = Column(UUID(as_uuid=False)) # for device config rules
context_uuid = Column(UUID(as_uuid=False)) # for service/slice config rules
service_uuid = Column(UUID(as_uuid=False)) # for service config rules
#slice_uuid = Column(UUID(as_uuid=False)) # for slice config rules
kind = Column(Enum(ConfigRuleKindEnum))
action = Column(Enum(ORM_ConfigActionEnum))
position = Column(INTEGER, nullable=False)
data = Column(String, nullable=False)
__table_args__ = (
CheckConstraint(position >= 0, name='check_position_value'),
UniqueConstraint('device_uuid', 'position', name='unique_per_device'),
UniqueConstraint('context_uuid', 'service_uuid', 'position', name='unique_per_service'),
#UniqueConstraint('context_uuid', 'slice_uuid', 'position', name='unique_per_slice'),
ForeignKeyConstraint(
['device_uuid'],
['device.device_uuid'],
ondelete='CASCADE'),
ForeignKeyConstraint(
['context_uuid', 'service_uuid'],
['service.context_uuid', 'service.service_uuid'],
ondelete='CASCADE'),
#ForeignKeyConstraint(
# ['context_uuid', 'slice_uuid'],
# ['slice.context_uuid', 'slice.slice_uuid'],
# ondelete='CASCADE'),
)
device = relationship('DeviceModel', back_populates='config_rules')
service = relationship('ServiceModel', back_populates='config_rules')
#slice = relationship('SliceModel', back_populates='config_rules')
def dump(self) -> Dict:
return {self.kind.value: json.loads(self.data)}
......@@ -12,28 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Dict, List
from sqlalchemy import Column, Float, String
from typing import Dict
from sqlalchemy import Column, String
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from ._Base import _Base
class ContextModel(_Base):
__tablename__ = 'context'
context_uuid = Column(UUID(as_uuid=False), primary_key=True)
context_name = Column(String(), nullable=False)
created_at = Column(Float)
context_name = Column(String, nullable=False)
topologies = relationship('TopologyModel', back_populates='context')
#services = relationship('ServiceModel', back_populates='context')
#slices = relationship('SliceModel', back_populates='context')
#services = relationship('ServiceModel', back_populates='context')
#slices = relationship('SliceModel', back_populates='context')
def dump_id(self) -> Dict:
return {'context_uuid': {'uuid': self.context_uuid}}
def dump_topology_ids(self) -> List[Dict]:
return
def dump(self) -> Dict:
return {
'context_id' : self.dump_id(),
......
......@@ -14,12 +14,12 @@
import operator
from typing import Dict
from sqlalchemy import Column, Float, String, Enum
from sqlalchemy import Column, String, Enum
from sqlalchemy.dialects.postgresql import UUID, ARRAY
from sqlalchemy.orm import relationship
from ._Base import _Base
from .enums.DeviceDriver import ORM_DeviceDriverEnum
from .enums.DeviceOperationalStatus import ORM_DeviceOperationalStatusEnum
from ._Base import _Base
class DeviceModel(_Base):
__tablename__ = 'device'
......@@ -28,10 +28,9 @@ class DeviceModel(_Base):
device_type = Column(String, nullable=False)
device_operational_status = Column(Enum(ORM_DeviceOperationalStatusEnum))
device_drivers = Column(ARRAY(Enum(ORM_DeviceDriverEnum), dimensions=1))
created_at = Column(Float)
topology_devices = relationship('TopologyDeviceModel', back_populates='device')
config_rules = relationship('ConfigRuleModel', passive_deletes=True, back_populates='device', lazy='joined')
#config_rules = relationship('ConfigRuleModel', passive_deletes=True, back_populates='device', lazy='joined')
endpoints = relationship('EndPointModel', passive_deletes=True, back_populates='device', lazy='joined')
def dump_id(self) -> Dict:
......@@ -45,8 +44,11 @@ class DeviceModel(_Base):
'device_operational_status': self.device_operational_status.value,
'device_drivers' : [driver.value for driver in self.device_drivers],
'device_config' : {'config_rules': [
config_rule.dump()
for config_rule in sorted(self.config_rules, key=operator.attrgetter('position'))
#config_rule.dump()
#for config_rule in sorted(self.config_rules, key=operator.attrgetter('position'))
]},
'device_endpoints' : [endpoint.dump() for endpoint in self.endpoints],
'device_endpoints' : [
endpoint.dump()
for endpoint in self.endpoints
],
}
......@@ -13,7 +13,7 @@
# limitations under the License.
from typing import Dict
from sqlalchemy import Column, String, Enum, ForeignKeyConstraint
from sqlalchemy import Column, Enum, ForeignKey, String
from sqlalchemy.dialects.postgresql import ARRAY, UUID
from sqlalchemy.orm import relationship
from .enums.KpiSampleType import ORM_KpiSampleTypeEnum
......@@ -21,32 +21,23 @@ from ._Base import _Base
class EndPointModel(_Base):
__tablename__ = 'endpoint'
context_uuid = Column(UUID(as_uuid=False), primary_key=True)
topology_uuid = Column(UUID(as_uuid=False), primary_key=True)
device_uuid = Column(UUID(as_uuid=False), primary_key=True)
endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True)
endpoint_type = Column(String)
kpi_sample_types = Column(ARRAY(Enum(ORM_KpiSampleTypeEnum), dimensions=1))
__table_args__ = (
ForeignKeyConstraint(
['context_uuid', 'topology_uuid'],
['topology.context_uuid', 'topology.topology_uuid'],
ondelete='CASCADE'),
ForeignKeyConstraint(
['device_uuid'],
['device.device_uuid'],
ondelete='CASCADE'),
)
endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True)
device_uuid = Column(UUID(as_uuid=False), ForeignKey('device.device_uuid', ondelete='CASCADE'))
topology_uuid = Column(UUID(as_uuid=False), ForeignKey('topology.topology_uuid', ondelete='RESTRICT'))
name = Column(String)
endpoint_type = Column(String)
kpi_sample_types = Column(ARRAY(Enum(ORM_KpiSampleTypeEnum), dimensions=1))
topology = relationship('TopologyModel', back_populates='endpoints')
device = relationship('DeviceModel', back_populates='endpoints')
link_endpoints = relationship('LinkEndPointModel', back_populates='endpoint')
device = relationship('DeviceModel', back_populates='endpoints')
topology = relationship('TopologyModel')
#link_endpoints = relationship('LinkEndPointModel', back_populates='endpoint' )
#service_endpoints = relationship('ServiceEndPointModel', back_populates='endpoint' )
def dump_id(self) -> Dict:
result = {
'topology_id': self.topology.dump_id(),
'device_id': self.device.dump_id(),
'topology_id' : self.topology.dump_id(),
'device_id' : self.device.dump_id(),
'endpoint_uuid': {'uuid': self.endpoint_uuid},
}
return result
......@@ -54,34 +45,7 @@ class EndPointModel(_Base):
def dump(self) -> Dict:
return {
'endpoint_id' : self.dump_id(),
'name' : self.name,
'endpoint_type' : self.endpoint_type,
'kpi_sample_types': [kst.value for kst in self.kpi_sample_types],
}
# def get_endpoint(
# database : Database, grpc_endpoint_id : EndPointId,
# validate_topology_exists : bool = True, validate_device_in_topology : bool = True
# ) -> Tuple[str, EndPointModel]:
# endpoint_uuid = grpc_endpoint_id.endpoint_uuid.uuid
# endpoint_device_uuid = grpc_endpoint_id.device_id.device_uuid.uuid
# endpoint_topology_uuid = grpc_endpoint_id.topology_id.topology_uuid.uuid
# endpoint_topology_context_uuid = grpc_endpoint_id.topology_id.context_id.context_uuid.uuid
# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid])
#
# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0:
# # check topology exists
# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid])
# if validate_topology_exists:
# from .TopologyModel import TopologyModel
# get_object(database, TopologyModel, str_topology_key)
#
# # check device is in topology
# str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--')
# if validate_device_in_topology:
# from .RelationModels import TopologyDeviceModel
# get_object(database, TopologyDeviceModel, str_topology_device_key)
#
# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':')
#
# db_endpoint : EndPointModel = get_object(database, EndPointModel, str_endpoint_key)
# return str_endpoint_key, db_endpoint
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