Skip to content
Snippets Groups Projects
Commit 843911d9 authored by Mohammad Ismaeel's avatar Mohammad Ismaeel
Browse files

Merge branch 'develop' of https://labs.etsi.org/rep/tfs/controller into...

Merge branch 'develop' of https://labs.etsi.org/rep/tfs/controller into cnit_related_activity_openroadm
parents 9274f74b 459c6f14
No related branches found
No related tags found
2 merge requests!294Release TeraFlowSDN 4.0,!285Resolve: "(CNIT) New SBI Driver based on OpenROADM for ROADMs"
Showing
with 672 additions and 1 deletion
# 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, uuid
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.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException
from common.message_broker.MessageBroker import MessageBroker
from common.proto.context_pb2 import Empty
from common.proto.qkd_app_pb2 import (
AppList, App, AppId)
from common.tools.grpc.Tools import grpc_message_to_json_string
from .models.QKDAppModel import AppModel
from .models.enums.QKDAppStatus import grpc_to_enum__qkd_app_status
from .models.enums.QKDAppTypes import grpc_to_enum__qkd_app_types
from .uuids.QKDApp import app_get_uuid
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.QKDApp import json_app_id
from context.service.database.uuids.Context import context_get_uuid
#from .Events import notify_event_context, notify_event_device, notify_event_topology
LOGGER = logging.getLogger(__name__)
def app_list_objs(db_engine : Engine) -> AppList:
def callback(session : Session) -> List[Dict]:
obj_list : List[AppModel] = session.query(AppModel)\
.all()
return [obj.dump() for obj in obj_list]
apps = run_transaction(sessionmaker(bind=db_engine), callback)
return AppList(apps=apps)
def app_get(db_engine : Engine, request : AppId) -> App:
app_uuid = app_get_uuid(request, allow_random=False)
def callback(session : Session) -> Optional[Dict]:
obj : Optional[AppModel] = session.query(AppModel)\
.filter_by(app_uuid=app_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_app_uuid = request.app_uuid.uuid
raise NotFoundException('App', raw_app_uuid, extra_details=[
'app_uuid generated was: {:s}'.format(app_uuid)
])
return App(**obj)
def app_set(db_engine : Engine, messagebroker : MessageBroker, request : App) -> AppId:
context_uuid = context_get_uuid(request.app_id.context_id, allow_random=False)
raw_app_uuid = request.app_id.app_uuid.uuid
app_uuid = app_get_uuid(request.app_id, allow_random=True)
app_type = request.app_type
app_status = grpc_to_enum__qkd_app_status(request.app_status)
app_type = grpc_to_enum__qkd_app_types(request.app_type)
now = datetime.datetime.utcnow()
app_data = [{
'context_uuid' : context_uuid,
'app_uuid' : app_uuid,
'app_status' : app_status,
'app_type' : app_type,
'server_app_id' : request.server_app_id,
'client_app_id' : request.client_app_id,
'backing_qkdl_uuid' : [qkdl_id.qkdl_uuid.uuid for qkdl_id in request.backing_qkdl_id],
'local_device_uuid' : request.local_device_id.device_uuid.uuid,
'remote_device_uuid' : request.remote_device_id.device_uuid.uuid or None,
'created_at' : now,
'updated_at' : now,
}]
def callback(session : Session) -> Tuple[bool, List[Dict]]:
stmt = insert(AppModel).values(app_data)
stmt = stmt.on_conflict_do_update(
index_elements=[AppModel.app_uuid],
set_=dict(
app_status = stmt.excluded.app_status,
app_type = stmt.excluded.app_type,
server_app_id = stmt.excluded.server_app_id,
client_app_id = stmt.excluded.client_app_id,
backing_qkdl_uuid = stmt.excluded.backing_qkdl_uuid,
local_device_uuid = stmt.excluded.local_device_uuid,
remote_device_uuid = stmt.excluded.remote_device_uuid,
updated_at = stmt.excluded.updated_at,
)
)
stmt = stmt.returning(AppModel.created_at, AppModel.updated_at)
created_at,updated_at = session.execute(stmt).fetchone()
updated = updated_at > created_at
return updated
updated = run_transaction(sessionmaker(bind=db_engine), callback)
context_id = json_context_id(context_uuid)
app_id = json_app_id(app_uuid, context_id=context_id)
#event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
#notify_event_app(messagebroker, event_type, app_id)
#notify_event_context(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, context_id)
return AppId(**app_id)
def app_get_by_server(db_engine : Engine, request : str) -> App:
def callback(session : Session) -> Optional[Dict]:
obj : Optional[AppModel] = session.query(AppModel)\
.filter_by(server_app_id=request).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('No app match found for', request)
return App(**obj)
"""
def device_delete(db_engine : Engine, messagebroker : MessageBroker, request : DeviceId) -> Empty:
device_uuid = device_get_uuid(request, allow_random=False)
def callback(session : Session) -> Tuple[bool, List[Dict]]:
query = session.query(TopologyDeviceModel)
query = query.filter_by(device_uuid=device_uuid)
topology_device_list : List[TopologyDeviceModel] = query.all()
topology_ids = [obj.topology.dump_id() for obj in topology_device_list]
num_deleted = session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete()
return num_deleted > 0, topology_ids
deleted, updated_topology_ids = run_transaction(sessionmaker(bind=db_engine), callback)
device_id = json_device_id(device_uuid)
if deleted:
notify_event_device(messagebroker, EventTypeEnum.EVENTTYPE_REMOVE, device_id)
context_ids : Dict[str, Dict] = dict()
topology_ids : Dict[str, Dict] = dict()
for topology_id in updated_topology_ids:
topology_uuid = topology_id['topology_uuid']['uuid']
topology_ids[topology_uuid] = topology_id
context_id = topology_id['context_id']
context_uuid = context_id['context_uuid']['uuid']
context_ids[context_uuid] = context_id
for topology_id in topology_ids.values():
notify_event_topology(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, topology_id)
for context_id in context_ids.values():
notify_event_context(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, context_id)
return Empty()
def device_select(db_engine : Engine, request : DeviceFilter) -> DeviceList:
device_uuids = [
device_get_uuid(device_id, allow_random=False)
for device_id in request.device_ids.device_ids
]
dump_params = dict(
include_endpoints =request.include_endpoints,
include_config_rules=request.include_config_rules,
include_components =request.include_components,
)
def callback(session : Session) -> List[Dict]:
query = session.query(DeviceModel)
if request.include_endpoints : query = query.options(selectinload(DeviceModel.endpoints))
if request.include_config_rules: query = query.options(selectinload(DeviceModel.config_rules))
#if request.include_components : query = query.options(selectinload(DeviceModel.components))
obj_list : List[DeviceModel] = query.filter(DeviceModel.device_uuid.in_(device_uuids)).all()
return [obj.dump(**dump_params) for obj in obj_list]
devices = run_transaction(sessionmaker(bind=db_engine), callback)
return DeviceList(devices=devices)
"""
# 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.
# 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 operator
from sqlalchemy import CheckConstraint, Column, DateTime, Float, Enum, ForeignKey, Integer, String
from sqlalchemy.dialects.postgresql import UUID, ARRAY
from sqlalchemy.orm import relationship
from typing import Dict
from ._Base import _Base
from .enums.QKDAppStatus import ORM_QKDAppStatusEnum
from .enums.QKDAppTypes import ORM_QKDAppTypesEnum
class AppModel(_Base):
__tablename__ = 'qkd_app'
app_uuid = Column(UUID(as_uuid=False), primary_key=True)
context_uuid = Column(UUID(as_uuid=False), nullable=False) # Supposed to be Foreign Key
app_status = Column(Enum(ORM_QKDAppStatusEnum), nullable=False)
app_type = Column(Enum(ORM_QKDAppTypesEnum), nullable=False)
server_app_id = Column(String, nullable=False)
client_app_id = Column(ARRAY(String), nullable=False)
backing_qkdl_uuid = Column(ARRAY(UUID(as_uuid=False)), nullable=False)
local_device_uuid = Column(UUID(as_uuid=False), nullable=False)
remote_device_uuid = Column(UUID(as_uuid=False), nullable=True)
# Optare: Created_at and Updated_at are only used to know if an app was updated later on the code. Don't change it
created_at = Column(DateTime, nullable=False)
updated_at = Column(DateTime, nullable=False)
#__table_args__ = (
# CheckConstraint(... >= 0, name='name_value_...'),
#)
def dump_id(self) -> Dict:
return {
'context_id': {'context_uuid': {'uuid': self.context_uuid}},
'app_uuid': {'uuid': self.app_uuid}
}
def dump(self) -> Dict:
result = {
'app_id' : self.dump_id(),
'app_status' : self.app_status.value,
'app_type' : self.app_type.value,
'server_app_id' : self.server_app_id,
'client_app_id' : self.client_app_id,
'backing_qkdl_id' : [{'qkdl_uuid': {'uuid': qkdl_id}} for qkdl_id in self.backing_qkdl_uuid],
'local_device_id' : {'device_uuid': {'uuid': self.local_device_uuid}},
'remote_device_id' : {'device_uuid': {'uuid': self.remote_device_uuid}},
}
return result
# 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 sqlalchemy
from typing import Any, List
from sqlalchemy.orm import Session, sessionmaker, declarative_base
from sqlalchemy.sql import text
from sqlalchemy_cockroachdb import run_transaction
_Base = declarative_base()
'''
def create_performance_enhancers(db_engine : sqlalchemy.engine.Engine) -> None:
def index_storing(
index_name : str, table_name : str, index_fields : List[str], storing_fields : List[str]
) -> Any:
str_index_fields = ','.join(['"{:s}"'.format(index_field) for index_field in index_fields])
str_storing_fields = ','.join(['"{:s}"'.format(storing_field) for storing_field in storing_fields])
INDEX_STORING = 'CREATE INDEX IF NOT EXISTS {:s} ON "{:s}" ({:s}) STORING ({:s});'
return text(INDEX_STORING.format(index_name, table_name, str_index_fields, str_storing_fields))
statements = [
# In case of relations
]
def callback(session : Session) -> bool:
for stmt in statements: session.execute(stmt)
run_transaction(sessionmaker(bind=db_engine), callback)
'''
def rebuild_database(db_engine : sqlalchemy.engine.Engine, drop_if_exists : bool = False):
if drop_if_exists: _Base.metadata.drop_all(db_engine)
_Base.metadata.create_all(db_engine)
#create_performance_enhancers(db_engine)
# 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.
# 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 enum, functools
from common.proto.qkd_app_pb2 import QKDAppStatusEnum
from ._GrpcToEnum import grpc_to_enum
class ORM_QKDAppStatusEnum(enum.Enum):
ON = QKDAppStatusEnum.QKDAPPSTATUS_ON
DISCONNECTED = QKDAppStatusEnum.QKDAPPSTATUS_DISCONNECTED
OUT_OF_TIME = QKDAppStatusEnum.QKDAPPSTATUS_OUT_OF_TIME
ZOMBIE = QKDAppStatusEnum.QKDAPPSTATUS_ZOMBIE
grpc_to_enum__qkd_app_status = functools.partial(
grpc_to_enum, QKDAppStatusEnum, ORM_QKDAppStatusEnum)
# 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 enum, functools
from common.proto.qkd_app_pb2 import QKDAppTypesEnum
from ._GrpcToEnum import grpc_to_enum
class ORM_QKDAppTypesEnum(enum.Enum):
INTERNAL = QKDAppTypesEnum.QKDAPPTYPES_INTERNAL
CLIENT = QKDAppTypesEnum.QKDAPPTYPES_CLIENT
grpc_to_enum__qkd_app_types = functools.partial(
grpc_to_enum, QKDAppTypesEnum, ORM_QKDAppTypesEnum)
# 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 re
from enum import Enum
from typing import Optional
# Enumeration classes are redundant with gRPC classes, but gRPC does not provide a programmatical method to retrieve
# the values it expects from strings containing the desired value symbol or its integer value, so a kind of mapping is
# required. Besides, ORM Models expect Enum classes in EnumeratedFields; we create specific and conveniently defined
# Enum classes to serve both purposes.
def grpc_to_enum(grpc_enum_class, orm_enum_class : Enum, grpc_enum_value, grpc_enum_prefix : Optional[str] = None):
enum_name = grpc_enum_class.Name(grpc_enum_value)
if grpc_enum_prefix is None:
grpc_enum_prefix = orm_enum_class.__name__.upper()
#grpc_enum_prefix = re.sub(r'^ORM_(.+)$', r'\1', grpc_enum_prefix)
#grpc_enum_prefix = re.sub(r'^(.+)ENUM$', r'\1', grpc_enum_prefix)
#grpc_enum_prefix = grpc_enum_prefix + '_'
grpc_enum_prefix = re.sub(r'^ORM_(.+)ENUM$', r'\1_', grpc_enum_prefix)
if len(grpc_enum_prefix) > 0:
enum_name = enum_name.replace(grpc_enum_prefix, '')
orm_enum_value = orm_enum_class._member_map_.get(enum_name)
return orm_enum_value
# 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.
# 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.proto.qkd_app_pb2 import AppId
from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
def app_get_uuid(
app_id : AppId, allow_random : bool = False
) -> str:
app_uuid = app_id.app_uuid.uuid
if len(app_uuid) > 0:
return get_uuid_from_string(app_uuid)
if allow_random: return get_uuid_random()
raise InvalidArgumentsException([
('app_id.app_uuid.uuid', app_uuid),
], extra_details=['At least one is required to produce a App UUID'])
# 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 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 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.Constants import ServiceNameEnum
from common.Settings import get_service_baseurl_http, get_service_port_http
from common.tools.service.GenericRestServer import GenericRestServer
class RestServer(GenericRestServer):
def __init__(self, cls_name: str = __name__) -> None:
bind_port = get_service_port_http(ServiceNameEnum.APP)
base_url = get_service_baseurl_http(ServiceNameEnum.APP)
super().__init__(bind_port, base_url, cls_name=cls_name)
# 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.
# 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 uuid, json
from flask import request
from flask_restful import Resource
from common.proto.context_pb2 import Empty
from common.proto.qkd_app_pb2 import App, QKDAppTypesEnum
from common.Constants import DEFAULT_CONTEXT_NAME
from context.client.ContextClient import ContextClient
from qkd_app.client.QKDAppClient import QKDAppClient
class _Resource(Resource):
def __init__(self) -> None:
super().__init__()
self.context_client = ContextClient()
self.qkd_app_client = QKDAppClient()
class Index(_Resource):
def get(self):
return {'hello': 'world'}
class CreateQKDApp(_Resource):
# Optare: Post request for the QKD Node to call the TeraflowSDN. Example of requests below
def post(self):
app = request.get_json()['app']
devices = self.context_client.ListDevices(Empty())
devices = devices.devices
local_device = None
# This for-loop won't be necessary if we can garantee Device ID is the same as QKDN Id
for device in devices:
for config_rule in device.device_config.config_rules:
if config_rule.custom.resource_key == '__node__':
value = json.loads(config_rule.custom.resource_value)
qkdn_id = value['qkdn_id']
if app['local_qkdn_id'] == qkdn_id:
local_device = device
break
# Optare: Todo: Verify that a service is present for this app
'''
requests.post('http://10.211.36.220/app/create_qkd_app', json={'app': {'server_app_id':'1', 'client_app_id':[], 'app_status':'ON', 'local_qkdn_id':'00000001-0000-0000-0000-000000000000', 'backing_qkdl_id':['00000003-0002-0000-0000-000000000000']}})
requests.post('http://10.211.36.220/app/create_qkd_app', json={'app': {'server_app_id':'1', 'client_app_id':[], 'app_status':'ON', 'local_qkdn_id':'00000003-0000-0000-0000-000000000000', 'backing_qkdl_id':['00000003-0002-0000-0000-000000000000']}})
'''
if local_device is None:
return {"status": "fail"}
external_app_src_dst = {
'app_id': {'context_id': {'context_uuid': {'uuid': DEFAULT_CONTEXT_NAME}}, 'app_uuid': {'uuid': ''}},
'app_status': 'QKDAPPSTATUS_' + app['app_status'],
'app_type': QKDAppTypesEnum.QKDAPPTYPES_CLIENT,
'server_app_id': app['server_app_id'],
'client_app_id': app['client_app_id'],
'backing_qkdl_id': [{'qkdl_uuid': {'uuid': qkdl_id}} for qkdl_id in app['backing_qkdl_id']],
'local_device_id': local_device.device_id,
'remote_device_id': {'device_uuid': {'uuid': ''}},
}
# Optare: This will call our internal RegisterApp which supports the creation of both internal and external app.
# Optare the verification for knowing if two parties are requesting the same app is done inside RegisterApp's function
self.qkd_app_client.RegisterApp(App(**external_app_src_dst))
# Optare: Todo: Communicate by SBI with both Nodes of the new App
return {"status": "success"}
# 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 qkd_app.service.rest_server.RestServer import RestServer
from .Resources import (
CreateQKDApp, Index)
URL_PREFIX = '/qkd_app'
# Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type.
RESOURCES = [
# (endpoint_name, resource_class, resource_url)
('api.index', Index, '/'),
('api.register_qkd_app', CreateQKDApp, '/create_qkd_app'),
]
def register_qkd_app(app_server : RestServer):
for endpoint_name, resource_class, resource_url in RESOURCES:
app_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name)
......@@ -70,6 +70,8 @@ COPY src/pathcomp/frontend/__init__.py pathcomp/frontend/__init__.py
COPY src/pathcomp/frontend/client/. pathcomp/frontend/client/
COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py
COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/
COPY src/qkd_app/__init__.py qkd_app/__init__.py
COPY src/qkd_app/client/. qkd_app/client/
COPY src/service/. service/
# Start the service
......
......@@ -47,6 +47,8 @@ def main():
get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_HOST ),
get_env_var_name(ServiceNameEnum.QKD_APP, ENVVAR_SUFIX_SERVICE_HOST ),
get_env_var_name(ServiceNameEnum.QKD_APP, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
])
VAR_NAME_OPTICAL_CONTROLLER_HOST = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_HOST)
VAR_NAME_OPTICAL_CONTROLLER_PORT = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_PORT_GRPC)
......
......@@ -17,7 +17,7 @@ import json, logging, uuid
from typing import Any, Dict, List, Optional, Tuple, Union
from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
from common.proto.context_pb2 import ConfigRule, DeviceId, Service
from common.proto.app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum
from common.proto.qkd_app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum
from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.type_checkers.Checkers import chk_type
......
......@@ -20,6 +20,7 @@ from common.proto.context_pb2 import (
Connection, ConnectionId, Device, DeviceDriverEnum, DeviceId, Service, ServiceId,
OpticalConfig, OpticalConfigId,ConnectionList,ServiceConfigRule
)
from common.proto.qkd_app_pb2 import App
from common.tools.context_queries.Connection import get_connection_by_id
from common.tools.context_queries.Device import get_device
from common.tools.context_queries.Service import get_service_by_id
......@@ -27,6 +28,7 @@ from common.tools.grpc.Tools import grpc_message_to_json_string
from common.tools.object_factory.Device import json_device_id
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from qkd_app.client.QKDAppClient import QKDAppClient
from service.service.service_handler_api.Exceptions import (
UnsatisfiedFilterException, UnsupportedFilterFieldException, UnsupportedFilterFieldValueException
)
......@@ -34,6 +36,7 @@ from service.service.service_handler_api.ServiceHandlerFactory import ServiceHan
from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key
from service.service.tools.object_uuid import opticalconfig_get_uuid
from common.DeviceTypes import DeviceTypeEnum
from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key, get_app_key
if TYPE_CHECKING:
from service.service.service_handler_api._ServiceHandler import _ServiceHandler
......@@ -46,11 +49,14 @@ class CacheableObjectType(Enum):
CONNECTION = 'connection'
DEVICE = 'device'
SERVICE = 'service'
QKD_APP = 'qkd-app'
class TaskExecutor:
def __init__(self, service_handler_factory : ServiceHandlerFactory) -> None:
self._service_handler_factory = service_handler_factory
self._context_client = ContextClient()
# DEPENDENCY QKD
self._qkd_app_client = QKDAppClient()
self._device_client = DeviceClient()
self._grpc_objects_cache : Dict[str, CacheableObject] = dict()
......@@ -327,3 +333,12 @@ class TaskExecutor:
str(dict_connection_devices)
)
)
# ----- QkdApp-related methods -------------------------------------------------------------------------------------
def register_app(self, app: App) -> None:
app_key = get_app_key(app.app_id)
self._qkd_app_client.RegisterApp(app)
LOGGER.info("reg registered")
self._store_grpc_object(CacheableObjectType.QKD_APP, app_key, app)
......@@ -14,6 +14,7 @@
from common.proto.context_pb2 import ConnectionId, DeviceId, ServiceId
import logging
from common.proto.qkd_app_pb2 import AppId
def get_connection_key(connection_id : ConnectionId) -> str:
return connection_id.connection_uuid.uuid
......@@ -26,3 +27,7 @@ def get_service_key(service_id : ServiceId) -> str:
context_uuid = service_id.context_id.context_uuid.uuid
service_uuid = service_id.service_uuid.uuid
return '{:s}/{:s}'.format(context_uuid, service_uuid)
def get_app_key(app_id : AppId) -> str:
return app_id.app_uuid.uuid
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